Rails best practice - Engines - ruby-on-rails

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.

Related

Could someone please define an Engine in Ruby in Rails?

In computing terms, an engine is something that is continually producing output based on input. But in Ruby, the term seems a little bit loose. The people who've created Refinery CMS have taken to calling gems that extend the functionality of their system, 'engines'.
Basically, what I want to know is, is Spree, the open source ecommerce cms written in ruby, an engine? Would calling it the 'spree engine' be correct?
As defined by Rails, an Engine is a lot like an application within an application. Spree is one of these, and there are others. Each engine has its own app folder with the same sort of structure you'd see in a top-level application.
You can combine one or more engines together into an application, then add your own functionality on top. That's what makes systems like Spree far more flexible than a fixed-puropose Rails app that you have limited control over.
Things that extend Rails are only truly engines if they are self-contained applications. Many gems add functionality that's much more specific than that, so are better termed "plug-ins" or "modules" depending on the phrasing.
It's actually pretty easy to build your own engine and can be useful for separating and re-using code across several different applications.
I think that there is quite good explanation in guide Getting Started with Engines.
Spree is actually comprised of many engines...
The overcooked version: Engines come kitted with (many of) the guts of a typical rails application, with a few bonuses: namespacing out of the box, generators for easily copying migrations, and the ability to mount it in another rails application.
From http://guides.rubyonrails.org/engines.html
1 What are engines?
Engines can be considered miniature applications that provide functionality to their host applications. A Rails application is actually just a "supercharged" engine, with the Rails::Application class inheriting a lot of its behavior from Rails::Engine.
Try to read this guide: http://guides.rubyonrails.org/engines.html and also this cast: http://railscasts.com/episodes/277-mountable-engines

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.

Rails: Re-using models, controllers and even views between Rails applications

I have a rails app which contains some fairly generic functionality (eg managing users). I would like to use that common functionality in other rails apps without copying it. The hard part seems to be that this code contains a number of models, controllers and views.
I know that gems and plug-ins allow code to be shared but they seem to apply more to sharing utility and library functionality rather than core parts of an app.
Any advice on how to do this would be greatly appreciated.
We are working with a Rails engine to share functionality between client projects and have gotten quite far with it. The engine contains controllers, models, views, and even routes. It provides core functions needed in each project (access to our in-house content management system) so projects don't have to start from scratch.
Most of the code has been structured in a way that it can be easily extended or overwritten where needed in the projects (mostly by subclassing). It's distributed as gem.
I can't show you code (it's not open-source), but I can point you to some helpful resources:
Rails 3 Plugins - Part 1 - The Big Picture
Rails 3 Plugins - Part 2 - Writing an Engine
Rails::Engine documentation
Plugin Authors: Toward a Better Future
Hope this helps!
Making copies is not always bad. That's why I ask about the reasons you don't want to copy the code.
If you create a 'library', you must ensure that every application will use it in the same manner. Or you have to prepare the 'library' for every possible difference between your applications which use it.
If you share the code, you are adding a dependency to your program. Any change in that shared code will affect more than one application.
Often it's much simpler to copy the code to another application, because then your may apply any modification without thinking about others.
Are you sure that managing users is so generic that you will not make any application-specific changes to it?
What about creating links to the files/directories that have the MVC? That is better than making copies.

Does a "vertical" framework for RoR make sense?

I have been spending some time creating what I called a framework.
It was aimed at the creation of quiz likes games. It was supposed to have two players syncronized and present them a question to solve, etc.
I would like it to be fully customizable so I tried to develop components that can be put in or out of the pages. In the end the elements became slim ruby methods to add a whole bunch of Javascript and CSS to the pages.
Still the Javascript needs to connect to Ruby so methods supporting it are created but they will only be present when the component is present. Some components depend on one another making everything overly complex.
And after this attempt I wonder, is there is not a better and easier way to make a framework aimed to one kind of application on RoR? Or is the concept flawed or RoR in some way flawed?
Ruby on Rails is a framework on its own accord and is "opinionated software". This means that the writers of Rails looked at what would make most sense for creating a web application to them. Many people support the original developers views and so use Rails for their projects as well.
I think your concept of creating a quiz is a good one, but first you need to understand the rails stack. Depending on what you need exactly, you can create either an engine, plugin or whatever.
What I have seen a lot is that you specify what you need in your controller. (How you do that is up to you). All that information is stored in a class variable and transferred to the view where you can render everything you need with some helpers. The hard part is making it all generic enough to be reusable.
But, maybe Rails isn't the right tool for you. Maybe you need something more lightweight like Merb or even Sinatra.
There is no 'flaw' in Rails. Rails is not the 10**1000-in-one tool Java is. It's a framework that tries to do one way very good in a particular way. I think Rails can be the right tool for you, but you need to be skilled enough to wield the tool :)

Resources