MonthMay 2013

How to automatically build a hash

Sorry if the title misled you, but I couldn’t come up with anything better and equally short. A YAML configuration file can be loaded into a hash, and this works pretty much out of the box. Sometimes, however, I need to also add configuration on the fly to the same hash. Then it becomes a little tedious to manually build missing keys, so I created my own Ando::Hash, which is a thin wrapper around Ruby’s Hash.

module Ando

  class Hash

    def initialize(hash=nil)
      hash ||= {}
      raise ArgumentError, "Expected a Hash object. (#{hash.class})" unless hash.respond_to?(:has_key?)
      @hash = hash
    end

    def [](*args)
      get_last_value(args)
    end

    def []=(*args)
      value = args.pop
      if args.first
        set_last_value(args, value)
      else
        initialize(value)
      end
      value # this is to preserve assignment semantics
    end

    protected

    # hook into @hash
    def get_previous_hash(path)
      current = @hash
      path[0..-2].each do |key|
        current = current[key] || current[key.is_a?(Symbol) ? key.to_s : key.to_sym]
      end
      current
    end

    # initialize intermediate keys to a hash
    def init_last(path, current = @hash)
      key = path.first
      return unless key
      raise ArgumentError, "Expected a Hash object. (#{current.class})" unless current.respond_to?(:has_key?)
      (current[key] = {}) unless (current.has_key?(key) && current[key].respond_to?(:has_key?))
      init_last(path[1..-1], current[key])
    end

    # set the given value to the last key
    def set_last_value(path, value)
      init_last(path)
      previous            = get_previous_hash(path)
      previous[path.last] = value
    end

    # get the value of the last key
    def get_last_value(path)
      previous = get_previous_hash(path)
      if previous.respond_to?(:has_key?) && path.last
        key = path.last
        return previous[key] || previous[key.is_a?(Symbol) ? key.to_s : key.to_sym]
      end
      previous
    end

  end

end

And here is a very short usage example.

>> h = Ando::Hash.new({:a => 1, :b => {:c => 3}})
=> #<Ando::Hash:0x007fd96a87d1b0 @hash={:a=>1, :b=>{:c=>3}}>
>> h[:b]
=> {:c=>3}
>> h[:b, :c]
=> 3
>> h[:a, :b, :c, :d, :e] = 5
=> 5
>> h[:a, :b, :c, :d, :e]
=> 5
>> h[:b]
=> {:c=>3}
>> h[:a]
=> {:b=>{:c=>{:d=>{:e=>5}}}}
>> h
=> #<Ando::Hash:0x007fd96a87d1b0 @hash={:a=>{:b=>{:c=>{:d=>{:e=>5}}}}, :b=>{:c=>3}}>

How to run Rake tasks programmatically

Some time ago I was told to reimplement some Rake tasks such that they would be available from a simple admin interface. Instead of that, I came up with a web interface for Rake tasks, such that exactly the same code can be run from the web and from a console.

At the core there are a couple of global methods: runnable_tasks and run_task.

#see http://stackoverflow.com/questions/4459330/how-do-i-temporarily-redirect-stderr-in-ruby -----------------------
require "stringio"

def capture_stderr
  previous, $stderr = $stderr, StringIO.new
  yield
  $stderr.string
ensure
  $stderr = previous
end

def capture_stdout
  previous, $stdout = $stdout, StringIO.new
  yield
  $stdout.string
ensure
  $stdout = previous
end
#-------------------------------------------------------------------------------------------------------------------

# requires rake such that descriptions are collected too
def require_rake
  return if defined? Rake
  require 'rake'
  Rake::TaskManager.record_task_metadata = true
  require 'rake/testtask'
  require 'rdoc/task'
  require 'tasks/rails'
end

# returns the stdout generated by an execution of the given task
def run_task(task_name)
  require_rake
  capture_stdout { Rake.application[task_name].invoke } # or Rake::Task[task_name].invoke
end

