Adding variables from object passed from model to controller - ruby-on-rails

I have a Person model. When a new Person is created, I want to set a random_variable in the controller and pass it as part of the #person object. I have tried
Model
attr_accessor :random_variable
Controller:
def create
#person = Person.new(params[:person])
#person.random_variable = 'A Random string'
#person.save
end
Unfortunately, if I try and access self.random_variable in the model, this doesn't work (I get nil).
Can someone explain why this doesn't work, and how to go about doing it? (and yes, I know this doesn't really hold with MVC convention, but the only other way of doing what I need is a very heavy non-dry controller)

What you are describing should work. How are you determining that the random variable is in fact nil? Have you tried using the debugger?

Have you tried just using update_attribute?
update_attribute(:random_variable, 'A Random string')
Or, maybe jump into script/console to see exactly what's happening.

Something weird is going on. I should have explained, as Brad suggested, that I am trying to access random_variable inside a setter method for another object. I had understood that params was handed to the controller, than the model only accessed once #person.save was called. For some reason the setter seems to be being hit before the controller.
I am basing this on the fact that when I accidentally put a debugger statement in the model setter method and controller at the same time, the model debugger was triggered first. I am clearly going to have to do some reading unless someone can explain this. Unfortunately I need to get this working tonight, so I will be going for a hack workaround for the time being

Related

Create or replace from the model in Rails

In Rails, I would like to be able to have a model that checks if a value for a column signature already exists, if it does not, save it, if it does, update the existing model and then exit out. This has to be a model solution. I cannot use controller logic to achieve this, it has to be automatic.
I have tried using filters like before_create or before_save, but it doesn't seem like there is a clean way to stop the filter chain and update an existing record by those means. Any help is appreciated. Thanks.
The code below is for a method that you could use instead of save. It checks if a model exists with the same signature. If it finds one, it updates that existing model with the attributes and then return that model. If there is no other model that has that signature, it will continue on saving. You will have to use this in place of ActiveRecord's #save in places where you want this behavior. There was no way to do this using callbacks since the only way to cancel saving in a callback was to have it return false.
def save_signature
model = Model.find_by(signature: signature)
if model
model.update(attributes)
model
else
save
end
end
Let me know if I understood what you wanted. If not, then just point out what I misunderstood and I'd be happy to change my answer accordingly.
Maybe not the cleanest way but why to stop the chain?
fetch the object on before_* filter, do the check and update the model, it will be saved with the old value.
what do you think?
Read about find_or_create_by http://guides.rubyonrails.org/active_record_querying.html#find-or-create-by . This will either create a new record or output you the previous one. Hopefully it may work for you

Pass object or id

This is just a question about best practices.
Imagine you have a method that takes one parameter. This parameter is the id of an object. Ideally, I would like to be able to pass either the object's id directly, or just the object itself.
What is the most elegant way to do this?
I came up with the following:
def method_name object
object_id = object.to_param.to_i
### do whatever needs to be done with that object_id
end
So, if the parameter already is an id, it just pretty much stays the same; if it's an object, it gets its id.
This works, but I feel like this could be better. Also, to_param returns a string, which could in some cases return a "real" string (i.e. "string" instead of "2"), hence returning 0 upon calling to_i on it. This could happen, for example, when using the friendly id gem for classes.
Active record offers the same functionality. It doesn't matter if you say:
Table.where(user_id: User.first.id) # pass in id
or
Table.where(user_id: User.first) # pass in object and infer id
How do they do it? What is the best approach to achieve this effect?
If the process is cross controller actions or in session, better to use id. For example, you are going to save a cart in session, the better choice is id. It's hard to watch how big an object is, using id will help performance and avoid unnecessary error.
However, if the methods are inside same request and all actions are within memory, using object itself would be quicker. For example, you pass an user to an authority class to check if he can do something. Because all objects are just a reference in memory, extra step to extract id is unnecessary and inefficient.
My guess is that ActiveRecord does something like this. Or, rather, that's how I'd do it.
def my_method(oid)
oid = oid.id if oid.respond_to?(:id)
# proceed
end

Where and how to put Model query methods in Rails 3?

i'm looking for an elegant way to make a model method call. Here is my query:
where("order_id = ?", argument).first
I can't use this query in a scope, because of the first() method, which is chained after the where() method.
So I tried to create the following class method:
def self.find_order(value)
where("order_id = ?", value).first
end
When I use this method twice in my controller action, I get a warning from my IDE, that I should move this query into a scope. When I rename the method without 'find' in the beginning, I also get a warning in my controller, that I shouldn't call more than one method besides find() or new() (I have a another model method calling in this controller action). I know, it isn't a real problem because it works, but I'm interested in writing nice and dry code. I hope it isn't an annoying question, I should fairly say that I'm relatively new to programming.
Thanks in advance!

