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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +10 -0
  5. data/Gemfile +6 -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.rb +48 -0
  11. data/lib/solid/arguments.rb +26 -0
  12. data/lib/solid/block.rb +13 -0
  13. data/lib/solid/conditional_block.rb +35 -0
  14. data/lib/solid/context_error.rb +2 -0
  15. data/lib/solid/default_security_rules.rb +24 -0
  16. data/lib/solid/element.rb +51 -0
  17. data/lib/solid/engine.rb +4 -0
  18. data/lib/solid/extensions.rb +17 -0
  19. data/lib/solid/iterable.rb +18 -0
  20. data/lib/solid/liquid_extensions.rb +87 -0
  21. data/lib/solid/liquid_extensions/assign_tag.rb +21 -0
  22. data/lib/solid/liquid_extensions/for_tag.rb +102 -0
  23. data/lib/solid/liquid_extensions/if_tag.rb +44 -0
  24. data/lib/solid/liquid_extensions/unless_tag.rb +13 -0
  25. data/lib/solid/liquid_extensions/variable.rb +34 -0
  26. data/lib/solid/method_whitelist.rb +56 -0
  27. data/lib/solid/model_drop.rb +119 -0
  28. data/lib/solid/parser.rb +108 -0
  29. data/lib/solid/parser/ripper.rb +220 -0
  30. data/lib/solid/parser/ruby_parser.rb +88 -0
  31. data/lib/solid/tag.rb +11 -0
  32. data/lib/solid/template.rb +24 -0
  33. data/lib/solid/version.rb +3 -0
  34. data/locomotivecms-solid.gemspec +26 -0
  35. data/spec/solid/arguments_spec.rb +314 -0
  36. data/spec/solid/block_spec.rb +39 -0
  37. data/spec/solid/conditional_block_spec.rb +39 -0
  38. data/spec/solid/default_security_rules_spec.rb +180 -0
  39. data/spec/solid/element_examples.rb +67 -0
  40. data/spec/solid/liquid_extensions/assign_tag_spec.rb +27 -0
  41. data/spec/solid/liquid_extensions/for_tag_spec.rb +48 -0
  42. data/spec/solid/liquid_extensions/if_tag_spec.rb +64 -0
  43. data/spec/solid/liquid_extensions/unless_tag_spec.rb +54 -0
  44. data/spec/solid/liquid_extensions/variable_spec.rb +25 -0
  45. data/spec/solid/model_drop_spec.rb +26 -0
  46. data/spec/solid/parser/ripper_spec.rb +14 -0
  47. data/spec/solid/parser/ruby_parser_spec.rb +7 -0
  48. data/spec/solid/tag_spec.rb +26 -0
  49. data/spec/solid/template_spec.rb +37 -0
  50. data/spec/spec_helper.rb +8 -0
  51. data/spec/support/class_highjacker_examples.rb +33 -0
  52. data/spec/support/method_whitelist_matchers.rb +17 -0
  53. data/spec/support/parser_examples.rb +261 -0
  54. data/spec/support/tag_highjacker_examples.rb +33 -0
  55. metadata +204 -0
