I have a serialized field that I want to use in functional tests in Rails 4.
Fixtures are saving the hash as a string.
Step.rb
serialize :custom, ActiveRecord::Coders::NestedHstore
steps.yml
one:
name: Simple example.
custom:
name:
prompt: What is your name?
In step.custom, I want the hash {name: {prompt: "What is your name?"}. In testing, instead, I get a string along with the error:
undefined method `keys' for "{\"prompt\"=>\"What is your name?\"}":String
This is causing my tests to fail of course, because my codebase is expecting a Hash. I tried <%= hash.to_yaml.inspect %> but that doesn't seem to work.
I would try this:
require "yaml"
new_hash = YAML.parse hash
Related
In my Rails controller I'd like to require a single parameter, emails. It must be an array.
This is allowed.
emails[]=foo#bar.com&emails[]=up#down.com
This is not allowed.
emails=foo#bar.com
params.require(:emails) will allow both. The former comes as an Array. The latter as a String. This is a problem.
params.permit(emails: []) does not require an emails parameter.
params.require(:emails).permit([]) does not work because params.require(:emails) gets clever and returns an Array, not an ActionController::Parameters.
How do I allow emails[]=... and disallow emails=...?
Like what tadman said, Screening is what Strong Parameters does mostly.
You can look into Grape, perhaps it can show you some directions for your concerns
desc "API demo", {}
params do
requires :id, type: Integer, desc: 'ID'
requires :name, type: Boolean, desc: 'xxx'
end
Error msg would be received if you pass it an unexpected type of parameters
I have a nested form with three models vehicle, vehicle_key_feature and vehicle_detail where vehicle_key_feature and vehicle_detail has one to one relation with vehicle. It is working fine when I use strong params following way -
params.require(:vehicle).permit(:title, vehicle_key_feature_attributes: [:android_auto], vehicle_detail_attributes: [:tech_specs])
since I have lots of strong params for all three models, I would like to keep nested attributes params in a separate method and merge them with vehicle_params. But it's showing me following error
undefined method with_indifferent_access' for #Array
I have written the following codebase, I checked console and params.inspect which is in expected form.
def vehicle_params
params.require(:vehicle).permit(
:title, :category_id, :make, :model, :model_number, :mileage, :exterior, :interior, :transmission, :engine_type, :drivetrain, :fuel_efficiency, :engine, :condition, :description, :dealer_id
)
.merge(vehicle_key_feature_attributes)
.merge(vehicle_detail_attributes)
end
def vehicle_key_feature_attributes
{
vehicle_key_feature_attributes: [
:android_auto, :apple_carplay, :backup_camera, :blind_spot_monitor, :bluetooth,
:forward_collision_warning, :interior_accents, :keyless_entry, :side_impact_air_bags
]
}
end
def vehicle_detail_attributes
{
vehicle_detail_attributes: [
:exterior, :interior, :entertainment, :mechanical, :safety, :tech_specs, :details
]
}
end
What is the best solution to extract these two nested attributes in two separate methods?
Your second snippet is doing something else. To replicate what the first one does, add your hashes to permit's argument list.
params.require(:vehicle).permit(:title, ..., vehicle_key_feature_attributes.merge(vehicle_detail_attributes))
I have a field called custom that is defined as a NestedHstore:
# project.rb
serialize :custom, ActiveRecord::Coders::NestedHstore
I create fixtures for this model for testing:
one:
company: one
user: one
custom:
:name:
:prompt: 'What is your name?'
When this is run, the nested hashes are saved as strings, i.e. #project.custom = {"name"=>"{:prompt=>\"What is your name?\"}"}
I've gotten around this in unit and functional tests by adding a prepare_custom method that manually reconstructs the nested hash from the string BEFORE anything runs.
def prepare_custom(p)
if p.custom.present?
if p.custom.is_a? Hash
p.custom.each do |k,v|
if (v.class == String) && (v[0] == "{") && (v[-1] == "}")
p.custom[k] = eval(v)
end
end
end
p.save
p.reload
puts "*** project: #{p.inspect}"
end
end
When I run integration tests (with Capybara/Poltergeist/Puma), this happens:
prepare_custom(#project) seems to work. p.inspect at the end shows that custom is a properly nested hash.
The integration test code that runs immediately after prepare_custom seems to completely ignore that the project has been changed. It thinks p.custom[:name] is a string again.
How can I either get fixtures to save the nested hash properly, or get my integration tests to reflect the work done in prepare_custom?
Follow-up
After trying: custom: <%= {name: { prompt: 'What is your name?'}}.to_yaml.inspect %> in the fixture, we get:
ActiveRecord::StatementInvalid: ActiveRecord::StatementInvalid: PG::InternalError: ERROR: Syntax error near ':' at position 4
LINE 1: ...company_id") VALUES ('Simple prompt', 0, '---
^
: INSERT INTO "projects" ("name", "custom", "created_at", "updated_at", "id", "company_id") VALUES ('Simple prompt', '---
:name:
:prompt: What is your name?
', '2017-05-17 21:15:25', '2017-05-17 21:15:25', 159160234, 980190962, 159160234)
I have a generator class that I can run like this:
rails g shopping_template --attributes=email:hello
It recognizes email as input. But when I want to add several attributes its not working, it only recognizes email:
rails g shopping_template --attributes=email:hello,name:hans,house:big
Ho do I have to change --attributes=email:hello,name:hans,house:big so that its recognized correctly as a hash?
Here's the code from the class:
class_option :attributes,
type: :hash,
default: {}
Check the Thor document. This is the correct way to pass a hash:
--option=name:string age:integer
So for your example, it would be:
--attributes=email:hello name:hans house:big
I have a hard time understanding the form :attribute => parameter
Can anyone give me some explanations for it? Is :attribute a field (variable) belonging to the class or something else? Why we can pass this pair as one parameter to methods?
If you're referring to something like this:
some_method(:foo => "bar", :baz => "abc")
then it's just shorthand which causes ruby to convert those things into a Hash. Please note that when using this form, that the hash must be the final argument to the method in order for this to work.
Based on the explanation above, this
some_method(:foo => "bar", :baz => "abc")
is ok, but this
some_method(:foo => "bar", :baz => "abc", moo)
is not.
Though you will see this commonly in Rails, it is not a Rails specific question. It is Ruby.
The answer to your question is that it is key/value pairs in a Hash, generally passed as an argument to a method.
You will see this as well when it is being assigned to a variable directly. But let me show you a sample method, and a sample usage, so that you can put them together:
def some_method(*args, name: 'Joe', amount: 42, **other_params )
puts "#{name}, #{amount}, glob of arguments = #{args.inspect}",
"other params #{other_params}"
end
some_method(:occupation => 'programmer', :phone => '123-456-7890', name: 'Jane')
This is Ruby 2.0.0 specific in the fact that you can provide for that last argument, which provides for unnamed parameters, in practice. Using the 1.9+ syntax for a Hash in the argument list, you are allowed to provide for other unnamed "parameters" which can appear after the hash argument.
Notice that if I had used the older syntax for Hash, namely the :key => 'value' syntax, I would not be allowed (at least as of this writing) to have the **other_params argument at the end of the argument list.
You could also provide the hash using the newer syntax in the calling code, though I left it as the Hash syntax when calling some_method.
The Hash still needs to be the last provided in the calling argument list, the same as indicated in the argument list for the method definition.