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 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
- # If we don't have the :to option then we are proxying an existing
14
- # method
15
- if(!options[:to])
16
- alias_method "proxied_#{name}", "#{name}"
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
- define_method(name) do
21
- instance_variable_get("@#{name}_proxy") ||
22
- instance_variable_set("@#{name}_proxy", Proxy.new(self, options, &block))
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
- @proxy_target_object ||= @target.is_a?(Proc) ? @target.call(@owner) : @target
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 Person
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
@@ -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.1.2
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-11 00:00:00 -08:00
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/family_spec.rb
42
+ - spec/family_person_spec.rb
43
43
  - spec/fixtures
44
44
  - spec/fixtures/person.rb
45
- - spec/fixtures/family.rb
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