I have a params hash that looks like this:
puts contact_params
=> {"classifiable_classification_codes_attributes"=>{"0"=>{"id"=>"5", "relateds_attributes"=>{"0"=>{"classifiable_id"=>"6", "id"=>"15"}}}}}
So I expect when I do this contact_params["classifiable_classification_codes_attributes"], I subsequently get {"0"=>{"id"=>"5"...
In fact, that is exactly what happens in the console:
> contact_params["classifiable_classification_codes_attributes"]
=> {"0"=>{"id"=>"5", "relateds_attributes"=>{"0"=>{"classifiable_id"=>"6", "id"=>"15"}}}}
However, in the controller when I try do this, it returns a nil value, as if classifiable_classification_codes_attributes is not a key. I also tried the symbol form :classifiable_classification_codes_attributes. But neither of them return any results.
What might I be doing wrong?
I'm guessing this might be a Strong Parameters issue in which case you need to add something like
private
def my_params
params.require(:classifiable_classification_codes_attributes).permit(: id, :relateds_attributes)
end
Read more at: http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters
Related
I have an array of parameters and i want to replace all parameters by looping over array in ruby on rails.
I am using before_action in controller as
before_action :cost_format
This is an array of parameters. The format of the parameter is string, i want to run a function on each parameter and convert it into float. So i am looping an array and calling function on each element.
def cost_format
x = [params[:cost_1], params[:cost_2], params[:cost_3]]
x.each do |i|
convert_cost(i)
end
end
I have a function convert_cost as following
def convert_cost(x)
x.gsub(',', '.').to_f
end
How do i loop an array so that my parameters get replaced.? Parameters are not being replaced by above code. Any hint would be appreciated.
Thanks
I think you'll want something like this:
def cost_format
%i(cost_1 cost_2 cost_3).each do |key|
params[key] = convert_cost(params[key])
end
end
def convert_cost(val)
val.gsub(',', '.').to_f
end
This grabs each key from your params and replaces the value with it passed through the convert_cost method.
Edit: you might be able to ignore this section about convert_cost due if it works with the format you're getting your numbers in. Please excuse my ethnocentrism :)
I've not updated your convert_cost method, though I'm a little wary about whether it will work at the moment. If you've got, for example "1,234,567" and you call your gsub you get "1.234.567". Calling to_f on that gives you 1.234, which I wouldn't think you'd want?
Instead of that, you could use:
def convert_cost(val)
val.gsub(',', '').to_f
end
E.G.
convert_cost("1,234,567")
# => 1234567.0
Combining all that, the following would be converted like so:
params = { cost_1: "1,234,567", cost_2: "123", cost_3: "456.5", cost_4: "I won't be touched" }
# after `cost_format` before_action runs:
# => {:cost_1=>1234567.0, :cost_2=>123.0, :cost_3=>456.5, :cost_4=>"I won't be touched"}
Let me know how you get on or if you have any question - hope this helps.
Could you try something like
x.map{|param| param.gsub(',', '.').to_f}
directly on your array.
So your cost_format method will look like:
def cost_format
x = [params[:cost_1], params[:cost_2], params[:cost_3]]
result = x.map{|param| param.gsub(',', '.').to_f}
# do stuff with result
end
I think the reason it's not working because, each method returns its receiver, but map returns a new array.
I'm playing around with Netflix's Workflowable gem. Right now I'm working on making a custom action where the user can choose choices.
I end up pulling {"id":1,"value":"High"} out with #options[:priority][:value]
What I want to do is get the id value of 1. Any idea how to pull that out? I tried #options[:priority][:value][:id] but that seems to through an error.
Here's what the action looks like/how I'm logging the value:
class Workflowable::Actions::UpdateStatusAction < Workflowable::Actions::Action
include ERB::Util
include Rails.application.routes.url_helpers
NAME="Update Status Action"
OPTIONS = {
:priority => {
:description=>"Enter priority to set result to",
:required=>true,
:type=>:choice,
:choices=>[{id: 1, value: "High"} ]
}
}
def run
Rails.logger.debug #options[:priority][:value]
end
end
Here's the error:
Error (3a7b2168-6f24-4837-9221-376b98e6e887): TypeError in ResultsController#flag
no implicit conversion of Symbol into Integer
Here's what #options[:priority] looks like:
{"description"=>"Enter priority to set result to", "required"=>true, "type"=>:choice, "choices"=>[{"id"=>1, "value"=>"High"}], "value"=>"{\"id\":1,\"value\":\"High\"}", "user_specified"=>true}
#options[:priority]["value"] looks to be a strong containing json, not a hash. This is why you get an error when using [:id] (this method doesn't accept symbols) and why ["id"] returns the string "id".
You'll need to parse it first, for example with JSON.parse, at which point you'll have a hash which you should be able to access as normal. By default the keys will be strings so you'll need
JSON.parse(value)["id"]
I'm assuming the error is something like TypeError: no implicit conversion of Symbol into Integer
It looks like #options[:priority] is a hash with keys :id and :value. So you would want to use #options[:priority][:id] (lose the :value that returns the string).
I have the following function to sum all the records of an :amount field in my Pack model for that given user:
user.rb
def total_money_spent_cents
amount = self.packs.map(&:amount).sum
return amount
end
However, when I use this function I receive the following error:
nil can't be coerced into Fixnum
Any suggestions?
EDIT
I am still having issues in regards to Fixnum in my tests, and have another question open here.
This suggests that one of your packs has an amount field which has not yet been set, so is nil. When you try and add it to something else, it undergoes Type coercion, to see if Ruby can massage its type into one that can be added to numbers, but it can't, and so you have this error.
One solution is this:
def total_amount_spent_cents
packs.map(&:amount).compact.sum
end
Array#compact removes the nil elements.
This may be fixing the symptom and not the actual problem though. It could be the case that you shouldn't have nil's in there at all, in which case you should check the initialisation of your Pack model (or perhaps its validations, to ensure that amount is mandatory).
I added some extra methods into Array and Hash for this sort of thing: they're like compact but they remove all values returning true for blank? rather than just nil: so will remove empty strings, empty arrays, hashes etc.
class Hash
def compact_blank!
self.each{|k,v| self.delete(k) if v.blank? }
self
end
def compact_blank
self.dup.compact_blank!
end
end
class Array
def compact_blank!
self.delete_if(&:blank?)
end
def compact_blank
self.dup.compact_blank!
end
end
use like
["1", "abc", "", nil, []].compact_blank
=> ["1", "abc"]
it's useful with params especially, where you might get a lot of empty strings through.
Is it possible to dynamically create key names of a hash? I'm passing the following hash parameters:
params[:store][:store_mon_open(5i)]
params[:store][:store_mon_closed(5i)]
params[:store][:store_tue_open(5i)]
params[:store][:store_tue_closed(5i)]
.
.
.
params[:store][:store_sun_open(5i)]
params[:store][:store_sun_closed(5i)]
To check if each parameter exists, I'm using two arrays:
days_of_week = [:mon, :tue, ..., :sun]
open_or_closed = [:open, :closed]
But, I can't seem to figure out how to dynamically create the params hash (the second key( with the array. Here's what I have so far:
days_of_week.each do |day_of_week|
open_or_closed.each do |store_status|
if !eval("params[:store][:store_#{day_of_week}_#{store_status}(5i)").nil
[DO SOMETHING]
end
end
end
I've tried a bunch of things including the eval method (as listed above) but rails seems to dislike the parentheses around the "5i". Any help is greatly appreciated!
You should be able to do
if params[:store]["store_#{day_of_week}_#{store_status}(5i)".to_sym]
Note that you were missing the ? on .nil? and that !object.nil? can be shortened to just object
Assuming this is a HashWithIndifferentAccess, you should be able to access it via string just as you could with a symbol. Thus:
days_of_week.each do |day_of_week|
open_or_closed.each do |store_status|
key = "store_#{day_of_week}_#{store_status}(5i)"
unless params[:store][key]
# DO SOMETHING
end
end
end
If it's not a HashWithIndifferentAccess then you should just be able to call key.to_sym to turn it into a symbol.
When I assign in my controller
#my_hash = { :my_key => :my_value }
and test that controller by doing
get 'index'
assigns(:my_hash).should == { :my_key => :my_value }
then I get the following error message:
expected: {:my_key=>:my_value},
got: {"my_key"=>:my_value} (using ==)
Why does this automatic symbol to string conversion happen? Why does it affect the key of the hash?
It may end up as a HashWithIndifferentAccess if Rails somehow gets ahold of it, and that uses string keys internally. You might want to verify the class is the same:
assert_equal Hash, assigns(:my_hash).class
Parameters are always processed as the indifferent access kind of hash so you can retrieve using either string or symbol. If you're assigning this to your params hash on the get or post call, or you might be getting converted.
Another thing you can do is freeze it and see if anyone attempts to modify it because that should throw an exception:
#my_hash = { :my_key => :my_value }.freeze
You might try calling "stringify_keys":
assigns(:my_hash).should == { :my_key => :my_value }.stringify_keys
AHA! This is happening not because of Rails, per se, but because of Rspec.
I had the same problem testing the value of a Hashie::Mash in a controller spec (but it applies to anything that quacks like a Hash)
Specifically, in a controller spec, when you call assigns to access the instance variables set in the controller action, it's not returning exactly the instance variable you set, but rather, a copy of the variable that Rspec stores as a member of a HashWithIndifferentAccess (containing all the assigned instance variables). Unfortunately, when you stick a Hash (or anything that inherits from Hash) into a HashWithIndifferentAccess, it is automatically converted to an instance of that same, oh-so-convenient but not-quite-accurate class :)
The easiest work-around is to avoid the conversion by accessing the variable directly, before it's converted "for your convenience", using: controller.view_assigns['variable_name'] (note: the key here must be a string, not a symbol)
So the test in the original post should pass if it were changed to:
get 'index'
controller.view_assigns['my_hash'].should == { :my_key => :my_value }
(of course, .should is no longer supported in new versions of RSpec, but just for comparison I kept it the same)
See this article for further explanation:
http://ryanogles.by/rails/hashie/rspec/testing/2012/12/26/rails-controller-specs-dont-always-play-nice-with-hashie.html
I know this is old, but if you are upgrading from Rails-3 to 4, your controller tests may still have places where Hash with symbol keys was used but compared with the stringified version, just to prevent the wrong expectation.
Rails-4 has fixed this issue: https://github.com/rails/rails/pull/5082 .
I suggest updating your tests to have expectations against the actual keys.
In Rails-3 the assigns method converts your #my_hash to HashWithIndifferentAccess that stringifies all the keys -
def assigns(key = nil)
assigns = #controller.view_assigns.with_indifferent_access
key.nil? ? assigns : assigns[key]
end
https://github.com/rails/rails/blob/3-2-stable/actionpack/lib/action_dispatch/testing/test_process.rb#L7-L10
Rails-4 updated it to return the original keys -
def assigns(key = nil)
assigns = {}.with_indifferent_access
#controller.view_assigns.each { |k, v| assigns.regular_writer(k, v) }
key.nil? ? assigns : assigns[key]
end
https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_dispatch/testing/test_process.rb#L7-L11
You can also pass your Hash object to the initializer of HashWithIndifferentAccess.
You can use HashWithIndifferentAccess.new as Hash init:
Thor::CoreExt::HashWithIndifferentAccess.new( to: 'mail#somehost.com', from: 'from#host.com')