# returns a hash (each key is a task name and each value is a task description)
def runnable_tasks(include_tasks = /.^/, exclude_tasks = /.^/) # include (and exclude) nothing for safety
  require_rake
  list = []
  tasks = Rake.application.tasks
  tasks.each do |task|
    if task.name.match(include_tasks) && ! task.name.match(exclude_tasks)
      list.push(task.name)
    end
  end
  list.sort
  result = {}
  list.each do |item|
    result[item] = Rake::Task[item].comment
  end
  result
end

And here is how that it’s supposed to be used:

result = run_task(task_name) if runnable_tasks.has_key?(task_name)

URI encoding and decoding for Ruby

These are some global methods for dealing with URI encoding and decoding in Ruby, such that it behaves exactly like JavaScript.

      # printable ASCII chars (between 32 and 126) without 0..9, A..Z, a..z
      def symbols
        ' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' # symbols.length === 33
      end


      def gsub(input, replace)
        search = Regexp.new(replace.keys.map{|x| "(?:#{Regexp.quote(x)})"}.join('|'))
        input.gsub(search, replace)
      end

      # same as the JavaScript encodeURI
      def encodeURI(value)
        # encodeURI(symbols)  === "%20   ! %22   #   $ %25   &   '   (   )   *   +   , - .   /   :   ; %3C   = %3E   ?   @ %5B %5C %5D %5E _ %60 %7B %7C %7D   ~"
        # CGI.escape(symbols) === "  + %21 %22 %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C - . %2F %3A %3B %3C %3D %3E %3F %40 %5B %5C %5D %5E _ %60 %7B %7C %7D %7E"
        gsub(CGI.escape(value.to_s),
            '+'   => '%20',  '%21' => '!',  '%23' => '#',  '%24' => '$',  '%26' => '&',  '%27' => "'",
            '%28' => '(',    '%29' => ')',  '%2A' => '*',  '%2B' => '+',  '%2C' => ',',  '%2F' => '/',
            '%3A' => ':',    '%3B' => ';',  '%3D' => '=',  '%3F' => '?',  '%40' => '@',  '%7E' => '~'
        )
      end

      # same as the JavaScript encodeURIComponent, also for UTF8 multibyte chars (including gclef)
      def encodeURIComponent(value)
        # encodeURIComponent(symbols) === "%20   ! %22 %23 %24 %25 %26   '   (   )   * %2B %2C - . %2F %3A %3B %3C %3D %3E %3F %40 %5B %5C %5D %5E _ %60 %7B %7C %7D   ~"
        # CGI.escape(symbols)         === "  + %21 %22 %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C - . %2F %3A %3B %3C %3D %3E %3F %40 %5B %5C %5D %5E _ %60 %7B %7C %7D %7E"
        gsub(CGI.escape(value.to_s),
            '+'   => '%20',  '%21' => '!',  '%27' => "'",  '%28' => '(',  '%29' => ')',  '%2A' => '*',
            '%7E' => '~'
        )
      end

      # same as the JavaScript decodeURI
      def decodeURI(value)
        # decodeURI(encodeURI(symbols))     === symbols
        # CGI.unescape(CGI.escape(symbols)) === symbols
        CGI.unescape(gsub(value.to_s,
            '%20' => '+',    '!' => '%21',  '#' => '%23',  '$' => '%24',  '&' => '%26',  "'" => '%27',
            '('   => '%28',  ')' => '%29',  '*' => '%2A',  '+' => '%2B',  ',' => '%2C',  '/' => '%2F',
            ':'   => '%3A',  ';' => '%3B',  '=' => '%3D',  '?' => '%3F',  '@' => '%40',  '~' => '%7E'
        ))
      end

      # same as the JavaScript decodeURIComponent
      def decodeURIComponent(value)
        # decodeURIComponent(encodeURIComponent(symbols)) === symbols
        # CGI.unescape(CGI.escape(symbols))               === symbols
        CGI.unescape(gsub(value.to_s,
            '%20' => '+',    '!' => '%21',  "'" => '%27',  '(' => '%28',  ')' => '%29',  '*' => '%2A',
            '~'   => '%7E'
        ))
      end

More Generic Scopes

