Proper way to specify the Rails::Engine for dependency gems? - ruby-on-rails

I'm building a namespaced Rails Engine gem which will be extendable through additional gems.
For example: MyEngine is the main gem which will also be the namespace.
MyEngine-blog & MyEngine-support are optional gems to extend the MyEngine gem while inheriting the MyEngine namespace.
MyEngine-blog & MyEngine-support are dependent on MyEngine for core functionality and both gems will include spec.add_dependency "synculus" in their gemspec files.
What is the proper way to specify the Rails::Engine for the dependency gems?
Do I have to specify class Engine < ::Rails::Engine in each of the MyEngine-blog & MyEngine-support gem's engine.rb files?
# lib/myengine/engine.rb
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
end
end
# lib/myengine/blog/engine.rb
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
module Blog
end
end
end
# lib/myengine/support/engine.rb
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
module Support
end
end
end

Take a look at the way spree commerce is doing this with their 2.x release, they have a really great grasp on rails engines.
Spree Commerce 2.x engine.rb

Related

Add methods to a Rails engine model from a Rails plugin

I'm writing a Rails plugin to extend a Rails engine. Namely MyPlugin has MyEngine as a dependency.
On my Rails engine I have a MyEngine::Foo model.
I'd like to add new methods to this model so I created a file in my plugin app/models/my_engine/foo.rb which has the following code:
module MyEngine
class Foo
def sayhi
puts "hi"
end
end
end
If I enter the Rails console on the plugin dummy application I can find MyEngine::Foo, but runnning MyEngine::Foo.new.sayhi returns
NoMethodError: undefined method `sayhi'
Why MyPlugin cannot see the updates to MyEngine::Foo model? Where am I wrong?
Ok, found out. To make MyPlugin aware and able to modify MyEngine models the engine must be required on the plugin engine.rb like so:
require "MyEngine"
module MyPlugin
class Engine < ::Rails::Engine
isolate_namespace MyPlugin
# You can also inherit the ApplicationController from MyEngine
config.parent_controller = 'MyEngine::ApplicationController'
end
end
In order to extend MyEngine::Foo model I then had to create a file lib/my_engine/foo_extension.rb:
require 'active_support/concern'
module FooExtension
extend ActiveSupport::Concern
def sayhi
puts "Hi!"
end
class_methods do
def sayhello
puts "Hello!"
end
end
end
::MyEngine::Foo(:include, FooExtension)
And require it in config/initializers/my_engine_extensions.rb
require 'my_engine/foo_extension'
Now from MyPlugin I can:
MyEngine::Foo.new.sayhi
=> "Hi!"
MyEngine::Foo.sayhello
=> "Hello!"
See ActiveSupport Concern documentation for more details.

how to namespace in rails engine

I trying create a rails engine
Content default
lib/abc_xyz.rb
require 'abc_xyz/engine'
module AbcXyz
end
abc_xyz/engine.rb
module AbcXyz
class Engine < ::Rails::Engine
isolate_namespace AbcXyz
end
end
But, i want nested it in a namespace, as below:
lib/abc_xyz.rb
require 'abc/xyz/engine'
module Abc
module Xyz
end
end
abc/xyz/engine.rb
module Abc
module Xyz
class Engine < ::Rails::Engine
isolate_namespace Abc
end
end
end
but, rake abc:install:migrations not working for me
. How to do it?

What does "::" in front of "::Rails::Engine" Mean [duplicate]

This question already has answers here:
Ruby's double colon (::) operator usage differences
(2 answers)
Closed 7 years ago.
I am looking at the Rails Engine tutorial and one of the code blocks looks like the following:
module Blorgh
class Engine < ::Rails::Engine
isolate_namespace Blorgh
end
end
What does the ::Rails::Engine mean? I know this is probably a trivial Ruby question, however, I don't seem to be able to find anything anywhere.
Thanks.
So, Ray has this exactly right. I would just like to add a further example.
Let's say we have an engine called Foo (original, right?) that is mounted in a host application called Bar with something like:
#bar/config/routes.rb
Rails.application.routes.draw do
...
mount Foo::Engine, at: '/'
...
end
Foo has an application_controller:
#foo/app/controllers/foo/application_controller.rb
module Foo
class ApplicationController < ActionController::Base
...
def foo_action
...
end
...
end
end
And Bar has an application_controller:
#bar/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
...
def bar_action
...
end
...
end
Now, let's say we have two controllers within the Foo engine, like:
#foo/app/controllers/foo/baz_controller.rb
module Foo
def BazController < ApplicationController
...
end
end
and
#foo/app/controllers/foo/bif_controller.rb
module Foo
def BifController < ::ApplicationController
...
end
end
The BazController inherits from ApplicationController (no :: in front). This means that it looks up ApplicationController in the current namespace (Foo). And so it will have the foo_action defined in Foo::ApplicationController.
The BifController inherits from ::ApplicationController (:: in front). This means that it looks up ApplicationController in the global namespace which, in this case, is the host application Bar. And so it will have the bar_action defined in Bar's ApplicationController.
The :: at the front of ::Rails::Engine means to lookup Rails::Engine at the top of the global namespace, not inside the Blorgh namespace.
Without contrast, without the :: as in this code,
module Blorgh
class Engine < Rails::Engine
isolate_namespace Blorgh
end
end
the second line would be looking for Blorgh::Rails and you would get an error, NameError: uninitialized constant Blorgh::Rails.
Think of all the Modules and Classes hierarchically: it is looking for "Rails" at the top-level with "Engine" at the next level.
e.g.:
module Rails
class Engine
end
end
module SomethingElse
class Rails
end
end
::Rails selects the Module in the first code snippet, which is on the top-level.
::Rails::Engine selects the Class Engine which is in the top-level Rails Module.

Monkey patch ActiveAdmin class from engine

I have an engine that will be installed over an application that uses Active Admin...
After install Active Admin, I need to run my engine installer. This will create a file monkey patching an Active Admin class.
The file looks like this...
module ActiveAdmin
module Devise
# things I need to add...
end
end
So, the question is: where I need to put this file and why?
I used the Railtie's initializer method.
my_engine/lib/admin_invitable/engine.rb
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
initializer "ativeadmin_invitable_patch" do |app|
require_relative "activeadmin_invitable_patch"
end
end
end
my_engine/lib/admin_invitable/activeadmin_invitable_patch.rb
module ActiveAdmin
module Devise
# bla bla
end
end

Nesting modules inside of a Rails eninge gem

What is the proper syntax to nest child modules within a parent module that is being is an isolate_namespace Rails engine gem?
# lib/myengine/engine.rb
module MyEngine
class Engine < Rails::Engine
isolate_namespace Myengine
# def ...
end
end
For example. The parent module is MyEngine and the child module is Blog. MyEngine will share common domain, like CRUD, Taggable, Searchable, etc, which will keep the gem code DRY and isolated from the main app (MyApp), while inheriting the isolated namespace and Engine.
Are either of the two approaches correct? Any refactor advice?
# A
# lib/myengine/blog.rb
module MyEngine
module Blog
# def ...
end
end
# B
# lib/myengine/blog.rb
module MyEngine
class Engine < Rails::Engine
isolate_namespace Myengine
module Blog
# def ...
end
end
end
Option A. is correct, but it should be lib/my_engine/blog.rb. You can read more about Ruby & Rails naming conventions here.
Further, if you want to put more modules or classes under the blog namespace you put them in the folder lib/my_engine/blog and nest them under MyEngine::Blog.

Resources