Ruby Rails :Is it possible to overload index method in ActionController? - ruby-on-rails

I want some thing like below
class CompanyController < ApplicationController
def index
#return all of companies
end
def index
#return companies based filter on company :name, :location, :type (any combination of these)
end
end

You can't do that but you can do something like this:
class CompanyController < ApplicationController
def index
if params[:name] # add ifs etc
#companies = Company.where(:name => params[:name])
else
#companies = Company.all
end
end
end
I think thats what you mean (tell me if I'm wrong!)

You can't have two methods with the same name in Ruby. If you have multiple methods with the same name, the last method defined will be the one that Ruby uses.

Related

What is the Rails 4 way to deal with filters?

Background: I have a few filters which have to be available through every CRUD page on the system: search, livesearch, column sorting and pagination;
This is what I currently have:
.lib/record_filters.rb
module RecordFilters
##valid_directions = %w[asc desc]
def search_for(record)
record.present? ? where('name LIKE ?', record+"%") : all
end
def records_matching(search_term)
where('name LIKE ?', search_term+"%").map(&:name)
end
def order_records_by(attribute, direction)
order(sort_table_by(attribute) +" "+ sort_records_order_by(direction))
end
private
def sort_table_by(attribute)
column_names.include?(attribute) ? attribute : "name"
end
def sort_records_order_by(direction)
##valid_directions.include?(direction) ? direction : "asc"
end
end
./app/models/ticket_type.rb
class TicketType < ActiveRecord::Base
include RecordFilters
validates_presence_of :name
validates_uniqueness_of :name
end
./app/controllers/ticket_types_controller.rb
class TicketTypesController < ApplicationController
before_action :set_ticket_type, only: [:show, :edit, :update, :destroy]
def index
#ticket_types = TicketType.search_for(params[:search]).order_records_by(params[:sort], params[:direction]).paginate(per_page: 12, page: params[:page])
respond_to do |format|
format.html
format.js
format.json { render json: TicketType.records_matching(params[:term]) }
end
end
...
end
./config/application.rb
...
config.autoload_paths << "#{Rails.root}/lib"
The problem: Upon accessing the index on the browser, Rails returns NoMethodError for search_for
Question: What is the Rails Way to implement such filters? What am I doing wrong?
Thanks!
This is because Ruby's include will add the module's methods as instance methods:
module A
def value
5
end
end
class B
include A
end
puts B.new.a # prints 5
puts B.a # fails
If you want them as class methods, like the class object itself was extended, use extend:
method A
def value
5
end
end
class C
extend A
end
puts C.a # prints 5
puts C.new.a # fails
You can also, if you really want include, define some new methods in the module's included callback:
module A
def self.included(mod)
# mod is whatever (module or class) included A.
# in this case, it's B.
mod.class_eval do
def self.value
"class"
end
# there's no reason to do this here, instead of
# in the module itself, but just for demonstration purposes:
def inst
"inst"
end
end
end
end
class B
include A
end
puts B.value # prints "class"
puts B.new.inst # prints "inst"
Your methods are getting added as instance methods, but you're calling them like class methods, I'd recommend you look into http://api.rubyonrails.org/classes/ActiveSupport/Concern.html to implement the concern pattern for your models.

Is there are way that model methods can work with variable with out them being explicitly passed?

I know this sounds like a ridiculous question but I trying to solve a chalange given by an potential employer. I have a schema and a couple of models with their methods. Almost all the methods have no variables passed in. Meaning none of the methods look like this:
def this_is_my_method(variable)
#stuff
end
or
def this_is_my_method variable
#stuff
end
but there are methods that are clearly working with variables like this:
def build_address
if variable
# do something
end
end
Is there a RoR way that a model method will just know about certain parameters or variables in certain situations?
So if my controller was recieving params that looked like this:
?my_method[begin]=1&my_method[end]=5
would the model method "my_method" know what "begin" and "end" where?
def my_method
if self.begin == self.end
# do something
else
# do something else
end
end
Remember that a model method has access to all the attributes (and other methods) of that model instance.
So (for example) this would be a valid model method.
class User < ActiveRecord::Base
def full_name
[first_name, last_name].join(' ')
end
end
This is taking an attribute user.first_name and an attribute user.last_name and combining them to create a new method user.full_name
EDIT as #Sanket has suggested you can pass values into a model if you make them attribute accessor...
def SomeController < ApplicationController
def some_controller_method
#user = User.find(params[:id])
#user.begin = params[:begin]
#user.end = params[:end]
#user.some_model_method
end
end
def User < ActiveRecord::Base
attr_accessor :begin, :end
def some_model_method
if self.begin == self.end
# do something
else
# do something else
end
end
end
Although to be frank I'd rather just pass the values in as method arguments.

Rails 4 pass data from controller to model attr_accessor

Ok, I'm quite confused and a little stuck here, I'm trying to pass data to my Model via attr_accessor but I cant find the right way. Here is my setup so far:
class ApplicationController < ActionController::Base
before_filter :current_league
protected
def current_league
#current_league ||= Conf.all.order('updated_at ASC').last.league
end
end
class HerosController < ApplicationController
def index
#heros = Hero.all.order(:name)
end
end
class Hero < ActiveRecord::Base
attr_accessor :current_league
def some_method
puts current_league
end
end
<% #heros.each do |hero| %>
<tr>
<td><%= hero.some_method %></td>
</tr>
<% end %>
Now how do I set the #current_league inside my model? I know I can have an attr_accessor inside my model, but this only applies to an instance of this model but the index action doesn't create an instance as far as I know. Maybe someone can point me in the right direction. Thanks in advance.
The #heros variable is a collection of Hero instances. You can loop through them and set it if you want.
class HerosController < ApplicationController
def index
#heros = Hero.all.order(:name).each do |hero|
hero.current_league = current_league
end
end
end
While it works, I don't find this answer to be all that elegant. Granted, I don't know the full extend of the thing you are making, but based on the code here I would create a composite object. Something like this:
class HeroInLeague
attr_reader :league, :hero
def initialize(league, hero)
#league = league
#hero = hero
end
def some_method
# ...
end
end
Then you can create these objects inside your controller:
class HerosController < ApplicationController
def index
#heros_in_league = Hero.all.order(:name).map { |hero|
HeroInLeague.new(current_league, hero)
}
end
end
Now you've created a place for methods to go that are related the combination of heros and leagues. Why is this important? Well, with the previous approach you'd probably end up with methods on Hero that don't make any sense when there is no current league. (like the some_method method). That makes the Hero class a bit of a mess. Now you've created a place to put some_method and all its friends.
You can use delegators to make the interface of HeroInLeague a bit more friendly, so you don't have to do hero_in_league.hero.foo, but can call hero_in_league.foo directly.

