Using layouts from a mountable engine - ruby-on-rails

I am looking for a solution to the following problem:
db_core application contains only models for a small core system. The db_core application will not have any views.
On top of the db_core application we will have an admin interface. This will be a mountable engine. The admin interface will serve assets such as javascripts, css, images, etc.
Finally we will have additional mountable engines; eg. "blog", "forum", "authentication" that will be mounted on the db_core application, and all these mountable engines should have the same layout as the admin interface engine.
I have a test up and running where the db_core application serves assets, but I have not been able to find out how to let another engine serve the assets so the db_core application can remain small and free of any assets and views.
db_core
Engine A -> admin interface (assets)
Engine B, C, D, ... -> various other engines using assets from Engine A

I think you really mean an isolated engine. See "isolated" at Engine rdoc You CAN write an engine that is mountable but not isolated -- essentially meaning it's got it's own routing, but is otherwise the same as a plain old school non-isolated engine.
But I've stopped writing my engines as isolated for just these sorts of reasons. I rarely want an engine that is actually complete isolated from the host app. There's usually view templates (if not layouts specifically) that I want the host app to have access to (and be able to over-ride with their own local template, by just naming it the same but being earlier in the load path being in the host app), and/or helper methods that I want the host app to have access to and be able to over-ride on a method-by-method basis, and/or assets i want avail in the host app, etc. Yes, you can write a generator to copy them over, but that 'freezes' them in the host app, and can be a forwards-compatibility issue.
So, personally, I find isolated engines to cause more problems than they solve. I just use an 'ordinary' engine, and DIY isolate the parts that need isolating in the right way (module namespace controllers, for instance).
Same for routing/mountable. I don't use Rails 'mountable' engines, but I also don't have my engines automatically add routes to the host app by having their own engine/config/routes.rb that gets automatically loaded. Instead, I have the (non-mountable) engine provide a method that the host app can put in it's own routes.rb (with the routing object passed as an arg), that when called will add engine-needed routing to the app. The method can take arguments to customize things (only add some routes but not others, use custom routing namespaces, whatever).
I think Rails 3 gives you all the building blocks you need with a 'plain' engine to make it integrate with the app in exactly the ways you want. The higher level abstraction of mountable/isolated, which fixes a bunch of these choices for you, results in something that's fit only for certain use cases. If you really do want an engine that's a completely isolated subsystem, sure. If you need to write an engine that works with any Rack app not just Rails, then you essentially need to run it as a completely isolated sub-system (this is how devise ends up where it is), but you are giving up a lot to do that.
But I've come to believe it's not in fact the future of "the best way to do engines from now on, generally".
For what you're trying to do, I think making your engines isolated is going to be counter-productive. Make them not isolated. But then there will be some stuff to figure out about how to make your engine behave with just the kinds of isolating and routing you want (and not the kinds you don't want), it's true -- this stuff is sadly not documented great.

Related

Rails best practice - Engines

