Using text options in a rails form - ruby-on-rails

I have a form for a rails model, where a specific input is saved down as text, and that text is in the drop down in the form.
At the moment, I have the following (using formtastic here):
= f.input :education_level, :as => :select, :collection => ["Some GCSEs or less", "College A levels", "Some University, no degree", "University graduate - Bachelors or equivalent", "Masters degree", "Professional Degree", "Doctorate"], :include_blank => false
Thing is. This doesn't seem to be a great way of doing this. It's loaded up my view with something that should probably be in the model, but that's not very good when it comes to supporting multiple locales.
So, what's the best way of doing this?

This discussion on the rails-i18n google group has an interesting solution:
#in a locale file
en:
education_levels:
gcses: Some GCSEs or less
collegeA: College A levels
some_university: Some University, no degree
bachelors: University graduate - Bachelors or equivalent
masters: Masters degree
professional: Professional Degree
doctorate: Doctorate
#And in a model:
class Education < ActiveRecord::Base
def translated_education_level
I18n.t(education_level, :scope => :education_levels)
end
end
#In a helper:
module EducationHelper
def education_levels
I18n.t(:education_levels).map { |key, value| [ value, key ] }
end
end
#And then in your view:
<%= f.select :education_level, education_levels %>

Related

Rails simple_form check_boxes save multiple selections to database

