Passing extra data to find_or_create - ruby-on-rails

Something I've always wondered about rails is the ability to pass extra data to find_or_create methods in rails. For example, I can't do the following
User.find_or_create_by_name('ceilingfish', :email => 'an_email#a.domain', :legs => true, :face => false)
I could do
u = User.find_or_create_by_name('ceilingfish')
u.update_attributes(:email => 'an_email#a.domain', :legs => true, :face => false)
But that's uglier, and also requires three queries. I suppose I could do
User.find_or_create_by_name_and_email_and_face_and_legs('ceilingfish','an_email#a.domain',true, false)
But that kind of implies that I know what the values of email, legs and face are. Does anyone know if there's a really elegant way of doing this?

Try this:
User.find_or_create_by_name(:name=>'ceilingfish',
:email => 'an_email#a.domain', :legs => true, :face => false)
When you have additional parameters to find_or_create_by_, you have to pass all the parameters as a hash.
Rails 4
User.create_with(
email: 'an_email#a.domain',
legs: true, face:false
).find_or_create_by(:name=>'ceilingfish')

With rails 4.x
DEPRECATION WARNING: This dynamic method is deprecated. Please use e.g. Post.find_or_create_by(name: 'foo') instead
Use this
User.find_or_create_by(first_name: 'Scarlett') do |user|
user.last_name = 'Johansson'
end

Related

get selected items from select_tag

