roxy 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ 0.1.0
2
+
3
+ * Initial release.
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2008 Ryan Daigle
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,51 @@
1
+ h1. Roxy (A ruby proxy library)
2
+
3
+ h2. Summary
4
+
5
+ Roxy is a basic proxy library that lets you quickly create proxies between your ruby objects. Its syntax
6
+ is loosely based on "Association Extensions in ActiveRecord":http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
7
+ as that is a well-known use of proxies.
8
+
9
+ Please see the "CHANGELOG":http://github.com/yfactorial/utility_scopes/tree/master/CHANGELOG for contribution details.
10
+
11
+ Roxy has the following dependencies:
12
+
13
+ * rspec >= 1.1.4 (for specs only, not runtime)
14
+
15
+ h2. Installation
16
+
17
+ To install the roxy gem run the following:
18
+
19
+ sudo gem install yfactorial-roxy --source http://gems.github.com
20
+
21
+ And to enable the scopes in your project just require the @roxy@ library and give your object some moxie:
22
+
23
+ require 'roxy'
24
+ class Person
25
+ include Roxy::Moxie
26
+ ...
27
+ end
28
+
29
+ h2. Usage
30
+
31
+ See the announcement post for detailed usage examples: "http://ryandaigle.com/articles/2008/11/10/implement-ruby-proxy-objects-with-roxy":http://ryandaigle.com/articles/2008/11/10/implement-ruby-proxy-objects-with-roxy
32
+
33
+ Here's a basic example:
34
+
35
+ <pre><code>
36
+ require 'roxy'
37
+ class Person
38
+ include Roxy::Moxie
39
+
40
+ attr_accessor :first, :last, :parents
41
+
42
+ proxy :parents do
43
+ def divorced?
44
+ proxy_target.size > 1 and proxy_target.collect { |parent| parent.last }.uniq.size > 1
45
+ end
46
+ end
47
+ end
48
+
49
+ # Can then invoke your proxy methods directly on parents
50
+ person.parents.divorced?
51
+ </code></pre>
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/rdoctask'
4
+ require 'spec/rake/spectask'
5
+
6
+ desc 'Run the specs'
7
+ Spec::Rake::SpecTask.new(:spec) do |t|
8
+ t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
9
+ t.spec_files = FileList['spec/**/*_spec.rb']
10
+ end
11
+
12
+ desc 'Generate RDoc documentation.'
13
+ Rake::RDocTask.new(:rdoc) do |rdoc|
14
+ rdoc.rdoc_files.include('README.textile', 'LICENSE', 'Rakefile').
15
+ include('lib/**/*.rb')
16
+
17
+ rdoc.main = "README.textile"
18
+ rdoc.title = "roxy documentation"
19
+
20
+ rdoc.rdoc_dir = 'doc' # rdoc output folder
21
+ rdoc.options << '--inline-source' << '--charset=UTF-8'
22
+ rdoc.options << '--webcvs=http://github.com/yfactorial/roxy/tree/master/'
23
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'roxy'
@@ -0,0 +1,2 @@
1
+ require 'roxy/moxie'
2
+ require 'roxy/proxy'
@@ -0,0 +1,42 @@
1
+ module Roxy
2
+ module Moxie
3
+
4
+ def self.included(within)
5
+ within.class_eval { extend ClassMethods }
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ # Set up this class to proxy on the given name
11
+ def proxy(name, options = {}, &block)
12
+
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]
17
+
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
25
+ end
26
+
27
+ # Thanks to Jerry for this simplification of my original class_eval approach
28
+ # http://ryandaigle.com/articles/2008/11/10/implement-ruby-proxy-objects-with-roxy/comments/8059#comment-8059
29
+ if !original_method or original_method.arity == 0
30
+ define_method name do
31
+ (@proxy_for ||= {})[name] ||= Proxy.new(self, options, nil, &block)
32
+ end
33
+ else
34
+ define_method name do |*args|
35
+ Proxy.new(self, options, args, &block)
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ module Roxy
2
+
3
+ # The very simple proxy class that provides a basic pass-through
4
+ # mechanism between the proxy owner and the proxy target.
5
+ class Proxy
6
+
7
+ alias :proxy_instance_eval :instance_eval
8
+ alias :proxy_extend :extend
9
+
10
+ # Make sure the proxy is as dumb as it can be.
11
+ # Blatanly taken from Jim Wierich's BlankSlate post:
12
+ # http://onestepback.org/index.cgi/Tech/Ruby/BlankSlate.rdoc
13
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^proxy_)/ }
14
+
15
+ def initialize(owner, options, args, &block)
16
+ @owner = owner
17
+ @target = options[:to]
18
+ @args = args
19
+
20
+ # Adorn with user-provided proxy methods
21
+ [options[:extend]].flatten.each { |ext| proxy_extend(ext) } if options[:extend]
22
+ proxy_instance_eval &block if block_given?
23
+ end
24
+
25
+ def proxy_owner; @owner; end
26
+ def proxy_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
35
+ end
36
+
37
+ # Delegate all method calls we don't know about to target object
38
+ def method_missing(sym, *args, &block)
39
+ proxy_target.__send__(sym, *args, &block)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,9 @@
1
+ require File.join(File.dirname(__FILE__), *%w[spec_helper])
2
+
3
+ $:.unshift "#{File.dirname(__FILE__)}/../lib"
4
+ require 'roxy'
5
+
6
+ # Load a test class
7
+ def uses_fixture(fixture_name)
8
+ require File.join(File.dirname(__FILE__), 'fixtures', fixture_name.to_s)
9
+ end
@@ -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
@@ -0,0 +1,51 @@
1
+ class FamilyPerson
2
+
3
+ include Roxy::Moxie
4
+
5
+ attr_accessor :first, :last, :parents, :children
6
+
7
+ # Add ability to ask the parents collection if they are divorced
8
+ # (As defined by not having the same last name). Also print
9
+ # them out as a string taking this into account
10
+ proxy :parents do
11
+
12
+ def divorced?
13
+ proxy_target.size > 1 and proxy_target.collect { |parent| parent.last }.uniq.size > 1
14
+ end
15
+
16
+ def to_s
17
+ if divorced?
18
+ proxy_target.collect { |parent| parent.to_s }.join(' and ')
19
+ else
20
+ "Mr. and Mrs. #{proxy_target[0].last}"
21
+ end
22
+ end
23
+ end
24
+
25
+ # Add ability to ask the children collection for the list of
26
+ # step-children
27
+ proxy :children do
28
+ def step
29
+ proxy_target.select { |child| proxy_owner.last != child.last }
30
+ end
31
+ end
32
+
33
+ def initialize(first, last)
34
+ @first, @last = first, last
35
+ end
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
+
50
+ def to_s; "#{first} #{last}"; end
51
+ end
@@ -0,0 +1,46 @@
1
+ class Person
2
+
3
+ include Roxy::Moxie
4
+
5
+ proxy :children, :to => ['child1', 'child2'] do
6
+ def cost
7
+ 100 * proxy_target.size
8
+ end
9
+ end
10
+
11
+ def parents
12
+ ['parent1', 'parent2']
13
+ end
14
+
15
+ proxy :parents do
16
+ def divorced?
17
+ proxy_target.size < 2
18
+ end
19
+ end
20
+
21
+ module NeighborGeographics
22
+ def nearby?; true; end
23
+ end
24
+
25
+ module NeighborDemographics
26
+ def caucasian?; false; end
27
+ end
28
+
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
46
+ end
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), *%w[abstract_spec])
2
+
3
+ describe "Proxy" do
4
+
5
+ before(:each) do
6
+ @owner = "String owner"
7
+ @target = "String target"
8
+ @lambda_target = lambda { @target }
9
+ end
10
+
11
+ it "should properly evaluate a block-based target" do
12
+ proxy = Roxy::Proxy.new(@owner, {:to => @lambda_target}, nil)
13
+ proxy.should == @target
14
+ end
15
+
16
+ it "should properly adorn the proxy with proxy methods" do
17
+ proxy = Roxy::Proxy.new(@owner, {:to => @target}, nil) do
18
+ def poop; 'poop'; end
19
+ end
20
+ proxy.poop.should == 'poop'
21
+ end
22
+
23
+ it "should make the proxy owner accessible to the target block" do
24
+ proxy = Roxy::Proxy.new(@owner, {:to => proc { |owner| owner }}, nil)
25
+ proxy.should == @owner
26
+ end
27
+
28
+ end
@@ -0,0 +1,57 @@
1
+ require File.join(File.dirname(__FILE__), *%w[abstract_spec])
2
+
3
+ describe "Roxy" do
4
+
5
+ before do
6
+ uses_fixture(:person)
7
+ @person = Person.new
8
+ end
9
+
10
+ it "should return the target when the proxy name is called" do
11
+ @person.children.should == ['child1', 'child2']
12
+ end
13
+
14
+ it "should return the evaluated target if the target is an existing method" do
15
+ @person.parents.should == ['parent1', 'parent2']
16
+ end
17
+
18
+ it "should return the evaluated target if the target is a proc" do
19
+ @person.neighbors.should == ['neighbor1', 'neighbor2']
20
+ end
21
+
22
+ it "should pass method invocations through to the target" do
23
+ @person.children.size.should == 2
24
+ end
25
+
26
+ it "should intercept block-based proxy methods" do
27
+ @person.children.cost.should == 200
28
+ @person.parents.divorced?.should be_false
29
+ end
30
+
31
+ it "should intercept :extend based proxy methods" do
32
+ @person.neighbors.nearby?.should be_true
33
+ @person.neighbors.caucasian?.should be_false
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
57
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: roxy
3
+ version: !ruby/object:Gem::Version
4
+ hash: 21
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 1
10
+ version: 0.2.1
11
+ platform: ruby
12
+ authors:
13
+ - Ryan Daigle
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2008-11-22 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: A Ruby library for quickly creating proxy objects.
23
+ email: ryan@yfactorial.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.textile
30
+ - Rakefile
31
+ - LICENSE
32
+ - CHANGELOG
33
+ files:
34
+ - README.textile
35
+ - Rakefile
36
+ - LICENSE
37
+ - CHANGELOG
38
+ - init.rb
39
+ - lib/roxy.rb
40
+ - lib/roxy/moxie.rb
41
+ - lib/roxy/proxy.rb
42
+ - spec/abstract_spec.rb
43
+ - spec/proxy_spec.rb
44
+ - spec/roxy_spec.rb
45
+ - spec/family_person_spec.rb
46
+ - spec/fixtures/person.rb
47
+ - spec/fixtures/family_person.rb
48
+ has_rdoc: true
49
+ homepage: http://github.com/yfactorial/roxy
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options:
54
+ - --main
55
+ - README.textile
56
+ - --inline-source
57
+ - --charset=UTF-8
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ hash: 3
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ hash: 3
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ requirements: []
79
+
80
+ rubyforge_project:
81
+ rubygems_version: 1.3.7
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: A Ruby library for quickly creating proxy objects.
85
+ test_files: []
86
+