@@ -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
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ # FIXME: disabled for now
4
+
5
+ # describe Solid::LiquidExtensions::ForTag do
6
+ # it_should_behave_like 'a tag highjacker'
7
+
8
+ # context 'when Liquid::For is replaced' do
9
+
10
+ # before :each do
11
+ # described_class.load!
12
+ # end
13
+
14
+ # after :each do
15
+ # described_class.unload!
16
+ # end
17
+
18
+ # it 'should allow complex expression inside a for tag' do
19
+ # template = Solid::Template.parse(%(
20
+ # {% for foo in foos.concat(foos.reverse).flatten %}
21
+ # {{ foo }}
22
+ # {% endfor %}
23
+ # ))
24
+ # output = template.render('foos' => [1, 2, 3]).gsub(/[^\d]/, '')
25
+ # output.should be == '123321'
26
+ # end
27
+
28
+ # it 'should still provide the "forloop" object' do
29
+ # template = Solid::Template.parse(%(
30
+ # {% for foo in foos %}{{ forloop.first }},{{ forloop.index0 }}/{{ forloop.length }},{{ forloop.last }}|{% endfor %}
31
+ # ))
32
+ # output = template.render('foos' => [1, 2, 3]).strip
33
+ # output.should be == "true,0/3,false|false,1/3,false|false,2/3,true|"
34
+ # end
35
+
36
+ # it 'should consider all non iterable objects as empty arrays' do
37
+ # template = Solid::Template.parse(%(
38
+ # {% for foo in foos %}
39
+ # {{ foo }}
40
+ # {% endfor %}
41
+ # ))
42
+ # output = template.render('foos' => nil).strip
43
+ # output.should be == ''
44
+ # end
45
+
46
+ # end
47
+
48
+ # end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Solid::LiquidExtensions::IfTag do
4
+ it_should_behave_like 'a tag highjacker'
5
+
6
+ context 'when Liquid::If 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 if tag' do
17
+ template = Solid::Template.parse(%(
18
+ {% if foo.include?('bar') %}
19
+ Hello !
20
+ {% endif %}
21
+ ))
22
+ output = template.render('foo' => ' bar ').strip
23
+ output.should be == 'Hello !'
24
+ end
25
+
26
+ it 'should render nothing if the predicate return false' do
27
+ template = Solid::Template.parse(%(
28
+ {% if foo.include?('bar') %}
29
+ Hello !
30
+ {% endif %}
31
+ ))
32
+ output = template.render('foo' => ' plop ').strip
33
+ output.should be == ''
34
+ end
35
+
36
+ it 'should still accept an else tag' do
37
+ template = Solid::Template.parse(%(
38
+ {% if foo.include?('bar') %}
39
+ Hello !
40
+ {% else %}
41
+ Failed
42
+ {% endif %}
43
+ ))
44
+ output = template.render('foo' => ' spam ').strip
45
+ output.should be == 'Failed'
46
+ end
47
+
48
+ it 'should still accept some elsif tags' do
49
+ template = Solid::Template.parse(%(
50
+ {% if foo.include?('bar') %}
51
+ Hello !
52
+ {% elsif foo.include?('spam') %}
53
+ World !
54
+ {% else %}
55
+ Failed
56
+ {% endif %}
57
+ ))
58
+ output = template.render('foo' => ' spam ').strip
59
+ output.should be == 'World !'
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Solid::LiquidExtensions::UnlessTag do
4
+ it_should_behave_like 'a tag highjacker'
5
+
6
+ context 'when Liquid::Unless 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 unless tag' do
17
+ template = Solid::Template.parse(%(
18
+ {% unless foo.include?('bar') %}
19
+ Hello !
20
+ {% endunless %}
21
+ ))
22
+ output = template.render('foo' => ' spam ').strip
23
+ output.should be == 'Hello !'
24
+ end
25
+
26
+ it 'should still accept an else tag' do
27
+ template = Solid::Template.parse(%(
28
+ {% unless foo.include?('bar') %}
29
+ Hello !
30
+ {% else %}
31
+ Failed
32
+ {% endunless %}
33
+ ))
34
+ output = template.render('foo' => ' bar ').strip
35
+ output.should be == 'Failed'
36
+ end
37
+
38
+ it 'should still accept some elsif tags' do
39
+ template = Solid::Template.parse(%(
40
+ {% unless foo.include?('spam') %}
41
+ Hello !
42
+ {% elsif foo.include?('spam') %}
43
+ World !
44
+ {% else %}
45
+ Failed
46
+ {% endunless %}
47
+ ))
48
+ output = template.render('foo' => ' spam ').strip
49
+ output.should be == 'World !'
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Solid::LiquidExtensions::Variable do
4
+ it_should_behave_like 'a class highjacker'
5
+
6
+ context 'real world example' 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
+ let(:template) { template = Solid::Template.parse('{{ foo.include?("bar") | upcase }}') }
17
+
18
+ it 'should allow method call with arguments in variables brackets' do
19
+ template.render('foo' => 'egg bar spam').should be == 'TRUE'
20
+ template.render('foo' => '').should be == 'FALSE'
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'active_support/core_ext'
3
+ require 'ostruct'
4
+ Rails = OpenStruct.new(env: OpenStruct.new(test?: true))
5
+ require 'solid/model_drop'
6
+
7
+ describe Solid::ModelDrop do
8
+ describe "array methods" do
9
+
10
+ it "work on a drop" do
11
+ drop = described_class.new([1, 2, 3])
12
+ drop.reverse.should be == [3, 2, 1]
13
+ end
14
+
15
+ it "go through #each" do
16
+ klass = Class.new(described_class) do
17
+ def each
18
+ super.select(&:odd?)
19
+ end
20
+ end
21
+ drop = klass.new([1, 2, 3])
22
+ drop.reverse.should be == [3, 1]
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ ripper_support = true
4
+ begin
5
+ require 'ripper'
6
+ rescue LoadError
7
+ ripper_support = false
8
+ end
9
+
10
+ describe Solid::Parser::Ripper do
11
+
12
+ it_should_behave_like 'a solid parser'
13
+
14
+ end if ripper_support
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Solid::Parser::RubyParser do
4
+
5
+ it_should_behave_like 'a solid parser'
6
+
7
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ class DummyTag < Solid::Tag
4
+
5
+ def display(*args)
6
+ args.inspect
7
+ end
8
+
9
+ end
10
+
11
+ describe Solid::Tag do
12
+
13
+ it_behaves_like "a Solid element"
14
+
15
+ subject{ DummyTag.new('dummy', '1, "foo", myvar, myopts: false', 'token') }
16
+
17
+ it 'should works' do
18
+ subject.render('myvar' => 'bar').should be == '[1, "foo", "bar", {:myopts=>false}]'
19
+ end
20
+
21
+ it 'should send all parsed arguments do #display' do
22
+ subject.should_receive(:display).with(1, 'foo', 'bar', :myopts => false).and_return('result')
23
+ subject.render('myvar' => 'bar').should be == 'result'
24
+ end
25
+
26
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Solid::Template do
4
+
5
+ let(:liquid) { %{first_string{% comment %}
6
+ {% if foo %}ifcontent{% endif %}
7
+ {% if foo %}ifsecondcontent{% endif %}
8
+ {% endcomment %}
9
+ {% unless foo %}unlesscontent{% endunless %}
10
+
11
+ } }
12
+
13
+ let(:template) { Solid::Template.parse(liquid) }
14
+
15
+ specify { subject.should be_an(Enumerable) }
16
+
17
+ describe '#each' do
18
+
19
+ let(:yielded_nodes) do
20
+ [].tap do |nodes|
21
+ template.each{ |node| nodes << node }
22
+ end
23
+ end
24
+
25
+ let(:yielded_classes) { yielded_nodes.map(&:class) }
26
+
27
+ it 'should yield parent nodes before child nodes' do
28
+ yielded_classes.index(Liquid::Comment).should be < yielded_classes.index(Liquid::If)
29
+ end
30
+
31
+ it 'should yield first sibling first (No ! really ? ...)' do
32
+ yielded_classes.index(Liquid::Comment).should be < yielded_classes.index(Liquid::Unless)
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,8 @@
1
+ require 'rspec'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'solid'))
3
+
4
+ RSpec.configure do |c|
5
+ c.mock_with :rspec
6
+ end
7
+
8
+ Dir[File.join(File.dirname(__FILE__), '/**/*_examples.rb'), File.join(File.dirname(__FILE__), '/**/*_matchers.rb')].each{ |f| require f }
@@ -0,0 +1,33 @@
1
+ shared_examples "a class highjacker" do
2
+
3
+ context 'class highjacking' do
4
+
5
+ let(:highjacked_class_name) { "Liquid::#{described_class.demodulized_name}" }
6
+
7
+ def highjacked_class
8
+ highjacked_class_name.split('::').inject(Object) { |klass, const_name| klass.const_get(const_name) }
9
+ end
10
+
11
+ after :each do
12
+ described_class.unload!
13
+ end
14
+
15
+ let!(:original_class_id) { highjacked_class.object_id }
16
+
17
+ it 'should be able to replace original class' do
18
+ expect{
19
+ described_class.load!
20
+ }.to change{ highjacked_class.object_id }.from(original_class_id).to(described_class.object_id)
21
+ end
22
+
23
+ it 'should be able to restore original class' do
24
+ described_class.load!
25
+ expect{
26
+ described_class.unload!
27
+ }.to change{ highjacked_class.object_id }.from(described_class.object_id).to(original_class_id)
28
+ end
29
+
30
+ end
31
+
32
+
33
+ end