warning message when running specs - ruby-on-rails

I have the following:
def toggle_follow_user tmp_id
user=User.find(tmp_id)
but when I run specs I get the following warning:
DEPRECATION WARNING: You are passing an instance of ActiveRecord::Base to `find`. Please pass the id of the object by calling `.id`.
what is it trying to tell me and how do I fix it?

Just for the sake of a test... I'm not sure what you are passing in that gets assigned to the tmp_id parameter, but I would surmise its an active record object, can you try this?
def toggle_follow_user tmp_id
user=User.find(tmp_id.id)
end
If that works, its because whatever you were sending into toggle_follow_user was an entire active record "row" where Find wants an integer now (representing the ID... it used to pull it out natively, but that is going away it seems)

You're calling toggle_follow_user(a_user), not toggle_follow_user(a_users_id). Try this:
def toggle_follow_user(user)
# just delete the line where you ::find the user, you're already passing it in
This will fix this particular issue - but check that it doesn't break anything else in your code!
If you are wanting to pass the user's id and find it inside the method, then look a call or two down your stack trace, because it's the whole object that's being passed in.

Related

Execute a dynamic method using eval or public_send

I have these code that executes a dynamic method. I'm using eval here to execute it but what I wanted to do is changed it to public_send because I was told so and it's much safer.
Current code:
# update workstep logic here.
incoming_status = params[params[:name]]
# grab workflow, this is current data, use this to compare status to in comming status
workflow = get_workorder_product_workstep(params[:workflow_id])
# check current status if its pending allow to update
# security concern EVAL!
if eval("workflow.can_#{incoming_status}?")
# update status
eval("workflow.#{incoming_status}")
# updated attribute handled_by
workflow.update_attributes(handled_by_id: #curr_user.id)
workflow.save
else
flash[:notice] = 'Action not allowed'
end
The eval here is the concern. How can I changed this to public_send?
Here's what I did.
public_send("workflow.can_#{incoming_status}?")
public_send("#{workflow}.can_#{incoming_status}?")
both of them doesn't work. gives me an error of no method. The first public error returns this undefined method workflow.can_queue? for #<Spree::Admin::WorkordersController:0x00007ff71c8e6f00>
But it should work because I have a method workflow.can_queue?
the second error on public is this
undefined method #<Spree::WorkorderProductWorkstep:0x00007ff765663550>.can_queue? for #<Spree::Admin::WorkordersController:0x00007ff76597f798>
I think for the second workflow is being evaluated separately? I'm not sure.
Working with public_send you can change the relevant lines to:
if workflow.public_send("can_#{incoming_status}?")
# update status
workflow.public_send(incoming_status.to_s)
# ...
A note about security and risks
workflow.public_send("can_#{xyz}?") can only call methods on workflow that are public and which start with the prefix can_ and end with ?. That is probably only a small number of methods and you can easily decide if you want to allow all those methods.
workflow.public_send("#{incoming_status'}) is different because it allows all public methods on workflow – even destroy. That means using this without the "can_#{incoming_status}?" is probably a bad idea. Or you should at least first check if incoming_status is in a whitelist of allowed methods.
eval is the worst because it will evaluate the whole string without any context (e.q. an object like workflow). Imaging you have eval("workflow.#{incoming_status}") without to check first if incoming_status is actually allowed. If someone then sends an incoming_status like this "to_s; system('xyz')"then xyz could be everything – like commands to send a hidden file via email, to install a backdoor or to delete some files.

Missing param even though I can see it IS there

This is my params as seem in the rails abort() screen:
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"ptXYHkAUh/uvK9blLdcPiarvCYKHJ1HWhqw+dByy7PQ=",
"account"=>{"name"=>"Hokuriku",
"amount"=>"0",
"is_default"=>"1"},
"commit"=>"Save",
"id"=>"5"}
See "is_default". But, when I do:
def update #accounts controller method
abort(account_params.inspect);
.
.
.. in the controller, it only shows:
{"name"=>"Hokuriku", "amount"=>"0"}
I can't see 'is_default'. Btw this column is also a newly added column. I have migrated though, and I can confirm the new column exists. Also, I've managed to output the value of that column to the the previous screen so I know that the model is handling it.
To fix it, I do the following abort:
abort(params[:account][:is_default].inspect); # outputs "1"
.. and now I can see it. So it does exist.
Any ideas what could cause this to happen? Ideally I want to handle it in the simplest cleanest way possible, as well as understand exactly what account_params is as it doesn't seem to be the same as params[:account:]. Thanks
I'd bet that it's the account_params method that does the filtering. Whereas in params[:account] you access raw unfiltered data.
Look at your account_params method. It contains a number of instructions to ignore passed params (for security reasons).
It most likely have a form:
params.require(:account).permit(:name, :amount)
require will raise an exception if params do not contain given key and returns matching hash. Permits silently removes all the keys not listed in arguments.
You can read more about strong attributes on github: https://github.com/rails/strong_parameters

Rails ActiveRecord callbacks

I'm having an issue with a date format. I have a time picker that has the date in a funky format (well, it's a nice format, actually, but not to the computer). I'm trying to have Chronic parse the date so that it can be saved properly.
At first, I was doing, in the create action of my controller:
params[:event][:start] = Chronic.parse(params[:event][:start])
but if and when validation fails, it sends the parsed value back to the view, and my datetimepicker is all botched, then.
So, I thought... callback? In my model, I added:
private
def date_change
self.start = Chronic.parse(self.start)
end
I tried before_save, before_validation, after_validation... but nothing seems to get that date formatted correctly.
As it stands, I keep getting ArgumentError in EventsController#create - Argument out of range. I assume that's because the database is expecting a properly formatted datetime object.
Any idea on how I can accomplish my goal, here, of not changing the params, but still being able to save a properly formatted object?
I'm guessing that the problem is occurring the the start= mutator method that ActiveRecord supplies. If you're doing things like this in your controller:
#event.update_attributes(params[:events])
#event = Event.create(params[:event])
#...
then create and update_attributes should call start= internally. That should allow you to put the Chronic stuff in your own start=:
def start=(t)
super(Chronic.parse(t))
end
You might need to adjust that for non-String ts, I'm not sure what Chronic.parse(Time.now), for example, would do. You could also call write_attribute(:start, Chronic.parse(t)) or self[:start] = Chronic.parse(t) if you didn't want to punt to super.
Note that before_validation and similar handlers will be called too late to bypass whatever default string-to-timestamp conversion ActiveRecord is doing but a mutator override should happen at the right time.
Alternatively, you could parse the time in the controller with something like this:
event = params[:events].dup
events[:start] = Chronic.parse(events[:start])
#event = Event.create(event)
Assumption is the mother of all mess ups :)
are you sure the callback is hit? Because if it would, and the error occurred (like it did), wouldn't it still send back the incorrect data (because parsed) back to the view? In case of doubt: log something to make sure it is hit.
are you sure which field causes the Argument out of range error.
Most cases bugs are so hard to find/fix because we assume we know the error, but we are looking at the error in the wrong way.
Easy ways to test which attribute causes the error:
open rails console, build an object with the parameters, save it, and ask the errors. Something like
e = Event.new(params[:event]) # copy params verbatim from your logfile
e.save
e.errors
and that will display which field causes the error.
Alternatively: use pry and add a line binding.pry just after the save, so you inspect the errors (more info)
Answer (assuming your assumption was correct)
I see two options to do what you want:
use the after_validation callback, if you are sure the data will always be correct, and correctly parsed by Chronic. This way if validation is passed, then convert the field and normally nothing can go wrong anymore, and the value is never sent to the browser again.
Note: if some other attribute is causing the error, this callback is never hit, of course. Because it does not pass the validation.
use a virtual attribute, e.g. start_str, which is a visual representation of your start, and
before_save convert it to start. It does not really matter that much here, because if validation fails, you just show start_str and not the "real" start field.

