views:

161

answers:

3

At the moment I store each option in its own class attribute but this leads to hard to read code when I need to access the passed options from instance methods.

For example if I pass a column name as an option I have to use self.send(self.class.path_finder_column) to get the column value from an instance method.

Notice I have prefixed the class attribute with the name of my plugin to prevent name clashes.

Here is a simple code example of a plugin which is passed an option, column, which is then accessed from the instance method set_path. Can the getters/setters be simplified to be more readable?

# usage: path_find :column => 'path'

module PathFinder    
  def path_finder(options = {})
    send :include, InstanceMethods

    # Create class attributes for options 
    self.cattr_accessor :path_finder_column
    self.path_finder_column = options[:column]

    module InstanceMethods
      def set_path        
        # setter
        self.send(self.class.path_finder_column + '=', 'some value')
        # getter
        self.send(self.class.path_finder_column)
      end
    end
  end 
end

ActiveRecord::Base.send :extend, PathFinder
A: 

You can use cattr_accessor to store the configuration value at a class level and use in all your instance methods. You can see an example at http://github.com/smsohan/acts_as_permalinkable/blob/master/lib/active_record/acts/permalinkable.rb

The code to look at is this:

def acts_as_permalinkable(options = {})
          send :cattr_accessor, :permalink_options
          self.permalink_options = { :permalink_method => :name, :permalink_field_name => :permalink, :length => 200 }
          self.permalink_options.update(options) if options.is_a?(Hash)

          send :include, InstanceMethods
          send :after_create, :generate_permalink
        end

Hope it helps!

Sohan
-1: Code posted in the question already makes use of cattr_accessor.
EmFi
+1  A: 

To start with cattr_accessor creates a class variable for each symbol it's given. In ruby, class variables have their names prefixed with @@.

So you can use @@path_finder_column in place of self.class.path_finder_column.

However that's a moot point considering what I'm going to suggest next.

In the specific case presented by the code in the question. The combination getter and setter you've defined doesn't fit ruby conventions. Seeing as how you're essentially rebranding the accessors generated for the path_finder_column with a generic name, you can reduce it all to just a pair of aliases.

Assuming there's an error in the combo accessor (how is the code supposed to know whether to get or set), The finalized module will look like this:

module PathFinder    
  def path_finder(options = {})
    send :include, InstanceMethods

    # Create class attributes for options 
    self.cattr_accessor :path_finder_column
    self.path_finder_column = options[:column]
    alias :set_path, path_finder_column
    alias :set_path=, "#{path_finder_column}="
  end

  module InstanceMethods  
    # other instance methods here.
  end
end
EmFi
+1  A: 

You can generate all those methods at runtime.

module PathFinder
  def path_finder(options = {})

    # Create class attributes for options 
    self.cattr_accessor :path_finder_options
    self.path_finder_options = options

    class_eval <<-RUBY
      def path_finder(value)
        self.#{options[:column]} = value
      end

      def path_finder
        self.#{options[:column]}
      end
    RUBY
  end
end

ActiveRecord::Base.send :extend, PathFinder

Unless you need to store the options, you can also delete the lines

self.cattr_accessor :path_finder_options
self.path_finder_options = options

Note that my solution doesn't need a setter and a getter as long as you always use path_finder and path_finder=. So, the shortest solution is (assuming only the :column option and no other requirements)

module PathFinder
  def path_finder(options = {})

    # here more logic
    # ...

    class_eval <<-RUBY
      def path_finder(value)
        self.#{options[:column]} = value
      end

      def path_finder
        self.#{options[:column]}
      end
    RUBY
  end
end

ActiveRecord::Base.send :extend, PathFinder

This approach is similar to the one adopted by acts_as_list and acts_as_tree.

Simone Carletti