Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

About this user

wilhelm http://webjazz.blogspot.com

« Newer Snippets
Older Snippets »
Showing 1-6 of 6 total  RSS 

Numbering left and right attributes of a nested set

Uses a hash to model a tree that numbers the left and right attributes of a nested set.

# test case for depth_first traversal
#
# tree = { :electronics => {
# :televisions => { :tube => {},
# :lcd => {},
# :plasma => {} },
# :portable_elec => { :mp3_players => {
# :flash => { }
# },
# :cd_player => { },
# :two_way_radio => { }
# }
# }
# }
#
# tree.depth_first do |name, parent_name, lft, rgt|
# puts "#{parent_name}/#{name}: #{lft} - #{rgt}"
# end

   1  
   2  class Hash
   3    def depth_first(&block)
   4      root_node = self.keys.first
   5      root_children = self[root_node]
   6      depth_first_helper(root_node, root_children, nil, 1, &block)
   7    end
   8  
   9    private
  10    def depth_first_helper(name, children, parent_name, lft, &block)
  11      if children.empty?
  12        rgt = lft + 1
  13      else
  14        children_lft = lft + 1
  15        children.each do |child_name, grandchildren|
  16          rgt = depth_first_helper(child_name, grandchildren, name, children_lft, &block)
  17          children_lft = rgt
  18        end
  19      end
  20      block.call(name, parent_name, lft, rgt)
  21      return rgt + 1
  22    end
  23  end

MIME Responder before_filter for Rails

This is abridged for conciseness. For full post, visit MIME Responder filter for Rails

I spent last week integrating Mobtropolis with facebook. Mobtropolis doesn't require a facebook account to use it, so like other websites, it has its own authentication mechanism, something like:

   1  
   2  class PostController < ActionController::Base
   3    before_filter :website_authenticate_filter, :except => [:index, :list]
   4  end


When I started using facebooker library, it already came with an authentication before_filter. That means we have two authentication filters, one native, and one for facebook. Mobtropolis users don't have to be in facebook to use it, and facebookers don't have to sign up again in mobtropolis to use it.

However, since before_filters are executed in succession, it leads to a case where the facebook authentication would be called if html was requested, and vice versa. The alternative was to take apart both authentication filters, and create a monolithic filter to handle the two different cases. Instead, I made a before_filter respond to different MIME types.

   1  
   2  class PostController < ActionController::Base
   3     before_respond_to_filter :except => [ :index, :list ] do |format|
   4      format.html :website_authentication_filter
   5      format.fbml :facebook_authentication_filter
   6    end
   7  end


That way, I didn't have to mix together the guts of each authentication filter, and it solved the problem of the wrong authentication filter being run. You can also use it like:

   1  
   2  class PostController < ActionController::Base
   3    before_responds_to_filter :only => :home do |format|
   4      format.html do |controller|
   5        return if controller.logged_in?
   6        controller.send(:redirect_to, :controller => :home)
   7      end
   8      format.fbml :ensure_application_is_installed_by_facebook_user
   9    end   
  10  end


