qoobaa-liquid 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,44 @@
1
+ * Ruby 1.9.1 bugfixes
2
+
3
+ * Fix LiquidView for Rails 2.2. Fix local assigns for all versions of Rails
4
+
5
+ * Fixed gem install rake task
6
+ * Improve Error encapsulation in liquid by maintaining a own set of exceptions instead of relying on ruby build ins
7
+
8
+ * Added If with or / and expressions
9
+
10
+ * Implemented .to_liquid for all objects which can be passed to liquid like Strings Arrays Hashes Numerics and Booleans. To export new objects to liquid just implement .to_liquid on them and return objects which themselves have .to_liquid methods.
11
+
12
+ * Added more tags to standard library
13
+
14
+ * Added include tag ( like partials in rails )
15
+
16
+ * [...] Gazillion of detail improvements
17
+
18
+ * Added strainers as filter hosts for better security [Tobias Luetke]
19
+
20
+ * Fixed that rails integration would call filter with the wrong "self" [Michael Geary]
21
+
22
+ * Fixed bad error reporting when a filter called a method which doesn't exist. Liquid told you that it couldn't find the filter which was obviously misleading [Tobias Luetke]
23
+
24
+ * Removed count helper from standard lib. use size [Tobias Luetke]
25
+
26
+ * Fixed bug with string filter parameters failing to tolerate commas in strings. [Paul Hammond]
27
+
28
+ * Improved filter parameters. Filter parameters are now context sensitive; Types are resolved according to the rules of the context. Multiple parameters are now separated by the Liquid::ArgumentSeparator: , by default [Paul Hammond]
29
+
30
+ {{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }}
31
+
32
+
33
+ * Added Liquid::Drop. A base class which you can use for exporting proxy objects to liquid which can acquire more data when used in liquid. [Tobias Luetke]
34
+
35
+ class ProductDrop < Liquid::Drop
36
+ def top_sales
37
+ Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
38
+ end
39
+ end
40
+ t = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {% endfor %} ' )
41
+ t.render('product' => ProductDrop.new )
42
+
43
+
44
+ * Added filter parameters support. Example: {{ date | format_date: "%Y" }} [Paul Hammond]
data/History.txt ADDED
@@ -0,0 +1,44 @@
1
+ 1.9.0 / 2008-03-04
2
+
3
+ * Fixed gem install rake task
4
+ * Improve Error encapsulation in liquid by maintaining a own set of exceptions instead of relying on ruby build ins
5
+
6
+ Before 1.9.0
7
+
8
+ * Added If with or / and expressions
9
+
10
+ * Implemented .to_liquid for all objects which can be passed to liquid like Strings Arrays Hashes Numerics and Booleans. To export new objects to liquid just implement .to_liquid on them and return objects which themselves have .to_liquid methods.
11
+
12
+ * Added more tags to standard library
13
+
14
+ * Added include tag ( like partials in rails )
15
+
16
+ * [...] Gazillion of detail improvements
17
+
18
+ * Added strainers as filter hosts for better security [Tobias Luetke]
19
+
20
+ * Fixed that rails integration would call filter with the wrong "self" [Michael Geary]
21
+
22
+ * Fixed bad error reporting when a filter called a method which doesn't exist. Liquid told you that it couldn't find the filter which was obviously misleading [Tobias Luetke]
23
+
24
+ * Removed count helper from standard lib. use size [Tobias Luetke]
25
+
26
+ * Fixed bug with string filter parameters failing to tolerate commas in strings. [Paul Hammond]
27
+
28
+ * Improved filter parameters. Filter parameters are now context sensitive; Types are resolved according to the rules of the context. Multiple parameters are now separated by the Liquid::ArgumentSeparator: , by default [Paul Hammond]
29
+
30
+ {{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }}
31
+
32
+
33
+ * Added Liquid::Drop. A base class which you can use for exporting proxy objects to liquid which can acquire more data when used in liquid. [Tobias Luetke]
34
+
35
+ class ProductDrop < Liquid::Drop
36
+ def top_sales
37
+ Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
38
+ end
39
+ end
40
+ t = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {% endfor %} ' )
41
+ t.render('product' => ProductDrop.new )
42
+
43
+
44
+ * Added filter parameters support. Example: {{ date | format_date: "%Y" }} [Paul Hammond]
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005, 2006 Tobias Luetke
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/Manifest.txt ADDED
@@ -0,0 +1,34 @@
1
+ CHANGELOG
2
+ History.txt
3
+ MIT-LICENSE
4
+ Manifest.txt
5
+ README.txt
6
+ Rakefile
7
+ init.rb
8
+ lib/extras/liquid_view.rb
9
+ lib/liquid.rb
10
+ lib/liquid/block.rb
11
+ lib/liquid/condition.rb
12
+ lib/liquid/context.rb
13
+ lib/liquid/document.rb
14
+ lib/liquid/drop.rb
15
+ lib/liquid/errors.rb
16
+ lib/liquid/extensions.rb
17
+ lib/liquid/file_system.rb
18
+ lib/liquid/htmltags.rb
19
+ lib/liquid/module_ex.rb
20
+ lib/liquid/standardfilters.rb
21
+ lib/liquid/strainer.rb
22
+ lib/liquid/tag.rb
23
+ lib/liquid/tags/assign.rb
24
+ lib/liquid/tags/capture.rb
25
+ lib/liquid/tags/case.rb
26
+ lib/liquid/tags/comment.rb
27
+ lib/liquid/tags/cycle.rb
28
+ lib/liquid/tags/for.rb
29
+ lib/liquid/tags/if.rb
30
+ lib/liquid/tags/ifchanged.rb
31
+ lib/liquid/tags/include.rb
32
+ lib/liquid/tags/unless.rb
33
+ lib/liquid/template.rb
34
+ lib/liquid/variable.rb
data/README.txt ADDED
@@ -0,0 +1,38 @@
1
+ = Liquid template engine
2
+
3
+ Liquid is a template engine which I wrote for very specific requirements
4
+
5
+ * It has to have beautiful and simple markup.
6
+ Template engines which don't produce good looking markup are no fun to use.
7
+ * 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.
8
+ * 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
9
+ just render it passing in a hash with local variables and objects.
10
+
11
+ == Why should i use Liquid
12
+
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 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 one
18
+
19
+ == What does it look like?
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>
31
+
32
+ == Howto use Liquid
33
+
34
+ Liquid supports a very simple API based around the Liquid::Template class.
35
+ For standard use you can just pass it the content of a file and call render with a parameters hash.
36
+
37
+ @template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
38
+ @template.render( 'name' => 'tobi' ) # => "hi tobi"
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'hoe'
5
+
6
+ PKG_VERSION = "1.9.0"
7
+ PKG_NAME = "liquid"
8
+ PKG_DESC = "A secure non evaling end user template engine with aesthetic markup."
9
+
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << "lib"
12
+ t.libs << "test"
13
+ t.pattern = 'test/*_test.rb'
14
+ t.verbose = false
15
+ end
16
+
17
+ Hoe.new(PKG_NAME, PKG_VERSION) do |p|
18
+ p.rubyforge_name = PKG_NAME
19
+ p.summary = PKG_DESC
20
+ p.description = PKG_DESC
21
+ p.author = "Tobias Luetke"
22
+ p.email = "tobi@leetsoft.com"
23
+ p.url = "http://www.liquidmarkup.org"
24
+ end
@@ -0,0 +1,51 @@
1
+ # LiquidView is a action view extension class. You can register it with rails
2
+ # and use liquid as an template system for .liquid files
3
+ #
4
+ # Example
5
+ #
6
+ # ActionView::Base::register_template_handler :liquid, LiquidView
7
+ class LiquidView
8
+ PROTECTED_ASSIGNS = %w( template_root response _session template_class action_name request_origin session template
9
+ _response url _request _cookies variables_added _flash params _headers request cookies
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
12
+ @helpers @assigns_added @template @_render_stack @template_format @assigns )
13
+
14
+ def self.call(template)
15
+ "LiquidView.new(self).render(template, local_assigns)"
16
+ end
17
+
18
+ def initialize(view)
19
+ @view = view
20
+ end
21
+
22
+ def render(template, local_assigns = nil)
23
+ @view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8'
24
+
25
+ # Rails 2.2 Template has source, but not locals
26
+ if template.respond_to?(:source) && !template.respond_to?(:locals)
27
+ assigns = (@view.instance_variables - PROTECTED_INSTANCE_VARIABLES).inject({}) do |hash, ivar|
28
+ hash[ivar[1..-1]] = @view.instance_variable_get(ivar)
29
+ hash
30
+ end
31
+ else
32
+ assigns = @view.assigns.reject{ |k,v| PROTECTED_ASSIGNS.include?(k) }
33
+ end
34
+
35
+ source = template.respond_to?(:source) ? template.source : template
36
+ local_assigns = (template.respond_to?(:locals) ? template.locals : local_assigns) || {}
37
+
38
+ if content_for_layout = @view.instance_variable_get("@content_for_layout")
39
+ assigns['content_for_layout'] = content_for_layout
40
+ end
41
+ assigns.merge!(local_assigns.stringify_keys)
42
+
43
+ liquid = Liquid::Template.parse(source)
44
+ liquid.render(assigns, :filters => [@view.controller.master_helper_module], :registers => {:action_view => @view, :controller => @view.controller})
45
+ end
46
+
47
+ def compilable?
48
+ false
49
+ end
50
+
51
+ end
data/lib/liquid.rb ADDED
@@ -0,0 +1,68 @@
1
+ # Copyright (c) 2005 Tobias Luetke
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.
21
+
22
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
23
+
24
+ module Liquid
25
+ FilterSeparator = /\|/
26
+ ArgumentSeparator = ','
27
+ FilterArgumentSeparator = ':'
28
+ VariableAttributeSeparator = '.'
29
+ TagStart = /\{\%/
30
+ TagEnd = /\%\}/
31
+ VariableSignature = /\(?[\w\-\.\[\]]\)?/
32
+ VariableSegment = /[\w\-]\??/
33
+ VariableStart = /\{\{/
34
+ VariableEnd = /\}\}/
35
+ VariableIncompleteEnd = /\}\}?/
36
+ 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})/
44
+ AnyStartingTag = /\{\{|\{\%/
45
+ PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/
46
+ TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/
47
+ VariableParser = /\[[^\]]+\]|#{VariableSegment}+/
48
+ end
49
+
50
+ require 'liquid/drop'
51
+ require 'liquid/extensions'
52
+ require 'liquid/errors'
53
+ require 'liquid/strainer'
54
+ require 'liquid/context'
55
+ require 'liquid/tag'
56
+ require 'liquid/block'
57
+ require 'liquid/document'
58
+ require 'liquid/variable'
59
+ require 'liquid/file_system'
60
+ require 'liquid/template'
61
+ require 'liquid/htmltags'
62
+ require 'liquid/standardfilters'
63
+ require 'liquid/condition'
64
+ require 'liquid/module_ex'
65
+
66
+ # Load all the tags of the standard library
67
+ #
68
+ Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f }
@@ -0,0 +1,97 @@
1
+ module Liquid
2
+
3
+ class Block < Tag
4
+
5
+ def parse(tokens)
6
+ @nodelist ||= []
7
+ @nodelist.clear
8
+
9
+ while token = tokens.shift
10
+
11
+ case token
12
+ when /^#{TagStart}/
13
+ if token =~ /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/
14
+
15
+ # if we found the proper block delimitor just end parsing here and let the outer block
16
+ # proceed
17
+ if block_delimiter == $1
18
+ end_tag
19
+ return
20
+ end
21
+
22
+ # fetch the tag from registered blocks
23
+ if tag = Template.tags[$1]
24
+ @nodelist << tag.new($1, $2, tokens)
25
+ else
26
+ # this tag is not registered with the system
27
+ # pass it to the current block for special handling or error reporting
28
+ unknown_tag($1, $2, tokens)
29
+ end
30
+ else
31
+ raise SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} "
32
+ end
33
+ when /^#{VariableStart}/
34
+ @nodelist << create_variable(token)
35
+ when ''
36
+ # pass
37
+ else
38
+ @nodelist << token
39
+ end
40
+ end
41
+
42
+ # Make sure that its ok to end parsing in the current block.
43
+ # Effectively this method will throw and exception unless the current block is
44
+ # of type Document
45
+ assert_missing_delimitation!
46
+ end
47
+
48
+ def end_tag
49
+ end
50
+
51
+ def unknown_tag(tag, params, tokens)
52
+ case tag
53
+ when 'else'
54
+ raise SyntaxError, "#{block_name} tag does not expect else tag"
55
+ when 'end'
56
+ raise SyntaxError, "'end' is not a valid delimiter for #{block_name} tags. use #{block_delimiter}"
57
+ else
58
+ raise SyntaxError, "Unknown tag '#{tag}'"
59
+ end
60
+ end
61
+
62
+ def block_delimiter
63
+ "end#{block_name}"
64
+ end
65
+
66
+ def block_name
67
+ @tag_name
68
+ end
69
+
70
+ def create_variable(token)
71
+ token.scan(/^#{VariableStart}(.*)#{VariableEnd}$/) do |content|
72
+ return Variable.new(content.first)
73
+ end
74
+ raise SyntaxError.new("Variable '#{token}' was not properly terminated with regexp: #{VariableEnd.inspect} ")
75
+ end
76
+
77
+ def render(context)
78
+ render_all(@nodelist, context)
79
+ end
80
+
81
+ protected
82
+
83
+ def assert_missing_delimitation!
84
+ raise SyntaxError.new("#{block_name} tag was never closed")
85
+ end
86
+
87
+ def render_all(list, context)
88
+ list.collect do |token|
89
+ begin
90
+ token.respond_to?(:render) ? token.render(context) : token
91
+ rescue Exception => e
92
+ context.handle_error(e)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,123 @@
1
+ module Liquid
2
+ # Container for liquid nodes which conveniently wraps decision making logic
3
+ #
4
+ # Example:
5
+ #
6
+ # c = Condition.new('1', '==', '1')
7
+ # c.evaluate #=> true
8
+ #
9
+ class Condition #:nodoc:
10
+ @@operators = {
11
+ '==' => lambda { |cond, left, right| cond.send(:equal_variables, left, right) },
12
+ '!=' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
13
+ '<>' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
14
+ '<' => :<,
15
+ '>' => :>,
16
+ '>=' => :>=,
17
+ '<=' => :<=,
18
+ 'contains' => lambda { |cond, left, right| left.include?(right) },
19
+ }
20
+
21
+ def self.operators
22
+ @@operators
23
+ end
24
+
25
+ attr_reader :attachment
26
+ attr_accessor :left, :operator, :right
27
+
28
+ def initialize(left = nil, operator = nil, right = nil)
29
+ @left, @operator, @right = left, operator, right
30
+ @child_relation = nil
31
+ @child_condition = nil
32
+ end
33
+
34
+ def evaluate(context = Context.new)
35
+ result = interpret_condition(left, right, operator, context)
36
+
37
+ case @child_relation
38
+ when :or
39
+ result || @child_condition.evaluate(context)
40
+ when :and
41
+ result && @child_condition.evaluate(context)
42
+ else
43
+ result
44
+ end
45
+ end
46
+
47
+ def or(condition)
48
+ @child_relation, @child_condition = :or, condition
49
+ end
50
+
51
+ def and(condition)
52
+ @child_relation, @child_condition = :and, condition
53
+ end
54
+
55
+ def attach(attachment)
56
+ @attachment = attachment
57
+ end
58
+
59
+ def else?
60
+ false
61
+ end
62
+
63
+ def inspect
64
+ "#<Condition #{[@left, @operator, @right].compact.join(' ')}>"
65
+ end
66
+
67
+ private
68
+
69
+ def equal_variables(left, right)
70
+ if left.is_a?(Symbol)
71
+ if right.respond_to?(left)
72
+ return right.send(left.to_s)
73
+ else
74
+ return nil
75
+ end
76
+ end
77
+
78
+ if right.is_a?(Symbol)
79
+ if left.respond_to?(right)
80
+ return left.send(right.to_s)
81
+ else
82
+ return nil
83
+ end
84
+ end
85
+
86
+ left == right
87
+ end
88
+
89
+ def interpret_condition(left, right, op, context)
90
+
91
+ # If the operator is empty this means that the decision statement is just
92
+ # a single variable. We can just poll this variable from the context and
93
+ # return this as the result.
94
+ return context[left] if op == nil
95
+
96
+ left, right = context[left], context[right]
97
+
98
+
99
+ operation = self.class.operators[op] || raise(ArgumentError.new("Unknown operator #{op}"))
100
+
101
+ if operation.respond_to?(:call)
102
+ operation.call(self, left, right)
103
+ elsif left.respond_to?(operation) and right.respond_to?(operation)
104
+ left.send(operation, right)
105
+ else
106
+ nil
107
+ end
108
+ end
109
+ end
110
+
111
+
112
+ class ElseCondition < Condition
113
+
114
+ def else?
115
+ true
116
+ end
117
+
118
+ def evaluate(context)
119
+ true
120
+ end
121
+ end
122
+
123
+ end