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.
@@ -1,3 +1,3 @@
1
1
  class BindableBlock
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -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
- # match args to arity, since instance_method has lambda properties
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
@@ -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.2
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-03 00:00:00.000000000 Z
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.