I have several methods I call from my controllers that feel like they should be pulled out and put into a reusable class, outside of the controller. Where do people usually put this stuff? I know I can put them into my ApplicationController, but that doesn't seem to be a great solution if I think I can use these methods later in other applications.
Also, I have a bunch of utility methods in my controllers that likely won't be used in other controllers, or in the future at all, but I feel like they just bloat my controller a bit. Do people usually move these out someplace for cleanliness or just end up with a huge controller?
I'm coming from Java and Actionscript where I'd just create new util classes for this stuff.
The lib directory is a place you can put modules/classes which can be mixed in or used by controllers (and anything else, really). This can be a place to put code that doesn't fall into other areas (but be careful about making sure lib doesn't turn into a mess itself). Side comments just to keep in mind:
If you know you have a large amount of related functionality that could, or will, be used in other applications, that might be a plugin.
Its also worth keeping in mind that there's nothing wrong with creating a model that is not an Active Record object. So again, depending on what you have, this might make sense as well.
Create a module file in lib directory:
module ControllerUtil
def foo
end
def bar
end
end
Include the module in the controller:
class UsersController < ApplicationController
include ControllerUtil
end
You can create app/modules directory, and create XYZUtils module in it e.g
module XYZUtils
def abc
end
def efg
end
end
and include the module as and when required in Controllers or models etc.
include XYZUtils
You can create different modules for utility functions related to different Models or entities
I won't prefer /lib directory because that should contain the project related code, not app related, e.g tasks etc.
I would keep all the App related code in /app directory itself
Related to Sahil kalra's above answer from 2014:
Rails now has an app/controllers/concerns directory where you can put modules full of helper methods and easily include or extend them in your controllers. I just copied and pasted all of my logic-intensive methods out of my application_controller and they worked right out of the box.
(You should, of course, still make sure everything works correctly before putting anything into production.)
Controllers should be very minimal - basically ingesting arguments and making some very high level decisions. If you have some helper functions that are doing just those kinds of things but aren't going to be reused, then keeping them in the controller is the right thing to do. Just make sure to mark them as private.
For more commonly shared things, you can either back them into your ApplicationController (if they're used in all controllers) or into a class in your app/models directory. I recommend the models directory over lib because Rails in development mode is much better about spotting changes to those files and reloading them. Changes to files in /lib tend to require restarting the webserver, which slows down your development effort. This is unfortunate, because controller helpers really should not be mixed with models.
In general, though, if you have more than a handful of these helpers, you're probably doing too much in your controllers. Take a hard look at them and see if maybe some of them shouldn't be part of your models instead.
Related
This is my first on StackOverflow and I'm a new rails developer.
I'm using RoR to create an inventory application for Magic: The Gathering cards. I've found a Json API that I'd like to use to pull data on all of the cards, sets, etc into a local database.
My initial inclination is to create a helper class to manage all of this (which can also be called in seeds.rb during db:setup), but I have no idea where I should put this class in my project's directory structure. It's not really a model/controller/view, so I feel it should be kept separate from those parts of the app.
Further more, I'm having trouble testing any class I do make. I initially created a directory app/classes and put the class there. Then in my spec directory, I created spec/classes and created the spec file. Accessing my helper class from the spec did not work in the same way that accessing my models in their spec classes did.
I'm at a loss as to how to do this and quite a bit of googling and searching on here has just left me more confused. I'd love any help that can be offered. How would you do this?
You don't mention specifically what issues you're encountering, but for starters app/classes isn't in rails's autoload path - rails' require magic doesn't know to look in there to find these classes (as an aside, 'classes' sounds like a slightly meaningless name - models & controllers are classes too).
You can add to the paths rails searches (see config.autoload_paths in config/application.rb) but I would put this in lib (and the corresponding specs in spec/lib).
It would also work just fine with these classes in app/models, whether or not it does there is down to choice. There's nothing that says that files in there have to be active record subclasses - the decision of whether or not this functionality belongs in there boils down to whether it works/feels like a model to you.
In a Rails app I'm creating, I have some code in my controller that I'm wondering whether it's in the proper place. The code is fairly insignificant, it stores ids in an array to show 'recently viewed' pages. It's about 3 lines of code, but I'm thinking to the future, what if this feature expands? I don't want my controller to be bloated.
I could make a module, but in that case where should I store the file?
Is the controller the right place to be doing the session management?
Any suggestions on my code organization?
Thanks
If it is specific to the controller, keep it in the controller.
If it applies to all controllers, it goes in ApplicationController.
If it is shared by some controllers and not others, then inherit from a controller that inherits from ApplicationController, or use include/extend or make a module that extends ActiveSupport::Concern (which is what Rails uses internally fairly commonly).
And it's best to keep everything in app/controllers or some subdirectory, sub-subdirectory, etc. Rails autoloading depends on the path to match up with the module namespace, so A::B::C belongs in app/controllers/a/b/c.rb. Don't make it deep like Java, etc. Just have the number of directories/modules you need to keep it organized.
Note: though controllers aren't as problematic to have in their own modules, in my experience your models should stay in the root, like app/models, or you'll have problems.
I'd also avoid storing too much in session if you can help it. Store in the DB (or long-life cookies, if it is browser-environment specific) instead. For example- if someone logs out and they were looking at one record, they might want to log back in later and have a list containing the link to that record.
BTW- you weren't asking and probably already have the code for storing recently visited pages in session, but here are similar questions/answers:
https://stackoverflow.com/a/10813602/178651
http://www.rorexperts.com/how-to-store-visited-pages-in-session-object-in-rails-t941.html
My user model is about 2k lines of code. I'm sure this is the case for lots of apps. However, readability and even maintainability starts to degrade in models beyond 400 lines of code, in my opinion. So I started to take related code in the user model and organize it in modules in lib. For example, if I had code related to the user's friends, then I created a module called UserFriend and included it in the user model.
Doing this helped solve the original problem. However, the downside is every time I modify code in the module in my dev environment, I gotta restart the server to load it.
So:
Is my original approach to
handling the monolithic model
correct?
What can I do to avoid
having to restart my server to load
code changes in user modules?
There are a few solutions floating around the Internet for how to reload the lib folder automatically in development mode.
One post I found said that if you require files using require_dependency instead of require, the file will be reloaded each request.
Another more "hacky" solution can be found here, and involves adding a before_filter to your ApplicationController.
You can do one thing, Just keep your helper modules in model folder itself instead of keeping in in lib folder OR create a folder in model where you can keep all the helper modules. I don't see any problem with this approach.
I have a model that requires loading external data from an auxiliary source. A number of web services exist that my model can fetch the data from (swappable), but I don't want to create code that will make it difficult to change services (costs significantly differ based on variable and fixed usage and it is likely changing will be required).
I would like to create a driver to perform the interaction (and then create further custom drivers if the service requires switching). Unfortunately, due to the tight coupling of the driver and model, it does not makes sense to extract the code into a plugin or gem. I have extracted all the code into a module (see example), and currently have the code declared above my model.
module Synchronize
def refresh
self.attributes = ...
self.save
end
end
class Data < ActiveRecord::Base
include Synchronize
end
Does Rails (3.0.0) have a convention for storing modules tightly coupled with models? Should I be using a plugin to do this? Is this associated with the 'app/helpers' directory? If not, where is the most appropriate place to store the code? Thanks!
You are correct that if the module is tightly coupled to that specific model then it's not a good candidate for a gem/plugin.
app/helpers/ is for view helper methods and shouldn't contain modules that are solely for mixing into models.
One place you could put the module is within lib/. This is for code that doesn't really fit anywhere within app/ and is often the initial home of loosely coupled code before it is moved to a plugin (but that isn't a hard and fast rule). However, since your module is tightly coupled to your model, lib/ may not be the best place for it.
I know that 37signals (and others) use the concept of 'concerns' as a way of keeping related model code organised in modules. This is implemented by creating app/concerns/ and putting the modules in there. That directory is then added to the app's load path in config/application.rb (config/environment.rb for Rails 2) with:
config.load_paths += %W(#{Rails.root}/app/concerns)
The module can then be mixed into the model as normal.
Here's the original blog post about this by Jamis Buck - http://weblog.jamisbuck.org/2007/1/17/concerns-in-activerecord
Another variation of this which I personally prefer, although it doesn't involve modules, uses this plugin:
http://github.com/jakehow/concerned_with
Hope that helps.
This link has helped me out around this.
http://ander.heroku.com/2010/12/14/concerns-in-rails-3/
I have been sticking it in a model/extensions directory. The concerns directory makes sense but the word 'concerns' doesn't feel obvious to me. Maybe it will grow on me.
I also added the extensions path in the application.rb
config.autoload_paths += %W(#{config.root}/app/models/extensions)
I have a model that requires loading external data from an auxiliary source. A number of web services exist that my model can fetch the data from (swappable), but I don't want to create code that will make it difficult to change services (costs significantly differ based on variable and fixed usage and it is likely changing will be required).
I would like to create a driver to perform the interaction (and then create further custom drivers if the service requires switching). Unfortunately, due to the tight coupling of the driver and model, it does not makes sense to extract the code into a plugin or gem. I have extracted all the code into a module (see example), and currently have the code declared above my model.
module Synchronize
def refresh
self.attributes = ...
self.save
end
end
class Data < ActiveRecord::Base
include Synchronize
end
Does Rails (3.0.0) have a convention for storing modules tightly coupled with models? Should I be using a plugin to do this? Is this associated with the 'app/helpers' directory? If not, where is the most appropriate place to store the code? Thanks!
You are correct that if the module is tightly coupled to that specific model then it's not a good candidate for a gem/plugin.
app/helpers/ is for view helper methods and shouldn't contain modules that are solely for mixing into models.
One place you could put the module is within lib/. This is for code that doesn't really fit anywhere within app/ and is often the initial home of loosely coupled code before it is moved to a plugin (but that isn't a hard and fast rule). However, since your module is tightly coupled to your model, lib/ may not be the best place for it.
I know that 37signals (and others) use the concept of 'concerns' as a way of keeping related model code organised in modules. This is implemented by creating app/concerns/ and putting the modules in there. That directory is then added to the app's load path in config/application.rb (config/environment.rb for Rails 2) with:
config.load_paths += %W(#{Rails.root}/app/concerns)
The module can then be mixed into the model as normal.
Here's the original blog post about this by Jamis Buck - http://weblog.jamisbuck.org/2007/1/17/concerns-in-activerecord
Another variation of this which I personally prefer, although it doesn't involve modules, uses this plugin:
http://github.com/jakehow/concerned_with
Hope that helps.
This link has helped me out around this.
http://ander.heroku.com/2010/12/14/concerns-in-rails-3/
I have been sticking it in a model/extensions directory. The concerns directory makes sense but the word 'concerns' doesn't feel obvious to me. Maybe it will grow on me.
I also added the extensions path in the application.rb
config.autoload_paths += %W(#{config.root}/app/models/extensions)