开发者

How do I access options passed to an acts_as plugin (ActiveRecord decorator) from instance methods?

开发者 https://www.devze.com 2022-12-19 01:13 出处:网络
At the moment I store each option in its own class attribute but this lea开发者_如何转开发ds to hard to read code when I need to access the passed options from instance methods.

At the moment I store each option in its own class attribute but this lea开发者_如何转开发ds 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


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.


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


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!

0

精彩评论

暂无评论...
验证码 换一张
取 消