I'm really enjoying Rails (even though I'm generally RESTless), and I enjoy Ruby being very OO. Still, the tendency to make huge ActiveRecord subclasses and huge controllers is quite natural (even if you do use a controller per resource). If you were to create deeper object worlds, where would you put the classes (and modules, I suppose)? I'm asking about views (in the Helpers themselves?), controllers and models.
Lib is okay, and I've found some solutions to get it to reload in a dev environment, but I'd like to know if there's a better way to do this stuff. I'm really just concerned about classes growing too large. Also, what about Engines and how do they fit in?
Because Rails provides structure in terms of MVC, it's natural to end up using only the model, view, and controller containers that are provided for you. The typical idiom for beginners (and even some intermediate programmers) is to cram all logic in the app into the model (database class), controller, or view.
At some point, someone points out the "fat-model, skinny-controller" paradigm, and intermediate developers hastily excise everything from their controllers and throw it into the model, which starts to become a new trash can for application logic.
Skinny controllers are, in fact, a good idea, but the corollary--putting everything in the model, isn't really the best plan.
In Ruby, you have a couple of good options for making things more modular. A fairly popular answer is to just use modules (usually stashed in lib) that hold groups of methods, and then include the modules into the appropriate classes. This helps in cases where you have categories of functionality that you wish to reuse in multiple classes, but where the functionality is still notionally attached to the classes.
Remember, when you include a module into a class, the methods become instance methods of the class, so you still end up with a class containing a ton of methods, they're just organized nicely into multiple files.
This solution can work well in some cases--in other cases, you're going to want to think about using classes in your code that are not models, views or controllers.
A good way to think about it is the "single responsibility principle," which says that a class should be responsible for a single (or small number) of things. Your models are responsible for persisting data from your application to the database. Your controllers are responsible for receiving a request and returning a viable response.
If you have concepts that don't fit neatly into those boxes (persistence, request/response management), you probably want to think about how you would model the idea in question. You can store non-model classes in app/classes, or anywhere else, and add that directory to your load path by doing:
config.load_paths << File.join(Rails.root, "app", "classes")
If you're using passenger or JRuby, you probably also want to add your path to the eager load paths:
config.eager_load_paths << File.join(Rails.root, "app", "classes")
The bottom-line is that once you get to a point in Rails where you find yourself asking this question, it's time to beef up your Ruby chops and start modeling classes that aren't just the MVC classes that Rails gives you by default.
Update: This answer applies to Rails 2.x and higher.
Update: The use of Concerns have been confirmed as the new default in Rails 4.
It really depends on the nature of the module itself.
I usually place controller/model extensions in a /concerns folder within app.
# concerns/authentication.rb
module Authentication
...
end
# controllers/application_controller.rb
class ApplicationController
include Authentication
end
# concerns/configurable.rb
module Configurable
...
end
class Model
include Indexable
end
# controllers/foo_controller.rb
class FooController < ApplicationController
include Indexable
end
# controllers/bar_controller.rb
class BarController < ApplicationController
include Indexable
end
/lib is my preferred choice for general purpose libraries. I always have a project namespace in lib where I put all application-specific libraries.
/lib/myapp.rb
module MyApp
VERSION = ...
end
/lib/myapp/CacheKey.rb
/lib/myapp/somecustomlib.rb
Ruby/Rails core extensions usually take place in config initializers so that libraries are only loaded once on Rails boostrap.
/config/initializer/config.rb
/config/initializer/core_ext/string.rb
/config/initializer/core_ext/array.rb
For reusable code fragments, I often create (micro)plugins so that I can reuse them in other projects.
Helper files usually holds helper methods and sometimes classes when the object is intended to be used by helpers (for instance Form Builders).
This is a really general overview. Please provide more details about specific examples if you want to get more customized suggestions. :)
... the tendency to make huge
ActiveRecord subclasses and huge
controllers is quite natural ...
"huge" is a worrisome word... ;-)
How are your controllers becoming huge? That's something you should look at: ideally, controllers should be thin. Picking a rule-of-thumb out of thin air, I'd suggest that if you regularly have more than, say, 5 or 6 lines of code per controller method (action), then your controllers are probably too fat. Is there duplication that could move into a helper function or a filter? Is there business logic that could be pushed down into the models?
How do your models get to be huge? Should you be looking at ways to reduce the number of responsibilities in each class? Are there any common behaviours you can extract into mixins? Or areas of functionality you can delegate to helper classes?
EDIT: Trying to expand a bit, hopefully not distorting anything too badly...
Helpers: live in app/helpers and are mostly used to make views simpler. They're either controller-specific (also available to all views for that controller) or generally available (module ApplicationHelper in application_helper.rb).
Filters: Say you have the same line of code in several actions (quite often, retrieval of an object using params[:id] or similar). That duplication can be abstracted first to a separate method and then out of the actions entirely by declaring a filter in the class definition, such as before_filter :get_object. See Section 6 in the ActionController Rails Guide Let declarative programming be your friend.
Refactoring models is a bit more of a religious thing. Disciples of Uncle Bob will suggest, for example, that you follow the Five Commandments of SOLID. Joel & Jeff may recommend a more, er, "pragmatic" approach, although they did appear to be a little more reconciled subsequently. Finding one or more methods within a class that operate on a clearly-defined subset of its attributes is one way to try identifying classes that might be refactored out of your ActiveRecord-derived model.
Rails models don't have to be subclasses of ActiveRecord::Base, by the way. Or to put it another way, a model doesn't have to be an analogue of a table, or even related to anything stored at all. Even better, as long as you name your file in app/models according to Rails' conventions (call #underscore on the class name to find out what Rails will look for), Rails will find it without any requires being necessary.
Here's an excellent blog post about refactoring the fat models that seem to arise from the "thin controller" philosphy:
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
Basic message is "Don’t Extract Mixins from Fat Models", use service classes instead, the author provides 7 patterns to do so
Related
I am a newbie in Ruby on Rails. Coming from a C# and Java background, Ruby on Rails seems bizzare, yet interesting at the same time. It's almost like coming from a class-based object-oriented world to the prototyping concept of JavaScript, or even to a functional language.
Anyways, in a traditional C# or Java MVC application, I tend to keep my models and controllers as clean as possible, extracting the business logic into service classes. My models are just POCOs/POJOs (with some calculated fields and validation at most). And my controllers just process incoming requests (relying heavily on dependency injection) and then return a view or JSON.
Yet, I do not see any clear pattern in the RoR world. Some people tend to put all their business logic into controllers, some put it into models (with ActiveRecords, it kind of makes sense, although I don't like it).
And then there is the concept of Concerns. Are they the right place to extract my business logic, instead of using services? If yes, could you include an example of proper Concers use? I still struggle with the concept of modules (are they more of namespaces, or rather interfaces)? As said at the beginning, Ruby seems like a whole new galaxy to me.
This question could get into the weeds a bit as it brings in a lot of personal preferences. However here is my take on it.
First, Concerns are not a replacement for Service classes. Concerns are a clean and nifty way to manage your mix-ins. If you are new to Ruby, mix-ins are basically a way of injecting instance and/or class methods into existing classes. For example, given these classes:
class EvilRobot < ActiveRecord::Base
def destroy(target)
...
end
end
class OrneryTeenAger < ActiveRecord::Base
def destroy(target)
...
end
end
you could dry out the code with:
require 'active_support/concern'
module EvilTools
extend ActiveSupport::Concern
included do
def destroy(target)
...
end
end
end
class EvilRobot < ActiveRecord::Base
include EvilTools
end
class OrneryTeenAger < ActiveRecord::Base
include EvilTools
end
I think that the vast majority of Rails developers, myself included, go for a fat-model, thin controller design. Just how fat though is a matter of taste. I also tend to move functionality to classes under lib if they don't fit logically within a model, or extract into an engine or gem.
I would say jpgeek reaction is part of the answer. The is a lot of movement towards service objects to clean up fat models or large controllers actions. Just create an app/services folder and create service classes like:
class TargetDestructionService
def initialize(shooter, target)
#shooter = shooter
#target = target
end
def execute
#Lot of code that causes the destruction of the target.
end
end
Then in your model or controller you would call:
TargetDestructionService.new(EvilRobot.new, Human.new).execute
Here is a nice article about it: https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services
TL;DR: I don't know how organise my logic domain classes.
I have the model "Application", this model is in the "core" of the App and is the way I "enter" and operate over other models like:
#application = Application.find(params[:application_id])
#application.payment.update_attribute 'active', true
or
unless #application.report.status
or
#application.set_income(params[:income][:new_income])
so the models Payment, Income and Report are basically empty because I initialise the Application model and from there I do things "on cascade" to change the "subordinated" models. But now the Application model has more than forty methods and 600 lines.
I'm doing it right? For instance when I want to add a new Payment I like to do :
payment = Payment.create params
inside the Application model because ActiveRecord "knows" how to handle the foreign keys automatically. I could create the payment inside the Payment model using:
application = Application.find(application_id)
params[:application_id] = application.id
self.create params
but this way, I need to set the Application.id manually and that looks more verbose and not elegant.
So --if I want to reduce my Application model--, should I create modules in APP/lib directory or should I move methods to the other models?
should I create modules in APP/lib directory
Basically, yes, that's what you should do. Although I'd probably make them classes rather than modules. The pattern it sounds like you're after is called "service Objects" (or sometimes "use cases"). What this does is takes the logic from a specific operation you want to perform, and puts it in it's own self-contained class. That class then collaborates with whatever models it needs to. So, your models stay quite small, and your "Service Classes" follow the Single Responsibility Principle. Your controllers then usually call a single "service class" to do what they need to do - so your controllers stay pretty minimal too.
If you google "rails service objects" or similar, you'll find lots of great stuff, but here's some resources to get you started.
Service objects rails casts: https://www.youtube.com/watch?v=uIp6N89PH-c
https://webuild.envato.com/blog/a-case-for-use-cases/
https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ (there's one section on service objects there)
Keep in mind, once you do start using service objects, you don't necessarily have to ALWAYS go through your Application model to get to the related ones. A service object might take an application_id and then do eg. #payment = Payment.find_by(application_id: application_id) and so you don't have to fetch the application instance at all and can manipulate the #payment variable directly.
The fact that Rails makes it "easy" and "pretty" to get to related models doesn't necessarily mean you should do it.
I would not worry about long controller and spec files in Rails.
These files tend to get very long and the usual advice of keeping classes and methods short does not necessarily apply for controllers and their specs.
For example, in our production system user_controller.rb is 8500 lines long and the corresponding user_controller_spec.rb is 7000 lines long.
This is the length of our top 10 controllers
1285 app/controllers/*********_controller.rb
1430 app/controllers/***********_controller.rb
1444 app/controllers/****_controller.rb
1950 app/controllers/****_controller.rb
1994 app/controllers/********_controller.rb
2530 app/controllers/***********_controller.rb
2697 app/controllers/*********_controller.rb
2998 app/controllers/*****_controller.rb
3134 app/controllers/application_controller.rb
8737 app/controllers/users_controller.rb
TL;DR: If your app has four models that are all tied to tables in your database (ie. leveraging ActiveRecord and inheriting from ActiveModel::Base), the framework is pretty opinionated toward using model classes.
Abstractions of the service class pattern can be useful in some cases, but give yourself a break. One of the advantages of Rails is that its supposed to remove a lot of the barriers to development, among many things, by making organization decisions for you. Leverage your model classes.
Let's see if this starts an epic developer bickering war.
Also, its ok to create interfaces in your models for related model creation:
class Application < ActiveModel::Base
has_one :payment
def create_payment(attrs)
payment.create(attrs)
end
end
And by ok, i mean that the framework will allow this. But remember, you're already inheriting from ActiveModel::Base which defines many instance methods, including create.
I would recommend, esp. if this is a small project and you're just getting your feet wet, to use well-named rails controllers to read and write objects to the database:
class ApplicationPaymentsController < ActionController::Base
def create
application = Application.find(params[:id])
application.create_payment(payment_params)
end
private
def payment_params
params.require(:payment).permit(:x, :y) - whatever your attr names are.
end
end
The sleekness you're looking for in abstracting foreign keys in creating a relational record is taken care of for you with Rails associations:
http://guides.rubyonrails.org/association_basics.html (good starting point)
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_one (more explicit docs)
That will help you slim down models if that is your goal. Just for clarification, this is one of those things that devs are extremely opinionated on, one way or another, but the truth is that there are code smells (which should be addressed) and then there are folks who arbitrary preach file length maxes. The most important thing in all of this is readable code.
A good litmus test for refactoring working code is put it down for a few weeks, come back to it, and if its confusing then put in some time to make it better (hopefully guided by already written test coverage). Otherwise, enjoy what you do, especially if you're working solo.
I am trying to build a rack based web framework on my own, and I got confused when I refer to the design of Ruby on Rails.
Why should a model in Rails be named like User instead of UserModel, just like controllers or helpers do (for example, UsersController, PostsHelper)?
If the naming convention of model is good, why don't controllers or helpers follow the same rule? The Controller and Helper postfix is bit tedious.
Is it good to namespace everything? For example: Controller::User, Model::User.
Are there any good references/books that talk about the file structure of software?
I think that it's for convenience sake. You would never usually reference the controller and helper classes in your code, whereas you reference the model classes all the time. If they were called UserModel you would have lots of code like
user = UserModel.first
post = PostModel.first
there's two problems with this: it breaks the convention of "call the instance variable name after the class", which is a simple bit of self-documentation, and also it's cumbersome, with a lot of repeated information.
Also, the User class does actually represent a real life user, whereas the UserController class doesn't represent anything in the real world, it's just part of the machinery, if you know what i mean.
EDIT - some more answers:
3) Namespacing should be reserved for separate sections of your site, like an admin section for example: you can have an AdminController which inherits from ApplicationController, and adds a few more bits (such as a before_filter for checking that there's a current user and that they're an admin), and then your other admin section controllers inherit from AdminController. Namespacing in the routes allows easy management of this.
4) As for books, read this: http://www.amazon.co.uk/Agile-Development-Rails-Pragmatic-Programmers/dp/1937785564/ref=sr_1_1?s=books&ie=UTF8&qid=1450792683&sr=1-1&keywords=hansson+rails
It has some "Make a blog in 15 minutes" type stuff which i would actually recommend NOT diving into until you understand the underlying architecture, which is well explained elsewhere in the book. Too many people dive into Rails without understanding what they are doing (their hapless questions fill this forum).
All about convention, had to decide something. I don't speak for the rails core but this is my thoughts.
Model - In most cases, this is a representation of a single instance of a something. Although you could have cases where that is not the case, it helps me to conceptualize the singularity of it. I.e. I am dealing with a User not a Users
Controller/Helpers/Tablenames - In the database a User model maps to users table. The controller and helpers follow the same convention of the database.
Namespacing is good, but you should only namespace something when it makes sense on a clear boundary.
A great ruby framework to look at that is not rails is Lotus - http://lotusrb.org/ It uses namespaces heavily.
Background
I'm fairly new to Rails. I'm working with Ruby 2.1.1 and Rails 4.1 and have been reading a lot of books and tutorials but haven't quite got to the point of understanding what the best way is to keep controllers lean.
It looks like with Rails 4 there are concerns available for both models and controllers. At least for the controller concerns, it seems like the primary purpose is to help keep the routes and controllers DRY. A good example seems to be the defensible example at codeschool.
I also read some questions asking about similar things but couldn't find what I felt to be an exact match, especially in the context of Rails 4. What I saw seemed to say that that it would be possible to create a custom class or module and then invoke that in the controller action.
Specific Issues
I have a controller where most of the actions are lean but there is one that has to do a lot of data processing and this one has grown pretty large.
I created some private helper methods in the controller to break the processing into smaller pieces - but it seems like these should almost be combined into a module or class?
The data processing really is specific to this controller and is unlikely to be used elsewhere
Questions
Am I correct in feeling like this controller should not have one large, bloated action? Or is it normal for controllers' actions to become large like this?
Are concerns designed to be used to help reduce this? Or are they more to help with the DRY aspects of shared actions between different controllers?
Would a class or a module be a good solution? Is it typical to create custom classes / methods to help keep controllers lean?
If the class/module approach is a good solution, how does one choose module vs class?
If the class/module approach is a good solution, where should these be housed and how should they be loaded / invoked? ( It seems like they should not be global "helper" functions if they are just specific to one controller )
Depends -- if you have to process that data in the controller, perhaps it's the right way. But as with most things, if you show us the code, we may be able to refactor. You need to be aware of modularity, which means you should split your code as much as possible, to encourage reuse
Concerns are really for providing cross-controller / cross-model modularity. For example, we use friendly_id in several models; we've separated into different concerns, giving us the ability to change the concern once & update all the models
You're looking for class methods
Fat Model Skinny Controller
Although not well documented, one of the core Rails patterns is to keep your controller as lean as possible. To do this, dhh recommends putting lots of your methods into your models (scopes, class methods etc) - allowing you to call them with brevity
This is a general programming pattern
Inherited Resources
One of my favourites - this creates the standard RESTful action interface in your controller, so you don't need to include it yourself:
#app/controllers/posts_controller.rb
class PostsController < InheritedResources::Base
end
This will load index, show, new, create, edit, update, destroy automatically
Callbacks
Finally, you should be aware of callbacks
These allow you to run common code with a single instance method. A good example is find:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
before_action :load_post, only: [:show, :edit]
private
def load_post
#post = Post.find params[:id]
end
end
Your best option in this case is probably to use form objects. A form object exists simply to abstract away the complex interactions that are occurring in your controller. This results in cleaner more maintainable code, among other great benefits. You can read more and find some excellent examples of the technique at http://pivotallabs.com/form-backing-objects-for-fun-and-profit/ and http://railscasts.com/episodes/416-form-objects (pro), or just search for "rails form objects".
I have a rails app moving along fairly well, but the fact that I'm doing this myself means that some poor sod is eventually going to see this and say, "What the hell were you thinking? Why did you put this here?!?!"
Where is that poor, sorry soul going to expect to see a series of classes that aren't used by anything but a single model class? Obviously, I could chuck it in the_model.rb along with class TheModel, but this may expand beyond the planned two classes...
I thought about lib, but it doesn't need to clutter everyone's view of the world....
Thank you.
My predecessor thanks you.
Leave them in the_model.rb until you need them in more than one place. If you refactor needlessly, you're not doing the simplest thing that could possibly work. You aren't gonna need it.
At that point, the general pattern is to create a directory for "concerns". See this weblog post by Jamis Buck or this one by Peter Marklund for more information.
In general: follow the Rails naming conventions when translating class names into filesystem locations. (that is: keep the class FooHelper::Bar in foo_helper/bar.rb)
You can make exceptions for small helper classes that are only used once and keep them in the same file as your model, but those should be exceptions. (but the converse is also true, don't create one-line thousands of single line files)
Use modules and class namespaces to your advantage. If you have a helper class that is only used by (and dependent on) your model, put them into the namespace of the model class:
class TheModel::HelperClass
end
the location in the file system would be app/models/the_model/helper_class.rb
And something that is not dependent on your model can probably still be namespaced
module Bar
class Foo
end
end
living in bar/foo.rb, of course
You should probably not be afraid to put things that are not models into lib -- that's what this directory is for
I'd say concerns, while useful, are not really the right way to go because that is a way to split a single class into multiple files and you don't seem to be doing that.