Suppose for an integer field named 'favfood' we represent the radio button choices as
0 indicates "no favorite"
1 indicates "Wine and cheese"
2 indicates "Burger with everything"
On our _edit view we display the radio buttons with the friendly labels above
On the /show view and /index view (and in several other places) when we display the user preference we display the same corresponding long text.
It seems non-DRY to put literal strings next to each radio button in _edit and then provide some logic to display the SAME literal string on /show and /index etc etc etc based upon the current value.
Furthermore, we have to repeatedly use the SAME logic that says if the value=1 display "The first choice", if the value = 2 display "The second choice".
What's the "rails-way" to handle user-friendly labels for radio labels so the labels (AND their association with values of the field) are defined once ?
There are two things you should do. One use Internationalization (I18n) so that your text is stored in your en.yml file and you can use the labels instead of strings:
# en.yml
en:
no_favorite: 'No favorite'
wine_and_cheese: 'Wine and Cheese'
burger_with_everything: 'Burger with everything'
# You can then translate the labels like this
I18n.t :no_favorite
I18n.t :wine_and_cheese
I18n.t :burger_with_everything
The next step would be to create some kind of hash or class that stores and translates the integer values for you. A simple array solution might look like this:
OPTIONS = [:no_favorite, :wine_and_cheese, :burger_with_everything]
You could then use the options like this:
selected_value = 1
I18n.t OPTIONS[selected_value] # => wine_and_cheese
OPTIONS.index :wine_and_cheese # => 1
Furthermore, if the selected option was a model value (say User#food_preference) you could define a function for displaying that user's food preference:
class User
OPTIONS = [:no_favorite, :wine_and_cheese, :burger_with_everything]
def display_food_preference
I18n.t OPTIONS[food_preference]
end
end
I'm not sure what you're trying to do here, but why not store the strings in the model. Sth like:
class User < ActiveRecord::Base
def favfood_description
"bla bla" if favfood == 0
end
end
Related
If there is value in marital_status then prompt should not be displayed but in my case it's displaying. My code is mentioned below. Please help.
= select_tag( 'request[marital_status]',
options_for_select(marital_status_options,
#employee.marital_status.try(:upcase)), prompt: "Select Marital Status", id: 'employee_marital_status', disabled: #request.submitted?)
In employee_helper.rb
def marital_status_options
Employee::MaritalStatus::ALL.zip(Employee::MaritalStatus::ALL)
end
In employee model
module MaritalStatus
MARRIED = 'MARRIED'
SINGLE = 'SINGLE'
DIVORCED = 'DIVORCED'
ALL = [MARRIED, SINGLE, DIVORCED]
end
You're very close. The problem here likely stems from your marital_status_options method: this will simply return DIVORCED as it evaluates to the last line due to your assignment.
Therefore, you might find the value is selected if your instance contains 'DIVORCED', though not either of the other values; your instance's value needs to match one of these for it to be selected instead of the prompt.
You likely want to change this:
def marital_status_options
MARRIED = 'MARRIED' # this will be evaluated first
SINGLE = 'SINGLE' # then this
DIVORCED = 'DIVORCED' # finally, this will be evaluated and returned as 'DIVORCED'
end
To an array, either:
def marital_status_options
['MARRIED', 'SINGLE', 'DIVORCED']
end
Or, to present the options as lowercase but keep uppercase values in the db:
def marital_status_options
[['Married', 'MARRIED'], ['Single', 'SINGLE'], ['Divorced', 'DIVORCED']]
end
Take a look at the docs on options_for_select and you'll see who they can be setup.
Further down the line, you might want to consider switching to enums - these are very handy for managing selections such as these, and auto generate methods such as Employee.married, employee.divorced?, and so forth.
As someone else has mentioned, it's best practice to store data such as this in the relevant model, though I'd argue these should be stored as a constant as they won't be changing. So one of the following:
# employee.rb
MARITAL_STATUSES = ['MARRIED', 'SINGLE', 'DIVORCED'].freeze
# or
MARITAL_STATUSES = [['Married', 'MARRIED'], ['Single', 'SINGLE'], ['Divorced', 'DIVORCED']].freeze
= select_tag('request[marital_status]',
options_for_select(Employee::MARITAL_STATUSES,
#employee.marital_status.try(:upcase)),
prompt: "Select Marital Status",
id: 'employee_marital_status',
disabled: #request.submitted?)
Hope that helps - let me know if you've any questions or need anything else.
The format and usage is correct. Kindly verify if #employee.marital_status.try(:upcase) exactly matches one of the marital_status_options provided here.
That looks like a probable case for such a behaviour.
Also, the first argument expected in select_tag needs to in an appropriate format, in this case, an array of strings.
Hence, your method marital_status_options should return an array of options to be used for dropdown.
def marital_status_options
['MARRIED', 'SINGLE', 'DIVORCED']
end
= select_tag "request[marital_status]", options_for_select(Employee.marital_status_options,
#employee.marital_status.try(:upcase)), :include_blank => '--Select Marital Status--', id: id: 'employee_marital_status', disabled: #request.submitted?
It's a good practice to define marital_status_options(Business logic) inside model: -
Assuming that it's Employee model
def self.marital_status_options
[
["MARRIED","MARRIED"],
["SINGLE","SINGLE"],
["DIVORCED", "DIVORCED"]
]
end
Reason that it's not selecting your default marital_status is because if #employee.marital_status.try(:upcase) will not match any of the marital_status_options, it will show your prompt option, so check it carefully that if #employee.marital_status.try(:upcase) matches any of the given options of select tag's option.
I want to check if the variable which is basically a user input is a 10 digit phone number or not.
There are 2 sets of validations:
- If num is less than 10 digit then prompt a msg
- if num is a string instead rather than integer
#phone = params[:phone_num]
puts "phone_num: #{#phone}"
if #phone.is_a? Integer
puts "phone_num is int"
if #phone.to_s.length == 10
puts "10 digit"
perform(#phone)
#output = "Valid Number, will receive a call"
end
else
puts "Wont be calling"
#output = "The number is invalid"
end
The output that I get is always The number is invalid no matter what I enter in text box. There are many stack overflow answering dealing with different questions but wondering why my code didn't work.
There is standard validation (length) & (numericality) for this:
#app/models/user.rb
class User < ActiveRecord::Base
validates :phone_num, length: { is: 10 }, numericality: { only_integer: true }
end
This type of validation belongs in the model.
Notes
Your controller will look as follows:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def create
#user = User.new user_params
#user.save #-> validations handled by model
end
end
There's a principle called fat model, skinny controller - you should put "data" logic in your model.
The reason for this is to remove inefficient code from the controller.
It gives you the ability to delegate much of your logic to the Rails core helpers (validations for example), instead of calling your own mass of code in the front-end (like you're doing).
Each time you run a Rails app, the various classes (controller & model) are loaded into memory. Along with all of the Rails classes (ActiveRecord etc), your controllers & models have to be loaded, too.
Any extra code causes causes bloat, making your application buggy & unusable. The best developers know when to use their own code, and when to delegate to Rails. This example is a perfect demonstration of when to delegate.
The output that I get is always The number is invalid no matter what I
enter in text box.
The reason why your code always falls back to else part because the values that are coming from the params will always be strings. So the value of params[:phone_num] is a string. So your code is failing here if #phone.is_a? Integer. Instead you need change it to params[:phone_num].to_i
#phone = params[:phone_num].to_i
puts "phone_num: #{#phone}"
if #phone.is_a? Integer
puts "phone_num is int"
if #phone.to_s.length == 10
puts "10 digit"
perform(#phone)
#output = "Valid Number, will receive a call"
end
else
puts "Wont be calling"
#output = "The number is invalid"
end
Note:
Yes. This is poor way to perform validations. I'm just answering the OP's question.
Take a look at this - A comprehensive regex for phone number validation - how to determine a string looks like a phone number. There's a very complex regex, because people have various forms for entering phone numbers!
I personally don't like super complex regexes, but it's pretty much what they were invented for. So this is when you want to figure out what sorts of forms are acceptable, write some tests, and make your code pass to your acceptance based on the massive link above!
edit: your code is wrong in a bunch of places; params are already a string, so try this! Remember your nested if/else/end, too.
#phone = params[:phone_num]
if #phone =~ /\A\d+\Z/ # replace with better regex
# this just means "string is all numbers"
puts "phone_num is int"
if #phone.length == 10
puts "10 digit"
perform(#phone)
#output = "Valid Number, will receive a call"
else
puts "Number length wrong, #{#phone.length}"
end
else
puts "Wont be calling, not a number: #{#phone.inspect}"
#output = "The number is invalid"
end
I have a database of different food menus that I am trying to search through. In general everything works fine, but I think that there must be a cleverer way in writing the code compared to what I am doing now.
Every menu has a set of boolean attributes describing the kind of kitchen (e.g. cuisine_thai, cuisine_italian, etc.). In my view I have a dropdown allowing the user to select the type of food he wants and then I am passing the param on and save it in my search-object.
#search.cuisine_type = params[:cuisine_type]
I then continue to check for the different kitchen types and see if there is a match.
#Filter for Thai cuisine
if(#search.cuisine_type == "cuisine_thai")
#menus = #menus.select{ |menu| menu.cuisine_thai == true}
end
#Filter for French cuisine
if(#search.cuisine_type == "cuisine_italian")
#menus = #menus.select{ |menu| menu.cuisine_italian == true}
end
#Filter for Peruvian cuisine
if(#search.cuisine_type == "cuisine_peruvian")
#menus = #menus.select{ |menu| menu.cuisine_peruvian == true}
end
Eventhough the way I do it works, there must be a better way to do this. I feel that the value stored in #search.cuisine_type could just determine the attribute I check on #menus.
Any help on this is greatly appreciated. Thanks!
Yes, your intuition is correct!
I'll assume #menus is an array are ActiveRecord Menu objects, and that the cuisine_* attributes correspond to database columns. In this case you can use ActiveRecord's attributes method.
Every ActiveRecord object has an attributes property. The docs (Rails 4.2.1) say:
attributes()
Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
To verify this is the case, if you peek at the attributes of a given menu, you should see a hash containing:
{
"cuisine_italian" => true,
"cuisine_thai" => false,
# etc.
}
Also, as a minor point of code readability, these two statements are effectively the same:
#menus = #menus.select { ... }
#menus.select! { ... }
Therefore, your search can be rewritten as:
if #search.cuisine_type.present?
#menus.select! { |m| m.attributes[#search.cuisine_type] }
end
Wouldn't it be better if you had column "cuisine" in database and have it set to thai, italian and so on?
Then you'd only check if certain food matches array of kitchens selected by the user.
I have a model called foo with a date field.
On my index view, I am showing a typical "weekly view" for a specified week. To put the data in my view, I loop through each day of the specified week and query the data one day at time. I do this so that I can make sure to put a NIL on the correct day.
foos_controller.rb
for day in 0..6
foo = Foo.this_date(#date+day.days).first
#foos[day] = foo
end
index.html.haml
- for day in 0..6
%li
- if #foos[day].nil?
Create a new foo?
- else
Display a foo information here
Obviously, there's a lot of things wrong here.
I should find someone smart member to tell me how to write a good query so that I only have to do it once.
I should not have any if/else in my view
My goal here is to either show the content if the it is there for a particular day or show a "create new" link if not.
thanks for the help in advance!!
First, I have no idea what this_date actually does, but I'll assume it's retrieving a record with a specific date from your datastore. Instead of doing 7 queries, you can condense this into one using a date range:
Foo.where(date: (#date..(#date + 6.days)))
You can tack on a .group_by(&:date) to return something similar to the hash you are manually constructing, but using the actual dates as keys instead of the date offset.
To iterate over the dates in the view, I would recommend using Hash#fetch, which allows you to define a default return when a key is not present, e.g:
hash = { :a => 1, :b => 2 }
hash.fetch(:a){ Object.new } #=> 1
hash.fetch(:c){ Object.new } # #<Object:...>
The question now is what object to substitute for nil. If you want to avoid using conditionals here, I'd recommend going with the NullObject pattern (you could involve presenters as well but that might be a bit overkill for your situation). The idea here is that you would create a new class to substitute for a missing foo, and then simply define a method called to_partial_path on it that will tell Rails how to render it:
class NullFoo
def to_partial_path
"null_foos/null_foo"
end
end
You'll need to create partials at both app/views/foos/_foo.html.erb and app/views/null_foos/_null_foo.html.erb that define what to render in each case. Then, in your view, you can simply iterate thusly:
<% (#date..(#date + 6.days)).each do |date| %>
<%= render #foos.fetch(date){ NullDate.new } %>
<% end %>
Is this appropriate for your situation? Maybe it's also a bit overkill, but in general, I think it's a good idea to get in the habit of avoid nil checks whenever possible. Another benefit of the NullObject is that you can hang all sorts of behavior on it that handle these situations all throughout your app.
In the controller, how can I add a variable at the end of a params[]?
If I try this I get an error: params[:group_] + variable
How should it be done?
Edit per request
Ok, I have a form that sets groups of radio buttons with names like this:
group_01DRN0
Obviously I have different groups in the form (group_01AAI0, group_01AUI0, etc.) and the value is set according to the radio button selected within the group:
Radio button "group_01DRN0" could have value of "21" or "22" or "23", radio button "group_01AAI0" could have value of "21" or "22" or "23", etc.
In the DB I have every code (01DRN0, 01AAI0, 01AUI0, etc) so I want to select them from DB and iterate in the params value so I can get the radio button group value, I've tried this with no luck:
#codes=Code.get_codes
for c in #codes
#all=params[:group_] + c.name
end
Thanks.
p = params
p[:new_param_name] = new_param_value
It works for me (rails 3.2).
Nota: using p instead of altering params avoids altering original parameters.
params looks like a hash, but it really isn't. So if you need to "augment" params as you deal with the incoming data in your controller, invent a new data structure that includes either params or its members.
Added:
Maybe you're looking for
#codes=Code.get_codes
#all = []
for c in #codes
#all << params["group_#{c.name}"]
end