I'm learned RoR programming through Mickael Hartl's tutorial, which I really enjoyed.
Now I'm developing an application with 8 types of objects.
For each object, I need to assign a status, coming from a statuses table, and to assign current user's name.
In each controller, in private section, I wrote a statuses_list function and a current_user_name, which make these data available to select fields in the forms respectively.
This does not sound DRY-like that much.
Would it be relevant and secure to write such functions as helpers so the data are available anywhere in my application ?
Thanks for your advice,
Best regards,
Frédéric
Move those methods to their home classes. Status.list() and User.current_user(). That's generally how people solve the problem "everyone needs a current_user, but nobody wants a $current_user global variable.
Then assign the current_user very early in your before_filters, when the authentication system identifies the user. And read /Confident Ruby/, by Avdi Grimm, for a very good write-up on how and why to create a Guest user, for current_user to return if nobody is logged in: http://devblog.avdi.org/2013/08/26/confident-ruby-is-finished/
Putting the methods in a helper file is the best way to make them available in your views, but if you also want to make these methods available in your controllers, then put these as methods in ApplicationController and at the top specify:
helper_method: :statuses_list, :current_user_name
Hope that helps.
To make your code accessible throughout your application, you have to mention it in helpers. This is ofcourse a DRY principle and would help you to reduce the same line of code again and again..
I would recomment using inheritance to give some controller some same methods.
Create a SuperClass that extends ApplicationController like:
class SuperController < ApplicationController
[...]
end
and subclass it
class MyControllerClass < SuperController
[...]
end
Put the repetitive stuff in the SuperController - et voila ! MyControllerClass inherits methods SuperController so you dont need to repeat them.
Well, thanks to all the answers collected, here is how I did :
Defined a set_statuses_list method in a ParametersHelper
Added include ParamtersHelper to the ApplicationController
Added before_action :set_statuses_list in each of my 8 objects controllers
It feels like a good balance, and I'll be happy to get comments about it.
Note that the current_user method was indeed already available through the SessionsHelper.
Thanks for your help,
Best regards,
Frédéric
Related
I have a User model which gets accessed through a REST API and ActiveAdmin. Since there is a lot duplicated code (almost all) it might be a good idea to dry things up. There are a lot of topics out there handling this issue but i do not find a proper way. I put some of the ideas i have below
UserController instance in each controller
Again duplicated code gets created and the functions
#AA
controller do
def create
#userController = UserController.new
#userController.create(params)
end
end
#REST API
#Create instance through callback
def create
#userController.create(params)
end
Inheritance
I would like to have one basic CRUD controller lying above my model through inheritance like this
Class BaseUserController < ApplicationController
Class Api::UserController < BaseUserController
but (yet) i dont know how to do this in AA also in mind with the inherited_resource gem in mind
Concerns
Also concerns seem to be a nice way. But i would then create seperate concerns for each action to have one concern to exactly handle one behaviour
aspect and also to make them more reuseable . This then again seems to unnecessarily bloat the app and also concerns are not really made to be used like that. (?)
Modules/Mixins
Since modules cannot be instantiated but implement actions fully, this might just be what i am looking for. So each controller includes the desired actions or extend fro ma model rather than a class then.
The thing is - the more i read the more i get confused. I hope some of you guys might help me bring some light or i might turn to the dark side an use C# and .NEt again :D
Thanks in advance!
Example Code
I took this create method for a SMS resource in both, the REST API and the AA, where i call a couple of Services, but if complexity grows i have to add these lines in two places. How could i avoid that? Cant i have a basic SMSController which is used from the API endpoints as well as the AA interface?
controller do
def create
#customer =Customer.find_by_phone(params[:sms_message][:phone])
SmsService.sendSMS(#customer[:phone],Cryptoalgorithm.sms(#customer[:id], #customer[:recharge_token],params[:sms_message][:text].to_i)
#sms = SmsMessage.create(:customer_id => #customer[:id], :text => params[:sms_message][:text], :phone => params[:sms_message][:phone])
#customer.increment!(:recharge_token)
redirect_to admin_sms_message_path(#sms[:id])
end
end
I'm having trouble using a decorator. I've never used one before and I've been trying to use one with regards to something that I've been doing for breaking up some emails.
However because I've never used one before, I've been having trouble even doing very simple things with my decorator and I'm thinking there is some form of setup issue with it. I do know that everything outside of my little feature (aka the gemfile and such) are all up to date and proper.
The error I am getting is simply,
Could not infer a decorator for ActiveRecord::Base.
Now I have a controller that is almost empty, but inside it, I have the active record portion saved like so.
class Admin::ReceivedEmailsController < Admin::ApplicationController
With my view being titled,
_receive_email.html.haml
All I am doing in my view as of right now is so:
%td= received_email.decorate
My decorator
class Admin::ReceivedEmailsDecorator < Admin::ApplicationDecorator
def received_email
if can? :update, #customer
received_email.content
else
"You need to have the correct admin access to view the email"
end
end
I feel like this would have to be such an elementary thing for me to be missing, but I'm not sure what it is. Would anybody have any idea what I'm missing?
After much further research, reverse engineering further decorators and reading more documentation. I learned that a model or a helper is needed for a decorator to be properly used. Which due to my partial I did not have one specific model or helper to use.
Im using bootstrap & rails and have a user model and post model..users create posts (collections)..
with bootstrap in the navbar i want the user to be able to click a dropdown which displays the name's of their posts..i did this on one controller with a private method and a before_action but i don't want to do this for all the controllers and it didn't work for the application controller...
is there a better way to do this??
I was doing this
def list
#user = User.find_by_username(params[:id])
#collections = #user.collections
end
and a
before_action :list
at the top of the controller
What's the most semantic way to accomplish this??
If you could move both to your application controller, then it would be available to any controller. More generally, I'm not sure if this is the best approach to solve your problem.
These tips might also be useful.
Are you using devise? Or some other authentication plugin? If so you're likely going to have a current_user helper. This would allow you to simply do #collections = current_user.collections
To the extent possible, I recommend using more descriptive names for your actions and parameters. def fetch_list_collections might be a better name or instead of passing a param named id, perhaps your param should be named username. These naming conventions become extremely important both for others who might look at your code as well as for yourself if you return to it and are trying to remember what you wrote N months ago.
Your list action is generating a N+1 queries. Meaning that you're hitting the database multiple times when you should do so just once. See the rails guide on this. You might also look at ways to avoid this w/ devise. Devise is pretty well documented and I'll bet there is something in the wiki discussing this.
You may want to consider limiting when you call this action - at a minimum - a post request to an update action? What about before they've logged in? current_user might be nil and you'd have an error attempting to call a collections method on nil.
Take your time learning this stuff. You don't have to learn it all at once, but I thought the above might be helpful.
I got it to work with this in the application controller
before_action :list
private
def list
#collections = current_user.collections
end
thanks #arieljuod
I see this all the time in rails code:
before filter :get_post, only: [:edit, :update, :destroy]
def edit
# code .......
end
def update
# code .......
end
def destroy
# code .......
end
private
def get_post
#post = Post.find(params[:id])
end
I understand it keeps from repeating the same line of code three times, but isn't there a much easier to read and better way to accomplish the same thing by just refactoring the code into a private method without hiding the instance variable and the before filter?
private
def get_post(post_id)
Post.find(post_id)
end
Then you can keep the instance variable in the action
def edit
#post = get_post(params[:id])
end
It doesn't make sense conceptually to hide instance variables in private methods. Why is this so prevalent in rails?
You'll probably see a lot of differing opinions on this particular practice. Personally, I agree with you; I feel that the strict adherence to DRY only serves to introduce a bit of confusion by hiding away the instance variable in a method down at the bottom of the file. I'm not even sure a get_post method really buys you very much myself. That said, I tend to prefer explicicity (if that's a word) over terseness in many cases, and for a team that uses this trick consistently in every controller, there may not be as much (if any) confusion.
If the filter encapsulates a little more complexity, it may be worth it. For example, the popular authorization library CanCan by Ryan Bates introduces a controller class macro called load_and_authorize_resource that installes before_filters such that RESTful resources are automatically loaded and authorized for access against the current user. This could potentially be more than a line or two per method, and would not repeated verbatim each time.
There's a popular middle-ground that some Rails devs use, which is to specify the before_filter as a block, so you don't have to repeat yourself, but the instance variable is up at the top of the file:
before_filter(only: [:edit, :update, :destroy]) do
#post = Post.find(params[:id])
end
As mentioned in the comments, there are some gems, like decent_exposure, which help formalize this pattern and make it significantly less "hidden" or confusing (since it's declared explicitly and the API is known).
As other have stated, this is a matter of opinion. I started off using before filters to hide common instance variables between controllers. I still do this on occasion, but I have found it to be a code smell that the controller is bloated. If you have so many instance variables that you need to hide them in filters you are doing too much. My opinion is that the controller action should be very small, and if all the actions have one line in common that handles the instance variable its not an issue.
My remedy for getting my controller actions to a level that made me comfortable with having a common instance variable was to use service object/presenters to consolidate the information that was once in several instance variables into a single instance variable per controller action. I've found this been a huge improvement in the readability and maintainability of my controllers and views.
I think your suggestion is an improvement over the typical Rails convention. I'd take it a step farther, though and drop the instance variable entirely. It's misleading to use an instance variable because it's typically not representing shared state across multiple controller actions, and often not even shared between any methods (except the before filter that assigns it).
The Rails convention of populating an instance variable for use in a view is a bad idea whose use should disappear. In a clean MVC implementation, nothing from the controller would be available in the view. I think this is a much cleaner implementation which properly separates the layers:
def edit
post = get_post(params[:post_id])
render 'edit', locals: { post: post }
end
No leaking state, no reliance on an instance variable in the view, explicit intent, and a view that is more easily reusable and can be rendered from within other views without injecting a hacky inline instance variable assignment.
Rails has a number of conventions that make for quick bootstrapping, but they don't make for good code.
It's a convention over configuration thing.
Neither approach is wrong, but people who like Ruby often prefer minimalist coding patterns, and value using the quickest approach to solve a problem so they can get on to solving more complex and interesting problems
If one has seen this approach used often enough, it becomes second nature to have all of the member functions (obviously not the collection functions) preload the member variable for you before you define your code. It gets to the point where specifying the member variables in the function does not create a significant clarity advantage (if any).
There is also another approach using helper_method. It's looks almost as you want it.
http://rails-bestpractices.com/posts/57-substituting-before_filter-load_object
It looks like you cannot use a helper in a controller even if both of them belong to the same class. For example: XYZHelper and XYZController...
I was under the impression that if the prefix is the same "XYZ" then the method in the helper can be used in the controller and in the view, but I think this is not the case.
So how do I remove some common functionality from a controller and place it in a helper. I want to place that piece of code in a helper because other controllers may be using it. What is the best way to approach this.
Thanks,
Jai.
There are a few ways you could share some code between controllers:
Application controller: If the code in question is an action/method which ought to be in a controller, but could be used by several controllers (or all of them), then this might be a place to put it.
the 'lib' directory. just a general purpose place to put code which should be shared.
Put it in the model. This may or may not be applicable, but its worth taking a good look at the code you're trying to move and thinking about whether it is something which makes sense on a model (instead of a controller or random class/module in lib).
Follow Pete's guidelines. If you still need to expose the methods then do the following:
Add the methods to ApplicationController class and register the methods as helper methods by calling helper_method.
class ApplicationController < ActionController::Base
helper_method :foo, :bar
private
def foo
"foo"
end
def bar
"bar"
end
end