agilitic-liquid 2.0.1

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.
@@ -0,0 +1,62 @@
1
+ module Liquid
2
+ # A Liquid file system is way to let your templates retrieve other templates for use with the include tag.
3
+ #
4
+ # You can implement subclasses that retrieve templates from the database, from the file system using a different
5
+ # path structure, you can provide them as hard-coded inline strings, or any manner that you see fit.
6
+ #
7
+ # You can add additional instance variables, arguments, or methods as needed.
8
+ #
9
+ # Example:
10
+ #
11
+ # Liquid::Template.file_system = Liquid::LocalFileSystem.new(template_path)
12
+ # liquid = Liquid::Template.parse(template)
13
+ #
14
+ # This will parse the template with a LocalFileSystem implementation rooted at 'template_path'.
15
+ class BlankFileSystem
16
+ # Called by Liquid to retrieve a template file
17
+ def read_template_file(template_path)
18
+ raise FileSystemError, "This liquid context does not allow includes."
19
+ end
20
+ end
21
+
22
+ # This implements an abstract file system which retrieves template files named in a manner similar to Rails partials,
23
+ # ie. with the template name prefixed with an underscore. The extension ".liquid" is also added.
24
+ #
25
+ # For security reasons, template paths are only allowed to contain letters, numbers, and underscore.
26
+ #
27
+ # Example:
28
+ #
29
+ # file_system = Liquid::LocalFileSystem.new("/some/path")
30
+ #
31
+ # file_system.full_path("mypartial") # => "/some/path/_mypartial.liquid"
32
+ # file_system.full_path("dir/mypartial") # => "/some/path/dir/_mypartial.liquid"
33
+ #
34
+ class LocalFileSystem
35
+ attr_accessor :root
36
+
37
+ def initialize(root)
38
+ @root = root
39
+ end
40
+
41
+ def read_template_file(template_path)
42
+ full_path = full_path(template_path)
43
+ raise FileSystemError, "No such template '#{template_path}'" unless File.exists?(full_path)
44
+
45
+ File.read(full_path)
46
+ end
47
+
48
+ def full_path(template_path)
49
+ raise FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ /^[^.\/][a-zA-Z0-9_\/]+$/
50
+
51
+ full_path = if template_path.include?('/')
52
+ File.join(root, File.dirname(template_path), "_#{File.basename(template_path)}.liquid")
53
+ else
54
+ File.join(root, "_#{template_path}.liquid")
55
+ end
56
+
57
+ raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path) =~ /^#{File.expand_path(root)}/
58
+
59
+ full_path
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,74 @@
1
+ module Liquid
2
+ class TableRow < Block
3
+ Syntax = /(\w+)\s+in\s+(#{VariableSignature}+)/
4
+
5
+ def initialize(tag_name, markup, tokens)
6
+ if markup =~ Syntax
7
+ @variable_name = $1
8
+ @collection_name = $2
9
+ @attributes = {}
10
+ markup.scan(TagAttributes) do |key, value|
11
+ @attributes[key] = value
12
+ end
13
+ else
14
+ raise SyntaxError.new("Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3")
15
+ end
16
+
17
+ super
18
+ end
19
+
20
+ def render(context)
21
+ collection = context[@collection_name] or return ''
22
+
23
+ if @attributes['limit'] or @attributes['offset']
24
+ limit = context[@attributes['limit']] || -1
25
+ offset = context[@attributes['offset']] || 0
26
+ collection = collection[offset.to_i..(limit.to_i + offset.to_i - 1)]
27
+ end
28
+
29
+ length = collection.length
30
+
31
+ cols = context[@attributes['cols']].to_i
32
+
33
+ row = 1
34
+ col = 0
35
+
36
+ result = ["<tr class=\"row1\">\n"]
37
+ context.stack do
38
+
39
+ collection.each_with_index do |item, index|
40
+ context[@variable_name] = item
41
+ context['tablerowloop'] = {
42
+ 'length' => length,
43
+ 'index' => index + 1,
44
+ 'index0' => index,
45
+ 'col' => col + 1,
46
+ 'col0' => col,
47
+ 'index0' => index,
48
+ 'rindex' => length - index,
49
+ 'rindex0' => length - index -1,
50
+ 'first' => (index == 0),
51
+ 'last' => (index == length - 1),
52
+ 'col_first' => (col == 0),
53
+ 'col_last' => (col == cols - 1)
54
+ }
55
+
56
+
57
+ col += 1
58
+
59
+ result << ["<td class=\"col#{col}\">"] + render_all(@nodelist, context) + ['</td>']
60
+
61
+ if col == cols and not (index == length - 1)
62
+ col = 0
63
+ row += 1
64
+ result << ["</tr>\n<tr class=\"row#{row}\">"]
65
+ end
66
+
67
+ end
68
+ end
69
+ result + ["</tr>\n"]
70
+ end
71
+ end
72
+
73
+ Template.register_tag('tablerow', TableRow)
74
+ end
@@ -0,0 +1,62 @@
1
+ # Copyright 2007 by Domizio Demichelis
2
+ # This library is free software. It may be used, redistributed and/or modified
3
+ # under the same terms as Ruby itself
4
+ #
5
+ # This extension is usesd in order to expose the object of the implementing class
6
+ # to liquid as it were a Drop. It also limits the liquid-callable methods of the instance
7
+ # to the allowed method passed with the liquid_methods call
8
+ # Example:
9
+ #
10
+ # class SomeClass
11
+ # liquid_methods :an_allowed_method
12
+ #
13
+ # def an_allowed_method
14
+ # 'this comes from an allowed method'
15
+ # end
16
+ # def unallowed_method
17
+ # 'this will never be an output'
18
+ # end
19
+ # end
20
+ #
21
+ # if you want to extend the drop to other methods you can defines more methods
22
+ # in the class <YourClass>::LiquidDropClass
23
+ #
24
+ # class SomeClass::LiquidDropClass
25
+ # def another_allowed_method
26
+ # 'and this from another allowed method'
27
+ # end
28
+ # end
29
+ # end
30
+ #
31
+ # usage:
32
+ # @something = SomeClass.new
33
+ #
34
+ # template:
35
+ # {{something.an_allowed_method}}{{something.unallowed_method}} {{something.another_allowed_method}}
36
+ #
37
+ # output:
38
+ # 'this comes from an allowed method and this from another allowed method'
39
+ #
40
+ # You can also chain associations, by adding the liquid_method call in the
41
+ # association models.
42
+ #
43
+ class Module
44
+
45
+ def liquid_methods(*allowed_methods)
46
+ drop_class = eval "class #{self.to_s}::LiquidDropClass < Liquid::Drop; self; end"
47
+ define_method :to_liquid do
48
+ drop_class.new(self)
49
+ end
50
+ drop_class.class_eval do
51
+ def initialize(object)
52
+ @object = object
53
+ end
54
+ allowed_methods.each do |sym|
55
+ define_method sym do
56
+ @object.send sym
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,214 @@
1
+ require 'cgi'
2
+
3
+ module Liquid
4
+
5
+ module StandardFilters
6
+
7
+ # does nothing itself but prevent autoescaping: See Liquid::Variable
8
+ def raw(input)
9
+ input
10
+ end
11
+
12
+ # Return the size of an array or of an string
13
+ def size(input)
14
+
15
+ input.respond_to?(:size) ? input.size : 0
16
+ end
17
+
18
+ # convert a input string to DOWNCASE
19
+ def downcase(input)
20
+ input.to_s.downcase
21
+ end
22
+
23
+ # convert a input string to UPCASE
24
+ def upcase(input)
25
+ input.to_s.upcase
26
+ end
27
+
28
+ # capitalize words in the input centence
29
+ def capitalize(input)
30
+ input.to_s.capitalize
31
+ end
32
+
33
+ def escape(input)
34
+ CGI.escapeHTML(input) rescue input
35
+ end
36
+
37
+ alias_method :h, :escape
38
+
39
+ # Truncate a string down to x characters
40
+ def truncate(input, length = 50, truncate_string = "...")
41
+ if input.nil? then return end
42
+ l = length.to_i - truncate_string.length
43
+ l = 0 if l < 0
44
+ input.length > length.to_i ? input[0...l] + truncate_string : input
45
+ end
46
+
47
+ def truncatewords(input, words = 15, truncate_string = "...")
48
+ if input.nil? then return end
49
+ wordlist = input.to_s.split
50
+ l = words.to_i - 1
51
+ l = 0 if l < 0
52
+ wordlist.length > l ? wordlist[0..l].join(" ") + truncate_string : input
53
+ end
54
+
55
+ def strip_html(input)
56
+ input.to_s.gsub(/<.*?>/, '')
57
+ end
58
+
59
+ # Remove all newlines from the string
60
+ def strip_newlines(input)
61
+ input.to_s.gsub(/\n/, '')
62
+ end
63
+
64
+
65
+ # Join elements of the array with certain character between them
66
+ def join(input, glue = ' ')
67
+ [input].flatten.join(glue)
68
+ end
69
+
70
+ # Sort elements of the array
71
+ # provide optional property with which to sort an array of hashes or drops
72
+ def sort(input, property = nil)
73
+ ary = [input].flatten
74
+ if property.nil?
75
+ ary.sort
76
+ elsif ary.first.respond_to?('[]') and !ary.first[property].nil?
77
+ ary.sort {|a,b| a[property] <=> b[property] }
78
+ elsif ary.first.respond_to?(property)
79
+ ary.sort {|a,b| a.send(property) <=> b.send(property) }
80
+ end
81
+ end
82
+
83
+ # map/collect on a given property
84
+ def map(input, property)
85
+ ary = [input].flatten
86
+ if ary.first.respond_to?('[]') and !ary.first[property].nil?
87
+ ary.map {|e| e[property] }
88
+ elsif ary.first.respond_to?(property)
89
+ ary.map {|e| e.send(property) }
90
+ end
91
+ end
92
+
93
+ # Replace occurrences of a string with another
94
+ def replace(input, string, replacement = '')
95
+ input.to_s.gsub(string, replacement)
96
+ end
97
+
98
+ # Replace the first occurrences of a string with another
99
+ def replace_first(input, string, replacement = '')
100
+ input.to_s.sub(string, replacement)
101
+ end
102
+
103
+ # remove a substring
104
+ def remove(input, string)
105
+ input.to_s.gsub(string, '')
106
+ end
107
+
108
+ # remove the first occurrences of a substring
109
+ def remove_first(input, string)
110
+ input.to_s.sub(string, '')
111
+ end
112
+
113
+ # add one string to another
114
+ def append(input, string)
115
+ input.to_s + string.to_s
116
+ end
117
+
118
+ # prepend a string to another
119
+ def prepend(input, string)
120
+ string.to_s + input.to_s
121
+ end
122
+
123
+ # Add <br /> tags in front of all newlines in input string
124
+ def newline_to_br(input)
125
+ input.to_s.gsub(/\n/, "<br />\n")
126
+ end
127
+
128
+ # Reformat a date
129
+ #
130
+ # %a - The abbreviated weekday name (``Sun'')
131
+ # %A - The full weekday name (``Sunday'')
132
+ # %b - The abbreviated month name (``Jan'')
133
+ # %B - The full month name (``January'')
134
+ # %c - The preferred local date and time representation
135
+ # %d - Day of the month (01..31)
136
+ # %H - Hour of the day, 24-hour clock (00..23)
137
+ # %I - Hour of the day, 12-hour clock (01..12)
138
+ # %j - Day of the year (001..366)
139
+ # %m - Month of the year (01..12)
140
+ # %M - Minute of the hour (00..59)
141
+ # %p - Meridian indicator (``AM'' or ``PM'')
142
+ # %S - Second of the minute (00..60)
143
+ # %U - Week number of the current year,
144
+ # starting with the first Sunday as the first
145
+ # day of the first week (00..53)
146
+ # %W - Week number of the current year,
147
+ # starting with the first Monday as the first
148
+ # day of the first week (00..53)
149
+ # %w - Day of the week (Sunday is 0, 0..6)
150
+ # %x - Preferred representation for the date alone, no time
151
+ # %X - Preferred representation for the time alone, no date
152
+ # %y - Year without a century (00..99)
153
+ # %Y - Year with century
154
+ # %Z - Time zone name
155
+ # %% - Literal ``%'' character
156
+ def date(input, format)
157
+
158
+ if format.to_s.empty?
159
+ return input.to_s
160
+ end
161
+
162
+ date = input.is_a?(String) ? Time.parse(input) : input
163
+
164
+ if date.respond_to?(:strftime)
165
+ date.strftime(format.to_s)
166
+ else
167
+ input
168
+ end
169
+ rescue => e
170
+ input
171
+ end
172
+
173
+ # Get the first element of the passed in array
174
+ #
175
+ # Example:
176
+ # {{ product.images | first | to_img }}
177
+ #
178
+ def first(array)
179
+ array.first if array.respond_to?(:first)
180
+ end
181
+
182
+ # Get the last element of the passed in array
183
+ #
184
+ # Example:
185
+ # {{ product.images | last | to_img }}
186
+ #
187
+ def last(array)
188
+ array.last if array.respond_to?(:last)
189
+ end
190
+
191
+ # addition
192
+ def plus(input, operand)
193
+ input + operand if input.respond_to?('+')
194
+ end
195
+
196
+ # subtraction
197
+ def minus(input, operand)
198
+ input - operand if input.respond_to?('-')
199
+ end
200
+
201
+ # multiplication
202
+ def times(input, operand)
203
+ input * operand if input.respond_to?('*')
204
+ end
205
+
206
+ # division
207
+ def divided_by(input, operand)
208
+ input / operand if input.respond_to?('/')
209
+ end
210
+
211
+ end
212
+
213
+ Template.register_filter(StandardFilters)
214
+ end
@@ -0,0 +1,51 @@
1
+ require 'set'
2
+
3
+ module Liquid
4
+
5
+ parent_object = if defined? BlankObject
6
+ BlankObject
7
+ else
8
+ Object
9
+ end
10
+
11
+ # Strainer is the parent class for the filters system.
12
+ # New filters are mixed into the strainer class which is then instanciated for each liquid template render run.
13
+ #
14
+ # One of the strainer's responsibilities is to keep malicious method calls out
15
+ class Strainer < parent_object #:nodoc:
16
+ INTERNAL_METHOD = /^__/
17
+ @@required_methods = Set.new([:__id__, :__send__, :respond_to?, :extend, :methods, :class, :object_id])
18
+
19
+ @@filters = {}
20
+
21
+ def initialize(context)
22
+ @context = context
23
+ end
24
+
25
+ def self.global_filter(filter)
26
+ raise ArgumentError, "Passed filter is not a module" unless filter.is_a?(Module)
27
+ @@filters[filter.name] = filter
28
+ end
29
+
30
+ def self.create(context)
31
+ strainer = Strainer.new(context)
32
+ @@filters.each { |k,m| strainer.extend(m) }
33
+ strainer
34
+ end
35
+
36
+ def respond_to?(method, include_private = false)
37
+ method_name = method.to_s
38
+ return false if method_name =~ INTERNAL_METHOD
39
+ return false if @@required_methods.include?(method_name)
40
+ super
41
+ end
42
+
43
+ # remove all standard methods from the bucket so circumvent security
44
+ # problems
45
+ instance_methods.each do |m|
46
+ unless @@required_methods.include?(m.to_sym)
47
+ undef_method m
48
+ end
49
+ end
50
+ end
51
+ end