From: Jamis Buck Date: 2004-11-19T07:09:19+09:00 Subject: Re: Needle/Rails, Injected - Interceptors - Attn: Jamis Buck John Wilger wrote: > 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. Well, I'm not exactly an expert in AR, so I'll probably be making some assumptions that are false. First of all, I'm assuming StoryCard#project_id= takes an integer and not a Project instance. This means that the interceptor would not have access to the project instance, unless you did a look up in the interceptor itself. So, let's attempt that. In your 'project.rb' file, do something like this: # do this first so that we can guarantee that the story_card service # will have been registered. require 'storycard' class Project < ActiveRecord::Base registry.intercept( :story_card ).doing do |chain,context| # only intercept the #project_id= method if context.sym == :project_id= # use the registry, not the Project constant, so that we # if interceptors are added to the project service, they # will be invoked. project = registry[ :project ].find( context.args.first ) if project.status.is_closed? raise "cannot add storycard to a closed service!" else chain.process_next( context ) end else chain.process_next( context ) end end ... end That said, I'm not confident that this approach will work, and here's why: I believe that when you assign a storycard to a project, the #project_id= call occurs internally, within the StoryCard class itself. That means that the interceptors are bypassed (interceptors are only invoked when a client invokes a method on the service--methods invoked internally by the service do not go through the interceptors). Also, the AR objects use the constant names internally to reference other AR classes, which means that they aren't going through the registry anyway to get the dependencies. (Which means interceptors won't help you at all, in that case.) This all goes back to why I don't, in general, like giving classes themselves prominant acting roles. Class methods are just globals, and like all globals should be used sparingly. DI canhelp reduce the necessity of class methods, but only if the framework was designed with DI in mind. -- Jamis Buck jgb3@email.byu.edu http://www.jamisbuck.org/jamis