Rails: Best practice for model dependency class locations? - ruby-on-rails

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.

Related

Rails: too many methods in model

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.

Lots of different models in tests (rspec)? Advanced

I am only looking for answers from senior/more experienced Ruby/Rails developers on this one, since I think this is a bit more advanced of a question.
I have a gem I am working on that adds some behavior to AR models. I have to test it for a lot of different associations (has_many, habtm, has_one etc), and I also have to test behavior when different options are passed for the associations (e.g. :foreign_key). Now with all these different models, I can use the same table in the database because the fields themselves do not need to change, only behavior specified through has_many, belongs_to and so on.
Keep in mind there are a lot of different options, so the number of models is quite large.
First I don't think it would be bad practice to have the definition of the models next to / in the test itself, for readability purposes (if I have multiple tests that use the same model then I would group them together and use the before method). So this is one of my goals, and you can comment on this if you don't agree.
The second thing I am not sure of is I wanted to keep the simple/same name of the model in all the tests, for example "Task", instead of TaskWithManySubtasksAndForeignKey or something ugly like that. The problem is there are so many models it's hard to come up with meaningful and simple names. I'm not quite sure about this - using the same name, since it's a constant, is a little problematic. I have a solution with a proxy class but I don't think this is the optimal solution. I was considering using variables (with the let method) like "taskModel", but it seemed a little verbose and unusual.
One other option that comes to mind, but I am not sure is possible to do easily, is to remove an existing association and then define a new one. So e.g. add a has_many and then remove it, add a habtm...
How would you go about doing this?
Defining unique models in the spec files is not necessarily a bad idea since it makes it easy to see exactly how each model is defined. The obvious problem with this approach is if you want to reuse the models in other test files. The Rails approach to this is to define all the models in separate files and then just require them in the tests that need it.
I think it really just depends on how many models you have and how much you want to reuse. In one of my gems, I took the approach of defining the models in the spec file, in another gem, I defined them in the spec helper, and in yet another I took the Rails approach and used a separate directory for them. If you asked me which one I preferred, I'd probably go with the spec that also contains the models because it's all in one place. Definitely a subjective problem though.
Another approach I've taken on occasion is to create an anonymous class that's guaranteed to only be around for the life of that test:
describe 'my test' do
let(:my_class) do
Class.new(Task) do
has_many :things
belongs_to :something_else
end
end
it 'should have many things' do
my_class.should have(100).things
end
end

Convert text into code

I'm in the process of creating a product configurator. Trouble is that each product has many components, which are themselves configurable. Imagine you're configuring a computer when buying it.
The dependencies sometimes apply when a component is selected, or when a quantity of a component, or even a combination of both. It all becomes very complicated at some point, and is not consistent across products, as each is its own universe in a sense.
Oh, and these rules change all the time, so coding them in the app is not really an option for me.
I was hoping to be able to store the code of a method called validate in the database.
Each configuration would have it's own version of validate, which would be stored as a text in the db, and could be changed if/when required.
Is is possible to do this? How can i take text and have rails actually execute whatever this text says?
For example i have this:
irb(main):246:0> #h= Configuration.find(1).rule
=> "def getmax3(inputarray)\n #maxpos = 0\n inputarray.each do |h|\n if #maxpos < h.position\n #maxpos = h.position\n end\n end\n 1\n end"
which obviosly isn't code. The method above is of course just a test as I'm trying to figure things out.
(Oh and I know it must be horrible practice to do this, but I really can't think of another way)
Thanks a lot!
Yes it is an horrible practice :)
If you had simple and stabilized rules to implement, you could store some variables in a dedicated table and use those variables in your rules; since it looks like you don't really know what is going to happen about those rules, be ready to accept any challenge by using a tested and proven workflow.
Here's my suggestion:
Setup an automatic deploy procedure using capistrano and other tools
Write your rules in a dedicated validation library
Code & test rules, commit and deploy
Defining a DSL can make your life easier, if you are committed in the long term to manipulate such rules and you could not be the only one in charge of such task; observe how the rules are changed over a good amount of time and you will know if having a dedicated DSL is a good thing or a waste of time.
I might be misunderstanding your problem, but if you could get that code out of the database and into files that can be tested and versioned, you'll be in a much happier place. Without knowing the intricacies of your models, it's hard to say what the best approach would be, but I guarantee one is out there.
I've used Single Table Inheritance in the past for modeling complex systems that have related components which share common properties and behavior. You might be able to use this approach with your application. STI basically says you have a parent class, in your case Configuration, and subclasses, that all reside in the same table. If there is shared behavior among some but not all of the subclasses, then you can move that code to modules and include in the classes that need it.
Basic parent class would just inherit from ActiveRecord:
class Configuration < ActiveRecord::Base
end
Then each subclass would have its logic defined within it:
class Component < Configuration
def getmax3(inputarray)
#maxpos = 0
inputarray.each do |h|
if #maxpos < h.position
#maxpos = h.position
end
end
1
end
end
I'm not saying this is the direction you want to go, but hopefully it will make you think about other ways of modeling your system.

Rails naming conventions for models with forbidden names

I'm writing a rails application, and I need to name one of my model Test, and inside that model I need a class attribute - when I tried the first one, I couldn't run any UT since Test ceased to be a module - so I ranamed to Tst. I haven't even tried naming a column class - I went with clss. What names would you use? Are there any conventions known amongs RoR developers for such situations?
I haven't seen anything for Test but class is typically changed to klass.
Ruby and Rails have several different objects part of a standard library. You cannot use an already-defined class name as name for your models.
For instance, you cannot call a model Thread or Object because Ruby already defines a Thread and Object class.
Likewise, Ruby has a library called Test::Unit so you can't use the Test namespace as model name.
There isn't a real full list of reserve objects because it really depends on your current environment. However, in order to successfully use Rails, you should at least have a basic Ruby knowledge so that you know the names of the most common standard library classes.
I've run up against this a few times (writing educational software--where you regularly want to model 'tests' :-). Depends exactly what you're modeling I suppose, but I usually opt for 'quiz' to avoid conflicts. I'd love to hear a better solution, since I find 'quizzes' an awkward plural.
Like dj2 said, class is usually done as 'klass'.
Please check the following page for Rails reserved words: http://reservedwords.herokuapp.com/ None of these words should be used as class or attribute name.

OO Design in Rails: Where to put stuff

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

Resources