Why won't my params pass to my model? - ruby-on-rails

I am trying to pass a simple variable in my params to a class method, however it doesnt seem to work. This seems elementary, but I'm still learning. Can someone explain why this doesn't work and offer an alternative? My code is below.
controller
#profile = current_user.profile
#dogs = Dog.by_profile(params[#profile])
model
def self.by_profile(profile)
Dog.where(kids_under_10: profile.kids_under_10 )
end
*note: profile.kids_under_10 is a boolean. When I manually replace it with true or false, everything works fine.

params is a special rails hash that contains url parameters. So your code is looking for a url parameter passed with the request containing the string version of your user profile. This is definitely not what you want to be doing.
When you're calling a rails model method, you call it with arguments like any other method: Dog.by_profile(#profile)
You don't want the params part, or you're trying to do something crazy that should be refactored :)

Your key value for params should look like params[:profile].
So try #dogs = Dog.by_profile(params[:profile]).

Because params is a hash that comes from a request. What you are doing is trying to search the hash with an instance variable which is wrong.
I think what you meant is to do params[:profile] or just #profile

Related

Can't use rails params even though I can see them. Specifically charge_id

I've been working on hooking up my Rails, Shopify embedded app to the Shopify billing API. It seems simple enough:
You create a new Shopify RecurringApplicationCharge object through the API, submit it to Shopify.
They return the object with additional fields filled in, including a 'confirmation_url' which you can redirect to.
This confirmation URL has a charge_id param, which you can use to activate the billing.
I can get through the above fine and can even see the 'charge_id' param in the log as well as when I run params.inspect . However, I can't use it. Every time I try and assign params to an instance variable I get 'nil class'.
Controller
class HomeController < AuthenticatedController
before_action :require_login, except: [:activate_charge]
skip_before_action :verify_authenticity_token, only: [:activate_charge]
before_action :require_subscription, except: [:activate_charge]
def require_subscription
unless ShopifyAPI::RecurringApplicationCharge.current
#recurring_application_charge = ShopifyAPI::RecurringApplicationCharge.new
#recurring_application_charge.name = "Monthly Plan"
#recurring_application_charge.price = 15.0
#recurring_application_charge.return_url = "https://xxxxx.ngrok.io/activatecharge"
#recurring_application_charge.test = true
#recurring_application_charge.trial_days = 7
#recurring_application_charge.save
if #recurring_application_charge.id
##tokens[:confirmation_url] = #recurring_application_charge.confirmation_url
fullpage_redirect_to(#recurring_application_charge.T)
end
end
end
def activate_charge
#params = params[:charge_id]
##recurring_application_charge = ShopifyAPI::RecurringApplicationCharge.find(params[:charge_id])
##recurring_application_charge.status == "accepted" ? #recurring_application_charge.activate : redirect_to('/')
##recurring_application_charge.save
redirect_to('/')
end
def recurring_application_charge_params
params.require(:recurring_application_charge).permit(:name, :price, :capped_amount, :terms, :trial_days, :charge_id)
end
params.inspect
<ActionController::Parameters {"charge_id"=>"12271520309", "hmac"=>"f7a75bba157ef40b3144d8d0e8ceb8b37628ea294fbe16887b623c0d311d84da", "locale"=>"en-GB", "session"=>"f8b8ef448a1d9883e4884c858e0bc39d789d53d2eeffffb34c42f5bc390af6f1", "shop"=>"xxxxx.myshopify.com", "timestamp"=>"1578555752", "controller"=>"home", "action"=>"activate_charge"} permitted: true>
Some clues
I have an API controller which I use for other things, this is not authenticated by Shopify or Clearance. I can get 'charge_id' param if I use this controller (although this controller cant be use for other reasons).
I've played around with trying to make sure strong params allow for it, but I'm not writing to a model, so this shouldn't matter?
On an older Shopify page on how to set billing up, they have this line of code in the first object creation phase, that I haven't been able to implement:
#tokens[:confirmation_url] = recurring_application_charge.confirmation_url
Any help would be great, I can't figure out if this is a really simple problem or whether there's an authentication issue I'm completely oblivious to.
Thanks!
Edit
The Return, when params.class and params.inspect is called
ActionController::Parameters
<ActionController::Parameters {"charge_id"=>"12284756045", "hmac"=>"9ff4d8a30d623f2d03d0226ff3d98c780e451615d630c149697e5b2dd4ffd4b5", "locale"=>"en-GB", "session"=>"f8b8ef448a1d9883e4884c852e0bc39d789d53d2eeffffb34c42f5bc390af6f1", "shop"=>"xxxx.myshopify.com", "timestamp"=>"1578737591", "controller"=>"home", "action"=>"activate_charge"} permitted: false>
Another piece of info, if I add the following to the controller:
params = params.to_h[:charge_id]
Then call params["charge_id"], from the view I can get the charge_id returned.
However, when I try and assign it to #charge_id in the controller, then try and call #charge_id in the view to test it, it returns nothing :S
params = params.to_h[:charge_id]
#charge_id = params["charge_id"]
The ActionController::Parameters use "charge_id", not :charge_id. Maybe you should try that. Or work with params.to_h[:charge_id], as to_h will return a HashWithIndifferentAccess.
params convert your id to string while you may be assigning it to an id which is not string which means you should:
#params = params[:charge_id].to_i
Share more details and I guess we will be able to solve this issue.
UPDATE (Debugging help):
For beginner controller debugging (like you prefer, I guess), before your line where you are getting #params = params[:charge_id]:
puts "*"*100
puts params
puts "*"*100
Now go to console where your server is running and after reloading the page (in browser), Ctrl+Shift+F and enter *******. This will find the place where your params are printed. GLHF
Ugh, anticlimax.
I noticed that none of the code in the activate_charge method was actually running. I then figured out that having the require_subscription and activate_charge method in the private section of my controller was causing the issue.

Rails - Mass-Assignment ForbiddenAttributesError for nested attribute

I'm having an issue with mass assignment for nested attributes. I'm having a hash in params that represents an object that results from a form_for form.
I tried to authorize the params like this but I get the following error...
ActiveModel::ForbiddenAttributesError
for
params.require(:country).permit(:language, :flag_path)
#country.update(params[:country])
Here is the params array :
{"utf8"=>"✓",
"authenticity_token"=>"xxxxxxx",
"country"=>{"language"=>"xxxx",
"flag_path"=>"xxxxx"},
"commit"=>"Update",
"country_id"=>"xxxx"}
Thanks for your help.
EDIT : I know it is possible to user permit! but if I understand correctly, this authorize all parameters for the ressource and I'd like to permit only some of it.
There are two problems in your code:
1st:
Consider if below is your permit param method in your controller :
def country_param
params.require(:country).permit(:language, :flag_path)
end
then your update action should be like this:
#country.update(country_param)
not
#country.update(params[:country])
2nd: Why you have country_id in your update action. It should be id instead.
It's not big one both will work. But with country_id You will not reach to proper edit action.
Anyway according to your params your action should be look like:
def update
#country = Country.find(params[:country_id])
#country.update(country_param)
end
private
def country_param
params.require(:country).permit(:language, :flag_path)
end
What you are doing with params.require(:country).permit is the correct way to do it. ActiveModel::ForbiddenAttributesError will be raised if any one of the params is not permitted so it is likely that you have missed off one of the params.
Check all of the params in your params hash and make sure that they have been permitted, it looks like commit and contry_id are missing, for example.

Why does Rails use the params variable rather than passing arguments through the function?

As title says, why does Rails prefer to use the #params variable inside of a Controller action when you are responding to the action instead of passing the individual parameters through the function arguments when we call the function?
Other frameworks use this (i.e, ASP MVC) and I was just wondering if there was a reason for that design decision, because it doesn't seem very intuitive.
Ie. Why does Rails do
def index
name = params[:name]
end
Instead of
def index(name)
end
The point is, most of the actions in a controller handles the view REST-fully. The params comes from the user's browser when they interact with the page or send a new variable request to the page.
These requests are variable, and Rails makes it uniform by maintaining the parameters in params hash. If the following GET requests arrive:
http://localhost:3000/products?color=red&quality=best
the params hash will automatically be populated as {'color' => 'red', 'quality' => 'best'}. Rails doesn't expect your action to manually handle the parameters.
Similarly, consider you are getting a POST request from a page where a user filled a form. In that scenario, the params obtain the parameters which are composed with form helpers inside views.
Though in hyptothetical case you are dealing with general methods instead of actions, such as below, you will have to do it by passing arguments.
def show
if params['color'] == 'red'
#product = obtain_product('red')
else
#,..
end
end
def obtain_product(color)
Product.where('color = ?', color).first
end
Hope it is clear. :)
#kidorrails has a great answer, and I wanted to add to it:
If you wanted to pass the params to each method directly, it would go against the #1 Rails convention - keep it DRY. By having a separate params hash, you not only have access to all the params you want, but you can access them through as many methods as you need
For example, take strong_params:
#controller
def new
#model = Model.new
end
def create
#model = Model.new(strong_params)
#model.save
end
private
def strong_params
params.require(:model).permit(:your, :params)
end
As #apneadiving mentioned, the params hash is created in another part of the stack, meaning it's available over all the methods required. It's most efficient & versatile way to do it IMO

