locomotive_liquid 2.2.3 → 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,38 +2,44 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- Liquid is a template engine which I wrote for very specific requirements
5
+ Liquid is a template engine which was written with very specific requirements:
6
6
 
7
7
  * It has to have beautiful and simple markup. Template engines which don't produce good looking markup are no fun to use.
8
8
  * It needs to be non evaling and secure. Liquid templates are made so that users can edit them. You don't want to run code on your server which your users wrote.
9
- * It has to be stateless. Compile and render steps have to be seperate so that the expensive parsing and compiling can be done once and later on you can just render it passing in a hash with local variables and objects.
9
+ * It has to be stateless. Compile and render steps have to be separate so that the expensive parsing and compiling can be done once and later on you can just render it passing in a hash with local variables and objects.
10
10
 
11
- ## Why should I use Liquid
11
+ ## Why you should use Liquid
12
12
 
13
13
  * You want to allow your users to edit the appearance of your application but don't want them to run **insecure code on your server**.
14
- * You want to render templates directly from the database
15
- * You like smarty (PHP) style template engines
16
- * You need a template engine which does HTML just as well as emails
17
- * You don't like the markup of your current templating engine
14
+ * You want to render templates directly from the database.
15
+ * You like smarty (PHP) style template engines.
16
+ * You need a template engine which does HTML just as well as emails.
17
+ * You don't like the markup of your current templating engine.
18
18
 
19
19
  ## What does it look like?
20
20
 
21
- <ul id="products">
22
- {% for product in products %}
23
- <li>
24
- <h2>{{product.name}}</h2>
25
- Only {{product.price | price }}
26
-
27
- {{product.description | prettyprint | paragraph }}
28
- </li>
29
- {% endfor %}
30
- </ul>
21
+ ```html
22
+ <ul id="products">
23
+ {% for product in products %}
24
+ <li>
25
+ <h2>{{ product.name }}</h2>
26
+ Only {{ product.price | price }}
31
27
 
32
- ## Howto use Liquid
28
+ {{ product.description | prettyprint | paragraph }}
29
+ </li>
30
+ {% endfor %}
31
+ </ul>
32
+ ```
33
+
34
+ ## How to use Liquid
35
+ >>>>>>> upstream/master
33
36
 
34
37
  Liquid supports a very simple API based around the Liquid::Template class.
35
38
  For standard use you can just pass it the content of a file and call render with a parameters hash.
36
39
 
40
+ ```ruby
41
+ @template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
42
+ @template.render('name' => 'tobi') # => "hi tobi"
43
+ ```
37
44
 