Help with ruby and def respond_to?(method,*args, &block) in library

I have a Gem that deals with images that get modified. I want to modify it to update the old image but, I need to be able to get the object id.
Right now it uses the following code:
def respond_to?(method,*args, &block)
puts ("++++METHOD #{method.to_s} ARGS #{args} BLOCK #{block}")
args.each do |value|
puts ("ARGS #{value}")
end
but I don't know how to get id from something I pass in nor do I know how to pass the id in, I've tried
#asset.image.s_245_245(:asset_id=>#asset.id) with no success. Args returns nothing. What am I doing wrong?
Update: I am currently reading http://www.simonecarletti.com/blog/2009/09/inside-ruby-on-rails-extract_options-from-arrays/
Update: This too returned blank.
Your question is very unclear. How is the method respond_to? related to your problem with object id?
I am guessing that in reality you wanted to override method_missing, because now you do not call respond_to?, or it is not shown in your examples.
If you have not defined such method, calling image.s_245_245 will trigger method_missing (in the image object) with the parameters you used for respond_to?.
There is a rule, which says that if you use method_missing to handle some calls, then you should also modify respond_to?, and make it returning true when asked for the methods handled by method_missing.
As for object ID, there are two possibilities:
Every object in ruby responds to .object_id (which returns an internal identifier of every object)
ActiveRecord objects respond to .id (which is a primary key in the database).
This is just a side-note, because I suppose that if you start experimenting with method_missing instead of respond_to? you will know which one you want.

How can I see the details about the object in server log?

I am trying to debug the user object created by writing ruby code like
puts user
which then I can check it on the server log.
Apparently, the server log says something like
#<User:0x3b53440>
but it does not show details about the user object. (for example, its name or email values)
How should I modify the code so that the detail information about object will be produced?
I want some function in ruby that does similar job as PHP's print_r or var_dump.
Try using the Object.inspect method:
puts user.inspect
Here's the documentation: http://www.ruby-doc.org/core/classes/Object.html#M001025
Often I'll write my own inspect or to_s method for an object, to provide me the view into the object that I want.
If Ruby can't find either of those methods for an object, it'll return the object's ID, and nothing more, because it doesn't know what else to do.

Resources