Ember Engines inside Rails Engines via ember-cli-rails - ruby-on-rails

We have a high modular rails5 app, which is splitted in rails engines over serveral repositories and integrated as ruby gems.
Now we want to introduce EmberJS by using ember-cli-rails. The main rails application contains the main ember application in the frontend directory while each of the rails engines contain an ember engine (via ember-engine) in a frontend directory.
How to mount the ember engines of the modules into the main ember engine?

Due the fact that I've found no other solution so far, I've created an initializer which symlinks the ember engine directories of all rails engines into the node_modules of the consuming ember engine in the consuming rails app:
# Get the node_modules dir
nm_dir = Rails.root.join('frontend', 'node_modules')
# Delete existing symlinks
Dir.new(nm_dir.to_s).each { |entry| FileUtils.rm_rf(entry) if entry =~ /^.+\-frontend$/ }
# MODULES contains an array of the rails engine gem names
MODULES.each do |module_name|
# Each module has a Manifest class, load that
manifest = load_manifest(module_name)
# Get the path to the frontend dir of the rails engine
source = Pathname.new(manifest.method(:setup).source_location[0].split('/lib/')[0]).join('frontend').to_s
# Symlink destination
destination = nm_dir.join("#{module_name}-frontend").to_s
# Symlink it
FileUtils.symlink source, destination, force: true
end
This approach is probably not very clean, but it seems to work.

Related

How to include a plugin/engine in a rails application?

This is my first time interaction with a rails plugin and I'm unable to wrap my head around how this thing works. I'm going to make a rails plugin/engine. I want to include this plugin in my rails application(a different project), how can I do it? I found this which suggests to
At the root of this brand new engine's directory lives a plugin_name.gemspec file. When you include the engine into an application later on, you will do so with this line in the Rails application's Gemfile:
gem 'plugin_name', path: 'engines/plugin_name'
My question is that how exactly do I do it? I mean when I create a rails application I do not find any engines directory in the folder structure. And also which directories from the plugin project do I have to place in the engines/ directory?
I created a plugin by doing
rails plugin new plugin_name --mountable
and it generated following files/directories:
|-app/
|-bin/
|-config/
|-demo_project.gemspec
|-Gemfile
|-Gemfile.lock
|-lib/
|-MIT-LICENSE
|-Rakefile
|-README.md
|-test
|-dummy
|-(contains a rails application)
Now once I'm done with this plugin, how do I include this in another rails application? What directories do I need to put in engines/ in addition to including the plugin in the gemfile?
In addition to this I also want some info about what the following is indicating
Additionally, the --mountable option tells the generator to mount the engine inside the dummy testing application located at test/dummy by adding the following to the dummy application's routes file at test/dummy/config/routes.rb:
mount PluginName::Engine => "/plugin_name"
What does the mount point indicate and what will be it's role once the plugin is included in a rails application?
Rails engine is an isolated rails app, that is mounted inside main app. Two main reasons to create an engine are isolation of an application part (with possible intent to split the app into pieces) and code reuse (when you use same engine in several apps)
Technically it is a ruby gem, but often engines start (and live) as an isolated part of a larger app, without being completely extracted into own repository, gem publishing etc.
For example, if you have admin part of your app, you can make it an engine:
# in gemfile:
gem 'admin', path: 'engines/admin'
# routes:
mount Admin::Engine => '/admin'
# engine's routes:
resources :foo # => because of being in an engine this will be /admin/foo
directory structure can look like:
|-app/
|-bin/
|-...
|-config/
|-application.rb
|-routes.rb
|-engines/
|-admin_engine/
|-app/
|-controllers/admin/
|- foo_controller.rb
|-config/
|-routes.rb # <- this is engine's routes
|-lib/
|-admin/
|-engine.rb
|- admin.gemspec

Use Rack::Static to mount a directory outside of the project directory

I'm building a Rails Engine that includes some assets built through Webpacker, and I'm following the Webpacker documentation on this.
I would like to distribute this engine as a gem, and to serve the engine's assets directly from the engine's gem, through a Rack::Static middleware in the host application.
This is what I'm trying:
# in the engine project: lib/my_engine/engine.rb
module MyEngine
class Engine < ::Rails::Engine
(...)
config.app_middleware.use(
Rack::Static,
urls: ['/myengine'], root: File.join(__dir__, 'public')
)
end
end
If I set the root: option for Rack::Static to a relative path (eg: my_engine/public) and then create a symlink in the host project to the relevant directory in the gem, this will work, however the code above, which generates an absolute path, does not work.
What could I do to fix this?
root: MyEngine::Engine.root.join("public")

Gem shared between Rails and Sinatra applications

I'm writing a gem of my own which is shared between a Sinatra app and a Rails app.
The gem generates a logger which will write to a default location - "/log/[logger_name]" in the project root directory.
My problem is that I have no way of computing the project root directory which will be common between both apps.
Is there a way to compute the path which the project is running from, regardless of if it's a Rails or Sinatra app? Is there an elegant way to solve it without using a regex?
Since both Rails and Sinatra are based on Rack, I would try to get the project root directory with Rack::Directory.new('').root and join it with logger file's relative path.
project_root = Rack::Directory.new('').root
logger_path = Pathname.new(project_root).join('log/production.log')

Ember CLI app inside a Rails engine

I'm making a Rails engine that has an /admin route. I'd like to create this admin interface with Ember CLI.
I know rails will automatically precompile any static JS/CSS that live in my engine's lib dir, and only load them when the parent application mounts my engine and visits that route. However, I'd like to use Ember CLI to build the admin interface.
What would be a good way to do this? Ideally I'd like to keep Ember CLI builds out of the repo.
My solution involved storing a build of the Ember CLI app in the engine.
I wrote a rake task that runs ember build and moves the static dist into the public/my-engine directory, and merges those public static assets with the host app's public folder.
Here's the task for our particular project:
namespace :admin do
task :build do
Dir.chdir('admin') do
sh 'ember build --environment=production'
end
# Copy the dist to public
FileUtils.rm_r 'public/front_end_builds'
FileUtils.mv 'admin/dist', 'public/front_end_builds'
# Move the index out of public
FileUtils.mv 'public/front_end_builds/index.html', 'app/views/front_end_builds/admin/index.html.erb'
end
end

Run engine RSpec/Cucumber tests in the client app

I have mountable rails engine Users:
users/
...
features/
...
specs/
dummy/
controllers/
models/
factories/
...
I have a client app MySite, which uses the Users engine:
my_site/
app/
...
features/
...
specs/
controllers/
models/
factories/
...
Gemfile
Gemfile:
gem "users", git: "..."
Is it possible to run engine rspec/cucumber tests in the MySite app instead of the mock?
If so, what is the best way?
You should not do this for two reasons:
The User engine is its own project. It should work in isolation in a certain way. Adding these tests to any project the User engine is included in does not make sense. Why not run all the specs of all the gems you include in your project as well?
If you have specific acceptance criteria for your application, add cucumbers for that to your project. That way, if you decide to ditch the User engine in the future with something else, you can still verify your application behaving as expected. If you'd included the cucumbers from the engine, then those criteria would have been lost as well.

Resources