liquid 1.9.0 → 2.0.0

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 (48) hide show
  1. data/Manifest.txt +0 -31
  2. data/Rakefile +1 -1
  3. data/lib/extras/liquid_view.rb +15 -2
  4. data/lib/liquid.rb +15 -15
  5. data/lib/liquid/block.rb +1 -2
  6. data/lib/liquid/context.rb +89 -99
  7. data/lib/liquid/drop.rb +6 -4
  8. data/lib/liquid/errors.rb +1 -0
  9. data/lib/liquid/standardfilters.rb +56 -11
  10. data/lib/liquid/strainer.rb +1 -1
  11. data/lib/liquid/tags/assign.rb +1 -1
  12. data/lib/liquid/tags/case.rb +2 -2
  13. data/lib/liquid/tags/cycle.rb +3 -4
  14. data/lib/liquid/tags/for.rb +53 -35
  15. data/lib/liquid/tags/if.rb +3 -3
  16. data/lib/liquid/template.rb +8 -7
  17. data/lib/liquid/variable.rb +10 -11
  18. metadata +5 -35
  19. data/example/server/example_servlet.rb +0 -37
  20. data/example/server/liquid_servlet.rb +0 -28
  21. data/example/server/server.rb +0 -12
  22. data/example/server/templates/index.liquid +0 -6
  23. data/example/server/templates/products.liquid +0 -45
  24. data/test/block_test.rb +0 -58
  25. data/test/condition_test.rb +0 -109
  26. data/test/context_test.rb +0 -418
  27. data/test/drop_test.rb +0 -141
  28. data/test/error_handling_test.rb +0 -78
  29. data/test/extra/breakpoint.rb +0 -547
  30. data/test/extra/caller.rb +0 -80
  31. data/test/file_system_test.rb +0 -30
  32. data/test/filter_test.rb +0 -98
  33. data/test/helper.rb +0 -20
  34. data/test/html_tag_test.rb +0 -31
  35. data/test/if_else_test.rb +0 -127
  36. data/test/include_tag_test.rb +0 -114
  37. data/test/module_ex_test.rb +0 -89
  38. data/test/output_test.rb +0 -121
  39. data/test/parsing_quirks_test.rb +0 -29
  40. data/test/regexp_test.rb +0 -40
  41. data/test/security_test.rb +0 -41
  42. data/test/standard_filter_test.rb +0 -126
  43. data/test/standard_tag_test.rb +0 -383
  44. data/test/statements_test.rb +0 -137
  45. data/test/strainer_test.rb +0 -16
  46. data/test/template_test.rb +0 -26
  47. data/test/unless_else_test.rb +0 -27
  48. data/test/variable_test.rb +0 -135
@@ -28,10 +28,12 @@ module Liquid
28
28
  end
29
29
 
30
30
  # called by liquid to invoke a drop
31
- def invoke_drop(method)
32
- result = before_method(method)
33
- result ||= send(method.to_sym) if self.class.public_instance_methods.include?(method.to_s)
34
- result
31
+ def invoke_drop(method)
32
+ if self.class.public_instance_methods.include?(method.to_s)
33
+ send(method.to_sym)
34
+ else
35
+ before_method(method)
36
+ end
35
37
  end
36
38
 
37
39
  def has_key?(name)
@@ -7,4 +7,5 @@ module Liquid
7
7
  class FileSystemError < Error; end
8
8
  class StandardError < Error; end
9
9
  class SyntaxError < Error; end
10
+ class StackLevelError < Error; end
10
11
  end
@@ -63,9 +63,27 @@ module Liquid
63
63
  end
64
64
 
65
65
  # Sort elements of the array
66
- def sort(input)
67
- [input].flatten.sort
66
+ # provide optional property with which to sort an array of hashes or drops
67
+ def sort(input, property = nil)
68
+ ary = [input].flatten
69
+ if property.nil?
70
+ ary.sort
71
+ elsif ary.first.respond_to?('[]') and !ary.first[property].nil?
72
+ ary.sort {|a,b| a[property] <=> b[property] }
73
+ elsif ary.first.respond_to?(property)
74
+ ary.sort {|a,b| a.send(property) <=> b.send(property) }
75
+ end
68
76
  end
