rebound 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +14 -0
- data/Manifest.txt +8 -0
- data/README.textile +74 -0
- data/Rakefile +4 -0
- data/lib/rebound.rb +44 -0
- data/rebound.gemspec +20 -0
- data/spec/rebound_spec.rb +100 -0
- data/spec/spec_helper.rb +3 -0
- metadata +73 -0
data/History.txt
ADDED
@@ -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.
|
data/Manifest.txt
ADDED
data/README.textile
ADDED
@@ -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.
|
data/Rakefile
ADDED
data/lib/rebound.rb
ADDED
@@ -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
|
data/rebound.gemspec
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
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
|