Rails select from database - ruby-on-rails

Can somebody explain this ? , it is from ruby guides
<%= collection_select(:person, :city_id, City.all, :id, :name) %>
I have an attachment model, I want to select its version with combobox when I create an object, and also I want a new version option.
here what attachment has
def change
create_table :attachments do |t|
t.string :filename
t.attachment :file
t.string :version
t.text :description
t.timestamps null: false
end
UPDATE:
<%= f.collection_select( :version, Attachment.where.not(version: nil), :version, :version) %>
it is working like that, but I don't understand,

Try this to avoid the nil value of the version:
collection_select(:f, :attachment_id, Attachment.where.not(version: nil), :id, :version)
Explanation of How collection_select Works:
collection_select(
:f, # field namespace
:attachment_id, # field name
# result of these two params will be: <select name="f[attachment_id]">...
# then you should specify some collection or array of rows.
# In your example it is:
Attachment.where.not(version: nil)
# Then, you should specify methods for generating options
:id, # this is name of method that will be called for every row, result will be set as key
:version # this is name of method that will be called for every row, result will be set as value
)
See this and this for more information.

check accepted answer from this thread for explanation on how collection_select works: Can someone explain collection_select to me in clear, simple terms?
In this select:
<%= collection_select(:f, :attachment_id, Attachment.all, :id, :version) %>
you display all versions from already created attachments, so if attachments table is empty you will get null

Related

rails not receive array value strong parameter

I had a problem that it didn't received array value when i tried to submit array value which i used checkbox.
Here is my code:
This is migration file:
create_table :users do |t|
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
t.text :receive_mail, array: true, default: []
...
This is function which i put array attributes:
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [
:address,
{receive_mail: []}
])
end
This is view file:
<div class="field">
<%= f.collection_check_boxes(:receive_mail, [[0, "receive new article"], [1, "receive new ebook"]], :first, :last, include_hidden: false) do |b| %>
<%= b.label {b.check_box + b.text} %>
<% end %>
</div>
When i submit, i checked params and it had a value as "receive_mail"=>["0", "1"], but when i checked when it permited, it always return "receive_mail"=>[].
I didn't understand why it didn't receive value.
Any help would be great appreciated.
I think your issue stems from your use of the keys option, which appears to only accept a list of symbols. You could try using:
devise_parameter_sanitizer.permit(:sign_up, keys: [
:address,
:receive_mail
])
However, I think you'll run into the following issue, mentioned in the Devise docs:
If you have some checkboxes that express the roles a user may take on registration, the browser will send those selected checkboxes as an array. An array is not one of Strong Parameters' permitted scalars, so we need to configure Devise in the following way ...
Given that, here's a variant of your code based on the example from the docs that might work:
devise_parameter_sanitizer.permit(:sign_up) do |user_params|
user_params.permit({receive_mail: []}, :address)
end

How to default collection_check_boxes to checked?

I have this line that I'm trying to default to checked <%= f.collection_check_boxes :committed, checked, Date::ABBR_DAYNAMES, :downcase, :to_s, %>
In db t.text "committed".
I tried variations of checked & true, but maybe I overlooked something.
Here's the Gist of it.
Here is a quick answer on how to add checked as the default to the collection_check_boxes form helper since it took me some time to figure it out. Break it into a block and you can set checked and add classes. More info at http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_check_boxes.
<%= f.collection_check_boxes(:author_ids, Author.all, :id, :name) do |b| %>
<%= b.label(class: "check_box") { b.check_box(checked: true, class: "add_margin") + b.text } %>
<% end %>
You are using a form_for, so that f is a form builder. This means that it is bound to the object you initialized it with, let's call it #habit. Since you're calling collection_check_boxes on the form builder, it will do something like #habit.send(:commit) to consult whether or not it should have the check box checked, and currently (apparently) it is not. In other words, if you want to use form_for you need to have this "everything is checked" fact represented in the model itself.
Now I am not sure what your model layer looks like, so I'll address a few scenarios. If you have a has_and_belongs_to_many relationship like this:
class Habit < ActiveRecord::Base
has_and_belongs_to_many :committed_days
end
class CommittedDay < ActiveRecord::Base
has_and_belongs_to_many :habits
# let's assume it has the columns :id and :name
# also, let's assume the n:m table committed_days_habits exists
end
Then I think the easiest way is in the controller itself do something like this:
def new
#habit = Habit.new
#habit.committed_day_ids = CommittedDay.all.map(&:id)
end
And in your ERB do:
<%= f.collection_check_boxes(:committed_day_ids, CommittedDay.all, :id, :name)
Now, it might be an overkill to do this with a has-and-belongs-to-many, especially with days of week (it means the CommittedDay table has 7 records, one for each day, which is kinda awkward). So you could also consider simply serializing an array of days of week into the db, and then just make sure the default for that column contains all of them.
The ERB will be similar to what you wrote:
<%= f.collection_check_boxes :committed, Date::ABBR_DAYNAMES, :downcase, :to_s %>
If you're using Postgres your class can be simply:
class Habit < ActiveRecord::Base
end
And the serialization code would be in the migration:
# downcase is used since in the ERB you are using :downcase for the id method
t.text :committed, default: Date::ABBR_DAYNAMES.map(&:downcase), array: true
If you are not using Postgres, you can use Rails serialization which is DB agnostic:
class Habit < ActiveRecord::Base
serialize :committed, Array
end
And then your migration would look like this:
t.text :committed, default: Date::ABBR_DAYNAMES.map(&:downcase).to_yaml