77
+
78
+ # map/collect on a given property
79
+ def map(input, property)
80
+ ary = [input].flatten
81
+ if ary.first.respond_to?('[]') and !ary.first[property].nil?
82
+ ary.map {|e| e[property] }
83
+ elsif ary.first.respond_to?(property)
84
+ ary.map {|e| e.send(property) }
85
+ end
86
+ end
69
87
 
70
88
  # Replace occurrences of a string with another
71
89
  def replace(input, string, replacement = '')
@@ -85,7 +103,17 @@ module Liquid
85
103
  # remove the first occurrences of a substring
86
104
  def remove_first(input, string)
87
105
  input.to_s.sub(string, '')
88
- end
106
+ end
107
+
108
+ # add one string to another
109
+ def append(input, string)
110
+ input.to_s + string.to_s
111
+ end
112
+
113
+ # prepend a string to another
114
+ def prepend(input, string)
115
+ string.to_s + input.to_s
116
+ end
89
117
 
90
118
  # Add <br /> tags in front of all newlines in input string
91
119
  def newline_to_br(input)
@@ -126,16 +154,13 @@ module Liquid
126
154
  return input.to_s
127
155
  end
128
156
 
129
- date = case input
130
- when String
131
- Time.parse(input)
132
- when Date, Time, DateTime
133
- input
157
+ date = input.is_a?(String) ? Time.parse(input) : input
158
+
159
+ if date.respond_to?(:strftime)
160
+ date.strftime(format.to_s)
134
161
  else
135
- return input
162
+ input
136
163
  end
137
-
138
- date.strftime(format.to_s)
139
164
  rescue => e
140
165
  input
141
166
  end
@@ -158,6 +183,26 @@ module Liquid
158
183
  array.last if array.respond_to?(:last)
159
184
  end
160
185
 
186
+ # addition
187
+ def plus(input, operand)
188
+ input + operand if input.respond_to?('+')
189
+ end
190
+
191
+ # subtraction
192
+ def minus(input, operand)
193
+ input - operand if input.respond_to?('-')
194
+ end
195
+
196
+ # multiplication
197
+ def times(input, operand)
198
+ input * operand if input.respond_to?('*')
199
+ end
200
+
201
+ # division
202
+ def divided_by(input, operand)
203
+ input / operand if input.respond_to?('/')
204
+ end
205
+
161
206
  end
162
207
 
163
208
  Template.register_filter(StandardFilters)
@@ -34,7 +34,7 @@ module Liquid
34
34
  strainer
35
35
  end
36
36
 
37
- def respond_to?(method)
37
+ def respond_to?(method, include_private = false)
38
38
  method_name = method.to_s
39
39
  return false if method_name =~ INTERNAL_METHOD
40
40
  return false if @@required_methods.include?(method_name)
@@ -9,7 +9,7 @@ module Liquid
9
9
  # {{ monkey }}
10
10
  #
11
11
  class Assign < Tag
