Rails Model Controller Best Practice - ruby-on-rails

I have basic functionality for sending a user to the moon:
#Action in a controller
def outer_space
user = User.find(params[:id])
user.board_rocket_to_the_moon
end
#user model
def board_rocket_to_the_moon
#put on space suit, climb in rocket, etc.
end
Now, I want to add to this by only sending users to the moon if they like to travel.
Is it better to put the if statement in the controller or the model and why?
#option 1: Put an if in the controller
def outer_space
user = User.find(params[:id])
user.board_rocket_to_the_moon if user.likes_to_travel
end
#option 2: Stick the if in the user model
def board_rocket_to_the_moon
if self.likes_to_travel
#put on space suit, climb in rocket, etc.
return "BLAST OFF"
else
return "There is no way THIS dude is getting on THAT ship."
end
end

According to the SRP, I'd stick to option 1.
Controller is the conductor: it's responsible for the logic and it's more readable.
An alternative would be to create a well named method in your model which would handle the logic and trigger the other method if needed.
Don't forget the tests!

Condition in the model will be better.
But here it depends on requirement.
If you need to display whenever the method calls, it would require in the model.
Only from this action call, you require to display message then condition in controller is good.

Related

How to implement controller in order to handle the creation of one or more than one record?

I am using Ruby on Rails 4.1. I have a "nested" model and in its controller I would like to make the RESTful create action to handle cases when one or more than one records are submitted. That is, my controller create action is:
def create
#nester = Nester.find(:nester_id)
#nesters_nested_objects = #nester.nested_objects.build(create_params)
if #nnesters_ested_objects.save
# ...
else
# ...
end
end
def create_params
params.require(:nesters_nested_object).permit(:attr_one, :attr_two, :attr_three)
end
I would like it to handle both cases when params contain data related to one object and when it contains data related to more than one object.
How can I make that? Should I implement a new controller action (maybe called create_multiple) or what? There is a common practice in order to handling these cases?
Well, if you insist on creating those records aside from their nest, I can propose to go with something like this (it better be a separate method really):
def create_multiple
#nest = Nester.find(params[:nester])
params[:nested_objects].each do |item|
#nest.nested.new(item.permit(:attr_one, :attr_two, :attr_three))
end
if #nest.save
....
else
....
end
end

Scoping authorization to current devise user?

My Rails 3.2 project has a devise-generated user and a set of models that all contain data that's specific to that user. I want a logged-in user to be able to access only his own data through the APIs exposed by the controllers.
Now, a brute-force way to enable this would be to change each and every controller from something like:
def index
#stuff = Stuff.all
to
def index
#stuff = Stuff.find_all_by_user_id current_user.id
And I have to repeat this for every single action of every single controller. Is there perhaps a more succinct and DRY way of achieving the same effect? The amount of boilerplate I have to write feels wrong.
Thanks!
Take a look at the CanCan gem.
a) You can have a before callback in application_controller.rb that looks something like
def find_stuff_from_current_user
#stuff = Stuff.find_all_by_user_id current_user.id
end
And than call this in every controller like this:
before_filter :find_stuff_from_current_user
Now you have #stuff variable available in every controller and in every action.
b) Or you can use scoping in stuff model.rb where you say something like:
scope :stuff_from_current_user, where(:user => current_user)

How do I respect RESTful methods when using find_or_initialize_by in Rails 3.2?

I have a resource in my project that collects some information from a user. Basically it's a form that they fill out before they can access another area of the site. It then sets a cookie for a week, but if they come back it will look up their previous entry and keep their preferences tied to them (and will update any details as long as the email address matches).
Currently I have a Applicants controller that looks like this:
class ApplicantsController < ApplicationController
...
def create
#applicant = Applicant.find_or_initialize_by_email(params[:applicant])
if #applicant.new_record? ? #applicant.save : #applicant.update_attributes(params[:applicant])
set_cookie_and_redirect
else
render 'new'
end
end
def update
if #applicant.update_attributes(params[:applicant])
set_cookie_and_redirect
else
render 'new'
end
end
end
The set_cookie_and_redirect is a private method that just sets some cookies and redirects the user to a page. The code works, but it just feels dirty. It's essentially updating a record within the create method under the condition that it's not a new record. I'm also forced to have an update method in case an existing record comes back with a validation error--the form helper will then switch the form over to sending to the update method.
So to my point... is there a more appropriate way to push the update_attributes call in the create method to the update method? Or better put, is there a better way to respect the RESTful methods in isolating the create and update functionality?
UPDATE: I wanted to be a little more specific too. If the user has filled this form out before it will set a cookie so they don't have to fill it out again for seven days. However after seven days the cookie is expired and they see the form again. The controller doesn't know if the user is new or existing until they add user input into the form which is then compared based on the email address.
Thanks in advance! I definitely look forward to anyone's thoughts on this.
The create method should only create, and the update method should only update. Let Rails decide which is going to happen based on what is inside of #applicant when the form is rendered - It essentially does what you're doing: Checks if the record is new or not, and sends it to update/create accordingly. Example:
def applicant
#applicant = Applicant.find_or_initialize_by_email(cookies[:email])
# renders applicant.html.erb form
end
<%= form_for #applicant do |f| %>
# ... fields ...
<% end %>
def create
#applicant = Applicant.new(params[:applicant])
#applicant.save
# .. etc.
end
def update
#applicant = Applicant.find_by_email(cookies[:email])
#applicant.update_attributes(params[:applicant])
# ... etc.
end
Rails will send the request to the correct action based on the new_record? status of the Applicant object.

Rails getting started wizard in separate controller from users controller?

I have a Rails site that requires a lot of form fields that need to be filled out after the user first signs up (using a large jQuery wizard). At first, I wrapped all the "getting started" (executed when the user logs in for the first time) specific code in the users controller like this:
Class UsersController < ApplicationController
def new
#user = User.new
end
def getting_started
def getting_started
#user = User.find(current_user.id)
unless #user.employees.length == 15
15.times { #user.employees.build }
end
end
end
My question is, should I separate out the getting started method into it's own controller if the getting started method is beginning to grow rather large? What is the "rails way" of doing this?
Size isn't what dictates a new controller--controller purpose dictates a new controller. If it's not related to a User and is entity-like on its own, new controller. If it's just more User data, it should stay.
If it's simple size you're concerned with, it depends. If it's code acting directly on a User, it may belong in the User model. If not, it belong in private methods or its own library.
Without further details regarding what getting_started actually does, it's difficult to be more specific.

Where do I put the Current user query so as to not repeat per controller?

I have a standard query that gets the current user object:
#user = User.find_by_email(session[:email])
but I'm putting it as the first line in every single controller action which is obviously not the best way to do this. What is the best way to refactor this?
Do I put this as a method in the Application controller (and if so, can you just show me a quick example)?
Do I put the entire #user object into the session (has about 50 columns and some sensitive ones like is_admin)?
Or is there another way to remove this kind of redundancy?
I suggest making it into a helper placed in the ApplicationHelper module
def current_user
return nil if #user === false
#This ensures that the find method is only called once
#user = #user || User.find_by_email(session[:email]) || false
end
I prefer the above usage instead of the standard #user ||= User.find... because it prevents repetitive queries if the user record isn't found the first time. You could also just bang the find method: find_by_email! to make it throw an exception when the user can't be found
You could specify a before_filter, which is automatically called at the beginning of every controller action. Read up on it to see how to use it.

Resources