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

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.

Related

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

Rails 4 paperclip Background job

CURRENT LOGIC
First, I think I have read all the articles on paperclip and I'm still stucked despite all the infos learned... So I can say taht your help is really really precious.
Secondly, I do not use delayed_paperclip, nor s3_direct_upload (but jquery directUpload).
I have users profile with 4 differents pictures : logo, avatar, worka, workb
Each format (logo, avatar, worka, workb) has 3 styles (:small, :thumb, :medium)
User can update its pictures => so update action is concerned
The 4 files fields are in a form with other classic fields (name, email, ...)
Upload is managed by jQuery DirectUpload + Paperclip
When user click the file field to add an image:
jQuery DirectUpload uploads the file into a temp directory on s3
jQuery callbacks with the url(key)
The url is assigned as :temp to an hidden field generated in javascript
When form submit button is pressed:
I assign to paperclip the url of the file uploaded with direct upload, with the help of #user.logo.temp which contains the url(key)
Paperclip generates all styles
<input type="hidden" name="user[logo_attributes][temp]" value="https://bucketname.s3.amazonaws.com/temp/e4b46d01-5d69-483b.jpg">
MY PROBLEM: STYLES GENERATION BRINGS ME TO HEROKU IDLE and TIMEOUT #15 #12
.
ATTEMPTS: I tried to isolate the upload process to put it in a background job
.
I can't figure out how to block paperclip styles generation at the first upload and generate them after in a background job
before_post_process block all post process, even in background job
I didn't use .reprocess! Since paperclip 4, update is triggered and... infinite loop..., so I use .assign and .save instead
The file is correctly assigned from S3 hosted file, then processed by paperclip
I'm not sure about the file from file field, if it is uploaded or not (no trace of that in the console, but since the form is submited, the file too, even if unused.
.
NEED: STYLES BLOCKED THEN PROCESSING IN A BACKGROUND JOB
My Logo Model
class Logo < Document
S3_TEMP_URL_FORMAT = %r{\/\/bucketname\.s3\.amazonaws\.com\/(?<path>temp\/.+\/(?<filename>.+))\z}.freeze
has_attached_file :attachment,
styles: { medium: "300x300#" },
convert_options: { medium: "-quality 75 -strip" },
default_url: ":parent_type/:class/:style/missing.png",
path: "/documents/:parent_type/:id_partition/:class/:style/:basename.:extension"
validates_attachment :attachment,
content_type: { content_type: ["image/gif", "image/png", "image/jpg", "image/jpeg"] },
size: { less_than: 1.megabyte }
validates :temp,
# presence: true,
format: { with: S3_TEMP_URL_FORMAT }
before_save :set_attachment
after_save :set_remote_url
before_post_process :stop_process
def stop_process
false
end
def styles_process
self.attachment.assign(attachment)
self.save
end
def set_attachment
# puts "BEGIN -- SET ATTACHMENT"
tries ||= 5
s3_temp_url_data = S3_TEMP_URL_FORMAT.match(self.temp)
s3 = AWS::S3.new
s3_temp_head = s3.buckets[ENV['S3_BUCKET']].objects[s3_temp_url_data[:path]].head
self.attachment_file_name = s3_temp_url_data[:filename]
self.attachment_file_size = s3_temp_head.content_length
self.attachment_content_type = s3_temp_head.content_type
self.attachment_updated_at = s3_temp_head.last_modified
rescue AWS::S3::Errors::NoSuchKey => e
tries -= 1
if tries > 0
sleep(3)
retry
else
false
end
end
def set_remote_url
s3_temp_url_data = S3_TEMP_URL_FORMAT.match(self.temp)
s3 = AWS::S3.new
self.attachment = URI.parse(self.temp)
self.save
s3.buckets[ENV['S3_BUCKET']].objects.with_prefix(s3_temp_url_data[:path]).delete_all
end
end
My Controller
def update
account_update_params = devise_parameter_sanitizer.sanitize(:account_update)
#user = User.find(current_user.id)
if #user.update_attributes(account_update_params)
# Here is the styles processing
# This is where the Resque background job would go
#user.logo.styles_process
set_flash_message :notice, :updated
redirect_to after_update_path_for(#user)
else
render :edit
end
end
My Form
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), method: :put, html: { class: "form-horizontal directUpload", role: "form" }) do |f| %>
<%= f.fields_for :logo do |l| %>
<%= l.file_field(:attachment, accept: 'image/gif,image/png,image/jpg,image/jpeg') %>
<% end %>
<input type="hidden" name="user[logo_attributes][temp]" value="https://bucketname.s3.amazonaws.com/temp/e4b46d01-5d69-483b.jpg">
<% end %>

