A little new to Rails, but simply, I have a model method that calculates the date difference:
def days_diff
(end_date.to_date - start_date.to_date).to_i + 1
end
I want to be able to use this value/method in my controller to loop a form field x number of times. How do I about calling this method from the controller to use this variable?
Thanks in advance!
Say your model is named ModelName. In your controller method you would have:
def controller_method
#model_name = ModelName.find(params[:id]) # or however you obtain your model
x = #model_name.days_diff
x.times do |index|
# call your loop stuff here
end
end
Try defining the method in your model as
self.days_diff
and then call it in the controller as
model_name.days_diff
Related
My application has a Group object, of which a user has many. The navbar displays which group is currently selected, and the objects displayed on various pages are based on this. A number of my models have 'group_id' fields, and I'd like for these fields to be populated with the id of the currently selected group when they're saved.
In my application controller I have a helper_method which returns the current_group however this can't and shouldn't be accessed from a model which is the DRYest way I could think of doing this.
#inhereted_model.rb
before_save :assign_group_reference
def assign_group_reference
self.group_id = current_group.id
end
Is there an efficient and DRY way to do this that I'm missing?
You are right; any controller helper-methods cannot and should not be accessed directly from a model method.
I think a standard DRY way is to set the parameter of a model in your Controller methods. For example, do as follows in your Controller(s):
# In a Controller
def my_helper(mymodel)
mymodel.group_id = current_group
# where current_group is your Controller helper method to obtain the group name.
end
def create # or update etc.
#mymodel = set_my_model # your arbitrary method to set a model
my_helper(#mymodel)
respond_to do |format|
if #mymodel.save
format.html { redirect_to #mymodel, notice: 'Success.' }
else
raise
end
end
end
If you want, you can write my_helper (which in this case takes no argument and sets the instance variable #mymodel instead of a local variable) in before_action in combination with only or except, where you make sure the method is called after a model #mymodel is set, in order to avoid calling my_helper repeatedly in many methods in the Controller.
Alternatively, if you really want to set it at a model level for some reason, a potential workaround is to use a Ruby Thread variable, like the following.
# In a controller
def create
model = set_my_model # Arbitrary routine to set a model
Thread.new{
Thread.current.thread_variable_set(:grp, current_group)
# where current_group is your Controller helper method to obtain the group name.
model.save!
# In the model, you define a before_save callback
# in which you write something like
# self.group_id = Thread.current.thread_variable_get(:grp)
}.join
end
But I think this is a little dirty hack and I would avoid it in principle.
I have this function in my user.rb Model :
def change_key
self.public_key = params[:public_key]
end
I want to call this function from a script on Views (new.html.erb) like this :
change_key(x);
and I want self.public_key to get the x value
The result I get is :
undefined local variable or method `params' for
Any thoughts??
To pass an argument, you would do
def change_key(params)
self.public_key = params[:public_key]
end
Which you could call as:
#model.change_key(x)
And, as long as #model has an attribute public_key, and x is something hash-like, then it should work.
Unless, of course, you expect #model.public_key to be persisted. In which case, it would be something more like:
def change_key(params)
self.public_key = params[:public_key]
self.save
end
Which is silly because you could do that in one shot, like:
def change_key(params)
self.update(public_key: params[:public_key])
end
But, why declare a change_key method at all when you can simply do:
#model.update(public_key: params[:public_key])
Which, of course, you definitely do not want to do in a view because (IMO) your views should never be making changes to your models in your views. Doing so requires that your view layer requires intimate knowledge of your model layer - which makes your application brittle.
Your method needs to expect an argument:
def change_key(arg)
self.public_key = arg
end
However since this is a model method, it should not be called directly in the view as this is not MVC. But if you wanted to use it in a controller action, that might work. You'd need to define a controller action and maybe use javascript to make an ajax request to your controller action, where it could then operate on your model.
What is the best way to achieve the following in Rails 4? Parent and child model have the same attribute. If child model hasn't set said attribute then it inherits from the parent, otherwise, it renders its own value.
I tried to create a method in the child method with the same name as the model attribute to do the logic, but that causes a stack level too deep error.
def age_of_seniority
age_of_seniority.present? ? age_of_seniority : borough.age_of_seniority
end
Update
I don't want to change the method name, I would like to be able to access it as a normal attribute
You can do this using read_attribute
def age_of_seniority
read_attribute(:age_of_seniority) || borough.age_of_seniority
end
Call super:
def seniority_age
super || borough.age_of_seniority
end
A simple example:
class Parent
attr_accessor :seniority_age
end
class Child < Parent
def seniority_age
super||'foo'
end
end
c = Child.new
puts c.seniority_age
c.seniority_age = "bar"
puts c.seniority_age
returns:
foo
bar
You are recursively calling the method from within the method, this of course will cause a stack overflow.
def age_of_seniority
age_of_seniority.present? ? age_of_seniority : borough.age_of_seniority
end
If the age_of_seniority is stored in the instance variable, you access it with:
#age_of_seniority
So in your code:
def age_of_seniority
#age_of_seniority.present? ? #age_of_seniority : borough.age_of_seniority
end
And to call the overrided method from parent, you can just use super.
not quite sure what's borough.age_of_seniority doing.
I'm have some difficulties here, I am unable to successfully call a method which belongs to a ProjectPage model in the ProjectPage controller.
I have in my ProjectPage controller:
def index
#searches = Project.published.financed
#project_pages = form_search(params)
end
And in my ProjectPage model:
def form_search(searches)
searches = searches.where('amount > ?', params[:price_min]) if check_params(params[:price_min])
#project_pages = ProjectPage.where(:project_id => searches.pluck(:'projects.id'))
end
However, I am unable to successfully call the form_search method.
To complete davidb's answer, two things you're doing wrong are:
1) you're calling a model's function from a controller, when the model function is only defined in the model itself. So you do need to call
Project.form_search
and define the function with
def self.form_search
2) you're calling params from the model. In the MVC architecture, the model doesn't know anything about the request, so params is not defined there. Instead, you'll need to pass the variable to your function like you're already doing...
Three thing:
1.) When you want to create a class wide method thats not limited to an object of the class you need to define it like
def self.method_name
..
end
and not
def method_name
...
end
2.) This can be done using a scope with lambda these are really nice features. Like This in the model add:
scope :form_search, lambda{|q| where("amount > ?", q) }
Will enable you to call
Project.form_search(params[:price_min])
The secound step would be to add a scope to the ProjectPage model so everything is at the place it belongs to!
3.) When you call a Class method in the Controller you need to specifiy the Model like this:
Class.class_method
Declare like this in model
def self.form_search(searches)
searches = searches.where('amount > ?', params[:price_min]) if check_params(params[:price_min])
#project_pages = ProjectPage.where(:project_id => searches.pluck(:'projects.id'))
end
and call from controller
#project_pages = ProjectPage.form_search(params)
I'm trying total up all "amount" columns with a definition in the model like so:
def self.total
self.all.collect(&:amount).sum
end
With that, "Recipe.total" works as expected. However, I'm using a plugin that passes "Recipe.find(:all)", and I can't seem to pass that to the method to find the total. That is:
Recipe.find(:all).total # doesn't work
Is there a way to define the method in my model differently to make Recipe.find(:all).total work like Recipe.total?
You can write your method as:
def self.total
self.sum(:amount)
end
And then you can use it also with named scopes:
Recipe.total # without any scopes
Recipe.my_custom_named_scope.total # with your custom named scope
Another variant is to override find method for that model:
def self.find(*args)
result = super
if args[0] && args[0] == :all
def result.total
self.sum(&:amount)
end
end
result
end
Then you get exactly what you want, you'll be able to write Recipe.find(:all).total.
Check out the Calculation Module
It has methods for: sum,average,count, etc ...
Its baked into ActiveRecord.
So you would want to write:
Recipe.sum(:total)
Have fun!