tco_method 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: