Breaking a large rails app into smaller apps?
Dude, thats a pretty insane amount of models... anyways for handling complex logic and easily reuse them across other projects I would recommend to you the engines (from 2.3+ is part of Rails).
With that in place you can split your model in different modules (engines)
http://railscasts.com/episodes/149-rails-engines
Toño
What to do when a rails application become too big?
There are some great suggestions here about to to split up a Rails code base however I think that before you apply any of then you need to stop and seriously consider how you got into this position. All of these solutions introduce new complexities and challenges. They might be worthwhile trade offs but only if you make sure they also solve a problem you have today.
Take each of the pain points you listed (startup time is slow, memory use if high, performance is poor, rest performance is poor, development speed is slow) and run a "5 whys" exercise on them. Why are these things happening. Why did the app get into this state.
Most importantly before you commit to any plan for splitting up a large app consider if the app should be large in the first place. If your app is more complex than your product demands then switching to an equally complex cluster of services is not an improvement.
More concretely I would recommend against shared database access between apps/services/whatever. A shared database has a shared schema which becomes fragile. It also leads to tightly coupled services which lack the separation of concerns you need to see any improvement in your pace of development. Just as splitting one massive class into several tightly coupled files does not improve it nor does splitting one app into coupled services.
If you must maintain a large app you need to isolate separate concerns. In order to do so you need to break the dependencies between them. Based on the efforts I have seen to repair Rails monoliths you'll have better success creating clean interface within your existing app an then splitting out components than if you split the app apart and then hope the resulting pieces can be improved independently.
Ruby: How to properly split an app into multiple gems
Gems don't need the Gemfile.lock
file, since their dependencies are declared in the gemspec
.
To add a dependency use add_runtime_dependency
:
spec.add_runtime_dependency 'example', '~> 1.1', '>= 1.1.4'
But this is not specific enough for deployment scenarios!
Well, true, but for a gem deployment means it is deployed into an application. The application should have the Gemfile.lock
file to pin down the specific library versions.
An application deployment needs to be fully replicable, so build numbers of all library versions should be meticulously maintained. Gems, on the other hand, strive to be compatible to as many versions as possible to be re-usable, otherwise, one gem update will break all the gems which depend on it.
As for the rest of your question (whether and how to split your app into gems) - I believe this is not the right forum for that, as it is too broad for SO, but I wish you luck on your endeavor for making your software more manageable and more re-usable!
How to split routes.rb into smaller files
You can do that:
routes.rb
require 'application_routes'
require 'rest_api_routes'
lib/application_routes.rb
YourApplication::Application.routes.draw do
# Application related routes
end
lib/rest_api_routes.rb
YourApplication::Application.routes.draw do
# REST API related routes
end
UPDATE: (This method has since been removed from Rails)
Rails edge just got a great addition, multiple route files:
# config/routes.rb
draw :admin
# config/routes/admin.rb
namespace :admin do
resources :posts
end
This will come handy for breaking down complex route files in large apps.
Integrating multiple apps in Rails
You might consider breaking your apps into engines. It's a great way to isolate functionality and make code modular. Here's a link to the Rails engine docs: http://guides.rubyonrails.org/engines.html
If you need examples of real-world uses of engines you might consider looking at the code for Spree: https://github.com/spree/spree
With Spree you can add custom functionality by installing or building extensions, which are effectively Rails engines.
If you want to reference local gems/engines, you can point to them in your Gemfile like this:
gem 'mygem', :path => '/path/to/gem'
But make sure that the individual gems within your project don't have a .git folder, or you might run into errors regarding sub-modules.
Is it the Rails Way to build a single giant application, or modularize?
The convention is generally:
- Build as one application
- Have the classes know as little as possible about other classes
- Extract when it hurts
Extracting to either engines or to services (service-oriented-architecture) is a decision for your scalability requirements.
How to organize controller in moderately large Rails application?
For organizing within our applications, I have done a little bit of everything depending on the situation.
First, regarding the separate controllers for admin/user functions, I will say that you probably don't want to go that route. We used authorization and before_filter
to manage rights within the application. We rolled our own, but 20/20 hind-sight, we should have use CanCan. From there you can setup something like this (this is pseudo-code, actual language would depend on how you implemented authorization):
before_filter :can_edit_user, :only => [:new, :create, :edit, :update] #or :except => [:index, :show]
protected
def can_edit_user
redirect_to never_never_land_path unless current_user.has_rights?('edit_user')
end
Or at a higher level
before_filter :require_admin, :only [:new, :create]
and in your application controller
def require_admin
redirect_to never_never_land_path unless current_user.administrator?
end
It would depend which route, but I would use that for authorization instead of splitting up controllers.
As far as name spaces vs. Nested Resources, it depends on the situation. In several of our apps, we have both. We use name spaces when there is a cause for a logical separation or there will be shared functions between a group of controllers. Case and point for us is we put administrative functions within a namespace, and within we have users, roles and other proprietary admin functions.
map.namespace :admin do |admin|
admin.resources :users
admin.resources :roles
end
and then within those controllers we have a base controller, that stores our shared functions.
class Admin::Base < ApplicationController
before_filter :require_admin
end
class Admin::UsersController < Admin::Base
def new
....
end
This provides us logical separation of data and the ability to dry up our code a bit by sharing things like the before_filter
.
We use nested controllers if there is going to be a section of code where you want some things to persist between controllers. The case from our application is our customers. We search for and load a customer and then within that customer, they have orders, tickets, locations. Within that area we have the customer loaded while we look at the different tabs.
map.resources :customers do |customer|
customer.resources :tickets
customer.resources :orders
customer.resources :locations
end
and that gives us urls:
customers/:id
customers/:customer_id/orders/:id
customers/:customer_id/tickets/:id
Other advantages we have experienced from this is ease of setting up menu systems and tabs. These structures lend themselves well to an organized site.
I hope this helps!
Advice/tools for working on a large existing rails application?
Railroad should help you understand the big picture. It generates diagrams for your models and controllers.
Related Topics
How to Create a Rails 3 Route That Will Match All Requests and Direct to One Resource/Page
Restrict File Upload to Some File Extensions
How Does One Downgrade Jekyll to Work with Github Pages
Carrierwave: Create 1 Uploader for Multiple Types of Files
Rails Sitemap_Generator Uninitialized Constant
Convert Named Matches in Matchdata to Hash
E: Unable to Locate Package Heroku-Toolbelt
Before(:Each) for All Tests Except One
"The Ruby Way" (Mixins and Class Reopening) VS. Dependency Injection
Ruby: How to Join Elements of an Array Together with a Prefix
Update Rails to a Specific Version
How Does Rake Db::Migrate Actually Work
Rails 5, "Nil Is Not a Valid Asset Source"