From: John Wilger Date: 2004-11-19T04:09:55+09:00 Subject: Needle/Rails, Injected - Interceptors - Attn: Jamis Buck 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 :project 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::PENDING * 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