Rails multiple select form: no implicit conversion of String into Integer - ruby-on-rails-4

In Rails 4, Why isn't an array of genre_id being saved to the database? I think it's because the ids are being read as a string, when it needs to be saved as an integer, but in the past I didn't have this problem.
I get a no implicit conversion of String into Integer error when trying to POST the following:
<%= form_for #project do |f| %>
<%= select_tag 'project[genres_projects_attributes][genre_id][]',
options_for_select(Genre.order(:id).collect{|g| [g.name, g.id]}),
{ include_hidden: true, multiple: true, class: 'form-control'} %>
<% end %>
In my app,
Project has_many :genres, through: :genres_projects
My parameters look like this:
{"utf8"=>"✓","authenticity_token"=>"[xxx]",
"project"=>{"name"=>"sdfds",
"genres_projects_attributes"=>{"genre_id"=>["2",
"3",
"4"]},
"commit"=>"Go!"}
My project_params:
def project_params
params.require(:project).permit(:id, genres_projects_attributes:
[:id, {genre_id: []}, :project_id])
end
Here's the line in my controller that's throwing the error:
def create
#project = Project.create(project_params)
end

Unfortunately didn't find an answer so did the following workaround:
#genre_ids = project_params[:genres_projects_attributes][:genre_id]
#genre_ids.each do |g|
GenresProject.create!(project_id: #project.id, genre_id: g)
end
UPDATE:
I found out that it was the first blank value that was causing the issue. As of Rails 4.x, you can also add include_hidden: false in your form.
UPDATE AGAIN:
if project_params[:genres_projects_attributes]
#project.genres.delete_all
project_params[:genres_projects_attributes][:genre_id].each do |g|
#project.genres << Genre.find(g)
end
#project.update!(project_params.except :genres_projects_attributes)
else
#project.update!(project_params)

Related

Ruby on Rails - Selected values are not working as expected in multiple: true (simple_form)

In my form, I have an f.select with multiple: true, but select doesn't work if it's not hardcoded in.
This is my form in new view:
= f.select :os, get_uniq_objects(:os), {}, {multiple: true }
My helper
def get_uniq_objects(obj)
somethings.pluck(obj).uniq
end
My controller
def campaign_params
params.require(:something).permit(os:[])
end
In new view, when OSs are selected, the result would saved as ['Linux', 'Windows'] so in my edit view I do as below but nothing gets selected:
= f.select :os, options_for_select(get_uniq_objects(:os), #something.os), {}, { multiple: true}
BUT if I hardcode them as below, everything works fine. I've even double checked what #something.os is by adding it to my view and its exactly like the hardcoded code.
= f.select :os, options_for_select(get_uniq_objects(:os), ['Linux', 'Windows']), {}, { multiple: true}
I'm not sure what I've done wrong here. Any help is appreciated and thanks in advance!
Taking a closer look into what value f.select got, helped me this solve this issue.
Value that was pass to it was as follow:
["Linux", "Windows"]
But for some reason, f.select got this array (with backslashes):
[\"Linux\", \"Windows\"]
This is my solution. In my model I did a gsub to change the saving values to string from an array:
before_save do
self.os_name.gsub!(/[\[\]\"]/, "") if attribute_present?("os_name")
end
so ["Linux", "Windows"], would become Linux, Windows
and I changed my f.select to following:
= f.select :os, options_for_select(get_uniq_objects(:os), #something.os.split(/\W+/)), {}, { multiple: true}
I did .split(/\W+/) to change the string to an array that f.select would accept.

rails 4 nested attributes won't create has_many model

I'm new to rails and have spent way too many hours on this. Thanks a lot, in advance, for any help!
I can't seem to get fields_for and/or accepts_nested_attributes_for to work for my nested attributes.
I have a smash_client that has_many contracts and a form that tries to create a smash_client with a parameter and at the same time it tries to also set a parameter on the contract object. The contract belongs_to the smash_client.
I've tried a lot of different solutions and have read the docs but I'm still missing something. I get this in my params hash, in the smash_clients_controller.rb
..., "smash_client"=>{"name"=>"fasdf", "user"=>"adam"}, "smash_client_id"=>{"instance_type"=>"spot"},...
from
= form_for #smash_client do |f|
.field
= f.label :name
= f.text_field :name
.field
= fields_for :smash_client_id do |c|
%p
= c.radio_button :instance_type, 'spot'
= c.label :instance_type, 'spot'
= c.radio_button :instance_type, 'on_demand'
= c.label :instance_type, 'on demand'
.actions
= f.submit 'Save'
and
class SmashClient < ActiveRecord::Base
has_many :contracts, dependent: :destroy
accepts_nested_attributes_for :contracts, allow_destroy: true,
reject_if: proc { |attributes| attributes[:instance_type].blank? }
...
def new
#smash_client = SmashClient.new
3.times { #smash_client.contracts.build }
end
...
def smash_client_params
#smash_client_params = params.require(:smash_client).
permit( :user, :name, contracts_attributes: [:instance_type] )
end
end
and
class Contract < ActiveRecord::Base
belongs_to :smash_client
after_create :determine_instance_type_and_start
before_destroy :stop_instances
...
end
I think the nested params would work if I hard coded it because if I try something like this, in the console, I don't get errors and I get a new SmashClient and Contract.
smash_client_params = {name: 'something', user: 'blah', contracts_attributes: [{instance_type: 'spot'}]}
SmashClient.create( smash_client_params )
I tried using :contracts, #smash_client.contracts and a few other things in the fields_for section. Also tried using select and collection_select but I can't seem to nail down the form.
sorry for the long post. Hopefully I got all the useful info with nothing extra in the question.
I'd really appreciate some direction or answers.
Thanks in advance.
I finally found it. The :instance_type had to be whitelisted in the Contract model. Thanks again, kalyani. I appreciate the help. Here's the changes to the code above:
.field
= fields_for :contracts do |c|
= c.label :instance_type, 'spot instance'
= c.radio_button :instance_type, 'spot', checked: true
= c.label :instance_type, 'on demand instance'
= c.radio_button :instance_type, 'on_demand'
and
def contract_params
params.require(:contract).
permit(:id, :name, :instance_id, :smash_client_id, :instance_type)
end
Instead of : fields_for :smash_client_id do |c|
write it as: fields_for :contracts do |c|
Refer:
1. http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
http://railscasts.com/episodes/196-nested-model-form-part-1
Rails 4 Nested Attributes Unpermitted Parameters ---- refer this for writing the code in controller and view the correct way

AWS credentials aren't working with Paperclip image upload in Rails 4 and mongoid

I'm receiving the following error:
The AWS Access Key Id you provided does not exist in our records.
I'm not sure why I would be getting this error because I have the correct access key and secret key shown to me in my aws security credentials page (I have copied and pasted multiple times to refute the possibility of a typo). Why would it be telling me that my access key is not in their records if I am able to log in and visit my STILL empty buckets?
Here is my s3.yml:
GMAIL_USERNAME: <my email>
GMAIL_PASSWORD: <my password>
MONGOHQ_URL: <my mongoid url>
S3_BUCKET_NAME_DEVELOPMENT: <my development bucket>
S3_BUCKET_NAME_PRODUCTION: <my production bucket>
S3_BUCKET_NAME_TEST: <my test bucket>
AWS_ACCESS_KEY_ID: <my access key>
AWS_SECRET_ACCESS_KEY: <my secret key>
Here is my picture.rb:
class Picture
include Mongoid::Document
include Mongoid::Paperclip
embedded_in :datum, inverse_of: :pictures
has_mongoid_attached_file :attachment,
path: ':attachment/:id/:style.:extension',
storage: :s3,
url: ':s3_domain_url',
bucket: Proc.new{ |a| a.instance.s3_bucket },
s3_credentials: File.join(Rails.root, 'config', 's3.yml'),
styles: {
original: ['1920x1680', :jpg],
small: ['100x100', :jpg],
medium: ['250x250', :jpg],
large: ['500x500', :jpg]
},
convert_options: { all: '-background white -flatten +matte' }
def s3_bucket
if Rails.env.development?
ENV['S3_BUCKET_NAME_DEVELOPMENT']
else
ENV['S3_BUCKET_NAME_PRODUCTION']
end
end
end
Here is my datum.rb:
class Datum
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paperclip
field :title, type: String
field :user_id, type: Integer
validates :title, presence: true
validates :user_id, presence: true
embeds_many :todo_items
embeds_many :pictures, cascade_callbacks: true
accepts_nested_attributes_for :pictures
end
Here is my datum_params method:
# Never trust parameters from the scary internet, only allow the white list through.
def datum_params
params.require(:datum).permit(:title, :user_id, :identifying_image, pictures_attributes: [:picture])
end
Here is my picture_params method:
def picture_params
params.require(:picture).permit(:datum_id, :attachment)
end
Here is the form I use for the upload:
<%= bootstrap_form_for(picture, url: { action: :create }, html: { multipart: true }, remote: true ) do |f| %>
<%= f.file_field :picture, multiple: true, name: 'picture[picture]' %>
<%= f.submit 'Upload' %>
<% end %>
Originally, I was having a strong_parameters issue that rails4guides.com (the user with the answer below) was able to help me overcome. I knew I made progress when I stopped getting an unpermitted parameter error and started getting errors from AWS. Can anyone see any reason why my credentials are not working? Am I supposed to be seeing anything about my access key in the request parameters? Because I don't at this time.
Okay, I will explain further in a separate answer to make further elaboration more clear.
In a typical situation you would have something like this in your form :
<%= f.file_field :picture %>
Picture is the name of the file you want to upload. Now imagine this form will pass on it's results to the ImagesController. In order for the ImagesController to be able to process the picture it should be whitelisted through strong parameters, as in:
def image_params
params.require(:image).require(:picture)
end
An other case would be using nested attributes. If you want to set a picture for an article your form will look something like this:
<% f.fields_for :image do |image_form| %>
<%= image_form.file_field :picture %>
<% end %>
And your article_params will look like this:
def article_params
params.require(:article).permit(:title, :content, images_attributes: [:picture])
end
Small note: It should be images_attributes (both plural) and not image_attributes. The actual nested attributes on the other hand should be singular.
This is pretty much a basic outline of strong parameter usage, I hope it will help you. P.s. There might be some typos, since I'm typing this on my mobile phone.

Mongoid Model won't persist has_one via embeds_one relation

I'm having trouble with embeds in a mongoid4-based rails 4 app. I've been looking for an answer everywhere for the past 2 days. So here is the code.
This is a church management app, with a Service model, embedding a team and a band. Each team/band has several roles such as "presidence", "communion" that refer to a user.
My models :
class Service
include Mongoid::Document
...
embeds_one :team, autobuild: true
embeds_one :band, autobuild: true
...
accepts_nested_attributes_for :team, :band
end
class Team
include Mongoid::Document
embedded_in :service
has_one :presidence, :class_name => 'User', autosave: true
has_one :message, :class_name => 'User', autosave: true
...
end
class Band
include Mongoid::Document
has_one :lead, :class_name => 'User', autosave: true
has_one :guitar, :class_name => 'User', autosave: true
...
embedded_in :service
end
class User
include Mongoid::Document
embeds_one :profile
belongs_to :team, :inverse_of => :presidence
belongs_to :team, :inverse_of => :message
belongs_to :band, :inverse_of => :lead
belongs_to :band, :inverse_of => :guitar
def fullname
"#{profile.firstname} #{profile.lastname}"
end
def self.find_by_talent(talent)
self.where("profile.talents.name" => talent)
end
end
The services controller :
# POST /services
# POST /services.json
def create
#service = Service.new(service_params)
respond_to do |format|
if #service.save
format.html { redirect_to #service, notice: 'Service was successfully created.' }
format.json { render action: 'show', status: :created, location: #service }
else
format.html { render action: 'new' }
format.json { render json: #service.errors, status: :unprocessable_entity }
end
end
end
...
def service_params
params.require(:service).permit(:date, :time, :place, :name, :theme, :team => [:id, :precedence, :message ], :band => [:id, :lead, :guitar ])
end
And the form in _form.html.erb :
<%= form_for(#service) do |f| %>
...
<%= f.fields_for #service.team do |tf| %>
<%= tf.collection_select :presidence, User.find_by_talent(:presidence), :_id, :fullname, {:include_blank => "select a person"} %>
<%= tf.collection_select :message, User.find_by_talent(:message), :id, :fullname, {:include_blank => "select a person"} %>
<% end %>
<%= f.fields_for #service.band do |bf| %>
<%= bf.collection_select :lead, User.find_by_talent(:lead), :id, :fullname, {:include_blank => "select a person"} %>
<%= bf.collection_select :guitar, User.find_by_talent(:guitar), :id, :fullname, {:include_blank => "select a person"} %>
<% end %>
...
<% end %>
When creating a service, everything seems to run fine, but this is what I get in the console :
2.0.0-p195 :001 > s = Service.last
=> #<Service _id: 52ea18834d61631e7e020000, date: "2014-02-02", time: "10:00", place: "Where it's at", name: "My great name", theme: "The service's theme">
2.0.0-p195 :002 > s.team
=> #<Team _id: 52ea18834d61631e7e030000, >
2.0.0-p195 :003 > s.team.presidence
=> nil
Why is s.team.presidence not created ? s.team looks weird, too, with an ending comma...
Here is the content of my rails log :
Started POST "/services" for 127.0.0.1 at 2014-01-30 10:16:51 +0100
Processing by ServicesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Ph6lbdHC2FbiANn/fGSzHWprenj3fWKXM40Hrsc5+AM=", "service"=>{"date"=>"2014-02-02", "name"=>"My great name", "theme"=>"The service's theme", "time"=>"10:00", "place"=>"Where it's at", "team"=>{"presidence"=>"52ea18324d61631e81010000", "message"=>"52ea18324d61631e81010000"}, "band"=>{"lead"=>"52ea18324d61631e81010000", "guitar"=>"52ea18324d61631e81010000"}}, "commit"=>"Create Service"}
MOPED: 127.0.0.1:27017 COMMAND database=admin command={:ismaster=>1} runtime: 0.6610ms
MOPED: 127.0.0.1:27017 UPDATE database=service_boot_camp_development collection=users selector={"band_id"=>BSON::ObjectId('52ea18834d61631e7e010000'), "_id"=>{"$nin"=>[]}} update={"$set"=>{"band_id"=>nil}} flags=[:multi]
COMMAND database=service_boot_camp_development command={:getlasterror=>1, :w=>1} runtime: 0.5800ms
MOPED: 127.0.0.1:27017 INSERT database=service_boot_camp_development collection=services documents=[{"_id"=>BSON::ObjectId('52ea18834d61631e7e020000'), "date"=>"2014-02-02", "time"=>"10:00", "place"=>"Where it's at", "name"=>"My great name", "theme"=>"The service's theme", "team"=>{"_id"=>BSON::ObjectId('52ea18834d61631e7e030000')}, "band"=>{"_id"=>BSON::ObjectId('52ea18834d61631e7e010000')}}] flags=[]
COMMAND database=service_boot_camp_development command={:getlasterror=>1, :w=>1} runtime: 2.7460ms
I guess I'm doing something wrong, but I have no clue if it is in the database model or in the form... or anything else...
You will not be able to do it this way. When you create an embedded document, its _id and all of its data are embedded directly within the parent document. This is in contrast to an association, where the document with the belongs_to gets a foreign key which points to its associated parent document. So here, your User documents each have a team_id and band_id, but when the database tries to get the documents, it can't find them, since you can't query directly for embedded documents; you need the parent document first. For more, see the Mongoid documentation.
Another potential issue is that you have multiple belongs_to definitions in the User models. This will also cause an issue, because for each one of those, Mongoid will attempt to create a team_id and band_id. You should name them separately and specify a class name; perhaps names like :presiding_team and :message_team, :lead_band and :guitar_band, etc. This answer should show you what that would look like.
I would recommend making the Team and Band separate referenced documents instead of embedded documents, since you won't be able to reference users effectively while they're embedded.
Hope this helps.

Why am I getting "Can't mass-assign protected attributes" after adding a field to a product on spree?

I'm trying to add a field to products on spree which is just a checkbox which is just ment to mark products if they are for sale or they're internal products.
I've added the migration and finally figured out how to add the checkbox on the form, but when I click Update I get Can't mass-assign protected attributes: for_sale
This is the migration
class AddProductForSaleField < ActiveRecord::Migration
def up
add_column :spree_products, :for_sale, :boolean
end
def down
remove_column :spree_products, :for_sale
end
end
Here's the field being added
Deface::Override.new(:virtual_path => "spree/admin/products/_form",
:name => "for_sale",
:insert_before => "code[erb-silent]:contains('track_inventory_levels')",
:partial => "spree/admin/products/for_sale")
And this is the partial
<%= f.field_container :for_sale do %>
<%= f.label :for_sale, t(:for_sale) %>
<%= f.check_box :for_sale, { :checked => true } %>
<% end %>
got it, was missing the model part
Spree::Product.class_eval do
attr_accessible :for_sale
end
Mass Assignment is the name Rails gives to the act of constructing your object with a parameters hash. It is "mass assignment" in that you are assigning multiple values to attributes via a single assignment operator.
The following snippets perform mass assignment of the name and topic attribute of the Post model:
Post.new(:name => "John", :topic => "Something")
Post.create(:name => "John", :topic => "Something")
Post.update_attributes(:name => "John", :topic => "Something")
In order for this to work, your model must allow mass assignments for each attribute in the hash you're passing in.
There are two situations in which this will fail:
You have an attr_accessible declaration which does not include :name
You have an attr_protected which does include :name
It recently became the default that attributes had to be manually white-listed via a attr_accessible in order for mass assignment to succeed. Prior to this, the default was for attributes to be assignable unless they were explicitly black-listed attr_protected or any other attribute was white-listed with attr_acessible.
If it's a permission issue then you can add :
Spree::Product.class_eval do
attr_accessible :variable_1, :variable_2 :as => [:default, :product]
end
Marking it as default for a specific model, will remove the mass-assignment warning message !