bindable_block 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/bindable_block/version.rb +1 -1
- data/lib/bindable_block.rb +59 -15
- data/spec/bindable_block_spec.rb +61 -13
- metadata +2 -2
data/lib/bindable_block.rb
CHANGED
@@ -1,6 +1,54 @@
|
|
1
1
|
require "bindable_block/version"
|
2
2
|
|
3
3
|
class BindableBlock
|
4
|
+
|
5
|
+
# match args to arity, since instance_method has lambda properties
|
6
|
+
class ArgAligner
|
7
|
+
def self.align(args, instance_method)
|
8
|
+
new(args, instance_method).call
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_reader :args, :parameters, :result
|
14
|
+
|
15
|
+
def initialize(args, instance_method)
|
16
|
+
@result, @args, @parameters = [], args, instance_method.parameters.map(&:first)
|
17
|
+
track_if_has_rest
|
18
|
+
parameters.delete :rest
|
19
|
+
remove_block
|
20
|
+
take num_required
|
21
|
+
parameters.delete :req
|
22
|
+
take 1 while parameters.shift && args.any?
|
23
|
+
take args.size if has_rest?
|
24
|
+
end
|
25
|
+
|
26
|
+
def num_required
|
27
|
+
parameters.count { |param| param == :req }
|
28
|
+
end
|
29
|
+
|
30
|
+
def take(n)
|
31
|
+
n.times { result << args.shift }
|
32
|
+
end
|
33
|
+
|
34
|
+
def remove_block
|
35
|
+
parameters.pop if parameters.last == :block
|
36
|
+
end
|
37
|
+
|
38
|
+
def track_if_has_rest
|
39
|
+
@has_splat = parameters.any? { |param| param == :rest }
|
40
|
+
end
|
41
|
+
|
42
|
+
def has_rest?
|
43
|
+
@has_splat
|
44
|
+
end
|
45
|
+
|
46
|
+
def call
|
47
|
+
result
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
4
52
|
def initialize(klass, &block)
|
5
53
|
@original_block = block
|
6
54
|
|
@@ -9,25 +57,11 @@ class BindableBlock
|
|
9
57
|
klass.__send__ :remove_method, method_name
|
10
58
|
end
|
11
59
|
|
12
|
-
def method_name
|
13
|
-
@method_name ||= "bindable_block_#{Time.now.to_i}_#{$$}_#{rand 1000000}"
|
14
|
-
end
|
15
|
-
|
16
|
-
def arg_size
|
17
|
-
instance_method.arity
|
18
|
-
end
|
19
|
-
|
20
60
|
attr_reader :instance_method, :original_block
|
21
61
|
|
22
62
|
def bind(target)
|
23
63
|
Proc.new do |*args, &block|
|
24
|
-
|
25
|
-
if args.size >= arg_size
|
26
|
-
args = args.take arg_size
|
27
|
-
else
|
28
|
-
args[arg_size-1] = nil
|
29
|
-
end
|
30
|
-
instance_method.bind(target).call(*args, &block)
|
64
|
+
instance_method.bind(target).call(*align(args), &block)
|
31
65
|
end
|
32
66
|
end
|
33
67
|
|
@@ -38,4 +72,14 @@ class BindableBlock
|
|
38
72
|
def to_proc
|
39
73
|
method(:call).to_proc
|
40
74
|
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def align(args)
|
79
|
+
ArgAligner.align args, instance_method
|
80
|
+
end
|
81
|
+
|
82
|
+
def method_name
|
83
|
+
@method_name ||= "bindable_block_#{Time.now.to_i}_#{$$}_#{rand 1000000}"
|
84
|
+
end
|
41
85
|
end
|
data/spec/bindable_block_spec.rb
CHANGED
@@ -23,19 +23,6 @@ describe BindableBlock do
|
|
23
23
|
block.call(3){4}.should == 10
|
24
24
|
end
|
25
25
|
|
26
|
-
it "doesn't care about arity" do
|
27
|
-
block = BindableBlock.new(klass) { |a| [a] }.bind(instance)
|
28
|
-
block.call.should == [nil]
|
29
|
-
block.call(1).should == [1]
|
30
|
-
block.call(1, 2).should == [1]
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'can take a block' do
|
34
|
-
block = BindableBlock.new(klass) { |a, &b| [a, (b&&b.call)] }.bind(instance)
|
35
|
-
block.call(1).should == [1, nil]
|
36
|
-
block.call(1){2}.should == [1, 2]
|
37
|
-
end
|
38
|
-
|
39
26
|
it 'can be passed to methods and shit' do
|
40
27
|
doubler = lambda { |&block| block.call + block.call }
|
41
28
|
doubler.call(&BindableBlock.new(klass) { 12 }).should == 24
|
@@ -50,4 +37,65 @@ describe BindableBlock do
|
|
50
37
|
# proc.define_singleton_method(:bind) { |target| bindable_block.bind target }
|
51
38
|
# proc
|
52
39
|
end
|
40
|
+
|
41
|
+
describe 'arguments' do
|
42
|
+
it 'can take a block' do
|
43
|
+
block = BindableBlock.new(klass) { |a, &b| [a, (b&&b.call)] }.bind(instance)
|
44
|
+
block.call(1).should == [1, nil]
|
45
|
+
block.call(1){2}.should == [1, 2]
|
46
|
+
end
|
47
|
+
|
48
|
+
specify "when given ordinal arguments at the start, it doesn't care about arity" do
|
49
|
+
block = BindableBlock.new(klass) { |a| [a] }.bind(instance)
|
50
|
+
block.call.should == [nil]
|
51
|
+
block.call(1).should == [1]
|
52
|
+
block.call(1, 2).should == [1]
|
53
|
+
end
|
54
|
+
|
55
|
+
specify 'when given optional args, it matches them up correctly' do
|
56
|
+
block = BindableBlock.new(klass) { |a, b=1, c=2| [a, b, c] }.bind(instance)
|
57
|
+
block.call.should == [nil, 1, 2]
|
58
|
+
block.call(:a).should == [:a, 1, 2]
|
59
|
+
block.call(:a, :b).should == [:a, :b, 2]
|
60
|
+
block.call(:a, :b, :c).should == [:a, :b, :c]
|
61
|
+
block.call(:a, :b, :c, :d).should == [:a, :b, :c]
|
62
|
+
end
|
63
|
+
|
64
|
+
specify 'splat acts as a catch all' do
|
65
|
+
block = BindableBlock.new(klass) { |a, *rest| [a, rest] }.bind(instance)
|
66
|
+
block.call.should == [nil, []]
|
67
|
+
block.call(1).should == [1, []]
|
68
|
+
block.call(1, 2).should == [1, [2]]
|
69
|
+
block.call(1, 2, 3).should == [1, [2, 3]]
|
70
|
+
end
|
71
|
+
|
72
|
+
specify "when given ordinal arguments at the end, it doesn't care about arity" do
|
73
|
+
block = BindableBlock.new(klass) { |*a, b, &c| [a, b] }.bind(instance)
|
74
|
+
block.call.should == [[], nil]
|
75
|
+
block.call(1).should == [[], 1]
|
76
|
+
block.call(1,2).should == [[1], 2]
|
77
|
+
block.call(1,2,3).should == [[1, 2], 3]
|
78
|
+
|
79
|
+
block = BindableBlock.new(klass) { |a=:a, b, c| [a, b, c] }.bind(instance)
|
80
|
+
block.call.should == [:a, nil, nil]
|
81
|
+
block.call(1).should == [:a, 1, nil]
|
82
|
+
block.call(1, 2).should == [:a, 1, 2]
|
83
|
+
block.call(1, 2, 3).should == [1, 2, 3]
|
84
|
+
block.call(1, 2, 3, 4).should == [1, 2, 3]
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
specify "when given complex arguments, it matches that shit up right" do
|
89
|
+
proc = Proc.new { |a, b, c=1, d=2, *e, f| [a,b,c,d,e,f] }
|
90
|
+
block = BindableBlock.new(klass, &proc).bind(instance)
|
91
|
+
block.call.should == proc.call
|
92
|
+
block.call(:a).should == proc.call(:a)
|
93
|
+
block.call(:a,:b).should == proc.call(:a,:b)
|
94
|
+
block.call(:a,:b,:c).should == proc.call(:a,:b,:c)
|
95
|
+
block.call(:a,:b,:c,:d).should == proc.call(:a,:b,:c,:d)
|
96
|
+
block.call(:a,:b,:c,:d,:e).should == proc.call(:a,:b,:c,:d,:e)
|
97
|
+
block.call(:a,:b,:c,:d,:e,:f).should == proc.call(:a,:b,:c,:d,:e,:f)
|
98
|
+
block.call(:a,:b,:c,:d,:e,:f,:g).should == proc.call(:a,:b,:c,:d,:e,:f,:g)
|
99
|
+
end
|
100
|
+
end
|
53
101
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bindable_block
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-06-
|
12
|
+
date: 2012-06-05 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: instance_exec can't pass block arguments through. Use a bindable block
|
15
15
|
instead.
|