Skip to content

Commit

Permalink
Use ActiveRecord pattern for observers
Browse files Browse the repository at this point in the history
Define own subclass of ActiveModel::Observer that takes care of hooking
into models and inheritance. As well as being a seemingly cleaner
separation of concerns, this allows us to handle single-columnfamily
inheritance properly in the context of observers.

Gets rid of Cequel::Model::Observing mixin -- functionality is defined
instead on Cequel::Model::Observer.
  • Loading branch information
outoftime committed Apr 17, 2012
1 parent 5501fbc commit 7b1c732
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 78 deletions.
5 changes: 3 additions & 2 deletions lib/cequel/model.rb
Expand Up @@ -12,7 +12,7 @@
require 'cequel/model/local_association'
require 'cequel/model/mass_assignment_security'
require 'cequel/model/naming'
require 'cequel/model/observing'
require 'cequel/model/observer'
require 'cequel/model/persistence'
require 'cequel/model/properties'
require 'cequel/model/remote_association'
Expand All @@ -34,6 +34,7 @@ module Cequel
module Model

extend ActiveSupport::Concern
extend ActiveModel::Observing::ClassMethods

included do
@_cequel = ClassInternals.new(self)
Expand All @@ -44,7 +45,7 @@ module Model
include Naming
include Callbacks
include Validations
include Observing
include ActiveModel::Observing
include Dirty
include MassAssignmentSecurity
include Associations
Expand Down
6 changes: 5 additions & 1 deletion lib/cequel/model/callbacks.rb
Expand Up @@ -6,9 +6,13 @@ module Callbacks

extend ActiveSupport::Concern

HOOKS = [:save, :create, :update, :destroy, :validation]
CALLBACKS = HOOKS.map { |hook| [:"before_#{hook}", :"after_#{hook}"] }.
flatten

included do
extend ActiveModel::Callbacks
define_model_callbacks :save, :create, :update, :destroy
define_model_callbacks *HOOKS
end

def save(*args)
Expand Down
1 change: 1 addition & 0 deletions lib/cequel/model/inheritable.rb
Expand Up @@ -24,6 +24,7 @@ def initialize(*args, &block)
end

def inherited(subclass)
super
unless @_cequel.type_column
raise ArgumentError,
"Can't subclass model class that does not define a type column"
Expand Down
42 changes: 42 additions & 0 deletions lib/cequel/model/observer.rb
@@ -0,0 +1,42 @@
module Cequel

module Model

#
# This is ripped directly off of ActiveRecord::Observer
#
class Observer < ActiveModel::Observer

protected

def observed_classes
klasses = super
klasses + klasses.map { |klass| klass.descendants }.flatten
end

def add_observer!(klass)
super
define_callbacks klass
end

def define_callbacks(klass)
observer = self
observer_name = observer.class.name.underscore.gsub('/', '__')

Cequel::Model::Callbacks::CALLBACKS.each do |callback|
next unless respond_to?(callback)
callback_meth = :"_notify_#{observer_name}_for_#{callback}"
unless klass.respond_to?(callback_meth)
klass.send(:define_method, callback_meth) do |&block|
observer.update(callback, self, &block)
end
klass.send(callback, callback_meth)
end
end
end

end

end

end
71 changes: 0 additions & 71 deletions lib/cequel/model/observing.rb

This file was deleted.

1 change: 0 additions & 1 deletion lib/cequel/model/validations.rb
Expand Up @@ -8,7 +8,6 @@ module Validations

included do
include ActiveModel::Validations
define_model_callbacks :validation
alias_method_chain :valid?, :callbacks # XXX is there no better way?
end

Expand Down
@@ -1,8 +1,8 @@
require File.expand_path('../spec_helper', __FILE__)

describe Cequel::Model::Observing do
describe Cequel::Model::Observer do
before do
Cequel::Model.observers = [:post_observer]
Cequel::Model.observers = [:post_observer, :asset_observer]
Cequel::Model.instantiate_observers
end

Expand Down Expand Up @@ -74,4 +74,13 @@

it_should_behave_like 'observing callbacks', :before_validation, :after_validation
end

context 'with inheritence' do
it 'should observe subclass' do
connection.stub(:execute).
with("INSERT INTO assets (id, label, class_name) VALUES (1, 'Cequel', 'Photo')")
photo = Photo.create!(:id => 1, :label => 'Cequel')
photo.should have_observed(:before_save)
end
end
end
11 changes: 11 additions & 0 deletions spec/models/asset.rb
@@ -1,7 +1,18 @@
class Asset

include Cequel::Model

key :id, :integer
column :class_name, :ascii
column :label, :varchar

def observed!(callback)
@observed ||= []
@observed << callback
end

def has_observed?(callback)
@observed.include?(callback)
end

end
5 changes: 5 additions & 0 deletions spec/models/asset_observer.rb
@@ -0,0 +1,5 @@
class AssetObserver < Cequel::Model::Observer
def before_save(asset)
asset.observed!(:before_save)
end
end
2 changes: 1 addition & 1 deletion spec/models/post_observer.rb
@@ -1,4 +1,4 @@
class PostObserver < ActiveModel::Observer
class PostObserver < Cequel::Model::Observer

def before_create(post)
post.observed!(:before_create)
Expand Down

0 comments on commit 7b1c732

Please sign in to comment.