12
- Syntax = /(#{VariableSignature}+)\s*=\s*(#{QuotedFragment}+)/
12
+ Syntax = /(#{VariableSignature}+)\s*=\s*(#{Expression}+)/
13
13
 
14
14
  def initialize(tag_name, markup, tokens)
15
15
  if markup =~ Syntax
@@ -1,7 +1,7 @@
1
1
  module Liquid
2
2
  class Case < Block
3
- Syntax = /(#{QuotedFragment})/
4
- WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/
3
+ Syntax = /(#{Expression})/
4
+ WhenSyntax = /(#{Expression})(?:(?:\s+or\s+|\s*\,\s*)(#{Expression}.*))?/
5
5
 
6
6
  def initialize(tag_name, markup, tokens)
7
7
  @blocks = []
@@ -13,8 +13,8 @@ module Liquid
13
13
  # <div class="green"> Item five</div>
14
14
  #
15
15
  class Cycle < Tag
16
- SimpleSyntax = /#{QuotedFragment}/
17
- NamedSyntax = /(#{QuotedFragment})\s*\:\s*(.*)/
16
+ SimpleSyntax = /^#{Expression}/
17
+ NamedSyntax = /^(#{Expression})\s*\:\s*(.*)/
18
18
 
19
19
  def initialize(tag_name, markup, tokens)
20
20
  case markup
@@ -27,7 +27,6 @@ module Liquid
27
27
  else
28
28
  raise SyntaxError.new("Syntax Error in 'cycle' - Valid syntax: cycle [name :] var [, var2, var3 ...]")
29
29
  end
30
-
31
30
  super
32
31
  end
33
32
 
@@ -49,7 +48,7 @@ module Liquid
49
48
 
50
49
  def variables_from_string(markup)
51
50
  markup.split(',').collect do |var|
52
- var =~ /\s*(#{QuotedFragment})\s*/
51
+ var =~ /\s*(#{Expression})\s*/
53
52
  $1 ? $1 : nil
54
53
  end.compact
55
54
  end
@@ -20,7 +20,9 @@ module Liquid
20
20
  #
21
21
  # {% for item in collection limit:5 offset:10 %}
22
22
  # {{ item.name }}
23
- # {% end %}
23
+ # {% end %}
24
+ #
25
+ # To reverse the for loop simply use {% for item in collection reversed %}
24
26
  #
25
27
  # == Available variables:
26
28
  #
@@ -40,13 +42,14 @@ module Liquid
40
42
  # forloop.last:: Returns true if the item is the last item.
41
43
  #
42
44
  class For < Block
43
- Syntax = /(\w+)\s+in\s+(#{VariableSignature}+)/
45
+ Syntax = /(\w+)\s+in\s+(#{Expression}+)\s*(reversed)?/
44
46
 
45
47
  def initialize(tag_name, markup, tokens)
46
48
  if markup =~ Syntax
47
49
  @variable_name = $1
48
50
  @collection_name = $2
49
- @name = "#{$1}-#{$2}"
51
+ @name = "#{$1}-#{$2}"
52
+ @reversed = $3
50
53
  @attributes = {}
51
54
  markup.scan(TagAttributes) do |key, value|
52
55
  @attributes[key] = value
@@ -64,35 +67,33 @@ module Liquid
64
67
  collection = context[@collection_name]
65
68
  collection = collection.to_a if collection.is_a?(Range)
66
69
 
67
- return '' if collection.nil? or collection.empty?
68
-
69
- range = (0..collection.length)
70
-
71
- if @attributes['limit'] or @attributes['offset']
72
- offset = 0
73
- if @attributes['offset'] == 'continue'
74
- offset = context.registers[:for][@name]
75
- else
76
- offset = context[@attributes['offset']] || 0
77
- end
78
- limit = context[@attributes['limit']]
79
-
80
- range_end = limit ? offset + limit : collection.length
81
- range = (offset..range_end-1)
82
-
83
- # Save the range end in the registers so that future calls to
84
- # offset:continue have something to pick up
85
- context.registers[:for][@name] = range_end
70
+ return '' unless collection.respond_to?(:each)
71
+
72
+ from = if @attributes['offset'] == 'continue'
73
+ context.registers[:for][@name].to_i
74
+ else
75
+ context[@attributes['offset']].to_i
86
76
  end
87
-
88
- result = []
89
- segment = collection[range]
90
- return '' if segment.nil?
77
+
78
+ limit = context[@attributes['limit']]
79
+ to = limit ? limit.to_i + from : nil
80
+
81
+
82
+ segment = slice_collection_using_each(collection, from, to)
83
+
84
+ return '' if segment.empty?
85
+
86
+ segment.reverse! if @reversed
91
87
 
88
+ result = []
89
+
90
+ length = segment.length
91
+
92
+ # Store our progress through the collection for the continue flag
93
+ context.registers[:for][@name] = from + segment.length
94
+
92
95
  context.stack do
93
- length = segment.length
94
-
95
- segment.each_with_index do |item, index|
96
+ segment.each_with_index do |item, index|
96
97
  context[@variable_name] = item
97
98
  context['forloop'] = {
98
99
  'name' => @name,
@@ -103,15 +104,32 @@ module Liquid
103
104
  'rindex0' => length - index -1,
104
105
  'first' => (index == 0),
105
106
  'last' => (index == length - 1) }
106
-
107
+
107
108
  result << render_all(@nodelist, context)
108
109
  end
109
110
  end
110
-
111
- # Store position of last element we rendered. This allows us to do
112
-
113
- result
114
- end
111
+ result
112
+ end
113
+
114
+ def slice_collection_using_each(collection, from, to)
115
+ segments = []
116
+ index = 0
117
+ yielded = 0
118
+ collection.each do |item|
119
+
120
+ if to && to <= index
121
+ break
122
+ end
123
+
124
+ if from <= index
125
+ segments << item
126
+ end
127
+
128
+ index += 1
129
+ end
130
+
131
+ segments
132
+ end
115
133
  end
116
134
 
117
135
  Template.register_tag('for', For)
@@ -13,7 +13,7 @@ module Liquid
13
13
  #
14
14
  class If < Block
15
15
  SyntaxHelp = "Syntax Error in tag 'if' - Valid syntax: if [expression]"
16
- Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/
16
+ Syntax = /(#{Expression})\s*([=!<>a-z_]+)?\s*(#{Expression})?/
17
17
 
18
18
  def initialize(tag_name, markup, tokens)
19
19
 
@@ -51,14 +51,14 @@ module Liquid
51
51
  else
52
52
 
53
53
  expressions = markup.split(/\b(and|or)\b/).reverse
54
- raise SyntaxHelp unless expressions.shift =~ Syntax
54
+ raise(SyntaxError, SyntaxHelp) unless expressions.shift =~ Syntax
55
55
 
56
56
  condition = Condition.new($1, $2, $3)
57
57
 
58
58
  while not expressions.empty?
59
59
  operator = expressions.shift
60
60
 
61
- raise SyntaxHelp unless expressions.shift.to_s =~ Syntax
61
+ raise(SyntaxError, SyntaxHelp) unless expressions.shift.to_s =~ Syntax
62
62
 
63
63
  new_condition = Condition.new($1, $2, $3)
64
64
  new_condition.send(operator.to_sym, condition)
@@ -83,7 +83,7 @@ module Liquid
83
83
  # filters and tags and might be useful to integrate liquid more with its host application
84
84
  #
85
85
  def render(*args)
86
- return '' if @root.nil?
86
+ return '' if @root.nil?
87
87
 
88
88
  context = case args.first
89
89
  when Liquid::Context
@@ -107,17 +107,17 @@ module Liquid
107
107
 
108
108
  if options[:filters]
109
109
  context.add_filters(options[:filters])
110
- end
110
+ end
111
+
111
112
  when Module
112
113
  context.add_filters(args.pop)
113
114
  when Array
114
115
  context.add_filters(args.pop)
115
116
  end
116
-
117
-
118
- # render the nodelist.
119
- # for performance reasons we get a array back here. to_s will make a string out of it
117
+
120
118
  begin
119
+ # render the nodelist.
120
+ # for performance reasons we get a array back here. join will make a string out of it
121
121
  @root.render(context).join
122
122
  ensure
123
123
  @errors = context.errors
@@ -131,7 +131,8 @@ module Liquid
131
131
  private
132
132
 
133
133
  # Uses the <tt>Liquid::TemplateParser</tt> regexp to tokenize the passed source
134
- def tokenize(source)
134
+ def tokenize(source)
135
+ source = source.source if source.respond_to?(:source)
135
136
  return [] if source.to_s.empty?
136
137
  tokens = source.split(TemplateParser)
137
138
 
@@ -1,5 +1,5 @@
1
1
  module Liquid
2
-
2
+
3
3
  # Holds variables. Variables are only loaded "just in time"
4
4
  # and are not evaluated as part of the render stage
5
5
  #
@@ -10,30 +10,29 @@ module Liquid
10
10
  #
11
11
  # {{ user | link }}
12
12
  #
13
- class Variable
13
+ class Variable
14
14
  attr_accessor :filters, :name
15
-
15
+
16
16
  def initialize(markup)
17
- @markup = markup
17
+ @markup = markup
18
18
  @name = nil
19
19
  @filters = []
20
20
  if match = markup.match(/\s*(#{QuotedFragment})/)
21
21
  @name = match[1]
22
- if markup.match(/#{FilterSperator}\s*(.*)/)
23
- filters = Regexp.last_match(1).split(/#{FilterSperator}/)
24
-
22
+ if markup.match(/#{FilterSeparator}\s*(.*)/)
23
+ filters = Regexp.last_match(1).split(/#{FilterSeparator}/)
25
24
  filters.each do |f|
26
25
  if matches = f.match(/\s*(\w+)/)
27
26
  filtername = matches[1]
28
- filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*(#{QuotedFragment})/).flatten
27
+ filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*(#{QuotedFragment})/).flatten
29
28
  @filters << [filtername.to_sym, filterargs]
30
29
  end
31
30
  end
32
31
  end
33
32
  end
34
- end
33
+ end
35
34
 
36
- def render(context)
35
+ def render(context)
37
36
  return '' if @name.nil?
38
37
  output = context[@name]
39
38
  @filters.inject(output) do |output, filter|
@@ -45,7 +44,7 @@ module Liquid
45
44
  rescue FilterNotFound
46
45
  raise FilterNotFound, "Error - filter '#{filter[0]}' in '#{@markup.strip}' could not be found."
47
46
  end
48
- end
47
+ end
49
48
  output
50
49
  end
51
50
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: liquid
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Luetke
@@ -9,17 +9,18 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-05-04 00:00:00 -04:00
12
+ date: 2009-03-10 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: hoe
17
+ type: :development
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
20
21
  - - ">="
21
22
  - !ruby/object:Gem::Version
22
- version: 1.5.1
23
+ version: 1.8.2
23
24
  version:
24
25
  description: A secure non evaling end user template engine with aesthetic markup.
25
26
  email: tobi@leetsoft.com
@@ -38,11 +39,6 @@ files:
38
39
  - Manifest.txt
39
40
  - README.txt
40
41
  - Rakefile
41
- - example/server/example_servlet.rb
42
- - example/server/liquid_servlet.rb
43
- - example/server/server.rb
44
- - example/server/templates/index.liquid
45
- - example/server/templates/products.liquid
46
42
  - init.rb
47
43
  - lib/extras/liquid_view.rb
48
44
  - lib/liquid.rb
@@ -71,32 +67,6 @@ files:
71
67
  - lib/liquid/tags/unless.rb
72
68
  - lib/liquid/template.rb
73
69
  - lib/liquid/variable.rb
74
- - test/block_test.rb
75
- - test/condition_test.rb
76
- - test/context_test.rb
77
- - test/drop_test.rb
78
- - test/error_handling_test.rb
79
- - test/extra/breakpoint.rb
80
- - test/extra/caller.rb
81
- - test/file_system_test.rb
82
- - test/filter_test.rb
83
- - test/helper.rb
84
- - test/html_tag_test.rb
85
- - test/if_else_test.rb
86
- - test/include_tag_test.rb
87
- - test/module_ex_test.rb
88
- - test/output_test.rb
89
- - test/parsing_quirks_test.rb
90
- - test/regexp_test.rb
91
- - test/security_test.rb
92
- - test/standard_filter_test.rb
93
- - test/standard_tag_test.rb
94
- - test/statements_test.rb
95
- - test/strainer_test.rb
96
- - test/template_test.rb
97
- - test/test_helper.rb
98
- - test/unless_else_test.rb
99
- - test/variable_test.rb
100
70
  has_rdoc: true
101
71
  homepage: http://www.liquidmarkup.org
102
72
  post_install_message:
@@ -120,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
90
  requirements: []
121
91
 
122
92
  rubyforge_project: liquid
123
- rubygems_version: 1.1.0
93
+ rubygems_version: 1.3.1
124
94
  signing_key:
125
95
  specification_version: 2
126
96
  summary: A secure non evaling end user template engine with aesthetic markup.