How to add a method for active record? - ruby-on-rails

I would want to use my method getScheduleFixed() on a Active Record Istance
ps = ProgramSchedule.all
ps.getScheduleFixed
the important fact is how to access to "ps" array (active record) on my method declaration
class ProgramSchedule < ActiveRecord::Base
def getScheduleFixed
array = (HERE ARRAY RETURNED BY ACTIVE RECORD)
# some stuff...
return array
end
end

You are mixing things up here.
1) There are (instance) methods that you can use on a single ActiveRecord object.
# Returns an ARRAY with all programschedule instances
all_ps = ProgramSchedule.all
# You can now iterate over over the array
all_ps.each do |ps|
# in here you can call the instance method on the actual model instance
ps.instance_method
end
# Definition of this method in app/models/program_schedule.rb
class ProgramSchedule < ActiveRecord::Base
def instance_method
# Foo
end
end
2) There are class methods that you can run on the ActiveRecord model itself.
ProgramSchedule.class_method
# Definition of this method in app/models/program_schedule.rb
class ProgramSchedule < ActiveRecord::Base
def self.class_method
# Bar
end
end

When you do ProgramSchedule.all, you get an Array, not an instance of ProgramSchedule.
If your method will be called with all the records at all times, you can use a class method like this one :
class ProgramSchedule < ActiveRecord::Base
def self.getAllScheduleFixed
array = ProgramSchedule.all # or self.class.all could be used if you subclass this class
#some stuff
end
end
If you need to work with only a subset of ProgramSchedule's, that is conditions, you will need to pass conditions to this class method, or to directly pass the array of results to some class method.

I think you should use scope for this:
class ProgramSchedule < ActiveRecord::Base
scope :fixed, { all.map(&:getScheduleFixed) }
end
or
class ProgramSchedule < ActiveRecord::Base
def self.fixed
all.map(&:getScheduleFixed)
end
end
Now you just need to call ProgramSchedule.fixed. Both these methods can be chained on other scopes such as ProgramSchedule.latest.fixed. See more details here

def getScheduleFixed
array = User.all.map(&:name)
return array
end

Related

Ruby - automatically initialize class instance variables of child classes without inheriting the value

I am looking for a solution to automatically initialize a class variable through inheritance (make it available as an accessor and initialize it to some value). But I do NOT want to inherit the value, just start with a new fresh object each time on each class.
I have been looking at class_attributes and thought I had found a workaround but it does not seem to be working as I thought (and even if it worked, it would most likely not do the thing I want since the same array would be used everywhere so it would behave like a ## variable)
class AbstractClass
class_attribute :metadata
#metadata = [] # initialize metadata to an empty array
def self.add_metadata(metadata)
#metadata << metadata
end
end
def ChildClass < AbstractClass
add_metadata(:child_class1)
end
def ChildClass2 < AbstractClass
add_metadata(:child_class2)
end
I'd like to have the following :
AbstractClass.metadata # Don't really care about this one
ChildClass1.metadata # => [:child_class1]
ChildClass2.metadata # => [:child_class2]
I can think of a way to do this using modules with AS::Support
module InitializeClassInstanceVars
extend ActiveSupport::Concern
included do
class_attribute :metadata
self.metadata = []
end
end
...and include this module in every nested class (and I believe this is what mongoid actually does for instance)
but I was hoping I could do this directly via inheritance
You don't have to initialize the class variable when it is being inherited. The Ruby style is to return and assign default value when the variable has not been set and is being accessed for the first time.
Just create another class method for that:
class AbstractClass
def self.metadata
#metadata ||= []
end
def self.add_metadata(metadata)
self.metadata << metadata
end
end
class ChildClass1 < AbstractClass
add_metadata(:child_class1)
end
class ChildClass2 < AbstractClass
add_metadata(:child_class2)
end
AbstractClass.metadata # => []
ChildClass1.metadata # => [:child_class1]
ChildClass2.metadata # => [:child_class2]
Hooks are a great idea, you're just working off of the wrong one :) If you want to run code every time something inherits your class, then inherited is the one to use:
class AbstractClass
class << self
attr_accessor :metadata
def inherited(child)
child.instance_variable_set(:#metadata, [child.name])
end
end
end
class ChildClass1 < AbstractClass; end
class ChildClass2 < AbstractClass; end
ChildClass1.metadata
# => ["ChildClass1"]
ChildClass2.metadata
# => ["ChildClass2"]
Given that the question is tagged rails, you should also have String#underscore available; replace child.name with child.name.underscore.to_s to get [:child_class1].
EDIT: I might have misunderstood the question. If you just want to start with an empty array that you can add to, chumakoff's answer is simpler.

How to call a parent class and instead create one of it's children?

I have a model directory structure like this:
/alerts
base_alert.rb
panic_alert.rb
hardware_alert.rb
alert.rb
With the /alerts/x_alert.rb models setup like this:
class base_alert < ActiveRecord::Base
...
end
class panic_alert < base_alert
...
end
class hardware_alert < base_alert
...
end
etc.
Is there any way to call create on alert.rb in the top directory, and, based on a parameter passed, it would create one of the children instead of alert.rb.
I.E. Alert.create({type:"panic_alert"})
And it would create and return one of the panic_alert types of alerts?
By making few changes to the class definitions, like subclassing the Alert from ActiveRecord::Base rather than BaseAlert, you could achieve what you are trying to accomplish.
Following are the updated classes:
# app/models/alert.rb
class Alert < ActiveRecord::Base
end
# app/models/alerts/base_alert.rb
module Alerts
class BaseAlert < ::Alert
end
end
# app/models/alerts/panic_alert.rb
module Alerts
class PanicAlert < BaseAlert
end
end
# app/models/alerts/hardware_alert.rb
module Alerts
class HardwareAlert < BaseAlert
end
end
Following are few ways to create the subclasses from the base class:
#panic_alert = Alert.create!(
type: 'Alerts::PanicAlert', #this has to be string
#other attributes
)
#alert = Alert.new
#alert.type = 'Alerts::PanicAlert' #this has to be string
# assign other attributes, if necessary
#alert.save
#alert = Alert.new
#panic_alert = #alert.becomes(Alerts::PanicAlert) #this has to be class
# assign other attributes, if necessary
#panic_alert.save
You can use the constantize or the safe_constantize methods to do that. What they do is take a string and try to return the class the string refers to. For instance:
"BaseAlert".safe_constantize
=> BaseAlert
or
def method_name(alert_type)
alert_type.safe_constantize.create()
end
The difference between the two is constantize will throw an error if there isn't a match for the string, while safe_constantize will just return nil. Remember, if you pass in a underscored string (say panic_alert) then you would have to camelize it.
What seems like a lifetime ago I created StiFactory for this. That said, I don't find much use for STI these days (hence the lack of maintenance).

Rails instance variable for record

Is there any built-in way to attach an instance variable to a record? For example, suppose I have a User class with a foo attr_accessor:
class User < ActiveRecord::Base
...
attr_accessor :foo
end
If I do
u = User.first
u.foo = "bar"
u.foo # -> "bar"
User.find(1).foo # => nil
This is the correct behavior and I understand why: the instance variable exists for the instance (in memory) and not for the record. What I want is something like a record variable, so that in the above example u.foo and User.find(1).foo return the same value within the same instance of my application. I don't want persistence: it's not appropriate for foo to be a column in the table, it's just appropriate for foo to return the same value for the same record during the life cycle of e.g., a controller action, console session, etc. Nor do I want a class variable via cattr_accessor, because there's no reason that User.find(1).foo should be the same as User.find(2).foo.
The best bet I can come up with is to fake it with a class variable array and instance methods to get/set the appropriate element of the array:
class User < ActiveRecord::Base
cattr_accessor :all_the_foos
self.all_the_foos = Array.new
def foo
self.all_the_foos[id]
end
def foo= new_foo
self.all_the_foos[id] = new_foo
end
end
This ALMOST works, but it doesn't work with un-saved records, e.g. User.new.foo = "bar" fails.
You can use Thread.current to set variables that are active within the context of a controller action invocation. Your current implementation doesn't guarantee context reset across calls.
class User < ActiveRecord::Base
after_create :set_thread_var
def foo
new_record? ? #foo : Thread.current["User-foo-#{id}"]
end
def foo=(val)
new_record? ? (#foo = val) : (Thread.current["User-foo-#{id}"] = val)
end
private
def set_thread_var
Thread.current["User-foo-#{id}"] = #foo if defined?(#foo)
end
end
I can't think of a better idea than your class variable solution. To solve your "new" problem I'd use after_initialize.
class User < ActiveRecord::Base
after_initialize :init_foos
cattr_accessor :all_the_foos
def foo
self.all_the_foos[id]
end
def foo= new_foo
self.all_the_foos[id] = new_foo
end
private
def init_foos
##all_the_foos ||= []
end
end

How do I create attr setter like property in superclass, and populate in subclasses?

I have a parent class in rails that inherits from ActiveRecord::Base. I'm trying to implement a freetext search, plus other queries, in that class such that all classes that inherit from it can use it with their own fields, which change based on the model:
#
# in base class
#
class GenericBase < ActiveRecord::Base
named_scope :freetext, lambda { |query|
if query.present?
{ :conditions => [ self.freetext_fields.join(' LIKE ? or '),
( ["%#{query}%"]*self.freetext_fields.size ) ].flatten }
else
{}
end
}
end
#
# in inheriting class
#
class Person < GenericBase
set_freetext_fields %w(firstname lastname username email)
end
# or
class Address < GenericBase
set_freetext_fields %w(street city)
end
#
# in controller
#
def search
#people = Person.freetext(params[:query])
end
In the example above, how do I implement the set_freetext_fields setter to be easily used in all models that inherit from GenericBase? This should be something very similar to set_table_name available in Rails.
I want to implement it in the parent or a mixin module such that the API for inheriting classes will be as clean as possible.
You can implement something like this:
module A
def set_freetext_fields=(*args)
#a = args
end
def some_meth
#a
end
end
class C
extend A
C.set_freetext_fields = %w(firstname lastname username email)
end
puts C.set_freetext_fields
Where C is GenericBase class

How to dynamically call accessor methods in my model object from view

I have a model:
class Mymodel < ActiveRecord :: Base
attr_accessible :the_date, :the_time, :the_event
def the_date
...
end
def the_time
...
end
def the_event
...
end
...
end
My controller holds a array of methods names, which is used by view:
class Mycontroller < ApplicationController
#methods=['the_date', 'the_time', 'the_event']
...
end
in my view index.html.haml, I would like to dynamically access the model methods:
%td
-index=SOME_USER_INPUT
=mymodel.#methods[index] /IT DOES NOT WORK HERE!!
But, I can not dynamically call the model methods in this way: mymodel.#methods[index], how to have dynamical method call based on my sample code??
#methods is an instance variable of your controller, not of your model. Assuming you want to call the method, try this:
=mymodel.send(#methods[index])

Resources