I have searched everywhere but does anyone know if it is possible to permit and array of arrays using strong parameters in rails? My code looks like this:
params.require(:resource).permit(:foo, :bar => [[:baz, :bend]])
This is giving me:
ArgumentError (wrong number of arguments (0 for 1..2))
I have also tried:
params.require(:resource).permit(:foo, :bar => [[]])
params.require(:resource).permit(:foo, :bar => [][])
params.require(:resource).permit(:foo, :bar => [])
But these all give me invalid parameter errors or do not process the parameters.
Thanks in advance for any help
Looking at the code I think this is not possible. you have to flatten the second level.
def permit(*filters)
params = self.class.new
filters.each do |filter|
case filter
when Symbol, String
permitted_scalar_filter(params, filter)
when Hash then
hash_filter(params, filter)
end
end
unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters
params.permit!
end
Here's an example taken from rails strong parameter Github page:
params.permit(:name, {:emails => []}, :friends => [ :name, { :family => [ :name ], :hobbies => [] }])
Related
I pass a params like this
{
"utf8" => true,
"supply" => {
"items" => { 111 => 112, 89 => 10},
"another_params" => "something"
}
}
My supply_params are:
params.fetch(:supply, {}).permit(:another_params, items: {})
But I get an unpermitted parameters 111 and 89. How can I make items permit all kinds of keys?
This thread in github provides a solution:
def supply_params
params.require(:supply).permit(:another_params).tap do |whitelisted|
whitelisted[:items] = params[:supply][:items] if params[:supply][:items]
end
end
The idea is to explicitly permit any known attributes which are needed and then tack on nested attributes.
According to the #steve klein link to github issue, this is considered as a good solution:
params.permit(:test).tap do |whitelisted|
whitelisted[:more] = params[:more]
end
How to permit this parameters:
contacts: [
{:value => 'value', :contacts_type => 'contact_type'},
{:value => 'value', :contacts_type => 'contact_type'},
]
To create many objects by controller action in one JSON request?
Like below, contacts will be an array of resources with specific attributes value and contacts_type:
params.permit(contacts: [:value, :contacts_type])
If you get params like the following:--
:params=>{:xyz => {:contacts => [{:value => 'value', :contacts_type => 'type'}, ..]}}
Then do the folowing:--
params.require(:xyz).permit(contacts: [:value, :contacts_type])
And add attr_accessor :contacts to your model if contacts is just a form field name part.
Work around for this should be
def contact_params
new_params = params.permit(contacts: [:value, :contacts_type])
new_params[:contacts] if new_params
end
Please suggest alternate solution if any
I have a class Sample
Sample.class returns
(id :integer, name :String, date :date)
and A hash has all the given attributes as its keys.
Then how can I initialize a variable of Sample without assigning each attribute independently.
Something like
Sample x = Sample.new
x.(attr) = Hash[attr]
How can I iterate through the attributes, the problem is Hash contains keys which are not part of the class attributes too
class Sample
attr_accessor :id, :name, :date
end
h = {:id => 1, :name => 'foo', :date => 'today', :extra1 => '', :extra2 => ''}
init_hash = h.select{|k,v| Sample.method_defined? "#{k}=" }
# This will work
s = Sample.new
init_hash.each{|k,v| s.send("#{k}=", v)}
# This may work if constructor takes a hash of attributes
s = Sample.new(init_hash)
Take a look at this article on Object initialization. You want an initialize method.
EDIT You might also take a look at this SO post on setting instance variables, which I think is exactly what you're trying to do.
Try this:
class A
attr_accessor :x, :y, :z
end
a = A.new
my_hash = {:x => 1, :y => 2, :z => 3, :nono => 5}
If you do not have the list of attributes that can be assigned from the hash, you can do this:
my_attributes = (a.methods & my_hash.keys)
Use a.instance_variable_set(:#x = 1) syntax to assign values:
my_attributes.each do |attr|
a.instance_variable_set("##{attr.to_s}".to_sym, my_hash[attr])
end
Note(Thanks to Abe): This assumes that either all attributes to be updated have getters and setters, or that any attribute which has getter only, does not have a key in my_hash.
Good luck!
Here's some of my production code (I had to force line breaks):
task = Task.find_or_create_by_username_and_timestamp_and_des \
cription_and_driver_spec_and_driver_spec_origin(username,tim \
estamp,description,driver_spec,driver_spec_origin)
Yes, I'm trying to find or create a unique ActiveRecord::Base object. But in current form it's very ugly. Instead, I'd like to use something like this:
task = Task.SOME_METHOD :username => username, :timestamp => timestamp ...
I know about find_by_something key=>value, but it's not an option here. I need all values to be unique. Is there a method that'll do the same as find_or_create_by, but take a hash as an input? Or something else with similat semantics?
Rails 3.2 first introduced first_or_create to ActiveRecord. Not only does it have the requested functionality, but it also fits in the rest of the ActiveRecord relations:
Task.where(attributes).first_or_create
In Rails 3.0 and 3.1:
Task.where(attributes).first || Task.create(attributes)
In Rails 2.1 - 2.3:
Task.first(:conditions => attributes) || Task.create(attributes)
In the older versions, you could always write a method called find_or_create to encapsulate this if you'd like. Definitely done it myself in the past:
class Task
def self.find_or_create(attributes)
# add one of the implementations above
end
end
I also extend the #wuputah's method to take in an array of hashes, which is very useful when used inside db/seeds.rb
class ActiveRecord::Base
def self.find_or_create(attributes)
if attributes.is_a?(Array)
attributes.each do |attr|
self.find_or_create(attr)
end
else
self.first(:conditions => attributes) || self.create(attributes)
end
end
end
# Example
Country.find_or_create({:name => 'Aland Islands', :iso_code => 'AX'})
# take array of hashes
Country.find_or_create([
{:name => 'Aland Islands', :iso_code => 'AX'},
{:name => 'Albania', :iso_code => 'AL'},
{:name => 'Algeria', :iso_code => 'DZ'}
])
This is more of a style question, I'm wondering what other people do.
Let's say I have a field in my database called "status" for a blog post. And I want it to have several possible values, like "draft", "awaiting review", and "posted", just as an example.
Obviously we don't want to "hard code" in these magic values each time, that wouldn't be DRY.
So what I sometimes do is something like this:
class Post
STATUS = {
:draft => "draft",
:awaiting_review => "awaiting review",
:posted => "posted"
}
...
end
Then I can write code referring to it later as STATUS[:draft] or Post::STATUS[:draft] etc.
This works ok, but there are a few things I don't like about it.
If you have a typo and call something like STATUS[:something_that_does_not_exist] it won't throw an error, it just returns nil, and may end up setting this in the database, etc before you ever notice a bug
It doesn't look clean or ruby-ish to write stuff like if some_var == Post::STATUS[:draft] ...
I dunno, something tells me there is a better way, but just wanted to see what other people do. Thanks!
You can use Hash.new and give it a block argument which is called if a key is unknown.
class Post
STATUS = Hash.new{ |hash, key| raise( "Key #{ key } is unknown" )}.update(
:draft => "draft",
:awaiting_review => "awaiting review",
:posted => "posted" )
end
It's a bit messy but it works.
irb(main):007:0> Post::STATUS[ :draft ]
=> "draft"
irb(main):008:0> Post::STATUS[ :bogus ]
RuntimeError: Key bogus is unknown
from (irb):2
from (irb):8:in `call'
from (irb):8:in `default'
from (irb):8:in `[]'
from (irb):8
This is a common problem. Consider something like this:
class Post < ActiveRecord::Base
validates_inclusion_of :status, :in => [:draft, :awaiting_review, :posted]
def status
read_attribute(:status).to_sym
end
def status= (value)
write_attribute(:status, value.to_s)
end
end
You can use a third-party ActiveRecord plugin called symbolize to make this even easier:
class Post < ActiveRecord::Base
symbolize :status
end
You could use a class method to raise an exception on a missing key:
class Post
def self.status(key)
statuses = {
:draft => "draft",
:awaiting_review => "awaiting review",
:posted => "posted"
}
raise StatusError unless statuses.has_key?(key)
statuses[key]
end
end
class StatusError < StandardError; end
Potentially, you could also use this method to store the statuses as integers in the database by changing your strings to integers (in the hash), converting your column types, and adding a getter and a setter.
I do it like this:
class Post
DRAFT = "draft"
AWAITING_REPLY = "awaiting reply"
POSTED = "posted"
STATUSES = [DRAFT, AWAITING_REPLY, POSTED]
validates_inclusion_of :status, :in => STATUSES
...
end
This way you get errors if you misspell one. If I have multiple sets of constants, I might do something like DRAFT_STATUS to distinguish.
Take a look at the attribute_mapper gem.
There's a related article that shows how you can handle the problem declaratively, like this (borrowed from the article):
class Post < ActiveRecord::Base
include AttributeMapper
map_attribute :status, :to => {
:draft => 1,
:reviewed => 2,
:published => 3
}
end
...which looks rather stylish.
Even though this is an old post, for somebody stumbling across this, you can use the fetch method on Hash, which raises an error (when no default is passed) if the given key is not found.
STATUS = {
:draft => "draft",
:awaiting_review => "awaiting review",
:posted => "posted"
}
STATUS.fetch(:draft) #=> "draft"
STATUS.fetch(:invalid_key) #=> KeyError: key not found: invalid_key