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.
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 would like to do something like:
class TestController < InheritedResources::Base
def test_method
self.var1 + self.var2
end
private
def test_params
params.require(:test).permit(:var1, :var2)
end
end
Where in the view I could call from the built in controller index:
test.test_method
I've tried adding a create method to the controller as follows:
def create
Test.create!(require(:test).permit(:var1, :var2, :test_method))
end
I've also tried updating the params directly:
private
def test_params
params.require(:test).permit(:var1, :var2, :test_method)
end
I've also tried making a helper method, but I knew that was doomed to fail because it wouldn't have access to var1 and var2.
I guess I just don't understand two things: one how to make my var1 and var2 white-listed so I can use them, and more importantly how to add a method to my model using strong parameters, because attr_accessible doesn't work in my models anymore.
EDIT:
Let me rephrase a little, maybe it will help. I can get access to individual Test objects in my view with a simple call to tests.each |test| in the view. I just want to make methods that act on my already defined active record variables for that object, hence var1 and var2. The problem is when I define a new method in my controller it is private to the object and I won't have access to it with a call from an instance of the object. Better yet, I would like to just be able to define a new variable, local to the object, that is created after it has propagated its other fields from the db.
EDIT2: I'm aware I'm probably missing the design pattern here. It can be hard to describe that I want X, when really I need Z. Thanks for the patience.
Thanks for the help.
There's no reason for white-listing parameters that you'll directly use.
White-listing with strong parameters is useful only when you call function like ActiveRecord#update that simply take every key from the dictionary, so you can control with key you want to allow and which not.
In this case, just do:
class TestController < InheritedResources::Base
def test_method
#result = params[:var1] + params[:var2]
end
end
And in your view, just print the #result variable wherever you want
<%= #result %>
This is the Rails way. You can of course call the variable as you want.
Helper methods are useful only for more complex cases.
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
Here the docu: https://relishapp.com/rspec/rspec-mocks/v/3-0/docs/message-expectations/expect-a-message-on-any-instance-of-a-class
Im wondering what is the right use of it.
I have a controller
class UserController < ApplicationController
def edit
generate_token!
end
end
And the method generate_token! is defined in the model.
class User < ActiveRecord::Base
def generate_token!
self.update!(token: 'something')
end
end
I just want to check if the method receives something.
The spec would be something like.
describe 'edit'
it 'receives something' do
expect_any_instance_of(Object).to receive(:generate_token!)
end
end
But what do I have to use for the Object? I tried the class and some other random stuff, but nothing worked yet. It seems I dont get the Mock at all.
Any suggestions?
best regards
denym_
You need to replace Object with Client in your spec. Also the method's name is only "generate_token" not "generate_token!" as you have in your spec currently.
It seems you are mixing the generate_token! in your controller and the generate_token method in your Client model.
In the edit action you are calling the generate_token! defined in the same class (controller) (I would assume you only pasted the edit action here, so you might really have this method in your controller).
Anyway, if you do not have a generate_token! method in your controller which has a line like that:
#client.generate_token
the generate_token inside your Client model will never get called from your controller.
One more thing: the name of the controller that handles your client records really called users_controller?
That could also cause problem, if you really have a separate User and Client model.
I think now I know what could be your main confusion.
Expect only set your expectation. You still need to create the instance of the class and trigger the action where you are expecting something that you want to see to happen.
Eg. if you are testing a controller action you need to call the edit action after you set your expectation.
client = create(:client)
get :edit, id: client
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)