Why do I get the error "undefined method 'merge' for true:TrueCLass" when adding a checkbox in Rails

I am very new to development and was hoping someone could assist:
I receive the error "undefined method 'merge' error when I include the following checkbox:
<%= f.check_box :is_female, true %> <%= f.label :is_female, "Female" %>
<%= f.check_box :is_female, false %> <%= f.label :is_female, "Male" %>
The above code is in the profiles folder which I created using the following:
$ rails generate scaffold profiles description:string
I then generated the following migration:
$ rails generate migration add_websites_to_profiles website:string
This created the migrate file *************_add_website_to_profiles.rb
Here is the add_websites_to_profiles.rb:
class AddWebsiteToProfiles < ActiveRecord::Migration
def change
add_column :profiles, :website, :string
add_column :profiles, :is_female, :boolean, default: false
end
end
I manually added the following:
add_column :profiles, :is_female, :boolean, default: false
I receive the error when I have the true and false command in the checkbox. When I remove true false it appears on the page with no error, but it is not saved to db.
You are getting mixed up between two ways of generating a check box I think.
The form builder version, f.check_box, you are using expects a hash as the second parameter. You'd need to call f.check_box(:is_female, {}, true) for a value of true.
check_box_tag on the other hand does expect a value as the second parameter. You could use check_box_tag(:is_female, true) instead.
However, as #Santosh points out in the comments, you'd probably be better off having a pair of radio buttons if you want the object to be either male or female.

Rails 4: Accept year as string and store as date

In my Rails 4 app, I'd like to accept a year as a string in a text field but store it as a date in the database, in case I ever decide to accept full dates. However, when I try to do this, I keep getting a nil date value. I've verified that the controller is properly passing along the parameters to the model, but at some point before the validation happens, the date has been set to nil.
Is what I'm trying to do possible? And if so, what critical step have I missed? Thanks!
This is the relevant part of the form:
<p>
<%= f.label :publication_date %><br>
<%= f.text_field :publication_date, :size => 4 %>
</p>
And the schema:
create_table "books", force: true do |t|
t.string "title"
t.datetime "created_at"
t.datetime "updated_at"
t.date "publication_date"
...
end
And the model validation:
validates :publication_date, length: { is: 4 }, numericality: true
before_validation :check_pub_date
def check_pub_date
# this prints nil
logger.debug "Publication date: #{self.publication_date}"
end
before_save :convert_pub_year
def convert_pub_year
# Never gets here because validation never passes
logger.debug "Publication year: #{self.publication_date}"
if self.publication_date
self.publication_date = DateTime.strptime(self.publication_date, "%Y")
end
end
Given that your model is expecting a full date to come through, you are hitting a problem when you are passing only a string from your form - I would create a simple text_field to capture the year and go from there...
In your view
<%= text_field_tag :pub_year %>
In your controller
publication_year = params[:pub_year]
#book.publication_date = DateTime.strptime(publication_year, "%Y")
or, of course, factor the parsing of year into a full date into the model.
Edit - expanding on this, it's also possible to create a property in your model that doesn't get saved to the database if you prefer, so in your model, you would have this:
attr_accessor :pub_year
Then, in your convert_pub_year, substitute in pub_year instead of publication_date
def convert_pub_year
if self.pub_year
self.publication_date = DateTime.strptime(self.pub_year, "%Y")
end
end
You controller would need to allow the pub_year parameter in addition to any others you want - so in the private methods - something like the following:
def book_params
params.require(:book).permit(:title, :pub_year)
end
And finally in your view, remove publication_date' and includepub_year`:
<%= f.label :pub_year %><br>
<%= f.text_field :pub_year %>
Hope that helps
The problem exists because validation occurs AFTER assignment of the string passed from the controller, i.e. what you are validating is not the string but the attribute after the type cast to a date. If this fails (because - as in your case - the date is incomplete and consists only of a year), the attribute is nil.
For situations like yours you might want to look into the attribute_before_type_cast functions/variables which will give you access to the original date string BEFORE it was type cast into nil.

Rails form not saving 'Type:' field

I generated a simple Post scaffold which has title:string body:text category:string. I later added type:string (and performed the migration) to the model and added the selection fields in new.html.erb and edit.html.erb. I also added validation for all these fields.
<%= f.label :type %>
<%= f.select :type, Post::TYPES, :prompt => "Select post type" %>
When I try and create a post it gives me:
"There were problems with the following fields:
Type can't be blank
Type is not included in the list"
Even though I DO make a selection. Am I missing something obvious here?
Select code from Post class:
TYPES = [
["Job", "job"],
["Volunteer", "vol"]
]
validates_presence_of :title, :body, :category, :type
validates_inclusion_of :category, :in => CATEGORIES.map {|disp, value| value}
validates_inclusion_of :type, :in => TYPES.map {|disp, value| value}
The type field is a reserved field used for single table inheritance(STI). You have to rename the field.
Refer to this article for more details
Edit: Changed the link to point to the article provide by Matchu.
If you really want to, you can use field called type in Rails 4 by setting inheritance_column to something else:
class Product < ActiveRecord::Base
self.inheritance_column = :ruby_type
end
In Rails 3 and below, use method set_inheritance_column instead.

Resources