I'm currently upgrading an app to rails 4. I planned to install this app for few persons but they have different needs. I decide to use this upgrading version time to put some of my models in engines. The objective is to spend less time tweaking the app for the needs of the situation. The core app will manage the basic resources and the engines will add features (The app is managing membership for small organizations).
I read a lot documentation including the "guides". I tested some engines to see the behaviour of the app. Here are my questions I couldn't answer by my searches:
1 - Naming convention:
How do you name your engines? My first attempt was to name it by their function but when I generated my first model I saw that I can't use the function name for my model.
I was think about something like: Coreappname_functionality
for exemple I want to add activities for my members the engine will be named : member_activities
2 - full vs mountable
I read a lot about this subject, lot of people seem to use mountable engines. I try both of them and I think that the full option is really fast to implement (no routing, no namespace isolation that I have to be aware of). But I also understand the risk for the class collisions. If I'm the only one writing code for this app is it a bad habit to use a full engine(it's just a laziness question). Are there others advantages of mountable engine even if I'm not planning to use them in another app?
3 - "if engine exists?"
Inside the core app i'll put the code that all the engines need. For example inside the side bar I want to display the list of the last activities but only if the activities engine is used. The objective is to put all the necessary code inside the core app but use this code depending on the present of engines or not.
During my test time I used:
if defined? Activity
#activities = Activity.all
end
and render it the views something like this:
<% if defined? Activity %>
<h3><%= #activities.first.title %></h3>
<% end %>
It's working well but I'm not sure that it is a good practice. Is there an alternative?
Do you have any advices before I jump in the engine's world?
I prefer posting my questions before instead of posting my errors after the attempt!
For anybody reading that...
I think there is a misunderstanding about what an isolated engine is. In fact it is a bit confusing. Some may think that's choosing between isolated and not isolated is a bit like a matter of preference. But that's not entirely true.
Isolated engine is just that. An isolated engine. It's not suitable for a "module" of your application. It's more like a "subapplication" of you application. The difference is critical. Modules of your application probably will share models, api or some business logic. Still, encapsulation of responsibilities is important, but there will be some connections. For example probably almost all of your modules will be using something like EventDispatcher module. While isolated engine is a whole application on it's own. Although it could use models from the host application it can't use models from different isolated engine (at least without hacks, a lot of pain in 'certain situations' and that's generally a bad idea causing bad design).
So if you try to implement your app modules using isolated engines your probably end up storing all your models in main/host application to be reusable between your - in fact - subapplications. Probably the same with views. And maybe with assets too. And if some business logic have to be shared it probably would end up in main application too. So that's basically defeats the whole purpose. That's why you should use not isolated engines for your modules. While isolated engines are good for a - totally encapsulated - subapplication. For example if you want to have an e-commerce shop alongside with your main application (spree gem does exactly that).
A more enterprise example would be an ERP system. It could have subapplications like: CRM, Resource Management, etc (isolated engines). And those subapplications could have their own modules (not isolated engines).
Just for the completeness of my answer. As a isolated engine i mean that generated using rails plugin new engine_name --mountable, while a not isolated would be generated using rails plugin new engine_name --full.
Disclaimer:
Maybe there are some cases when you would want to go different way (probably when implementing some kind of magic gem doing some magic things), but that's up to you to figure it out. By this answer I just mean it should be applicable in most applications.

Rails Engines: engine views or partials as reusable widgets

Our team is starting to use a lot of engines, and we would like these to display "widgets" when the user logs in the main or host app. We already use the public activity gem to some extent.
Essentially, what we need is to for the host app to loop through each mounted engine, and then if the engine has "widgets", the host app should render them. Ideally, the engine should tell the app how to render the views.
I found the Cells gem to be promising, but it requires the host app to know exactly what cells to render. (I think one quick way around this is for the engine to tell the app the path.)
What MVC, Rails, etc. pattern(s) should I look into?

Nested Rails Engines

Is it possible to embed (nest) Rails engines inside of other Rails engines?
I am working on a Rails 3.1 app that I want to split up into a couple different sections and turn each section into a Rails engines. However, I also want to be able to turn the entire app into a Rails engine.
Possible, but not easy.
http://confreaks.com/videos/863-railsconf2012-railes-engines-patterns
Think what benefits you get from nested engines. Nesting means coupling and if you want to improve maintainability and testability you should reduce that coupling.
I think the ideal engine should be easily able to run standalone - this is why you have dummy app created in test directory. Sure you can keep nesting or keep common stuff in main app but you will see that with growing code base it is harder to maintain tests with tightly coupled dependencies.
As a solution I would try composition instead. Put engine directories next to each other but use nested modules:
engines/foo_project - namespaced FooProject::*
engines/foo_project_user - namespaced FooProjectUser::* or FooProject::User::*
If there is something which FooProject::User needs from FooProject then ideally it shoud be extracted into another engine like FooProject::Layout. In another direction the main app should wire User stuff into FooProject. Add integration tests on the main app level and you get ordnung.

Spree as a mountable engine

So, we want to rearchitect a portion of our site as a Rails app. The original plan was to have a main "site" app, with a number of plugin apps (Rails 3.1 Engines) with compartmentalized functionality -- a store component, a social/forums/chat component, etc. Also, we wanted to put themes/styling in a gem so that our web designers could modify the site appearance and some minor layout tweaks without having to "know Rails." Initially, this was going well; we created the main architecture and plugins and the theme gem, and it was all playing nicely together; cross-cutting functionality like auth was put in the main "site" app and was consumable by the plugin apps, giving us a single sign-in for the site (a design requirement).
Our initial plan for the store component was to use the Spree (http://spreecommerce.com/) since it had, out of the box, 95% plus of the functionality we needed. However, there's a catch -- Spree is distributed as a mountable engine, but it's also an app. Meaning that not only does Spree mount inside an app (which is not a problem, in fact it's behavior we were counting on), but it depends upon being in control of the main app. Looking into the "why" for this behavior, it seems to depend upon two core pieces of functionality. The first chunk of functionality is some SEO permalink rewrite functionality that has to go into middleware; we could hack things so that our main app included this chunk of code (even though this would begin to entangle store functionality across our entire site, muddying the "Spree as a mountable engine" story... more on that in a moment).
More complicated is Spree's use of Deface to do theming and customization. While this is "clever" (note quotes), it really makes the integration of Spree kind of a nightmare; either you follow the path of least resistance and make Spree an entire store to itself (which completely breaks our story of "the store is just one part of our site, and plays nice with the rest of the site, including auth, theming, etc."), or you have to hack the hell out of Spree.
Not only that, but Spree doesn't follow the standard Rails Engine routing paradigm, where routes are isolated beneath an engine root (if you look in the lib's routes.rb file, you can see that it uses Rails.Application's routes, instead of an Engine's routes). This means that we couldn't have www.oursite.com/store/...all_the_spree_goodness, we'd have to have www.oursite.com/spree_owns_the_sites_routes...
So, has anyone else tried this? We LOVE Spree and would like to use it as our store. But it's starting to look that there's no real way to "integrate" it with the rest of our site aside from maybe some proxying magic with nginx or something like that (which is a separate nightmare, since we're hoping to host on Heroku, and then we have to figure out how to integrate multiple disparate apps into one DB -- for single sign-in auth -- and an HTTP front router).
Spree devs, we LOVE your code, but is there any work being done to make it an actual, for-real Rails Engine, as opposed to a stand-alone app that just happens to package all of its features into Engines? Without the ability to integrate it into an existing site (including not "owning" the app, being able to have all of its routes partitioned off, and so on), there's just no way we can use it :(
TIA.
I'm the Community Manager for Spree, so I think I may be able to answer your question.
Yes, there is work going on that will allow Spree to be a true Rails engine. In fact, that was my first task that I did when I was hired by Spree. The work is on the master branch (https://github.com/spree/spree) and we're looking to release this code as a 1.0.0.rc around Christmas time.
With this code, a couple of changes have been made. For starters, Spree is now a proper Rails engine meaning that you can now have it mounted at /spree or /shop or /whatever and Spree's cool with that. Secondly, all the models and other classes are namespaced so they won't conflict with anything in your application.
I'm not sure what you mean about Deface being "clever", though. What problems do you forsee with this? If you want to override an entire view you could do this by overriding the path in app/views/spree/products/show.html.erb. Mind you, this overrides the whole view, and if you only want to override a part of it that's when you'd use Deface.
Could you perhaps elaborate on the issues you're having with Deface? Would be interested to help you sort them out.
Thanks for using Spree!

What are practical uses of mountable engines?

Just finished watching "Railscast 277" on mountable engines in Rails, what are practical uses of mountable engines?
One of the best examples would be Devise (for authentication) which is an engine (plugin) but its mounted as a shared-engine; you also get namespaced engines and this helps to retain a sense of context.
Another example would be the Active Admin add-on, error notification plugins...and a whole lot more. In terms of 'mounting' from a purely routing point of view, you can see how — with a namespaced engine you can serve a completely isolated rails app from a route from within your app itself.
You should consider reading this as well.
Two major uses:
Reusability. Gems that expose a lot of application functionality are often distributed as engines.
Modularity. Particularly for large applications, there are often parts of the application that don't seem like they want to be mixed in with the main application, yet are too closely related to be extracted to entirely separate applications. Engines can work well for this.
Spree takes this to extremes: the core Spree framework is the main Rails application, and to customize it, you write engines. This means you're never modifying the Spree core directly.

Resources