locomotivecms_solid 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +20 -0
  7. data/README.md +152 -0
  8. data/Rakefile +7 -0
  9. data/lib/locomotivecms_solid.rb +2 -0
  10. data/lib/solid/arguments.rb +28 -0
  11. data/lib/solid/block.rb +13 -0
  12. data/lib/solid/conditional_block.rb +35 -0
  13. data/lib/solid/context_error.rb +2 -0
  14. data/lib/solid/default_security_rules.rb +24 -0
  15. data/lib/solid/element.rb +51 -0
  16. data/lib/solid/engine.rb +4 -0
  17. data/lib/solid/extensions.rb +17 -0
  18. data/lib/solid/iterable.rb +18 -0
  19. data/lib/solid/liquid_extensions/assign_tag.rb +21 -0
  20. data/lib/solid/liquid_extensions/for_tag.rb +102 -0
  21. data/lib/solid/liquid_extensions/if_tag.rb +44 -0
  22. data/lib/solid/liquid_extensions/unless_tag.rb +13 -0
  23. data/lib/solid/liquid_extensions/variable.rb +34 -0
  24. data/lib/solid/liquid_extensions.rb +87 -0
  25. data/lib/solid/method_whitelist.rb +56 -0
  26. data/lib/solid/model_drop.rb +119 -0
  27. data/lib/solid/parser.rb +265 -0
  28. data/lib/solid/tag.rb +11 -0
  29. data/lib/solid/template.rb +24 -0
  30. data/lib/solid/version.rb +3 -0
  31. data/lib/solid.rb +48 -0
  32. data/locomotivecms_solid.gemspec +25 -0
  33. data/spec/solid/arguments_spec.rb +310 -0
  34. data/spec/solid/block_spec.rb +39 -0
  35. data/spec/solid/conditional_block_spec.rb +39 -0
  36. data/spec/solid/default_security_rules_spec.rb +180 -0
  37. data/spec/solid/element_examples.rb +67 -0
  38. data/spec/solid/liquid_extensions/assign_tag_spec.rb +27 -0
  39. data/spec/solid/liquid_extensions/for_tag_spec.rb +48 -0
  40. data/spec/solid/liquid_extensions/if_tag_spec.rb +64 -0
  41. data/spec/solid/liquid_extensions/unless_tag_spec.rb +54 -0
  42. data/spec/solid/liquid_extensions/variable_spec.rb +25 -0
  43. data/spec/solid/model_drop_spec.rb +26 -0
  44. data/spec/solid/tag_spec.rb +26 -0
  45. data/spec/solid/template_spec.rb +37 -0
  46. data/spec/spec_helper.rb +8 -0
  47. data/spec/support/class_highjacker_examples.rb +33 -0
  48. data/spec/support/method_whitelist_matchers.rb +17 -0
  49. data/spec/support/tag_highjacker_examples.rb +33 -0
  50. metadata +201 -0
