Multiple incarnations of one Rails app - ruby-on-rails

What's the best way to organize multiple, slightly-varied incarnations of one Rails app?
The incarnations vary on css, images, database and domain. The app/logic is otherwise exactly the same. There is a chance public-facing path names of some routes will need to vary, but still the same route structure.
So I'm thinking of having one installation of the app and managing these changes in the environment.
DB is obviously already set up nicely for this. CSS and image paths I imagine would be easy enough to make environment-aware. Routes shouldn't be too difficult either I guess, in theory anyway.
The reason I'm writing this post is because I'm sure this problem must been solved. Is there an existing "skins" gem or something I can use for this? Otherwise, any comments on my planned approach would be greatly appreciated! I'm using Rails 3.

Why not just maintain separate branches in your revision control system?

A lot of it depends on why you're trying to achieve this. We currently serve four different 'apps' on a few different domains and subdomains from the same Rails Application. We do this because we have different clients that want to see different features of our product. For instance every product shares the idea of customers, contracts, merchants and many other models but they want to have the site branded differently, tab and menu items named differently and have some parts of the site inaccessible to certain people.
You've outlined CSS and images as your main concerns so I'll stick to those, however we've also had to employ ideas to remedy layouts, authorization and javascript amongst other issues and resources.
For simplicity sake I'll call a skin what in our case is a separate product.
Our app has a plugin that sets up some environment variables based on the incoming domain. Then based on these variables our Rails app will source different layouts and partials as appropriate. These layouts then reference the appropriate JS and images which are stored in locations that make sense for designers and frontend devs. For instance customer.js may store the usual customer operations, then in a sub directory there may be a customer.js for a skin and it will extend the main customer.js file (we use Dojo to do this).
In other areas where security is a concern we have libraries that do what is required based on the environment variables that were set by our domain gem.
We also have to provide dynamic branding so again these views and layouts have helpers with logic in them to determine where images should come from and so on.
For CSS we use SASS, so a lot of the time you can use mixins for colours and images. We do this by having a root SASS file with layout information that uses variables for colours and images. Then we'll require those colour files and image files into the root SASS as required.
The answer may be better if you give some information on how you are determining between these two skins. As far as I know there are domain plugins out there already that could help you determine which site the user is trying to access. As for how to organise your application to render the correct CSS, images, JS and so on hopefully what I've outlined helps a little.

Related

guidelines for where to put classes in Rails apps that don't fit anywhere

I'm wondering if there are any best practices about where to put non-standard Ruby files in Rails apps, those that don't fit in any of the default directories (controllers/models etc.).
I'm talking about classes that are used by controllers/models etc., but are not subclasses of any of the Rails base classes. Classes that include functionality extracted from models to make them less fat. Some of them kind of look like models but aren't AR models, some of them look more like "services", some are something in between or something else.
A few random examples:
"strategy" classes that handle authentication with password, via facebook etc.
"XParams" objects that encapsulate params or "XCreator" objects that handle processing of params to execute some complex action that results in creating some AR models in the end
classes that make requests to external APIs or encapsulate those requests and responses
fake models that can be substituted for a real AR model (e.g. guest user)
Resque jobs
classes that store and read information from Redis
classes that execute some specific actions like processing data, generating reports etc. and are called from Resque jobs or rake tasks
I've got quite a lot of these now, some of them are added to lib which ends up as a pile of random classes and modules, some sneak into app/models. I'd like to organize this somehow, but I don't know where to start.
Should only AR models go into app/models? Or is it ok to also put there any domain or helper models? How you decide if something is a model?
Should everything that doesn't fit into app go into lib? Or maybe I should add a few new custom subdirectories to app? What subdirectories, and how do I divide the custom classes?
How do you handle this in your projects? I know every project is a bit different, but there must be some similarities.
Good question - i don't have a concrete answer for you
but I recommend checking out this post
- http://blog.codeclimate.com/blog/2012/02/07/what-code-goes-in-the-lib-directory/
- be sure to read through all the comments
on a current project i have a ton of non-ActiveRecord objects under app/models, it works but not ideal
i put 're-useable' non application specific code under lib
other alternatives I have tried on side projects (say we have a bunch of command objects)
rails is a pain when it comes to namespaces under app, it loads everything up into the same namespace by default
app/
commands/
products/create_command.rb # Products::CreateCommand
products/update_price_command.rb # Products::UpdatePriceCommand
alternate, everything besides rails under src or an app_name directory
app/
src/
commands/
create_product.rb # Commands::CreateProduct
update_product_price.rb # Commands::UpdateProductPrice
I haven't come across a good solution for this, ideally the 2nd one is better, but would be nice to not have the additional directory under app, that way you open app and see controllers, commands, models etc...
You touch on a number of different use cases, and I think that this part is the closest to the "right" answer:
I've got quite a lot of these now, some of them are added to lib which ends up as a pile of random classes and modules, some sneak into app/models. I'd like to organize this somehow, but I don't know where to start.
That's pretty much right on in my book. The one thing you don't mention is extracting various pieces into separate gems. Classes that talk to external services are excellent candidates for extraction, as are strategy classes if they're sufficiently general. These can be private, since running your own gem server isn't hard, and you can then obviously reuse them across ROR apps.
Last and most concretely, resque jobs I stuff into lib/jobs.
My rule of thumb is if it's a model of some kind, it goes into app/models. If not, it probably belongs in lib or some appropriately named subdirectory thereof, e.g. lib/jobs, lib/extensions, lib/external, or the like.
If you're interested, I also wrote a follow-up article about this a bit later summing up what I found: http://blog.lunarlogic.io/2013/declutter-lib-directory/
I place any model classes (like STI subclasses) in apps/models. I place my other classes in lib, as it seems to be the best place to put them. It's easy for me to know where to look. It's also easier for me to group my tests since my model classes are all in one place.
Convention-wise I'm loathe to put helper classes in app/models. If they're presenter classes they belong in the app/helpers. If they're not then lib seems to be the best place for them.
Often my classes find their way into lib in subdirectories where modules with the same name as the subdirectory is responsible for including them. (Rails is very touchy about filenames and classnames when it comes to the autoloader.)
Another option is to encapsulate each module into its own gem and then refer to the gem via your Gemfile. This permits code sharing across projects.

