Call a model method in a Controller - ruby-on-rails

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)

Related

RAILS How to give an argument to Model function

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 appropriate scope to replace this method?

I have a dashboard_controller that I am using to manage users. Here is that controller:
class DashboardController < ApplicationController
before_action :authenticate_user!
def index
if current_user.admin?
#users = current_user.get_organization_users
else
flash[:notice] = "Unauthorized Page View"
redirect_to(tasks_url)
end
end
Note I am using #users = current_user.get_organization_users. Here is the get_organization_users method in my user model...
def get_organization_users
self.organization.users
end
How would I replace this with a scope? I tried...
scope :organization_users, -> { self.organization.users }
...but no worky. Any help appreciated.
A scope is used to add a class method to your model. But you're trying to call the method on an instance. So, in this case, the instance method makes sense.
However, if you want to create a scope, pass in the user_id as a parameter to the scope.
scope :organization_users(user_id), -> { find(user_id).organization.users }
Internally, Active record converts scopes into class methods.
That would mean that you can't replace the instance method get_organization_users with a scope and expect to call it on current_user, an instance of the class.
What you could do is create a scope and pass an argument (most probably the user id) to it, then call that scope directly on the user class.
I could give an example if you wish, but I think this approach is much longer than the desired one.
I am not sure about your question. You want to fetch all users that belongs to the organization associated with the current user, right? In such case you should just call current_user.organization.users.
Scope is used for filtering records in the current model and not for getting objects that are in the relation. You can read about it in the official documentation: http://guides.rubyonrails.org/active_record_querying.html#scopes.

Rails, calling model method from controller

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

How to query Rails / ActiveRecord model based on custom model method?

Disclaimer: I'm relatively new to rails.
I have a custom method in my model that I'd like to query on. The method, called 'active?', returns a boolean. What I'd really like to do is create an ActiveRecord query of the following form:
Users.where(:active => true)
Naturally, I get a "column does not exist" when I run the above as-is, so my question is as follows:
How do I do the equivalent of the above, but for a custom method on the model rather than an actual DB column?
Instead of using the active? method, you would have a scope to help find items that match.
Something like this...
def self.active
joins(:parent_table).where(:archived => false).where("? BETWEEN parent_table.start_date AND parent_table.end_date ", Time.now)
end
And, you should be able to do this
def active?
User.active.exists?(self)
end
If you would like to reuse this scope for the instance test.
An easy way to do this would be by using the select method with your exiting model method.
Users.select{|u| u.active}
This will return an array so you won't be able to use Active Record Query methods on it. To return the results as an ActiveRecord_Relation object, you can use the where function to query instances that have matching ids:
class User < ActiveRecord::Base
def self.active
active_array = self.select{|r| r.active?}
active_relation = self.where(id: active_array.map(&:id))
return active_relation
end
end

How to return results filtered on relations count to a view in RAILS?

Basically, I defined a property on my model which returns true or false depending on values in another table.
What I want is to have my Index action in the controller to return only those results that meet this condition.
I tried this:
#What I've tried on my Controller:
def index
#projects = Project.where(:is_available?)
end
#What I've on my Controller:
def index
#projects = Project.all
end
#What I've on my Model:
def is_available?
workers.count<2 ? true : false
end
Thanks.
Why your code doesn't work?
Project.where(:is_available?)
Here in the where method, you have to pass a hash of arguments OR a string of (SQL) conditions. What you are trying to do here is to select all projects where the method is_available? returns true. The problem is that the method is_available? is a Ruby method (defined in your model). Since it is a Ruby function, you can't call it inside SQL. The where method is expecting SQL conditions, not ruby code.
(Thanks to #benzado for the comment)
To fix your problem:
This is what you are looking for, computed only at the db level:
Project.joins(:workers)
.select('projects.*')
.group('projects.id')
.having('COUNT(workers.*) > 2')
This should returns all project having at least 2 workers associated with.
How to improve this?
You can make a scope of this query, to use it everywhere easily:
#in your model
class Project < ActiveRecord::Base
scope :having_more_than_x_workers, lambda do |workers_count|
joins(:workers).select('projects.*').group('projects.id').having("COUNT(workers.*) > #{workers_count || 0}")
end
end
To use it, in your controller for example:
#in your controller
def index
#projects = Project.having_more_than_x_workers(2)
end
The Rails query methods (like where) work by creating a database query; in other words, you can't use an attribute in where unless it actually exists on your data model. Otherwise, the database doesn't know about it and can't perform the filtering for you.
In your case, you should define a method on the Project class which performs the "is available?" query, so you can use your method in place of where. You can do it like this:
class Project < ActiveRecord::Base
def self.available_projects
where('workers_count > 2')
end
end
See MrYoshiji's answer for specifics on how to write the query or how to define it as a named scope.

Resources