15

I have a mountable engine called Blog that apps can use.

What's the best way to allow apps that use the engine to set a configuration variable like site_name (so that the engine can display it in the design)?

Update:

I've seen some gems create a 'config/initializers/gem_name.rb' file. Is there any specification on how to:

  1. Create a file like that on the engine's side
  2. Copy it into the app's side
  3. How to access those set variables on the engine's side?

I tried creating Blog.site_name = "My Site" in the app's config/initializers/blog.rb file but get an Undefined method error.

Simone Carletti
  • 164,184
  • 42
  • 341
  • 356
Hopstream
  • 5,893
  • 9
  • 47
  • 77

3 Answers3

20

Figured out an even better solution that also allows you to set default values (incase the app using the engine doesn't specify a config)...

  1. Create config variables in your app's /config/initializers/blog.rb like this:
    Blog.setup do |config|
        config.site_name = "My Site Name"
    end
  1. In your engine's /lib/blog/engine.rb set default values like this:
    module Blog
    
         self.mattr_accessor :site_name
         self.site_name = "Site Name"
         # add default values of more config vars here
    
         # this function maps the vars from your app into your engine
         def self.setup(&block)
            yield self
         end
    
    end
  1. Now you can simply access the config variables in your engine like this:

Blog.site_name

Much cleaner.

vtamara
  • 118
  • 6
Hopstream
  • 5,893
  • 9
  • 47
  • 77
  • If you are using the `yield` keyword, assigning the &block variable is not necessary. You can simply use `def self.setup` instead. – eltiare Apr 13 '15 at 22:17
  • In rails 4 / ruby 2 I had to use **Blog::Engine.site_name** since after `module Blog` you have a `class Engine < ::Rails::Engine` – chech Apr 28 '15 at 08:58
  • 1
    Skip the `setup` method definition and `alias_method :setup :tap`. It's an identical method signature – sbonami Feb 10 '17 at 15:40
  • The original answer didn't work with Rails 6.1. It would produce an error `TypeError: module attributes should be defined directly on class, not singleton` as reported at https://github.com/rails/rails/issues/40831. I have edited the answer to make it work also with Rails 6.1, but to keep trace, before `mattr_accessor :site_name` it had `class << self` and after the comment `# add default values of more config vars here` it had `end` – vtamara Dec 20 '20 at 03:56
6

After a lot of testing and looking into existing gems, here is what works in Rails 4:

Considering your engine's name is Blog:

In your engine's /lib/blog/engine.rb put in this:

module Blog

    def self.setup(&block)
        @config ||= Blog::Engine::Configuration.new

        yield @config if block

        @config
    end

    def self.config
        Rails.application.config
    end

end

In your app, create a file called /config/initalizers/blog.rb and set config vars like this:

Blog.setup do |config|
    config.testingvar = "asdfasdf"
end

Then you can access these config variables ANYWHERE in your engine like this:

Blog.config.testingvar

Hope this helps someone. There is very little documentation on this right now so it was all trial and error.

astropanic
  • 10,140
  • 17
  • 64
  • 128
Hopstream
  • 5,893
  • 9
  • 47
  • 77
1

I know this is a fairly old post, but in the event someone in the future finds this, I'd like to recommend the Angostura gem for passing dependencies into a Rails engine. To use it, assuming my engine is called 'Blog' and I want to access a variable called 'site_name', the engine's lib/blog.rb looks something like:

require "blog/engine"
require "angostura"

module Blog
  include Angostura::Dependencies

  dependency :site_name
end

In my main app, in config/initializers/blog.rb, I added

Blog.setup do |config|
  config.site_name = "My site name"
end

Now, I can access site_name in my engine by calling Blog.site_name.

I'd like to point out that defaults are also supported, so you could do something like dependency site_name: 'Default site name' in lib/blog.rb. Furthermore, you can pass in whole classes as dependencies by sending it stringified classnames, like config.my_class = 'MyClass'.

For posterity, in order to use the gem, I added s.add_dependency "angostura", "0.6.0" in my engine's gemspec, and then ran bundle install.

G Klausner
  • 11
  • 2