tco_method 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,66 +1,86 @@
1
1
  require "test_helper"
2
2
 
3
- class MixinTest < TCOMethod::TestCase
4
- TestClass = Class.new { extend TCOMethod::Mixin }
5
- TestModule = Module.new { extend TCOMethod::Mixin }
3
+ module TCOMethod
4
+ class MixinTest < TCOMethod::TestCase
5
+ TestClass = Class.new { extend TCOMethod::Mixin }
6
+ TestModule = Module.new { extend TCOMethod::Mixin }
6
7
 
7
8
 
8
- context "Module extensions" do
9
- subject { TestModule }
9
+ context "Module extensions" do
10
+ subject { TestModule }
10
11
 
11
- context "#tco_module_method" do
12
- should "call TCOMethod.reevaluate_method_with_tco with expected arguments" do
13
- method_name = :some_method
14
- args = [subject, method_name, :module]
15
- TCOMethod.expects(:reevaluate_method_with_tco).with(*args)
16
- subject.tco_module_method(method_name)
12
+ context "#tco_module_method" do
13
+ should "call MethodReevaluator#new with expected arguments" do
14
+ method_name = :some_method
15
+ args = [subject, method_name, :module]
16
+ MethodReevaluator.expects(:new).with(*args)
17
+ subject.tco_module_method(method_name)
18
+ end
17
19
  end
18
- end
19
20
 
20
- context "#tco_eval" do
21
- should "call TCOMethod.eval with expected arguments" do
22
- code = "some_code"
23
- TCOMethod.expects(:tco_eval).with(code)
24
- subject.tco_eval(code)
21
+ context "#tco_method" do
22
+ should "call MethodReevaluator#new with expected arguments" do
23
+ method_name = :some_method
24
+ args = [subject, method_name, :instance]
25
+ MethodReevaluator.expects(:new).with(*args)
26
+ subject.tco_method(method_name)
27
+ end
25
28
  end
26
- end
27
29
 
28
- context "#tco_method" do
29
- should "call TCOMethod.reevaluate_method_with_tco with expected arguments" do
30
- method_name = :some_method
31
- args = [subject, method_name, :instance]
32
- TCOMethod.expects(:reevaluate_method_with_tco).with(*args)
33
- subject.tco_method(method_name)
30
+ context "#tco_eval" do
31
+ should "call TCOMethod.tco_eval with expected arguments" do
32
+ code = "some_code"
33
+ TCOMethod.expects(:tco_eval).with(code)
34
+ subject.tco_eval(code)
35
+ end
34
36
  end
35
- end
36
- end
37
37
 
38
- context "Class extensions" do
39
- subject { TestClass }
38
+ context "#with_tco" do
39
+ should "call TCOMethod.with_tco with the given block" do
40
+ # Mocha doesn't offer a good way for sensing passed blocks, so run
41
+ # through the process twice, once with a stub, once without.
42
+
43
+ # Stubbed
44
+ TCOMethod.expects(:with_tco).returns(true)
45
+ assert_equal true, subject.with_tco { }
40
46
 
41
- context "#tco_class_method" do
42
- should "call TCOMethod.reevaluate_method_with_tco with expected arguments" do
43
- method_name = :some_method
44
- args = [subject, method_name, :module]
45
- TCOMethod.expects(:reevaluate_method_with_tco).with(*args)
46
- subject.tco_class_method(method_name)
47
+ # Now unstubbed to make sure the expected block is invoked.
48
+ TCOMethod.unstub(:with_tco)
49
+
50
+ # Must use some sort of global for sensing side effects because the
51
+ # block given to with_tco is called with a different binding than the
52
+ # one used here.
53
+ module ::WithTCOSensor
54
+ def self.call; @called = true; end
55
+ def self.called?; !!@called; end
56
+ end
57
+
58
+ result = subject.with_tco { ::WithTCOSensor.call; ::WithTCOSensor }
59
+ assert_equal true, ::WithTCOSensor.called?
60
+ assert_equal ::WithTCOSensor, result
61
+ end
47
62
  end
48
63
  end
49
64
 