RoR filter on controller as this SQL

How to write filter on the controller in Ruby on Rails, that is eqvivalent to this SQL code
select * from persons where persons.category = 'developers'
Use this:
before_filter :nerds_only
private
def nerds_only
#people = Person.where(:category => 'developers')
end
You may want to consider creating a named scope to get the nerdies:
class Person < ActiveRecord::Base
scope :developers, where(category: 'developers')
end
In your controller:
before_filter :developers_only
private
def developers_only
#people = Person.developers
end

Setting default_scope according to some criteria

I'm trying to set default scope according to some criteria determined by ana ActionController before_filter. In controller:
before_filter :authorize
...
def authorize
if some_condition
#default_scope_conditions = something
elsif another_condition
#default_scope_conditions = something_else
end
end
Inside the ActiveRecord
default_scope :conditions => #default_scope_conditions
But it doesn't seem to work, the before filter gets called but the default_scope doesn't get set. Could you please advise me what I'm doing wrong and how to fix it or suggest me some other way of achieving that.
You set #default_scope_conditions - which is an instance variable from the controller and you expect to read it from the model. It is not visible from the model unless passed as method parameter.
More, this approach would break the MVC principle that separates the model logic from the controller logic: Your model shouldn't automatically access info about current state of the controller.
What you can do: use anonymous scopes.
def scope_user
if some_condition
#default_scope_conditions = something
elsif another_condition
#default_scope_conditions = something_else
end
#user_scoped = User.scoped(:conditions => #default_scope_conditions)
end
Then, in your method, you can:
def my_method
users = #user_scoped.all
end
or
def my_method
User.with_scope(:conditions => #default_scope_conditions) do
# ..
#users = User.all #users get scoped
#products.users # also gets scoped
end
end
Try one default_scope and override it using custome finder.
The default options can always be overridden using a custom finder.
class User < ActiveRecord::Base
default_scope :order => '`users`.name asc'
end
User.all # will use default scope
User.all(:order => 'name desc') # will use passed in order option.
Then you can try something like following
before_filter :authorize
...
def authorize
if some_condition
#order = '' # default scope
elsif another_condition
#order = something_else
end
end
def method_name
User.all(:order => #order)
end
No Check though.

Resources