What are best ways to consolidate instance variables (to pass data from controller to the view) in Rails?

I have a details page in Rails 3 app that has several instance variable. For example, my view has the following instances:
#product
#program
#action
...etc...
Some of them are single strings, some are arrays (result of requests from external APIs). Overall, the data comes from at least 3 models.
My question is: What is the best way to consolidate instance variables so my view and my controller look cleaner (without sacrificing readability)? I understand the question is open-ended and everyone has their own preferences, but what are some of the options? I am thinking along the lines of combining the instance variables into an object so the previous code becomes something like this:
Details.product
Details.program
Details.action
Thanks a lot!
UPDATE: Going the object route, is there any good examples (github, blog posts) of actually implementing it?
In my opinion the best way to clean up your controller actions is to assign as few instance variables as possible there. In practice, I try to only assign instance variables in the action/before_filters that are directly derived from the parameters passed to it. Also, in general I try to push as many instance variable assignments to before_filters as I can so as to keep the actions slim.
But the best way to cut down on instance variable assignment in the actions is to use view helpers. For instance if you are currently assigning an instance variable in an action which you use to output some div with, just use a view helper to do the output directly without any need to pass an object to the view.
Here's an example of a before_filter on a controller:
before_filter :assign_variables, :only => :show
def show; end
private
def assign_variables
#product = Product.find(params[:product_id])
#program = Program.find(params[:program_id])
#action = Action.find(params[:action_id])
end
I would suggest not using instance variables at all and go with the decent exposure gem instead.
https://github.com/voxdolo/decent_exposure
http://railscasts.com/episodes/259-decent-exposure
Whenever you find yourself with several instance variables in a controller action, it best to use the presenter pattern.
This video explains everything in details
"Ruby on Rails - Presenters & Decorators"

Do methods that help find data in a db such as "find", "find_by", "where" etc belong in the model or controller in ruby on rails?

I've finished a personal project now just going through my code cleaning things up. I'm wondering if methods that help find things in the database belong in the model?
E.g.
This was in my controller:
#user = User.find_by_username(username)
I then moved it to my model:
class << self
def find_user_by_username(username)
User.find_by_username(username)
end
end
added this to my controller:
#user = find_user_by_username(username)
Is there anything wrong with this? does it really matter if I have find, where and other methods that help find things in my controller? What about putting them in helpers?
Another thing is I tried to call that same method in a show action and pass in params with a users username as the value. I get:
undefined method `find_user_by_username' for #<UsersController:0x000001034a6060>
I just want to clean up but not break things. I don't understand why that method would work fine in my new action but not in show action.
Thanks in advance
kind regards
In a good design, you want to have the skinniest controller possible, and that means moving a maximum of code from the controlelr to the model. Then, if your model become too large, there are other technique to move code down the model to other layers (libs, observers, etc).
The find_by_* method is already in the model but it his a class method. So it's perfectly reasonable to call it from your controller.
If your search was not a simple find but , let's say, a search by user.username or user.company.name , then you would probably have to make that search method in the model and call it from your controller.
This way also allows you to call that method from different controller instead of copy/paste-ing it
More info on where to put your code can be found here : http://qualityonrails.com/archives/33
The controller is the perfect place for calls to your model's methods. It's not the perfect place for model logic, though.
Hint: the perfect place for that begins with 'M', ends with 'odel'.
The controller should call User.find_by_username. There should be no find_by_username method for the controller itself, because that's one layer of abstraction too many and "hides" what exactly the find_by_username method is doing.
Call the model method from your controller. You're obsessing about cleaning up when you don't need to.
Your original code is perfectly fine, it is only when you starting chaining methods in the queries that you need to consider refactoring.
ie Refactoring this
User.where(:age => 0..25).where(:owns_a_dog => true).includes(:dogs)
into
User.young_dog_owners
The reason you're getting an undefined method error is because what you defined is still a class method, so you need to call it as such:
#user = User.find_user_by_username(username)
However, you'll notice this isn't really any better than:
#user = User.find_by_username(username)
In general, my feeling is that a simple find(id) or find_by_xxxx(xxxx) is ok to have in a controller, but more advanced logic should be moved to the model. For example, if you have something like User.where(:activated => true).where("created_at > ?", Date.today - 1.week) you would probably want that to be moved to your User model under a find_recent_users method or something.

Resources