Forcing Lowercase URL Parameters in Rails

I am developing an app in Rails 3.2 that uses the to_params to change the URL/route to a custom one.
The to_params in the model is something like this:
def to_params
keyword
end
Then, in the controllers, I look up the object using:
def show
#object = Object.find_by_keyword(params[:id])
end
I also have a before_save in the model that ensures all keyword entries are lowercase, so the URLs come out like http://mydomain.com/object/keyword.
My question is... Some users might be tempted to capitalize a keyword or something when putting it in the URL themselves. How can I convert that URL into lowercase before trying to find the object in the controller? I've tried #object = Object.find_by_keyword(params[:id].lowercase), but it didn't seem to work.
Any help would be greatly appreciated!
#object = Object.find_by_keyword(params[:id].downcase)
Should work

Rails: Iterate dynamically over params hash to verify they exist

I've got a form with quite a bit of params being passed to the controller for processing. The different 'sets' of params are named in a similar fashion:
setname1_paramname
setname1_paramname2
Now, I need to check one of these 'sets' to verify that all of the fields are submitted. Right now, I'm doing this with a manual If Or style statement:
if setname1_paramname.blank? || setname1_paramname2.blank? || ...etc
#object.errors.add_to_base("All setname1 fields are required.").
render :action => 'new'
return false
end
Is there way to programmatically loop over these params, and add them to the #object errors?
Thanks!
Since it sounds like you have a ton of params and also seems like you need to be able to do checks on groups of params, maybe something like this would be useful? Basically, iterate over the params hash, and use regular expressions to target sets of params. Then, inside the loop, you can do any sort of validations:
params.each do |key, value|
# target groups using regular expressions
if (key.to_s[/setname1.*/])
# whatever logic you need for params that start with 'setname1'
if param[key].blank?
#object.errors.add_to_base("All setname1 fields are required.").
end
end
end
If the names are arbitrary and of your own choosing, you could make virtual attributes for them in your model and let Rails handle the presence checking.
class SomeModel < ActiveRecord::Base
VIRTUAL_ATTRIBUTES = [:billing_address, :billing_state, :something_else]
attr_accessor *VIRTUAL_ATTRIBUTES
validates_presence_of *VIRTUAL_ATTRIBUTES
…
end
Is there a reason you wouldn't just store this information in a model, even if temporarily, and then just use rails validations for your information?
I'm rusty but I assume that even if the value is blank the param will still be returned in the params hash as long as it is coming from a form element, yes? Could you just iterate through the params hash and keep a counter of how many values are not blank and then compare the length of the params hash to the counter. If the counter is short then you have blank parameters and can handle the error that way without having to hardcode checks for each individual parameter, yes?
If what you need is a multi-step form as I suspect, you may find the Railscast on Multistep Forms to be useful

Resources