Rails multiple select form: no implicit conversion of String into Integer

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)

bootstrap typehead dropdown doesn't expand over bootstrap collapse

So I have bootstrap collapse and inside it I have tabs, and inside one of the tabs I have a form with text_field that has bootstrap typeahead and the problem is that typeahead's dropdown dosn't expand over collapse.
that text_field with autocomplete is the last element in there.
here is the picture.
I want that dropdown expands below the collapse element (below the line on the picture)
EDIT:
Here is the haml for that view
- #i = 0
- #trainings.each do |training|
- #i = #i+1
.accordion#accordion2
.accordion-group
.accordion-heading
%a{:class => 'accordion-toggle', 'data-toggle' => 'collapse', :href => "#collapse#{#i}"}
= "Training #{#i}"
%div{:id => "collapse#{#i}", :class => 'accordion-body collapse'}
.accordion-inner
%pre= "Description: #{training.description}"
%ul.nav.nav-tabs#myTab
%li.active
%a{"data-toggle" => "tab", :href => "#planirano#{#i}"} Planirano
%li
%a{"data-toggle" => "tab", :href => "#napravljeno#{#i}"} Napravljeno
.tab-content
%div{:class => 'tab-pane active', :id => "planirano#{#i}"}
- training.exercises.each do |exercise|
%pre= "#{exercise.element.name} | #{exercise.description} | #{exercise.number_of_series}"
= form_for :training_exercise, :url => training_exercises_path(:training => training.id), remote: true, html: {:class => 'form-inline'} do |f|
= f.label :exercise
= f.text_field :exercise, :id => 'add_training_exercise'
= f.button :Add, :class => 'btn'
%div{:class => 'tab-pane', :id => "napravljeno#{#i}"} to sam napravio
f.text_ifeld :exercise, :id => 'add_training_exercise' is the field with autocomplete I am asking about.
EDIT:
and here is the rendered HTML
I somehow find the answer on stack overflow the solution is
.accordion-body.in { overflow:visible; }
It is from here.
I am sorry for asking question that already has the answer but I really was not able to find it because I didn't guess the right word for searching.
Applying the following css works only partially, accordion-body.in { overflow:visible; }, since it only displays the overflow of the "slice" that is being expanded. You'd need to apply it to the parent as well. In addition the above css affects the expand/collapse effect; i.e. the content of what is being shown get's displayed over the accordion, versus gradually being shown. A solution I tried is to:
1. Apply the overflow:visible only to the parent, i.e. #myAccordion { overflow:visible } AND
2. Apply overflow:visible only to the "slice" being opened when it is needed (on open), and removing it on close, like so:
$("#myAccordion").collapse();
// Here, we are attaching event handlers to the accordion "slice" body so that we can update it's overflow when opened AND when it's about to be closed.
$("#myAccordion .accordion-body").on("shown", function () {
$(this).css("overflow", "visible");
});
$("#myAccordion .accordion-body").on("hide", function () {
$(this).css("overflow", "hidden");
});
This worked for me since my typeahead is in a navbar
.navbar-collapse.in{
/*allows typeahead to overflow nav bar during collapse*/
overflow-y:initial !important;
}

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 !