I have this line in my rails app:
<%= select_tag :questionnaire_id,
options_for_select(#questionnaires_types, #questionnaires_ids),
:multiple => true, :size => 7 %>
which works fine.
but when I try to use the multiple values that were selected I get this:
questionnaire_id"=>["1687,1688,1689,1690,1691,1724"]
instead of this:
questionnaire_id"=>["1687", "1688", "1689" ,"1690", "1691", "1724"]
i.e. I get 1 item instead of 6 items.
any suggestions?
According to rails code: https://github.com/rails/rails/blob/41231ef6c6c6a6e546b69add28f04aafb9e0e952/actionview/lib/action_view/helpers/form_tag_helper.rb#L134
The name must end with [] to be make sure you receive an array.
def select_tag(name, option_tags = nil, options = {})
option_tags ||= ""
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
if options.delete(:include_blank)
option_tags = content_tag(:option, '', :value => '').safe_concat(option_tags)
end
if prompt = options.delete(:prompt)
option_tags = content_tag(:option, prompt, :value => '').safe_concat(option_tags)
end
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
end
So just change it to questionnaire_ids[]
Hope that helps.
I think a collection_select would look nice but I cannot help with that since you did not post anything about the model. Maybe try this so that it knows it is a collection:
<%= select_tag "questionnaire_ids[]", options_for_select(#questionnaires_types, #questionnaires_ids), :multiple => true, :size => 7 %>
Or you could just parse the string you currently receive using #split.
Otherwise post a bit more code about the associations between Questionnaire and what ever this model is.
Well, just in case that someone will come to this issue, I found the problem.
It seems to be a bug in rails.
I was using remote_form_for, and that gave me the strange behaviour. I tried to change the form to form_for instead, and I got an array with 6 items.
Rails, Rails, when will you be like .Net? :-(

Passing params for creating and updating a model

I wonder, is there an easier way to do the following
#controller's action
#my_model = MyModel.create field1: params[:field1],
field2: params[:field2],
field3: params[:field3],
field4: params[:field4]
# and so on.....
I would use
#my_model = MyModel.create params
but would it work since params always contains other keys added by Rails?
P.S.
The same question for updating a model (would this work properly?)
MyModel.update_attributes params
Send params as a nested hash like
{:my_model => {:field1 => 'blah', :field2 => 'blah'}, :controller => 'something', :action => 'something_else'}
This way you could just say
#my_model = MyModel.create params[:my_model]
Rails does this automatically if you have followed the conventions while creating the form.

How to update attributes only if the params are defined?

currently in my update controller method I have:
#group.attributes = {
:title => params[:group][:title],
:description => params[:group][:description],
:password_required => params[:group][:password_required],
:password => params[:group][:password],
:archived => params[:group][:archived]
}
The problem is that this method is used in multiple places and all of these params are not always passed which results in a "nil" which causes the db commit to rollback.
How can you set attributes only when they are defined w/o having to use if blocks?
Thanks
Could probably do something lousy like this:
#group.attributes = {
:title => params[:group][:title] || #group.title,
:description => params[:group][:description] || #group.description,
:password_required => params[:group][:password_required] || #group.password_required,
:password => params[:group][:password] || #group.password,
:archived => params[:group][:archived] || #group.archived
}
This kind of ugly code is not recommended, but it answers the question of how to do this without explicit if blocks.
why not simplify it with
#group.update_attributes(params[:group])
this way if the value us nil it wont be updated

Why when adding a hash to a hash, Ruby seems to nest the added hash?

I'm trying to create a two-dimensional hash like the following:
permissions['enrollment'] = ['read' => true, 'create' => true, 'update' => true]
permissions['invoices'] = ['read' => true, 'create' => false, 'update' => false]
This is what I do...
permissions = Hash.new
permissions['enrollment'] = ['read' => true, 'create' => true, 'update' => true]
permissions['invoices'] = ['read' => true, 'create' => false, 'update' => false]
When I "puts" permissions in irb I get this...
{
"enrollment"=>[{"read"=>true, "create"=>false, "edit"=>false}],
"invoices"=>[{"read"=>true, "create"=>false, "update"=>false}]
}
As you can see from the above output, there seems to be a hash inside another hash for permissions['enrollment'] [{}]!!!
I want to access the read permissions for enrollment like this: permissions['enrollment']['read'] but according to the puts permission it won't work and I get this error TypeError: can't convert String into Integer
For me to access the read permissions for enrollment I have to do this: permissions['enrollment'][0]['read'].
How can I make the enrollment read permissions just like this... permissions['enrollment']['read']?
Your syntax is off. Hash is delimited by curly braces. Square brackets are for arrays. You might confuse the two if you're coming from PHP world. Try this:
permissions = Hash.new
permissions['enrollment'] = {'read' => true, 'create' => true, 'update' => true}
permissions['invoices'] = {'read' => true, 'create' => false, 'update' => false}

accepts_nested_attributes_for and nested_form plugin

I've the following code in a _form.html.haml partial, it's used for new and edit actions.
(fyi I use the Ryan Bates' plugin nested_form)
.fields
- f.fields_for :transportations do |builder|
= builder.collection_select :person_id, #people, :id, :name, {:multiple => true}
= builder.link_to_remove 'effacer'
= f.link_to_add "ajouter", :transportations
works fine for the new action...
for the edit action, as explain in the doc, I've to add the :id of already existing associations, so, I've to add something like
= builder.hidden_field :id, ?the value? if ?.new_record?
How can I get the value?
Here is the doc of accepts_nested_attributes_for for reference (source: http://github.com/rails/rails/blob/master/activerecord/lib/active_record/nested_attributes.rb#L332)
# Assigns the given attributes to the collection association.
#
# Hashes with an <tt>:id</tt> value matching an existing associated record
# will update that record. Hashes without an <tt>:id</tt> value will build
# a new record for the association. Hashes with a matching <tt>:id</tt>
# value and a <tt>:_destroy</tt> key set to a truthy value will mark the
# matched record for destruction.
#
# For example:
#
# assign_nested_attributes_for_collection_association(:people, {
# '1' => { :id => '1', :name => 'Peter' },
# '2' => { :name => 'John' },
# '3' => { :id => '2', :_destroy => true }
# })
#
# Will update the name of the Person with ID 1, build a new associated
# person with the name `John', and mark the associatied Person with ID 2
# for destruction.
#
# Also accepts an Array of attribute hashes:
#
# assign_nested_attributes_for_collection_association(:people, [
# { :id => '1', :name => 'Peter' },
# { :name => 'John' },
# { :id => '2', :_destroy => true }
# ])
Thanks for your help.
I found my error, here is what i learned fyi:
When you use accepts_nested_attributes_for with many to many associations, keep the :id primary key for the association table.
Cheers
Mine works when using ":_delete" instead of ":_destroy". I am on rails 2.3.4. Ruby 1.8.7
Check out this: http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#M001605
Nested forms are officially supported with Rails. What you are doing (specifically with the fields_for method) may be conflicting with RAils' built-in way to render fields_for.
Here's the documentation for the Rails way to do fields_for...it's very thorough:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#M001605
I highly recommend you try the built-in way instead of the plugin, as that will continue to be supported almost indefinitely.
Hope this helps!

Resources