Multisite application in Rails (like shopify.com)

I would like create web app like shopify.com.
User can pickup subdomain(or domain), theme and have own store.
How can I do this?
Create main application, deploy it automatically like new standalone version and update it via git?
I'm using Rails 3.
Thanks for your advice.
Based on replies:
When I choose to use only one application (without multiple instances) and give user his subdomain, it will looks like their own website. But everything will be in one database (It's good idea?). And how can I have multiple themes in Rails app?
Take a look at LocomotiveCMS, specifically the routing system. Locomotive actually hosts multiple sites inside a single rails application. It does this by inspecting the request URL when it comes in and setting the current_site variable with the site which is set up to handle the domain. Then the current_site is actually just an object which contains all the pages, contents, settings, etc. for the specific site being served up.
So to answer your question, I think a good solution is to give your rails app the ability to serve up multiple sites based on the domain. It's not that hard, and it seems less fragile to me than trying to automatically deploy new instances of an app.
So far I have understood, you want to let your users have their own subdomain, different theme but the functionality would be same right. Users just need to have a feel of something of their own.
Well definitely, you need to have a single application that supports multiple subdomains.
A quick googling gave me [ http://37signals.com/svn/posts/1512-how-to-do-basecamp-style-subdomains-in-rails ]. May be you can get some insights from here.
For example if your service is http://www.myfi.com, a brief idea can be:
When a customer is registering, you should let him choose his subdomain. And the newly created account will be associated with this subdomain with a url. Say, http://customer1.myfi.com.
You should register for domain *.myfi.com so that anyone in the world hit with anysubdomain.myfi.com, it comes in your application.
Then from the url part, you should identify the subdomain (customer1) that is being used, and need to set that in session.
Now when someone will try to login, you must verify the account in the context of that subdomain's account.
In fact, all following actions need to be handled in the context of the subdomain's account.
Just tried the gather a glimpse of the implementation here. If you have confusion about something specific, share that also.
Edit:
Whenever you are thinking about multiple theme, you must have simple design which is completely driven by css and js. The app/view files should contain only content and HTML nodes with class names or ids.
Generally a UI designer can put more helpful ideas about how to make such theming mechanism. But all I can feel is, based on the chosen theme by customer, you have to load different css and js.
Actually the strategies can be indefinitely sophisticated and scalable, but its always wise to start with something easy. Then ideas will automatically evolve into better ones.

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!

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.

Multi domain rails app. How to intelligently use MVC?

Background:
We have app a, b, and plan to add more apps into this same application. The apps are similar enough they could share many views, assets, and actions. Currently a,b live in a single rails app(2.3.10). c will be similar enough that it could also be in this rails app.
The problem:
As we continue to add more apps to this one app, there's going to be too much case logic that the app will soon become a nightmare to maintain. There will also be potential namespace issues. However, the apps are very similar in function and layout, it also makes sense to keep them in one app so that it's one app to maintain(since roughly 50% of site look/functionality will be shared).
What we are trying to do is keep this as clean as possible so it's easy for multiple teams to work on and easy to maintain.
Some things we've thought about/are trying:
Engines. Make each app an engine. This would let us base routes on the domain. It also allows us to pull out controllers, models and views for the specific app. This solution does not seem ideal as we won't be reusing the apps any time soon. And explicitly stating the host in the routes doesn't seem right.
Skinning/themes. The auth logic would be different between the apps. Each user model would be different. So it's not just a skinning problem.
In app/view add folder sitea for sitea views, siteb for siteb views and so on. Do the same for controllers and models. This is still pretty messy and since it didn't follow naming conventions, it did not work with rails so nicely and made much of the code messier.
Making another rails app. We just didn't want to maintain the same controller or view in 2 apps if they are identical.
What we want to do is make the app intelligently use a controller based on the host. So there would be a sessions controller for each app, and perhaps some parent session controller for shared logic(not needed now). In each of these session controllers, it handles authentication for that specific app. So if the domain is a.mysite.com, it would use session controller for app a and know to use app a's views,models,controllers. And if the domain is b.mysite, it would use the session controller for b. And there would be a user model for a and user model for b, which also would be determined by the domain.
Does anyone have any suggestions or experience with this situation? And ideally using rails 2.3.x as updating to rails 3 isn't an option right now.
Devise does exactly this. You would do well to check out its architecture and apply that architecture to your own case.
You will have multiple separate Rails applications. The shared code will be a separate project, perhaps distributed as a gem or at least a separate Git repository. The shared code will include many controller actions and many view templates that are there to be sensible defaults, and which will be overridden in some apps but not in others.
All the custom code for application A will belong in a project solely devoted to containing the custom code for application A. It will be its own fully-functioning Rails application and will depend heavily on the majority of the sensible defaults provided by the shared code in the shared-code project.
I've used the theme support plugin before and dynamically set the theme based on the request uri:
http://mattmccray.com/svn/rails/plugins/theme_support
It will probably need some work to support Rails 2.3.
Update: Looks like there's a rewrite: https://github.com/dasil003/rails-multisite
Sounds like you want to make the 'base' app a plugin and use that in each of your site apps. You can use something like svn-extern so it's automatically updated whenever something changes.

Resources