I'm trying to have a list to checkboxes with multiple selection. I have this array in the model.
QOL = ["your health",
"your stress levels",
"how safe you feel",
"your education",
"your home"
]
I'm trying to do somthing like this in my simple_form form.
<%= f.input :qolselections, as: :check_boxes, :collection => Goal::QOL %>
How can I save the selections to the database and be able to retrieve them as the selected string values?
Instead of a Constant, you should consider an enum:
class YourModel < ActiveRecord::Base
enum qol_selection: ["your health", "your stress levels", "how safe you feel", "your education", "your home"]
end
Then, your solution becomes similart to the one described in the following question Saving enum from select in Rails 4.1 (https://stackoverflow.com/a/23686698/637094):
f.input :qol_selection, :as => :select, :collection => YourModel.qol_selections.keys.to_a

How do I build a Rails form using inheritance and nested attributes?

I have a survey app, basically along the lines of Railscast 196, but with one snag: where the Railscast has one Question class, which has_many :answers, I have several:
Question (self.abstract_class = true)
BasicQuestion < Question
MultipleChoiceQuestion < Question
To make this work I had to override the questions getter in Survey, which seems a bit of a kludge but not too bad (is there a standard way to do this?):
Survey.rb
has_many :questions
accepts_nested_attributes_for :questions
def questions # simplified a bit for brevity
questions = []
[BasicQuestion, LikertQuestion, MultipleChoiceQuestion].each do |model|
questions += model.where(:survey_id => self.id)
end
questions
end
Survey_Controller.rb
def survey_params
params.require(:survey).permit(:name, :questions_attributes => [:id, :name])
end
So far, so good. The problem is this:
Again from the Railscast, I have this in surveys/edit.html.erb:
surveys/edit.html.erb
<%= f.fields_for :questions do |builder| %>
<%= render 'edit_question_fields', f: builder %>
<% end %>
However, this returns a hash of the form:
{ "survey" => { "name" => "Howard", questions_attributes => { "id" => "1", "name" => "Vince" }}}
Rails gives me an error: ActiveRecord::StatementInvalid (Could not find table '') --
presumably, because there is no Questions table (it's an abstract class).
So, how do I fix it? Without abandoning nested_attributes or inheritance entirely, I can think of four ways:
Switch to STI (instead of Question being an abstract class), include the _type field in the params hash, and go from there.
Let Survey deal with each question type separately:
Survey.rb
has_many :basic_questions
accepts_nested_attributes_for :basic_questions
has_many :multiple_choice_questions
accepts_nested_attributes_for :multiple_choice_questions
def questions
# same as before, still comes in handy
end
surveys/edit.html.erb
<% #survey.questions.each do |question| %>
<%= f.fields_for question do |builder| %>
<%= render 'edit_question_fields', f: builder %>
<% end %>
<% end %>`
This almost works, except that now my hash looks like this:
{ "survey" => { "name" => "Howard", "basic_question" => { "id" => "1", "name" => "Vince" }, "multiple_choice_question" => { "id" => "1", "name" => "Naboo" }}}
I need the questions indexed by, e.g., "basic_questions_attributes" instead of "basic_question" -- anyone know how to do this?
Override questions_attributes= in Survey.rb to sort it all out.
Create a new QuestionsFormBuilder object to handle everything, along the lines of "Rails nested attributes form for polymorphic/single table inheritance associations".
Obviously a primary concern is being able to drop in new Question subclasses later (or change the behavior of existing ones) with a minimum of hassle.
At the moment I'm inclined to go with option #3, as it seems simplest and most elegant, however, I'm not sure I'm not missing some better way to do this. (Or somehow screwing up the Question subclassing implementation.) Does anyone have any better ideas or more Rails-like ways of getting this to work?!
Take a look at using a form object to encapsulate the logic and creation of the questions? http://railscasts.com/episodes/416-form-objects
I would also look at using STI so that your Survey.rb doesn't need to redefine questions

Rails 4- Associations -Tutorial Movielist

Hi I am trying to use Rails on Rest 2 - movielist tutorial with rails 4 and making adjustments as I go for new rails. I am stuck on associations of Movies Roles. I have added to Movie.rb
class Movie < ActiveRecord::Base
has_many :roles, :dependent => :destroy
has_many :people, :through => :roles
validates_presence_of :title
def new_role=(values)
values.each do |i, hash|
unless hash[:name].blank?
roles.create(:person_id => hash[:person_id], :name => hash[:name])
roles.save
end
end
end
def deleted_roles=(values)
values.each do |role_id|
roles.find(role_id).destroy
end
end
end
and also to show _Form.html.rb that I render (excert below)
<b>Add New People</b><br />
<% (1..3).each do |i| %>
<%= select_tag 'movie_new_role_person_id', options_for_select(#people), {
:name => "movie[new_role][#{i}][person_id]"
} %>
<%= text_field_tag 'movie_new_role_name', '', {
:name => "movie[new_role][#{i}][name]"
} %><br />
<% end %>
</p>
<p>
<%= f.submit "Update" %>
</p>
It renders the list to choose from but when i submit - nothing is written to database table.
If I manually enter data in database then it displays on movielist page ie: "Stephen Spielberg - Director" etc...
Any help appreciated- Driving me nuts at this stage
I thought it might be params driven restriction but I do not have a good example of associations style params filter
Thanks
Alan
After debugging including Chicagogrrl's !flag I investigated the params.permits in the movies_Controller again and figured I would have to add the method types to the list of excepted. I could not find detailed info on syntax for this but trial and error paid off.
app/controllers/movies_controllers.rb (excerpt)
......
# Never trust parameters from the scary internet, only allow the white list through.
def movie_params
params.require(:movie).permit(:title,:description, :rating, :role, :deleted_roles=>[],
:new_role=> ['person_id', 'name'])
end
......
The delete_roles=>[] takes the array params and process to delete_roles method in movies.rb
the new_role=>['person_id', 'name'] takes the new_role individual params.
I Hope this saves somebody else some time andIf anybody needs anymore info just ask thanks again Alan

In Rails 3 how should I manage a model column with limited choices

In my Rails 3 application I have numerous models that have columns that have limited choices (IE a select box). It seems overkill in these cases to create another model and a relationship to the original model just to manage the choices.
One option I can think of is to just create a select box and have the choices in there, but that doesn't seem very DRY. Does anyone have a good suggestion how to handle this situation?
Thanks for looking.
You could create a constant in your model like so
# formatted as an array of options, option being an array of key, value
OPTIONS = [['Email', 'email'], ['Text', 'text'], ['Email and Text', 'both']]
validates_inclusion_of :field, :in => OPTIONS
Which can then be used to populate a select menu in a view very easily
Example using formtastic
<%= f.input :field, :as => :select, :collection => Model::OPTIONS %>
I usually do this with a constant list in the model.
class Model < ActiveRecord::Base
PROPERTY_OPTIONS = ['Option One', 'Option Two', ...]
validates_inclusion_of :property, :in => PROPERTY_OPTIONS
end
And in the view:
<%= f.select :property, Model::PROPERTY_OPTIONS %>
You can also use the enum_column plugin: https://github.com/electronick/enum_column
You can then render your select boxes in your views as follows:
<%= f.select :status, Model.columns_hash['status'].limit %>
(Where Model is an example model name, such as Book or Product, or whatever it is your application is really about.)
In some cases, I will just create a hash of options and use Class Methods to display and set them. For example, a Problem model with different statuses could be done like so:
def self.statuses
{:open => 1, :closed => 2}
end
Then you just store the integer value in the status_id of the model. You can configure getters/setters as well.

Internationalization for constants-hashes in rails 3

Could you tell me whats the best practice for storing constants with internationalization in rails3?
f.e. i want to have a constant-hash for haircolours for my user model:
# btw: how can I store such hashes in the locales.yml-files?
# en.yml
HAIR_COLOURS = { "brown" => 0, "white" => 1, "red" => 2, "dark-brown" => 3...}
# de.yml
HAIR_COLOURS = { "braun" => 0, "weiss" => 1, "rot" => 2, "dunkel-braun" => 3...}
# i18n.default_locale = :de
User.find(1).haircolour
=> 0
User.find(1).haircolour_str
=> "brown"
# i18n.default_locale = :de
User.find(1).haircolour
=> 0
User.find(1).haircolour_str
=> "braun"
I would suggest the following. Create a string column for the hair colour. This would normally be an enumeration column (ENUM), but this isn't supported by Rails unless you're okay with some SQL in your migrations.
In your model, restrict the colours to a few valid values.
class User < ActiveRecord::Base
# Store the colours in the database as string identifiers (my preference
# would be English, lower case, with underscores). Only accept known values.
validates_inclusion_of :hair_colour, :in => %w{brown white red dark_brown}
end
Then, in config/locales/en.yml:
en:
user:
hair_colours:
brown: brown
white: white
red: red
dark_brown: dark brown
And in config/locales/de.yml:
de:
user:
hair_colours:
brown: braun
white: weiss
red: rot
dark_brown: dunkelbraun
In any view, you can do:
<%= t "user.hair_colours.#{#user.hair_colour}" %>
Or you can write a helper method in app/helpers/users_helper.rb:
def translated_hair_colour(user)
t "user.hair_colours.#{user.hair_colour}"
end
Because I believe that translation is in principle a concern of the presentation, I would not create a method on the User model, but in principle there is nothing stopping you from doing:
class User
# ...
def hair_colour_name
I18n.t "user.hair_colours.#{hair_colour}"
end
end
Update:
Making select boxes in a view that are translated can be done in two ways. The first option is to use the translated values as a source. This requires the translations to be complete and accurate. If not all values are translated, the missing values will not be displayed in the select box.
<%= form_for #user do |user| %>
<%= user.select :hair_colour, t("user.hair_colours").invert %>
<%= user.submit %>
<% end %>
The second option is to use the validation values from your model. This is the "right" way, but it requires a slight adjustment to the setup of the validation.
class User < ActiveRecord::Base
HAIR_COLOURS = %w{brown white red dark_brown}
validates_inclusion_of :hair_colour, :in => HAIR_COLOURS
end
Now, in your views:
<%= form_for #user do |user| %>
<%= user.select :hair_colour,
User::HAIR_COLOURS.map { |c| [t("user.hair_colours.#{c}"), c] } %>
<%= user.submit %>
<% end %>
Of course, the mapping can be easily extracted into a helper.

Resources