Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make gemspec dependencies autoload in a Rails 3 app, using a Gemfile

I have a Rails 3 app that I am turning into a Rails engine / gem. This engine has some gem dependencies that I have put inside it's .gemspec file.

I have created a new 'parent' Rails 3 app, and I would like to add my engine gem to the Gemfile and have the gem's dependencies automatically 'loaded', but this does not work for me! bundle install installs the gem dependencies fine, but when I start the server, the app crashes because they are not loaded.

For example, my engine's gemspec contains these lines:

s.add_runtime_dependency(%q<rails>, ["= 3.0.7"])
s.add_runtime_dependency(%q<acts_as_commentable>, [">= 3.0.1"])
s.add_runtime_dependency(%q<haml>, [">= 3.1.1"])

.. and the parent Rails 3 application has these lines in its Gemfile:

source 'http://rubygems.org'

gem 'my_engine', :path => "~/src/gems/my_engine"

But I get the following error:

undefined local variable or method `acts_as_commentable'
from /home/user/src/gems/my_engine/app/models/account.rb:66:in `<class:Account>'

But if I add gem 'acts_as_commentable', '>= 3.0.1' to the Gemfile of the parent Rails 3 app, then the gem is loaded and the error disappears.

I am using Rails 3.0.8.

Does anyone have any suggestions? Do I need to change something about the way my engine is loading?

like image 891
ndbroadbent Avatar asked Aug 05 '11 17:08

ndbroadbent


4 Answers

During main Rails app boot, Bundler will only require dependencies directly listed in the Gemfile but not any sub-dependencies. It's your library's/Engine's responsibility to require its dependencies when it itself gets required. You can do so using initializers in your Railtie.

class MyRailtie < Rails::Railtie
  initializer "require stuff" do
    require "stuff"
  end
end
like image 125
mislav Avatar answered Nov 04 '22 03:11

mislav


In our Rails Engine we used a small trick to require dependencies automatically. Unfortunately you can't specify whether or not they should load in the .gemspec, which would allow for greater control.

Gem.loaded_specs["our_rails_engine"].dependencies.each do |d|
  begin
    require d.name
  rescue LoadError => le
    # Put exceptions here.
    raise le if d.name !~ /factory_girl_rails/
  end
end
like image 22
Colin Curtin Avatar answered Nov 04 '22 03:11

Colin Curtin


I'm looking at Spree (the superhero of Rails Engines!), and they do this in spree_core-0.60.1/lib/spree_core.rb:

require "rails/all"

require 'state_machine'
require 'paperclip'
require 'stringex'
require 'will_paginate'
require 'nested_set'
require 'acts_as_list'
require 'resource_controller'
require 'active_merchant'
require "meta_search"
require "find_by_param"

So the answer is that within your gem, you have to require all of it's gem dependencies one by one. Well, that's how I will do it for now. But please comment if this ever changes in the future.

like image 44
ndbroadbent Avatar answered Nov 04 '22 03:11

ndbroadbent


Seems it don't work, i create a host project and a sub-project with rails 3 engine.

Added the gem to engine's gemspec

s.add_dependency 'simple_form'

then added the require to engine_name.rb like below

require 'simple_form'

But if delete the line [gem 'simple_form'] in host project's Gemfile, it will show undefined immediatly

like image 22
lanvige Avatar answered Nov 04 '22 01:11

lanvige