Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a Rails engine's gem name have to "match" the module/namespace name?

I am building my first Rails engine. I called it my_engine, so it generates the files

lib/my_engine.rb
lib/my_engine/engine.rb
lib/my_engine/version.rb

That all have the module named MyEngine. And in the gemspec, the gem name is also set to my_engine.

If I create a model my_model, it goes into app/models/my_engine/my_model.rb, and is generated as

module MyEngine
  class MyModel < ActiveRecord::Base
  end
end

If I make a class method in here, and put the gem in a Rails project, it all works fine.

def self.hello
  "Hello from your Engine's model!"
end

$ bundle exec rails c
[1] (pry) main: 0> MyEngine::MyModel.hello
=> "Hello from your Engine's model!"

However, I do not want the gem name to be my_engine. But if I change the name in the gemspec to something else, like what-i-really-want-to-name-it, everything stops working. Rails cannot see my model, although it can see my namespace. I am indeed changing the gem name in the Rails app and re-bundling, so it's not an issue of that.

$ bundle exec rails c
[1] (pry) main: 0> MyEngine::VERSION
=> "0.0.1"   # default version from engine generation
[2] (pry) main: 0> MyEngine::MyModel.hello
NameError: uninitialized constant MyEngine::MyModel
from (pry):2:in `__pry__'

Why is "this" tied directly to the gem name? Is there any kind of workaround for this? I would really like to have the gem name and the module name to be different values.

Using: Rails 4.2.6, Ruby 2.3.0

like image 343
istrasci Avatar asked Oct 22 '25 16:10

istrasci


1 Answers

The answer is that one of Rails' foundational concepts is:

Convention over configuration.

When you decide to override the principle of Convention with Configuration, that's a Rails anti-pattern. Is it possible to to do it and be happy and have a working Rails app? Sure, but it's not code you would want to show as an example of your work on a Rails app.

So, the convention is that the module name matches the gem name. It's just a convention, but since convention is the mutually agreed law in Rails town, you're going anti-pattern when you don't follow it.

Added in response to OP comment

Rails engines are typified by the fact they use an isolated namespace and isolated resources. Gems don't, so in effect, the answer is yes, using a Rails engine does enforce a namespace convention that does not exist for a gem. And that namespacing is used by the middleware to keep the main_app separated from the engine at runtime. Two examples to illustrate:

An extreme example: you can have an app mounted as as engine on itself. Namespacing isolates one from the other so the middleware services act on the right processes which are only differentiated by the namespace.

Another example: 2 engines mounted on a main_app. Now there are essentially 3 apps running. How would you want to implement non-conventional namespace isolation in this case?

So... It is possible to hammer in a nonconforming namespace in a Rails engine? Probably. I've never tried. But your engine will not be portable. And worse, someone installing it will not be able to mount another engine that is conforming (and sharing the main_app and the middleware stack) because you've forced them into a configuration maze that breaks a conventional Rails engine. This last part is a theory.

like image 127
Elvn Avatar answered Oct 25 '25 19:10

Elvn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!