This is a followup to my previous post How to share code among ActiveRecord models. In this installment I’m going to show some additional generic scopes that I found useful for building up queries. In particular, some of these scopes are twice as generic, because they are defined by wrappers, so that their real name is specific enough to be meaningful.

Field Scope

This scope sets a field to some value. Think about a state field. Instead of writing

where(:state, :accepted)

you can write

with_state(:accepted)

which is a bit easier to read.

Here it is.

    # field_scope :state
    #   --> named_scope :with_state, ...
    def field_scope(field, cast = :to_s)
      named_scope :"with_#{field}", lambda { |*args|
        where(field => (args.size == 1 ? args.first.send(cast) : args.map(&cast)))
      }
    end

Example:

class Order
  field_scope :state
  named_scope :not_paid, with_state(:to_be_paid, :paying)
end

Order.not_paid.created_before(Date.yesterday).delete

What I like about this generic scope is that a common word like state becomes a specific term that I can later easily find with a simple and fast text search. So, instead of searching for state, whose matches could include a lot of false positives, I can search for with_state which is specific enough to only match what I’m really looking for. Anyway this is a dull surrogate of what future IDEs (able to understand semantics) will allow, like search for specific usages of common words.

Belonging-To Scope

This scope works analogously to the previous one, but for belongs_to relations. Think about a user_id field. Instead of writing

where(:user_id => tom.id)

you can write

belonging_to(tom)

which is a bit easier to read.

It’s almost always possible to guess the name of the field from the class of the object a model is supposed to belong to. If that’s not the case, I’ve provided an as option that accepts the name of the field (without the _id suffix).

      model.named_scope :belonging_to, lambda { |obj, options = {}|
        options = {
            :as => (obj.nil? || obj.is_a?(Integer)) ? 'user' : obj.class.to_s.underscore
        }.merge(options || {})
        where(:"#{options[:as]}_id" => (obj.respond_to?(:id) ? obj.id : obj))
      }

Example:

class Document
  belongs_to :user
  belongs_to :editor, :class_name => 'User'
end

owned_by_ann = Document.belonging_to(User.with_name('Ann'))
edited_by_me = Document.belonging_to(current_user, :as => :editor)

Polymorphic Scope

This scope works analogously to the previous one, but for belongs_to relations that are also polymorphic. In this case, I decided to retain simplicity by means of the with_ prefix. Think about a liked field that has been defined like this

belongs_to :liked, :polymorphic => true

Then it’s a bit tedious (and rude) to explicitly refer to liked_table and liked_id in queries, so this is how to elegantly magic them away.

    def sti_member?
      column_names.include?(inheritance_column)
    end

    def sti_root?
      return nil unless sti_member?
      superclass == ActiveRecord::Base
    end

    def sti_root
      return nil unless sti_member?
      klass = self
      while ! klass.sti_root?
        klass = klass.superclass
      end
      klass
    end

    # polymorphic_scope :favorite
    #   --> named_scope :with_favorite, ...
    def polymorphic_scope(field)
      named_scope :"with_#{field}", lambda { |obj|
        if obj.is_a?(Class)
          where(:"#{field}_type" => obj.to_s)
        else
          where(:"#{field}_type" => (obj.class.sti_root || obj.class).to_s, :"#{field}_id" => obj.id)
        end
      }
    end

All the sti_* methods account for Single Table Inheritance, which is a little tricky to deal with because of how it works behind the scenes.

Example:

class Like
  belongs_to :liked, :polymorphic => true
  polymorphic_scope :liked
end

class User
  has_many :likes
end

favorite_singers = current_user.likes.with_liked(Artist)
favorite_people = current_user.likes.with_liked(User)
favorite_songs = current_user.likes.with_liked(Song)

liked_lady_gaga = current_user.likes.with_liked(Singer.with_name('Lady_Gaga'))
puts "You like Lady Gaga since #{liked_lady_gaga.created_at}" unless liked_lady_gaga.nil?

Note how natural it all becomes, working equally well for classes (also for STI classes, like Artist < User) and objects (like Lady Gaga).

© 2017 Notes Log

Theme by Anders NorenUp ↑