Friday, 27 April 2012

import a gem list

If your project doesn't use Gemfile, paste the wanted gemlist in the GEM_LIST string, run this script and that's it. I know, it could be improved, but that's it by now. NOTE: only the first version of the gem is installed, which is the highest.

#!/usr/bin/ruby

gem_list = <<GEM_LIST
actionmailer (2.3.8)
actionpack (2.3.8)
activerecord (2.3.8)
activeresource (2.3.8)
activesupport (2.3.8)
aws-s3 (0.6.3)
barista (1.3.0)
bistro_car (0.2.2)
soap4r (1.5.8)
therubyracer (0.10.2)
will_paginate (2.3.16)
xml-simple (1.1.2)
GEM_LIST

gem_list.split("\n").each do |line|
  gem      = line.match(/^.+? /).to_s # with a space at the end
  versions = line.match(/\(.+\)/)     # surrounded by parenthesis
  highest_version = line.match(/([\d\.]+)/).to_s
  puts `gem install #{gem} -v #{highest_version}`
end

null_store cache for Rails 2

In Rails 3 you can use a fake cache, very useful in development, this uses ActiveSupport::Cache::NullStore, which is not available for Rails 2, but can be easily integrated with some copy and paste with a little customization.
      def initialize
        super # options parameter has been removed 
        extend Strategy::LocalCache
      end
and this is the call
# config/environments/development.rb
require 'vendor/gems/activesupport/cache/null_store.rb' # could also go in environment.rb
# [...]

config.cache_store = :null_store # In Rails 3 it's simply :null 

Update for rails 2.3.18:

Have your file in lib, rather than vendor/gems and have it referenced with full path, something like this:

require "#{Rails.root}/lib/activesupport/cache/null_store.rb"

If every other attempt fail, how about redirecting the cache to dev/null?
config.cache_store = :file_store, "/dev/null"


Thursday, 19 April 2012

Ruby 1.9.2 Goodies

[1,2,3].each_with_object(1).map(&:+)
# => [2, 3, 4]

# Same outcome, even shorter
[1, 2, 3].map(&1.method(:+))
# => [2, 3, 4]
# Thanks to rubyquicktips.com

######## even in 1.8
a = [[1,2,3],[2,3,4],[3,4,5]]

# get the union:
a.inject(a.first) { |f,x| f = f | x } # => [1, 2, 3, 4, 5]

# get the intersection:
a.inject(a.first) { |f,x| f = f & x } => [3]

Wednesday, 4 April 2012

Ruby Koan #183 about scoring project: The Greed Game

When I searched the site for koan greed I was not surprised to see 19 answers and though I've seen very performing implementations, I was quite surprised to see that nobody solved the problem with a class(maybe because about_classes was the next set of koans?), which was my first take. I admit I got lost in refactoring before looking for a comparison, but this is just my spare time.
Here it goes!
class Greed
  def initialize(dice_faces)
    @dice_count = count_occurrences_in(dice_faces)
    @score      = calculate_score!
  end

  attr_reader :score

  def calculate_score!
    @score  = 0
    @score += score_of_triple_1(1000)
    @score += score_of_other_triples(100)
    @score += score_of_5s(50)
    @score += score_of_1s(100)
  end

private

  def score_of_5s(score)
    face_score(5, score)
  end

  def score_of_1s(score)
    face_score(1, score)
  end

  def score_of_other_triples(score_each)
    score_triples score_each, (2..6).to_a
  end

  # >>>>> here starts the meat

  def score_of_triple_1(score)
    score if @dice_count[1] >= 3
  end

  def score_triples(score, faces)
    partial_score = 0
    @dice_count.each_with_index do |times, face|
      next unless faces.include? face
      partial_score += score * face if times >= 3
    end
    partial_score
  end

  # faces counted in triples are escluded
  def face_score(face, score = 0)
    times = @dice_count[face]
    score * (times % 3)
  end

  # <<<<< here ends the meat

  def count_occurrences_in(dice_faces)
    # nil for 0 which is not a face, then the 6 faces
    [nil,0,0,0,0,0,0].tap do |occurrences|
      dice_faces.each { |face| occurrences[face] += 1 }
    end
  end