50
- context "#tco_eval" do
51
- should "call TCOMethod.eval with expected arguments" do
52
- code = "some_code"
53
- TCOMethod.expects(:tco_eval).with(code)
54
- subject.tco_eval(code)
65
+ context "Class extensions" do
66
+ subject { TestClass }
67
+
68
+ context "#tco_class_method" do
69
+ should "call MethodReevaluator#new with expected arguments" do
70
+ method_name = :some_method
71
+ args = [subject, method_name, :module]
72
+ MethodReevaluator.expects(:new).with(*args)
73
+ subject.tco_class_method(method_name)
74
+ end
55
75
  end
56
- end
57
76
 
58
- context "#tco_method" do
59
- should "call TCOMethod.reevaluate_method_with_tco with expected arguments" do
60
- method_name = :some_method
61
- args = [subject, method_name, :instance]
62
- TCOMethod.expects(:reevaluate_method_with_tco).with(*args)
63
- subject.tco_method(method_name)
77
+ context "#tco_method" do
78
+ should "call MethodReevaluator#new with expected arguments" do
79
+ method_name = :some_method
80
+ args = [subject, method_name, :instance]
81
+ MethodReevaluator.expects(:new).with(*args)
82
+ subject.tco_method(method_name)
83
+ end
64
84
  end
65
85
  end
66
86
  end
@@ -1,182 +1,68 @@
1
1
  require "test_helper"
2
2
 
3
- class TCOMethodTest < TCOMethod::TestCase
4
- include TCOMethod::TestHelpers::Assertions
3
+ module TCOMethod
4
+ class TCOMethodTest < TCOMethod::TestCase
5
+ include TCOMethod::TestHelpers::Assertions
5
6
 
6
- Subject = TCOMethod
7
+ Subject = TCOMethod
7
8
 
8
- test_subject_builder = proc do
9
- extend TCOMethod::Mixin
9
+ # Grab source before it's recompiled for use later
10
+ InstanceFibYielderSource = TestClass.instance_method(:instance_fib_yielder).source
10
11
 
11
- class << self
12
- define_method(:singleton_block_method) { }
13
- end
14
-
15
- # Equivalent to the below, but provides a target for verifying that
16
- # tco_module_method works on Classes and tco_class_method works on Modules.
17
- def self.module_fib_yielder(index, back_one = 1, back_two = 0, &block)
18
- yield back_two if index > 0
19
- index < 1 ? back_two : module_fib_yielder(index - 1, back_one + back_two, back_one, &block)
20
- end
21
-
22
- # Equivalent to the above, but provides a target for verifying that
23
- # tco_module_method works on Classes and tco_class_method works on Modules.
24
- def self.class_fib_yielder(index, back_one = 1, back_two = 0, &block)
25
- yield back_two if index > 0
26
- index < 1 ? back_two : class_fib_yielder(index - 1, back_one + back_two, back_one, &block)
27
- end
28
-
29
- define_method(:instance_block_method) { }
30
-
31
- # Equivalent to the above, but provides a target for verifying that
32
- # instance methods work for both Classes and Modules
33
- def instance_fib_yielder(index, back_one = 1, back_two = 0, &block)
34
- yield back_two if index > 0
35
- index < 1 ? back_two : instance_fib_yielder(index - 1, back_one + back_two, back_one, &block)
36
- end
37
- end
38
-
39
- TestModule = Module.new(&test_subject_builder)
40
- TestClass = Class.new(&test_subject_builder)
41
-
42
- # Grab source before it's recompiled for use later
43
- InstanceFibYielderSource = TestClass.instance_method(:instance_fib_yielder).source
44
-
45
- subject { Subject }
46
-
47
- context Subject.name do
48
- should "be defined" do
49
- assert defined?(subject), "Expected #{subject.name} to be defined!"
50
- end
51
- end
12
+ subject { Subject }
52
13
 
