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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3c7a10871a1bd686fd52392900ae05429a57efa5
4
+ data.tar.gz: 18d617cded1dc8a1abd28548be9c40aee0343e86
5
+ SHA512:
6
+ metadata.gz: fb7ffdbd7aeb5a8755ce513be15d39f02a3f0196eb70ed69c853098845feac19b82409656fd7c354210ce26a234015d4f06c13c9ac8feaad7f17f806e35279ec
7
+ data.tar.gz: a9cb451974d001e7ebe5a34872dab917090481f1f8b79011a263e24883987d0ec9142278f8375f45bcf578ec60851999076fdee66ceb0987b9b1814fe97c2b6c
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - jruby-19mode
6
+ - rbx-19mode
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: jruby-19mode
10
+ - rvm: rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in solid.gemspec
4
+ gemspec
5
+
6
+ # gem 'locomotivecms-liquid', path: '/Users/didier/Documents/LocomotiveCMS/gems/liquid'
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Tigerlily, http://tigerlilyapps.com/
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # Solid
2
+
3
+ Solid aim to provide a easier and nicer API to create custom Liquid tags and blocks
4
+
5
+ ## Installation
6
+
7
+ Due to a name conflict the gem is called tigerlily-solid. So to use it:
8
+
9
+ ```
10
+ gem 'tigerlily-solid', :require => 'solid'
11
+ ```
12
+
13
+ ## Build Status [![Build Status](https://secure.travis-ci.org/tigerlily/solid.png)](http://travis-ci.org/tigerlily/solid)
14
+
15
+ ## Tags
16
+
17
+ To create a new tag, you just have to:
18
+
19
+ - Extend `Solid::Tag`
20
+ - Define a `display` method
21
+ - Give a `tag_name`
22
+
23
+ ```ruby
24
+ class DummyTag < Solid::Tag
25
+
26
+ tag_name :dummy # register in Liquid under the name of `dummy`
27
+
28
+ def display
29
+ 'dummy !!!'
30
+ end
31
+
32
+ end
33
+ ```
34
+
35
+ ```html
36
+ <p>{% dummy %}<p>
37
+ ```
38
+
39
+ ## Arguments
40
+
41
+ This is the simpliest tag ever but, Solid tags can receive rich arguments:
42
+
43
+ ```ruby
44
+ class TypeOfTag < Solid::Tag
45
+
46
+ tag_name :typeof
47
+
48
+ def display(*values)
49
+ ''.tap do |output|
50
+ values.each do |value|
51
+ output << "<p>Type of #{value} is #{value.class.name}</p>"
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+ ```
58
+
59
+ ```html
60
+ {% capture myvar %}eggspam{% endcapture %}
61
+ {% typeof "foo", 42, 4.2, myvar, myoption:"bar", otheroption:myvar %}
62
+ <!-- produce -->
63
+ <p>Type of "foo" is String</p>
64
+ <p>Type of 42 is Integer</p>
65
+ <p>Type of 4.2 is Float</p>
66
+ <p>Type of "eggspam" is String</p>
67
+ <p>Type of {:myoption=>"bar", :otheroption=>"eggspam"} is Hash</p>
68
+ ```
69
+
70
+ ## Context attributes
71
+
72
+ If there is some "global variables" in your liquid context you can declare that
73
+ your tag need to access it:
74
+
75
+ ```ruby
76
+ class HelloTag < Solid::Tag
77
+
78
+ tag_name :hello
79
+
80
+ context_attribute :current_user
81
+
82
+ def display
83
+ "Hello #{current_user.name} !"
84
+ end
85
+
86
+ end
87
+ ```
88
+
89
+ ```html
90
+ <p>{% hello %}</p>
91
+ <!-- produce -->
92
+ <p>Hello Homer</p>
93
+ ```
94
+ ## Blocks
95
+
96
+ Block are just tags with a body. They perform the same argument parsing.
97
+ To render the block body from it's `display` method you just have to `yield`:
98
+
99
+ ```ruby
100
+ class PBlock < Solid::Block
101
+
102
+ tag_name :p
103
+
104
+ def display(options)
105
+ "<p class='#{options[:class]}'>#{yield}</p>"
106
+ end
107
+
108
+ end
109
+ ```
110
+
111
+ ```html
112
+ {% p class:"content" %}
113
+ It works !
114
+ {% endp %}
115
+ <!-- produce -->
116
+ <p class="content">It works !</p>
117
+ ```
118
+
119
+ Of course you are free to yield once, multiple times or even never.
120
+
121
+ ## Conditional Blocks
122
+
123
+ Conditional blocks are blocks with two bodies. If you yield `true` you will receive the main block
124
+ and if you yield `false` you will receive the else block:
125
+
126
+ ```ruby
127
+ class IfAuthorizedToTag < Solid::ConditionalBlock
128
+
129
+ tag_name :if_authorized_to
130
+
131
+ context_attribute :current_user
132
+
133
+ def display(permission)
134
+ yield(current_user.authorized_to?(permission))
135
+ end
136
+
137
+ end
138
+ ```
139
+
140
+ ```html
141
+ {% if_authorized_to "publish" %}
142
+ You are authorized !
143
+ {% else %}
144
+ Get out !
145
+ {% endif_authorized_to %}
146
+ ```
147
+
148
+ ## License
149
+
150
+ Solid is released under the MIT license:
151
+
152
+ http://www.opensource.org/licenses/MIT
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
@@ -0,0 +1,2 @@
1
+ # Thanks to this file, the gem will be automatically loaded at startup.
2
+ require 'solid'
data/lib/solid.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'liquid'
2
+
3
+ module Solid
4
+ BASE_PATH = File.join(File.expand_path(File.dirname(__FILE__)), 'solid')
5
+
6
+ require File.join(BASE_PATH, 'extensions')
7
+
8
+ autoload :Argument, File.join(BASE_PATH, 'argument')
9
+ autoload :Arguments, File.join(BASE_PATH, 'arguments')
10
+ autoload :Block, File.join(BASE_PATH, 'block')
11
+ autoload :ConditionalBlock, File.join(BASE_PATH, 'conditional_block')
12
+ autoload :ContextError, File.join(BASE_PATH, 'context_error')
13
+ autoload :Element, File.join(BASE_PATH, 'element')
14
+ autoload :Iterable, File.join(BASE_PATH, 'iterable')
15
+ autoload :MethodWhitelist, File.join(BASE_PATH, 'method_whitelist')
16
+ autoload :Parser, File.join(BASE_PATH, 'parser')
17
+ autoload :Tag, File.join(BASE_PATH, 'tag')
18
+ autoload :Template, File.join(BASE_PATH, 'template')
19
+ autoload :VERSION, File.join(BASE_PATH, 'version')
20
+
21
+ if defined?(Rails) # Rails only features
22
+ autoload :ModelDrop, File.join(BASE_PATH, 'model_drop')
23
+ require File.join(BASE_PATH, 'engine')
24
+ end
25
+
26
+ require File.join(BASE_PATH, 'liquid_extensions')
27
+
28
+ class << self
29
+
30
+ def unproxify(object)
31
+ class_name = object.class.name
32
+ if class_name && class_name.end_with?('::LiquidDropClass')
33
+ return object.instance_variable_get('@object')
34
+ end
35
+ object
36
+ end
37
+
38
+ def to_liquid(object, context)
39
+ object = object.to_liquid
40
+ object.context = context if object.respond_to?(:context=)
41
+ object
42
+ end
43
+
44
+ end
45
+
46
+ SyntaxError = Class.new(Liquid::SyntaxError)
47
+
48
+ end
@@ -0,0 +1,26 @@
1
+ class Solid::Arguments
2
+ include Enumerable
3
+
4
+ def self.parse(string)
5
+ new("[#{string}]").parse!
6
+ end
7
+
8
+ attr_accessor :values
9
+
10
+ def initialize(string)
11
+ @string = string
12
+ end
13
+
14
+ def parse!
15
+ self.values = Solid::Parser.parse(@string)
16
+ self
17
+ end
18
+
19
+ def each(*args, &block)
20
+ values.each(*args, &block)
21
+ end
22
+
23
+ def interpolate(context)
24
+ values.evaluate(context)
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ class Solid::Block < Liquid::Block
2
+
3
+ include Solid::Element
4
+
5
+ def render(context)
6
+ with_context(context) do
7
+ display(*arguments.interpolate(context)) do
8
+ super
9
+ end
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,35 @@
1
+ class Solid::ConditionalBlock < Liquid::Block
2
+ include Solid::Element
3
+
4
+ def initialize(tag_name, variable, tokens, context = {})
5
+ @blocks = []
6
+ push_block!
7
+ super
8
+ end
9
+
10
+ def render(context)
11
+ with_context(context) do
12
+ display(*arguments.interpolate(context)) do |condition_satisfied|
13
+ block = condition_satisfied ? @blocks.first : @blocks[1]
14
+ render_all(block, context) if block
15
+ end
16
+ end
17
+ end
18
+
19
+ def unknown_tag(tag, markup, tokens, context = {})
20
+ if tag == 'else'
21
+ push_block!
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def push_block!
30
+ block = []
31
+ @blocks.push(block)
32
+ @nodelist = block
33
+ end
34
+
35
+ end
@@ -0,0 +1,2 @@
1
+ class Solid::ContextError < Exception
2
+ end
@@ -0,0 +1,24 @@
1
+ Solid::MethodWhitelist
2
+ .allow(
3
+ BasicObject => [:!, :!=, :==],
4
+ Object => [:present?, :blank?, :in?, :to_json],
5
+ Kernel => [:nil?, :!~],
6
+ Module => [:==],
7
+ Enumerable => [:sort],
8
+ Comparable => [:<, :<=, :==, :>, :>=, :between?],
9
+ Numeric => [:blank?, :+@,
10
+ :second, :seconds, :minute, :minutes, :hour, :hours, :day, :days, :week, :weeks,
11
+ :bytes, :kilobytes, :megabytes, :gigabytes, :terabytes, :petabytes, :exabytes],
12
+ Integer => [:multiple_of?, :month, :months, :year, :years, :to_json],
13
+ ).deny(
14
+ Module => [:const_get, :const_set, :const_defined?, :freeze, :ancestors],
15
+ Class => [:new, :allocate, :superclass],
16
+ )
17
+
18
+ if defined?(JSON::Ext::Generator::GeneratorMethods::Fixnum) &&
19
+ defined?(JSON::Ext::Generator::GeneratorMethods::Bignum)
20
+ Solid::MethodWhitelist.allow(
21
+ JSON::Ext::Generator::GeneratorMethods::Fixnum => [:to_json],
22
+ JSON::Ext::Generator::GeneratorMethods::Bignum => [:to_json],
23
+ )
24
+ end
@@ -0,0 +1,51 @@
1
+ module Solid::Element
2
+
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ base.send(:include, Solid::Iterable)
6
+ end
7
+
8
+ def initialize(tag_name, arguments_string, tokens, context = {})
9
+ super
10
+ @arguments = Solid::Arguments.parse(arguments_string)
11
+ end
12
+
13
+ def arguments
14
+ @arguments
15
+ end
16
+
17
+ def with_context(context)
18
+ previous_context = @current_context
19
+ @current_context = context
20
+ yield
21
+ ensure
22
+ @current_context = previous_context
23
+ end
24
+
25
+ def current_context
26
+ @current_context or raise Solid::ContextError.new("There is currently no context, do you forget to call render ?")
27
+ end
28
+
29
+ def display(*args)
30
+ raise NotImplementedError.new("Solid::Element implementations SHOULD define a #display method")
31
+ end
32
+
33
+ module ClassMethods
34
+
35
+ def tag_name(value=nil)
36
+ if value
37
+ @tag_name = value
38
+ Liquid::Template.register_tag(value.to_s, self)
39
+ end
40
+ @tag_name
41
+ end
42
+
43
+ def context_attribute(name)
44
+ define_method(name) do
45
+ Solid.unproxify(current_context[name.to_s])
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,4 @@
1
+ module Solid
2
+ class Engine < Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ module LiquidSafe
2
+ def to_liquid
3
+ self
4
+ end
5
+ end
6
+
7
+ class Symbol
8
+ include LiquidSafe
9
+ end
10
+
11
+ class Regexp
12
+ include LiquidSafe
13
+ end
14
+
15
+ class Time
16
+ extend LiquidSafe
17
+ end