I'm learning alot about the DRY concept in Rails so here's my question:
I have a fixed sidebar in my application that always needs to load various Objects, such as Locations.
In the controller related to my index page, actually in controllers/pages_controller.rb I'm loading #locations:
#company = current_user.company
#locations = if #company
current_user.company.locations.order(:name)
else
[]
end
Which works fine for my navigation menu (sidebar), however, once I select a location and load a view delegated from a different controller (in this case locations/show.html.erb) my #locations variable is no longer found.
Sure, I could add it to the show method in my locations_controller.rb file, but that isn't very DRY considering I'd have to put the above code in every single controller method that relies on the sidebar navigation menu (all of them!) since it's fixed.
What's the DRY way to do this once and not have to do it in every controller?
You can create a method in your application_controller
def set_locations
....
end
Then in your pages_controller and locations_controller add a before_filter
before_filter :set_locations
It will set call set location before all all methods of the pages and locations controller.
If you want to set locations only for few controller actions then call:
before_filter :set_locations,:only=>[:show,:index, :your_action_name]
Updated:
In Rails 5 before_filter is deprecated and you can use before_action
You could add it to the ApplicationController in a method that runs before every action, every controller inherits from this ApplicationController so it will be available.
Related
I would like to ask if i should keep the empty methods in my controller (Its a question about code style):
before_action :set_project, only: [:show,:new]
def show
end
def new
end
Should i keep it like this or simpy remove show and new action
class ProjectController < ApplicationController
before_action :set_project
def index
#indexaction
end
def create
#createaction
end
is it more Railish way? Rails Styleguide doesnt indicate any sollution to it, only that:
def show
end
is better than
def show; end
If you're not defining any data in those methods, you can remove them.
Rendering By Default:
Rails automatically infers the view from action names in routes.
If you're not defining data (only to be done in the controller), you'll can rely on the view being loaded without the action being present:
You've heard that Rails promotes "convention over configuration". Default rendering is an excellent example of this. By default, controllers in Rails automatically render views with names that correspond to valid routes. For example, if you have this code in your BooksController class:
class BooksController < ApplicationController
end
And the following in your routes file:
resources :books
And you have a view file app/views/books/index.html.erb:
<h1>Books are coming soon!</h1>
Rails will automatically render app/views/books/index.html.erb when you navigate to /books and you will see "Books are coming soon!" on your screen.
Good ref: Rails doesn't need index method in controller defined?
If you want to use those methods in future, keep those methods, else remove them. There will not be any problem even if they are kept.
Also remove the routes to those methods if they are created and you dont want to use them. Code should be as simple as it can.
If it's truly about style first keep your comments above, so that if you ever run rdoc or generate docs, they take whatever comments come before the def show and use that to build out the paragraph describing what this method "does".
Example
class ProjectController < ApplicationController
before_action :set_project
#indexaction is awesome and will be used to do stuff like look at all the projects
def index
end
# will hopefully create your project
def create
end
Yeah but don't keep it if it isn't used...
If routes aren't used, don't have them lying around. By default you get things like blah.com/projects/1.json and if you haven't locked this down (or even if you have and a user is inside the app) they could easily get the json result, assuming you left everything there for later. Let alone if you have a before_filter that does 'stuff' to projects on load`.
commit it once into git and then delete it and commit again. If you ever need to reinstate it, at least github/bitbucket history or git history if you are sadistic, will have it to copy and paste.
Apoligies if this is a noob question, but I am new and have a question about initializing and using instance variables in ruby on rails. I can create an instance variable like this in a model:
class MyClass
def do_stuff
#object_to_be_reused = []
#make a DB call here populating the array with a bunch of objects
end
end
And while I'm there on that page using that class, #object_to_be_reused works great as I only have to make a db call to set it once. However...
When I switch pages and come back later, #object_to_be_reused is still set to it's previous value. How do I re-initialize #object_to_be_reused such that, it is always not present when loading the page?
Thanks!
If you want to reset a variable each time an action is performed (or for selected actions only) I recommend you creating a before_action / before_filter in the controllers and do it:
before_action :reset_my_class_variable, only: [:show, :edit] #applies to actions
before_filter :reset_my_class_variable #applies wide
#code
private
def reset_my_class_variable
Myclass.do_stuff
end
My code is starting to have duplicates.... because I don't know where to put methods/functions that need to be accessed by (2) different controllers, and just to get it up quickly I just duplicated it.
So I have a users_controller, and a pages_controller and posts_controller.
On the users page, I have posts being displayed.
On the homepage, I also have posts being displayed.
Where should I put shared/common code so that the users_controller and pages_controller and posts_controller can all have access to ithese methods?
Right now, I have duplicated def methods in some controllers, and also some duplicate private methods in each controller.
On a related note, how can I access methods defined in one controller from another controller?
Maybe a module?
module ProductSharedMethods
def product_list
Product.scoped
end
end
class UsersController < ApplicationController
include ProductSharedMethods
def index
#products = product_list
end
end
If you have posts being displayed on two separate pages then use a partial. I don't know what version of rails you are using but newer versions have a views/application folder for shared partials.
If you have a method that is shared across more than one controller then put it in the application controller. Controllers are classes so follow all of the normal rules.
Hope that helps.
I have a collection model. I succesfully created a _collection.html.erb that i call with <%= render #collections%> in my application layout.
My problem is that in ALL my controlers method I must add #collections = Collection.all
I found it very very ugly,it will make my collection scope a pain to change, and I'm sure that I am missing a rails magic something that would be way nicer.
Is there a way to have a part of the layout generated by model data without having a identical piece of code in AAAALLLLLL the controllers?
Notice that your controllers all inherit from ApplicationController. Use this to your advantage. Add a before_filter to ApplicationController that loads your collections.
#cam was right. Any rails project has an ApplicationController. Your controllers all start with MyController < ApplicationController, right? If so, that means you can create a before_filter in your ApplicationController, which will be inherited by all your controllers. To do so :
/app/controllers/application_controller.rb
before_filter :load_collection
def load_collection
#collections = Collection.all
end
From now on, you can use #collections from all your controllers (as long as they inherited from ApplicationController)
A newbie question:
I have a partial that I'm loading on a website on different pages.
That partial needs some data, so in the controller of the parent view I do the query:
#properties = Property.find(:all)
I pass the query results to the partial in the view using :locals
Now, I would like to render the same partial from another view. This view has a different controller. Do I have to repeat the query in that controller also? Or can I have a separate controller for partials I use one more places in the website. In this last case, how do I indicate which controller to use in the view when I put the render command?
Or should I use somethink different than partials for this kind of use?
Kind regards,
Michael
How i would solve this, is to define a new file inside your lib folder, with a meaningful name. For now i will just use does_as_shared.rb:
module DoesAsShared
def setup_shared_data
#shared_data = ...do something useful here ...
end
end
and then inside your controllers that need that code you write:
class ThingsController < ApplicationController
include DoesAsShared
def index
setup_shared_data
.. and do some more stuff ...
end
end
The advantage of this code is that the shared code is limited to those controllers that really need it. And at the same time it is pretty readable: the include statement, given that the name is chosen well, clearly indicates what functionality is included.
You don't need to copy the code to set up the partial's data, but you would need to hoist it into a controller that your two controllers inherit from. Normally, this would be your ApplicationController. Define a method in there that loads the data you need. For example:
class ApplicationController < ActionController::Base
# ... your code ...
protected
def load_partial_data
#properties = Property.find(:all)
end
end
Now to actually call this method you have two options:
If all your controllers need this data, add a before_filter to your ApplicationController like before_filter :load_partial_data
Otherwise, add the before_filter to just the controllers that actually need to load the data.
I hope this helps.