end


def score(dice)
  # You need to write this method
  Greed.new(dice).score
end


Even considering only "the meat" of my class, the solution on github is still a few lines shorter... Hei, wait, I could implement the sandwitch in score_triple and count_times! I feel like Randy Marshall watching cooking programs!

Monday, 2 April 2012

restrict dates in datepicker with html data attribute

In this fiddle you can see how easy it is to configure a datepicker using the data attribute. Here's the code anyway:
class DateDisabler

  constructor: (@only) ->

  beforeShowDay: (date) =>
    if @only == date.getDate()
      [true, 'available', 'This date is a good date']
    else
      [false, 'unavailable', 'This date is NOT good']


jQuery ->
  $('.datepicker').each (i, e) ->
    dates = $(e).data('only')
    disabler = new DateDisabler dates
    $(e).datepicker({beforeShowDay: disabler.beforeShowDay})
and the haml that calls it:
.datepicker{data: {only: 5}}
Please note the fat arrow in the beforeShowDay, I'll have a look at it soon. Of corse you want it to make it accept an array but that's quite easy and I'm going into it right now. For more complex solutions I recommend gazay/gon, which I know thanks to this railscast

Sunday, 1 April 2012

Realex RealVault ActiveMerchant implementation

The following is the extension of the ActiveMerchant gem that will allow the system to store information about the customer and its credit card in realex, typically this is used for recurring payments (in fact it's the name of the pdf provided by Realex), but in our case the need was for charging customers that don't show up at the hotel. It's not the final refactory.

# see: RealVault - Recurring Payments.pdf
module ActiveMerchant::Billing

  class RealVault < RealexGateway
    # 7. on page 24
    URL = 'https://check-doc.cgi'

    # 7.1 (Setup a new Payer)
    # Create a Profile for a payer using payer‐new
    # If the payer exists another one will be stored
    # payer is a hash (see self.test)
    def send_payer(payer, options = {})
      requires! options, :order_id
      request = build_payer_new_request payer, options
      commit request, URL
    end
  
    # 7.2 (Payment method setup)
    # Store the Card details using Card‐New
    # If the card exists another one will be stored
    # card is a ActiveMerchant::Billing::CreditCard
    # options is a hash
    def send_card(card, options)
      requires! options, :order_id
      request = build_card_new_request card, options
      commit request, URL
    end

    # see self.test or send_x methods
    def store!(payer, card, options)
      send_payer_response = send_payer(payer, options)
      send_card_response  = send_card(card, options) if send_payer_response.success?
      
      { :success? => (send_payer_response.success? && send_card_response.success?),
        :send_payer_response => send_payer_response,
        :send_card_response  => send_card_response }
    end

    # ActiveMerchant::Billing::RealVault.test
    # will store the payer and the card in the test account
    # makes a purchase before as suggested by the doc. to make sure auth is ok
    # works out of the box
    def self.test
      timestamp = Time.new.to_s :number
      currency  = 'EUR'
      firstname = 'John'
      surname   = 'Smith'
      payerref  = timestamp
      orderid   = timestamp
      
      rv = ActiveMerchant::Billing::RealVault.new({:password => "pwd",
                                                   :currency => currency,
                                                   :rebate   => "rebate",
                                                   :account  => "internettest",
                                                   :login    => "merchant_id",
                                                   :gateway  => "Realex"})

      card = ActiveMerchant::Billing::CreditCard.new(
                    :number             => 'test_cc_number',
                    :month              => '12',
                    :year               => '2012',
                    :type               => 'visa',
                    :first_name         => firstname,
                    :last_name          => surname,
                    :verification_value => '123')

      payer = { :type      => 'Business',
                :ref       => payerref, # 'smithj01'
                :title     => 'Mr',
                :firstname => firstname,
                :surname   => surname,
                :email     => 'jsmith@acme.com',
                :company   => 'Acme Inc',
                :address   => {
                  :line1   => '123 Fake St.',
                  # :line2
                  # :line3
                  :postcode     => '3',
                  :city         => 'Hytown',
                  :county       => 'Dunham',
                  :country      => 'Ireland',
                  :country_code => 'IE'
                },
                :phonenumbers => {
                  :home   => '55555555',
                  :work   => '+35317433923',
                  :fax    => '+35317893248',
                  :mobile => '+353873748392',
                }
              }

      options = {:order_id => orderid,
                 :currency => currency,
                 :payerref => payer[:ref]}

      rv.purchase 777, card, options
      rv.store! payer, card, options
      # rv.send_payer payer, options
      # rv.send_card card, options
    end

    private

    def build_card_new_request(card, options)
      action    = 'card-new'
      timestamp = Time.now.strftime '%Y%m%d%H%M%S' # same as to_s(:number), but not subjected to override
      orderid   = sanitize_order_id options[:order_id]
      chname    = "#{card.first_name}#{card.last_name}"
      expdate   = card.year.to_s[2..3] + card.month.to_s
      
      sha1      = sha1from "#{timestamp}.#{@options[:login]}.#{orderid}...#{options[:payerref]}.#{chname}.#{card.number}"

      xml       = Builder::XmlMarkup.new :indent => 2
      
      xml.tag! :request, :timestamp => timestamp, :type => action do
        xml.tag! :merchantid, @options[:login]
        xml.tag! :orderid   , orderid
        xml.tag! :card do
          xml.tag! :ref     , card.type + timestamp[3..14]
          xml.tag! :payerref, options[:payerref]
          xml.tag! :number  , card.number
          xml.tag! :expdate , expdate
          xml.tag! :chname  , chname
          xml.tag! :type    , CARD_MAPPING[card_brand(card).to_s]
          xml.tag! :issueno
        end
        xml.tag! :sha1hash, sha1
      end
    end
    
    
    def build_payer_new_request(payer, options)
      action    = 'payer-new'
      timestamp = Time.now.strftime '%Y%m%d%H%M%S' # same as to_s(:number), but not subjected to override
      orderid   = sanitize_order_id options[:order_id]
      sha1      = sha1from "#{timestamp}.#{@options[:login]}.#{orderid}...#{payer[:ref]}"

      xml       = Builder::XmlMarkup.new :indent => 2

      xml.tag! :request, :timestamp => timestamp, :type => action do
    
        xml.tag! :merchantid, @options[:login] 
        xml.tag! :account   , @options[:account]
        xml.tag! :orderid   , orderid

        xml.tag! :payer, :type => payer[:type], :ref => payer[:ref] do
          [:title, :firstname, :surname, :company].each do |tag|
            xml.tag! tag, payer.send('[]', tag)
          end
          xml.tag! :address do
            address = payer[:address]
            [:line1, :line2, :line3, :city, :county, :postcode].each do |tag|
              xml.tag! tag, address.send('[]', tag)
            end
            xml.tag! :country, address[:country], :code => address[:country_code]
          end
          xml.tag! :phonenumbers do
            phonenumbers = payer[:phonenumbers]
            [:home, :work, :fax, :mobile].each do |tag|
              xml.tag! tag, phonenumbers[tag]
            end
          end
          xml.tag! :email, payer[:email]
          xml.tag! :comments do
            xml.tag! :comment, nil, :id => 1 
            xml.tag! :comment, nil, :id => 2 
          end
        end
        xml.tag! :sha1hash, sha1
        xml.tag! :comments do
            xml.tag! :comment, nil, :id => 1 
            xml.tag! :comment, nil, :id => 2 
        end
      end

      xml.target!
    end


  end


end
Unfortunately even the gem itself needed a slight change, due to constant priority order in Ruby (this wouldn't be necessary in java for example):
  # Realex.rb in ActiveMerchant 1.9.4
  # commit method in private section
-      def commit(request)
+      def commit(request, url = URL)