38
- @template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
39
- @template.render( 'name' => 'tobi' ) # => "hi tobi"
45
+ [![Build Status](https://secure.travis-ci.org/Shopify/liquid.png)](http://travis-ci.org/Shopify/liquid)
data/Rakefile CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
+
2
3
  require 'rubygems'
3
4
  require 'bundler/setup'
4
5
 
5
6
  require 'rake'
7
+ require 'rake/testtask'
6
8
  require 'rspec'
7
9
  require 'rspec/core/rake_task'
8
10
  require 'rubygems/package_task'
@@ -27,14 +29,21 @@ RSpec::Core::RakeTask.new('spec:progress') do |spec|
27
29
  spec.pattern = "spec/**/*_spec.rb"
28
30
  end
29
31
 
30
- task :default => :spec
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << '.' << 'lib' << 'test'
34
+ t.test_files = FileList['test/liquid/**/*_test.rb']
35
+ t.verbose = false
36
+ end
37
+
38
+ task :default => [:spec, :test]
31
39
 
32
40
  gemspec = eval(File.read('locomotive_liquid.gemspec'))
41
+
33
42
  Gem::PackageTask.new(gemspec) do |pkg|
34
43
  pkg.gem_spec = gemspec
35
44
  end
36
45
 
37
- desc "build the gem and release it to rubygems.org"
46
+ desc "Build the gem and release it to rubygems.org"
38
47
  task :release => :gem do
39
48
  puts "Tagging #{gemspec.version}..."
40
49
  system "git tag -a #{gemspec.version} -m 'Tagging #{gemspec.version}'"
@@ -44,20 +53,30 @@ task :release => :gem do
44
53
  system "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
45
54
  end
46
55
 
47
- namespace :profile do
48
-
49
- task :default => [:run]
56
+ namespace :benchmark do
50
57
 
51
- desc "Run the liquid profile/perforamce coverage"
58
+ desc "Run the liquid benchmark"
52
59
  task :run do
60
+ ruby "./performance/benchmark.rb"
61
+ end
53
62
 
54
- ruby "performance/shopify.rb"
63
+ end
64
+
65
+ namespace :profile do
55
66
 
67
+ desc "Run the liquid profile/performance coverage"
68
+ task :run do
69
+ ruby "./performance/profile.rb"
56
70
  end
57
71
 
58
72
  desc "Run KCacheGrind"
59
73
  task :grind => :run do
60
- system "kcachegrind /tmp/liquid.rubyprof_calltreeprinter.txt"
74
+ system "qcachegrind /tmp/liquid.rubyprof_calltreeprinter.txt"
61
75
  end
76
+
62
77
  end
63
78
 
79
+ desc "Run example"
80
+ task :example do
81
+ ruby "-w -d -Ilib example/server/server.rb"
82
+ end
@@ -19,8 +19,6 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
- $LOAD_PATH.unshift(File.dirname(__FILE__))
23
-
24
22
  module Liquid
25
23
  FilterSeparator = /\|/
26
24
  ArgumentSeparator = ','
@@ -34,23 +32,23 @@ module Liquid
34
32
  VariableEnd = /\}\}/
35
33
  VariableIncompleteEnd = /\}\}?/
36
34
  QuotedString = /"[^"]*"|'[^']*'/