@@ -0,0 +1,310 @@
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
+ end
242
+
243
+ end
244
+
245
+ context 'security !!!' do
246
+
247
+ it 'should not allow to call unsecure methods' do
248
+ parse('42.send("`", "echo foo")').should be == [nil]
249
+ end
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 raise a Solid::SyntaxError on unknown constructs" do
256
+ expect {
257
+ parse('{}[]')
258
+ }.to raise_error(Solid::SyntaxError)
259
+ end
260
+
261
+ it "should use #to_liquid" do
262
+ drop = Object.new
263
+ def drop.to_liquid
264
+ 'liquid'
265
+ end
266
+ parse('drop', 'drop' => drop).should be == ['liquid']
267
+ end
268
+
269
+ it "should use #context= if available" do
270
+ drop = Object.new
271
+ class << drop
272
+ def to_liquid
273
+ self
274
+ end
275
+ def to_s
276
+ @alias
277
+ end
278
+ def context=(context)
279
+ @alias = context['alias']
280
+ end
281
+ end
282
+ parse('drop.to_s', 'drop' => drop, 'alias' => 'liquid').should be == ['liquid']
283
+ end
284
+
285
+ end
286
+
287
+ context 'with useless round brackets' do
288
+
289
+ it 'should still work' do
290
+ parse('(42)').should be == [42]
291
+ parse('(((((((42)))))))').should be == [42]
292
+ end
293
+
294
+ end
295
+
296
+ context 'with multiple arguments' do
297
+
298
+ it 'should return 3 arguments and an option hash' do
299
+ args = parse('1, "2", myvar, myopt:false', {'myvar' => 4.2})
300
+ args.should be == [1, '2', 4.2, {:myopt => false}]
301
+ end
302
+
303
+ it 'should be tolerant about whitespace around commas and colons' do
304
+ args = parse(" 1\t, '2' ,myvar, myopt: false", {'myvar' => 4.2})
305
+ args.should be == [1, '2', 4.2, {:myopt => false}]
306
+ end
307
+
308
+ end
309
+
310
+ 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
@@ -0,0 +1,67 @@
1
+ require 'solid'
2
+
3
+ shared_examples "a Solid element" do
4
+
5
+ describe '.tag_name' do
6
+
7
+ it 'should register tag to Liquid with given name' do
8
+ Liquid::Template.should_receive(:register_tag).with('dummy', described_class)
9
+ described_class.tag_name 'dummy'
10
+ end
11
+
12
+ it 'should return previously given name' do
13
+ Liquid::Template.stub(:register_tag)
14
+ described_class.tag_name 'dummy'
15
+ described_class.tag_name.should be == 'dummy'
16
+ end
17
+
18
+ end
19
+
20
+ describe '.context_attribute' do
21
+
22
+ let(:element) do
23
+ described_class.context_attribute :current_user
24
+ Solid::Arguments.stub(:parse).with('ARGUMENTS_STRING')
25
+ element = described_class.new('name', 'ARGUMENTS_STRING', ['{% endname %}'])
26
+ end
27
+
28
+ it 'should define a custom accessor to the rendered context' do
29
+ element.stub(:current_context => {'current_user' => 'me'})
30
+ element.current_user.should be == 'me'
31
+ end
32
+
33
+ it 'should raise a Solid::ContextError if called outside render' do
34
+ expect{
35
+ element.current_user
36
+ }.to raise_error(Solid::ContextError)
37
+ end
38
+
39
+ end
40
+
41
+ describe '#arguments' do
42
+
43
+ it 'should instanciate a Solid::Arguments with his arguments_string' do
44
+ Solid::Arguments.should_receive(:parse).with('ARGUMENTS_STRING')
45
+ described_class.new('name', 'ARGUMENTS_STRING', ['{% endname %}'])
46
+ end
47
+
48
+ it 'should store his Solid:Arguments instance' do
49
+ element = described_class.new('name', '1', ['{% endname %}'])
50
+ element.arguments.should be_a(Solid::Arguments)
51
+ end
52
+
53
+ end
54
+
55
+ describe '#display' do
56
+
57
+ it 'should force developper to define it in child class' do
58
+ Solid::Arguments.stub(:parse).with('ARGUMENTS_STRING')
59
+ element = described_class.new('name', 'ARGUMENTS_STRING', ['{% endname %}'])
60
+ expect{
61
+ element.display
62
+ }.to raise_error(NotImplementedError)
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Solid::LiquidExtensions::AssignTag do
4
+ it_should_behave_like 'a tag highjacker'
5
+
6
+ context 'when Liquid::Assign is replaced' do
7
+
8
+ before :each do
9
+ described_class.load!
10
+ end
11
+
12
+ after :each do
13
+ described_class.unload!
14
+ end
15
+
16
+ it 'should allow complex expression inside an assign tag' do
17
+ template = Solid::Template.parse(%(
18
+ {% assign included = foo.include?('bar') %}
19
+ {{ included }}
20
+ ))
21
+ output = template.render('foo' => ' bar ').strip
22
+ output.should be == 'true'
23
+ end
24
+
25
+ end
26
+
27
+ end