Reputation: 3371
I know this question has been asked several times, but I think never in this conditions.
I try to create an "helper" for my models (an acts_as
), for automatically set a status from a status_id.
acts_as_statusable :status, [ :in_progress, closed ]
This helper creates methods status
, status=(sym)
, in_progress?
, closed?
and named scopes in_progress
and closed
.
My helper work, this is its code put in lib/
class ActiveRecord::Base
def self.acts_as_statusable(*args)
field_name = args.first
field_name_id = :"#{field_name}_id"
statuses = args.second
# Define getter
define_method(field_name) do
return nil if self.send(field_name_id).nil?
return statuses[self.send(field_name_id)]
end
# Define setter
define_method(:"#{field_name}=") do |v|
return self.send(:"#{field_name_id}=", statuses.index(v))
end
# Define a status? for each status,
# and a named scope .status
statuses.each do |status|
define_method(:"#{status}?") do
return self.send(field_name) == status
end
scope status, -> { where(["#{table_name}.#{field_name_id} = ?", statuses.index(status)]) }
end
validates field_name_id, :inclusion => { :in => (0...statuses.length).to_a }
end
end
Now, the problem
I need to have a Class method, for every class using acts_as_statusable
, named status_index(sym)
which returns the status_id from a status.
The problem is that every solutions I found for defining a class models are using Module
, extend
or intend
, but I can not insert the statuses
variable of the acts_as_statusable
line into this modules...
How can I do this ? I use Rails 4.
Upvotes: 1
Views: 380
Reputation: 8295
you can use class_eval
, and class_variable_set
class_variable_set :"@@#{field_name}_options", statuses
class_eval <<-RUBY
def self.status_index(sym)
@@#{field_name}_options.index(sym)
end
RUBY
which in your case will give a
@@status_options
class variable
and a
status_index(sym)
method that returns the offset of the given status
Upvotes: 4
Reputation: 1089
xlembouras - your solution works great! I have two models:
class User < ActiveRecord::Base
acts_as_statusable :status, [ :little, :big ]
end
class Price < ActiveRecord::Base
acts_as_statusable :status, [ :in_progress, :closed ]
end
'rails c' execution:
> User.status_index(:little)
=> 0
User.status_index(:big)
=> 1
> Price.status_index(:closed)
=> 1
> Price.status_index(:in_progress)
=> 0
I modified the structure, and used code written by ForgetTheNorm. Rails 4, ActiveSupport::Concern used
lib/active_record_extension.rb :
module ActiveRecordExtension
extend ActiveSupport::Concern
end
ActiveRecord::Base.send(:include, ActiveRecordExtension)
config/initializers/extensions.rb :
require "active_record_extension"
config/initializers/active_record_monkey_patch.rb
class ActiveRecord::Base
def self.acts_as_statusable(*args)
...
# ForgetTheNorm's CODE..
...
# xlembouras CODE:
class_variable_set :"@@#{field_name}_options", statuses
class_eval <<-RUBY
def self.status_index(sym)
@@#{field_name}_options.index(sym)
end
RUBY
end
end
Upvotes: 2