37
- QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/
38
- StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s,\|,\:,\,]+/
39
- FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/
40
- OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/
41
- SpacelessFilter = /^(?:'[^']+'|"[^"]+"|[^'"])*#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/
42
- Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/
43
- TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
35
+ QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o
36
+ StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s|:,]+/
37
+ FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/o
38
+ OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/o
39
+ SpacelessFilter = /^(?:'[^']+'|"[^"]+"|[^'"])*#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/o
40
+ Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/o
41
+ TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/o
44
42
  AnyStartingTag = /\{\{|\{\%/
45
- PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/
46
- TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/
47
- VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/
48
- LiteralShorthand = /^(?:\{\{\{\s?)(.*?)(?:\s*\}\}\})$/
43
+ PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/o
44
+ TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/o
45
+ VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o
49
46
  end
50
47
 
51
48
  require 'liquid/drop'
52
49
  require 'liquid/extensions'
53
50
  require 'liquid/errors'
51
+ require 'liquid/interrupts'
54
52
  require 'liquid/strainer'
55
53
  require 'liquid/context'
56
54
  require 'liquid/tag'
@@ -63,6 +61,7 @@ require 'liquid/htmltags'
63
61
  require 'liquid/standardfilters'
64
62
  require 'liquid/condition'
65
63
  require 'liquid/module_ex'
64
+ require 'liquid/utils'
66
65
 
67
66
  # Load all the tags of the standard library
68
67
  #
@@ -1,10 +1,10 @@
1
1
  module Liquid
2
2
 
3
3
  class Block < Tag
4
- IsTag = /^#{TagStart}/
5
- IsVariable = /^#{VariableStart}/
6
- FullToken = /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/
7
- ContentOfVariable = /^#{VariableStart}(.*)#{VariableEnd}$/
4
+ IsTag = /^#{TagStart}/o
5
+ IsVariable = /^#{VariableStart}/o
6
+ FullToken = /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/o
7
+ ContentOfVariable = /^#{VariableStart}(.*)#{VariableEnd}$/o
8
8
 
9
9
  def parse(tokens)
10
10
  @nodelist ||= []
@@ -89,13 +89,27 @@ module Liquid
89
89
  end
90
90
 
91
91
  def render_all(list, context)
92
- list.collect do |token|
92
+ output = []
93
+ list.each do |token|
94
+ # Break out if we have any unhanded interrupts.
95
+ break if context.has_interrupt?
96
+
93
97
  begin
94
- token.respond_to?(:render) ? token.render(context) : token
95
- rescue Exception => e
96
- context.handle_error(e)
98
+ # If we get an Interrupt that means the block must stop processing. An
99
+ # Interrupt is any command that stops block execution such as {% break %}
100
+ # or {% continue %}
101
+ if token.is_a? Continue or token.is_a? Break
102
+ context.push_interrupt(token.interrupt)
103
+ break
104
+ end
105
+
106
+ output << (token.respond_to?(:render) ? token.render(context) : token)
107
+ rescue ::StandardError => e
108
+ output << (context.handle_error(e))
97
109
  end
98
110
  end
111
+
112
+ output.join
99
113
  end
100
114
  end
101
115
  end
@@ -22,6 +22,8 @@ module Liquid
22
22
  @errors = []
23
23
  @rethrow_errors = rethrow_errors
24
24
  squash_instance_assigns_with_environments
25
+
26
+ @interrupts = []
25
27
  end
26
28
 
27
29
  def strainer
@@ -41,6 +43,21 @@ module Liquid
41
43
  end
42
44
  end
43
45
 
46
+ # are there any not handled interrupts?
47
+ def has_interrupt?
48
+ !@interrupts.empty?
49
+ end
50
+
51
+ # push an interrupt to the stack. this interrupt is considered not handled.
52
+ def push_interrupt(e)
53
+ @interrupts.push(e)
54
+ end
55
+
56
+ # pop an interrupt from the stack
57
+ def pop_interrupt
58
+ @interrupts.pop
59
+ end
60
+
44
61
  def handle_error(e)
45
62
  errors.push(e)
46
63
  raise if @rethrow_errors
@@ -63,8 +80,8 @@ module Liquid
63
80
 
64
81
  # Push new local scope on the stack. use <tt>Context#stack</tt> instead
65
82
  def push(new_scope={})
66
- raise StackLevelError, "Nesting too deep" if @scopes.length > 100
67
83
  @scopes.unshift(new_scope)
84
+ raise StackLevelError, "Nesting too deep" if @scopes.length > 100
68
85
  end
69
86
 
70
87
  # Merge a hash of variables in the current local scope
@@ -86,17 +103,11 @@ module Liquid
86
103
  # end
87
104
  #
88
105
  # context['var] #=> nil
89
- def stack(new_scope={},&block)
90
- result = nil
106
+ def stack(new_scope={})
91
107
  push(new_scope)
92
-
93
- begin
94
- result = yield
95
- ensure
96
- pop
97
- end
98
-
99
- result
108
+ yield
109
+ ensure
110
+ pop
100
111
  end
101
112
 
102
113
  def clear_instance_assigns
@@ -117,6 +128,15 @@ module Liquid
117
128
  end
118
129
 
119
130
  private
131
+
132
+ LITERALS = {
133
+ nil => nil, 'nil' => nil, 'null' => nil, '' => nil,
134
+ 'true' => true,
135
+ 'false' => false,
136
+ 'blank' => :blank?,
137
+ 'empty' => :empty?
138
+ }
139
+
120
140
  # Look up variable, either resolve directly after considering the name. We can directly handle
121
141
  # Strings, digits, floats and booleans (true,false).
122
142
  # If no match is made we lookup the variable in the current scope and
@@ -126,29 +146,23 @@ module Liquid
126
146
  # Example:
127
147
  # products == empty #=> products.empty?
128
148
  def resolve(key)
129
- case key
130
- when nil, 'nil', 'null', ''
131
- nil
132
- when 'true'
133
- true
134
- when 'false'
135
- false
136
- when 'blank'
137
- :blank?
138
- when 'empty' # Single quoted strings
139
- :empty?
140
- when /^'(.*)'$/ # Double quoted strings
141
- $1.to_s
142
- when /^"(.*)"$/ # Integer and floats
143
- $1.to_s
144
- when /^(\d+)$/ # Ranges
145
- $1.to_i
146
- when /^\((\S+)\.\.(\S+)\)$/ # Floats
147
- (resolve($1).to_i..resolve($2).to_i)
148
- when /^(\d[\d\.]+)$/
149
- $1.to_f
149
+ if LITERALS.key?(key)
150
+ LITERALS[key]
150
151
  else
151
- variable(key)
152
+ case key
153
+ when /^'(.*)'$/ # Single quoted strings
154
+ $1
155
+ when /^"(.*)"$/ # Double quoted strings
156
+ $1
157
+ when /^(-?\d+)$/ # Integer and floats
158
+ $1.to_i
159
+ when /^\((\S+)\.\.(\S+)\)$/ # Ranges
160
+ (resolve($1).to_i..resolve($2).to_i)
161
+ when /^(-?\d[\d\.]+)$/ # Floats
162
+ $1.to_f
163
+ else
164
+ variable(key)
165
+ end
152
166
  end
153
167
  end
154
168
 
@@ -1,40 +1,40 @@
1
1
  module Liquid
2
2
 
3
- # A drop in liquid is a class which allows you to to export DOM like things to liquid
3
+ # A drop in liquid is a class which allows you to export DOM like things to liquid.
4
4
  # Methods of drops are callable.
5
- # The main use for liquid drops is the implement lazy loaded objects.
5
+ # The main use for liquid drops is to implement lazy loaded objects.
6
6
  # If you would like to make data available to the web designers which you don't want loaded unless needed then
7
- # a drop is a great way to do that
7
+ # a drop is a great way to do that.
8
8
  #
9
9
  # Example:
10
10
  #
11
- # class ProductDrop < Liquid::Drop
12
- # def top_sales
13
- # Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
11
+ # class ProductDrop < Liquid::Drop
12
+ # def top_sales
13
+ # Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
14
+ # end
14
15
  # end
15
- # end
16
16
  #
17
- # tmpl = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {%endfor%} ' )
18
- # tmpl.render('product' => ProductDrop.new ) # will invoke top_sales query.
17
+ # tmpl = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {%endfor%} ' )
18
+ # tmpl.render('product' => ProductDrop.new ) # will invoke top_sales query.
19
19
  #
20
20
  # Your drop can either implement the methods sans any parameters or implement the before_method(name) method which is a
21
- # catch all
21
+ # catch all.
22
22
  class Drop
23
23
  attr_writer :context
24
24
 
25
+ EMPTY_STRING = ''.freeze
26
+
25
27
  # Catch all for the method
26
28
  def before_method(method)
27
29
  nil
28
30
  end
29
31
 
30
32
  # called by liquid to invoke a drop
31
- def invoke_drop(method)
32
- # for backward compatibility with Ruby 1.8
33
- methods = self.class.public_instance_methods.map { |m| m.to_s }
34
- if methods.include?(method.to_s)
35
- send(method.to_sym)
33
+ def invoke_drop(method_or_key)
34
+ if method_or_key && method_or_key != EMPTY_STRING && self.class.public_method_defined?(method_or_key.to_s.to_sym)
35
+ send(method_or_key.to_s.to_sym)
36
36
  else
37
- before_method(method)
37
+ before_method(method_or_key)
38
38
  end
39
39
  end
40
40
 
@@ -8,4 +8,4 @@ module Liquid
8
8
  class StandardError < Error; end
9
9
  class SyntaxError < Error; end
10
10
  class StackLevelError < Error; end
11
- end
11
+ end
@@ -43,14 +43,20 @@ class Date # :nodoc:
43
43
  end
44
44
  end
45
45
 
46
- def true.to_liquid # :nodoc:
47
- self
46
+ class TrueClass
47
+ def to_liquid # :nodoc:
48
+ self
49
+ end
48
50
  end
49
51
 
50
- def false.to_liquid # :nodoc:
51
- self
52
+ class FalseClass
53
+ def to_liquid # :nodoc:
54
+ self
55
+ end
52
56
  end
53
57
 
54
- def nil.to_liquid # :nodoc:
55
- self
56
- end
58
+ class NilClass
59
+ def to_liquid # :nodoc:
60
+ self
61
+ end
62
+ end