How can I modify Rails' config within an engine plugin? - ruby-on-rails

I'm working on an engine for Rails as a plugin. I'd like it to be able to make necessary changes to Rails' configuration when it's loaded so it can specify its Gem dependencies as well as add some load paths.
The plugin's init.rb file has access to the config object but this is effectively read-only, you can specify a gem but it makes no difference, the initializer must have run already at this point.
I've got around this for now by requiring a file with a new Rails::Initializer block like so:
Rails::Initializer.run do |config|
config.gem "authlogic", :version => ">= 2.0.9"
# etc
end
This works but wipes out any existing configuration in the main application's environment.rb.
Maybe I can solve this by having a generator in the engine that adds something to environment.rb that loads the plugin's config at the right stage, or maybe there is a way of adding a file to config/initializers to do this job. Not sure how best to go about this though.

I would go with the config/initializers route. That is the standard folder to put plugin-specific config code and it will get loaded at the correct time.
For implementation, I would try my hardest to choose sensible defaults for everything that allowed me to not have a config file. (I understand this is not always possible.)
Next I would create a generator with the plugin that would automatically create the config file in config/initializers using:
./script/generate plugin MyPlugin --with-generator
Finally, I would put something in install.rb of my plugin to run the generator script when the plugin is installed. This way the config file is generated automatically with the install, and the user still has an easy way to regenerate if he wants to restore the default configuration.

Are you sure you want to distribute this as a plugin rather than a gem? If you package your engine as a gem then you can specify gem dependencies as part of your gem build process. For example, if you use Jeweler to create your gem you just add a single line:
s.add_dependency 'authlogic'
When your gem is installed it will make sure all dependencies are installed. Google 'jeweler gem dependency' for a full Jeweler config example.
Also, I've been doing a lot of work on my own rails engine and recently extracted a lot of useful base functionality. You may find this helpful for other engine issues:
http://keithschacht.com/creating-a-rails-3-engine-plugin-gem/

You can easily add this line to init.rb (under your plugin directory)
config.gem 'quick_magick'
I tried it with rails 2.3.5 and it worked like magic.

Related

How to require a gem in .irbrc without needing to also add it to a Rails Gemfile?

I've added awesome_print to my ~/.irbrc file like so:
require 'ap'
Inside a Rails project directory, if I run irb it loads the gem fine, because I've already installed the gem locally. But if I run rails console, it spits out this error:
cannot load such file -- ap
How can I resolve this? I am guessing that it's looking for the gem in the app's Gemfile, but I don't want to add it to the Gemfile because I don't want other developers requiring that dependency. I only want to use awesome_print on my machine.
I am also using rbenv, if that is of any help.
There is this trick.
What you need to do is
# Copy the definition of the debundle! method into your ~/.irbrc
# Call 'debundle!' from IRB when you need to.
(as explained at the top of the file)
The text as it appears on the referred to site:
debundle.rb allows you to require gems that are not in your Gemfile when inspecting
programs that are run with Bundler.
Use at your own risk!
Paste the code of debundle.rb and you are done! A good place would be your .irbrc file
before requiring irbtools.
The code is directly taken from pry-debundle.
Please look there for any further information. This repo exists to simplify debundling
without using the pry repl.

how to set gem dependencies without having to declare them on the Gemfile that uses it?

