liquid 2.5.5 → 2.6.0.rc1
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.
- checksums.yaml +13 -5
- data/History.md +26 -11
- data/MIT-LICENSE +1 -1
- data/README.md +6 -0
- data/lib/extras/liquid_view.rb +9 -9
- data/lib/liquid.rb +1 -0
- data/lib/liquid/block.rb +16 -5
- data/lib/liquid/context.rb +17 -9
- data/lib/liquid/document.rb +1 -1
- data/lib/liquid/errors.rb +2 -1
- data/lib/liquid/file_system.rb +12 -12
- data/lib/liquid/htmltags.rb +1 -1
- data/lib/liquid/module_ex.rb +1 -1
- data/lib/liquid/standardfilters.rb +44 -22
- data/lib/liquid/strainer.rb +3 -3
- data/lib/liquid/tag.rb +1 -1
- data/lib/liquid/tags/assign.rb +14 -12
- data/lib/liquid/tags/break.rb +2 -2
- data/lib/liquid/tags/capture.rb +1 -0
- data/lib/liquid/tags/case.rb +28 -28
- data/lib/liquid/tags/continue.rb +1 -1
- data/lib/liquid/tags/cycle.rb +21 -21
- data/lib/liquid/tags/decrement.rb +7 -7
- data/lib/liquid/tags/for.rb +26 -26
- data/lib/liquid/tags/if.rb +3 -3
- data/lib/liquid/tags/ifchanged.rb +9 -9
- data/lib/liquid/tags/include.rb +42 -14
- data/lib/liquid/tags/increment.rb +7 -7
- data/lib/liquid/tags/raw.rb +5 -4
- data/lib/liquid/tags/unless.rb +8 -8
- data/lib/liquid/template.rb +11 -5
- data/lib/liquid/utils.rb +0 -1
- data/lib/liquid/variable.rb +2 -2
- data/lib/liquid/version.rb +4 -0
- data/test/liquid/assign_test.rb +1 -1
- data/test/liquid/condition_test.rb +1 -1
- data/test/liquid/hash_ordering_test.rb +25 -0
- data/test/liquid/output_test.rb +2 -2
- data/test/liquid/standard_filter_test.rb +18 -0
- data/test/liquid/tags/for_tag_test.rb +47 -34
- data/test/liquid/tags/html_tag_test.rb +2 -2
- data/test/liquid/tags/if_else_tag_test.rb +0 -6
- data/test/liquid/tags/include_tag_test.rb +28 -1
- data/test/liquid/tags/raw_tag_test.rb +11 -2
- data/test/liquid/template_test.rb +72 -0
- data/test/liquid/variable_test.rb +6 -0
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZTIwZjU2MjYxMGExMjE4NWMxM2YwMzdlOGY4ZThmNDFmZGMzZGYzMA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NTgxNTU0ODAxMGEyZGNlN2VkMDgxNDAxZGZkZmNiZjJjZjdhZmJiNA==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NjQ4YTI3NWZjYmE2ZTZiYzkzOTI1NDE0NTQ3M2EwN2Q4NGNiYjUxM2NjZmEy
|
10
|
+
NWNhMmVlNzY1YTM4NWU1NzNjYjAzMzI2NzhlOWYyMTkxYTA5NTkyYjBiMjJj
|
11
|
+
NzdkMDAzNzRjNmIwODk1MGJjNjNjZjY2MTg3MmVjNGM4NjhkMTU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MDQ1ZWE4MDM0NTU5MmFlOWU3ZmQzMWE2NTEyOTMzYjVjODViZDJkNGM3ODQ1
|
14
|
+
NmYyNDM1M2NiYjAzZmFhMThmNDIyOGJmN2E5NmFmODQ4YWY3MTY3MjZlMTg0
|
15
|
+
Y2I1ODVlMmJkZDQyYTMwOTY1MWM5ZDkyOTQ0NTA1MmE0NTEzYWQ=
|
data/History.md
CHANGED
@@ -1,16 +1,30 @@
|
|
1
1
|
# Liquid Version History
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
*
|
12
|
-
|
13
|
-
|
3
|
+
IMPORTANT: Liquid 2.6 is going to be the last version of Liquid which maintains explicit Ruby 1.8 compatability.
|
4
|
+
The following releases will only be tested against Ruby 1.9 and Ruby 2.0 and are likely to break on Ruby 1.8.
|
5
|
+
|
6
|
+
## 2.6.0 (not yet released)
|
7
|
+
|
8
|
+
* ...
|
9
|
+
* Bugfix for #106: fix example servlet [gnowoel]
|
10
|
+
* Bugfix for #97: strip_html filter supports multi-line tags [Jo Liss, joliss]
|
11
|
+
* Bugfix for #114: strip_html filter supports style tags [James Allardice, jamesallardice]
|
12
|
+
* Bugfix for #117: 'now' support for date filter in Ruby 1.9 [Notre Dame Webgroup, ndwebgroup]
|
13
|
+
* Bugfix for #166: truncate filter on UTF-8 strings with Ruby 1.8 [Florian Weingarten, fw42]
|
14
|
+
* Bugfix for #204: 'raw' parsing bug [Florian Weingarten, fw42]
|
15
|
+
* Bugfix for #150: 'for' parsing bug [Peter Schröder, phoet]
|
16
|
+
* Bugfix for #126: Strip CRLF in strip_newline [Peter Schröder, phoet]
|
17
|
+
* Allow a Liquid::Drop to be passed into Template#render [Daniel Huckstep, darkhelmet]
|
18
|
+
* Resource limits [Florian Weingarten, fw42]
|
19
|
+
* Add reverse filter [Jay Strybis, unreal]
|
20
|
+
* Add utf-8 support
|
21
|
+
* Use array instead of Hash to keep the registered filters [Tasos Stathopoulos, astathopoulos]
|
22
|
+
* Cache tokenized partial templates [Tom Burns, boourns]
|
23
|
+
* Avoid warnings in Ruby 1.9.3 [Marcus Stollsteimer, stomar]
|
24
|
+
* Better documentation for 'include' tag (closes #163) [Peter Schröder, phoet]
|
25
|
+
* Use of BigDecimal on filters to have better precision (closes #155) [Arthur Nogueira Neves, arthurnn]
|
26
|
+
|
27
|
+
## 2.5.3 / branch "2.5-stable"
|
14
28
|
|
15
29
|
* #232, #234, #237: Fix map filter bugs [Florian Weingarten, fw42]
|
16
30
|
|
@@ -30,6 +44,7 @@ Yanked from rubygems, as it contained too many changes that broke compatibility.
|
|
30
44
|
* Fix filter parser for args without space separators
|
31
45
|
* Add support for filter keyword arguments
|
32
46
|
|
47
|
+
|
33
48
|
## 2.4.0 / 2012-08-03
|
34
49
|
|
35
50
|
* Performance improvements
|
data/MIT-LICENSE
CHANGED
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
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.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Liquid template engine
|
2
2
|
|
3
|
+
* [Contributing guidelines](CONTRIBUTING.md)
|
4
|
+
* [Version history](History.md)
|
5
|
+
* [Liquid documentation from Shopify](http://docs.shopify.com/themes/liquid-basics)
|
6
|
+
* [Liquid Wiki from Shopify](http://wiki.shopify.com/Liquid)
|
7
|
+
* [Website](http://liquidmarkup.org/)
|
8
|
+
|
3
9
|
## Introduction
|
4
10
|
|
5
11
|
Liquid is a template engine which was written with very specific requirements:
|
data/lib/extras/liquid_view.rb
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
# and use liquid as an template system for .liquid files
|
3
3
|
#
|
4
4
|
# Example
|
5
|
-
#
|
5
|
+
#
|
6
6
|
# ActionView::Base::register_template_handler :liquid, LiquidView
|
7
7
|
class LiquidView
|
8
8
|
PROTECTED_ASSIGNS = %w( template_root response _session template_class action_name request_origin session template
|
9
9
|
_response url _request _cookies variables_added _flash params _headers request cookies
|
10
10
|
ignore_missing_templates flash _params logger before_filter_chain_aborted headers )
|
11
|
-
PROTECTED_INSTANCE_VARIABLES = %w( @_request @controller @_first_render @_memoized__pick_template @view_paths
|
11
|
+
PROTECTED_INSTANCE_VARIABLES = %w( @_request @controller @_first_render @_memoized__pick_template @view_paths
|
12
12
|
@helpers @assigns_added @template @_render_stack @template_format @assigns )
|
13
|
-
|
13
|
+
|
14
14
|
def self.call(template)
|
15
15
|
"LiquidView.new(self).render(template, local_assigns)"
|
16
16
|
end
|
@@ -18,10 +18,10 @@ class LiquidView
|
|
18
18
|
def initialize(view)
|
19
19
|
@view = view
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def render(template, local_assigns = nil)
|
23
23
|
@view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8'
|
24
|
-
|
24
|
+
|
25
25
|
# Rails 2.2 Template has source, but not locals
|
26
26
|
if template.respond_to?(:source) && !template.respond_to?(:locals)
|
27
27
|
assigns = (@view.instance_variables - PROTECTED_INSTANCE_VARIABLES).inject({}) do |hash, ivar|
|
@@ -31,15 +31,15 @@ class LiquidView
|
|
31
31
|
else
|
32
32
|
assigns = @view.assigns.reject{ |k,v| PROTECTED_ASSIGNS.include?(k) }
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
source = template.respond_to?(:source) ? template.source : template
|
36
36
|
local_assigns = (template.respond_to?(:locals) ? template.locals : local_assigns) || {}
|
37
|
-
|
37
|
+
|
38
38
|
if content_for_layout = @view.instance_variable_get("@content_for_layout")
|
39
39
|
assigns['content_for_layout'] = content_for_layout
|
40
40
|
end
|
41
41
|
assigns.merge!(local_assigns.stringify_keys)
|
42
|
-
|
42
|
+
|
43
43
|
liquid = Liquid::Template.parse(source)
|
44
44
|
liquid.render(assigns, :filters => [@view.controller.master_helper_module], :registers => {:action_view => @view, :controller => @view.controller})
|
45
45
|
end
|
@@ -48,4 +48,4 @@ class LiquidView
|
|
48
48
|
false
|
49
49
|
end
|
50
50
|
|
51
|
-
end
|
51
|
+
end
|
data/lib/liquid.rb
CHANGED
data/lib/liquid/block.rb
CHANGED
@@ -16,7 +16,7 @@ module Liquid
|
|
16
16
|
when IsTag
|
17
17
|
if token =~ FullToken
|
18
18
|
|
19
|
-
# if we found the proper block
|
19
|
+
# if we found the proper block delimiter just end parsing here and let the outer block
|
20
20
|
# proceed
|
21
21
|
if block_delimiter == $1
|
22
22
|
end_tag
|
@@ -43,8 +43,8 @@ module Liquid
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
# Make sure that
|
47
|
-
# Effectively this method will throw
|
46
|
+
# Make sure that it's ok to end parsing in the current block.
|
47
|
+
# Effectively this method will throw an exception unless the current block is
|
48
48
|
# of type Document
|
49
49
|
assert_missing_delimitation!
|
50
50
|
end
|
@@ -90,20 +90,31 @@ module Liquid
|
|
90
90
|
|
91
91
|
def render_all(list, context)
|
92
92
|
output = []
|
93
|
+
context.resource_limits[:render_length_current] = 0
|
94
|
+
context.resource_limits[:render_score_current] += list.length
|
95
|
+
|
93
96
|
list.each do |token|
|
94
97
|
# Break out if we have any unhanded interrupts.
|
95
98
|
break if context.has_interrupt?
|
96
99
|
|
97
100
|
begin
|
98
101
|
# 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 %}
|
102
|
+
# Interrupt is any command that stops block execution such as {% break %}
|
100
103
|
# or {% continue %}
|
101
104
|
if token.is_a? Continue or token.is_a? Break
|
102
105
|
context.push_interrupt(token.interrupt)
|
103
106
|
break
|
104
107
|
end
|
105
108
|
|
106
|
-
|
109
|
+
token_output = (token.respond_to?(:render) ? token.render(context) : token)
|
110
|
+
context.resource_limits[:render_length_current] += (token_output.respond_to?(:length) ? token_output.length : 1)
|
111
|
+
if context.resource_limits_reached?
|
112
|
+
context.resource_limits[:reached] = true
|
113
|
+
raise MemoryError.new("Memory limits exceeded")
|
114
|
+
end
|
115
|
+
output << token_output
|
116
|
+
rescue MemoryError => e
|
117
|
+
raise e
|
107
118
|
rescue ::StandardError => e
|
108
119
|
output << (context.handle_error(e))
|
109
120
|
end
|
data/lib/liquid/context.rb
CHANGED
@@ -13,19 +13,26 @@ module Liquid
|
|
13
13
|
#
|
14
14
|
# context['bob'] #=> nil class Context
|
15
15
|
class Context
|
16
|
-
attr_reader :scopes, :errors, :registers, :environments
|
17
|
-
|
18
|
-
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false)
|
19
|
-
@environments
|
20
|
-
@scopes
|
21
|
-
@registers
|
22
|
-
@errors
|
23
|
-
@rethrow_errors
|
16
|
+
attr_reader :scopes, :errors, :registers, :environments, :resource_limits
|
17
|
+
|
18
|
+
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = {})
|
19
|
+
@environments = [environments].flatten
|
20
|
+
@scopes = [(outer_scope || {})]
|
21
|
+
@registers = registers
|
22
|
+
@errors = []
|
23
|
+
@rethrow_errors = rethrow_errors
|
24
|
+
@resource_limits = (resource_limits || {}).merge!({ :render_score_current => 0, :assign_score_current => 0 })
|
24
25
|
squash_instance_assigns_with_environments
|
25
26
|
|
26
27
|
@interrupts = []
|
27
28
|
end
|
28
29
|
|
30
|
+
def resource_limits_reached?
|
31
|
+
(@resource_limits[:render_length_limit] && @resource_limits[:render_length_current] > @resource_limits[:render_length_limit]) ||
|
32
|
+
(@resource_limits[:render_score_limit] && @resource_limits[:render_score_current] > @resource_limits[:render_score_limit] ) ||
|
33
|
+
(@resource_limits[:assign_score_limit] && @resource_limits[:assign_score_current] > @resource_limits[:assign_score_limit] )
|
34
|
+
end
|
35
|
+
|
29
36
|
def strainer
|
30
37
|
@strainer ||= Strainer.create(self)
|
31
38
|
end
|
@@ -46,7 +53,7 @@ module Liquid
|
|
46
53
|
|
47
54
|
# are there any not handled interrupts?
|
48
55
|
def has_interrupt?
|
49
|
-
|
56
|
+
@interrupts.any?
|
50
57
|
end
|
51
58
|
|
52
59
|
# push an interrupt to the stack. this interrupt is considered not handled.
|
@@ -165,6 +172,7 @@ module Liquid
|
|
165
172
|
# Fetches an object starting at the local scope and then moving up the hierachy
|
166
173
|
def find_variable(key)
|
167
174
|
scope = @scopes.find { |s| s.has_key?(key) }
|
175
|
+
variable = nil
|
168
176
|
|
169
177
|
if scope.nil?
|
170
178
|
@environments.each do |e|
|
data/lib/liquid/document.rb
CHANGED
data/lib/liquid/errors.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Liquid
|
2
2
|
class Error < ::StandardError; end
|
3
|
-
|
3
|
+
|
4
4
|
class ArgumentError < Error; end
|
5
5
|
class ContextError < Error; end
|
6
6
|
class FilterNotFound < Error; end
|
@@ -8,4 +8,5 @@ module Liquid
|
|
8
8
|
class StandardError < Error; end
|
9
9
|
class SyntaxError < Error; end
|
10
10
|
class StackLevelError < Error; end
|
11
|
+
class MemoryError < Error; end
|
11
12
|
end
|
data/lib/liquid/file_system.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Liquid
|
2
|
-
# A Liquid file system is way to let your templates retrieve other templates for use with the include tag.
|
2
|
+
# A Liquid file system is a way to let your templates retrieve other templates for use with the include tag.
|
3
3
|
#
|
4
|
-
# You can implement subclasses that retrieve templates from the database, from the file system using a different
|
4
|
+
# You can implement subclasses that retrieve templates from the database, from the file system using a different
|
5
5
|
# path structure, you can provide them as hard-coded inline strings, or any manner that you see fit.
|
6
6
|
#
|
7
7
|
# You can add additional instance variables, arguments, or methods as needed.
|
@@ -18,7 +18,7 @@ module Liquid
|
|
18
18
|
raise FileSystemError, "This liquid context does not allow includes."
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
# This implements an abstract file system which retrieves template files named in a manner similar to Rails partials,
|
23
23
|
# ie. with the template name prefixed with an underscore. The extension ".liquid" is also added.
|
24
24
|
#
|
@@ -27,36 +27,36 @@ module Liquid
|
|
27
27
|
# Example:
|
28
28
|
#
|
29
29
|
# file_system = Liquid::LocalFileSystem.new("/some/path")
|
30
|
-
#
|
30
|
+
#
|
31
31
|
# file_system.full_path("mypartial") # => "/some/path/_mypartial.liquid"
|
32
32
|
# file_system.full_path("dir/mypartial") # => "/some/path/dir/_mypartial.liquid"
|
33
33
|
#
|
34
34
|
class LocalFileSystem
|
35
35
|
attr_accessor :root
|
36
|
-
|
36
|
+
|
37
37
|
def initialize(root)
|
38
38
|
@root = root
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
def read_template_file(template_path, context)
|
42
42
|
full_path = full_path(template_path)
|
43
43
|
raise FileSystemError, "No such template '#{template_path}'" unless File.exists?(full_path)
|
44
|
-
|
44
|
+
|
45
45
|
File.read(full_path)
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def full_path(template_path)
|
49
49
|
raise FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ /^[^.\/][a-zA-Z0-9_\/]+$/
|
50
|
-
|
50
|
+
|
51
51
|
full_path = if template_path.include?('/')
|
52
52
|
File.join(root, File.dirname(template_path), "_#{File.basename(template_path)}.liquid")
|
53
53
|
else
|
54
54
|
File.join(root, "_#{template_path}.liquid")
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path) =~ /^#{File.expand_path(root)}/
|
58
|
-
|
58
|
+
|
59
59
|
full_path
|
60
60
|
end
|
61
61
|
end
|
62
|
-
end
|
62
|
+
end
|
data/lib/liquid/htmltags.rb
CHANGED
@@ -57,7 +57,7 @@ module Liquid
|
|
57
57
|
|
58
58
|
result << "<td class=\"col#{col}\">" << render_all(@nodelist, context) << '</td>'
|
59
59
|
|
60
|
-
if col == cols and
|
60
|
+
if col == cols and (index != length - 1)
|
61
61
|
col = 0
|
62
62
|
row += 1
|
63
63
|
result << "</tr>\n<tr class=\"row#{row}\">"
|
data/lib/liquid/module_ex.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# This library is free software. It may be used, redistributed and/or modified
|
3
3
|
# under the same terms as Ruby itself
|
4
4
|
#
|
5
|
-
# This extension is
|
5
|
+
# This extension is used in order to expose the object of the implementing class
|
6
6
|
# to liquid as it were a Drop. It also limits the liquid-callable methods of the instance
|
7
7
|
# to the allowed method passed with the liquid_methods call
|
8
8
|
# Example:
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'cgi'
|
2
|
+
require 'bigdecimal'
|
2
3
|
|
3
4
|
module Liquid
|
4
5
|
|
@@ -10,12 +11,12 @@ module Liquid
|
|
10
11
|
input.respond_to?(:size) ? input.size : 0
|
11
12
|
end
|
12
13
|
|
13
|
-
# convert
|
14
|
+
# convert an input string to DOWNCASE
|
14
15
|
def downcase(input)
|
15
16
|
input.to_s.downcase
|
16
17
|
end
|
17
18
|
|
18
|
-
# convert
|
19
|
+
# convert an input string to UPCASE
|
19
20
|
def upcase(input)
|
20
21
|
input.to_s.upcase
|
21
22
|
end
|
@@ -42,7 +43,8 @@ module Liquid
|
|
42
43
|
if input.nil? then return end
|
43
44
|
l = length.to_i - truncate_string.length
|
44
45
|
l = 0 if l < 0
|
45
|
-
|
46
|
+
truncated = RUBY_VERSION[0,3] == "1.8" ? input.scan(/./mu)[0...l].to_s : input[0...l]
|
47
|
+
input.length > length.to_i ? truncated + truncate_string : input
|
46
48
|
end
|
47
49
|
|
48
50
|
def truncatewords(input, words = 15, truncate_string = "...")
|
@@ -63,15 +65,14 @@ module Liquid
|
|
63
65
|
end
|
64
66
|
|
65
67
|
def strip_html(input)
|
66
|
-
input.to_s.gsub(/<script.*?<\/script
|
68
|
+
input.to_s.gsub(/<script.*?<\/script>/m, '').gsub(/<!--.*?-->/m, '').gsub(/<style.*?<\/style>/m, '').gsub(/<.*?>/m, '')
|
67
69
|
end
|
68
70
|
|
69
71
|
# Remove all newlines from the string
|
70
72
|
def strip_newlines(input)
|
71
|
-
input.to_s.gsub(/\n/, '')
|
73
|
+
input.to_s.gsub(/\r?\n/, '')
|
72
74
|
end
|
73
75
|
|
74
|
-
|
75
76
|
# Join elements of the array with certain character between them
|
76
77
|
def join(input, glue = ' ')
|
77
78
|
[input].flatten.join(glue)
|
@@ -90,6 +91,12 @@ module Liquid
|
|
90
91
|
end
|
91
92
|
end
|
92
93
|
|
94
|
+
# Reverse the elements of an array
|
95
|
+
def reverse(input)
|
96
|
+
ary = [input].flatten
|
97
|
+
ary.reverse
|
98
|
+
end
|
99
|
+
|
93
100
|
# map/collect on a given property
|
94
101
|
def map(input, property)
|
95
102
|
ary = [input].flatten
|
@@ -178,14 +185,23 @@ module Liquid
|
|
178
185
|
input = Time.at(input.to_i)
|
179
186
|
end
|
180
187
|
|
181
|
-
date = input.is_a?(String)
|
188
|
+
date = if input.is_a?(String)
|
189
|
+
case input.downcase
|
190
|
+
when 'now', 'today'
|
191
|
+
Time.now
|
192
|
+
else
|
193
|
+
Time.parse(input)
|
194
|
+
end
|
195
|
+
else
|
196
|
+
input
|
197
|
+
end
|
182
198
|
|
183
199
|
if date.respond_to?(:strftime)
|
184
200
|
date.strftime(format.to_s)
|
185
201
|
else
|
186
202
|
input
|
187
203
|
end
|
188
|
-
rescue
|
204
|
+
rescue
|
189
205
|
input
|
190
206
|
end
|
191
207
|
|
@@ -209,41 +225,47 @@ module Liquid
|
|
209
225
|
|
210
226
|
# addition
|
211
227
|
def plus(input, operand)
|
212
|
-
|
228
|
+
apply_operation(input, operand, :+)
|
213
229
|
end
|
214
230
|
|
215
231
|
# subtraction
|
216
232
|
def minus(input, operand)
|
217
|
-
|
233
|
+
apply_operation(input, operand, :-)
|
218
234
|
end
|
219
235
|
|
220
236
|
# multiplication
|
221
237
|
def times(input, operand)
|
222
|
-
|
238
|
+
apply_operation(input, operand, :*)
|
223
239
|
end
|
224
240
|
|
225
241
|
# division
|
226
242
|
def divided_by(input, operand)
|
227
|
-
|
243
|
+
apply_operation(input, operand, :/)
|
228
244
|
end
|
229
245
|
|
230
246
|
def modulo(input, operand)
|
231
|
-
|
247
|
+
apply_operation(input, operand, :%)
|
232
248
|
end
|
233
249
|
|
234
250
|
private
|
235
251
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
252
|
+
def to_number(obj)
|
253
|
+
case obj
|
254
|
+
when Float
|
255
|
+
BigDecimal.new(obj.to_s)
|
256
|
+
when Numeric
|
257
|
+
obj
|
258
|
+
when String
|
259
|
+
(obj.strip =~ /^\d+\.\d+$/) ? BigDecimal.new(obj) : obj.to_i
|
260
|
+
else
|
261
|
+
0
|
245
262
|
end
|
263
|
+
end
|
246
264
|
|
265
|
+
def apply_operation(input, operand, operation)
|
266
|
+
result = to_number(input).send(operation, to_number(operand))
|
267
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
268
|
+
end
|
247
269
|
end
|
248
270
|
|
249
271
|
Template.register_filter(StandardFilters)
|