I have a piece of controller code where some values are calculated. The result is in the form of an array of hashes. This needs to get into a partial form somehow so that it may be retrieved later during commit (which is through the Submit button).
The questions is how do we pass the array of hashes?
thanks.
Is there a reason it has to be through the form? This is the type of thing I usually use the session for.
I can't really think of a nice way to do what you're asking with forms. I guess you could create hidden fields for each key in your hash in the form with hidden_field_tag as an alternative. Then you run into problems translating it (what if a key's value is an array or another hash?).
You could easily store the hash in the session and then on each page load, check to see if there is a hash where you expect it. On calculating values:
session[:expected_info] = results
And each page load, something like this:
if session.has_key?(:expected_info)
results = session.delete(:expected_info)
# you already calculated the results, just grab them and
# do what you need to do
else
# you don't have the expected info
end
You should be able to pass it as a string to your partial:
[{}].inspect
and eval it when it is submitted back through the form:
eval("[{}]"))
but that would be really dirty…
Related
I'm having trouble with a little Ruby on Rails I'm building and need some help.
I have a Table with 20+ Columns and a corresponding XML File which can be parsed as some sort of hash with a gem. Every key would be mapped to a column and every value would be a data record in said column.
The way I access a specific value in the already parsed XML file is:
filename["crs","inputkeyhere"]
which returns the value, for example "52" or whatever.
What I am trying to do is upload the file, parse it with the gem and give each column the corresponding value.
My table (or model) is called "Attributeset" and I already know how I can access every column:
#attributeset = Attributeset.new
#attributeset.attributes.keys
So my thought process was:
Iterate over all the keys
Pass every key into a block called |a|
Use the rails possibilty to set attributes by calling the corresponding #attributeset.
Set colum attribute to the corresponding xml key
So my code would go something like this:
#attributeset.attributes.keys.each do |a|
#attributeset.a=filename["crs",a]
end
But my problem is, that ruby thinks ".a" is a method and apparently does not evaluate "a" to the block parameter.
I've read through lambdas and procs and whatnot but didn't really understand how they could work for my specific situation.
Coming from bash scripting maybe my thinking might be wrong but I thought that the .a might get evaluated.
I know I can run the block with yield, but this only works in methods as far as I know..
Any help is appreciated.
Thanks and stay healthy,
Alex
Thanks for the input!
I wanted to make it as clean as possible, and not using any temporary hashes to pass arguments.
I've found the method
write_attribute
which can be used like this:
#attributeset.write_attribute(a, xmp["crs",a])
worked perfectly for me.
You can use []= method to set values dynamically:
#attributeset.attribute_names.each do |attribute|
#attributeset[attribute] = filename["crs", attribute]
end
I'm currently trying to understand how permitted parameters works in ruby.
Usually, in my_model.rb I have:
has_many: some_other_model
*
*
*
def my_model_params
params.require(:my_model).permit( :column1, some_other_model_attributes %i[other_column1])
etc...
and in the update function
my_object.update_attributes(my_model_params)
with a well formatted json which has some my_model root, and some_other_model_attributes as a child (array) with values.
My problem is I receive a json like this one
However the different arrays inside (such as codification, general_information) do contain attributes of the mission (general_information contains reference that is a column in the mission table) but there isn't any column named codification, or relation to a codification_attributes.
So, when I add :
general_information: %i[reference] in the permitted params, it says unknown attribute 'general_information' for Mission.
If not, no error are raised but in the log I can see unpermitted_parameter: general_information. And my object is not updated.
Finally if I reject it, there is no more unpermitted_parameter: general_information in the log but my object is not updated either.
I tried to set config.action_controller.action_on_unpermitted_parameters to false in my development config, it did nothing and it's probably a bad idea for production environment anyway.
The use of .permit! (even if it works) is currently not an option. And even though I think the json needs to be re-formatted it'd be better to find an other solution.
Thanks for the help.
unpermitted_parameter: ... in logs in not a problem which you need to fix, it's just an info.
How it works - you just permit needed parameters, you may think about them as a hash. Unpermitted parameters will not go into the model even if they are present in params. It means when you call
my_object.update_attributes(my_model_params)
it works like
my_object.update_attributes(column1: value_for_column1_from_params)
Keys in params should be named exactly as columns in the model, otherwise you need to prepare params somehow before create/update
In a Rails 5 controller, you can call params and it returns a hash of the parameters from the request.
But you can't modify the params that way. Because what you're modifying is a copy of the params hash values, not a reference to the underlying params.
params[:starting_value] ||= "abc" # doesn't work for my purposes
What you're supposed to do is store the values elsewhere.
#starting_value = params[:starting_value] || "abc"
But if a bunch of other places in the code expect params[:starting_value], then this solution might require some messy changes.
Is there a way to set the default value of a param in the controller? Or am I going to have to do it the slightly messier way.
I could also accomplish what I want with a redirect, but that isn't ideal either.
I think you're looking for the merge! method. Docs Here
params = params.merge!(:starting_value, 'abc)
It returns the original params with the new one merged in or overwritten. Be aware that merge without an exclamation mark does not modify in place. You need it to keep the changes.
Is there a best practice to validate params in a controller?
#user = User.find_by_id(params[:id])
If I tamper with the param to give it an invalid :id param, say by visiting "/users/test", I can generate the following error:
Conversion failed when converting the nvarchar value 'test' to data type int.
I am thinking right now of params that won't go straight to a model and can be validated by model validations.
Yes you should always validate your parameters. People can always mess around with the parameters in their web browser's address bar, or modify parameters stored in the DOM. Another example where parameters can be screwed up is if the webpage is left open a long time. Imagine someone is viewing the page "/users/3/edit" and leaves it open for an hour, then hits refresh. In the mean time that user may have been deleted. You don't want your website to crash - it should handle that gracefully.
Depending on your database and adapter, doing User.find_by_id("test") will not crash. But your database/adapter was not able to convert the string in to an integer. One thing you can do in this particular case is use Ruby's .to_i method.
User.find_by_id(params[:id].to_i)
If params[:id] = "12", Ruby will convert that to the integer 12 and the code will run fine. If params[:id] = "test", Ruby will convert that to the integer 0, and you should never have a database record with an ID of 0.
You can also use regular expressions to test if a string is an integer.
But in general, yes, try to always validate your parameters so you can handle errors gracefully and control the data coming in.
I'm serializing many attributes on a model Page as hashes.
Because of the high number of attributes, I've taken a meta-programming approach and want to use .send() to iterate through a collection of attributes (such that I don't have to type out an update action for each attribute.
I've done something like this:
insights.each do |ins|
self.send("#{ins.name}=", {(Time.now) => ins.values[1]['value'].to_f})
self.save
end
The problem is that this obviously overwrites the whole serialized column, whereas I wish to add this as an element to the serialized hash.
Tried something like this:
insights.each do |ins|
self.send("#{ins.name}[#{Time.now}]=", ins.values[1]['value'].to_f)
self.save
end
But get a NoMethodError: undefined method page_fan_adds_unique[Mon Aug 13 13:31:58 -0400 2012]=
In the console I'm able to do Page.find(5).page_fan_adds_unique[Time.now]= 12345 and save it as an additional element to the hash as expected.
So how can I use .send() to save an additional element to a serialized hash? Or is there some other approach? Such as using update_attribute or another method? Writing my own? Any help is appreciated, even if the advice is that I shouldn't be using serialization for this.
I'd do :
self.ins.name.send(:[]=, key, value)