I'm building a gem locally, suppose it name is "MyGem".
Now suppose that MyGem depends on other already built gem, for example "cancan".
So, i've added in my mygem.gemspec the line:
add_runtime_dependency("cancan")
Here is the problem: if I installl MyGem into a new project by adding it into my gemfile like this:
gem "mygem", :path => "path/to/my/local/gem"
then this new project is not being able to use cancan methods, and I have to explicity declare cancan on the new project gemfile in order to use it.
I tried also using gemspec method, but didn't solve my problem either.
Any ideas?
Update
I just wanted to add that when i only have myGem declared in my new application gemfile, after I run bundle install all the dependencies are installed.
That is, if i run gem list the "cancan" gem is displayed, but I still can't access it methods from the application level.
Thanks for the help.
Ok, i've solved this. I'm not sure if this is the best solution but it did work.
Making the application level developer to explicity add the dependencies in his gemfile didn't make any sense. So, as the gems did were being installed, i just required all the gem dependencies in my my_engine.rb file inside my gem.
Following the example, in my my_engine.rb I added the following line
require "cancan"
And that'it...
Even better you could do:
autoload :CanCan, "cancan"
So the module would be loaded only when it is called.
And even better than that, you could load only the file from cancan that you are using (maybe you don't need to load all of it).
You can add that line in you my_gem.rb file or your engine.rb file if you are using engines.
That worked for me, I hope this help someone.
Use add_dependency instead of add_runtime_dependency (this may help with cancan) then run bundle update on your new project.
Does the "cancan" gem name actually match the name to be require'd?
Example for yajl-ruby gem:
Gem::Specification.new do |s|
s.name = %q{yajl-ruby}
...
But when require'ing the library, you'd use a different string:
require 'yajl'
That means in your Gemfile, you have to explicitly require the dependency (which you said you wanted to avoid).
gem 'yajl-ruby', '>=1.0', :require => 'yajl'
To avoid needing to do this, and if you're the author of the "cancan" dependency, you should make the gem name matches the require name.
The only alternative I can think of is require'ing the dependency directly in one of your source files (like you did in your solution).

What is the difference between 'require' and 'config.gem' in a RoR app?

What is the difference between having require 'gem_name' in a controller and config.gem "gem_name" in environments.rb? I'm new to RoR, and am looking through an app and can't work out the difference. Thanks for reading.
"environments.rb" is a file that contains various configuration setting for your application, e.g. which gem the application needs to run correctly (mainly for portability). They have to be specified using config.gem "gem_name". This post about Gem Dependencies could help you.
With require "gem_name" you can explictly import a gem into your code in order to be able to use it´s classes.
config.gem in your environment.rb is needed to set up the correct rails environment. For example if you download a ruby app from github you can run rake gems:install from the application directory and all the correct versions of the required gems will be installed.
require in a controller is like import in vb.net and allows the classes in that gem to be used in your controller.

Rails: vendor/gems or vendor/plugins?

I'm to tracking dependencies with git-submodules in my rails app. So far I've added submodules for things like, haml, shoulda, and authlogic to 'vendor/plugins'. I've seen indications that I should be using 'vendor/gems' instead though.
Question: which directory is the appropriate place to put dependencies being tracked as submodules? Is the choice arbitrary?
Thanks
In short, I'd use config.gems for everything except for the exceptions I've noted below.
This is the workflow I'd use:
All plugins/libraries for which gems exist for the version I need, I do
environment.rb
config.gem "plugin_name", :version => ">=1.0.0"
then:
rake gems:unpack:dependencies
All other plugins that I install from source (Github, etc.) I run:
script/plugin install github_url
and the plugin is placed in vendor plugins.
As far as submodules go, I'd only use submodules for plugins/gems that I'm actively developing or for which I plan to use specific commits, rather than the HEAD, and if I plan to change the commit I'm using often.
The short version - if it's a plugin put it into vendor/plugins, if it's a gem it goes to vendor/gems. However, there's no need to track gem dependencies via submodules - Rails config is just good enough for that. So, the usual pattern is this: use config.gem for gem dependencies tracking and git-submodules for plugin dependencies - and plugins always go to vendor/plugins.

how to control gems vs plugins loading order in Rails

I have a plugin that must be loaded before resource_controller. The reason is that Resourcecontroller tries to load ApplicationController and it depends on the said plugin (and will fail to load if plugin's init.rb was not loaded yet).
The problem is that ResourceController comes from a gem and not a plugin.
Is there a way to load plugins before the gems (from environment.rb's "config.gem ...")?
There's no present way to load plugins before gems if you depend exclusively on config.gem to load them, but that doesn't mean that you can't take the loading of the resource_controller gem into your own hands.
As a very brutal solution, you can remove the relevant config.gem line, and then explicitly 'require' it at the bottom of environment.rb.
For rails2.3x in environment.rb set your gem to lib => false and then require the gem in the after_initialize block
config.gem 'some_gem', :lib => false
config.after_initialize do
require 'some_gem'
end
That'll do it.
a quick glance at the Initializer.rb code shows:
load_gems
load_plugins
# pick up any gems that plugins depend on
add_gem_load_paths
load_gems
check_gem_dependencies
If I understand it correctly, gems always comes before plugins... then some gems that the plugins require.
why not use resource_controller as a plugin, also?
just don't use the "config.gem ..." and put it in the plugins directory.

Resources