Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading gem at runtime in Rails 3

I have Rails 3.0.x application. I would like to load gems on runtime, without using Gemfile.

What I would like to accomplish is to load my application as usual, with regular gems being loaded by Bundler. After that I would like to load all gems (Rails Engines) located in a specific directory (but until runtime, I don't know what gems will that be).

Does anybody know if this is possible in Rails, maybe using Bundler API?

like image 821
maciej-rosiek Avatar asked Dec 13 '11 16:12

maciej-rosiek


People also ask

How do I use gems in Ruby on Rails?

The Rails gem is a great example of this. You run rails new PROJECT_NAME from the command line to generate a new rails project; then, you'll use it at other times to generate models, controllers, etc. Then, there are gems that you'll only use from inside projects of your own, like the Amazon S3 gem.

What is require RubyGems?

require 'rubygems' will adjust the Ruby loadpath allowing you to successfully require the gems you installed through rubygems, without getting a LoadError: no such file to load -- sinatra .


1 Answers

What you're trying to do is dangerous. If each of your Rails Engines are also gems - then they would also have Gemfiles with other dependencies, and those would in turn have other dependencies, etc. If you allow Bundler to resolve those, then you would have lesser problems at runtime.

Here's how you would do it without any hacks. Remember that your Gemfile is just Ruby code, and you can have gems which are not loaded by default.

# In your Gemfile, add at the end:
Dir[YOUR_RAILS_ENGINES_SUBFOLDER + "/*/*.gemspec"].each do |gemspec_file|
  dir_name = File.dirname(gemspec_file)
  gem_name = File.basename(gemspec_file, File.extname(gemspec_file))

  # sometimes "-" and "_" are used interchangeably in gems
  # for e.g. gemspec_file is "engines/my-engine/my_engine.gemspec"
  #   dir_name will be engines/my-engine
  #   gem_name will be my_engine

  # Register that engine as a dependency, *without* being required
  gem gem_name, :path => dir_name, :require => false

  # e.g. this is similar to saying
  #  gem 'my_engine', :path => 'engines/my-engine', :require => false
end

Now you have all your dynamic Rails engines registered as gem dependencies. Bundler will resolve them, and all their sub-dependencies, so you don't have to worry about anything. Just run bundle install once before running the application, or whenever you add/remove any engine in that folder.

The good thing is, these gems will just be registered, and not loaded. So in your production code, you can now load whatever gem that you choose at runtime simply by saying require <your-engine-name>

Edit: Extra code comments

like image 56
Subhas Avatar answered Sep 28 '22 10:09

Subhas