views:

278

answers:

2

i am trying to create my first rails plugin, and i want it to be configurable, that is to say, i want to be able to set a variable in the environment.rb file or something.

UPDATE: i'm trying to do something like what's done here: http://soakedandsoaped.com/articles/read/exception-notifier-ruby-on-rails-plugin. i have tried mimicking their code, but i can't get it working.

i have the plugin working with the value hard-coded, but everything i have tried so far for making it configurable hasn't worked.

Here's some of the code:

#vendor/plugin/markup/lib/markup_helper.rb
module MarkupHelper
    def stylesheet_cache_link_tag(*sources)
      cache = assests_cache_dir ? assests_cache_dir : ""
      options = sources.extract_options!.stringify_keys
      cached_name = options.delete("cached_name")
      stylesheet_link_tag(sources, :cache=> File.join(cache, cached_name))
    end

    def javascript_cache_include_tag(*sources)
      cache = assests_cache_dir ? assests_cache_dir : ""
      options = sources.extract_options!.stringify_keys
      cached_name = options.delete("cached_name")
      javascript_include_tag(sources, :cache=> File.join(cache, cached_name))
    end
end

#something like the following in config/environment.rb or probably config/environments/production.rb
MarkupConfig.assests_cache_dir = "cache"

i want assests_cache_dir to default to "cache" but be able to set in an environment config file. i have googled a long time on this, and can't find anything discussing this. How can i accomplish this?

+2  A: 
module MarkupHelper
  mattr_accessor :assets_cache_dir
  self.assets_cache_dir = "cache"

  def assets_cache_dir
    MarkupHelper.assets_cache_dir
  end
end

Then in environment.rb (or development.rb/test.rb/production.rb if you want different values for each environment):

MarkupHelper.assets_cache_dir = "my-value"
tomafro
sweet, thx! so what's the difference between attr_accessor and mattr_accessor (i'm a newbie to ruby obviously)?
gabe
mattr_accessor is rails-only, and defines accessors on an actual module. attr_accessor defines accessors on instances of classes (not the classes themselves).
tomafro
There is also `cattr_accessor` which defines accessors on the class itself.
Topher Fangio
so kind of like the difference between a static member and an instance member in C#? attr_accessor would be accessible only on an instantiated object (like an instance member in C#), and in rails, mattr_accessor and cattr_accessor would be accessible by just using the module name + dot or class name + dot, respectively (like a static member in C#)?
gabe
i guess i prematurely set this as the answer. i actually could not get the line for the config file to work. i am receiving "uninitialized constant MarkupHelper (NameError)" when i try to start the dev web server. here's all i have in my vendor/plugin/markup/init.rb: ActionView::Base.send :include, MarkupHelper
gabe
Seems like you are having an unrelated problem. Where is MarkupHelper defined? You should require the file it is defined in before referencing it.
tomafro
it's a plugin, so i have it in vendor/plugin/markup/lib/markup_helper.rb. so do i have to have a require 'vendor/plugin/markup/lib/markup_helper' in the environment config file (i.e. environment.rb)?
gabe
A: 

Although the approach used by tomafro is quite easy to use, another approach is to use a database.yml-style configuration file that can be split according to environments:

module MyPlugin
  class Configuration
    # == Constants ==========================================================

    CONFIG_FILES = [
      "#{RAILS_ROOT}/config/myplugin.yml",
      "#{RAILS_ROOT}/config/myplugin.yaml"
    ].freeze

    DEFAULT_CONFIGURATION = {
      :url => DEFAULT_HOSTNAME
    }.freeze

    # == Class Methods ======================================================

    # :nodoc:
    def self.config_file_found
      CONFIG_FILES.find do |path|
        File.exist?(path)
      end
    end

    # Returns the default path to the configuration file
    def self.default_path
      config_file_found or CONFIG_FILES.first
    end

    # == Instance Methods ===================================================

    # Creates a new MyPlugin::Configuration instance by reading from the
    # configuration file.
    # +env+ The Rails environment to load
    def initialize(env)
      config_file = self.class.config_file_found

      @env_config = DEFAULT_CONFIGURATION

      if (@config = (config_file and YAML.load(File.open(config_file))))
        [ @config['defaults'], @config[env] ].each do |options|
          if (options)
            @env_config = @env_config.merge(options.symbolize_keys)
          end
        end
      end
    end

    # Will return +true+ if a configuration file was found and loaded, or
    # +false+ otherwise.
    def exists?
      @env_config != DEFAULT_CONFIGURATION
    end

    # Returns a particular configuration option.
    def [](key)
      @env_config[key.to_sym]
    end
  end

  def self.config
    @config ||= Configuration.new(Rails.env)
  end
end

You would use this as:

settting = MyPlugin.config[:param_name]

You can also write utility methods to fetch particular values, or use OpenStruct instead of a configuration Hash. This is posted merely as an example of another design pattern.

tadman