updated the callback system using Yehuda's latest version and bumped the release to make the latest fixes available
This commit is contained in:
parent
fdb0854859
commit
5607936540
File diff suppressed because one or more lines are too long
|
@ -28,7 +28,7 @@ require 'couchrest/monkeypatches'
|
||||||
|
|
||||||
# = CouchDB, close to the metal
|
# = CouchDB, close to the metal
|
||||||
module CouchRest
|
module CouchRest
|
||||||
VERSION = '0.2' unless self.const_defined?("VERSION")
|
VERSION = '0.2.1' unless self.const_defined?("VERSION")
|
||||||
|
|
||||||
autoload :Server, 'couchrest/core/server'
|
autoload :Server, 'couchrest/core/server'
|
||||||
autoload :Database, 'couchrest/core/database'
|
autoload :Database, 'couchrest/core/database'
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
require File.join(File.dirname(__FILE__), '..', 'support', 'class')
|
require File.join(File.dirname(__FILE__), '..', 'support', 'class')
|
||||||
|
|
||||||
# Extracted from ActiveSupport::Callbacks written by Yehuda Katz
|
# Extracted from ActiveSupport::Callbacks written by Yehuda Katz
|
||||||
|
# http://github.com/wycats/rails/raw/abstract_controller/activesupport/lib/active_support/new_callbacks.rb
|
||||||
# http://github.com/wycats/rails/raw/18b405f154868204a8f332888871041a7bad95e1/activesupport/lib/active_support/callbacks.rb
|
# http://github.com/wycats/rails/raw/18b405f154868204a8f332888871041a7bad95e1/activesupport/lib/active_support/callbacks.rb
|
||||||
|
|
||||||
module CouchRest
|
module CouchRest
|
||||||
|
@ -11,7 +12,7 @@ module CouchRest
|
||||||
#
|
#
|
||||||
# Example:
|
# Example:
|
||||||
# class Storage
|
# class Storage
|
||||||
# include CouchRest::Callbacks
|
# include ActiveSupport::Callbacks
|
||||||
#
|
#
|
||||||
# define_callbacks :save
|
# define_callbacks :save
|
||||||
# end
|
# end
|
||||||
|
@ -45,7 +46,7 @@ module CouchRest
|
||||||
#
|
#
|
||||||
# Example:
|
# Example:
|
||||||
# class Storage
|
# class Storage
|
||||||
# include CouchRest::Callbacks
|
# include ActiveSupport::Callbacks
|
||||||
#
|
#
|
||||||
# define_callbacks :save
|
# define_callbacks :save
|
||||||
#
|
#
|
||||||
|
@ -85,8 +86,8 @@ module CouchRest
|
||||||
klass.extend ClassMethods
|
klass.extend ClassMethods
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_callbacks(kind, options = {})
|
def run_callbacks(kind, options = {}, &blk)
|
||||||
send("_run_#{kind}_callbacks")
|
send("_run_#{kind}_callbacks", &blk)
|
||||||
end
|
end
|
||||||
|
|
||||||
class Callback
|
class Callback
|
||||||
|
@ -166,9 +167,13 @@ module CouchRest
|
||||||
|
|
||||||
# This will supply contents for before and around filters, and no
|
# This will supply contents for before and around filters, and no
|
||||||
# contents for after filters (for the forward pass).
|
# contents for after filters (for the forward pass).
|
||||||
def start(key = nil, object = nil)
|
def start(key = nil, options = {})
|
||||||
|
object, terminator = (options || {}).values_at(:object, :terminator)
|
||||||
|
|
||||||
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
|
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
|
||||||
|
|
||||||
|
terminator ||= false
|
||||||
|
|
||||||
# options[0] is the compiled form of supplied conditions
|
# options[0] is the compiled form of supplied conditions
|
||||||
# options[1] is the "end" for the conditional
|
# options[1] is the "end" for the conditional
|
||||||
|
|
||||||
|
@ -177,8 +182,14 @@ module CouchRest
|
||||||
# if condition # before_save :filter_name, :if => :condition
|
# if condition # before_save :filter_name, :if => :condition
|
||||||
# filter_name
|
# filter_name
|
||||||
# end
|
# end
|
||||||
[@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
|
filter = <<-RUBY_EVAL
|
||||||
elsif @compiled_options[0]
|
unless halted
|
||||||
|
result = #{@filter}
|
||||||
|
halted ||= (#{terminator})
|
||||||
|
end
|
||||||
|
RUBY_EVAL
|
||||||
|
[@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
|
||||||
|
else
|
||||||
# Compile around filters with conditions into proxy methods
|
# Compile around filters with conditions into proxy methods
|
||||||
# that contain the conditions.
|
# that contain the conditions.
|
||||||
#
|
#
|
||||||
|
@ -196,8 +207,8 @@ module CouchRest
|
||||||
|
|
||||||
name = "_conditional_callback_#{@kind}_#{next_id}"
|
name = "_conditional_callback_#{@kind}_#{next_id}"
|
||||||
txt = <<-RUBY_EVAL
|
txt = <<-RUBY_EVAL
|
||||||
def #{name}
|
def #{name}(halted)
|
||||||
#{@compiled_options[0]}
|
#{@compiled_options[0] || "if true"} && !halted
|
||||||
#{@filter} do
|
#{@filter} do
|
||||||
yield self
|
yield self
|
||||||
end
|
end
|
||||||
|
@ -207,16 +218,16 @@ module CouchRest
|
||||||
end
|
end
|
||||||
RUBY_EVAL
|
RUBY_EVAL
|
||||||
@klass.class_eval(txt)
|
@klass.class_eval(txt)
|
||||||
"#{name} do"
|
"#{name}(halted) do"
|
||||||
else
|
|
||||||
"#{@filter} do"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# This will supply contents for around and after filters, but not
|
# This will supply contents for around and after filters, but not
|
||||||
# before filters (for the backward pass).
|
# before filters (for the backward pass).
|
||||||
def end(key = nil, object = nil)
|
def end(key = nil, options = {})
|
||||||
|
object = (options || {})[:object]
|
||||||
|
|
||||||
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
|
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
|
||||||
|
|
||||||
if @kind == :around || @kind == :after
|
if @kind == :around || @kind == :after
|
||||||
|
@ -299,7 +310,7 @@ module CouchRest
|
||||||
# This method_missing is supplied to catch callbacks with keys and create
|
# This method_missing is supplied to catch callbacks with keys and create
|
||||||
# the appropriate callback for future use.
|
# the appropriate callback for future use.
|
||||||
def method_missing(meth, *args, &blk)
|
def method_missing(meth, *args, &blk)
|
||||||
if meth.to_s =~ /_run_(\w+)_(\w+)_(\w+)_callbacks/
|
if meth.to_s =~ /_run__([\w:]+)__(\w+)__(\w+)__callbacks/
|
||||||
return self.class._create_and_run_keyed_callback($1, $2.to_sym, $3.to_sym, self, &blk)
|
return self.class._create_and_run_keyed_callback($1, $2.to_sym, $3.to_sym, self, &blk)
|
||||||
end
|
end
|
||||||
super
|
super
|
||||||
|
@ -307,20 +318,26 @@ module CouchRest
|
||||||
|
|
||||||
# An Array with a compile method
|
# An Array with a compile method
|
||||||
class CallbackChain < Array
|
class CallbackChain < Array
|
||||||
def compile(key = nil, object = nil)
|
def initialize(symbol)
|
||||||
|
@symbol = symbol
|
||||||
|
end
|
||||||
|
|
||||||
|
def compile(key = nil, options = {})
|
||||||
method = []
|
method = []
|
||||||
|
method << "halted = false"
|
||||||
each do |callback|
|
each do |callback|
|
||||||
method << callback.start(key, object)
|
method << callback.start(key, options)
|
||||||
end
|
end
|
||||||
method << "yield self"
|
method << "yield self if block_given?"
|
||||||
reverse_each do |callback|
|
reverse_each do |callback|
|
||||||
method << callback.end(key, object)
|
method << callback.end(key, options)
|
||||||
end
|
end
|
||||||
method.compact.join("\n")
|
method.compact.join("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
def clone(klass)
|
def clone(klass)
|
||||||
CallbackChain.new(map {|c| c.clone(klass)})
|
chain = CallbackChain.new(@symbol)
|
||||||
|
chain.push(*map {|c| c.clone(klass)})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -339,16 +356,18 @@ module CouchRest
|
||||||
# will be used to compile an optimized callback method for each
|
# will be used to compile an optimized callback method for each
|
||||||
# key. See #define_callbacks for more information.
|
# key. See #define_callbacks for more information.
|
||||||
def _define_runner(symbol, str, options)
|
def _define_runner(symbol, str, options)
|
||||||
self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
str = <<-RUBY_EVAL
|
||||||
def _run_#{symbol}_callbacks(key = nil)
|
def _run_#{symbol}_callbacks(key = nil)
|
||||||
if key
|
if key
|
||||||
send("_run_\#{self.class}_#{symbol}_\#{key}_callbacks") { yield }
|
send("_run__\#{self.class.name.split("::").last}__#{symbol}__\#{key}__callbacks") { yield if block_given? }
|
||||||
else
|
else
|
||||||
#{str}
|
#{str}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
RUBY_EVAL
|
RUBY_EVAL
|
||||||
|
|
||||||
|
class_eval str, __FILE__, __LINE__ + 1
|
||||||
|
|
||||||
before_name, around_name, after_name =
|
before_name, around_name, after_name =
|
||||||
options.values_at(:before, :after, :around)
|
options.values_at(:before, :after, :around)
|
||||||
end
|
end
|
||||||
|
@ -359,15 +378,18 @@ module CouchRest
|
||||||
def _create_and_run_keyed_callback(klass, kind, key, obj, &blk)
|
def _create_and_run_keyed_callback(klass, kind, key, obj, &blk)
|
||||||
@_keyed_callbacks ||= {}
|
@_keyed_callbacks ||= {}
|
||||||
@_keyed_callbacks[[kind, key]] ||= begin
|
@_keyed_callbacks[[kind, key]] ||= begin
|
||||||
str = self.send("_#{kind}_callbacks").compile(key, obj)
|
str = self.send("_#{kind}_callbacks").compile(key, :object => obj, :terminator => self.send("_#{kind}_terminator"))
|
||||||
|
|
||||||
self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
||||||
def _run_#{klass}_#{kind}_#{key}_callbacks
|
def _run__#{klass.split("::").last}__#{kind}__#{key}__callbacks
|
||||||
#{str}
|
#{str}
|
||||||
end
|
end
|
||||||
RUBY_EVAL
|
RUBY_EVAL
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
obj.send("_run_#{klass}_#{kind}_#{key}_callbacks", &blk)
|
|
||||||
|
obj.send("_run__#{klass.split("::").last}__#{kind}__#{key}__callbacks", &blk)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Define callbacks.
|
# Define callbacks.
|
||||||
|
@ -402,20 +424,32 @@ module CouchRest
|
||||||
# method that took into consideration the per_key conditions. This
|
# method that took into consideration the per_key conditions. This
|
||||||
# is a speed improvement for ActionPack.
|
# is a speed improvement for ActionPack.
|
||||||
def define_callbacks(*symbols)
|
def define_callbacks(*symbols)
|
||||||
|
terminator = symbols.pop if symbols.last.is_a?(String)
|
||||||
symbols.each do |symbol|
|
symbols.each do |symbol|
|
||||||
|
self.class_inheritable_accessor("_#{symbol}_terminator")
|
||||||
|
self.send("_#{symbol}_terminator=", terminator)
|
||||||
self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
||||||
class_inheritable_accessor :_#{symbol}_callbacks
|
class_inheritable_accessor :_#{symbol}_callbacks
|
||||||
self._#{symbol}_callbacks = CallbackChain.new
|
self._#{symbol}_callbacks = CallbackChain.new(:#{symbol})
|
||||||
|
|
||||||
def self.#{symbol}_callback(type, *filters, &blk)
|
def self.#{symbol}_callback(*filters, &blk)
|
||||||
|
type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
|
||||||
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
||||||
filters.unshift(blk) if block_given?
|
filters.unshift(blk) if block_given?
|
||||||
filters.map! {|f| Callback.new(f, type, options.dup, self, :#{symbol})}
|
|
||||||
|
filters.map! do |filter|
|
||||||
|
# overrides parent class
|
||||||
|
self._#{symbol}_callbacks.delete_if {|c| c.matches?(type, :#{symbol}, filter)}
|
||||||
|
Callback.new(filter, type, options.dup, self, :#{symbol})
|
||||||
|
end
|
||||||
self._#{symbol}_callbacks.push(*filters)
|
self._#{symbol}_callbacks.push(*filters)
|
||||||
_define_runner(:#{symbol}, self._#{symbol}_callbacks.compile, options)
|
_define_runner(:#{symbol},
|
||||||
|
self._#{symbol}_callbacks.compile(nil, :terminator => _#{symbol}_terminator),
|
||||||
|
options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.skip_#{symbol}_callback(type, *filters, &blk)
|
def self.skip_#{symbol}_callback(*filters, &blk)
|
||||||
|
type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
|
||||||
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
||||||
filters.unshift(blk) if block_given?
|
filters.unshift(blk) if block_given?
|
||||||
filters.each do |filter|
|
filters.each do |filter|
|
||||||
|
@ -428,11 +462,18 @@ module CouchRest
|
||||||
else
|
else
|
||||||
self._#{symbol}_callbacks.delete(filter)
|
self._#{symbol}_callbacks.delete(filter)
|
||||||
end
|
end
|
||||||
_define_runner(:#{symbol}, self._#{symbol}_callbacks.compile, options)
|
_define_runner(:#{symbol},
|
||||||
|
self._#{symbol}_callbacks.compile(nil, :terminator => _#{symbol}_terminator),
|
||||||
|
options)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.reset_#{symbol}_callbacks
|
||||||
|
self._#{symbol}_callbacks = CallbackChain.new(:#{symbol})
|
||||||
|
_define_runner(:#{symbol}, self._#{symbol}_callbacks.compile, {})
|
||||||
|
end
|
||||||
|
|
||||||
self.#{symbol}_callback(:before)
|
self.#{symbol}_callback(:before)
|
||||||
RUBY_EVAL
|
RUBY_EVAL
|
||||||
end
|
end
|
||||||
|
|
|
@ -167,24 +167,24 @@ describe "ExtendedDocument views" do
|
||||||
# TODO: moved to Design, delete
|
# TODO: moved to Design, delete
|
||||||
describe "adding a view" do
|
describe "adding a view" do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
|
reset_test_db!
|
||||||
Article.by_date
|
Article.by_date
|
||||||
@design_docs = Article.database.documents :startkey => "_design/",
|
@design_docs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
|
||||||
:endkey => "_design/\u9999"
|
|
||||||
end
|
end
|
||||||
it "should not create a design doc on view definition" do
|
it "should not create a design doc on view definition" do
|
||||||
Article.view_by :created_at
|
Article.view_by :created_at
|
||||||
newdocs = Article.database.documents :startkey => "_design/",
|
newdocs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
|
||||||
:endkey => "_design/\u9999"
|
|
||||||
newdocs["rows"].length.should == @design_docs["rows"].length
|
newdocs["rows"].length.should == @design_docs["rows"].length
|
||||||
end
|
end
|
||||||
it "should create a new design document on view access" do
|
it "should create a new version of the design document on view access" do
|
||||||
|
old_design_doc = Article.database.documents(:key => @design_docs["rows"].first["key"], :include_docs => true)["rows"][0]["doc"]
|
||||||
Article.view_by :updated_at
|
Article.view_by :updated_at
|
||||||
Article.by_updated_at
|
Article.by_updated_at
|
||||||
newdocs = Article.database.documents :startkey => "_design/",
|
newdocs = Article.database.documents({:startkey => "_design/", :endkey => "_design/\u9999"})
|
||||||
:endkey => "_design/\u9999"
|
|
||||||
# puts @design_docs.inspect
|
doc = Article.database.documents(:key => @design_docs["rows"].first["key"], :include_docs => true)["rows"][0]["doc"]
|
||||||
# puts newdocs.inspect
|
doc["_rev"].should_not == old_design_doc["_rev"]
|
||||||
newdocs["rows"].length.should == @design_docs["rows"].length + 1
|
doc["views"].keys.should include("by_updated_at")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -195,10 +195,9 @@ describe "ExtendedDocument views" do
|
||||||
Article.by_field
|
Article.by_field
|
||||||
end
|
end
|
||||||
it "should clean them up" do
|
it "should clean them up" do
|
||||||
|
ddocs = Article.all_design_doc_versions
|
||||||
Article.view_by :stream
|
Article.view_by :stream
|
||||||
Article.by_stream
|
Article.by_stream
|
||||||
ddocs = Article.all_design_doc_versions
|
|
||||||
ddocs["rows"].length.should > 1
|
|
||||||
Article.cleanup_design_docs!
|
Article.cleanup_design_docs!
|
||||||
ddocs = Article.all_design_doc_versions
|
ddocs = Article.all_design_doc_versions
|
||||||
ddocs["rows"].length.should == 1
|
ddocs["rows"].length.should == 1
|
||||||
|
|
Loading…
Reference in a new issue