rebound 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ === 0.0.2 / 2008-07-21
2
+
3
+ * Added JavaScript-esque ability to show method definition
4
+ for unbound methods like so:
5
+
6
+ m = Person.instance_method(:hello)
7
+ puts m.to_s(:ruby)
8
+
9
+ * Using new to_s abilities to enable the binding of unbound
10
+ methods that take block arguments.
11
+
12
+ === 0.0.1 / 2008-07-20
13
+
14
+ * Had an idea. Made it a gem.
@@ -0,0 +1,8 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.textile
4
+ Rakefile
5
+ rebound.gemspec
6
+ lib/rebound.rb
7
+ spec/rebound_spec.rb
8
+ spec/spec_helper.rb
@@ -0,0 +1,74 @@
1
+ h1. rebound
2
+
3
+ By default, instances of UnboundMethod can only be bound to objects that
4
+ are a kind_of? the method's original class. Pretty lame.
5
+
6
+ rebound allows unbound methods (instances of UnboundMethod class) to be
7
+ bound to objects of any class. It uses the alias_method_chain pattern to
8
+ accomplish this, meaning you also get a bind_without_indifference method
9
+ that retains the original behavior.
10
+
11
+ "http://github.com/nakajima/rebound":http://github.com/nakajima/rebound
12
+
13
+ h3. USAGE
14
+
15
+ <pre>
16
+ class Guy
17
+ def greet
18
+ puts "Hello!"
19
+ end
20
+ end
21
+
22
+ class Girl
23
+ # No #greet method here
24
+ end
25
+
26
+ girl = Girl.new
27
+
28
+ m = Guy.instance_method(:greet)
29
+
30
+ m.bind(girl)
31
+
32
+ girl.greet # => Hello!
33
+ </pre>
34
+
35
+ h3. TODO:
36
+
37
+ * Could probably use some more specs, since I'm pretty sure I didn't think
38
+ of everything.
39
+
40
+ h3. REQUIREMENTS:
41
+
42
+ * ParseTree
43
+ * Ruby2Ruby
44
+
45
+ h3. Credit
46
+
47
+ I'm just standing on "Ryan Davis'":http://www.zenspider.com shoulders with
48
+ this project. He did all the hard work. I just did something cool with it.
49
+ (Which isn't to say that what he did wasn't cool, since what he did is *way*
50
+ cooler than what you see here.)
51
+
52
+ Also, "Magnus Holm":http://judofyr.net contributed a patch that made things
53
+ cleaner behind the scenes.
54
+
55
+ Copyright (c) 2008 Pat Nakajima
56
+
57
+ Permission is hereby granted, free of charge, to any person obtaining
58
+ a copy of this software and associated documentation files (the
59
+ 'Software'), to deal in the Software without restriction, including
60
+ without limitation the rights to use, copy, modify, merge, publish,
61
+ distribute, sublicense, and/or sell copies of the Software, and to
62
+ permit persons to whom the Software is furnished to do so, subject to
63
+ the following conditions:
64
+
65
+ The above copyright notice and this permission notice shall be
66
+ included in all copies or substantial portions of the Software.
67
+
68
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
69
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
70
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
71
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
72
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
73
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
74
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,4 @@
1
+ desc "Run specs"
2
+ task :default do
3
+ puts `spec spec/ --colour`
4
+ end
@@ -0,0 +1,44 @@
1
+ module Nakajima
2
+ module Rebound
3
+ VERSION = '0.0.2'
4
+ end
5
+ end
6
+
7
+ %w(rubygems ruby2ruby).each { |lib| require lib } unless defined?(Ruby2Ruby)
8
+
9
+ class UnboundMethod
10
+ # eval's and memoizes the output of Ruby2Ruby's #to_ruby method
11
+ def to_proc
12
+ @to_proc ||= eval(to_ruby)
13
+ end
14
+
15
+ alias_method :to_s_without_ruby, :to_s
16
+ # This is sort of ugly, but it does allow us to bind methods that
17
+ # take block arguments (you can't use block arguments with blocks).
18
+ def to_s_with_ruby(opt=nil)
19
+ (opt != :ruby) ? to_s_without_ruby : begin
20
+ @to_s ||= begin
21
+ res = to_ruby
22
+ res.gsub!(/\Aproc \{ /, "def #{name}") # Replace proc definition
23
+ res.gsub!(/\|([^\|]*)\|\n/, "(#{'\1'})\n") # Use method param declaration
24
+ res.gsub!(/\}\z/, 'end') # Replace proc end brace
25
+ res
26
+ end
27
+ end
28
+ end
29
+ alias_method :to_s, :to_s_with_ruby
30
+
31
+ # Simple string name. Taken from Pat Maddox's with_context
32
+ def name
33
+ @name ||= to_s.split("#").last.delete(">")
34
+ end
35
+
36
+ alias_method :bind_without_indifference, :bind
37
+ # Allows an unbound method to be bound to any object, instead
38
+ # of only those of the same class. Goes with original #bind method
39
+ # first, and if that fails, meta_def's using #to_proc
40
+ def bind_with_indifference(obj)
41
+ bind_without_indifference(obj) rescue class << obj; self end.class_eval(to_s(:ruby))
42
+ end
43
+ alias_method :bind, :bind_with_indifference
44
+ end
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{rebound}
3
+ s.version = "0.0.2"
4
+
5
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["Pat Nakajima"]
7
+ s.date = %q{2008-07-20}
8
+ s.description = %q{By default, instances of UnboundMethod can only be bound to objects that are a kind_of? the method's original class. Pretty lame. rebound allows unbound methods (instances of UnboundMethod class) to be bound to objects of any class. It uses the alias_method_chain pattern to accomplish this, meaning you also get a bind_without_indifference method that retains the original behavior.}
9
+ s.email = ["patnakajima@gmail.com"]
10
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.textile"]
11
+ s.files = ["History.txt", "Manifest.txt", "README.textile", "Rakefile", "lib/rebound.rb", "spec/rebound_spec.rb", "spec/spec_helper.rb", "rebound.gemspec"]
12
+ s.has_rdoc = true
13
+ s.homepage = %q{http://github.com/nakajima/rebound}
14
+ s.rdoc_options = ["--main", "README.textile"]
15
+ s.require_paths = ["lib"]
16
+ s.rubygems_version = %q{1.2.0}
17
+ s.summary = %q{By default, instances of UnboundMethod can only be bound to objects that are a kind_of? the method's original class. This fixes that.}
18
+ s.test_files = ["spec/rebound_spec.rb"]
19
+ s.add_dependency('ruby2ruby', [">= 1.1.9"])
20
+ end
@@ -0,0 +1,100 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe UnboundMethod do
4
+ before(:each) do
5
+ @a = Class.new { def foo; :bar end }
6
+ @b = Class.new { def called?; @called end }
7
+ @mod = Module.new { def call; @called = true; end }
8
+ @object_a = @a.new
9
+ @object_b = @b.new
10
+ end
11
+
12
+ describe "#name" do
13
+ it "should return name" do
14
+ m = @a.instance_method(:foo)
15
+ m.name.should == 'foo'
16
+ end
17
+ end
18
+
19
+ describe "#to_s" do
20
+ it "should provide source" do
21
+ m = @a.instance_method(:foo)
22
+ m.to_s(:ruby).should == "def foo()\n :bar\nend"
23
+ end
24
+
25
+ it "should be memoized" do
26
+ m = @a.instance_method(:foo)
27
+ m.should_receive(:to_ruby).once.and_return(proc { :bar }.to_ruby)
28
+ 2.times { m.to_s(:ruby) }
29
+ end
30
+ end
31
+
32
+ describe "#bind" do
33
+ it "should bind method from Class.instance_method to object of different class" do
34
+ m = @a.instance_method(:foo)
35
+ m.bind(@object_b)
36
+ @object_b.foo.should == :bar
37
+ end
38
+
39
+ it "should bind singleton method to object of different class" do
40
+ class << @object_a; def greet; :hello end end
41
+ m = @object_a.method(:greet).unbind
42
+ m.bind(@object_b)
43
+ @object_b.greet.should == :hello
44
+ end
45
+
46
+ it "should bind methods that take an argument" do
47
+ class << @object_a; def greet(name); "hello #{name}" end end
48
+ m = @object_a.method(:greet).unbind
49
+ m.bind(@object_b)
50
+ @object_b.greet('pat').should == "hello pat"
51
+ end
52
+
53
+ it "should bind methods that take splat of arguments" do
54
+ class << @object_a
55
+ def add_these(container, *args)
56
+ args.each { |a| container << a }
57
+ end
58
+ end
59
+ m = @object_a.method(:add_these).unbind
60
+ m.bind(@object_b)
61
+ collector = []
62
+ @object_b.add_these(collector, 'pat', 'tim', 'drew')
63
+ collector.should == ['pat', 'tim', 'drew']
64
+ end
65
+
66
+ it "should bind methods that take block" do
67
+ class << @object_a
68
+ def append(&block)
69
+ res = [:original]
70
+ res << yield
71
+ res
72
+ end
73
+ end
74
+
75
+ m = @object_a.method(:append).unbind
76
+ m.bind(@object_b)
77
+ @object_b.append { :addition }.should == [:original, :addition]
78
+ end
79
+
80
+ it "should bind module instance method" do
81
+ m = @mod.instance_method(:call)
82
+ m.bind(@object_b)
83
+ @object_b.call
84
+ @object_b.should be_called
85
+ end
86
+ end
87
+
88
+ describe "#to_proc" do
89
+ it "should return proc version" do
90
+ m = @a.instance_method(:foo)
91
+ m.to_proc.call.should == :bar
92
+ end
93
+
94
+ it "should be memoized" do
95
+ m = @a.instance_method(:foo)
96
+ m.should_receive(:to_ruby).once.and_return(proc { :bar }.to_ruby)
97
+ 2.times { m.to_proc }
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,3 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'rebound')
2
+ require 'rubygems'
3
+ require 'spec'
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rebound
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Pat Nakajima
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-07-20 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ruby2ruby
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.9
24
+ version:
25
+ description: By default, instances of UnboundMethod can only be bound to objects that are a kind_of? the method's original class. Pretty lame. rebound allows unbound methods (instances of UnboundMethod class) to be bound to objects of any class. It uses the alias_method_chain pattern to accomplish this, meaning you also get a bind_without_indifference method that retains the original behavior.
26
+ email:
27
+ - patnakajima@gmail.com
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - History.txt
34
+ - Manifest.txt
35
+ - README.textile
36
+ files:
37
+ - History.txt
38
+ - Manifest.txt
39
+ - README.textile
40
+ - Rakefile
41
+ - lib/rebound.rb
42
+ - spec/rebound_spec.rb
43
+ - spec/spec_helper.rb
44
+ - rebound.gemspec
45
+ has_rdoc: true
46
+ homepage: http://github.com/nakajima/rebound
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --main
50
+ - README.textile
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.0
69
+ signing_key:
70
+ specification_version: 2
71
+ summary: By default, instances of UnboundMethod can only be bound to objects that are a kind_of? the method's original class. This fixes that.
72
+ test_files:
73
+ - spec/rebound_spec.rb