Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: kudelabs/method_proxy
base: fcf9a79117
...
head fork: kudelabs/method_proxy
compare: 6dbfb85924
Checking mergeability… Don't worry, you can still create the pull request.
  • 3 commits
  • 3 files changed
  • 0 commit comments
  • 1 contributor
Showing with 111 additions and 30 deletions.
  1. +13 −7 README.markdown
  2. +10 −8 lib/method_proxy.rb
  3. +88 −15 test/method_proxy_test.rb
View
20 README.markdown
@@ -13,7 +13,8 @@ generated from supplied block. Supplied block should expect following
arguments:
1) object on which the call is made;
2) original method bound to the object on which call is made;
- 3-...) *args - arguments of original function call.
+ *args - arguments of original method call;
+ &block - block passed to the original method call.
An important thing to remember, is the &block should not use 'return <result>'
and should use 'next <result>' construct instead - as 'return' will result in
@@ -33,8 +34,9 @@ Replaces original class method with method generated from supplied block.
Supplied block should expect following arguments:
1) class on which the call is made;
2) original method bound to that class;
- 3-...) *args - arguments of original function call.
-
+ *args - arguments of original method call;
+ &block - block passed to the original method call.
+
Just like in case of proxy_instance_method, &block should use 'next <result>'
construct instead of 'return <result>' or LocalJumpError will be raised.
@@ -101,8 +103,8 @@ arrange interception of actions of interest (say, those that change database sta
actions' parameters:
tbd = {
- UsersController => %w(create update delete).collect(&:to_sym),
- PostsController => %w(create update delete).collect(&:to_sym)
+ UsersController => [:create, :update, :delete],
+ PostsController => [:create, :update, :delete]
}
tbd.each_pair do |cntrlr, actns|
@@ -129,7 +131,6 @@ Later, the resulting "records" can be "replayed" in automated tests.
There is a number of known issues:
-- currently, method_proxy cannot tap into method calls that accept blocks;
- have to remember to use "next <result>" instead of "return <result>" syntax;
- if one needs to tap into methods that are dynamically created, e.g.
'find_user_by_id' created on-the-fly with the help of 'method_missing' in
@@ -166,7 +167,12 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+### CREDITS
+Special thanks for their invaluable feedback, advice, suggestions and fixes to:
+wtaysom, adehvadeh, shaokun and rainchen.
+
+
### PROJECT ON THE WEB
The project is hosted on GitHub:
-https://github.com/kudelabs/method_proxy/
+https://github.com/kudelabs/method_proxy/
View
18 lib/method_proxy.rb
@@ -1,3 +1,5 @@
+require 'thread' if RUBY_VERSION <"1.9"
+
class MethodProxyException < Exception
end
@@ -73,13 +75,13 @@ def self.reset_tmp_binding!
# reference to the original method so that it can still be called or restored later on.
#
# Common idiom:
- # MethodProxy.proxy_instance_method(SomeClass, :some_instance_method) do |obj, original_instance_meth, *args|
+ # MethodProxy.proxy_instance_method(SomeClass, :some_instance_method) do |obj, original_instance_meth, *args, &block|
#
# # do stuff before calling original method
# ... ... ...
#
# # call the original method (already bound to object obj), with supplied arguments
- # result = original_instance_meth.call(*args)
+ # result = original_instance_meth.call(*args, &block)
#
# # do stuff after calling original method
# ... ... ...
@@ -106,8 +108,8 @@ def self.proxy_instance_method(klass, meth, &block)
undef_method(meth)
- define_method meth do |*args|
- ret = proc.call(self, MethodProxy.original_instance_method(klass, meth).bind(self), *args)
+ define_method meth do |*args, &blk|
+ ret = proc.call(self, MethodProxy.original_instance_method(klass, meth).bind(self), *args, &blk)
return ret
end
end
@@ -136,13 +138,13 @@ def self.unproxy_instance_method(klass, meth)
# reference to the original method so that it can still be called or restored later on.
#
# Common idiom:
- # MethodProxy.proxy_class_method(SomeClass, :some_class_method) do |klass, original_class_meth, *args|
+ # MethodProxy.proxy_class_method(SomeClass, :some_class_method) do |klass, original_class_meth, *args, &block|
#
# # do stuff before calling original method
# ... ... ...
#
# # call original method (already bound to SomeClass), with supplied arguments
- # result = original_class_meth.call(*args)
+ # result = original_class_meth.call(*args, &block)
#
# # do stuff after calling original method
# ... ... ...
@@ -176,8 +178,8 @@ class << klass
remove_method meth
self.instance_eval do
- define_method(meth) do |*args|
- ret = proc.call(self, @@proxied_class_methods[klass.name.to_sym][meth].bind(self), *args)
+ define_method(meth) do |*args, &blk|
+ ret = proc.call(self, @@proxied_class_methods[klass.name.to_sym][meth].bind(self), *args, &blk)
return ret
end
end
View
103 test/method_proxy_test.rb
@@ -1,11 +1,23 @@
class GuineaPig
- def lr_instance_method a, b
+ def gp_instance_method a, b
return "#{a}-#{b}"
end
- def self.lr_class_method c, d
+ def gp_instance_method_w_block a, b, &block
+ interm = "*#{a}*#{b}*"
+ res = yield interm
+ res
+ end
+
+ def self.gp_class_method c, d
return "#{c}$#{d}"
end
+
+ def self.gp_class_method_w_block c, d, &block
+ interm = "-=#{c}O#{d}=-"
+ res = yield interm
+ res
+ end
end
require File.join(File.expand_path(File.dirname(__FILE__)), "../lib/method_proxy")
@@ -14,40 +26,101 @@ def self.lr_class_method c, d
class MethodProxyTest < Test::Unit::TestCase
def test_proxy_unproxy_instance_method
gpig = GuineaPig.new
- assert_equal "2-3", gpig.lr_instance_method(2, 3)
+ assert_equal "2-3", gpig.gp_instance_method(2, 3)
assert [].eql?(MethodProxy.classes_with_proxied_instance_methods)
assert_equal [], MethodProxy.proxied_instance_methods_for(GuineaPig)
- MethodProxy.proxy_instance_method(GuineaPig, :lr_instance_method) do |obj, meth, a, b|
+ MethodProxy.proxy_instance_method(GuineaPig, :gp_instance_method) do |obj, meth, a, b|
res = meth.call a, b
next "<#{res}>" # within Proc's should be 'next' instead of 'return' -- in order to avoid returning from the caller method
end
- assert_equal "<2-3>", gpig.lr_instance_method(2, 3)
+ assert_equal "<2-3>", gpig.gp_instance_method(2, 3)
+ assert [GuineaPig].eql?(MethodProxy.classes_with_proxied_instance_methods)
+ assert_equal [:gp_instance_method], MethodProxy.proxied_instance_methods_for(GuineaPig)
+
+ MethodProxy.unproxy_instance_method(GuineaPig, :gp_instance_method)
+ assert_equal "2-3", gpig.gp_instance_method(2, 3)
+ assert [].eql?(MethodProxy.classes_with_proxied_instance_methods)
+ assert_equal [], MethodProxy.proxied_instance_methods_for(GuineaPig)
+ end
+
+ def test_proxy_unproxy_instance_method_w_block
+ gpig = GuineaPig.new
+ before_res = gpig.gp_instance_method_w_block(6, 7) do |interm|
+ "#{interm}<-->#{interm}"
+ end
+ assert [].eql?(MethodProxy.classes_with_proxied_instance_methods)
+ assert_equal [], MethodProxy.proxied_instance_methods_for(GuineaPig)
+ assert_equal "*6*7*<-->*6*7*", before_res
+
+ MethodProxy.proxy_instance_method(GuineaPig, :gp_instance_method_w_block) do |obj, meth, a, b, &block|
+ res = meth.call a, b, &block
+ next "!!!__#{res}__!!!" # within Proc's should be 'next' instead of 'return' -- in order to avoid returning from the caller method
+ end
+
+ after_res = gpig.gp_instance_method_w_block(6, 7) do |interm|
+ "#{interm}<-->#{interm}"
+ end
+
+ assert_equal "!!!__*6*7*<-->*6*7*__!!!", after_res
assert [GuineaPig].eql?(MethodProxy.classes_with_proxied_instance_methods)
- assert_equal [:lr_instance_method], MethodProxy.proxied_instance_methods_for(GuineaPig)
+ assert_equal [:gp_instance_method_w_block], MethodProxy.proxied_instance_methods_for(GuineaPig)
- MethodProxy.unproxy_instance_method(GuineaPig, :lr_instance_method)
- assert_equal "2-3", gpig.lr_instance_method(2, 3)
+ MethodProxy.unproxy_instance_method(GuineaPig, :gp_instance_method_w_block)
+ after_un_res = gpig.gp_instance_method_w_block(6, 7) do |interm|
+ "#{interm}<-->#{interm}"
+ end
+ assert_equal before_res, after_un_res
assert [].eql?(MethodProxy.classes_with_proxied_instance_methods)
assert_equal [], MethodProxy.proxied_instance_methods_for(GuineaPig)
end
def test_proxy_unproxy_class_method
- assert_equal "4$5", GuineaPig.lr_class_method(4, 5)
+ assert_equal "4$5", GuineaPig.gp_class_method(4, 5)
assert [].eql?(MethodProxy.classes_with_proxied_class_methods)
assert_equal [], MethodProxy.proxied_class_methods_for(GuineaPig)
- MethodProxy.proxy_class_method(GuineaPig, :lr_class_method) do |obj, meth, c, d|
+ MethodProxy.proxy_class_method(GuineaPig, :gp_class_method) do |obj, meth, c, d|
res = meth.call c, d
next "[#{res}]" # within Proc's should be 'next' instead of 'return' -- in order to avoid returning from the caller method
end
- assert_equal "[4$5]", GuineaPig.lr_class_method(4, 5)
+ assert_equal "[4$5]", GuineaPig.gp_class_method(4, 5)
+ assert [GuineaPig].eql?(MethodProxy.classes_with_proxied_class_methods)
+ assert_equal [:gp_class_method], MethodProxy.proxied_class_methods_for(GuineaPig)
+
+ MethodProxy.unproxy_class_method(GuineaPig, :gp_class_method)
+ assert_equal "4$5", GuineaPig.gp_class_method(4, 5)
+ assert [].eql?(MethodProxy.classes_with_proxied_class_methods)
+ assert_equal [], MethodProxy.proxied_class_methods_for(GuineaPig)
+ end
+
+ def test_proxy_unproxy_class_method_w_block
+ before_res = GuineaPig.gp_class_method_w_block(8, 9) do |interm|
+ "#{interm}>--<#{interm}"
+ end
+ assert_equal "-=8O9=->--<-=8O9=-", before_res
+ assert [].eql?(MethodProxy.classes_with_proxied_class_methods)
+ assert_equal [], MethodProxy.proxied_class_methods_for(GuineaPig)
+
+ MethodProxy.proxy_class_method(GuineaPig, :gp_class_method_w_block) do |obj, meth, a, b, &block|
+ res = meth.call a, b, &block
+ next "???__#{res}__???" # within Proc's should be 'next' instead of 'return' -- in order to avoid returning from the caller method
+ end
+
+ after_res = GuineaPig.gp_class_method_w_block(8, 9) do |interm|
+ "#{interm}>--<#{interm}"
+ end
+
+ assert_equal "???__-=8O9=->--<-=8O9=-__???", after_res
assert [GuineaPig].eql?(MethodProxy.classes_with_proxied_class_methods)
- assert_equal [:lr_class_method], MethodProxy.proxied_class_methods_for(GuineaPig)
+ assert_equal [:gp_class_method_w_block], MethodProxy.proxied_class_methods_for(GuineaPig)
- MethodProxy.unproxy_class_method(GuineaPig, :lr_class_method)
- assert_equal "4$5", GuineaPig.lr_class_method(4, 5)
+ MethodProxy.unproxy_class_method(GuineaPig, :gp_class_method_w_block)
+ after_un_res = GuineaPig.gp_class_method_w_block(8, 9) do |interm|
+ "#{interm}>--<#{interm}"
+ end
+ assert_equal before_res, after_un_res
assert [].eql?(MethodProxy.classes_with_proxied_class_methods)
assert_equal [], MethodProxy.proxied_class_methods_for(GuineaPig)
end
-end
+end

No commit comments for this range

Something went wrong with that request. Please try again.