I have a static array in my User model declared like this:
class User < ActiveRecord::Base
...
states = ['NYC', 'CAL', ...]
...
end
I know I should create a model for the states but I figured I just need the list for registration purposes. When I try to use it in a view like this:
= f.select(:state, options_for_select(states))
I get a Undefinded Method error. I tried using instance variables through the controller and that didnt work either. Whats the correct way of doing this?
You should be able to access it as
User::STATES
that's assuming you upcase it from states to STATES since that's idiomatic :)
Another option is to create a class method that returns the array
def self.states
['NYC', 'CAL', etc]
end
Capitalizing the constant in the model and using the Model::CONSTANT syntax is probably the most common way to do this.
States collection is not specific to a user, so I wouldn't have it under the User model. As shown in this answer, I would add a us_states helper to your application, and use that in your views:
= f.select(:state, options_for_select(us_states))
Related
The User model has an id column that is used all throughout your schema, but you'd like to create other models using the username rather than the id:
User.create!(username: "phillip")
User.create!(username: "joe")
Message.create!(from_username: "phillip", to_username: "joe", message: "hello")
Here, 'from_username' and 'to_username' don't actually exist on the table. Rather, there is a 'from_id' and 'to_id'. You can add 'from_username' and 'to_username' as methods to the model, but then you can't create a new Message using them.
What is the recommended way to add these 'virtual attributes' to a model?
Most idiomatic way
You could define a custom create function, like this:
class Message < ActiveRecord::Base
def self.create_from_usernames(from_username, to_username)
from_user = User.find_by_username(from_username)
to_user = User.find_by_username(to_username)
self.create(from_user: from_user, to_user: to_user)
end
end
Making call-site code handle this itself
This is still somewhat idiomatic, but not very DRY.
from_user = User.find_by_username("phillip")
to_user = User.find_by_username("joe")
Message.create!(from_user: from_user, to_user: to_user)
Using create directly
This is not really recommended, but you could do something like this:
class Message < ActiveRecord::Base
attr_accessor :from_username, :to_username
before_create :find_users
private
def find_users
self.from_user = User.find_by_username(from_username)
self.to_user = User.find_by_username(to_username)
end
end
This leads to somewhat "magical" behavior though, and there should be more error checking / handling if you're going to do it.
I think the normal way to do that would be to include these attributes into your Message model. So instead of saving the user_id you can also save the user_name. Otherwise, just work around it and get the username by the user_id. May be I did not fully understand your question, though..
I have a join table called ProductFeatures which joins Product and Feature instances via has_many: ..., through: product_features, and has an additional column called rating.
I want to add .rating method on Feature which will return a rating float based on specific product instance that is calling it. Something like:
Product.find(...).features.first.rating #=> should specific product_feature rating
I've tried:
passing caller_id as an argument to .rating. This works, but makes me use product.id each time I want to get a specific product rating feature.
Obtaining a caller instance id from inside the method using .caller (with binding_of_caller, or vanilla Ruby), but .caller does not seem to let me get a calling instance id, and would also fail in tests as the caller would be the spec's ExampleGroup
You can get these data with other way.
I want to add #rating method on Feature which will return a rating
float based on specific product instance that is calling it. Something
like:
## Controller add this code snipped
get_feature = Product.find(...).features
rating(get_feature)
protected
def rating(get_all_feature)
all_rating = []
get_all_feature.each do |feature|
all_rating << feature.product_features.each { |u| u.rating }
end
all_rating
end
Hope this help you!
What should one use to persist instance variables through different controller#actions, without using the session?
Here is an example of the problem:
There is an Hospitalization which has_one Prescription
From hospitalization#show I have a link_to prescription#new with additional parameters as seen bellow:
<%= link_to t('hospitalizations.prescription'), new_prescription_path(hospitalization_id: #hospitalization.id,...)
And the trick is: at prescription#new I can retrieve
#hospitalization = Hospitalization.find_by(id: params[:hospitalization_id])
But when I press submit and it comes to prescription#create, #hospitalization answers to nil when trying to do something like
#prescription = #hospitalization.build_prescription(prescription_params)
How would be the best way to instantiate these parameters so they persist or get easily carried between the controller#actions one needs? How should I instantiate the belonging model Prescription? Should I put the ID directly there, on prescription#new? Like:
#prescription = Prescription.new(hospitalization_id: params[:hospitalization_id])
Use neseted resources this way:
resources hospitalizations do
resource :prescription
end
This way your path is going to look like this:
/hospitalizations/:hospitalization_id/prescription
and you can use the following to generate it:
new_hospitalization_prescription_path(#hospitalization)
now in your controller you can access the parameter as params[:hospitalization_id]. For example in the create method of your PrescriptionController:
def create
hospitalization = Hospitalization.find(params[:hospitalization])
prescription = Prescription.new(hospitalization: hospitalization, ...)
# ...
end
How do I follow OOP standards within RoR controllers?
The setup: submitting data to a form & then manipulating it for display. This is a simplified example.
app/controllers/discounts_controller.rb
...
def show
#discount = Discount.find(params[:id])
formatted = calc_discounts(#discount)
end
...
private
calc_discounts
half_off = #discount.orig_price * .5
quarter_off = #discount.orig_price * .25
return {:half => half_off, :quarter => quarter_off}
end
...
Or is it better to place this in a library with attr_accessor and then create new instances of the library class within the controller? Or is there an even better way of accomplishing this?
The question to ask yourself is "is this logic useful for the view, model, or both?"
If the answer is that it's only useful for display purposes, I would put that logic in a view helper. If it's also beneficial to the model, put it there. Maybe something like this:
class Discount
def options
{half: (self.orig_price * .5), quarter: (self.orig_price * .25)}
end
end
Then in your controller you can just locate the record in question:
def show
#discount = Discount.find(params[:id])
end
And display it in the view:
<h1>half: <%= #discount.options[:half] %> </h1>
<h1>quarter: <%= #discount.options[:quarter] %> </h1>
Well, you can can add half_off and quarter_off as methods to your model:
class Discount < ActiveRecord::Base
def half_off
orig_price * 0.5
end
def quarter_off
orig_price * 0.25
end
end
.. and then do the following:
def show
#discount = Discount.find(params[:id])
end
Now you can call #discount.half_off and #discount.quarter_off in your view..
First off, you've got some syntax issues there. When you define methods you need to use a def keyword, and since Ruby 1.9 you can use a shortcut when defining hashes that avoids hashrockets, so it's:
def calc_discounts
half_off = #discount.orig_price * .5
quarter_off = #discount.orig_price * .25
return {half: half_off, quarter: quarter_off}
end
Also, you defined a local variable formatter inside of your controller's show method. This doesn't actually do anything but assign some values to a variable that only exists within that method. Only the controller's instance variables (variables with an #) can be passed to the view.
That being said, the best practice in RoR is to keep controllers "skinny", which means only using controllers to authenticate, authorize, load a model, assign an instance variable for you view, handle errors with any of the former, and then render the view according to the format requested.
It's another best practice not to include much logic in your views. This way, your logic can be shared with and reused by other views instead of having to be re-written for each new view you make. It also makes your views more readable, as they will read like simple lists of what is to be shown instead of making people try to decipher embedded ruby all over the place.
If the code is something that one of your other models could benefit from being able to use, put it inside your model code (or make a new plain old Ruby object if the logic is complex or not really cohesive with the existing model).
If the logic is something that is just for making a view prettier or in a better format, but won't actually be used by the models, then it should go in some type of view helper or decorator.
I have this loop in rails
- #companies.people.each do |person|
%p
Hello there :
= "#{person.manager.name} (#{person.manager.email})"
but i only want to print the managers name once.....but lots of people have the same manager and they are printing dupes...any idea how to not print dupes here
Wouldn't you rather do:
#companies.managers do |manager|
...
So you need to amend the underlying model (Company?) with a managers method. And whether that's done via a scope, or a model relation or alfonso's brute force answer, we don't have enough information to determine. But in any case this logic is best tucked away in the model and not exposed in the view.
class Company
scope :managers, ->(){where(manager: true)}
end
module EmployeeListViewHelper
def manager_list
Company.managers.each do |m|
content_tag(:p, "Hello There : #{m.name} #{m.email}")
end
end
end
Then just this in your view:
= manager_list
Well, it looks like you're going about this probably the wrong way. If you don't want the manager's name duplicated for each person, you might have to group people under managers.
Your view then should look hierarchical, people under the manager should be visually placed like that, as well.
You could do this with the uniq method:
#companies.people.map{|p| p.manager}.uniq