53
- context "::tco_eval" do
54
- should "raise ArgumentError unless code is a String" do
55
- bad_code = [
56
- :bad_code,
57
- 5,
58
- proc { puts "hello" },
59
- ]
60
- bad_code.each do |non_code|
61
- assert_raises(ArgumentError) do
62
- subject.tco_eval(non_code)
63
- end
14
+ context Subject.name do
15
+ should "be defined" do
16
+ assert defined?(subject), "Expected #{subject.name} to be defined!"
64
17
  end
65
18
  end
66
19
 
67
- should "compile the given code with tail call optimization" do
68
- EvalDummy = dummy_class = Class.new
69
- subject.tco_eval(<<-CODE)
70
- class #{dummy_class.name}
71
- #{InstanceFibYielderSource}
72
- end
73
- CODE
74
-
75
- fib_yielder = dummy_class.new.method(:instance_fib_yielder)
76
- assert tail_call_optimized?(fib_yielder, 5)
77
- end
78
- end
79
-
80
- context "::reevaluate_method_with_tco" do
81
- subject { Subject.method(:reevaluate_method_with_tco) }
82
-
83
- [TestClass, TestModule].each do |method_owner|
84
- method_owner_class = method_owner.class.name.downcase.to_sym
85
-
86
- context "validation" do
87
- should "raise ArgumentError unless receiver given" do
88
- assert_raises(ArgumentError) do
89
- subject.call(nil, :nil?, :instance)
90
- end
91
- end
92
-
93
- should "raise ArgumentError unless method name given" do
94
- assert_raises(ArgumentError) do
95
- subject.call(method_owner, nil, :instance)
96
- end
97
- end
98
-
99
- should "raise ArgumentError unless method owner given" do
20
+ context "::tco_eval" do
21
+ should "raise ArgumentError unless code is a String" do
22
+ bad_code = [
23
+ :bad_code,
24
+ 5,
25
+ proc { puts "hello" },
26
+ ]
27
+ bad_code.each do |non_code|
100
28
  assert_raises(ArgumentError) do
101
- subject.call(method_owner, :class_factorial, nil)
102
- end
103
- end
104
-
105
- should "raise TypeError for block methods" do
106
- assert_raises(TypeError) do
107
- subject.call(method_owner, :singleton_block_method, :class)
108
- end
109
- assert_raises(TypeError) do
110
- subject.call(method_owner, :instance_block_method, :instance)
29
+ subject.tco_eval(non_code)
111
30
  end
112
31
  end
113
32
  end
114
33
 
115
- context "#{method_owner_class} receiver" do
116
- context "with module method" do
117
- should "raise NameError if no #{method_owner_class} method with given name defined" do
118
- assert_raises(NameError) do
119
- subject.call(method_owner, :marmalade, method_owner_class)
120
- end
34
+ should "compile the given code with tail call optimization" do
35
+ EvalDummy = dummy_class = Class.new
36
+ subject.tco_eval(<<-CODE)
37
+ class #{dummy_class.name}
38
+ #{InstanceFibYielderSource}
121
39
  end
40
+ CODE
122
41
 
123
- should "re-compile the given method with tail call optimization" do
124
- fib_yielder = method_owner.method(:module_fib_yielder)
125
- refute tail_call_optimized?(fib_yielder, 5)
126
-
127
- subject.call(method_owner, :module_fib_yielder, :module)
128
- tco_fib_yielder = method_owner.method(:module_fib_yielder)
129
- assert tail_call_optimized?(tco_fib_yielder, 5)
42
+ fib_yielder = dummy_class.new.method(:instance_fib_yielder)
43
+ assert tail_call_optimized?(fib_yielder, 5)
44
+ end
45
+ end
130
46
 
131
- assert_equal fib_yielder.source_location, tco_fib_yielder.source_location
132
- end
133
- end
47
+ context "::with_tco" do
48
+ should "raise ArgumentError if a block is not given" do
49
+ exception = assert_raises(ArgumentError) { subject.with_tco }
50
+ assert_match(/block required/i, exception.message)
51
+ end
134
52
 
135
- context "with class method" do
136
- should "raise NameError if no class method with given name defined" do
137
- assert_raises(NameError) do
138
- subject.call(method_owner, :marmalade, :class)
53
+ should "compile the given code with tail call optimization" do
54
+ subject.with_tco do
55
+ class WithTCODummy
56
+ def countdown(count, &block)
57
+ yield count
58
+ count.zero? ? 0 : countdown(count - 1, &block)
139
59
  end
