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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With