scribble 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +455 -0
- data/Rakefile +2 -0
- data/lib/scribble.rb +44 -0
- data/lib/scribble/block.rb +25 -0
- data/lib/scribble/converter.rb +10 -0
- data/lib/scribble/errors.rb +24 -0
- data/lib/scribble/method.rb +91 -0
- data/lib/scribble/methods/if.rb +26 -0
- data/lib/scribble/methods/layout.rb +25 -0
- data/lib/scribble/methods/partial.rb +14 -0
- data/lib/scribble/methods/times.rb +11 -0
- data/lib/scribble/nodes/call.rb +55 -0
- data/lib/scribble/nodes/ending.rb +6 -0
- data/lib/scribble/nodes/node.rb +24 -0
- data/lib/scribble/nodes/value.rb +16 -0
- data/lib/scribble/objects/boolean.rb +33 -0
- data/lib/scribble/objects/fixnum.rb +53 -0
- data/lib/scribble/objects/nil.rb +21 -0
- data/lib/scribble/objects/string.rb +62 -0
- data/lib/scribble/parsing/nester.rb +49 -0
- data/lib/scribble/parsing/parser.rb +132 -0
- data/lib/scribble/parsing/reporter.rb +71 -0
- data/lib/scribble/parsing/transform.rb +87 -0
- data/lib/scribble/partial.rb +41 -0
- data/lib/scribble/registry.rb +95 -0
- data/lib/scribble/support/context.rb +98 -0
- data/lib/scribble/support/matcher.rb +74 -0
- data/lib/scribble/support/unmatched.rb +70 -0
- data/lib/scribble/support/utilities.rb +49 -0
- data/lib/scribble/template.rb +61 -0
- data/lib/scribble/version.rb +3 -0
- data/scribble.gemspec +22 -0
- data/test/all.rb +23 -0
- data/test/errors_test.rb +94 -0
- data/test/methods/if_test.rb +49 -0
- data/test/methods/layout_test.rb +71 -0
- data/test/methods/partial_test.rb +85 -0
- data/test/methods/times_test.rb +10 -0
- data/test/objects/boolean_test.rb +162 -0
- data/test/objects/fixnum_test.rb +236 -0
- data/test/objects/nil_test.rb +83 -0
- data/test/objects/string_test.rb +268 -0
- data/test/parsing/parsing_test.rb +234 -0
- data/test/registry_test.rb +264 -0
- data/test/template_test.rb +51 -0
- data/test/test_helper.rb +65 -0
- metadata +127 -0
@@ -0,0 +1,234 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
describe Scribble do
|
4
|
+
it 'keeps text between tags' do
|
5
|
+
assert_scribble_parse 'Hi! Nice template!', "'Hi! Nice template!'"
|
6
|
+
assert_scribble_parse 'Hi!{{ }} Nice template!', "'Hi!', ' Nice template!'"
|
7
|
+
assert_scribble_parse 'H{{ }}i! Nice template{{}}!', "'H', 'i! Nice template', '!'"
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'parses literal values' do
|
11
|
+
assert_scribble_parse '{{ 1 }}', "1"
|
12
|
+
assert_scribble_parse '{{ 100 }}', "100"
|
13
|
+
assert_scribble_parse "{{ 'foo' }}", "'foo'"
|
14
|
+
assert_scribble_parse "{{ 'bar' }}", "'bar'"
|
15
|
+
assert_scribble_parse '{{ true }}', "true"
|
16
|
+
assert_scribble_parse '{{ false }}', "false"
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'parses binary and unary operators' do
|
20
|
+
assert_scribble_parse '{{ 1 | 2 }}', '1.or(2)'
|
21
|
+
|
22
|
+
assert_scribble_parse '{{ 1 & 2 }}', '1.and(2)'
|
23
|
+
|
24
|
+
assert_scribble_parse '{{ 1 = 2 }}', '1.equals(2)'
|
25
|
+
assert_scribble_parse '{{ 1 != 2 }}', '1.differs(2)'
|
26
|
+
|
27
|
+
assert_scribble_parse '{{ 1 > 2 }}', '1.greater(2)'
|
28
|
+
assert_scribble_parse '{{ 1 < 2 }}', '1.less(2)'
|
29
|
+
assert_scribble_parse '{{ 1 >= 2 }}', '1.greater_or_equal(2)'
|
30
|
+
assert_scribble_parse '{{ 1 <= 2 }}', '1.less_or_equal(2)'
|
31
|
+
|
32
|
+
assert_scribble_parse '{{ 1 + 2 }}', '1.add(2)'
|
33
|
+
assert_scribble_parse '{{ 1 - 2 }}', '1.subtract(2)'
|
34
|
+
|
35
|
+
assert_scribble_parse '{{ 1 * 2 }}', '1.multiply(2)'
|
36
|
+
assert_scribble_parse '{{ 1 / 2 }}', '1.divide(2)'
|
37
|
+
assert_scribble_parse '{{ 1 % 2 }}', '1.remainder(2)'
|
38
|
+
|
39
|
+
assert_scribble_parse '{{ -1 }}', '1.negative()'
|
40
|
+
assert_scribble_parse '{{ !1 }}', '1.not()'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'parses chains of operators with equal precedence' do
|
44
|
+
assert_scribble_parse '{{ 1 | 2 | 3 }}', '1.or(2).or(3)'
|
45
|
+
|
46
|
+
assert_scribble_parse '{{ 1 & 2 & 3 }}', '1.and(2).and(3)'
|
47
|
+
|
48
|
+
assert_scribble_parse '{{ 1 = 2 != 3 }}', '1.equals(2).differs(3)'
|
49
|
+
assert_scribble_parse '{{ 1 != 2 = 3 }}', '1.differs(2).equals(3)'
|
50
|
+
|
51
|
+
assert_scribble_parse '{{ 1 > 2 <= 3 }}', '1.greater(2).less_or_equal(3)'
|
52
|
+
assert_scribble_parse '{{ 1 < 2 > 3 }}', '1.less(2).greater(3)'
|
53
|
+
assert_scribble_parse '{{ 1 >= 2 < 3 }}', '1.greater_or_equal(2).less(3)'
|
54
|
+
assert_scribble_parse '{{ 1 <= 2 >= 3 }}', '1.less_or_equal(2).greater_or_equal(3)'
|
55
|
+
|
56
|
+
assert_scribble_parse '{{ 1 + 2 - 3 }}', '1.add(2).subtract(3)'
|
57
|
+
assert_scribble_parse '{{ 1 - 2 + 3 }}', '1.subtract(2).add(3)'
|
58
|
+
|
59
|
+
assert_scribble_parse '{{ 1 * 2 % 3 }}', '1.multiply(2).remainder(3)'
|
60
|
+
assert_scribble_parse '{{ 1 / 2 * 3 }}', '1.divide(2).multiply(3)'
|
61
|
+
assert_scribble_parse '{{ 1 % 2 / 3 }}', '1.remainder(2).divide(3)'
|
62
|
+
|
63
|
+
assert_scribble_parse '{{ --!-1 }}', '1.negative().not().negative().negative()'
|
64
|
+
assert_scribble_parse '{{ !!-!1 }}', '1.not().negative().not().not()'
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'combines operators with different precedence' do
|
68
|
+
assert_scribble_parse '{{ 1 & 2 | 3 }}', '1.and(2).or(3)'
|
69
|
+
|
70
|
+
assert_scribble_parse '{{ 1 = 2 & 3 }}', '1.equals(2).and(3)'
|
71
|
+
assert_scribble_parse '{{ 1 != 2 & 3 }}', '1.differs(2).and(3)'
|
72
|
+
|
73
|
+
assert_scribble_parse '{{ 1 > 2 = 3 }}', '1.greater(2).equals(3)'
|
74
|
+
assert_scribble_parse '{{ 1 < 2 = 3 }}', '1.less(2).equals(3)'
|
75
|
+
assert_scribble_parse '{{ 1 >= 2 = 3 }}', '1.greater_or_equal(2).equals(3)'
|
76
|
+
assert_scribble_parse '{{ 1 <= 2 = 3 }}', '1.less_or_equal(2).equals(3)'
|
77
|
+
assert_scribble_parse '{{ 1 > 2 != 3 }}', '1.greater(2).differs(3)'
|
78
|
+
assert_scribble_parse '{{ 1 < 2 != 3 }}', '1.less(2).differs(3)'
|
79
|
+
assert_scribble_parse '{{ 1 >= 2 != 3 }}', '1.greater_or_equal(2).differs(3)'
|
80
|
+
assert_scribble_parse '{{ 1 <= 2 != 3 }}', '1.less_or_equal(2).differs(3)'
|
81
|
+
|
82
|
+
assert_scribble_parse '{{ 1 + 2 > 3 }}', '1.add(2).greater(3)'
|
83
|
+
assert_scribble_parse '{{ 1 + 2 < 3 }}', '1.add(2).less(3)'
|
84
|
+
assert_scribble_parse '{{ 1 + 2 >= 3 }}', '1.add(2).greater_or_equal(3)'
|
85
|
+
assert_scribble_parse '{{ 1 + 2 <= 3 }}', '1.add(2).less_or_equal(3)'
|
86
|
+
assert_scribble_parse '{{ 1 - 2 > 3 }}', '1.subtract(2).greater(3)'
|
87
|
+
assert_scribble_parse '{{ 1 - 2 < 3 }}', '1.subtract(2).less(3)'
|
88
|
+
assert_scribble_parse '{{ 1 - 2 >= 3 }}', '1.subtract(2).greater_or_equal(3)'
|
89
|
+
assert_scribble_parse '{{ 1 - 2 <= 3 }}', '1.subtract(2).less_or_equal(3)'
|
90
|
+
|
91
|
+
assert_scribble_parse '{{ 1 * 2 + 3 }}', '1.multiply(2).add(3)'
|
92
|
+
assert_scribble_parse '{{ 1 / 2 + 3 }}', '1.divide(2).add(3)'
|
93
|
+
assert_scribble_parse '{{ 1 % 2 + 3 }}', '1.remainder(2).add(3)'
|
94
|
+
assert_scribble_parse '{{ 1 * 2 - 3 }}', '1.multiply(2).subtract(3)'
|
95
|
+
assert_scribble_parse '{{ 1 / 2 - 3 }}', '1.divide(2).subtract(3)'
|
96
|
+
assert_scribble_parse '{{ 1 % 2 - 3 }}', '1.remainder(2).subtract(3)'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'respects operator precedence' do
|
100
|
+
assert_scribble_parse '{{ 1 | 2 & 3 }}', '1.or(2.and(3))'
|
101
|
+
|
102
|
+
assert_scribble_parse '{{ 1 & 2 = 3 }}', '1.and(2.equals(3))'
|
103
|
+
assert_scribble_parse '{{ 1 & 2 != 3 }}', '1.and(2.differs(3))'
|
104
|
+
|
105
|
+
assert_scribble_parse '{{ 1 = 2 > 3 }}', '1.equals(2.greater(3))'
|
106
|
+
assert_scribble_parse '{{ 1 = 2 < 3 }}', '1.equals(2.less(3))'
|
107
|
+
assert_scribble_parse '{{ 1 = 2 >= 3 }}', '1.equals(2.greater_or_equal(3))'
|
108
|
+
assert_scribble_parse '{{ 1 = 2 <= 3 }}', '1.equals(2.less_or_equal(3))'
|
109
|
+
assert_scribble_parse '{{ 1 != 2 > 3 }}', '1.differs(2.greater(3))'
|
110
|
+
assert_scribble_parse '{{ 1 != 2 < 3 }}', '1.differs(2.less(3))'
|
111
|
+
assert_scribble_parse '{{ 1 != 2 >= 3 }}', '1.differs(2.greater_or_equal(3))'
|
112
|
+
assert_scribble_parse '{{ 1 != 2 <= 3 }}', '1.differs(2.less_or_equal(3))'
|
113
|
+
|
114
|
+
assert_scribble_parse '{{ 1 > 2 + 3 }}', '1.greater(2.add(3))'
|
115
|
+
assert_scribble_parse '{{ 1 < 2 + 3 }}', '1.less(2.add(3))'
|
116
|
+
assert_scribble_parse '{{ 1 >= 2 + 3 }}', '1.greater_or_equal(2.add(3))'
|
117
|
+
assert_scribble_parse '{{ 1 <= 2 + 3 }}', '1.less_or_equal(2.add(3))'
|
118
|
+
assert_scribble_parse '{{ 1 > 2 - 3 }}', '1.greater(2.subtract(3))'
|
119
|
+
assert_scribble_parse '{{ 1 < 2 - 3 }}', '1.less(2.subtract(3))'
|
120
|
+
assert_scribble_parse '{{ 1 >= 2 - 3 }}', '1.greater_or_equal(2.subtract(3))'
|
121
|
+
assert_scribble_parse '{{ 1 <= 2 - 3 }}', '1.less_or_equal(2.subtract(3))'
|
122
|
+
|
123
|
+
assert_scribble_parse '{{ 1 + 2 * 3 }}', '1.add(2.multiply(3))'
|
124
|
+
assert_scribble_parse '{{ 1 + 2 / 3 }}', '1.add(2.divide(3))'
|
125
|
+
assert_scribble_parse '{{ 1 + 2 % 3 }}', '1.add(2.remainder(3))'
|
126
|
+
assert_scribble_parse '{{ 1 - 2 * 3 }}', '1.subtract(2.multiply(3))'
|
127
|
+
assert_scribble_parse '{{ 1 - 2 / 3 }}', '1.subtract(2.divide(3))'
|
128
|
+
assert_scribble_parse '{{ 1 - 2 % 3 }}', '1.subtract(2.remainder(3))'
|
129
|
+
|
130
|
+
assert_scribble_parse '{{ -1 * !2 }}', '1.negative().multiply(2.not())'
|
131
|
+
assert_scribble_parse '{{ -1 / !2 }}', '1.negative().divide(2.not())'
|
132
|
+
assert_scribble_parse '{{ -1 % !2 }}', '1.negative().remainder(2.not())'
|
133
|
+
assert_scribble_parse '{{ !1 * -2 }}', '1.not().multiply(2.negative())'
|
134
|
+
assert_scribble_parse '{{ !1 / -2 }}', '1.not().divide(2.negative())'
|
135
|
+
assert_scribble_parse '{{ !1 % -2 }}', '1.not().remainder(2.negative())'
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'respects operator precedence in complex situations' do
|
139
|
+
assert_scribble_parse '{{ !1 / 2 > 3 < -4 * 5 }}', '1.not().divide(2).greater(3).less(4.negative().multiply(5))'
|
140
|
+
assert_scribble_parse '{{ 1 | 2 <= !-3 | 4 < 5 }}', '1.or(2.less_or_equal(3.negative().not())).or(4.less(5))'
|
141
|
+
assert_scribble_parse '{{ -1 + -2 & 3 != 4 = 5 }}', '1.negative().add(2.negative()).and(3.differs(4).equals(5))'
|
142
|
+
assert_scribble_parse '{{ 1 % 2 - 3 >= !4 != !5 }}', '1.remainder(2).subtract(3).greater_or_equal(4.not()).differs(5.not())'
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'respects parentheses' do
|
146
|
+
assert_scribble_parse '{{ (1 > 2) = 3 }}', '1.greater(2).equals(3)'
|
147
|
+
assert_scribble_parse '{{ (1 < 2) = 3 }}', '1.less(2).equals(3)'
|
148
|
+
assert_scribble_parse '{{ (1 >= 2) = 3 }}', '1.greater_or_equal(2).equals(3)'
|
149
|
+
assert_scribble_parse '{{ (1 <= 2) = 3 }}', '1.less_or_equal(2).equals(3)'
|
150
|
+
assert_scribble_parse '{{ (1 > 2) != 3 }}', '1.greater(2).differs(3)'
|
151
|
+
assert_scribble_parse '{{ (1 < 2) != 3 }}', '1.less(2).differs(3)'
|
152
|
+
assert_scribble_parse '{{ (1 >= 2) != 3 }}', '1.greater_or_equal(2).differs(3)'
|
153
|
+
assert_scribble_parse '{{ (1 <= 2) != 3 }}', '1.less_or_equal(2).differs(3)'
|
154
|
+
end
|
155
|
+
|
156
|
+
# Functions / variables
|
157
|
+
|
158
|
+
it 'parses names' do
|
159
|
+
assert_scribble_parse '{{ foo }}', 'foo'
|
160
|
+
assert_scribble_parse '{{ foo123 }}', 'foo123'
|
161
|
+
assert_scribble_parse '{{ bar? }}', 'bar?'
|
162
|
+
assert_scribble_parse '{{ bar! }}', 'bar!'
|
163
|
+
assert_scribble_parse '{{ foo1 + bar2 }}', 'foo1.add(bar2)'
|
164
|
+
assert_scribble_parse '{{ foo1 - bar2 }}', 'foo1.subtract(bar2)'
|
165
|
+
assert_scribble_parse '{{ foo1 - bar2 / baz3 }}', 'foo1.subtract(bar2.divide(baz3))'
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'parses regular calls' do
|
169
|
+
assert_scribble_parse '{{ foo() }}', 'foo()'
|
170
|
+
assert_scribble_parse '{{ foo(1) }}', 'foo(1)'
|
171
|
+
assert_scribble_parse '{{ bar(1, 2, baz) }}', 'bar(1, 2, baz)'
|
172
|
+
assert_scribble_parse '{{ foo(1 + 2, bar()) }}', 'foo(1.add(2), bar())'
|
173
|
+
assert_scribble_parse '{{ foo(1) + 2 }}', 'foo(1).add(2)'
|
174
|
+
assert_scribble_parse '{{ 1 + foo() }}', '1.add(foo())'
|
175
|
+
assert_scribble_parse '{{ 1 + foo(2) }}', '1.add(foo(2))'
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'parses method style calls' do
|
179
|
+
assert_scribble_parse '{{ 1.foo(2) }}', '1.foo(2)'
|
180
|
+
assert_scribble_parse '{{ 1.foo }}', '1.foo'
|
181
|
+
assert_scribble_parse '{{ (1 + 2).foo(3) }}', '1.add(2).foo(3)'
|
182
|
+
assert_scribble_parse '{{ (1 + 2.foo(3)).bar(4 + 5, baz) }}', '1.add(2.foo(3)).bar(4.add(5), baz)'
|
183
|
+
assert_scribble_parse '{{ (1 + 2).foo(3).bar(4).baz(5) }}', '1.add(2).foo(3).bar(4).baz(5)'
|
184
|
+
assert_scribble_parse '{{ (1 - 2).foo.bar.baz(3) }}', '1.subtract(2).foo.bar.baz(3)'
|
185
|
+
assert_scribble_parse '{{ -1.foo(2) * !3.bar(4) * 5.baz(6) }}', '1.negative().foo(2).multiply(3.not().bar(4)).multiply(5.baz(6))'
|
186
|
+
assert_scribble_parse '{{ -1.foo * !2.bar * 3.baz }}', '1.negative().foo.multiply(2.not().bar).multiply(3.baz)'
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'parses command style calls' do
|
190
|
+
assert_scribble_parse '{{ foo 1 }}', 'foo(1)'
|
191
|
+
assert_scribble_parse '{{ foo bar 1 }}', 'foo(bar(1))'
|
192
|
+
assert_scribble_parse '{{ foo bar baz 1, 2, 3 }}', 'foo(bar(baz(1, 2, 3)))'
|
193
|
+
assert_scribble_parse '{{ foo 1, 2, bar }}', 'foo(1, 2, bar)'
|
194
|
+
assert_scribble_parse '{{ foo 1 + 2 }}', 'foo(1.add(2))'
|
195
|
+
assert_scribble_parse '{{ foo 1 + 2, bar }}', 'foo(1.add(2), bar)'
|
196
|
+
assert_scribble_parse '{{ 1 + (foo 2) }}', '1.add(foo(2))'
|
197
|
+
assert_scribble_parse '{{ foo 1.bar(2).baz(3) }}', 'foo(1.bar(2).baz(3))'
|
198
|
+
assert_scribble_parse '{{ foo(bar 1) }}', 'foo(bar(1))'
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'gives precedence to operations over commands' do
|
202
|
+
assert_scribble_parse '{{ foo - 2 }}', 'foo.subtract(2)'
|
203
|
+
assert_scribble_parse '{{ (foo - 2) }}', 'foo.subtract(2)'
|
204
|
+
assert_scribble_parse '{{ foo(bar - 2) }}', 'foo(bar.subtract(2))'
|
205
|
+
assert_scribble_parse '{{ foo bar - 2 }}', 'foo(bar.subtract(2))'
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'parses calls that are both command and method style' do
|
209
|
+
assert_scribble_parse '{{ 1.foo 2 }}', '1.foo(2)'
|
210
|
+
assert_scribble_parse '{{ -1.foo bar 2 }}', '1.negative().foo(bar(2))'
|
211
|
+
assert_scribble_parse '{{ !1.foo bar baz 2, 3, 4 }}', '1.not().foo(bar(baz(2, 3, 4)))'
|
212
|
+
assert_scribble_parse '{{ 1.foo 2, 3, bar }}', '1.foo(2, 3, bar)'
|
213
|
+
assert_scribble_parse '{{ 1.foo 2 + 3 }}', '1.foo(2.add(3))'
|
214
|
+
assert_scribble_parse '{{ -1.foo 2 + 3, bar }}', '1.negative().foo(2.add(3), bar)'
|
215
|
+
assert_scribble_parse '{{ !1 + (2.foo 3) }}', '1.not().add(2.foo(3))'
|
216
|
+
assert_scribble_parse '{{ 1.foo 2.bar(3).baz(4) }}', '1.foo(2.bar(3).baz(4))'
|
217
|
+
assert_scribble_parse '{{ 1.foo(2.bar 3) }}', '1.foo(2.bar(3))'
|
218
|
+
end
|
219
|
+
|
220
|
+
# Nesting
|
221
|
+
|
222
|
+
it 'nests blocks of nodes in calls that need a block' do
|
223
|
+
Scribble::Registry.reset do
|
224
|
+
class SomeBlock < Scribble::Block
|
225
|
+
def qux; end; register :qux
|
226
|
+
end
|
227
|
+
|
228
|
+
assert_scribble_parse '{{ qux }}foo{{ end }}', "qux { 'foo' }"
|
229
|
+
assert_scribble_parse '{{ qux }}{{ qux }}foo{{ end }}{{ end }}', "qux { qux { 'foo' } }"
|
230
|
+
assert_scribble_parse '{{ qux }}foo{{ end }}{{ qux }}foo{{ end }}', "qux { 'foo' }, qux { 'foo' }"
|
231
|
+
assert_scribble_parse '{{ foo }}{{ qux }}{{ foo }}foo{{ foo }}{{ end }}{{ foo }}', "foo, qux { foo, 'foo', foo }, foo"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
@@ -0,0 +1,264 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
describe Scribble::Registry do
|
4
|
+
before do
|
5
|
+
@registry = Scribble::Registry.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'gathers methods' do
|
9
|
+
@registry.for String do
|
10
|
+
method :foo
|
11
|
+
method :bar
|
12
|
+
end
|
13
|
+
|
14
|
+
assert_equal 2, @registry.methods.size
|
15
|
+
assert_equal [:foo, :bar], @registry.methods.map(&:method_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'gathers method signatures along with method names' do
|
19
|
+
@registry.for String do
|
20
|
+
method :foo, String
|
21
|
+
method :bar, Fixnum, [String]
|
22
|
+
end
|
23
|
+
|
24
|
+
assert_equal [String], @registry.methods[0].signature
|
25
|
+
assert_equal [Fixnum, [String]], @registry.methods[1].signature
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'gathers methods for multiple classes at once' do
|
29
|
+
@registry.for TrueClass, FalseClass do
|
30
|
+
method :foo
|
31
|
+
end
|
32
|
+
|
33
|
+
assert_equal 2, @registry.methods.size
|
34
|
+
assert_equal [TrueClass, FalseClass], @registry.methods.map(&:receiver_class)
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'methods' do
|
38
|
+
|
39
|
+
it 'calculates arity' do
|
40
|
+
@registry.for String do
|
41
|
+
method :foo
|
42
|
+
method :bar, String, Fixnum
|
43
|
+
method :baz, [String]
|
44
|
+
method :qux, String, [String, 3]
|
45
|
+
end
|
46
|
+
|
47
|
+
assert_equal 0, @registry.methods[0].min_arity
|
48
|
+
assert_equal 0, @registry.methods[0].max_arity
|
49
|
+
assert_equal 2, @registry.methods[1].min_arity
|
50
|
+
assert_equal 2, @registry.methods[1].max_arity
|
51
|
+
assert_equal 0, @registry.methods[2].min_arity
|
52
|
+
assert_equal nil, @registry.methods[2].max_arity
|
53
|
+
assert_equal 1, @registry.methods[3].min_arity
|
54
|
+
assert_equal 4, @registry.methods[3].max_arity
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'block and split' do
|
59
|
+
|
60
|
+
it 'knows if names have block methods' do
|
61
|
+
@registry.for String do
|
62
|
+
method :foo
|
63
|
+
method :bar, block: true
|
64
|
+
end
|
65
|
+
|
66
|
+
assert_equal false, @registry.block?(:foo)
|
67
|
+
assert_equal true, @registry.block?(:bar)
|
68
|
+
assert_equal nil, @registry.block?(:baz)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'knows if names have split methods' do
|
72
|
+
@registry.for String do
|
73
|
+
method :foo
|
74
|
+
method :bar, split: true
|
75
|
+
end
|
76
|
+
|
77
|
+
refute @registry.split? :foo
|
78
|
+
assert @registry.split? :bar
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'Rails autoloading support' do
|
83
|
+
|
84
|
+
it 'can unregister a method by class name' do
|
85
|
+
class MyMethod < Scribble::Method
|
86
|
+
def bar; end
|
87
|
+
setup String, :bar, []
|
88
|
+
end
|
89
|
+
|
90
|
+
MyMethod.insert @registry
|
91
|
+
|
92
|
+
@registry.for String do
|
93
|
+
method :foo
|
94
|
+
method :baz
|
95
|
+
end
|
96
|
+
|
97
|
+
assert_equal 3, @registry.methods.size
|
98
|
+
|
99
|
+
@registry.unregister 'MyMethod'
|
100
|
+
|
101
|
+
assert_equal 2, @registry.methods.size
|
102
|
+
assert_equal [:foo, :baz], @registry.methods.map(&:method_name)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe 'evaluation' do
|
107
|
+
|
108
|
+
it 'it delegates evaluation to matching methods' do
|
109
|
+
@registry.for Fixnum do
|
110
|
+
method :foo, returns: 0
|
111
|
+
method :foo, String, returns: 1
|
112
|
+
method :foo, String, Fixnum, returns: 2
|
113
|
+
method :foo, Fixnum, returns: 3
|
114
|
+
method :foo, Fixnum, [String], returns: 4
|
115
|
+
method :foo, [String, 2], returns: 5
|
116
|
+
method :foo, [String], returns: 6
|
117
|
+
method :foo, [String], Fixnum, returns: 7
|
118
|
+
method :foo, [Object], returns: 8
|
119
|
+
end
|
120
|
+
|
121
|
+
assert_equal 0, @registry.evaluate(:foo, 0, [])
|
122
|
+
assert_equal 1, @registry.evaluate(:foo, 0, [''])
|
123
|
+
assert_equal 2, @registry.evaluate(:foo, 0, ['', 0])
|
124
|
+
assert_equal 3, @registry.evaluate(:foo, 0, [0])
|
125
|
+
assert_equal 4, @registry.evaluate(:foo, 0, [0, ''])
|
126
|
+
assert_equal 4, @registry.evaluate(:foo, 0, [0, '', ''])
|
127
|
+
assert_equal 5, @registry.evaluate(:foo, 0, ['', ''])
|
128
|
+
assert_equal 5, @registry.evaluate(:foo, 0, ['', ''])
|
129
|
+
assert_equal 6, @registry.evaluate(:foo, 0, ['', '', ''])
|
130
|
+
assert_equal 7, @registry.evaluate(:foo, 0, ['', '', 0])
|
131
|
+
assert_equal 8, @registry.evaluate(:foo, 0, [false])
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'raises an error when no method matches' do
|
135
|
+
@registry.for String do
|
136
|
+
method :add, String, to: ->(other) { self + other }
|
137
|
+
end
|
138
|
+
|
139
|
+
assert_raises(Scribble::Support::Unmatched) { @registry.evaluate :add, 'foo', [] }
|
140
|
+
assert_raises(Scribble::Support::Unmatched) { @registry.evaluate :addd, 'foo', ['bar'] }
|
141
|
+
assert_raises(Scribble::Support::Unmatched) { @registry.evaluate :add, 'foo', ['bar', 'baz'] }
|
142
|
+
assert_raises(Scribble::Support::Unmatched) { @registry.evaluate :add, 'foo', [1] }
|
143
|
+
assert_raises(Scribble::Support::Unmatched) { @registry.evaluate :add, 1, ['foo'] }
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'provides different implementation options for methods' do
|
147
|
+
@registry.for TrueClass do
|
148
|
+
method :baz, Object, to: ->(object) { object }
|
149
|
+
end
|
150
|
+
|
151
|
+
@registry.for String do
|
152
|
+
to_boolean { true }
|
153
|
+
method :foo, Object, as: '=='
|
154
|
+
method :bar, Object, to: ->(object) { object }
|
155
|
+
method :baz, Object, cast: 'to_boolean'
|
156
|
+
method :qux, Object, returns: 'qux'
|
157
|
+
end
|
158
|
+
|
159
|
+
assert_equal true, @registry.evaluate(:foo, 'foo', ['foo'])
|
160
|
+
assert_equal false, @registry.evaluate(:foo, 'foo', ['bar'])
|
161
|
+
assert_equal 'baz', @registry.evaluate(:bar, 'foo', ['baz'])
|
162
|
+
assert_equal 'baz', @registry.evaluate(:baz, 'foo', ['baz'])
|
163
|
+
assert_equal 'qux', @registry.evaluate(:qux, 'foo', ['baz'])
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'gathers and evaluates to_boolean cast methods' do
|
167
|
+
@registry.for String do
|
168
|
+
to_boolean { self == 'foo' }
|
169
|
+
end
|
170
|
+
|
171
|
+
@registry.for Fixnum do
|
172
|
+
to_boolean { self > 0 }
|
173
|
+
end
|
174
|
+
|
175
|
+
assert_equal 2, @registry.methods.size
|
176
|
+
|
177
|
+
assert_equal @registry.to_boolean('foo'), true
|
178
|
+
assert_equal @registry.to_boolean('bar'), false
|
179
|
+
assert_equal @registry.to_boolean(1), true
|
180
|
+
assert_equal @registry.to_boolean(0), false
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'gathers and evaluated to_string cast methods' do
|
184
|
+
@registry.for String do
|
185
|
+
to_string { self }
|
186
|
+
end
|
187
|
+
|
188
|
+
@registry.for Fixnum do
|
189
|
+
to_string { to_s }
|
190
|
+
end
|
191
|
+
|
192
|
+
assert_equal 2, @registry.methods.size
|
193
|
+
|
194
|
+
assert_equal @registry.to_string('foo'), 'foo'
|
195
|
+
assert_equal @registry.to_string(12345), '12345'
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe 'sanity checks' do
|
200
|
+
|
201
|
+
it 'makes sure method names are symbols' do
|
202
|
+
assert_raises_message(/needs to be a Symbol/) { @registry.for(String) { method 'foo' } }
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'does not allow methods with duplicate class, name and signature' do
|
206
|
+
@registry.for String do
|
207
|
+
method :foo
|
208
|
+
method :foo, String
|
209
|
+
method :bar, String
|
210
|
+
end
|
211
|
+
|
212
|
+
@registry.for Fixnum do
|
213
|
+
method :foo
|
214
|
+
end
|
215
|
+
|
216
|
+
assert_raises_message(/Duplicate method/) { @registry.for(String) { method :foo } }
|
217
|
+
assert_raises_message(/Duplicate method/) { @registry.for(String) { method :bar, String } }
|
218
|
+
assert_raises_message(/Duplicate method/) { @registry.for(Fixnum) { method :foo } }
|
219
|
+
|
220
|
+
assert_equal 4, @registry.methods.size
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'only allows a single implementation option per method' do
|
224
|
+
assert_raises_message(/multiple implementation options/) { @registry.for(String) { method :foo, as: 'bar', to: -> {} } }
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'checks the type of implementation options and split' do
|
228
|
+
assert_raises_message(/requires String/) { @registry.for(String) { method :foo, as: :bar } }
|
229
|
+
assert_raises_message(/requires Proc/) { @registry.for(String) { method :foo, to: :bar } }
|
230
|
+
assert_raises_message(/:cast must be/) { @registry.for(String) { method :foo, cast: 'to_float' } }
|
231
|
+
assert_raises_message(/:split must be/) { @registry.for(String) { method :foo, split: :true } }
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'requires methods with the same name to always or never be a block' do
|
235
|
+
@registry.for String do
|
236
|
+
method :foo
|
237
|
+
method :bar, block: true
|
238
|
+
end
|
239
|
+
|
240
|
+
assert_raises_message(/must be a non-block/) { @registry.for(String) { method :foo, String, block: true } }
|
241
|
+
assert_raises_message(/must be a block/) { @registry.for(String) { method :bar, String } }
|
242
|
+
|
243
|
+
assert_raises_message(/must be a non-block/) { @registry.for(Fixnum) { method :foo, block: true } }
|
244
|
+
assert_raises_message(/must be a block/) { @registry.for(Fixnum) { method :bar } }
|
245
|
+
|
246
|
+
assert_equal 2, @registry.methods.size
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'requires methods with the same name to always or never be splits' do
|
250
|
+
@registry.for String do
|
251
|
+
method :foo
|
252
|
+
method :bar, split: true
|
253
|
+
end
|
254
|
+
|
255
|
+
assert_raises_message(/must be a non-split/) { @registry.for(String) { method :foo, String, split: true } }
|
256
|
+
assert_raises_message(/must be a split/) { @registry.for(String) { method :bar, String } }
|
257
|
+
|
258
|
+
assert_raises_message(/must be a non-split/) { @registry.for(Fixnum) { method :foo, split: true } }
|
259
|
+
assert_raises_message(/must be a split/) { @registry.for(Fixnum) { method :bar } }
|
260
|
+
|
261
|
+
assert_equal 2, @registry.methods.size
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|