It ended up the code for this sort of magic was fairly easy. I'm not sure if there's an easier way to do what I wanted, but I'll see if Rails core people would find it useful (or not). In the meanwhile, for those of you Rubyists that have written plugins before that want to play with it. As with the usual mumbo jumbo, it's provided as is, I'm not maintaining it, and do whatever you want with it:

   1  
   2  # init.rb
   3  require 'mime_responder_filter'
   4  ActionController::Base.send :include, Threecglabs::Filters::MimeResponderFilter

   1  
   2  # mime_responder_filter.rb
   3  module Threecglabs
   4    module Filters
   5  
   6      # MimeResponderFilter 
   7      module MimeResponderFilter
   8  
   9        def self.included(mod)
  10          mod.extend(ClassMethods)
  11        end
  12    
  13        # Filters can respond to different mime types, so that you can use 
  14        # different filters depending on which mime type is being requested
  15        #
  16        #   before_responds_to_filter :except => [:login, :signup, :forgot, :invite_request, :profile] do |format|
  17        #     format.html :authentication_filter
  18        #     format.fbml :ensure_application_is_installed_by_facebook_user
  19        #   end
  20        #
  21        # This way, one can take the appropriate actions in setting up authentication 
  22        # from different mime types, and still separate the implemenation of the different
  23        # kinds of implementations
  24        #
  25        # The formats also take blocks, like regular filters
  26        #
  27        #   before_responds_to_filter :only => :home do |format|
  28        #     format.html do |controller|
  29        #       return if controller.logged_in?
  30        #       controller.send(:redirect_to, :controller => :home)
  31        #     end
  32        #     format.fbml :ensure_application_is_installed_by_facebook_user
  33        #   end
  34        #
  35        # NOTE: an :all format defaults to :html, therefore, a format.html is required
  36        module ClassMethods        
  37          def before_respond_to_filter(options = {}, &block)
  38            before_filter MimeResponderFilter.new(&block), options
  39          end
  40          
  41          private
  42          # This is a call that implements a MIME responder filter
  43          class MimeResponderFilter#:nodoc:
  44            attr_reader :filters
  45            
  46            def initialize(&block)
  47              @filters = {}
  48              block.call(self)
  49            end
  50            
  51            def filter(controller)
  52              filter = @filters[controller.request.format.to_sym] || @filters[:html]
  53              if filter.kind_of?(Proc)
  54                filter.call(controller)
  55              else
  56                controller.send!(filter)
  57              end
  58            end
  59            
  60            # implements the "format.#{mime_type}" part of the filter
  61            def method_missing(mime_type, method_name = nil, &block)
  62              if block_given?
  63                @filters[mime_type.to_sym] = block
  64              else
  65                @filters[mime_type.to_sym] = method_name.to_sym
  66              end
  67            end
  68          end
  69        end
  70  
  71      end
  72    end
  73  end


Snippet!

State change observer for ActiveRecord in Rails

This is something to observe state changes as indicated by an attribute in an ActiveRecord Object in Rails. Normally, if you just need to observe an object and do things based on what state it's in, use the Observer provided in Rails. You can then use callbacks to do whatever you need to in the observer. However, if you want to do something upon a particular state transition, then you can try the stuff below:

Create a model like this. Here, the attribute mode keeps track of the state.
   1  
   2  class CreateCarTransmission < ActiveRecord::Migration
   3    def self.up
   4      create_table :car_transmission do |t|
   5        t.column :engine_id, :integer, :null => false
   6        t.column :mode, :string, :null => false, :default => "park"
   7      end
   8    end
   9  
  10    def self.down
  11      drop_table :car_transmission
  12    end
  13  end

In the model, you include the state transition observable class, and call state_observable.
   1  
   2  class CarTransmission < ActiveRecord::Base
   3    include StateTransition::Observable
   4    state_observable CarTransmissionNotifier, :state_name => :mode
   5  end

Then you define a notifier class that has different callbacks based on the transitions. It follows the pattern, "#{state_name}_from_#{state you're coming from}_to_#{state you're going to}"
   1  
   2  class CarTransmissionNotifier < StateTransition::Observer
   3    def mode_from_drive_to_reverse(transmission)
   4      # send out mail and flash lights about how this is bad.
   5    end
   6  end


So whenever you change the state from "drive" to "reverse" of a car transmission model in the controller, the notifier will fire up.

To use it, put the following code in a file called 'state_transition.rb', put it in your lib/ directory in rails.
   1  
   2  require 'observer'
   3  
   4  module StateTransition
   5    module Observable
   6      class StateNameNotFoundError < RuntimeError
   7        def message
   8          "option :state_name needs to be set to the name of an attribute"
   9        end
  10      end
  11  
  12      def self.included(mod)
  13        mod.extend(ClassMethods)
  14      end
  15  
  16      module ClassMethods
  17  
  18        def state_observable(observer_class, options)
  19          raise StateNameNotFoundError.new if options[:state_name].nil?
  20          state_name = options[:state_name].to_s
  21          
  22          include Object::Observable
  23  
  24          define_method(:after_initialize) do 
  25            add_observer(observer_class.new)
  26          end
  27  
  28          define_method("#{state_name}=") do |new_state|
  29            old_state = read_attribute(state_name)
  30            if old_state != new_state
  31              write_attribute(state_name, new_state) # TODO yield the update method
  32              changed
  33              notify_observers(self, state_name, old_state, new_state)
  34            end
  35          end
  36        end
  37  
  38      end
  39      
  40    end
  41  
  42    class Observer
  43      def update(observable, state_name, old_state, new_state)
  44        send("#{state_name}_from_#{old_state}_to_#{new_state}", observable)
  45      rescue NoMethodError => e
  46        # ignore any methods not found here
  47      end  
  48    end
  49  
  50  end


more details here

Syntactic sugar for empty containers

In any web application, we're often just reading a collection of rows from the database and displaying it in the browser. Often times, we'll have code that looks like this:

   1  
   2  unless @friends.empty? 
   3    @friends.each do |friend| 
   4      puts friend.username
   5    end
   6  else 
   7    puts No friends yet
   8  end


dunno why, but I find this kinda ugly. Can we do better?

   1  
   2  @friends.each do |friend| 
   3    puts friend.username
   4  end.empty do
   5    puts No friends yet
   6  end


Well, that worked. I kinda like it. In a way, it's almost like being able to write my own "else" statement. If I had used curly braces instead of "do/end", it might look pretty close. Here's the code for empty:
   1  
   2  class Array
   3    def empty(message = "")
   4      if self.empty?
   5        return block_given? ? (yield message) : message
   6      end
   7    end
   8  end

You can read the full post at:
http://webjazz.blogspot.com/2007/09/syntactic-sugar-for-dealing-with-empty.html

Attribute Cache

Sometimes, you don't want to calculate the value of an attribute every time--like the sum of an array. However, you don't want it cached on the outside of the object since that makes for messy code. But you seem to cache things over and over again inside objects. Here's a module AttributeCache that does a little meta programming to make attribute caching a little bit easier. more details at http://webjazz.blogspot.com/2007/05/ruby-snippet-caching-object-attributes.html

   1  
   2  module AttributeCache
   3  
   4    def metaclass; class << self; self; end; end
   5    
   6    def cache(attr_name, options)
   7      instance_variable_set "@#{attr_name}", options[:initial]
   8      instance_variable_set "@#{:sum}_outdated", true
   9  
  10      metaclass.instance_eval do 
  11        define_method("outdate_#{attr_name}") do
  12          puts "in outdated"
  13          instance_variable_set "@#{attr_name}_outdated", true
  14        end
  15      end
  16      
  17      metaclass.class_eval %Q{
  18        def cached_#{attr_name}(&block)
  19          if @#{attr_name}_outdated
  20            puts "in cached to update!"
  21            @#{attr_name}_outdated = false
  22            @#{attr_name} = block.call
  23          else
  24            puts "in cached to give cache!"
  25          end
  26          return @#{attr_name}
  27        end
  28      }
  29  
  30    end
  31    
  32  end
  33  
  34  class Collection
  35    include AttributeCache
  36  
  37    def initialize
  38      @array = []
  39      cache :sum, :initial => 0
  40    end
  41  
  42    def add(x)
  43      outdate_sum
  44      @array << x
  45    end
  46  
  47    def sum
  48      cached_sum { @array.inject {|t, e| t += e} }
  49    end
  50    
  51  end
  52  
  53  c = Collection.new
  54  puts c.public_methods(false).inspect
  55  c.add(2)
  56  c.add(3)
  57  puts c.sum
  58  puts c.sum
  59  c.add(4)
  60  puts c.sum
  61  puts c.sum

How to interlace two arrays

This is a recursive implementation of interlacing two arrays.
A more in-depth description is available.

   1  
   2  class Array
   3    # interlaces an array with another array.  
   4    # It dovetails the two arrays together.
   5    # 
   6    #   [1,2,3,4,5,6].interlace([7,8,9]) # => [1, 7, 2, 8, 3, 9, 4, 5, 6]
   7    #   
   8    #   [1,2,3].interlace([1,2,3,4,5]) # => [1, 1, 2, 2, 3, 3, 4, 5]
   9    def interlace(other_array)
  10      return other_array if self.empty? 
  11      return [self[0]] + other_array.interlace(self[1..-1])
  12    end
  13  end
« Newer Snippets
Older Snippets »
Showing 1-6 of 6 total  RSS