140
60
  end
141
-
142
- should "re-compile the given method with tail call optimization" do
143
- fib_yielder = method_owner.method(:class_fib_yielder)
144
- refute tail_call_optimized?(fib_yielder, 5)
145
-
146
- subject.call(method_owner, :class_fib_yielder, :module)
147
- tco_fib_yielder = method_owner.method(:class_fib_yielder)
148
- assert tail_call_optimized?(tco_fib_yielder, 5)
149
-
150
- assert_equal fib_yielder.source_location, tco_fib_yielder.source_location
151
- end
152
61
  end
153
62
 
154
- context "with instance method" do
155
- should "raise NameError if no instance method with given name defined" do
156
- assert_raises(NameError) do
157
- subject.call(method_owner, :marmalade, :instance)
158
- end
159
- end
160
-
161
- should "re-compile the given method with tail call optimization" do
162
- instance_class = instance_class_for_receiver(method_owner)
163
-
164
- fib_yielder = instance_class.new.method(:instance_fib_yielder)
165
- refute tail_call_optimized?(fib_yielder, 5)
166
-
167
- subject.call(method_owner, :instance_fib_yielder, :instance)
168
- tco_fib_yielder = instance_class.new.method(:instance_fib_yielder)
169
- assert tail_call_optimized?(tco_fib_yielder, 5)
170
-
171
- assert_equal fib_yielder.source_location, tco_fib_yielder.source_location
172
- end
173
- end
63
+ meth = WithTCODummy.new.method(:countdown)
64
+ assert tail_call_optimized?(meth, 5)
174
65
  end
175
66
  end
176
67
  end
177
-
178
- def instance_class_for_receiver(receiver)
179
- return receiver if receiver.is_a?(Class)
180
- Class.new { include receiver }
181
- end
182
68
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tco_method
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Danny Guinther
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-12 00:00:00.000000000 Z
11
+ date: 2015-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: method_source
@@ -67,13 +67,21 @@ files:
67
67
  - README.md
68
68
  - Rakefile
69
69
  - lib/tco_method.rb
70
+ - lib/tco_method/ambiguous_source_error.rb
71
+ - lib/tco_method/block_extractor.rb
72
+ - lib/tco_method/block_with_tco.rb
70
73
  - lib/tco_method/method_info.rb
74
+ - lib/tco_method/method_reevaluator.rb
71
75
  - lib/tco_method/mixin.rb
72
76
  - lib/tco_method/version.rb
73
77
  - tco_method.gemspec
74
78
  - test/test_helper.rb
75
79
  - test/test_helpers/assertions.rb
80
+ - test/test_helpers/fibbers.rb
81
+ - test/unit/block_extractor_test.rb
82
+ - test/unit/block_with_tco_test.rb
76
83
  - test/unit/method_info_test.rb
84
+ - test/unit/method_reevaluator_test.rb
77
85
  - test/unit/mixin_test.rb
78
86
  - test/unit/tco_method_test.rb
79
87
  homepage: https://github.com/tdg5/tco_method
@@ -96,14 +104,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
104
  version: '0'
97
105
  requirements: []
98
106
  rubyforge_project:
99
- rubygems_version: 2.4.5
107
+ rubygems_version: 2.4.8
100
108
  signing_key:
101
109
  specification_version: 4
102
110
  summary: Simplifies compiling code with tail call optimization in MRI Ruby.
103
111
  test_files:
104
112
  - test/test_helper.rb
105
113
  - test/test_helpers/assertions.rb
114
+ - test/test_helpers/fibbers.rb
115
+ - test/unit/block_extractor_test.rb
116
+ - test/unit/block_with_tco_test.rb
106
117
  - test/unit/method_info_test.rb
118
+ - test/unit/method_reevaluator_test.rb
107
119
  - test/unit/mixin_test.rb
108
120
  - test/unit/tco_method_test.rb
109
121
  has_rdoc: