Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 3.1 Engines: Difference of my_engine.gemspec, add_dependency, add_development_dependency, and Gemfile

Just out of curiosity... in my previous post Rails3.1 engine: can't get SLIM or HAML to work in test/dummy app I asked where to tell Ruby to use some gem in my test/dummy application.

The (obvious?) answer was to just put it into the Gemfile of my Engine. This works, but it makes me a bit uncomfortable because in Yehuda Katz' Post Clarifying the Roles of the .gemspec and Gemfile he mentions that...

...when developing a gem, the Gemfile "a gem’s Gemfile should contain the Rubygems source and a single gemspec line".

On the other hand, within my Engine's Gemfile (which was generated using Rails' rails plugin new my_engine) there's:

# jquery-rails is used by the dummy application
gem "jquery-rails"

So this seems to be right. Update: no, it doesn't! Look at my answer below...

Still, somewhere else on StackOverflow I see the solution for this is said to simply require the needed gem in config/application.rb, while https://stackoverflow.com/questions/5159607/rails-engine-gems-dependencies-how-to-load-them-into-the-application it's told to be put best into lib/<your_engine>/engine.rb file.

And here's my thought: why doesn't the test/dummy app simply automatically require all the Gems specified in the .gemspec file? We even tell the gem, which gems to use for production and which for development mode by explicitly using add_dependency and add_development_dependency, so I don't see any reason why test/dummy doesn't do this.

So here's the final question: Where exactly do I have to tell Ruby to use a gem in my test/dummy app? I DON'T WANT TO FORCE RUBY TO USE THE GEM ALSO IN THE HOST APP.

like image 370
Joshua Muheim Avatar asked Sep 20 '12 09:09

Joshua Muheim


3 Answers

I think the proper way to proceed is the following:

An Engine is a normal gem. When you develop a gem, you put its dependencies in the gemspec file. If you use bundle, you, as a developer, can then create a .lock file with the specific versions you had no problems. But having that dependencies declared in the gemspec is not enough to use them, you have to require them in your gem code. When they are required, if the gem is used with bundle, the .lock versions are used.

In an Engine, as any other gem, it's the same. You define your dependencies in your gemspec file and you run bundle install but it is not enough to use them. You have to require them, for example, in lib/my_engine.rb.

For example:

# File: my_engine.rspec
# ...
s.add_dependency `slim_rails`, ' ~>1.0'

# ...

# File: lib/my_engine.rb
require "my_engine/engine"
require "slim-rails"

module MyEngine
end

I'm not sure why they are used without more troubles if set in the Gemfile, but as rails documentation says:

Gem dependencies inside an engine should be specified inside the .gemspec file at the root of the engine. The reason is that the engine may be installed as a gem. If dependencies were to be specified inside the Gemfile, these would not be recognized by a traditional gem install and so they would not be installed, causing the engine to malfunction.

like image 114
Waiting for Dev... Avatar answered Oct 03 '22 08:10

Waiting for Dev...


Here is what I found out so far.

Within an engine (created using rails plugin new my_engine) you have to specify the needed gems only in the my_engine.gemspec file, as they are then referenced from the test/dummy/Gemfile using gemspec.

Here's the generated test/dummy/Gemfile content:

source "http://rubygems.org"

# Declare your gem's dependencies in simple_view_helpers.gemspec.
# Bundler will treat runtime dependencies like base dependencies, and
# development dependencies will be added by default to the :development group.
gemspec

# jquery-rails is used by the dummy application
gem "jquery-rails"

# Declare any dependencies that are still in development here instead of in
# your gemspec. These might include edge Rails or gems from your path or
# Git. Remember to move these dependencies to your gemspec before releasing
# your gem to rubygems.org.

# To use debugger
# gem 'debugger'

What the line gem "jquery-rails" is doing here I really don't know, seems to completely contradict what's proposed in the comments. On the other hand, when I'm trying to use the SLIM gem (instead of ERB) in my test/dummy application, it seems I do have to specify it in the Gemfile, otherwise it won't work. Still a bit confusing, this stuff...

like image 44
Joshua Muheim Avatar answered Oct 03 '22 08:10

Joshua Muheim


TL;DR:

Don't use any Gemfile when developing an Engine. Put everything in your Engine's gemspec and let the engine itself require everything it needs.


why doesn't the test/dummy app simply automatically require all the Gems specified in the .gemspec file?

I guess applications don't automatically require their engine's dependencies for performance reasons.

So if the dummy app is supposed to be an authentic simulation of a real app using the engine, it shouldn't do that either.

Also, assume the dummy app were to automatically require all dependencies, including development dependencies. In that case, your tests might succeed even if one of your development dependencies was actually a runtime dependency. That would be really annoying.

it is said to simply require the needed gem in config/application.rb

This could also mask runtime dependencies of your engine, so better don't do that.

So here's the final question: Where exactly do I have to tell Ruby to use a gem in my test/dummy app? I don't want to force ruby to use the gem also in the host app.

If that gem is just needed for tests, require it in your test/spec helper.

Else, if that gem is needed for your engine to run, you really should enforce it's usage.

James: This would lead me to believe that the engine's dependencies go in the gemspec, while the dummy app's dependencies are put in the Gemfile

The dummy app's bundler stub actually references the dummy app's own, missing Gemfile. So I guess it is left out on purpose. And that makes perfect sense.

Think of it this way: anything you do to change the dummy app would lead to the tests not being performed against a neutral background, and thus not being representative.

If you really want to be able to reuse your engine, and to drop it into any blank, new Rails app, your dummy app should be just that: a blank, new Rails app.

like image 40
Janosch Avatar answered Oct 03 '22 08:10

Janosch