yfactorial-roxy 0.1.2 → 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/roxy/moxie.rb +43 -10
- data/lib/roxy/proxy.rb +11 -3
- data/spec/family_person_spec.rb +77 -0
- data/spec/fixtures/{family.rb → family_person.rb} +15 -2
- data/spec/fixtures/person.rb +16 -0
- data/spec/proxy_spec.rb +3 -3
- data/spec/roxy_spec.rb +22 -0
- metadata +4 -4
- data/spec/family_spec.rb +0 -53
data/lib/roxy/moxie.rb
CHANGED
|
@@ -8,19 +8,52 @@ module Roxy
|
|
|
8
8
|
module ClassMethods
|
|
9
9
|
|
|
10
10
|
# Set up this class to proxy on the given name
|
|
11
|
-
def proxy(name, options = {}, &block)
|
|
11
|
+
def proxy(name, options = {}, &block)
|
|
12
12
|
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
options[:to] = lambda { |owner| owner.send("proxied_#{name}") }
|
|
18
|
-
end
|
|
13
|
+
# Make sure args are OK
|
|
14
|
+
original_method = method_defined?(name) ? instance_method(name) : nil
|
|
15
|
+
raise "Cannot proxy an existing method, \"#{name}\", and also have a :to option. Please use one or the other." if
|
|
16
|
+
original_method and options[:to]
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
# If we're proxying an existing method, we need to store
|
|
19
|
+
# the original method and move it out of the way so
|
|
20
|
+
# we can take over
|
|
21
|
+
if original_method
|
|
22
|
+
new_method = "proxied_#{name}"
|
|
23
|
+
alias_method new_method, "#{name}"
|
|
24
|
+
options[:to] = original_method
|
|
23
25
|
end
|
|
26
|
+
|
|
27
|
+
roxy_proxy_methods[name] = [options, block]
|
|
28
|
+
|
|
29
|
+
# If we have a no-arg method we're proxying, or if we're not
|
|
30
|
+
# proxying an existing method at all, we can do a basic def
|
|
31
|
+
# and memoize the proxy
|
|
32
|
+
if !original_method or original_method.arity == 0
|
|
33
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
|
34
|
+
def #{name}
|
|
35
|
+
@#{name}_proxy ||= Proxy.new(self, self.class.roxy_proxy_methods[:#{name}][0],
|
|
36
|
+
nil,
|
|
37
|
+
&self.class.roxy_proxy_methods[:#{name}][1])
|
|
38
|
+
end
|
|
39
|
+
EOS
|
|
40
|
+
|
|
41
|
+
# If we have a proxied method with arguments, we need to
|
|
42
|
+
# retain them
|
|
43
|
+
else
|
|
44
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
|
45
|
+
def #{name}(*args)
|
|
46
|
+
Proxy.new(self, self.class.roxy_proxy_methods[:#{name}][0],
|
|
47
|
+
args,
|
|
48
|
+
&self.class.roxy_proxy_methods[:#{name}][1])
|
|
49
|
+
end
|
|
50
|
+
EOS
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def roxy_proxy_methods
|
|
56
|
+
@roxy_proxy_methods ||= {}
|
|
24
57
|
end
|
|
25
58
|
end
|
|
26
59
|
end
|
data/lib/roxy/proxy.rb
CHANGED
|
@@ -12,9 +12,10 @@ module Roxy
|
|
|
12
12
|
# http://onestepback.org/index.cgi/Tech/Ruby/BlankSlate.rdoc
|
|
13
13
|
instance_methods.each { |m| undef_method m unless m =~ /(^__|^proxy_)/ }
|
|
14
14
|
|
|
15
|
-
def initialize(owner, options, &block)
|
|
15
|
+
def initialize(owner, options, args, &block)
|
|
16
16
|
@owner = owner
|
|
17
|
-
@target = options[:to]
|
|
17
|
+
@target = options[:to]
|
|
18
|
+
@args = args
|
|
18
19
|
|
|
19
20
|
# Adorn with user-provided proxy methods
|
|
20
21
|
[options[:extend]].flatten.each { |ext| proxy_extend(ext) } if options[:extend]
|
|
@@ -23,7 +24,14 @@ module Roxy
|
|
|
23
24
|
|
|
24
25
|
def proxy_owner; @owner; end
|
|
25
26
|
def proxy_target
|
|
26
|
-
|
|
27
|
+
if @target.is_a?(Proc)
|
|
28
|
+
@target.call(@owner)
|
|
29
|
+
elsif @target.is_a?(UnboundMethod)
|
|
30
|
+
bound_method = @target.bind(proxy_owner)
|
|
31
|
+
bound_method.arity == 0 ? bound_method.call : bound_method.call(*@args)
|
|
32
|
+
else
|
|
33
|
+
@target
|
|
34
|
+
end
|
|
27
35
|
end
|
|
28
36
|
|
|
29
37
|
# Delegate all method calls we don't know about to target object
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[abstract_spec])
|
|
2
|
+
|
|
3
|
+
describe "Whole family example" do
|
|
4
|
+
|
|
5
|
+
before do
|
|
6
|
+
uses_fixture(:family_person)
|
|
7
|
+
@ryan = FamilyPerson.new('Ryan', 'Daigle')
|
|
8
|
+
@ryan_parents = [FamilyPerson.new('Dad', 'Daigle'), FamilyPerson.new('Mom', 'Daigle')]
|
|
9
|
+
@ryan.parents = @ryan_parents
|
|
10
|
+
@ryan_children = [FamilyPerson.new('Child1', 'Daigle'), FamilyPerson.new('Child2', 'Daigle')]
|
|
11
|
+
@ryan.children = @ryan_children
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should know a person's parents (proxy should not overwrite target method)" do
|
|
15
|
+
@ryan.parents.should == @ryan_parents
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should know if a person's parents are divorced" do
|
|
19
|
+
@ryan.parents.divorced?.should be_false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should know how to print out parents names' to a string when not divorced" do
|
|
23
|
+
@ryan.parents.to_s.should == "Mr. and Mrs. Daigle"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should know how to get a person's step-children" do
|
|
27
|
+
@ryan.children.step.should be_empty
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "Divorced family example" do
|
|
32
|
+
|
|
33
|
+
before do
|
|
34
|
+
uses_fixture(:family_person)
|
|
35
|
+
@ryan = FamilyPerson.new('Ryan', 'Daigle')
|
|
36
|
+
@ryan_parents = [FamilyPerson.new('Dad', 'Daigle'), FamilyPerson.new('Mom', 'NotDaigle')]
|
|
37
|
+
@ryan.parents = @ryan_parents
|
|
38
|
+
@ryan_children = [FamilyPerson.new('Child1', 'Daigle'), FamilyPerson.new('Child2', 'NotDaigle')]
|
|
39
|
+
@ryan.children = @ryan_children
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should know if a person's parents are divorced" do
|
|
43
|
+
@ryan.parents.divorced?.should be_true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "should know how to print out parents names' to a string when divorced" do
|
|
47
|
+
@ryan.parents.to_s.should == "Dad Daigle and Mom NotDaigle"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "should know how to get a person's step-children" do
|
|
51
|
+
@ryan.children.step.should == [@ryan_children.last]
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "Ancestors example" do
|
|
56
|
+
|
|
57
|
+
before do
|
|
58
|
+
uses_fixture(:family_person)
|
|
59
|
+
@ryan = FamilyPerson.new('Ryan', 'Daigle')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should proxy through the ancestors method and retain arguments" do
|
|
63
|
+
@ryan.ancestors(true).sort.should == ['rancestor1', 'rancestor3', 'rancestor2', 'rancestor4'].sort
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should call proxied ancestor methods and retain arguments" do
|
|
67
|
+
@ryan.ancestors(true).men.sort.should == ['rancestor1', 'rancestor3'].sort
|
|
68
|
+
@ryan.ancestors(true).women.sort.should == ['rancestor2', 'rancestor4'].sort
|
|
69
|
+
@ryan.ancestors(false).men.sort.should == ['ancestor1', 'ancestor3'].sort
|
|
70
|
+
@ryan.ancestors(false).women.sort.should == ['ancestor2', 'ancestor4'].sort
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should call proxied ancestor methods and retain default arguments" do
|
|
74
|
+
@ryan.ancestors.men.sort.should == ['ancestor1', 'ancestor3'].sort
|
|
75
|
+
@ryan.ancestors.women.sort.should == ['ancestor2', 'ancestor4'].sort
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class FamilyPerson
|
|
2
2
|
|
|
3
3
|
include Roxy::Moxie
|
|
4
4
|
|
|
@@ -28,11 +28,24 @@ class Person
|
|
|
28
28
|
def step
|
|
29
29
|
proxy_target.select { |child| proxy_owner.last != child.last }
|
|
30
30
|
end
|
|
31
|
-
end
|
|
31
|
+
end
|
|
32
32
|
|
|
33
33
|
def initialize(first, last)
|
|
34
34
|
@first, @last = first, last
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def ancestors(reload = false)
|
|
38
|
+
1.upto(4).to_a.collect { |i| "#{reload ? 'r' : ''}ancestor#{i}" }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
proxy :ancestors do
|
|
42
|
+
def men
|
|
43
|
+
proxy_target.select { |a| a.include?('1') || a.include?('3') }
|
|
44
|
+
end
|
|
45
|
+
def women
|
|
46
|
+
proxy_target - men
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
37
50
|
def to_s; "#{first} #{last}"; end
|
|
38
51
|
end
|
data/spec/fixtures/person.rb
CHANGED
|
@@ -27,4 +27,20 @@ class Person
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
proxy :neighbors, :to => ['neighbor1', 'neighbor2'], :extend => [NeighborGeographics, NeighborDemographics]
|
|
30
|
+
|
|
31
|
+
def ancestors(maternal = true, paternal = true)
|
|
32
|
+
paternal_ancestors = ['p_ancestor1', 'p_ancestor2']
|
|
33
|
+
maternal_ancestors = ['m_ancestor1', 'm_ancestor2']
|
|
34
|
+
(maternal ? maternal_ancestors : []) + (paternal ? paternal_ancestors : [])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
proxy :ancestors do
|
|
38
|
+
def men
|
|
39
|
+
proxy_target.select { |ancestor| ancestor[-1, 1] == '1' }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def women
|
|
43
|
+
proxy_target.select { |ancestor| ancestor[-1, 1] == '2' }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
30
46
|
end
|
data/spec/proxy_spec.rb
CHANGED
|
@@ -9,19 +9,19 @@ describe "Proxy" do
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
it "should properly evaluate a block-based target" do
|
|
12
|
-
proxy = Roxy::Proxy.new(@owner, :to => @lambda_target)
|
|
12
|
+
proxy = Roxy::Proxy.new(@owner, {:to => @lambda_target}, nil)
|
|
13
13
|
proxy.should == @target
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
it "should properly adorn the proxy with proxy methods" do
|
|
17
|
-
proxy = Roxy::Proxy.new(@owner, :to => @target) do
|
|
17
|
+
proxy = Roxy::Proxy.new(@owner, {:to => @target}, nil) do
|
|
18
18
|
def poop; 'poop'; end
|
|
19
19
|
end
|
|
20
20
|
proxy.poop.should == 'poop'
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
it "should make the proxy owner accessible to the target block" do
|
|
24
|
-
proxy = Roxy::Proxy.new(@owner, :to => proc { |owner| owner })
|
|
24
|
+
proxy = Roxy::Proxy.new(@owner, {:to => proc { |owner| owner }}, nil)
|
|
25
25
|
proxy.should == @owner
|
|
26
26
|
end
|
|
27
27
|
|
data/spec/roxy_spec.rb
CHANGED
|
@@ -32,4 +32,26 @@ describe "Roxy" do
|
|
|
32
32
|
@person.neighbors.nearby?.should be_true
|
|
33
33
|
@person.neighbors.caucasian?.should be_false
|
|
34
34
|
end
|
|
35
|
+
|
|
36
|
+
it "should be able to pass arguments through the proxy" do
|
|
37
|
+
@person.ancestors(true, false).should == ['m_ancestor1', 'm_ancestor2']
|
|
38
|
+
@person.ancestors(false, true).should == ['p_ancestor1', 'p_ancestor2']
|
|
39
|
+
@person.ancestors(true, true).should == ['m_ancestor1', 'm_ancestor2'] + ['p_ancestor1', 'p_ancestor2']
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should be able to retain default argument values" do
|
|
43
|
+
@person.ancestors.should == ['m_ancestor1', 'm_ancestor2'] + ['p_ancestor1', 'p_ancestor2']
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "should be able to call a proxy method through a method with arguments" do
|
|
47
|
+
@person.ancestors(true, false).men.should == ['m_ancestor1']
|
|
48
|
+
@person.ancestors(true, false).women.should == ['m_ancestor2']
|
|
49
|
+
@person.ancestors(false, true).men.should == ['p_ancestor1']
|
|
50
|
+
@person.ancestors(false, true).women.should == ['p_ancestor2']
|
|
51
|
+
@person.ancestors(true, true).men.sort.should == ['p_ancestor1', 'm_ancestor1'].sort
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "should retain default argument values when calling a proxied method with arguments" do
|
|
55
|
+
@person.ancestors.women.sort.should == ['p_ancestor2', 'm_ancestor2'].sort
|
|
56
|
+
end
|
|
35
57
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yfactorial-roxy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: "0.2"
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ryan Daigle
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2008-11-
|
|
12
|
+
date: 2008-11-14 00:00:00 -08:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies: []
|
|
15
15
|
|
|
@@ -39,10 +39,10 @@ files:
|
|
|
39
39
|
- spec/abstract_spec.rb
|
|
40
40
|
- spec/proxy_spec.rb
|
|
41
41
|
- spec/roxy_spec.rb
|
|
42
|
-
- spec/
|
|
42
|
+
- spec/family_person_spec.rb
|
|
43
43
|
- spec/fixtures
|
|
44
44
|
- spec/fixtures/person.rb
|
|
45
|
-
- spec/fixtures/
|
|
45
|
+
- spec/fixtures/family_person.rb
|
|
46
46
|
has_rdoc: true
|
|
47
47
|
homepage: http://github.com/yfactorial/roxy
|
|
48
48
|
post_install_message:
|
data/spec/family_spec.rb
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
require File.join(File.dirname(__FILE__), *%w[abstract_spec])
|
|
2
|
-
|
|
3
|
-
describe "Whole family example" do
|
|
4
|
-
|
|
5
|
-
before do
|
|
6
|
-
uses_fixture(:family)
|
|
7
|
-
@ryan = Person.new('Ryan', 'Daigle')
|
|
8
|
-
@ryan_parents = [Person.new('Dad', 'Daigle'), Person.new('Mom', 'Daigle')]
|
|
9
|
-
@ryan.parents = @ryan_parents
|
|
10
|
-
@ryan_children = [Person.new('Child1', 'Daigle'), Person.new('Child2', 'Daigle')]
|
|
11
|
-
@ryan.children = @ryan_children
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
it "should know a person's parents (proxy should not overwrite target method)" do
|
|
15
|
-
@ryan.parents.should == @ryan_parents
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it "should know if a person's parents are divorced" do
|
|
19
|
-
@ryan.parents.divorced?.should be_false
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it "should know how to print out parents names' to a string when not divorced" do
|
|
23
|
-
@ryan.parents.to_s.should == "Mr. and Mrs. Daigle"
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
it "should know how to get a person's step-children" do
|
|
27
|
-
@ryan.children.step.should be_empty
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
describe "Divorced family example" do
|
|
32
|
-
|
|
33
|
-
before do
|
|
34
|
-
uses_fixture(:family)
|
|
35
|
-
@ryan = Person.new('Ryan', 'Daigle')
|
|
36
|
-
@ryan_parents = [Person.new('Dad', 'Daigle'), Person.new('Mom', 'NotDaigle')]
|
|
37
|
-
@ryan.parents = @ryan_parents
|
|
38
|
-
@ryan_children = [Person.new('Child1', 'Daigle'), Person.new('Child2', 'NotDaigle')]
|
|
39
|
-
@ryan.children = @ryan_children
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it "should know if a person's parents are divorced" do
|
|
43
|
-
@ryan.parents.divorced?.should be_true
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it "should know how to print out parents names' to a string when divorced" do
|
|
47
|
-
@ryan.parents.to_s.should == "Dad Daigle and Mom NotDaigle"
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it "should know how to get a person's step-children" do
|
|
51
|
-
@ryan.children.step.should == [@ryan_children.last]
|
|
52
|
-
end
|
|
53
|
-
end
|