How to get the parent objects class name from an attribute - ruby-on-rails

Is it possible to return the parent object of a given attribute?
Example
a = User.birthdate
a.parent_object ... should return the user record that is the parent of the birthdate attribute
A better example?
Helper
def item_grade(subject, obj)
obj.scale.grades.find(subject.grade_id).name # would return something like "Pass", "Fail", "Good Job"
end
In the view
item_grade(#course.subject, #course)
This approach requires two options to be passed to the helper. It seems I should be able to pass #course.subject and then get the parent object from that
Helper
def item_grade(subject)
a = subject.parent_object.scale
a.grades.find(subject.grade_id).name
end
View
item_grade(#course.subject)

This approach requires two options to be passed to the helper.
You can remove some duplication by doing this, for example.
def item_grade(obj, property)
obj.scale.grades.find(obj.send(property).grade_id).name
end
item_grade(#course, :subject)
Now you don't have to repeat #course in the call.
Having to pass two parameters is much less harmful than any sort of hackery you can come up with (thanks #muistooshort). There's no built-in way to do this.

Related

How to use an argument's value to build a `.collection` method for an associated model?

I'm not sure what the correct terminology is for my question, but is it possible to use an argument's value to "build" its appropriate .collection method?
For instance, a Submission has_many images, tags, and documents. And depending on what the user is interacting with, I'd like to create the appropriate association.
Initially, I had it set up for only the tags...
def add_to_submission(task, user, tag)
sub = Submission.find_or_create_by(task: task, user: user)
sub.tags << tag
end
But is there a way I can generalize it further so that the third argument can by more dynamic? So rather than only accept a tag, it could be used like
add_to_submission(#task, #current_user, #new_image)
Something along the lines of...
def add_to_submission(task, user, associated_item)
sub = Submission.find_or_create_by(task: task, user: user)
items = associated_item.pluralize
sub.items << associated_item
end
For dynamic calling of methods .send can be used. You can pass a symbol of the method name. You should be able to do something like this, but I would make sure to have good unit tests for your method.
def add_to_submission(task, user, associated_item)
submission = Submission.find_or_create_by(task: task, user: user)
children = associated_item.pluralize.to_sym
submission.send(children) << associated_item
end

How to implement controller in order to handle the creation of one or more than one record?

I am using Ruby on Rails 4.1. I have a "nested" model and in its controller I would like to make the RESTful create action to handle cases when one or more than one records are submitted. That is, my controller create action is:
def create
#nester = Nester.find(:nester_id)
#nesters_nested_objects = #nester.nested_objects.build(create_params)
if #nnesters_ested_objects.save
# ...
else
# ...
end
end
def create_params
params.require(:nesters_nested_object).permit(:attr_one, :attr_two, :attr_three)
end
I would like it to handle both cases when params contain data related to one object and when it contains data related to more than one object.
How can I make that? Should I implement a new controller action (maybe called create_multiple) or what? There is a common practice in order to handling these cases?
Well, if you insist on creating those records aside from their nest, I can propose to go with something like this (it better be a separate method really):
def create_multiple
#nest = Nester.find(params[:nester])
params[:nested_objects].each do |item|
#nest.nested.new(item.permit(:attr_one, :attr_two, :attr_three))
end
if #nest.save
....
else
....
end
end

Why does Rails use the params variable rather than passing arguments through the function?

As title says, why does Rails prefer to use the #params variable inside of a Controller action when you are responding to the action instead of passing the individual parameters through the function arguments when we call the function?
Other frameworks use this (i.e, ASP MVC) and I was just wondering if there was a reason for that design decision, because it doesn't seem very intuitive.
Ie. Why does Rails do
def index
name = params[:name]
end
Instead of
def index(name)
end
The point is, most of the actions in a controller handles the view REST-fully. The params comes from the user's browser when they interact with the page or send a new variable request to the page.
These requests are variable, and Rails makes it uniform by maintaining the parameters in params hash. If the following GET requests arrive:
http://localhost:3000/products?color=red&quality=best
the params hash will automatically be populated as {'color' => 'red', 'quality' => 'best'}. Rails doesn't expect your action to manually handle the parameters.
Similarly, consider you are getting a POST request from a page where a user filled a form. In that scenario, the params obtain the parameters which are composed with form helpers inside views.
Though in hyptothetical case you are dealing with general methods instead of actions, such as below, you will have to do it by passing arguments.
def show
if params['color'] == 'red'
#product = obtain_product('red')
else
#,..
end
end
def obtain_product(color)
Product.where('color = ?', color).first
end
Hope it is clear. :)
#kidorrails has a great answer, and I wanted to add to it:
If you wanted to pass the params to each method directly, it would go against the #1 Rails convention - keep it DRY. By having a separate params hash, you not only have access to all the params you want, but you can access them through as many methods as you need
For example, take strong_params:
#controller
def new
#model = Model.new
end
def create
#model = Model.new(strong_params)
#model.save
end
private
def strong_params
params.require(:model).permit(:your, :params)
end
As #apneadiving mentioned, the params hash is created in another part of the stack, meaning it's available over all the methods required. It's most efficient & versatile way to do it IMO

update_attributes field tweaks

So I've got an edit page that has butt-load of editable fields on it...simple update...
#patient.update_attributes(params[:patient])...everything's great, except....
I've got one field out of these 20 that I need to tweak a little before it's ready for the db and it would seem I either need to do
two trips
#patient.update_attributes(params[:patient])
#patient.update_attribute( :field=>'blah')
or set them all individually
patient.update_attributes(:field1=>'asdf', :field2=>'sdfg',:field3=>'dfgh', etc...)
Am I missing a way to do this is one swoop?
What's the attribute you need to tweak? There's two ways to do this:
Either massage the params before you send them to the update_attribute method:
I'm just giving an example here if you wanted to underscore one of the values:
params[:patient][:my_tweak_attribute].gsub!(" ", "_")
#patient.update_attributes(params[:patient])
Then there's the preferred way of doing your tweaking in a before_save or before_update callback in your model:
class Patient < ActiveRecord::Base
before_update :fix_my_tweak_attribute, :if => :my_tweak_attribute_changed?
protected
def fix_my_tweak_attribute
self.my_tweak_attribute.gsub!(" ", "_")
end
end
This keeps your controller clean of code that it probably doesn't really need.
If you just need to add a new param that didn't get sent by the form you can do it in the controller like this:
params[:patient][:updated_by_id] = current_user.id
#patient.update_attributes(params[:patient])
Assuming current_user is defined for you somewhere (again, just an example)
You can create a virtual attribute for that field. Say the field is :name. You create a function in your Patient model like :
def name
self[:name] = self[:name] * 2
end
And of course, you do your things inside that function :) Instaed of self[:name], you can also use read_attribute(:name).

Accessing model properties in Rails

So basically I have a controller. something like this
def show
#user = User.find[:params[id]]
#code to show in a view
end
User has properties such as name, address, gender etc. How can I access these properties in the model? Can I overload the model accesser for name for example and replace it with my own value or concatenate something to it. Like in the show.html.erb view for this method I might want to concatenate the user's name with 'Mr.' or 'Mrs.' depending upon the gender? How is it possible?
I would hesitate to override the attributes, and instead add to the model like this:
def titled_name
"#{title} #{name}"
end
However, you can access the fields directly like this:
def name
"#{title} #{self[:name]}"
end
You can create virtual attributes within your model to represent these structures.
There is a railscast on this very subject but in summary you can do something like this in your model
def full_name
[first_name, last_name].join(' ')
end
def full_name=(name)
split = name.split(' ', 2)
self.first_name = split.first
self.last_name = split.last
end
If you wish to explicitly change the value of an attribute when reading or writing then you can use the read_attribute or write_attribute methods. (Although I believe that these may be deprecated).
These work by replacing the accessor method of the attribute with your own. As an example, a branch identifier field can be entered as either xxxxxx or xx-xx-xx. So you can change your branch_identifier= method to remove the hyphens when the data is stored in the database. This can be achieved like so
def branch_identifier=(value)
write_attribute(:branch_identifier, value.gsub(/-/, '')) unless value.blank?
end
If you are accessing data stored directly in the database you can do this in you view:
<%= #user.firstname %>
<%= #user.gender %>
etc.
If you need to build custom representations of the data, then you will either need to create helpers, or extend the model (as above).
I tend to use helper methods added to the model for things like that:
def formatted_name
"#{title} #{first_name} #{last_name}"
end
(Edit previous post. Looked back at my code and realized helpers are supposed to be for presentation-related (mark-up) stuff only.)
(Edit again to remove left-over parameter... Geez, not enough coffee this morning.)
(Edit again to replace $ with #... Perhaps I should just remove this one huh?)
You can easily overload the attributes as you suggest.
i.e. if name is a field in the users database table, you can do:
def name
"#{title} #{read_attribute[:name]}"
end
The read_attribute function will return the database column value for the field.
Caveat: I am not sure this is a good idea. If you want a method that displays model data in a modified way, I would be tempted not to overload the default methods, and call them something different - this will avoid a certain level of obfuscation.
Documentation here: http://api.rubyonrails.org/classes/ActiveRecord/Base.html (under 'Overwriting default accessors')
in http://api.rubyonrails.org/classes/ActionController/Base.html
search for
Overwriting default accessors

Resources