locomotivecms-solid 0.2.2
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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.travis.yml +10 -0
- data/Gemfile +6 -0
- data/LICENSE +20 -0
- data/README.md +152 -0
- data/Rakefile +7 -0
- data/lib/locomotivecms-solid.rb +2 -0
- data/lib/solid.rb +48 -0
- data/lib/solid/arguments.rb +26 -0
- data/lib/solid/block.rb +13 -0
- data/lib/solid/conditional_block.rb +35 -0
- data/lib/solid/context_error.rb +2 -0
- data/lib/solid/default_security_rules.rb +24 -0
- data/lib/solid/element.rb +51 -0
- data/lib/solid/engine.rb +4 -0
- data/lib/solid/extensions.rb +17 -0
- data/lib/solid/iterable.rb +18 -0
- data/lib/solid/liquid_extensions.rb +87 -0
- data/lib/solid/liquid_extensions/assign_tag.rb +21 -0
- data/lib/solid/liquid_extensions/for_tag.rb +102 -0
- data/lib/solid/liquid_extensions/if_tag.rb +44 -0
- data/lib/solid/liquid_extensions/unless_tag.rb +13 -0
- data/lib/solid/liquid_extensions/variable.rb +34 -0
- data/lib/solid/method_whitelist.rb +56 -0
- data/lib/solid/model_drop.rb +119 -0
- data/lib/solid/parser.rb +108 -0
- data/lib/solid/parser/ripper.rb +220 -0
- data/lib/solid/parser/ruby_parser.rb +88 -0
- data/lib/solid/tag.rb +11 -0
- data/lib/solid/template.rb +24 -0
- data/lib/solid/version.rb +3 -0
- data/locomotivecms-solid.gemspec +26 -0
- data/spec/solid/arguments_spec.rb +314 -0
- data/spec/solid/block_spec.rb +39 -0
- data/spec/solid/conditional_block_spec.rb +39 -0
- data/spec/solid/default_security_rules_spec.rb +180 -0
- data/spec/solid/element_examples.rb +67 -0
- data/spec/solid/liquid_extensions/assign_tag_spec.rb +27 -0
- data/spec/solid/liquid_extensions/for_tag_spec.rb +48 -0
- data/spec/solid/liquid_extensions/if_tag_spec.rb +64 -0
- data/spec/solid/liquid_extensions/unless_tag_spec.rb +54 -0
- data/spec/solid/liquid_extensions/variable_spec.rb +25 -0
- data/spec/solid/model_drop_spec.rb +26 -0
- data/spec/solid/parser/ripper_spec.rb +14 -0
- data/spec/solid/parser/ruby_parser_spec.rb +7 -0
- data/spec/solid/tag_spec.rb +26 -0
- data/spec/solid/template_spec.rb +37 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/class_highjacker_examples.rb +33 -0
- data/spec/support/method_whitelist_matchers.rb +17 -0
- data/spec/support/parser_examples.rb +261 -0
- data/spec/support/tag_highjacker_examples.rb +33 -0
- metadata +204 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "solid/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "locomotivecms-solid"
|
|
7
|
+
s.version = Solid::VERSION
|
|
8
|
+
s.authors = ["Jean Boussier", "Yannick François", "Didier Lafforgue"]
|
|
9
|
+
s.email = ["jean.boussier@tigerlilyapps.com", "yannick@tigerlilyapps.com", "didier.lafforgue@gmail.com"]
|
|
10
|
+
s.homepage = ""
|
|
11
|
+
s.summary = %q{Helpers for easily creating custom Liquid tags and block}
|
|
12
|
+
s.description = %q{The Solid gem from the TigerLily team but modified to work with LocomotiveCMS}
|
|
13
|
+
|
|
14
|
+
s.files = `git ls-files`.split("\n")
|
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
17
|
+
s.require_paths = ["lib"]
|
|
18
|
+
|
|
19
|
+
s.add_development_dependency "rspec"
|
|
20
|
+
s.add_development_dependency "rake"
|
|
21
|
+
s.add_development_dependency "i18n"
|
|
22
|
+
s.add_development_dependency "ruby_parser", "~> 3.2"
|
|
23
|
+
s.add_development_dependency "activesupport", "~> 3"
|
|
24
|
+
|
|
25
|
+
s.add_runtime_dependency "locomotivecms-liquid", "~> 2.6.0"
|
|
26
|
+
end
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Solid::Arguments do
|
|
4
|
+
|
|
5
|
+
class DummyDrop < Liquid::Drop
|
|
6
|
+
|
|
7
|
+
def before_method(name)
|
|
8
|
+
"dummy #{name}"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def parse(string, context={})
|
|
14
|
+
Solid::Arguments.parse(string).interpolate(Liquid::Context.new(context))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
context 'with no arguments' do
|
|
18
|
+
|
|
19
|
+
it "parses as an empty array" do
|
|
20
|
+
parse('').should be == []
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'with a single argument' do
|
|
26
|
+
|
|
27
|
+
context 'of type string' do
|
|
28
|
+
|
|
29
|
+
it 'can parse an empty string' do
|
|
30
|
+
parse("''").should be == ['']
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'can parse a constant' do
|
|
34
|
+
parse("FooBar", {'FooBar' => 42}).should be == [42]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'can parse a simple string (between simple quotes)' do
|
|
38
|
+
parse("'foobar'").should be == ['foobar']
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'can parse a simple string (between double quotes)' do
|
|
42
|
+
parse('"foobar"').should be == ['foobar']
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'should not consider this string as a context var' do
|
|
46
|
+
parse('"foobar"', {'foobar' => 'plop'}).should_not == ['plop']
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'should not be disturbed by a string containing a comma' do
|
|
50
|
+
parse(%{"foo,bar", 'egg,spam'}).should be == ['foo,bar', 'egg,spam']
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'should not be disturbed by a string containing a simple quote' do
|
|
54
|
+
parse('"foo\'bar"').should be == ["foo'bar"]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'should not be disturbed by a string containing a double quote' do
|
|
58
|
+
parse("'foo\"bar'").should be == ['foo"bar']
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
pending('not yet implemented') do
|
|
62
|
+
it 'should work for a string containing interpolation' do
|
|
63
|
+
parse('"1#{foo}3"', {'foo' => 2}).should be == ['123']
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context 'of type integer' do
|
|
70
|
+
|
|
71
|
+
it 'should work' do
|
|
72
|
+
parse('42').should be == [42]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context 'of type float' do
|
|
78
|
+
|
|
79
|
+
it 'should work' do
|
|
80
|
+
parse('4.2').should be == [4.2]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context 'of type boolean' do
|
|
86
|
+
|
|
87
|
+
it 'should work with `true`' do
|
|
88
|
+
parse('true').should be == [true]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'should work with `false`' do
|
|
92
|
+
parse('false').should be == [false]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context 'of type Regexp' do
|
|
98
|
+
|
|
99
|
+
it 'should work for simple cases' do
|
|
100
|
+
parse('/bb|[^b]{2}/').should be == [/bb|[^b]{2}/]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'should work for a regexp containing interpolation' do
|
|
104
|
+
pending('not yet implemented')
|
|
105
|
+
parse('/#{mystring}|[^b]{2}/', {'mystring' => 'bb'}).should be == [/bb|[^b]{2}/]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context 'of type Range' do
|
|
111
|
+
|
|
112
|
+
it 'should work for integer ranges' do
|
|
113
|
+
parse('1..10').should be == [1..10]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'should work for integer exclusive ranges' do
|
|
117
|
+
parse('1...10').should be == [1...10]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it 'should work for float ranges' do
|
|
121
|
+
parse('1.0..10.0').should be == [1.0..10.0]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it 'should work with context variables' do
|
|
125
|
+
parse('a..b', {'a' => 1, 'b' => 10}).should be == [1..10]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
context 'binary operators' do
|
|
131
|
+
|
|
132
|
+
it 'should permit additions' do
|
|
133
|
+
parse('1 + 2').should be == [3]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'should permit multiplications' do
|
|
137
|
+
parse('2 * 3').should be == [6]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it 'should permit to use builtins boolean operators' do
|
|
141
|
+
parse('true && false').should be == [false]
|
|
142
|
+
parse('false || true').should be == [true]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
context 'unary operators' do
|
|
148
|
+
|
|
149
|
+
it 'should allow to use "!"' do
|
|
150
|
+
parse('!true').should be == [false]
|
|
151
|
+
parse('!false').should be == [true]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
context 'of type "context var"' do
|
|
157
|
+
|
|
158
|
+
it 'should work' do
|
|
159
|
+
parse('myvar', {'myvar' => 'myvalue'}).should be == ['myvalue']
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'can call methods without arguments' do
|
|
163
|
+
parse('myvar.length', {'myvar' => ' myvalue '}).should be == [9]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it 'can call methods without arguments on immediate values' do
|
|
167
|
+
parse('"foobar".length').should be == [6]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'can call methods without arguments but parentheses on immediate values' do
|
|
171
|
+
parse('"foobar".length()').should be == [6]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it 'can call a method with arguments' do
|
|
175
|
+
parse('myvar.split(",", 2)', {'myvar' => 'foo,bar'}).should be == [%w(foo bar)]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it 'can call a method with context var arguments' do
|
|
179
|
+
parse('myvar.split(myseparator, 2)', {'myvar' => 'foo,bar', 'myseparator' => ','}).should be == [%w(foo bar)]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it 'can evaluate context var deeply unclosed in collections' do
|
|
183
|
+
parse('[{1 => [{2 => myvar}]}]', {'myvar' => 'myvalue'}).first.should be == [{1 => [{2 => 'myvalue'}]}]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it 'can call methods chain without arguments' do
|
|
187
|
+
parse('myvar.strip.length', {'myvar' => ' myvalue '}).should be == [7]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it 'can call predicate methods' do
|
|
191
|
+
parse('myvar.empty?', {'myvar' => ' myvalue '}).should be == [false]
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it 'can get a hash value' do
|
|
195
|
+
parse('myvar.mykey', {'myvar' => {'mykey' => 'myvalue'}}).should be == ['myvalue']
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it 'can fallback on Liquid::Drop#before_method' do
|
|
199
|
+
parse('myvar.mymethod', {'myvar' => DummyDrop.new}).should be == ['dummy mymethod']
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it 'should manage errors'
|
|
203
|
+
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
context 'of type hash' do
|
|
207
|
+
|
|
208
|
+
it 'should be able to parse unclosed hashes' do
|
|
209
|
+
parse('permissions: ""').should be == [{permissions: ''}]
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
context 'of type "named parameter"' do
|
|
215
|
+
|
|
216
|
+
it 'should be able to parse a string' do
|
|
217
|
+
parse('foo:"bar"').should be == [{:foo => 'bar'}]
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it 'should be able to parse an int' do
|
|
221
|
+
parse('foo:42').should be == [{:foo => 42}]
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it 'should be able to parse a context var' do
|
|
225
|
+
parse('foo:bar', {'bar' => 'baz'}).should be == [{:foo => 'baz'}]
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
it "should not be disturbed by a comma into a named string" do
|
|
229
|
+
parse('foo:"bar,baz"').should be == [{:foo => 'bar,baz'}]
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it "should support a mix of types" do
|
|
233
|
+
parse('foo:"bar",bar:42, baz:true, egg:spam', {'spam' => 'egg'}).should be == [{
|
|
234
|
+
foo: 'bar',
|
|
235
|
+
bar: 42,
|
|
236
|
+
baz: true,
|
|
237
|
+
egg: 'egg',
|
|
238
|
+
}]
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
it "should not be disturbed by a dot into the key" do
|
|
242
|
+
parse(':"foo.bar" => "bar"').should be == [{:'foo.bar' => 'bar'}]
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
context 'security !!!' do
|
|
250
|
+
|
|
251
|
+
it 'should not allow to call unsecure methods' do
|
|
252
|
+
parse('42.send("`", "echo foo")').should be == [nil]
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
it 'should not allow to call unsecure methods' do
|
|
256
|
+
parse('42.__send__("`", "echo foo")').should be == [nil]
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it "should raise a Solid::SyntaxError on unknown constructs" do
|
|
260
|
+
expect {
|
|
261
|
+
parse('{}\[]')
|
|
262
|
+
}.to raise_error(Solid::SyntaxError)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it "should use #to_liquid" do
|
|
266
|
+
drop = Object.new
|
|
267
|
+
def drop.to_liquid
|
|
268
|
+
'liquid'
|
|
269
|
+
end
|
|
270
|
+
parse('drop', 'drop' => drop).should be == ['liquid']
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
it "should use #context= if available" do
|
|
274
|
+
drop = Object.new
|
|
275
|
+
class << drop
|
|
276
|
+
def to_liquid
|
|
277
|
+
self
|
|
278
|
+
end
|
|
279
|
+
def to_s
|
|
280
|
+
@alias
|
|
281
|
+
end
|
|
282
|
+
def context=(context)
|
|
283
|
+
@alias = context['alias']
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
parse('drop.to_s', 'drop' => drop, 'alias' => 'liquid').should be == ['liquid']
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
context 'with useless round brackets' do
|
|
292
|
+
|
|
293
|
+
it 'should still work' do
|
|
294
|
+
parse('(42)').should be == [42]
|
|
295
|
+
parse('(((((((42)))))))').should be == [42]
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
context 'with multiple arguments' do
|
|
301
|
+
|
|
302
|
+
it 'should return 3 arguments and an option hash' do
|
|
303
|
+
args = parse('1, "2", myvar, myopt:false', {'myvar' => 4.2})
|
|
304
|
+
args.should be == [1, '2', 4.2, {:myopt => false}]
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it 'should be tolerant about whitespace around commas and colons' do
|
|
308
|
+
args = parse(" 1\t, '2' ,myvar, myopt: false", {'myvar' => 4.2})
|
|
309
|
+
args.should be == [1, '2', 4.2, {:myopt => false}]
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class DummyBlock < Solid::Block
|
|
4
|
+
|
|
5
|
+
def display(condition)
|
|
6
|
+
if condition
|
|
7
|
+
yield
|
|
8
|
+
else
|
|
9
|
+
'not_yielded'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe Solid::Block do
|
|
16
|
+
|
|
17
|
+
it_behaves_like "a Solid element"
|
|
18
|
+
|
|
19
|
+
describe '#display' do
|
|
20
|
+
|
|
21
|
+
let(:tokens) { ["dummy", "{% enddummy %}", "outside"] }
|
|
22
|
+
|
|
23
|
+
subject{ DummyBlock.new('dummy', 'condition', tokens) }
|
|
24
|
+
|
|
25
|
+
it 'yielding should render the block content' do
|
|
26
|
+
subject.render(Liquid::Context.new('condition' => true)).should be == 'dummy'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'should only render until the {% endblock %} tag' do
|
|
30
|
+
subject.render(Liquid::Context.new('condition' => true)).should_not include('outside')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'should not render its content if it do not yield' do
|
|
34
|
+
subject.render(Liquid::Context.new('condition' => false)).should_not include('dummy')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class IfPresent < Solid::ConditionalBlock
|
|
4
|
+
|
|
5
|
+
def display(string)
|
|
6
|
+
yield(!string.strip.empty?)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe Solid::ConditionalBlock do
|
|
12
|
+
|
|
13
|
+
it_behaves_like "a Solid element"
|
|
14
|
+
|
|
15
|
+
describe '#display' do
|
|
16
|
+
|
|
17
|
+
let(:tokens) { ["present", "{% else %}", "blank", "{% endifpresent %}"] }
|
|
18
|
+
|
|
19
|
+
subject{ IfPresent.new('ifpresent', 'mystring', tokens) }
|
|
20
|
+
|
|
21
|
+
it 'yielding true should render the main block' do
|
|
22
|
+
context = Liquid::Context.new('mystring' => 'blah')
|
|
23
|
+
subject.render(context).should be == 'present'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'yielding false should render the `else` block' do
|
|
27
|
+
context = Liquid::Context.new('mystring' => '')
|
|
28
|
+
subject.render(context).should be == 'blank'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'yielding false without a `else` block does not render anything' do
|
|
32
|
+
context = Liquid::Context.new('mystring' => '')
|
|
33
|
+
subject = IfPresent.new('ifpresent', 'mystring', ['present', '{% endifpresent %}'])
|
|
34
|
+
subject.render(context).should be_nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'active_support/core_ext'
|
|
3
|
+
|
|
4
|
+
describe Solid, 'default security rules' do
|
|
5
|
+
|
|
6
|
+
shared_examples_for 'a ruby object' do
|
|
7
|
+
it_should_safely_respond_to :nil?, :==, :!=, :!, :!~, :blank?, :present?, :in?, :to_json
|
|
8
|
+
it_should_not_safely_respond_to :const_get, :const_set,
|
|
9
|
+
:instance_variable_get, :instance_variable_set, :instance_variable_defined?,
|
|
10
|
+
:send, :__send__, :public_send,
|
|
11
|
+
:__id__, :object_id,
|
|
12
|
+
:class, :singleton_class, :trust, :taint, :untaint, :untrust,
|
|
13
|
+
:clone, :dup, :initialize_dup, :initialize_clone, :freeze,
|
|
14
|
+
:methods, :singleton_methods, :protected_methods, :private_methods,
|
|
15
|
+
:method, :public_method, :define_singleton_method, :extend,
|
|
16
|
+
:eval, :instance_eval, :instance_exec, :exec, :`, :system, :test,
|
|
17
|
+
:global_variables, :local_variables,
|
|
18
|
+
:gets, :readline, :readlines, :sleep,
|
|
19
|
+
:at_exit!, :exit, :fork, :spawn, :trap, :exit!, :syscall
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
shared_examples_for 'a ruby module' do
|
|
23
|
+
it_should_behave_like 'a ruby object'
|
|
24
|
+
it_should_not_safely_respond_to :ancestors
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
shared_examples_for 'a ruby class' do
|
|
28
|
+
it_should_behave_like 'a ruby module'
|
|
29
|
+
it_should_not_safely_respond_to :new, :allocate, :superclass
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
shared_examples_for 'a boolean' do
|
|
33
|
+
it_should_behave_like 'a ruby object'
|
|
34
|
+
it_should_safely_respond_to :false?, :true?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
shared_examples_for 'an enumerable' do
|
|
38
|
+
it_should_safely_respond_to :sort, :length, :size
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
shared_examples_for 'a comparable' do
|
|
42
|
+
it_should_safely_respond_to :<, :<=, :==, :>, :>=, :between?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
shared_examples_for 'a numeric' do
|
|
46
|
+
it_should_behave_like 'a ruby object', 'a comparable'
|
|
47
|
+
it_should_safely_respond_to :%, :*, :**, :+, :-, :-@, :/, :<=>, :===, :to_s, :abs,
|
|
48
|
+
:second, :seconds, :minute, :minutes, :hour, :hours, :day, :days, :week, :weeks,
|
|
49
|
+
:bytes, :kilobytes, :megabytes, :gigabytes, :terabytes, :petabytes, :exabytes
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
shared_examples_for 'an integer' do
|
|
53
|
+
it_should_behave_like 'a numeric'
|
|
54
|
+
it_should_safely_respond_to :div, :divmod, :even?, :odd?, :to_f,
|
|
55
|
+
:month, :months, :year, :years
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe 'nil' do
|
|
59
|
+
subject { nil }
|
|
60
|
+
|
|
61
|
+
it_should_behave_like 'a ruby object'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe true do
|
|
65
|
+
subject { true }
|
|
66
|
+
|
|
67
|
+
it_should_behave_like 'a ruby object'
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe false do
|
|
71
|
+
subject { false }
|
|
72
|
+
|
|
73
|
+
it_should_behave_like 'a ruby object'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe 'Basic object instance' do
|
|
77
|
+
let(:basic_class) { Class.new(Object) }
|
|
78
|
+
subject { basic_class.new }
|
|
79
|
+
|
|
80
|
+
it_should_behave_like 'a ruby object'
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe 'Array instances' do
|
|
84
|
+
subject { [] }
|
|
85
|
+
|
|
86
|
+
it_should_behave_like 'a ruby object', 'an enumerable'
|
|
87
|
+
it_should_safely_respond_to :[], :[]=, :first, :last, :join, :reverse, :uniq, :include?, :empty?,
|
|
88
|
+
:to_sentence, :in_groups_of, :in_groups
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe 'Array class' do
|
|
92
|
+
subject { Array }
|
|
93
|
+
|
|
94
|
+
it_should_behave_like 'a ruby class'
|
|
95
|
+
it_should_safely_respond_to :wrap
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe 'Hash instances' do
|
|
99
|
+
subject { {} }
|
|
100
|
+
|
|
101
|
+
it_should_behave_like 'a ruby object', 'an enumerable'
|
|
102
|
+
it_should_safely_respond_to :[], :[]=, :has_key?, :has_value?, :empty?, :except, :slice
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe 'Range instances' do
|
|
106
|
+
subject { 1..10 }
|
|
107
|
+
|
|
108
|
+
it_should_behave_like 'a ruby object', 'an enumerable'
|
|
109
|
+
it_should_safely_respond_to :first, :last, :begin, :end, :max, :min, :cover?, :include?, :member?
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
describe 'Regexp instances' do
|
|
113
|
+
subject { /bb|[^b]{2}/ }
|
|
114
|
+
|
|
115
|
+
it_should_behave_like 'a ruby object'
|
|
116
|
+
it_should_safely_respond_to :==, :===, :=~, :match
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe 'Bignum instances' do
|
|
120
|
+
subject { 2 ** 123 }
|
|
121
|
+
|
|
122
|
+
it { should be_a Bignum }
|
|
123
|
+
it_should_behave_like 'an integer'
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
describe 'Fixnum instances' do
|
|
127
|
+
subject { 4 }
|
|
128
|
+
|
|
129
|
+
it_should_behave_like 'an integer'
|
|
130
|
+
it_should_safely_respond_to :multiple_of?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
describe 'Float instances' do
|
|
134
|
+
subject { 4.2 }
|
|
135
|
+
|
|
136
|
+
it_should_behave_like 'a numeric'
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
describe 'Time class' do
|
|
140
|
+
subject { Time }
|
|
141
|
+
|
|
142
|
+
it_should_behave_like 'a ruby class'
|
|
143
|
+
it_should_safely_respond_to :at, :now
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
describe 'Time instances' do
|
|
147
|
+
subject { Time.now }
|
|
148
|
+
|
|
149
|
+
it_should_safely_respond_to :to_i, :to_f, :<=>,
|
|
150
|
+
:localtime, :gmtime, :utc, :getlocal, :getgm, :getutc,
|
|
151
|
+
:ctime, :asctime, :to_s, :inspect, :to_a, :+, :-, :round,
|
|
152
|
+
:sec, :min, :hour, :mday, :day, :mon, :month, :year, :wday, :yday,
|
|
153
|
+
:isdst, :dst?, :zone, :gmtoff, :gmt_offset, :utc_offset, :utc?, :gmt?,
|
|
154
|
+
:sunday?, :monday?, :tuesday?, :wednesday?, :thursday?, :friday?, :saturday?,
|
|
155
|
+
:tv_sec, :tv_usec, :usec, :tv_nsec, :nsec, :subsec, :strftime,
|
|
156
|
+
:to_time, :to_date, :to_datetime
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
describe 'String instances' do
|
|
160
|
+
subject { 'string' }
|
|
161
|
+
|
|
162
|
+
it_should_behave_like 'a ruby object', 'a comparable', 'an enumerable'
|
|
163
|
+
it_should_safely_respond_to :gsub, :strip, :chop, :chomp, :start_with?, :end_with?,
|
|
164
|
+
:[], :length, :size, :empty?, :=~, :split, :upcase, :downcase, :capitalize, :squeeze, :tr,
|
|
165
|
+
:exclude?, :truncate
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
describe 'Module instances' do
|
|
169
|
+
subject { Module.new }
|
|
170
|
+
|
|
171
|
+
it_should_behave_like 'a ruby module'
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
describe 'Class instances' do
|
|
175
|
+
subject { Class.new }
|
|
176
|
+
|
|
177
|
+
it_should_behave_like 'a ruby class'
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
end
|