J
John Wilger
Jamis (or anyone else who cares to give it a go),
I'm having a bit of trouble wrapping my head around the usage of
interceptors with Needle. I've implemented the changes you outlined in
you code examples from you proposal on "Rails, Injected"
(http://ruby.jamisbuck.org/rails-injected.html) and would like to use
the method interception within a few of my ActiveRecord classes.
Specifically, I have the following (abbreviated) classes:
-- snip --
class Project < ActiveRecord::Base
has_many :story_cards
def status
ProjectStatus::find(self.status_name)
end
def status=(new_status)
if ProjectStatus.include?(new_status)
self.status_name = new_status.name
else
raise ArgumentError, "Only predefined instances of ProjectStatus
can be assigned."
end
end
end
class StoryCard < ActiveRecord::Base
belongs_to roject
end
-- end snip --
And I have RankedValue/ProjectStatus defined as:
-- snip --
module RankedValue
include Comparable
attr_reader :name, :rank
def initialize(name, rank)
@name = name
@rank = rank
end
def self.included(mod)
mod.module_eval <<-EOF,__FILE__,__LINE__
def self.new(*args, &block)
name = args[0]
name = name.upcase.gsub(' ', '_')
const_set(name, super(*args, &block))
end
private_class_method :new
def self.to_a
constants.collect! { |cnst| const_get(cnst) }
end
def self.include?(obj)
self.to_a.include?(obj)
end
def self.find(name)
begin
name_trans = name.upcase.gsub(' ', '_')
const_get(name_trans)
rescue NameError
raise "No predefined instance of #{mod} matches #name == '\#{name}'."
end
end
EOF
end
def <=>(other)
self.rank <=> other.rank
end
end
class ProjectStatus
include RankedValue
def initialize(name, rank, closed)
super(name, rank)
@closed = closed
end
def is_closed?
@closed
end
new('Pending', 1, false)
new('In Progress', 2, false)
new('On Hold', 3, true)
new('Completed', 4, true)
new('Cancelled', 5, true)
end
-- end snip --
So, basically, I have the following instances of ProjectStatus available:
* ProjectStatus:ENDING
* ProjectStatus::IN_PROGRESS
* ProjectStatus::ON_HOLD
* ProjectStatus::COMPLETED
* ProjectStatus::CANCELLED
each of which responds to #is_closed? with true or false depending on
how the instance was initialized.
OK, now that we're through all the background (whew!), here's what I
want to be able to do. I want to make sure that a StoryCard can only
be assigned to a Project when Project#status.is_closed? == false.
I know that I could implement this by overriding the
StoryCard#project_id= method, but this seems to break encapsulation.
It should ultimately be the Project class's responsibility to decide
if a StoryCard can be assigned to it. I'm thinking I could create an
interceptor within the Project class definition to intercept the calls
to StoryCard#project_id= and add the necessary advice before the
assignment takes place (and raise a RuntimeError if the Project is
closed), but I'm not sure where to start.
Any advice?
--
Regards,
John Wilger
-----------
Alice came to a fork in the road. "Which road do I take?" she asked.
"Where do you want to go?" responded the Cheshire cat.
"I don't know," Alice answered.
"Then," said the cat, "it doesn't matter."
- Lewis Carrol, Alice in Wonderland
I'm having a bit of trouble wrapping my head around the usage of
interceptors with Needle. I've implemented the changes you outlined in
you code examples from you proposal on "Rails, Injected"
(http://ruby.jamisbuck.org/rails-injected.html) and would like to use
the method interception within a few of my ActiveRecord classes.
Specifically, I have the following (abbreviated) classes:
-- snip --
class Project < ActiveRecord::Base
has_many :story_cards
def status
ProjectStatus::find(self.status_name)
end
def status=(new_status)
if ProjectStatus.include?(new_status)
self.status_name = new_status.name
else
raise ArgumentError, "Only predefined instances of ProjectStatus
can be assigned."
end
end
end
class StoryCard < ActiveRecord::Base
belongs_to roject
end
-- end snip --
And I have RankedValue/ProjectStatus defined as:
-- snip --
module RankedValue
include Comparable
attr_reader :name, :rank
def initialize(name, rank)
@name = name
@rank = rank
end
def self.included(mod)
mod.module_eval <<-EOF,__FILE__,__LINE__
def self.new(*args, &block)
name = args[0]
name = name.upcase.gsub(' ', '_')
const_set(name, super(*args, &block))
end
private_class_method :new
def self.to_a
constants.collect! { |cnst| const_get(cnst) }
end
def self.include?(obj)
self.to_a.include?(obj)
end
def self.find(name)
begin
name_trans = name.upcase.gsub(' ', '_')
const_get(name_trans)
rescue NameError
raise "No predefined instance of #{mod} matches #name == '\#{name}'."
end
end
EOF
end
def <=>(other)
self.rank <=> other.rank
end
end
class ProjectStatus
include RankedValue
def initialize(name, rank, closed)
super(name, rank)
@closed = closed
end
def is_closed?
@closed
end
new('Pending', 1, false)
new('In Progress', 2, false)
new('On Hold', 3, true)
new('Completed', 4, true)
new('Cancelled', 5, true)
end
-- end snip --
So, basically, I have the following instances of ProjectStatus available:
* ProjectStatus:ENDING
* ProjectStatus::IN_PROGRESS
* ProjectStatus::ON_HOLD
* ProjectStatus::COMPLETED
* ProjectStatus::CANCELLED
each of which responds to #is_closed? with true or false depending on
how the instance was initialized.
OK, now that we're through all the background (whew!), here's what I
want to be able to do. I want to make sure that a StoryCard can only
be assigned to a Project when Project#status.is_closed? == false.
I know that I could implement this by overriding the
StoryCard#project_id= method, but this seems to break encapsulation.
It should ultimately be the Project class's responsibility to decide
if a StoryCard can be assigned to it. I'm thinking I could create an
interceptor within the Project class definition to intercept the calls
to StoryCard#project_id= and add the necessary advice before the
assignment takes place (and raise a RuntimeError if the Project is
closed), but I'm not sure where to start.
Any advice?
--
Regards,
John Wilger
-----------
Alice came to a fork in the road. "Which road do I take?" she asked.
"Where do you want to go?" responded the Cheshire cat.
"I don't know," Alice answered.
"Then," said the cat, "it doesn't matter."
- Lewis Carrol, Alice in Wonderland