diff --git a/app/controllers/admin/layouts_controller.rb b/app/controllers/admin/layouts_controller.rb
index 987fc4e30..ba857fe53 100644
--- a/app/controllers/admin/layouts_controller.rb
+++ b/app/controllers/admin/layouts_controller.rb
@@ -1,4 +1,7 @@
class Admin::LayoutsController < ApplicationController
+
+ layout "admin"
+
# GET /layouts
# GET /layouts.xml
def index
diff --git a/app/controllers/admin/pages_controller.rb b/app/controllers/admin/pages_controller.rb
index 2f2dd0b48..a44d1491d 100644
--- a/app/controllers/admin/pages_controller.rb
+++ b/app/controllers/admin/pages_controller.rb
@@ -1,4 +1,7 @@
class Admin::PagesController < ApplicationController
+
+ layout "admin"
+
# GET /pages
# GET /pages.xml
def index
diff --git a/app/controllers/admin/snippets_controller.rb b/app/controllers/admin/snippets_controller.rb
new file mode 100644
index 000000000..6baebe8a3
--- /dev/null
+++ b/app/controllers/admin/snippets_controller.rb
@@ -0,0 +1,85 @@
+class Admin::SnippetsController < ApplicationController
+
+ layout "admin"
+
+ # GET /snippets
+ # GET /snippets.xml
+ def index
+ @snippets = Snippet.all
+
+ respond_to do |format|
+ format.html # index.html.erb
+ format.xml { render :xml => @snippets }
+ end
+ end
+
+ # GET /snippets/1
+ # GET /snippets/1.xml
+ def show
+ @snippet = Snippet.find(params[:id])
+
+ redirect_to "/#{@snippet.name}"
+ end
+
+ # GET /snippets/new
+ # GET /snippets/new.xml
+ def new
+ @snippet = Snippet.new
+
+ respond_to do |format|
+ format.html # new.html.erb
+ format.xml { render :xml => @snippets }
+ end
+ end
+
+ # GET /snippets/1/edit
+ def edit
+ @snippet = Snippet.find(params[:id])
+ end
+
+ # POST /snippets
+ # POST /snippets.xml
+ def create
+ @snippet = Snippet.new(params[:snippet])
+
+ respond_to do |format|
+ if @snippet.save
+ flash[:notice] = 'Snippet was successfully created.'
+ format.html { redirect_to admin_snippets_url }
+ format.xml { render :xml => @snippet, :status => :created, :location => @snippets }
+ else
+ format.html { render :action => "new" }
+ format.xml { render :xml => @snippet.errors, :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ # PUT /snippets/1
+ # PUT /snippets/1.xml
+ def update
+ @snippet = Snippet.find(params[:id])
+
+ respond_to do |format|
+ if @snippet.update_attributes(params[:snippet])
+ flash[:notice] = 'Snippet was successfully updated.'
+ format.html { redirect_to admin_snippets_url }
+ format.xml { head :ok }
+ else
+ format.html { render :action => "edit" }
+ format.xml { render :xml => @snippet.errors, :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ # DELETE /snippets/1
+ # DELETE /snippets/1.xml
+ def destroy
+ @snippet = Snippet.find(params[:id])
+ @snippet.destroy
+
+ respond_to do |format|
+ format.html { redirect_to admin_snippets_url }
+ format.xml { head :ok }
+ end
+ end
+end
diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb
index 159d2e6c7..8a06afeef 100644
--- a/app/controllers/pages_controller.rb
+++ b/app/controllers/pages_controller.rb
@@ -1,5 +1,7 @@
class PagesController < ApplicationController
+ Liquid::Template.register_filter(SnippetFilter)
+
def show
@page = Page.find_by_name(params[:page_name])
@layout = @page.layout
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
new file mode 100644
index 000000000..c21f958ff
--- /dev/null
+++ b/app/models/snippet.rb
@@ -0,0 +1,8 @@
+class Snippet < CouchFoo::Base
+
+ property :name, String
+ property :content, String
+
+ validates_presence_of :name
+
+end
\ No newline at end of file
diff --git a/app/models/snippet_filter.rb b/app/models/snippet_filter.rb
new file mode 100644
index 000000000..e0b12c238
--- /dev/null
+++ b/app/models/snippet_filter.rb
@@ -0,0 +1,13 @@
+module SnippetFilter
+ def render_snippet(snippet_name)
+
+ snippet = Snippet.find_by_name(snippet_name)
+
+ if snippet
+ return Liquid::Template.parse(snippet.content).render
+ else
+ return "nothing"
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/app/views/admin/snippets/edit.html.erb b/app/views/admin/snippets/edit.html.erb
new file mode 100644
index 000000000..5511b2b11
--- /dev/null
+++ b/app/views/admin/snippets/edit.html.erb
@@ -0,0 +1,21 @@
+
Editing snippets
+
+<% form_for @snippet, :url => admin_snippet_path(@snippet) do |f| %>
+ <%= f.error_messages %>
+
+
+ <%= f.label :name, "Name" %>
+ <%= f.text_field :name %>
+
+
+
+ <%= f.label :content, "Content" %>
+ <%= f.text_area :content, :size => '100x30' %>
+
+
+
+ <%= f.submit 'Update' %>
+
+<% end %>
+
+<%= link_to 'Back', admin_snippets_path %>
\ No newline at end of file
diff --git a/app/views/admin/snippets/index.html.erb b/app/views/admin/snippets/index.html.erb
new file mode 100644
index 000000000..c8304b5cc
--- /dev/null
+++ b/app/views/admin/snippets/index.html.erb
@@ -0,0 +1,18 @@
+Listing snippets
+
+
+
+
+
+<% @snippets.each do |snippet| %>
+
+ <%= snippet.name %> |
+ <%= link_to 'Edit', edit_admin_snippet_path(snippet) %> |
+ <%= link_to 'Destroy', admin_snippet_path(snippet), :confirm => 'Are you sure?', :method => :delete %> |
+
+<% end %>
+
+
+
+
+<%= link_to 'New snippets', new_admin_snippet_path %>
\ No newline at end of file
diff --git a/app/views/admin/snippets/new.html.erb b/app/views/admin/snippets/new.html.erb
new file mode 100644
index 000000000..ef7fafa9f
--- /dev/null
+++ b/app/views/admin/snippets/new.html.erb
@@ -0,0 +1,21 @@
+New snippets
+
+<% form_for :snippet, :url => admin_snippets_path do |f| %>
+ <%= f.error_messages %>
+
+
+ <%= f.label :name, "Name" %>
+ <%= f.text_field :name %>
+
+
+
+ <%= f.label :content, "Content" %>
+ <%= f.text_area :content, :size => '100x30' %>
+
+
+
+ <%= f.submit 'Create' %>
+
+<% end %>
+
+<%= link_to 'Back', admin_snippets_path %>
\ No newline at end of file
diff --git a/app/views/layouts/pages.html.erb b/app/views/layouts/admin.html.erb
similarity index 100%
rename from app/views/layouts/pages.html.erb
rename to app/views/layouts/admin.html.erb
diff --git a/config/environment.rb b/config/environment.rb
index 7d8fd7082..67a147357 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -38,7 +38,6 @@ Rails::Initializer.run do |config|
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
# config.i18n.default_locale = :de
- config.gem "liquid"
end
diff --git a/config/routes.rb b/config/routes.rb
index 6e988cb0a..357f5d0f0 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,6 +3,7 @@ ActionController::Routing::Routes.draw do |map|
map.namespace :admin do |admin|
admin.resources :pages
admin.resources :layouts
+ admin.resources :snippets
end
# The priority is based upon order of creation: first created -> highest priority.
diff --git a/vendor/plugins/liquid/CHANGELOG b/vendor/plugins/liquid/CHANGELOG
new file mode 100644
index 000000000..02920abc3
--- /dev/null
+++ b/vendor/plugins/liquid/CHANGELOG
@@ -0,0 +1,44 @@
+* Ruby 1.9.1 bugfixes
+
+* Fix LiquidView for Rails 2.2. Fix local assigns for all versions of Rails
+
+* Fixed gem install rake task
+* Improve Error encapsulation in liquid by maintaining a own set of exceptions instead of relying on ruby build ins
+
+* Added If with or / and expressions
+
+* 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.
+
+* Added more tags to standard library
+
+* Added include tag ( like partials in rails )
+
+* [...] Gazillion of detail improvements
+
+* Added strainers as filter hosts for better security [Tobias Luetke]
+
+* Fixed that rails integration would call filter with the wrong "self" [Michael Geary]
+
+* 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]
+
+* Removed count helper from standard lib. use size [Tobias Luetke]
+
+* Fixed bug with string filter parameters failing to tolerate commas in strings. [Paul Hammond]
+
+* 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]
+
+ {{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }}
+
+
+* 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]
+
+ class ProductDrop < Liquid::Drop
+ def top_sales
+ Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
+ end
+ end
+ t = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {% endfor %} ' )
+ t.render('product' => ProductDrop.new )
+
+
+* Added filter parameters support. Example: {{ date | format_date: "%Y" }} [Paul Hammond]
diff --git a/vendor/plugins/liquid/History.txt b/vendor/plugins/liquid/History.txt
new file mode 100644
index 000000000..2f1c37f65
--- /dev/null
+++ b/vendor/plugins/liquid/History.txt
@@ -0,0 +1,44 @@
+1.9.0 / 2008-03-04
+
+* Fixed gem install rake task
+* Improve Error encapsulation in liquid by maintaining a own set of exceptions instead of relying on ruby build ins
+
+Before 1.9.0
+
+* Added If with or / and expressions
+
+* 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.
+
+* Added more tags to standard library
+
+* Added include tag ( like partials in rails )
+
+* [...] Gazillion of detail improvements
+
+* Added strainers as filter hosts for better security [Tobias Luetke]
+
+* Fixed that rails integration would call filter with the wrong "self" [Michael Geary]
+
+* 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]
+
+* Removed count helper from standard lib. use size [Tobias Luetke]
+
+* Fixed bug with string filter parameters failing to tolerate commas in strings. [Paul Hammond]
+
+* 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]
+
+ {{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }}
+
+
+* 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]
+
+ class ProductDrop < Liquid::Drop
+ def top_sales
+ Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
+ end
+ end
+ t = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {% endfor %} ' )
+ t.render('product' => ProductDrop.new )
+
+
+* Added filter parameters support. Example: {{ date | format_date: "%Y" }} [Paul Hammond]
diff --git a/vendor/plugins/liquid/MIT-LICENSE b/vendor/plugins/liquid/MIT-LICENSE
new file mode 100644
index 000000000..441ca02c3
--- /dev/null
+++ b/vendor/plugins/liquid/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2005, 2006 Tobias Luetke
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/plugins/liquid/Manifest.txt b/vendor/plugins/liquid/Manifest.txt
new file mode 100644
index 000000000..593e0bfee
--- /dev/null
+++ b/vendor/plugins/liquid/Manifest.txt
@@ -0,0 +1,34 @@
+CHANGELOG
+History.txt
+MIT-LICENSE
+Manifest.txt
+README.txt
+Rakefile
+init.rb
+lib/extras/liquid_view.rb
+lib/liquid.rb
+lib/liquid/block.rb
+lib/liquid/condition.rb
+lib/liquid/context.rb
+lib/liquid/document.rb
+lib/liquid/drop.rb
+lib/liquid/errors.rb
+lib/liquid/extensions.rb
+lib/liquid/file_system.rb
+lib/liquid/htmltags.rb
+lib/liquid/module_ex.rb
+lib/liquid/standardfilters.rb
+lib/liquid/strainer.rb
+lib/liquid/tag.rb
+lib/liquid/tags/assign.rb
+lib/liquid/tags/capture.rb
+lib/liquid/tags/case.rb
+lib/liquid/tags/comment.rb
+lib/liquid/tags/cycle.rb
+lib/liquid/tags/for.rb
+lib/liquid/tags/if.rb
+lib/liquid/tags/ifchanged.rb
+lib/liquid/tags/include.rb
+lib/liquid/tags/unless.rb
+lib/liquid/template.rb
+lib/liquid/variable.rb
diff --git a/vendor/plugins/liquid/README.txt b/vendor/plugins/liquid/README.txt
new file mode 100644
index 000000000..1d019af6f
--- /dev/null
+++ b/vendor/plugins/liquid/README.txt
@@ -0,0 +1,38 @@
+= Liquid template engine
+
+Liquid is a template engine which I wrote for very specific requirements
+
+* It has to have beautiful and simple markup.
+ Template engines which don't produce good looking markup are no fun to use.
+* 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.
+* 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.
+
+== Why should i use Liquid
+
+* 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.
+* You want to render templates directly from the database
+* You like smarty style template engines
+* You need a template engine which does HTML just as well as Emails
+* You don't like the markup of your current one
+
+== What does it look like?
+
+
+ {% for product in products %}
+ -
+
{{product.name}}
+ Only {{product.price | price }}
+
+ {{product.description | prettyprint | paragraph }}
+
+ {% endfor %}
+
+
+== Howto use Liquid
+
+Liquid supports a very simple API based around the Liquid::Template class.
+For standard use you can just pass it the content of a file and call render with a parameters hash.
+
+ @template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
+ @template.render( 'name' => 'tobi' ) # => "hi tobi"
\ No newline at end of file
diff --git a/vendor/plugins/liquid/Rakefile b/vendor/plugins/liquid/Rakefile
new file mode 100755
index 000000000..a2057ac13
--- /dev/null
+++ b/vendor/plugins/liquid/Rakefile
@@ -0,0 +1,24 @@
+#!/usr/bin/env ruby
+require 'rubygems'
+require 'rake'
+require 'hoe'
+
+PKG_VERSION = "1.9.0"
+PKG_NAME = "liquid"
+PKG_DESC = "A secure non evaling end user template engine with aesthetic markup."
+
+Rake::TestTask.new(:test) do |t|
+ t.libs << "lib"
+ t.libs << "test"
+ t.pattern = 'test/*_test.rb'
+ t.verbose = false
+end
+
+Hoe.new(PKG_NAME, PKG_VERSION) do |p|
+ p.rubyforge_name = PKG_NAME
+ p.summary = PKG_DESC
+ p.description = PKG_DESC
+ p.author = "Tobias Luetke"
+ p.email = "tobi@leetsoft.com"
+ p.url = "http://www.liquidmarkup.org"
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/example/server/example_servlet.rb b/vendor/plugins/liquid/example/server/example_servlet.rb
new file mode 100644
index 000000000..18e528e95
--- /dev/null
+++ b/vendor/plugins/liquid/example/server/example_servlet.rb
@@ -0,0 +1,37 @@
+module ProductsFilter
+ def price(integer)
+ sprintf("$%.2d USD", integer / 100.0)
+ end
+
+ def prettyprint(text)
+ text.gsub( /\*(.*)\*/, '\1' )
+ end
+
+ def count(array)
+ array.size
+ end
+
+ def paragraph(p)
+ "#{p}
"
+ end
+end
+
+class Servlet < LiquidServlet
+
+ def index
+ { 'date' => Time.now }
+ end
+
+ def products
+ { 'products' => products_list, 'section' => 'Snowboards', 'cool_products' => true}
+ end
+
+ private
+
+ def products_list
+ [{'name' => 'Arbor Draft', 'price' => 39900, 'description' => 'the *arbor draft* is a excellent product' },
+ {'name' => 'Arbor Element', 'price' => 40000, 'description' => 'the *arbor element* rocks for freestyling'},
+ {'name' => 'Arbor Diamond', 'price' => 59900, 'description' => 'the *arbor diamond* is a made up product because im obsessed with arbor and have no creativity'}]
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/example/server/liquid_servlet.rb b/vendor/plugins/liquid/example/server/liquid_servlet.rb
new file mode 100644
index 000000000..8f24f0026
--- /dev/null
+++ b/vendor/plugins/liquid/example/server/liquid_servlet.rb
@@ -0,0 +1,28 @@
+class LiquidServlet < WEBrick::HTTPServlet::AbstractServlet
+
+ def do_GET(req, res)
+ handle(:get, req, res)
+ end
+
+ def do_POST(req, res)
+ handle(:post, req, res)
+ end
+
+ private
+
+ def handle(type, req, res)
+ @request, @response = req, res
+
+ @request.path_info =~ /(\w+)$/
+ @action = $1 || 'index'
+ @assigns = send(@action) if respond_to?(@action)
+
+ @response['Content-Type'] = "text/html"
+ @response.status = 200
+ @response.body = Liquid::Template.parse(read_template).render(@assigns, :filters => [ProductsFilter])
+ end
+
+ def read_template(filename = @action)
+ File.read( File.dirname(__FILE__) + "/templates/#{filename}.liquid" )
+ end
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/example/server/server.rb b/vendor/plugins/liquid/example/server/server.rb
new file mode 100644
index 000000000..6d71c72e8
--- /dev/null
+++ b/vendor/plugins/liquid/example/server/server.rb
@@ -0,0 +1,12 @@
+require 'webrick'
+require 'rexml/document'
+
+require File.dirname(__FILE__) + '/../../lib/liquid'
+require File.dirname(__FILE__) + '/liquid_servlet'
+require File.dirname(__FILE__) + '/example_servlet'
+
+# Setup webrick
+server = WEBrick::HTTPServer.new( :Port => ARGV[1] || 3000 )
+server.mount('/', Servlet)
+trap("INT"){ server.shutdown }
+server.start
\ No newline at end of file
diff --git a/vendor/plugins/liquid/example/server/templates/index.liquid b/vendor/plugins/liquid/example/server/templates/index.liquid
new file mode 100644
index 000000000..79a52b488
--- /dev/null
+++ b/vendor/plugins/liquid/example/server/templates/index.liquid
@@ -0,0 +1,6 @@
+Hello world!
+
+It is {{date}}
+
+
+Check out the Products screen
\ No newline at end of file
diff --git a/vendor/plugins/liquid/example/server/templates/products.liquid b/vendor/plugins/liquid/example/server/templates/products.liquid
new file mode 100644
index 000000000..05af4f7de
--- /dev/null
+++ b/vendor/plugins/liquid/example/server/templates/products.liquid
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+ products
+
+
+
+
+
+
+
+
+
+
+ There are currently {{products | count}} products in the {{section}} catalog
+
+ {% if cool_products %}
+ Cool products :)
+ {% else %}
+ Uncool products :(
+ {% endif %}
+
+
+
+ {% for product in products %}
+ -
+
{{product.name}}
+ Only {{product.price | price }}
+
+ {{product.description | prettyprint | paragraph }}
+
+ {{ 'it rocks!' | paragraph }}
+
+
+ {% endfor %}
+
+
+
+
+
diff --git a/vendor/plugins/liquid/init.rb b/vendor/plugins/liquid/init.rb
new file mode 100644
index 000000000..5ef3c072e
--- /dev/null
+++ b/vendor/plugins/liquid/init.rb
@@ -0,0 +1,8 @@
+require 'liquid'
+require 'extras/liquid_view'
+
+if defined? ActionView::Template and ActionView::Template.respond_to? :register_template_handler
+ ActionView::Template
+else
+ ActionView::Base
+end.register_template_handler(:liquid, LiquidView)
diff --git a/vendor/plugins/liquid/lib/extras/liquid_view.rb b/vendor/plugins/liquid/lib/extras/liquid_view.rb
new file mode 100644
index 000000000..6b983be72
--- /dev/null
+++ b/vendor/plugins/liquid/lib/extras/liquid_view.rb
@@ -0,0 +1,51 @@
+# LiquidView is a action view extension class. You can register it with rails
+# and use liquid as an template system for .liquid files
+#
+# Example
+#
+# ActionView::Base::register_template_handler :liquid, LiquidView
+class LiquidView
+ PROTECTED_ASSIGNS = %w( template_root response _session template_class action_name request_origin session template
+ _response url _request _cookies variables_added _flash params _headers request cookies
+ ignore_missing_templates flash _params logger before_filter_chain_aborted headers )
+ PROTECTED_INSTANCE_VARIABLES = %w( @_request @controller @_first_render @_memoized__pick_template @view_paths
+ @helpers @assigns_added @template @_render_stack @template_format @assigns )
+
+ def self.call(template)
+ "LiquidView.new(self).render(template, local_assigns)"
+ end
+
+ def initialize(view)
+ @view = view
+ end
+
+ def render(template, local_assigns = nil)
+ @view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8'
+
+ # Rails 2.2 Template has source, but not locals
+ if template.respond_to?(:source) && !template.respond_to?(:locals)
+ assigns = (@view.instance_variables - PROTECTED_INSTANCE_VARIABLES).inject({}) do |hash, ivar|
+ hash[ivar[1..-1]] = @view.instance_variable_get(ivar)
+ hash
+ end
+ else
+ assigns = @view.assigns.reject{ |k,v| PROTECTED_ASSIGNS.include?(k) }
+ end
+
+ source = template.respond_to?(:source) ? template.source : template
+ local_assigns = (template.respond_to?(:locals) ? template.locals : local_assigns) || {}
+
+ if content_for_layout = @view.instance_variable_get("@content_for_layout")
+ assigns['content_for_layout'] = content_for_layout
+ end
+ assigns.merge!(local_assigns.stringify_keys)
+
+ liquid = Liquid::Template.parse(source)
+ liquid.render(assigns, :filters => [@view.controller.master_helper_module], :registers => {:action_view => @view, :controller => @view.controller})
+ end
+
+ def compilable?
+ false
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid.rb b/vendor/plugins/liquid/lib/liquid.rb
new file mode 100644
index 000000000..679d1c2b4
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid.rb
@@ -0,0 +1,68 @@
+# Copyright (c) 2005 Tobias Luetke
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+
+module Liquid
+ FilterSeparator = /\|/
+ ArgumentSeparator = ','
+ FilterArgumentSeparator = ':'
+ VariableAttributeSeparator = '.'
+ TagStart = /\{\%/
+ TagEnd = /\%\}/
+ VariableSignature = /\(?[\w\-\.\[\]]\)?/
+ VariableSegment = /[\w\-]\??/
+ VariableStart = /\{\{/
+ VariableEnd = /\}\}/
+ VariableIncompleteEnd = /\}\}?/
+ QuotedString = /"[^"]+"|'[^']+'/
+ QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/
+ StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s,\|,\:,\,]+/
+ FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/
+ OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/
+ SpacelessFilter = /#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/
+ Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/
+ TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
+ AnyStartingTag = /\{\{|\{\%/
+ PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/
+ TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/
+ VariableParser = /\[[^\]]+\]|#{VariableSegment}+/
+end
+
+require 'liquid/drop'
+require 'liquid/extensions'
+require 'liquid/errors'
+require 'liquid/strainer'
+require 'liquid/context'
+require 'liquid/tag'
+require 'liquid/block'
+require 'liquid/document'
+require 'liquid/variable'
+require 'liquid/file_system'
+require 'liquid/template'
+require 'liquid/htmltags'
+require 'liquid/standardfilters'
+require 'liquid/condition'
+require 'liquid/module_ex'
+
+# Load all the tags of the standard library
+#
+Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f }
diff --git a/vendor/plugins/liquid/lib/liquid/block.rb b/vendor/plugins/liquid/lib/liquid/block.rb
new file mode 100644
index 000000000..2d4e293f2
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/block.rb
@@ -0,0 +1,97 @@
+module Liquid
+
+ class Block < Tag
+
+ def parse(tokens)
+ @nodelist ||= []
+ @nodelist.clear
+
+ while token = tokens.shift
+
+ case token
+ when /^#{TagStart}/
+ if token =~ /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/
+
+ # if we found the proper block delimitor just end parsing here and let the outer block
+ # proceed
+ if block_delimiter == $1
+ end_tag
+ return
+ end
+
+ # fetch the tag from registered blocks
+ if tag = Template.tags[$1]
+ @nodelist << tag.new($1, $2, tokens)
+ else
+ # this tag is not registered with the system
+ # pass it to the current block for special handling or error reporting
+ unknown_tag($1, $2, tokens)
+ end
+ else
+ raise SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} "
+ end
+ when /^#{VariableStart}/
+ @nodelist << create_variable(token)
+ when ''
+ # pass
+ else
+ @nodelist << token
+ end
+ end
+
+ # Make sure that its ok to end parsing in the current block.
+ # Effectively this method will throw and exception unless the current block is
+ # of type Document
+ assert_missing_delimitation!
+ end
+
+ def end_tag
+ end
+
+ def unknown_tag(tag, params, tokens)
+ case tag
+ when 'else'
+ raise SyntaxError, "#{block_name} tag does not expect else tag"
+ when 'end'
+ raise SyntaxError, "'end' is not a valid delimiter for #{block_name} tags. use #{block_delimiter}"
+ else
+ raise SyntaxError, "Unknown tag '#{tag}'"
+ end
+ end
+
+ def block_delimiter
+ "end#{block_name}"
+ end
+
+ def block_name
+ @tag_name
+ end
+
+ def create_variable(token)
+ token.scan(/^#{VariableStart}(.*)#{VariableEnd}$/) do |content|
+ return Variable.new(content.first)
+ end
+ raise SyntaxError.new("Variable '#{token}' was not properly terminated with regexp: #{VariableEnd.inspect} ")
+ end
+
+ def render(context)
+ render_all(@nodelist, context)
+ end
+
+ protected
+
+ def assert_missing_delimitation!
+ raise SyntaxError.new("#{block_name} tag was never closed")
+ end
+
+ def render_all(list, context)
+ list.collect do |token|
+ begin
+ token.respond_to?(:render) ? token.render(context) : token
+ rescue Exception => e
+ context.handle_error(e)
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/liquid/lib/liquid/condition.rb b/vendor/plugins/liquid/lib/liquid/condition.rb
new file mode 100644
index 000000000..69224ab47
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/condition.rb
@@ -0,0 +1,120 @@
+module Liquid
+ # Container for liquid nodes which conveniently wraps decision making logic
+ #
+ # Example:
+ #
+ # c = Condition.new('1', '==', '1')
+ # c.evaluate #=> true
+ #
+ class Condition #:nodoc:
+ @@operators = {
+ '==' => lambda { |cond, left, right| cond.send(:equal_variables, left, right) },
+ '!=' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
+ '<>' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
+ '<' => :<,
+ '>' => :>,
+ '>=' => :>=,
+ '<=' => :<=,
+ 'contains' => lambda { |cond, left, right| left.include?(right) },
+ }
+
+ def self.operators
+ @@operators
+ end
+
+ attr_reader :attachment
+ attr_accessor :left, :operator, :right
+
+ def initialize(left = nil, operator = nil, right = nil)
+ @left, @operator, @right = left, operator, right
+ @child_relation = nil
+ @child_condition = nil
+ end
+
+ def evaluate(context = Context.new)
+ result = interpret_condition(left, right, operator, context)
+
+ case @child_relation
+ when :or
+ result || @child_condition.evaluate(context)
+ when :and
+ result && @child_condition.evaluate(context)
+ else
+ result
+ end
+ end
+
+ def or(condition)
+ @child_relation, @child_condition = :or, condition
+ end
+
+ def and(condition)
+ @child_relation, @child_condition = :and, condition
+ end
+
+ def attach(attachment)
+ @attachment = attachment
+ end
+
+ def else?
+ false
+ end
+
+ def inspect
+ "#"
+ end
+
+ private
+
+ def equal_variables(left, right)
+ if left.is_a?(Symbol)
+ if right.respond_to?(left)
+ return right.send(left.to_s)
+ else
+ return nil
+ end
+ end
+
+ if right.is_a?(Symbol)
+ if left.respond_to?(right)
+ return left.send(right.to_s)
+ else
+ return nil
+ end
+ end
+
+ left == right
+ end
+
+ def interpret_condition(left, right, op, context)
+ # If the operator is empty this means that the decision statement is just
+ # a single variable. We can just poll this variable from the context and
+ # return this as the result.
+ return context[left] if op == nil
+
+ left, right = context[left], context[right]
+
+ operation = self.class.operators[op] || raise(ArgumentError.new("Unknown operator #{op}"))
+
+ if operation.respond_to?(:call)
+ operation.call(self, left, right)
+ elsif left.respond_to?(operation) and right.respond_to?(operation)
+ left.send(operation, right)
+ else
+ nil
+ end
+ end
+ end
+
+
+ class ElseCondition < Condition
+ def else?
+ true
+ end
+
+ def evaluate(context)
+ true
+ end
+ end
+
+end
diff --git a/vendor/plugins/liquid/lib/liquid/context.rb b/vendor/plugins/liquid/lib/liquid/context.rb
new file mode 100644
index 000000000..603cd552c
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/context.rb
@@ -0,0 +1,232 @@
+module Liquid
+
+ # Context keeps the variable stack and resolves variables, as well as keywords
+ #
+ # context['variable'] = 'testing'
+ # context['variable'] #=> 'testing'
+ # context['true'] #=> true
+ # context['10.2232'] #=> 10.2232
+ #
+ # context.stack do
+ # context['bob'] = 'bobsen'
+ # end
+ #
+ # context['bob'] #=> nil class Context
+ class Context
+ attr_reader :scopes
+ attr_reader :errors, :registers
+
+ def initialize(assigns = {}, registers = {}, rethrow_errors = false)
+ @scopes = [(assigns || {})]
+ @registers = registers
+ @errors = []
+ @rethrow_errors = rethrow_errors
+ end
+
+ def strainer
+ @strainer ||= Strainer.create(self)
+ end
+
+ # adds filters to this context.
+ # this does not register the filters with the main Template object. see Template.register_filter
+ # for that
+ def add_filters(filters)
+ filters = [filters].flatten.compact
+
+ filters.each do |f|
+ raise ArgumentError, "Expected module but got: #{f.class}" unless f.is_a?(Module)
+ strainer.extend(f)
+ end
+ end
+
+ def handle_error(e)
+ errors.push(e)
+ raise if @rethrow_errors
+
+ case e
+ when SyntaxError
+ "Liquid syntax error: #{e.message}"
+ else
+ "Liquid error: #{e.message}"
+ end
+ end
+
+
+ def invoke(method, *args)
+ if strainer.respond_to?(method)
+ strainer.__send__(method, *args)
+ else
+ args.first
+ end
+ end
+
+ # push new local scope on the stack. use Context#stack instead
+ def push
+ raise StackLevelError, "Nesting too deep" if @scopes.length > 100
+ @scopes.unshift({})
+ end
+
+ # merge a hash of variables in the current local scope
+ def merge(new_scopes)
+ @scopes[0].merge!(new_scopes)
+ end
+
+ # pop from the stack. use Context#stack instead
+ def pop
+ raise ContextError if @scopes.size == 1
+ @scopes.shift
+ end
+
+ # pushes a new local scope on the stack, pops it at the end of the block
+ #
+ # Example:
+ #
+ # context.stack do
+ # context['var'] = 'hi'
+ # end
+ # context['var] #=> nil
+ #
+ def stack(&block)
+ result = nil
+ push
+ begin
+ result = yield
+ ensure
+ pop
+ end
+ result
+ end
+
+ # Only allow String, Numeric, Hash, Array, Proc, Boolean or Liquid::Drop
+ def []=(key, value)
+ @scopes[0][key] = value
+ end
+
+ def [](key)
+ resolve(key)
+ end
+
+ def has_key?(key)
+ resolve(key) != nil
+ end
+
+ private
+
+ # Look up variable, either resolve directly after considering the name. We can directly handle
+ # Strings, digits, floats and booleans (true,false). If no match is made we lookup the variable in the current scope and
+ # later move up to the parent blocks to see if we can resolve the variable somewhere up the tree.
+ # Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions
+ #
+ # Example:
+ #
+ # products == empty #=> products.empty?
+ #
+ def resolve(key)
+ case key
+ when nil, 'nil', 'null', ''
+ nil
+ when 'true'
+ true
+ when 'false'
+ false
+ when 'blank'
+ :blank?
+ when 'empty'
+ :empty?
+ # filtered variables
+ when SpacelessFilter
+ filtered_variable(key)
+ # Single quoted strings
+ when /^'(.*)'$/
+ $1.to_s
+ # Double quoted strings
+ when /^"(.*)"$/
+ $1.to_s
+ # Integer and floats
+ when /^(\d+)$/
+ $1.to_i
+ # Ranges
+ when /^\((\S+)\.\.(\S+)\)$/
+ (resolve($1).to_i..resolve($2).to_i)
+ # Floats
+ when /^(\d[\d\.]+)$/
+ $1.to_f
+ else
+ variable(key)
+ end
+ end
+
+ # fetches an object starting at the local scope and then moving up
+ # the hierachy
+ def find_variable(key)
+ @scopes.each do |scope|
+ if scope.has_key?(key)
+ variable = scope[key]
+ variable = scope[key] = variable.call(self) if variable.is_a?(Proc)
+ variable = variable.to_liquid
+ variable.context = self if variable.respond_to?(:context=)
+ return variable
+ end
+ end
+ nil
+ end
+
+ # resolves namespaced queries gracefully.
+ #
+ # Example
+ #
+ # @context['hash'] = {"name" => 'tobi'}
+ # assert_equal 'tobi', @context['hash.name']
+ # assert_equal 'tobi', @context['hash["name"]']
+ #
+ def variable(markup)
+ parts = markup.scan(VariableParser)
+ square_bracketed = /^\[(.*)\]$/
+
+ first_part = parts.shift
+ if first_part =~ square_bracketed
+ first_part = resolve($1)
+ end
+
+ if object = find_variable(first_part)
+
+ parts.each do |part|
+ part = resolve($1) if part_resolved = (part =~ square_bracketed)
+
+ # If object is a hash- or array-like object we look for the
+ # presence of the key and if its available we return it
+ if object.respond_to?(:[]) and
+ ((object.respond_to?(:has_key?) and object.has_key?(part)) or
+ (object.respond_to?(:fetch) and part.is_a?(Integer)))
+
+ # if its a proc we will replace the entry with the proc
+ res = object[part]
+ res = object[part] = res.call(self) if res.is_a?(Proc) and object.respond_to?(:[]=)
+ object = res.to_liquid
+
+ # Some special cases. If the part wasn't in square brackets and
+ # no key with the same name was found we interpret following calls
+ # as commands and call them on the current object
+ elsif !part_resolved and object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
+
+ object = object.send(part.intern).to_liquid
+
+ # No key was present with the desired value and it wasn't one of the directly supported
+ # keywords either. The only thing we got left is to return nil
+ else
+ return nil
+ end
+
+ # If we are dealing with a drop here we have to
+ object.context = self if object.respond_to?(:context=)
+ end
+ end
+
+ object
+ end
+
+ def filtered_variable(markup)
+ Variable.new(markup).render(self)
+ end
+ end
+end
diff --git a/vendor/plugins/liquid/lib/liquid/document.rb b/vendor/plugins/liquid/lib/liquid/document.rb
new file mode 100644
index 000000000..bf95478d8
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/document.rb
@@ -0,0 +1,17 @@
+module Liquid
+ class Document < Block
+ # we don't need markup to open this block
+ def initialize(tokens)
+ parse(tokens)
+ end
+
+ # There isn't a real delimter
+ def block_delimiter
+ []
+ end
+
+ # Document blocks don't need to be terminated since they are not actually opened
+ def assert_missing_delimitation!
+ end
+ end
+end
diff --git a/vendor/plugins/liquid/lib/liquid/drop.rb b/vendor/plugins/liquid/lib/liquid/drop.rb
new file mode 100644
index 000000000..3accfd8a1
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/drop.rb
@@ -0,0 +1,51 @@
+module Liquid
+
+ # A drop in liquid is a class which allows you to to export DOM like things to liquid
+ # Methods of drops are callable.
+ # The main use for liquid drops is the implement lazy loaded objects.
+ # If you would like to make data available to the web designers which you don't want loaded unless needed then
+ # a drop is a great way to do that
+ #
+ # Example:
+ #
+ # class ProductDrop < Liquid::Drop
+ # def top_sales
+ # Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
+ # end
+ # end
+ #
+ # tmpl = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {%endfor%} ' )
+ # tmpl.render('product' => ProductDrop.new ) # will invoke top_sales query.
+ #
+ # Your drop can either implement the methods sans any parameters or implement the before_method(name) method which is a
+ # catch all
+ class Drop
+ attr_writer :context
+
+ # Catch all for the method
+ def before_method(method)
+ nil
+ end
+
+ # called by liquid to invoke a drop
+ def invoke_drop(method)
+ # for backward compatibility with Ruby 1.8
+ methods = self.class.public_instance_methods.map { |m| m.to_s }
+ if methods.include?(method.to_s)
+ send(method.to_sym)
+ else
+ before_method(method)
+ end
+ end
+
+ def has_key?(name)
+ true
+ end
+
+ def to_liquid
+ self
+ end
+
+ alias :[] :invoke_drop
+ end
+end
diff --git a/vendor/plugins/liquid/lib/liquid/errors.rb b/vendor/plugins/liquid/lib/liquid/errors.rb
new file mode 100644
index 000000000..ce4ca7e67
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/errors.rb
@@ -0,0 +1,11 @@
+module Liquid
+ class Error < ::StandardError; end
+
+ class ArgumentError < Error; end
+ class ContextError < Error; end
+ class FilterNotFound < Error; end
+ class FileSystemError < Error; end
+ class StandardError < Error; end
+ class SyntaxError < Error; end
+ class StackLevelError < Error; end
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/extensions.rb b/vendor/plugins/liquid/lib/liquid/extensions.rb
new file mode 100644
index 000000000..2752ba372
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/extensions.rb
@@ -0,0 +1,56 @@
+require 'time'
+require 'date'
+
+class String # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Array # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Hash # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Numeric # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Time # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class DateTime < Date # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Date # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+def true.to_liquid # :nodoc:
+ self
+end
+
+def false.to_liquid # :nodoc:
+ self
+end
+
+def nil.to_liquid # :nodoc:
+ self
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/file_system.rb b/vendor/plugins/liquid/lib/liquid/file_system.rb
new file mode 100644
index 000000000..8c6b76ddd
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/file_system.rb
@@ -0,0 +1,62 @@
+module Liquid
+ # A Liquid file system is way to let your templates retrieve other templates for use with the include tag.
+ #
+ # You can implement subclasses that retrieve templates from the database, from the file system using a different
+ # path structure, you can provide them as hard-coded inline strings, or any manner that you see fit.
+ #
+ # You can add additional instance variables, arguments, or methods as needed.
+ #
+ # Example:
+ #
+ # Liquid::Template.file_system = Liquid::LocalFileSystem.new(template_path)
+ # liquid = Liquid::Template.parse(template)
+ #
+ # This will parse the template with a LocalFileSystem implementation rooted at 'template_path'.
+ class BlankFileSystem
+ # Called by Liquid to retrieve a template file
+ def read_template_file(template_path)
+ raise FileSystemError, "This liquid context does not allow includes."
+ end
+ end
+
+ # This implements an abstract file system which retrieves template files named in a manner similar to Rails partials,
+ # ie. with the template name prefixed with an underscore. The extension ".liquid" is also added.
+ #
+ # For security reasons, template paths are only allowed to contain letters, numbers, and underscore.
+ #
+ # Example:
+ #
+ # file_system = Liquid::LocalFileSystem.new("/some/path")
+ #
+ # file_system.full_path("mypartial") # => "/some/path/_mypartial.liquid"
+ # file_system.full_path("dir/mypartial") # => "/some/path/dir/_mypartial.liquid"
+ #
+ class LocalFileSystem
+ attr_accessor :root
+
+ def initialize(root)
+ @root = root
+ end
+
+ def read_template_file(template_path)
+ full_path = full_path(template_path)
+ raise FileSystemError, "No such template '#{template_path}'" unless File.exists?(full_path)
+
+ File.read(full_path)
+ end
+
+ def full_path(template_path)
+ raise FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ /^[^.\/][a-zA-Z0-9_\/]+$/
+
+ full_path = if template_path.include?('/')
+ File.join(root, File.dirname(template_path), "_#{File.basename(template_path)}.liquid")
+ else
+ File.join(root, "_#{template_path}.liquid")
+ end
+
+ raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path) =~ /^#{File.expand_path(root)}/
+
+ full_path
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/htmltags.rb b/vendor/plugins/liquid/lib/liquid/htmltags.rb
new file mode 100644
index 000000000..c6db036f1
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/htmltags.rb
@@ -0,0 +1,74 @@
+module Liquid
+ class TableRow < Block
+ Syntax = /(\w+)\s+in\s+(#{VariableSignature}+)/
+
+ def initialize(tag_name, markup, tokens)
+ if markup =~ Syntax
+ @variable_name = $1
+ @collection_name = $2
+ @attributes = {}
+ markup.scan(TagAttributes) do |key, value|
+ @attributes[key] = value
+ end
+ else
+ raise SyntaxError.new("Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3")
+ end
+
+ super
+ end
+
+ def render(context)
+ collection = context[@collection_name] or return ''
+
+ if @attributes['limit'] or @attributes['offset']
+ limit = context[@attributes['limit']] || -1
+ offset = context[@attributes['offset']] || 0
+ collection = collection[offset.to_i..(limit.to_i + offset.to_i - 1)]
+ end
+
+ length = collection.length
+
+ cols = context[@attributes['cols']].to_i
+
+ row = 1
+ col = 0
+
+ result = ["\n"]
+ context.stack do
+
+ collection.each_with_index do |item, index|
+ context[@variable_name] = item
+ context['tablerowloop'] = {
+ 'length' => length,
+ 'index' => index + 1,
+ 'index0' => index,
+ 'col' => col + 1,
+ 'col0' => col,
+ 'index0' => index,
+ 'rindex' => length - index,
+ 'rindex0' => length - index -1,
+ 'first' => (index == 0),
+ 'last' => (index == length - 1),
+ 'col_first' => (col == 0),
+ 'col_last' => (col == cols - 1)
+ }
+
+
+ col += 1
+
+ result << [""] + render_all(@nodelist, context) + [' | ']
+
+ if col == cols and not (index == length - 1)
+ col = 0
+ row += 1
+ result << ["
\n"]
+ end
+
+ end
+ end
+ result + ["
\n"]
+ end
+ end
+
+ Template.register_tag('tablerow', TableRow)
+end
diff --git a/vendor/plugins/liquid/lib/liquid/module_ex.rb b/vendor/plugins/liquid/lib/liquid/module_ex.rb
new file mode 100644
index 000000000..c504ea109
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/module_ex.rb
@@ -0,0 +1,62 @@
+# Copyright 2007 by Domizio Demichelis
+# This library is free software. It may be used, redistributed and/or modified
+# under the same terms as Ruby itself
+#
+# This extension is usesd in order to expose the object of the implementing class
+# to liquid as it were a Drop. It also limits the liquid-callable methods of the instance
+# to the allowed method passed with the liquid_methods call
+# Example:
+#
+# class SomeClass
+# liquid_methods :an_allowed_method
+#
+# def an_allowed_method
+# 'this comes from an allowed method'
+# end
+# def unallowed_method
+# 'this will never be an output'
+# end
+# end
+#
+# if you want to extend the drop to other methods you can defines more methods
+# in the class ::LiquidDropClass
+#
+# class SomeClass::LiquidDropClass
+# def another_allowed_method
+# 'and this from another allowed method'
+# end
+# end
+# end
+#
+# usage:
+# @something = SomeClass.new
+#
+# template:
+# {{something.an_allowed_method}}{{something.unallowed_method}} {{something.another_allowed_method}}
+#
+# output:
+# 'this comes from an allowed method and this from another allowed method'
+#
+# You can also chain associations, by adding the liquid_method call in the
+# association models.
+#
+class Module
+
+ def liquid_methods(*allowed_methods)
+ drop_class = eval "class #{self.to_s}::LiquidDropClass < Liquid::Drop; self; end"
+ define_method :to_liquid do
+ drop_class.new(self)
+ end
+ drop_class.class_eval do
+ def initialize(object)
+ @object = object
+ end
+ allowed_methods.each do |sym|
+ define_method sym do
+ @object.send sym
+ end
+ end
+ end
+ end
+
+end
diff --git a/vendor/plugins/liquid/lib/liquid/standardfilters.rb b/vendor/plugins/liquid/lib/liquid/standardfilters.rb
new file mode 100644
index 000000000..19ace466f
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/standardfilters.rb
@@ -0,0 +1,209 @@
+require 'cgi'
+
+module Liquid
+
+ module StandardFilters
+
+ # Return the size of an array or of an string
+ def size(input)
+
+ input.respond_to?(:size) ? input.size : 0
+ end
+
+ # convert a input string to DOWNCASE
+ def downcase(input)
+ input.to_s.downcase
+ end
+
+ # convert a input string to UPCASE
+ def upcase(input)
+ input.to_s.upcase
+ end
+
+ # capitalize words in the input centence
+ def capitalize(input)
+ input.to_s.capitalize
+ end
+
+ def escape(input)
+ CGI.escapeHTML(input) rescue input
+ end
+
+ alias_method :h, :escape
+
+ # Truncate a string down to x characters
+ def truncate(input, length = 50, truncate_string = "...")
+ if input.nil? then return end
+ l = length.to_i - truncate_string.length
+ l = 0 if l < 0
+ input.length > length.to_i ? input[0...l] + truncate_string : input
+ end
+
+ def truncatewords(input, words = 15, truncate_string = "...")
+ if input.nil? then return end
+ wordlist = input.to_s.split
+ l = words.to_i - 1
+ l = 0 if l < 0
+ wordlist.length > l ? wordlist[0..l].join(" ") + truncate_string : input
+ end
+
+ def strip_html(input)
+ input.to_s.gsub(/<.*?>/, '')
+ end
+
+ # Remove all newlines from the string
+ def strip_newlines(input)
+ input.to_s.gsub(/\n/, '')
+ end
+
+
+ # Join elements of the array with certain character between them
+ def join(input, glue = ' ')
+ [input].flatten.join(glue)
+ end
+
+ # Sort elements of the array
+ # provide optional property with which to sort an array of hashes or drops
+ def sort(input, property = nil)
+ ary = [input].flatten
+ if property.nil?
+ ary.sort
+ elsif ary.first.respond_to?('[]') and !ary.first[property].nil?
+ ary.sort {|a,b| a[property] <=> b[property] }
+ elsif ary.first.respond_to?(property)
+ ary.sort {|a,b| a.send(property) <=> b.send(property) }
+ end
+ end
+
+ # map/collect on a given property
+ def map(input, property)
+ ary = [input].flatten
+ if ary.first.respond_to?('[]') and !ary.first[property].nil?
+ ary.map {|e| e[property] }
+ elsif ary.first.respond_to?(property)
+ ary.map {|e| e.send(property) }
+ end
+ end
+
+ # Replace occurrences of a string with another
+ def replace(input, string, replacement = '')
+ input.to_s.gsub(string, replacement)
+ end
+
+ # Replace the first occurrences of a string with another
+ def replace_first(input, string, replacement = '')
+ input.to_s.sub(string, replacement)
+ end
+
+ # remove a substring
+ def remove(input, string)
+ input.to_s.gsub(string, '')
+ end
+
+ # remove the first occurrences of a substring
+ def remove_first(input, string)
+ input.to_s.sub(string, '')
+ end
+
+ # add one string to another
+ def append(input, string)
+ input.to_s + string.to_s
+ end
+
+ # prepend a string to another
+ def prepend(input, string)
+ string.to_s + input.to_s
+ end
+
+ # Add
tags in front of all newlines in input string
+ def newline_to_br(input)
+ input.to_s.gsub(/\n/, "
\n")
+ end
+
+ # Reformat a date
+ #
+ # %a - The abbreviated weekday name (``Sun'')
+ # %A - The full weekday name (``Sunday'')
+ # %b - The abbreviated month name (``Jan'')
+ # %B - The full month name (``January'')
+ # %c - The preferred local date and time representation
+ # %d - Day of the month (01..31)
+ # %H - Hour of the day, 24-hour clock (00..23)
+ # %I - Hour of the day, 12-hour clock (01..12)
+ # %j - Day of the year (001..366)
+ # %m - Month of the year (01..12)
+ # %M - Minute of the hour (00..59)
+ # %p - Meridian indicator (``AM'' or ``PM'')
+ # %S - Second of the minute (00..60)
+ # %U - Week number of the current year,
+ # starting with the first Sunday as the first
+ # day of the first week (00..53)
+ # %W - Week number of the current year,
+ # starting with the first Monday as the first
+ # day of the first week (00..53)
+ # %w - Day of the week (Sunday is 0, 0..6)
+ # %x - Preferred representation for the date alone, no time
+ # %X - Preferred representation for the time alone, no date
+ # %y - Year without a century (00..99)
+ # %Y - Year with century
+ # %Z - Time zone name
+ # %% - Literal ``%'' character
+ def date(input, format)
+
+ if format.to_s.empty?
+ return input.to_s
+ end
+
+ date = input.is_a?(String) ? Time.parse(input) : input
+
+ if date.respond_to?(:strftime)
+ date.strftime(format.to_s)
+ else
+ input
+ end
+ rescue => e
+ input
+ end
+
+ # Get the first element of the passed in array
+ #
+ # Example:
+ # {{ product.images | first | to_img }}
+ #
+ def first(array)
+ array.first if array.respond_to?(:first)
+ end
+
+ # Get the last element of the passed in array
+ #
+ # Example:
+ # {{ product.images | last | to_img }}
+ #
+ def last(array)
+ array.last if array.respond_to?(:last)
+ end
+
+ # addition
+ def plus(input, operand)
+ input + operand if input.respond_to?('+')
+ end
+
+ # subtraction
+ def minus(input, operand)
+ input - operand if input.respond_to?('-')
+ end
+
+ # multiplication
+ def times(input, operand)
+ input * operand if input.respond_to?('*')
+ end
+
+ # division
+ def divided_by(input, operand)
+ input / operand if input.respond_to?('/')
+ end
+
+ end
+
+ Template.register_filter(StandardFilters)
+end
diff --git a/vendor/plugins/liquid/lib/liquid/strainer.rb b/vendor/plugins/liquid/lib/liquid/strainer.rb
new file mode 100644
index 000000000..b6dad262e
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/strainer.rb
@@ -0,0 +1,51 @@
+require 'set'
+
+module Liquid
+
+ parent_object = if defined? BlankObject
+ BlankObject
+ else
+ Object
+ end
+
+ # Strainer is the parent class for the filters system.
+ # New filters are mixed into the strainer class which is then instanciated for each liquid template render run.
+ #
+ # One of the strainer's responsibilities is to keep malicious method calls out
+ class Strainer < parent_object #:nodoc:
+ INTERNAL_METHOD = /^__/
+ @@required_methods = Set.new([:__id__, :__send__, :respond_to?, :extend, :methods, :class, :object_id])
+
+ @@filters = {}
+
+ def initialize(context)
+ @context = context
+ end
+
+ def self.global_filter(filter)
+ raise ArgumentError, "Passed filter is not a module" unless filter.is_a?(Module)
+ @@filters[filter.name] = filter
+ end
+
+ def self.create(context)
+ strainer = Strainer.new(context)
+ @@filters.each { |k,m| strainer.extend(m) }
+ strainer
+ end
+
+ def respond_to?(method, include_private = false)
+ method_name = method.to_s
+ return false if method_name =~ INTERNAL_METHOD
+ return false if @@required_methods.include?(method_name)
+ super
+ end
+
+ # remove all standard methods from the bucket so circumvent security
+ # problems
+ instance_methods.each do |m|
+ unless @@required_methods.include?(m.to_sym)
+ undef_method m
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/liquid/lib/liquid/tag.rb b/vendor/plugins/liquid/lib/liquid/tag.rb
new file mode 100644
index 000000000..2750064f0
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tag.rb
@@ -0,0 +1,26 @@
+module Liquid
+
+ class Tag
+ attr_accessor :nodelist
+
+ def initialize(tag_name, markup, tokens)
+ @tag_name = tag_name
+ @markup = markup
+ parse(tokens)
+ end
+
+ def parse(tokens)
+ end
+
+ def name
+ self.class.name.downcase
+ end
+
+ def render(context)
+ ''
+ end
+ end
+
+
+end
+
diff --git a/vendor/plugins/liquid/lib/liquid/tags/assign.rb b/vendor/plugins/liquid/lib/liquid/tags/assign.rb
new file mode 100644
index 000000000..7b48b84f6
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tags/assign.rb
@@ -0,0 +1,33 @@
+module Liquid
+
+ # Assign sets a variable in your template.
+ #
+ # {% assign foo = 'monkey' %}
+ #
+ # You can then use the variable later in the page.
+ #
+ # {{ monkey }}
+ #
+ class Assign < Tag
+ Syntax = /(#{VariableSignature}+)\s*=\s*(#{Expression}+)/
+
+ def initialize(tag_name, markup, tokens)
+ if markup =~ Syntax
+ @to = $1
+ @from = $2
+ else
+ raise SyntaxError.new("Syntax Error in 'assign' - Valid syntax: assign [var] = [source]")
+ end
+
+ super
+ end
+
+ def render(context)
+ context.scopes.last[@to.to_s] = context[@from]
+ ''
+ end
+
+ end
+
+ Template.register_tag('assign', Assign)
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/tags/capture.rb b/vendor/plugins/liquid/lib/liquid/tags/capture.rb
new file mode 100644
index 000000000..92b142ff1
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tags/capture.rb
@@ -0,0 +1,35 @@
+module Liquid
+
+ # Capture stores the result of a block into a variable without rendering it inplace.
+ #
+ # {% capture heading %}
+ # Monkeys!
+ # {% endcapture %}
+ # ...
+ # {{ monkeys }}
+ #
+ # Capture is useful for saving content for use later in your template, such as
+ # in a sidebar or footer.
+ #
+ class Capture < Block
+ Syntax = /(\w+)/
+
+ def initialize(tag_name, markup, tokens)
+ if markup =~ Syntax
+ @to = $1
+ else
+ raise SyntaxError.new("Syntax Error in 'capture' - Valid syntax: capture [var]")
+ end
+
+ super
+ end
+
+ def render(context)
+ output = super
+ context[@to] = output.join
+ ''
+ end
+ end
+
+ Template.register_tag('capture', Capture)
+end
diff --git a/vendor/plugins/liquid/lib/liquid/tags/case.rb b/vendor/plugins/liquid/lib/liquid/tags/case.rb
new file mode 100644
index 000000000..f6f24d4db
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tags/case.rb
@@ -0,0 +1,83 @@
+module Liquid
+ class Case < Block
+ Syntax = /(#{Expression})/
+ WhenSyntax = /(#{Expression})(?:(?:\s+or\s+|\s*\,\s*)(#{Expression}.*))?/
+
+ def initialize(tag_name, markup, tokens)
+ @blocks = []
+
+ if markup =~ Syntax
+ @left = $1
+ else
+ raise SyntaxError.new("Syntax Error in tag 'case' - Valid syntax: case [condition]")
+ end
+
+ super
+ end
+
+ def unknown_tag(tag, markup, tokens)
+ @nodelist = []
+ case tag
+ when 'when'
+ record_when_condition(markup)
+ when 'else'
+ record_else_condition(markup)
+ else
+ super
+ end
+ end
+
+ def render(context)
+ context.stack do
+ execute_else_block = true
+
+ @blocks.inject([]) do |output, block|
+
+ if block.else?
+
+ return render_all(block.attachment, context) if execute_else_block
+
+ elsif block.evaluate(context)
+
+ execute_else_block = false
+ output += render_all(block.attachment, context)
+ end
+
+ output
+ end
+ end
+ end
+
+ private
+
+ def record_when_condition(markup)
+ while markup
+ # Create a new nodelist and assign it to the new block
+ if not markup =~ WhenSyntax
+ raise SyntaxError.new("Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %} ")
+ end
+
+ markup = $2
+
+ block = Condition.new(@left, '==', $1)
+ block.attach(@nodelist)
+ @blocks.push(block)
+ end
+ end
+
+ def record_else_condition(markup)
+
+ if not markup.strip.empty?
+ raise SyntaxError.new("Syntax Error in tag 'case' - Valid else condition: {% else %} (no parameters) ")
+ end
+
+ block = ElseCondition.new
+ block.attach(@nodelist)
+ @blocks << block
+ end
+
+
+ end
+
+ Template.register_tag('case', Case)
+end
diff --git a/vendor/plugins/liquid/lib/liquid/tags/comment.rb b/vendor/plugins/liquid/lib/liquid/tags/comment.rb
new file mode 100644
index 000000000..8ce7e0eb8
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tags/comment.rb
@@ -0,0 +1,9 @@
+module Liquid
+ class Comment < Block
+ def render(context)
+ ''
+ end
+ end
+
+ Template.register_tag('comment', Comment)
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/tags/cycle.rb b/vendor/plugins/liquid/lib/liquid/tags/cycle.rb
new file mode 100644
index 000000000..64d6d209f
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tags/cycle.rb
@@ -0,0 +1,59 @@
+module Liquid
+
+ # Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
+ #
+ # {% for item in items %}
+ # {{ item }}
+ # {% end %}
+ #
+ # Item one
+ # Item two
+ # Item three
+ # Item four
+ # Item five
+ #
+ class Cycle < Tag
+ SimpleSyntax = /^#{Expression}/
+ NamedSyntax = /^(#{Expression})\s*\:\s*(.*)/
+
+ def initialize(tag_name, markup, tokens)
+ case markup
+ when NamedSyntax
+ @variables = variables_from_string($2)
+ @name = $1
+ when SimpleSyntax
+ @variables = variables_from_string(markup)
+ @name = "'#{@variables.to_s}'"
+ else
+ raise SyntaxError.new("Syntax Error in 'cycle' - Valid syntax: cycle [name :] var [, var2, var3 ...]")
+ end
+ super
+ end
+
+ def render(context)
+ context.registers[:cycle] ||= Hash.new(0)
+
+ context.stack do
+ key = context[@name]
+ iteration = context.registers[:cycle][key]
+ result = context[@variables[iteration]]
+ iteration += 1
+ iteration = 0 if iteration >= @variables.size
+ context.registers[:cycle][key] = iteration
+ result
+ end
+ end
+
+ private
+
+ def variables_from_string(markup)
+ markup.split(',').collect do |var|
+ var =~ /\s*(#{Expression})\s*/
+ $1 ? $1 : nil
+ end.compact
+ end
+
+ end
+
+ Template.register_tag('cycle', Cycle)
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/tags/for.rb b/vendor/plugins/liquid/lib/liquid/tags/for.rb
new file mode 100644
index 000000000..4ff867134
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tags/for.rb
@@ -0,0 +1,136 @@
+module Liquid
+
+ # "For" iterates over an array or collection.
+ # Several useful variables are available to you within the loop.
+ #
+ # == Basic usage:
+ # {% for item in collection %}
+ # {{ forloop.index }}: {{ item.name }}
+ # {% endfor %}
+ #
+ # == Advanced usage:
+ # {% for item in collection %}
+ #
+ # Item {{ forloop.index }}: {{ item.name }}
+ #
+ # {% endfor %}
+ #
+ # You can also define a limit and offset much like SQL. Remember
+ # that offset starts at 0 for the first item.
+ #
+ # {% for item in collection limit:5 offset:10 %}
+ # {{ item.name }}
+ # {% end %}
+ #
+ # To reverse the for loop simply use {% for item in collection reversed %}
+ #
+ # == Available variables:
+ #
+ # forloop.name:: 'item-collection'
+ # forloop.length:: Length of the loop
+ # forloop.index:: The current item's position in the collection;
+ # forloop.index starts at 1.
+ # This is helpful for non-programmers who start believe
+ # the first item in an array is 1, not 0.
+ # forloop.index0:: The current item's position in the collection
+ # where the first item is 0
+ # forloop.rindex:: Number of items remaining in the loop
+ # (length - index) where 1 is the last item.
+ # forloop.rindex0:: Number of items remaining in the loop
+ # where 0 is the last item.
+ # forloop.first:: Returns true if the item is the first item.
+ # forloop.last:: Returns true if the item is the last item.
+ #
+ class For < Block
+ Syntax = /(\w+)\s+in\s+(#{Expression}+)\s*(reversed)?/
+
+ def initialize(tag_name, markup, tokens)
+ if markup =~ Syntax
+ @variable_name = $1
+ @collection_name = $2
+ @name = "#{$1}-#{$2}"
+ @reversed = $3
+ @attributes = {}
+ markup.scan(TagAttributes) do |key, value|
+ @attributes[key] = value
+ end
+ else
+ raise SyntaxError.new("Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]")
+ end
+
+ super
+ end
+
+ def render(context)
+ context.registers[:for] ||= Hash.new(0)
+
+ collection = context[@collection_name]
+ collection = collection.to_a if collection.is_a?(Range)
+
+ return '' unless collection.respond_to?(:each)
+
+ from = if @attributes['offset'] == 'continue'
+ context.registers[:for][@name].to_i
+ else
+ context[@attributes['offset']].to_i
+ end
+
+ limit = context[@attributes['limit']]
+ to = limit ? limit.to_i + from : nil
+
+
+ segment = slice_collection_using_each(collection, from, to)
+
+ return '' if segment.empty?
+
+ segment.reverse! if @reversed
+
+ result = []
+
+ length = segment.length
+
+ # Store our progress through the collection for the continue flag
+ context.registers[:for][@name] = from + segment.length
+
+ context.stack do
+ segment.each_with_index do |item, index|
+ context[@variable_name] = item
+ context['forloop'] = {
+ 'name' => @name,
+ 'length' => length,
+ 'index' => index + 1,
+ 'index0' => index,
+ 'rindex' => length - index,
+ 'rindex0' => length - index -1,
+ 'first' => (index == 0),
+ 'last' => (index == length - 1) }
+
+ result << render_all(@nodelist, context)
+ end
+ end
+ result
+ end
+
+ def slice_collection_using_each(collection, from, to)
+ segments = []
+ index = 0
+ yielded = 0
+ collection.each do |item|
+
+ if to && to <= index
+ break
+ end
+
+ if from <= index
+ segments << item
+ end
+
+ index += 1
+ end
+
+ segments
+ end
+ end
+
+ Template.register_tag('for', For)
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/tags/if.rb b/vendor/plugins/liquid/lib/liquid/tags/if.rb
new file mode 100644
index 000000000..a84d56777
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tags/if.rb
@@ -0,0 +1,79 @@
+module Liquid
+
+ # If is the conditional block
+ #
+ # {% if user.admin %}
+ # Admin user!
+ # {% else %}
+ # Not admin user
+ # {% endif %}
+ #
+ # There are {% if count < 5 %} less {% else %} more {% endif %} items than you need.
+ #
+ #
+ class If < Block
+ SyntaxHelp = "Syntax Error in tag 'if' - Valid syntax: if [expression]"
+ Syntax = /(#{Expression})\s*([=!<>a-z_]+)?\s*(#{Expression})?/
+
+ def initialize(tag_name, markup, tokens)
+
+ @blocks = []
+
+ push_block('if', markup)
+
+ super
+ end
+
+ def unknown_tag(tag, markup, tokens)
+ if ['elsif', 'else'].include?(tag)
+ push_block(tag, markup)
+ else
+ super
+ end
+ end
+
+ def render(context)
+ context.stack do
+ @blocks.each do |block|
+ if block.evaluate(context)
+ return render_all(block.attachment, context)
+ end
+ end
+ ''
+ end
+ end
+
+ private
+
+ def push_block(tag, markup)
+ block = if tag == 'else'
+ ElseCondition.new
+ else
+
+ expressions = markup.split(/\b(and|or)\b/).reverse
+ raise(SyntaxError, SyntaxHelp) unless expressions.shift =~ Syntax
+
+ condition = Condition.new($1, $2, $3)
+
+ while not expressions.empty?
+ operator = expressions.shift
+
+ raise(SyntaxError, SyntaxHelp) unless expressions.shift.to_s =~ Syntax
+
+ new_condition = Condition.new($1, $2, $3)
+ new_condition.send(operator.to_sym, condition)
+ condition = new_condition
+ end
+
+ condition
+ end
+
+ @blocks.push(block)
+ @nodelist = block.attach(Array.new)
+ end
+
+
+ end
+
+ Template.register_tag('if', If)
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/tags/ifchanged.rb b/vendor/plugins/liquid/lib/liquid/tags/ifchanged.rb
new file mode 100644
index 000000000..a4406c6f6
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tags/ifchanged.rb
@@ -0,0 +1,20 @@
+module Liquid
+ class Ifchanged < Block
+
+ def render(context)
+ context.stack do
+
+ output = render_all(@nodelist, context)
+
+ if output != context.registers[:ifchanged]
+ context.registers[:ifchanged] = output
+ output
+ else
+ ''
+ end
+ end
+ end
+ end
+
+ Template.register_tag('ifchanged', Ifchanged)
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/tags/include.rb b/vendor/plugins/liquid/lib/liquid/tags/include.rb
new file mode 100644
index 000000000..2f9439fa3
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tags/include.rb
@@ -0,0 +1,55 @@
+module Liquid
+ class Include < Tag
+ Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/
+
+ def initialize(tag_name, markup, tokens)
+ if markup =~ Syntax
+
+ @template_name = $1
+ @variable_name = $3
+ @attributes = {}
+
+ markup.scan(TagAttributes) do |key, value|
+ @attributes[key] = value
+ end
+
+ else
+ raise SyntaxError.new("Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]")
+ end
+
+ super
+ end
+
+ def parse(tokens)
+ end
+
+ def render(context)
+ source = Liquid::Template.file_system.read_template_file(context[@template_name])
+ partial = Liquid::Template.parse(source)
+
+ variable = context[@variable_name || @template_name[1..-2]]
+
+ context.stack do
+ @attributes.each do |key, value|
+ context[key] = context[value]
+ end
+
+ if variable.is_a?(Array)
+
+ variable.collect do |variable|
+ context[@template_name[1..-2]] = variable
+ partial.render(context)
+ end
+
+ else
+
+ context[@template_name[1..-2]] = variable
+ partial.render(context)
+
+ end
+ end
+ end
+ end
+
+ Template.register_tag('include', Include)
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/tags/unless.rb b/vendor/plugins/liquid/lib/liquid/tags/unless.rb
new file mode 100644
index 000000000..74a76abda
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/tags/unless.rb
@@ -0,0 +1,33 @@
+require File.dirname(__FILE__) + '/if'
+
+module Liquid
+
+ # Unless is a conditional just like 'if' but works on the inverse logic.
+ #
+ # {% unless x < 0 %} x is greater than zero {% end %}
+ #
+ class Unless < If
+ def render(context)
+ context.stack do
+
+ # First condition is interpreted backwards ( if not )
+ block = @blocks.first
+ unless block.evaluate(context)
+ return render_all(block.attachment, context)
+ end
+
+ # After the first condition unless works just like if
+ @blocks[1..-1].each do |block|
+ if block.evaluate(context)
+ return render_all(block.attachment, context)
+ end
+ end
+
+ ''
+ end
+ end
+ end
+
+
+ Template.register_tag('unless', Unless)
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/lib/liquid/template.rb b/vendor/plugins/liquid/lib/liquid/template.rb
new file mode 100644
index 000000000..c0f189c9d
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/template.rb
@@ -0,0 +1,146 @@
+module Liquid
+
+ # Templates are central to liquid.
+ # Interpretating templates is a two step process. First you compile the
+ # source code you got. During compile time some extensive error checking is performed.
+ # your code should expect to get some SyntaxErrors.
+ #
+ # After you have a compiled template you can then render it.
+ # You can use a compiled template over and over again and keep it cached.
+ #
+ # Example:
+ #
+ # template = Liquid::Template.parse(source)
+ # template.render('user_name' => 'bob')
+ #
+ class Template
+ attr_accessor :root
+ @@file_system = BlankFileSystem.new
+
+ class << self
+ def file_system
+ @@file_system
+ end
+
+ def file_system=(obj)
+ @@file_system = obj
+ end
+
+ def register_tag(name, klass)
+ tags[name.to_s] = klass
+ end
+
+ def tags
+ @tags ||= {}
+ end
+
+ # Pass a module with filter methods which should be available
+ # to all liquid views. Good for registering the standard library
+ def register_filter(mod)
+ Strainer.global_filter(mod)
+ end
+
+ # creates a new Template object from liquid source code
+ def parse(source)
+ template = Template.new
+ template.parse(source)
+ template
+ end
+ end
+
+ # creates a new Template from an array of tokens. Use Template.parse instead
+ def initialize
+ end
+
+ # Parse source code.
+ # Returns self for easy chaining
+ def parse(source)
+ @root = Document.new(tokenize(source))
+ self
+ end
+
+ def registers
+ @registers ||= {}
+ end
+
+ def assigns
+ @assigns ||= {}
+ end
+
+ def errors
+ @errors ||= []
+ end
+
+ # Render takes a hash with local variables.
+ #
+ # if you use the same filters over and over again consider registering them globally
+ # with Template.register_filter
+ #
+ # Following options can be passed:
+ #
+ # * filters : array with local filters
+ # * registers : hash with register variables. Those can be accessed from
+ # filters and tags and might be useful to integrate liquid more with its host application
+ #
+ def render(*args)
+ return '' if @root.nil?
+
+ context = case args.first
+ when Liquid::Context
+ args.shift
+ when Hash
+ self.assigns.merge!(args.shift)
+ Context.new(assigns, registers, @rethrow_errors)
+ when nil
+ Context.new(assigns, registers, @rethrow_errors)
+ else
+ raise ArgumentError, "Expect Hash or Liquid::Context as parameter"
+ end
+
+ case args.last
+ when Hash
+ options = args.pop
+
+ if options[:registers].is_a?(Hash)
+ self.registers.merge!(options[:registers])
+ end
+
+ if options[:filters]
+ context.add_filters(options[:filters])
+ end
+
+ when Module
+ context.add_filters(args.pop)
+ when Array
+ context.add_filters(args.pop)
+ end
+
+ begin
+ # render the nodelist.
+ # for performance reasons we get a array back here. join will make a string out of it
+ @root.render(context).join
+ ensure
+ @errors = context.errors
+ end
+ end
+
+ def render!(*args)
+ @rethrow_errors = true; render(*args)
+ end
+
+ private
+
+ # Uses the Liquid::TemplateParser regexp to tokenize the passed source
+ def tokenize(source)
+ source = source.source if source.respond_to?(:source)
+ return [] if source.to_s.empty?
+ tokens = source.split(TemplateParser)
+
+ # removes the rogue empty element at the beginning of the array
+ tokens.shift if tokens[0] and tokens[0].empty?
+
+ tokens
+ end
+
+ end
+end
diff --git a/vendor/plugins/liquid/lib/liquid/variable.rb b/vendor/plugins/liquid/lib/liquid/variable.rb
new file mode 100644
index 000000000..fb39ed7b3
--- /dev/null
+++ b/vendor/plugins/liquid/lib/liquid/variable.rb
@@ -0,0 +1,49 @@
+module Liquid
+
+ # Holds variables. Variables are only loaded "just in time"
+ # and are not evaluated as part of the render stage
+ #
+ # {{ monkey }}
+ # {{ user.name }}
+ #
+ # Variables can be combined with filters:
+ #
+ # {{ user | link }}
+ #
+ class Variable
+ attr_accessor :filters, :name
+
+ def initialize(markup)
+ @markup = markup
+ @name = nil
+ @filters = []
+ if match = markup.match(/\s*(#{QuotedFragment})/)
+ @name = match[1]
+ if markup.match(/#{FilterSeparator}\s*(.*)/)
+ filters = Regexp.last_match(1).split(/#{FilterSeparator}/)
+ filters.each do |f|
+ if matches = f.match(/\s*(\w+)/)
+ filtername = matches[1]
+ filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*(#{QuotedFragment})/).flatten
+ @filters << [filtername.to_sym, filterargs]
+ end
+ end
+ end
+ end
+ end
+
+ def render(context)
+ return '' if @name.nil?
+ @filters.inject(context[@name]) do |output, filter|
+ filterargs = filter[1].to_a.collect do |a|
+ context[a]
+ end
+ begin
+ output = context.invoke(filter[0], output, *filterargs)
+ rescue FilterNotFound
+ raise FilterNotFound, "Error - filter '#{filter[0]}' in '#{@markup.strip}' could not be found."
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/liquid/liquid.gemspec b/vendor/plugins/liquid/liquid.gemspec
new file mode 100644
index 000000000..773c797aa
--- /dev/null
+++ b/vendor/plugins/liquid/liquid.gemspec
@@ -0,0 +1,29 @@
+Gem::Specification.new do |s|
+ s.name = %q{liquid}
+ s.version = "2.0.1"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Tobias Luetke"]
+ s.date = %q{2009-04-13}
+ s.description = %q{A secure non evaling end user template engine with aesthetic markup.}
+ s.email = %q{tobi@leetsoft.com}
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
+ s.files = ["CHANGELOG", "History.txt", "MIT-LICENSE", "Manifest.txt", "README.txt", "Rakefile", "lib/extras/liquid_view.rb", "lib/liquid.rb", "lib/liquid/block.rb", "lib/liquid/condition.rb", "lib/liquid/context.rb", "lib/liquid/document.rb", "lib/liquid/drop.rb", "lib/liquid/errors.rb", "lib/liquid/extensions.rb", "lib/liquid/file_system.rb", "lib/liquid/htmltags.rb", "lib/liquid/module_ex.rb", "lib/liquid/standardfilters.rb", "lib/liquid/strainer.rb", "lib/liquid/tag.rb", "lib/liquid/tags/assign.rb", "lib/liquid/tags/capture.rb", "lib/liquid/tags/case.rb", "lib/liquid/tags/comment.rb", "lib/liquid/tags/cycle.rb", "lib/liquid/tags/for.rb", "lib/liquid/tags/if.rb", "lib/liquid/tags/ifchanged.rb", "lib/liquid/tags/include.rb", "lib/liquid/tags/unless.rb", "lib/liquid/template.rb", "lib/liquid/variable.rb"]
+ s.has_rdoc = true
+ s.homepage = %q{http://www.liquidmarkup.org}
+ s.rdoc_options = ["--main", "README.txt"]
+ s.require_paths = ["lib"]
+ s.rubyforge_project = %q{liquid}
+ s.rubygems_version = %q{1.3.1}
+ s.summary = %q{A secure non evaling end user template engine with aesthetic markup.}
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 2
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ else
+ end
+ else
+ end
+end
diff --git a/vendor/plugins/liquid/test/assign_test.rb b/vendor/plugins/liquid/test/assign_test.rb
new file mode 100644
index 000000000..15d00e530
--- /dev/null
+++ b/vendor/plugins/liquid/test/assign_test.rb
@@ -0,0 +1,11 @@
+require File.dirname(__FILE__) + '/helper'
+
+class IfElseTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_with_filtered_expressions
+ assert_template_result('foo','{% assign foo = values|sort|last %}{{ foo }}', 'values' => %w{foo bar baz})
+ assert_template_result('foo','{% assign sorted = values|sort %}{{ sorted | last }}', 'values' => %w{foo bar baz})
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/block_test.rb b/vendor/plugins/liquid/test/block_test.rb
new file mode 100644
index 000000000..270938e51
--- /dev/null
+++ b/vendor/plugins/liquid/test/block_test.rb
@@ -0,0 +1,58 @@
+require File.dirname(__FILE__) + '/helper'
+
+class VariableTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_blankspace
+ template = Liquid::Template.parse(" ")
+ assert_equal [" "], template.root.nodelist
+ end
+
+ def test_variable_beginning
+ template = Liquid::Template.parse("{{funk}} ")
+ assert_equal 2, template.root.nodelist.size
+ assert_equal Variable, template.root.nodelist[0].class
+ assert_equal String, template.root.nodelist[1].class
+ end
+
+ def test_variable_end
+ template = Liquid::Template.parse(" {{funk}}")
+ assert_equal 2, template.root.nodelist.size
+ assert_equal String, template.root.nodelist[0].class
+ assert_equal Variable, template.root.nodelist[1].class
+ end
+
+ def test_variable_middle
+ template = Liquid::Template.parse(" {{funk}} ")
+ assert_equal 3, template.root.nodelist.size
+ assert_equal String, template.root.nodelist[0].class
+ assert_equal Variable, template.root.nodelist[1].class
+ assert_equal String, template.root.nodelist[2].class
+ end
+
+ def test_variable_many_embedded_fragments
+ template = Liquid::Template.parse(" {{funk}} {{so}} {{brother}} ")
+ assert_equal 7, template.root.nodelist.size
+ assert_equal [String, Variable, String, Variable, String, Variable, String], block_types(template.root.nodelist)
+ end
+
+ def test_with_block
+ template = Liquid::Template.parse(" {% comment %} {% endcomment %} ")
+ assert_equal [String, Comment, String], block_types(template.root.nodelist)
+ assert_equal 3, template.root.nodelist.size
+ end
+
+ def test_with_custom_tag
+ Liquid::Template.register_tag("testtag", Block)
+
+ assert_nothing_thrown do
+ template = Liquid::Template.parse( "{% testtag %} {% endtesttag %}")
+ end
+ end
+
+ private
+
+ def block_types(nodelist)
+ nodelist.collect { |node| node.class }
+ end
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/condition_test.rb b/vendor/plugins/liquid/test/condition_test.rb
new file mode 100644
index 000000000..7739bd9b4
--- /dev/null
+++ b/vendor/plugins/liquid/test/condition_test.rb
@@ -0,0 +1,109 @@
+require File.dirname(__FILE__) + '/helper'
+
+class ConditionTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_basic_condition
+ assert_equal false, Condition.new('1', '==', '2').evaluate
+ assert_equal true, Condition.new('1', '==', '1').evaluate
+ end
+
+ def test_default_operators_evalute_true
+ assert_evalutes_true '1', '==', '1'
+ assert_evalutes_true '1', '!=', '2'
+ assert_evalutes_true '1', '<>', '2'
+ assert_evalutes_true '1', '<', '2'
+ assert_evalutes_true '2', '>', '1'
+ assert_evalutes_true '1', '>=', '1'
+ assert_evalutes_true '2', '>=', '1'
+ assert_evalutes_true '1', '<=', '2'
+ assert_evalutes_true '1', '<=', '1'
+ end
+
+ def test_default_operators_evalute_false
+ assert_evalutes_false '1', '==', '2'
+ assert_evalutes_false '1', '!=', '1'
+ assert_evalutes_false '1', '<>', '1'
+ assert_evalutes_false '1', '<', '0'
+ assert_evalutes_false '2', '>', '4'
+ assert_evalutes_false '1', '>=', '3'
+ assert_evalutes_false '2', '>=', '4'
+ assert_evalutes_false '1', '<=', '0'
+ assert_evalutes_false '1', '<=', '0'
+ end
+
+ def test_contains_works_on_strings
+ assert_evalutes_true "'bob'", 'contains', "'o'"
+ assert_evalutes_true "'bob'", 'contains', "'b'"
+ assert_evalutes_true "'bob'", 'contains', "'bo'"
+ assert_evalutes_true "'bob'", 'contains', "'ob'"
+ assert_evalutes_true "'bob'", 'contains', "'bob'"
+
+ assert_evalutes_false "'bob'", 'contains', "'bob2'"
+ assert_evalutes_false "'bob'", 'contains', "'a'"
+ assert_evalutes_false "'bob'", 'contains', "'---'"
+ end
+
+ def test_contains_works_on_arrays
+ @context = Liquid::Context.new
+ @context['array'] = [1,2,3,4,5]
+
+ assert_evalutes_false "array", 'contains', '0'
+ assert_evalutes_true "array", 'contains', '1'
+ assert_evalutes_true "array", 'contains', '2'
+ assert_evalutes_true "array", 'contains', '3'
+ assert_evalutes_true "array", 'contains', '4'
+ assert_evalutes_true "array", 'contains', '5'
+ assert_evalutes_false "array", 'contains', '6'
+
+ assert_evalutes_false "array", 'contains', '"1"'
+
+ end
+
+ def test_or_condition
+ condition = Condition.new('1', '==', '2')
+
+ assert_equal false, condition.evaluate
+
+ condition.or Condition.new('2', '==', '1')
+
+ assert_equal false, condition.evaluate
+
+ condition.or Condition.new('1', '==', '1')
+
+ assert_equal true, condition.evaluate
+ end
+
+ def test_and_condition
+ condition = Condition.new('1', '==', '1')
+
+ assert_equal true, condition.evaluate
+
+ condition.and Condition.new('2', '==', '2')
+
+ assert_equal true, condition.evaluate
+
+ condition.and Condition.new('2', '==', '1')
+
+ assert_equal false, condition.evaluate
+ end
+
+
+ def test_should_allow_custom_proc_operator
+ Condition.operators['starts_with'] = Proc.new { |cond, left, right| left =~ %r{^#{right}}}
+
+ assert_evalutes_true "'bob'", 'starts_with', "'b'"
+ assert_evalutes_false "'bob'", 'starts_with', "'o'"
+ ensure
+ Condition.operators.delete 'starts_with'
+ end
+
+ private
+ def assert_evalutes_true(left, op, right)
+ assert Condition.new(left, op, right).evaluate(@context || Liquid::Context.new), "Evaluated false: #{left} #{op} #{right}"
+ end
+
+ def assert_evalutes_false(left, op, right)
+ assert !Condition.new(left, op, right).evaluate(@context || Liquid::Context.new), "Evaluated true: #{left} #{op} #{right}"
+ end
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/context_test.rb b/vendor/plugins/liquid/test/context_test.rb
new file mode 100644
index 000000000..5c23376d6
--- /dev/null
+++ b/vendor/plugins/liquid/test/context_test.rb
@@ -0,0 +1,480 @@
+require File.dirname(__FILE__) + '/helper'
+class HundredCentes
+ def to_liquid
+ 100
+ end
+end
+
+class CentsDrop < Liquid::Drop
+ def amount
+ HundredCentes.new
+ end
+
+ def non_zero?
+ true
+ end
+end
+
+class ContextSensitiveDrop < Liquid::Drop
+ def test
+ @context['test']
+ end
+end
+
+class Category < Liquid::Drop
+ attr_accessor :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ def to_liquid
+ CategoryDrop.new(self)
+ end
+end
+
+class CategoryDrop
+ attr_accessor :category, :context
+ def initialize(category)
+ @category = category
+ end
+end
+
+class CounterDrop < Liquid::Drop
+ def count
+ @count ||= 0
+ @count += 1
+ end
+end
+
+class ArrayLike
+ def fetch(index)
+ end
+
+ def [](index)
+ @counts ||= []
+ @counts[index] ||= 0
+ @counts[index] += 1
+ end
+
+ def to_liquid
+ self
+ end
+end
+
+
+class ContextTest < Test::Unit::TestCase
+ include Liquid
+
+ def setup
+ @template = Liquid::Template.new
+ @context = Liquid::Context.new(@template.assigns, @template.registers)
+ end
+
+ def test_variables
+ @context['string'] = 'string'
+ assert_equal 'string', @context['string']
+
+ @context['num'] = 5
+ assert_equal 5, @context['num']
+
+ @context['time'] = Time.parse('2006-06-06 12:00:00')
+ assert_equal Time.parse('2006-06-06 12:00:00'), @context['time']
+
+ @context['date'] = Date.today
+ assert_equal Date.today, @context['date']
+
+ now = DateTime.now
+ @context['datetime'] = now
+ assert_equal now, @context['datetime']
+
+ @context['bool'] = true
+ assert_equal true, @context['bool']
+
+ @context['bool'] = false
+ assert_equal false, @context['bool']
+
+ @context['nil'] = nil
+ assert_equal nil, @context['nil']
+ assert_equal nil, @context['nil']
+ end
+
+ def test_variables_not_existing
+ assert_equal nil, @context['does_not_exist']
+ end
+
+ def test_scoping
+ assert_nothing_raised do
+ @context.push
+ @context.pop
+ end
+
+ assert_raise(Liquid::ContextError) do
+ @context.pop
+ end
+
+ assert_raise(Liquid::ContextError) do
+ @context.push
+ @context.pop
+ @context.pop
+ end
+ end
+
+ def test_length_query
+
+ @context['numbers'] = [1,2,3,4]
+
+ assert_equal 4, @context['numbers.size']
+
+ @context['numbers'] = {1 => 1,2 => 2,3 => 3,4 => 4}
+
+ assert_equal 4, @context['numbers.size']
+
+ @context['numbers'] = {1 => 1,2 => 2,3 => 3,4 => 4, 'size' => 1000}
+
+ assert_equal 1000, @context['numbers.size']
+
+ end
+
+ def test_hyphenated_variable
+
+ @context['oh-my'] = 'godz'
+ assert_equal 'godz', @context['oh-my']
+
+ end
+
+ def test_add_filter
+
+ filter = Module.new do
+ def hi(output)
+ output + ' hi!'
+ end
+ end
+
+ context = Context.new(@template)
+ context.add_filters(filter)
+ assert_equal 'hi? hi!', context.invoke(:hi, 'hi?')
+
+ context = Context.new(@template)
+ assert_equal 'hi?', context.invoke(:hi, 'hi?')
+
+ context.add_filters(filter)
+ assert_equal 'hi? hi!', context.invoke(:hi, 'hi?')
+
+ end
+
+ def test_override_global_filter
+ global = Module.new do
+ def notice(output)
+ "Global #{output}"
+ end
+ end
+
+ local = Module.new do
+ def notice(output)
+ "Local #{output}"
+ end
+ end
+
+ Template.register_filter(global)
+ assert_equal 'Global test', Template.parse("{{'test' | notice }}").render
+ assert_equal 'Local test', Template.parse("{{'test' | notice }}").render({}, :filters => [local])
+ end
+
+ def test_only_intended_filters_make_it_there
+
+ filter = Module.new do
+ def hi(output)
+ output + ' hi!'
+ end
+ end
+
+ context = Context.new(@template)
+ methods_before = context.strainer.methods.map { |method| method.to_s }
+ context.add_filters(filter)
+ methods_after = context.strainer.methods.map { |method| method.to_s }
+ assert_equal (methods_before + ["hi"]).sort, methods_after.sort
+ end
+
+ def test_add_item_in_outer_scope
+ @context['test'] = 'test'
+ @context.push
+ assert_equal 'test', @context['test']
+ @context.pop
+ assert_equal 'test', @context['test']
+ end
+
+ def test_add_item_in_inner_scope
+ @context.push
+ @context['test'] = 'test'
+ assert_equal 'test', @context['test']
+ @context.pop
+ assert_equal nil, @context['test']
+ end
+
+ def test_hierachical_data
+ @context['hash'] = {"name" => 'tobi'}
+ assert_equal 'tobi', @context['hash.name']
+ assert_equal 'tobi', @context['hash["name"]']
+ end
+
+ def test_keywords
+ assert_equal true, @context['true']
+ assert_equal false, @context['false']
+ end
+
+ def test_digits
+ assert_equal 100, @context['100']
+ assert_equal 100.00, @context['100.00']
+ end
+
+ def test_strings
+ assert_equal "hello!", @context['"hello!"']
+ assert_equal "hello!", @context["'hello!'"]
+ end
+
+ def test_merge
+ @context.merge({ "test" => "test" })
+ assert_equal 'test', @context['test']
+ @context.merge({ "test" => "newvalue", "foo" => "bar" })
+ assert_equal 'newvalue', @context['test']
+ assert_equal 'bar', @context['foo']
+ end
+
+ def test_array_notation
+ @context['test'] = [1,2,3,4,5]
+
+ assert_equal 1, @context['test[0]']
+ assert_equal 2, @context['test[1]']
+ assert_equal 3, @context['test[2]']
+ assert_equal 4, @context['test[3]']
+ assert_equal 5, @context['test[4]']
+ end
+
+ def test_recoursive_array_notation
+ @context['test'] = {'test' => [1,2,3,4,5]}
+
+ assert_equal 1, @context['test.test[0]']
+
+ @context['test'] = [{'test' => 'worked'}]
+
+ assert_equal 'worked', @context['test[0].test']
+ end
+
+ def test_hash_to_array_transition
+ @context['colors'] = {
+ 'Blue' => ['003366','336699', '6699CC', '99CCFF'],
+ 'Green' => ['003300','336633', '669966', '99CC99'],
+ 'Yellow' => ['CC9900','FFCC00', 'FFFF99', 'FFFFCC'],
+ 'Red' => ['660000','993333', 'CC6666', 'FF9999']
+ }
+
+ assert_equal '003366', @context['colors.Blue[0]']
+ assert_equal 'FF9999', @context['colors.Red[3]']
+ end
+
+ def test_try_first
+ @context['test'] = [1,2,3,4,5]
+
+ assert_equal 1, @context['test.first']
+ assert_equal 5, @context['test.last']
+
+ @context['test'] = {'test' => [1,2,3,4,5]}
+
+ assert_equal 1, @context['test.test.first']
+ assert_equal 5, @context['test.test.last']
+
+ @context['test'] = [1]
+ assert_equal 1, @context['test.first']
+ assert_equal 1, @context['test.last']
+ end
+
+ def test_access_hashes_with_hash_notation
+ @context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
+ @context['product'] = {'variants' => [ {'title' => 'draft151cm'}, {'title' => 'element151cm'} ]}
+
+ assert_equal 5, @context['products["count"]']
+ assert_equal 'deepsnow', @context['products["tags"][0]']
+ assert_equal 'deepsnow', @context['products["tags"].first']
+ assert_equal 'draft151cm', @context['product["variants"][0]["title"]']
+ assert_equal 'element151cm', @context['product["variants"][1]["title"]']
+ assert_equal 'draft151cm', @context['product["variants"][0]["title"]']
+ assert_equal 'element151cm', @context['product["variants"].last["title"]']
+ end
+
+ def test_access_variable_with_hash_notation
+ @context['foo'] = 'baz'
+ @context['bar'] = 'foo'
+
+ assert_equal 'baz', @context['["foo"]']
+ assert_equal 'baz', @context['[bar]']
+ end
+
+ def test_access_hashes_with_hash_access_variables
+
+ @context['var'] = 'tags'
+ @context['nested'] = {'var' => 'tags'}
+ @context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
+
+ assert_equal 'deepsnow', @context['products[var].first']
+ assert_equal 'freestyle', @context['products[nested.var].last']
+ end
+
+ def test_hash_notation_only_for_hash_access
+ @context['array'] = [1,2,3,4,5]
+ @context['hash'] = {'first' => 'Hello'}
+
+ assert_equal 1, @context['array.first']
+ assert_equal nil, @context['array["first"]']
+ assert_equal 'Hello', @context['hash["first"]']
+ end
+
+ def test_first_can_appear_in_middle_of_callchain
+
+ @context['product'] = {'variants' => [ {'title' => 'draft151cm'}, {'title' => 'element151cm'} ]}
+
+ assert_equal 'draft151cm', @context['product.variants[0].title']
+ assert_equal 'element151cm', @context['product.variants[1].title']
+ assert_equal 'draft151cm', @context['product.variants.first.title']
+ assert_equal 'element151cm', @context['product.variants.last.title']
+
+ end
+
+ def test_cents
+ @context.merge( "cents" => HundredCentes.new )
+ assert_equal 100, @context['cents']
+ end
+
+ def test_nested_cents
+ @context.merge( "cents" => { 'amount' => HundredCentes.new} )
+ assert_equal 100, @context['cents.amount']
+
+ @context.merge( "cents" => { 'cents' => { 'amount' => HundredCentes.new} } )
+ assert_equal 100, @context['cents.cents.amount']
+ end
+
+ def test_cents_through_drop
+ @context.merge( "cents" => CentsDrop.new )
+ assert_equal 100, @context['cents.amount']
+ end
+
+ def test_nested_cents_through_drop
+ @context.merge( "vars" => {"cents" => CentsDrop.new} )
+ assert_equal 100, @context['vars.cents.amount']
+ end
+
+ def test_drop_methods_with_question_marks
+ @context.merge( "cents" => CentsDrop.new )
+ assert @context['cents.non_zero?']
+ end
+
+ def test_context_from_within_drop
+ @context.merge( "test" => '123', "vars" => ContextSensitiveDrop.new )
+ assert_equal '123', @context['vars.test']
+ end
+
+ def test_nested_context_from_within_drop
+ @context.merge( "test" => '123', "vars" => {"local" => ContextSensitiveDrop.new } )
+ assert_equal '123', @context['vars.local.test']
+ end
+
+ def test_ranges
+ @context.merge( "test" => '5' )
+ assert_equal (1..5), @context['(1..5)']
+ assert_equal (1..5), @context['(1..test)']
+ assert_equal (5..5), @context['(test..test)']
+ end
+
+ def test_cents_through_drop_nestedly
+ @context.merge( "cents" => {"cents" => CentsDrop.new} )
+ assert_equal 100, @context['cents.cents.amount']
+
+ @context.merge( "cents" => { "cents" => {"cents" => CentsDrop.new}} )
+ assert_equal 100, @context['cents.cents.cents.amount']
+ end
+
+ def test_drop_with_variable_called_only_once
+ @context['counter'] = CounterDrop.new
+
+ assert_equal 1, @context['counter.count']
+ assert_equal 2, @context['counter.count']
+ assert_equal 3, @context['counter.count']
+ end
+
+ def test_drop_with_key_called_only_once
+ @context['counter'] = CounterDrop.new
+
+ assert_equal 1, @context['counter["count"]']
+ assert_equal 2, @context['counter["count"]']
+ assert_equal 3, @context['counter["count"]']
+ end
+
+ def test_proc_as_variable
+ @context['dynamic'] = Proc.new { 'Hello' }
+
+ assert_equal 'Hello', @context['dynamic']
+ end
+
+ def test_lambda_as_variable
+ @context['dynamic'] = proc { 'Hello' }
+
+ assert_equal 'Hello', @context['dynamic']
+ end
+
+ def test_nested_lambda_as_variable
+ @context['dynamic'] = { "lambda" => proc { 'Hello' } }
+
+ assert_equal 'Hello', @context['dynamic.lambda']
+ end
+
+ def test_array_containing_lambda_as_variable
+ @context['dynamic'] = [1,2, proc { 'Hello' } ,4,5]
+
+ assert_equal 'Hello', @context['dynamic[2]']
+ end
+
+ def test_lambda_is_called_once
+ @context['callcount'] = proc { @global ||= 0; @global += 1; @global.to_s }
+
+ assert_equal '1', @context['callcount']
+ assert_equal '1', @context['callcount']
+ assert_equal '1', @context['callcount']
+
+ @global = nil
+ end
+
+ def test_nested_lambda_is_called_once
+ @context['callcount'] = { "lambda" => proc { @global ||= 0; @global += 1; @global.to_s } }
+
+ assert_equal '1', @context['callcount.lambda']
+ assert_equal '1', @context['callcount.lambda']
+ assert_equal '1', @context['callcount.lambda']
+
+ @global = nil
+ end
+
+ def test_lambda_in_array_is_called_once
+ @context['callcount'] = [1,2, proc { @global ||= 0; @global += 1; @global.to_s } ,4,5]
+
+ assert_equal '1', @context['callcount[2]']
+ assert_equal '1', @context['callcount[2]']
+ assert_equal '1', @context['callcount[2]']
+
+ @global = nil
+ end
+
+ def test_access_to_context_from_proc
+ @context.registers[:magic] = 345392
+
+ @context['magic'] = proc { @context.registers[:magic] }
+
+ assert_equal 345392, @context['magic']
+ end
+
+ def test_to_liquid_and_context_at_first_level
+ @context['category'] = Category.new("foobar")
+ assert_kind_of CategoryDrop, @context['category']
+ assert_equal @context, @context['category'].context
+ end
+
+end
diff --git a/vendor/plugins/liquid/test/drop_test.rb b/vendor/plugins/liquid/test/drop_test.rb
new file mode 100644
index 000000000..78a2f736d
--- /dev/null
+++ b/vendor/plugins/liquid/test/drop_test.rb
@@ -0,0 +1,162 @@
+
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+class ContextDrop < Liquid::Drop
+ def scopes
+ @context.scopes.size
+ end
+
+ def scopes_as_array
+ (1..@context.scopes.size).to_a
+ end
+
+ def loop_pos
+ @context['forloop.index']
+ end
+
+ def break
+ Breakpoint.breakpoint
+ end
+
+ def before_method(method)
+ return @context[method]
+ end
+end
+
+
+class ProductDrop < Liquid::Drop
+
+ class TextDrop < Liquid::Drop
+ def array
+ ['text1', 'text2']
+ end
+
+ def text
+ 'text1'
+ end
+ end
+
+ class CatchallDrop < Liquid::Drop
+ def before_method(method)
+ return 'method: ' << method
+ end
+ end
+
+ def texts
+ TextDrop.new
+ end
+
+ def catchall
+ CatchallDrop.new
+ end
+
+ def context
+ ContextDrop.new
+ end
+
+ protected
+ def callmenot
+ "protected"
+ end
+end
+
+class EnumerableDrop < Liquid::Drop
+
+ def size
+ 3
+ end
+
+ def each
+ yield 1
+ yield 2
+ yield 3
+ end
+end
+
+
+class DropsTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_product_drop
+
+ assert_nothing_raised do
+ tpl = Liquid::Template.parse( ' ' )
+ tpl.render('product' => ProductDrop.new)
+ end
+ end
+
+ def test_text_drop
+ output = Liquid::Template.parse( ' {{ product.texts.text }} ' ).render('product' => ProductDrop.new)
+ assert_equal ' text1 ', output
+
+ end
+
+ def test_text_drop
+ output = Liquid::Template.parse( ' {{ product.catchall.unknown }} ' ).render('product' => ProductDrop.new)
+ assert_equal ' method: unknown ', output
+
+ end
+
+ def test_text_array_drop
+ output = Liquid::Template.parse( '{% for text in product.texts.array %} {{text}} {% endfor %}' ).render('product' => ProductDrop.new)
+ assert_equal ' text1 text2 ', output
+ end
+
+ def test_context_drop
+ output = Liquid::Template.parse( ' {{ context.bar }} ' ).render('context' => ContextDrop.new, 'bar' => "carrot")
+ assert_equal ' carrot ', output
+ end
+
+ def test_nested_context_drop
+ output = Liquid::Template.parse( ' {{ product.context.foo }} ' ).render('product' => ProductDrop.new, 'foo' => "monkey")
+ assert_equal ' monkey ', output
+ end
+
+ def test_protected
+ output = Liquid::Template.parse( ' {{ product.callmenot }} ' ).render('product' => ProductDrop.new)
+ assert_equal ' ', output
+ end
+
+ def test_scope
+ assert_equal '1', Liquid::Template.parse( '{{ context.scopes }}' ).render('context' => ContextDrop.new)
+ assert_equal '2', Liquid::Template.parse( '{%for i in dummy%}{{ context.scopes }}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1])
+ assert_equal '3', Liquid::Template.parse( '{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1])
+ end
+
+ def test_scope_though_proc
+ assert_equal '1', Liquid::Template.parse( '{{ s }}' ).render('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] })
+ assert_equal '2', Liquid::Template.parse( '{%for i in dummy%}{{ s }}{%endfor%}' ).render('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] }, 'dummy' => [1])
+ assert_equal '3', Liquid::Template.parse( '{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}' ).render('context' => ContextDrop.new, 's' => Proc.new{|c| c['context.scopes'] }, 'dummy' => [1])
+ end
+
+ def test_scope_with_assigns
+ assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{{a}}' ).render('context' => ContextDrop.new)
+ assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}' ).render('context' => ContextDrop.new, 'dummy' => [1])
+ assert_equal 'test', Liquid::Template.parse( '{% assign header_gif = "test"%}{{header_gif}}' ).render('context' => ContextDrop.new)
+ assert_equal 'test', Liquid::Template.parse( "{% assign header_gif = 'test'%}{{header_gif}}" ).render('context' => ContextDrop.new)
+ end
+
+ def test_scope_from_tags
+ assert_equal '1', Liquid::Template.parse( '{% for i in context.scopes_as_array %}{{i}}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1])
+ assert_equal '12', Liquid::Template.parse( '{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1])
+ assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1])
+ end
+
+ def test_access_context_from_drop
+ assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{{ context.loop_pos }}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1,2,3])
+ end
+
+ def test_enumerable_drop
+ assert_equal '123', Liquid::Template.parse( '{% for c in collection %}{{c}}{% endfor %}').render('collection' => EnumerableDrop.new)
+ end
+
+ def test_enumerable_drop_size
+ assert_equal '3', Liquid::Template.parse( '{{collection.size}}').render('collection' => EnumerableDrop.new)
+ end
+
+
+
+end
+
+
diff --git a/vendor/plugins/liquid/test/error_handling_test.rb b/vendor/plugins/liquid/test/error_handling_test.rb
new file mode 100644
index 000000000..b361d540a
--- /dev/null
+++ b/vendor/plugins/liquid/test/error_handling_test.rb
@@ -0,0 +1,89 @@
+
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+class ErrorDrop < Liquid::Drop
+ def standard_error
+ raise Liquid::StandardError, 'standard error'
+ end
+
+ def argument_error
+ raise Liquid::ArgumentError, 'argument error'
+ end
+
+ def syntax_error
+ raise Liquid::SyntaxError, 'syntax error'
+ end
+
+end
+
+
+class ErrorHandlingTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_standard_error
+ assert_nothing_raised do
+ template = Liquid::Template.parse( ' {{ errors.standard_error }} ' )
+ assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)
+
+ assert_equal 1, template.errors.size
+ assert_equal StandardError, template.errors.first.class
+ end
+ end
+
+ def test_syntax
+
+ assert_nothing_raised do
+
+ template = Liquid::Template.parse( ' {{ errors.syntax_error }} ' )
+ assert_equal ' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new)
+
+ assert_equal 1, template.errors.size
+ assert_equal SyntaxError, template.errors.first.class
+
+ end
+
+ end
+
+ def test_argument
+
+ assert_nothing_raised do
+
+ template = Liquid::Template.parse( ' {{ errors.argument_error }} ' )
+ assert_equal ' Liquid error: argument error ', template.render('errors' => ErrorDrop.new)
+
+ assert_equal 1, template.errors.size
+ assert_equal ArgumentError, template.errors.first.class
+
+ end
+
+ end
+
+ def test_missing_endtag_parse_time_error
+
+ assert_raise(Liquid::SyntaxError) do
+
+ template = Liquid::Template.parse(' {% for a in b %} ... ')
+
+ end
+
+ end
+
+
+ def test_unrecognized_operator
+
+ assert_nothing_raised do
+
+ template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ')
+ assert_equal ' Liquid error: Unknown operator =! ', template.render
+
+ assert_equal 1, template.errors.size
+ assert_equal Liquid::ArgumentError, template.errors.first.class
+
+ end
+
+ end
+
+end
+
+
diff --git a/vendor/plugins/liquid/test/extra/breakpoint.rb b/vendor/plugins/liquid/test/extra/breakpoint.rb
new file mode 100755
index 000000000..c118e228a
--- /dev/null
+++ b/vendor/plugins/liquid/test/extra/breakpoint.rb
@@ -0,0 +1,547 @@
+# The Breakpoint library provides the convenience of
+# being able to inspect and modify state, diagnose
+# bugs all via IRB by simply setting breakpoints in
+# your applications by the call of a method.
+#
+# This library was written and is supported by me,
+# Florian Gross. I can be reached at flgr@ccan.de
+# and enjoy getting feedback about my libraries.
+#
+# The whole library (including breakpoint_client.rb
+# and binding_of_caller.rb) is licensed under the
+# same license that Ruby uses. (Which is currently
+# either the GNU General Public License or a custom
+# one that allows for commercial usage.) If you for
+# some good reason need to use this under another
+# license please contact me.
+
+require 'irb'
+require 'caller'
+require 'drb'
+require 'drb/acl'
+require 'thread'
+
+module Breakpoint
+ id = %q$Id: breakpoint.rb 52 2005-02-26 19:43:19Z flgr $
+ current_version = id.split(" ")[2]
+ unless defined?(Version)
+ # The Version of ruby-breakpoint you are using as String of the
+ # 1.2.3 form where the digits stand for release, major and minor
+ # version respectively.
+ Version = "0.5.0"
+ end
+
+ extend self
+
+ # This will pop up an interactive ruby session at a
+ # pre-defined break point in a Ruby application. In
+ # this session you can examine the environment of
+ # the break point.
+ #
+ # You can get a list of variables in the context using
+ # local_variables via +local_variables+. You can then
+ # examine their values by typing their names.
+ #
+ # You can have a look at the call stack via +caller+.
+ #
+ # The source code around the location where the breakpoint
+ # was executed can be examined via +source_lines+. Its
+ # argument specifies how much lines of context to display.
+ # The default amount of context is 5 lines. Note that
+ # the call to +source_lines+ can raise an exception when
+ # it isn't able to read in the source code.
+ #
+ # breakpoints can also return a value. They will execute
+ # a supplied block for getting a default return value.
+ # A custom value can be returned from the session by doing
+ # +throw(:debug_return, value)+.
+ #
+ # You can also give names to break points which will be
+ # used in the message that is displayed upon execution
+ # of them.
+ #
+ # Here's a sample of how breakpoints should be placed:
+ #
+ # class Person
+ # def initialize(name, age)
+ # @name, @age = name, age
+ # breakpoint("Person#initialize")
+ # end
+ #
+ # attr_reader :age
+ # def name
+ # breakpoint("Person#name") { @name }
+ # end
+ # end
+ #
+ # person = Person.new("Random Person", 23)
+ # puts "Name: #{person.name}"
+ #
+ # And here is a sample debug session:
+ #
+ # Executing break point "Person#initialize" at file.rb:4 in `initialize'
+ # irb(#):001:0> local_variables
+ # => ["name", "age", "_", "__"]
+ # irb(#):002:0> [name, age]
+ # => ["Random Person", 23]
+ # irb(#):003:0> [@name, @age]
+ # => ["Random Person", 23]
+ # irb(#):004:0> self
+ # => #
+ # irb(#):005:0> @age += 1; self
+ # => #
+ # irb(#):006:0> exit
+ # Executing break point "Person#name" at file.rb:9 in `name'
+ # irb(#):001:0> throw(:debug_return, "Overriden name")
+ # Name: Overriden name
+ #
+ # Breakpoint sessions will automatically have a few
+ # convenience methods available. See Breakpoint::CommandBundle
+ # for a list of them.
+ #
+ # Breakpoints can also be used remotely over sockets.
+ # This is implemented by running part of the IRB session
+ # in the application and part of it in a special client.
+ # You have to call Breakpoint.activate_drb to enable
+ # support for remote breakpoints and then run
+ # breakpoint_client.rb which is distributed with this
+ # library. See the documentation of Breakpoint.activate_drb
+ # for details.
+ def breakpoint(id = nil, context = nil, &block)
+ callstack = caller
+ callstack.slice!(0, 3) if callstack.first["breakpoint"]
+ file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
+
+ message = "Executing break point " + (id ? "#{id.inspect} " : "") +
+ "at #{file}:#{line}" + (method ? " in `#{method}'" : "")
+
+ if context then
+ return handle_breakpoint(context, message, file, line, &block)
+ end
+
+ Binding.of_caller do |binding_context|
+ handle_breakpoint(binding_context, message, file, line, &block)
+ end
+ end
+
+ # These commands are automatically available in all breakpoint shells.
+ module CommandBundle
+ # Proxy to a Breakpoint client. Lets you directly execute code
+ # in the context of the client.
+ class Client
+ def initialize(eval_handler) # :nodoc:
+ eval_handler.untaint
+ @eval_handler = eval_handler
+ end
+
+ instance_methods.each do |method|
+ next if method[/^__.+__$/]
+ undef_method method
+ end
+
+ # Executes the specified code at the client.
+ def eval(code)
+ @eval_handler.call(code)
+ end
+
+ # Will execute the specified statement at the client.
+ def method_missing(method, *args, &block)
+ if args.empty? and not block
+ result = eval "#{method}"
+ else
+ # This is a bit ugly. The alternative would be using an
+ # eval context instead of an eval handler for executing
+ # the code at the client. The problem with that approach
+ # is that we would have to handle special expressions
+ # like "self", "nil" or constants ourself which is hard.
+ remote = eval %{
+ result = lambda { |block, *args| #{method}(*args, &block) }
+ def result.call_with_block(*args, &block)
+ call(block, *args)
+ end
+ result
+ }
+ remote.call_with_block(*args, &block)
+ end
+
+ return result
+ end
+ end
+
+ # Returns the source code surrounding the location where the
+ # breakpoint was issued.
+ def source_lines(context = 5, return_line_numbers = false)
+ lines = File.readlines(@__bp_file).map { |line| line.chomp }
+
+ break_line = @__bp_line
+ start_line = [break_line - context, 1].max
+ end_line = break_line + context
+
+ result = lines[(start_line - 1) .. (end_line - 1)]
+
+ if return_line_numbers then
+ return [start_line, break_line, result]
+ else
+ return result
+ end
+ end
+
+ # Lets an object that will forward method calls to the breakpoint
+ # client. This is useful for outputting longer things at the client
+ # and so on. You can for example do these things:
+ #
+ # client.puts "Hello" # outputs "Hello" at client console
+ # # outputs "Hello" into the file temp.txt at the client
+ # client.File.open("temp.txt", "w") { |f| f.puts "Hello" }
+ def client()
+ if Breakpoint.use_drb? then
+ sleep(0.5) until Breakpoint.drb_service.eval_handler
+ Client.new(Breakpoint.drb_service.eval_handler)
+ else
+ Client.new(lambda { |code| eval(code, TOPLEVEL_BINDING) })
+ end
+ end
+ end
+
+ def handle_breakpoint(context, message, file = "", line = "", &block) # :nodoc:
+ catch(:debug_return) do |value|
+ eval(%{
+ @__bp_file = #{file.inspect}
+ @__bp_line = #{line}
+ extend Breakpoint::CommandBundle
+ extend DRbUndumped if self
+ }, context) rescue nil
+
+ if not use_drb? then
+ puts message
+ IRB.start(nil, IRB::WorkSpace.new(context))
+ else
+ @drb_service.add_breakpoint(context, message)
+ end
+
+ block.call if block
+ end
+ end
+
+ # These exceptions will be raised on failed asserts
+ # if Breakpoint.asserts_cause_exceptions is set to
+ # true.
+ class FailedAssertError < RuntimeError
+ end
+
+ # This asserts that the block evaluates to true.
+ # If it doesn't evaluate to true a breakpoint will
+ # automatically be created at that execution point.
+ #
+ # You can disable assert checking in production
+ # code by setting Breakpoint.optimize_asserts to
+ # true. (It will still be enabled when Ruby is run
+ # via the -d argument.)
+ #
+ # Example:
+ # person_name = "Foobar"
+ # assert { not person_name.nil? }
+ #
+ # Note: If you want to use this method from an
+ # unit test, you will have to call it by its full
+ # name, Breakpoint.assert.
+ def assert(context = nil, &condition)
+ return if Breakpoint.optimize_asserts and not $DEBUG
+ return if yield
+
+ callstack = caller
+ callstack.slice!(0, 3) if callstack.first["assert"]
+ file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
+
+ message = "Assert failed at #{file}:#{line}#{" in `#{method}'" if method}."
+
+ if Breakpoint.asserts_cause_exceptions and not $DEBUG then
+ raise(Breakpoint::FailedAssertError, message)
+ end
+
+ message += " Executing implicit breakpoint."
+
+ if context then
+ return handle_breakpoint(context, message, file, line)
+ end
+
+ Binding.of_caller do |context|
+ handle_breakpoint(context, message, file, line)
+ end
+ end
+
+ # Whether asserts should be ignored if not in debug mode.
+ # Debug mode can be enabled by running ruby with the -d
+ # switch or by setting $DEBUG to true.
+ attr_accessor :optimize_asserts
+ self.optimize_asserts = false
+
+ # Whether an Exception should be raised on failed asserts
+ # in non-$DEBUG code or not. By default this is disabled.
+ attr_accessor :asserts_cause_exceptions
+ self.asserts_cause_exceptions = false
+ @use_drb = false
+
+ attr_reader :drb_service # :nodoc:
+
+ class DRbService # :nodoc:
+ include DRbUndumped
+
+ def initialize
+ @handler = @eval_handler = @collision_handler = nil
+
+ IRB.instance_eval { @CONF[:RC] = true }
+ IRB.run_config
+ end
+
+ def collision
+ sleep(0.5) until @collision_handler
+
+ @collision_handler.untaint
+
+ @collision_handler.call
+ end
+
+ def ping() end
+
+ def add_breakpoint(context, message)
+ workspace = IRB::WorkSpace.new(context)
+ workspace.extend(DRbUndumped)
+
+ sleep(0.5) until @handler
+
+ @handler.untaint
+ @handler.call(workspace, message)
+ rescue Errno::ECONNREFUSED, DRb::DRbConnError
+ raise if Breakpoint.use_drb?
+ end
+
+ attr_accessor :handler, :eval_handler, :collision_handler
+ end
+
+ # Will run Breakpoint in DRb mode. This will spawn a server
+ # that can be attached to via the breakpoint-client command
+ # whenever a breakpoint is executed. This is useful when you
+ # are debugging CGI applications or other applications where
+ # you can't access debug sessions via the standard input and
+ # output of your application.
+ #
+ # You can specify an URI where the DRb server will run at.
+ # This way you can specify the port the server runs on. The
+ # default URI is druby://localhost:42531.
+ #
+ # Please note that breakpoints will be skipped silently in
+ # case the DRb server can not spawned. (This can happen if
+ # the port is already used by another instance of your
+ # application on CGI or another application.)
+ #
+ # Also note that by default this will only allow access
+ # from localhost. You can however specify a list of
+ # allowed hosts or nil (to allow access from everywhere).
+ # But that will still not protect you from somebody
+ # reading the data as it goes through the net.
+ #
+ # A good approach for getting security and remote access
+ # is setting up an SSH tunnel between the DRb service
+ # and the client. This is usually done like this:
+ #
+ # $ ssh -L20000:127.0.0.1:20000 -R10000:127.0.0.1:10000 example.com
+ # (This will connect port 20000 at the client side to port
+ # 20000 at the server side, and port 10000 at the server
+ # side to port 10000 at the client side.)
+ #
+ # After that do this on the server side: (the code being debugged)
+ # Breakpoint.activate_drb("druby://127.0.0.1:20000", "localhost")
+ #
+ # And at the client side:
+ # ruby breakpoint_client.rb -c druby://127.0.0.1:10000 -s druby://127.0.0.1:20000
+ #
+ # Running through such a SSH proxy will also let you use
+ # breakpoint.rb in case you are behind a firewall.
+ #
+ # Detailed information about running DRb through firewalls is
+ # available at http://www.rubygarden.org/ruby?DrbTutorial
+ #
+ # == Security considerations
+ # Usually you will be fine when using the default druby:// URI and the default
+ # access control list. However, if you are sitting on a machine where there are
+ # local users that you likely can not trust (this is the case for example on
+ # most web hosts which have multiple users sitting on the same physical machine)
+ # you will be better off by doing client/server communication through a unix
+ # socket. This can be accomplished by calling with a drbunix:/ style URI, e.g.
+ # Breakpoint.activate_drb('drbunix:/tmp/breakpoint_server')
. This
+ # will only work on Unix based platforms.
+ def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'],
+ ignore_collisions = false)
+
+ return false if @use_drb
+
+ uri ||= 'druby://localhost:42531'
+
+ if allowed_hosts then
+ acl = ["deny", "all"]
+
+ Array(allowed_hosts).each do |host|
+ acl += ["allow", host]
+ end
+
+ DRb.install_acl(ACL.new(acl))
+ end
+
+ @use_drb = true
+ @drb_service = DRbService.new
+ did_collision = false
+ begin
+ @service = DRb.start_service(uri, @drb_service)
+ rescue Errno::EADDRINUSE
+ if ignore_collisions then
+ nil
+ else
+ # The port is already occupied by another
+ # Breakpoint service. We will try to tell
+ # the old service that we want its port.
+ # It will then forward that request to the
+ # user and retry.
+ unless did_collision then
+ DRbObject.new(nil, uri).collision
+ did_collision = true
+ end
+ sleep(10)
+ retry
+ end
+ end
+
+ return true
+ end
+
+ # Deactivates a running Breakpoint service.
+ def deactivate_drb
+ Thread.exclusive do
+ @service.stop_service unless @service.nil?
+ @service = nil
+ @use_drb = false
+ @drb_service = nil
+ end
+ end
+
+ # Returns true when Breakpoints are used over DRb.
+ # Breakpoint.activate_drb causes this to be true.
+ def use_drb?
+ @use_drb == true
+ end
+end
+
+module IRB # :nodoc:
+ class << self; remove_method :start; end
+ def self.start(ap_path = nil, main_context = nil, workspace = nil)
+ $0 = File::basename(ap_path, ".rb") if ap_path
+
+ # suppress some warnings about redefined constants
+ old_verbose, $VERBOSE = $VERBOSE, nil
+ IRB.setup(ap_path)
+ $VERBOSE = old_verbose
+
+ if @CONF[:SCRIPT] then
+ irb = Irb.new(main_context, @CONF[:SCRIPT])
+ else
+ irb = Irb.new(main_context)
+ end
+
+ if workspace then
+ irb.context.workspace = workspace
+ end
+
+ @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
+ @CONF[:MAIN_CONTEXT] = irb.context
+
+ old_sigint = trap("SIGINT") do
+ begin
+ irb.signal_handle
+ rescue RubyLex::TerminateLineInput
+ # ignored
+ end
+ end
+
+ catch(:IRB_EXIT) do
+ irb.eval_input
+ end
+ ensure
+ trap("SIGINT", old_sigint)
+ end
+
+ class << self
+ alias :old_CurrentContext :CurrentContext
+ remove_method :CurrentContext
+ remove_method :parse_opts
+ end
+
+ def IRB.CurrentContext
+ if old_CurrentContext.nil? and Breakpoint.use_drb? then
+ result = Object.new
+ def result.last_value; end
+ return result
+ else
+ old_CurrentContext
+ end
+ end
+ def IRB.parse_opts() end
+
+ class Context # :nodoc:
+ alias :old_evaluate :evaluate
+ def evaluate(line, line_no)
+ if line.chomp == "exit" then
+ exit
+ else
+ old_evaluate(line, line_no)
+ end
+ end
+ end
+
+ class WorkSpace # :nodoc:
+ alias :old_evaluate :evaluate
+
+ def evaluate(*args)
+ if Breakpoint.use_drb? then
+ result = old_evaluate(*args)
+ if args[0] != :no_proxy and
+ not [true, false, nil].include?(result)
+ then
+ result.extend(DRbUndumped) rescue nil
+ end
+ return result
+ else
+ old_evaluate(*args)
+ end
+ end
+ end
+
+ module InputCompletor # :nodoc:
+ def self.eval(code, context, *more)
+ # Big hack, this assumes that InputCompletor
+ # will only call eval() when it wants code
+ # to be executed in the IRB context.
+ IRB.conf[:MAIN_CONTEXT].workspace.evaluate(:no_proxy, code, *more)
+ end
+ end
+end
+
+module DRb # :nodoc:
+ class DRbObject # :nodoc:
+ undef :inspect if method_defined?(:inspect)
+ undef :clone if method_defined?(:clone)
+ end
+end
+
+# See Breakpoint.breakpoint
+def breakpoint(id = nil, &block)
+ Binding.of_caller do |context|
+ Breakpoint.breakpoint(id, context, &block)
+ end
+end
+
+# See Breakpoint.assert
+def assert(&block)
+ Binding.of_caller do |context|
+ Breakpoint.assert(context, &block)
+ end
+end
diff --git a/vendor/plugins/liquid/test/extra/caller.rb b/vendor/plugins/liquid/test/extra/caller.rb
new file mode 100755
index 000000000..14c96eb28
--- /dev/null
+++ b/vendor/plugins/liquid/test/extra/caller.rb
@@ -0,0 +1,80 @@
+class Continuation # :nodoc:
+ def self.create(*args, &block) # :nodoc:
+ cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
+ result ||= args
+ return *[cc, *result]
+ end
+end
+
+class Binding; end # for RDoc
+# This method returns the binding of the method that called your
+# method. It will raise an Exception when you're not inside a method.
+#
+# It's used like this:
+# def inc_counter(amount = 1)
+# Binding.of_caller do |binding|
+# # Create a lambda that will increase the variable 'counter'
+# # in the caller of this method when called.
+# inc = eval("lambda { |arg| counter += arg }", binding)
+# # We can refer to amount from inside this block safely.
+# inc.call(amount)
+# end
+# # No other statements can go here. Put them inside the block.
+# end
+# counter = 0
+# 2.times { inc_counter }
+# counter # => 2
+#
+# Binding.of_caller must be the last statement in the method.
+# This means that you will have to put everything you want to
+# do after the call to Binding.of_caller into the block of it.
+# This should be no problem however, because Ruby has closures.
+# If you don't do this an Exception will be raised. Because of
+# the way that Binding.of_caller is implemented it has to be
+# done this way.
+def Binding.of_caller(&block)
+ old_critical = Thread.critical
+ Thread.critical = true
+ count = 0
+ cc, result, error, extra_data = Continuation.create(nil, nil)
+ error.call if error
+
+ tracer = lambda do |*args|
+ type, context, extra_data = args[0], args[4], args
+ if type == "return"
+ count += 1
+ # First this method and then calling one will return --
+ # the trace event of the second event gets the context
+ # of the method which called the method that called this
+ # method.
+ if count == 2
+ # It would be nice if we could restore the trace_func
+ # that was set before we swapped in our own one, but
+ # this is impossible without overloading set_trace_func
+ # in current Ruby.
+ set_trace_func(nil)
+ cc.call(eval("binding", context), nil, extra_data)
+ end
+ elsif type == "line" then
+ nil
+ elsif type == "c-return" and extra_data[3] == :set_trace_func then
+ nil
+ else
+ set_trace_func(nil)
+ error_msg = "Binding.of_caller used in non-method context or " +
+ "trailing statements of method using it aren't in the block."
+ cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
+ end
+ end
+
+ unless result
+ set_trace_func(tracer)
+ return nil
+ else
+ Thread.critical = old_critical
+ case block.arity
+ when 1 then yield(result)
+ else yield(result, extra_data)
+ end
+ end
+end
diff --git a/vendor/plugins/liquid/test/file_system_test.rb b/vendor/plugins/liquid/test/file_system_test.rb
new file mode 100644
index 000000000..d3ab94871
--- /dev/null
+++ b/vendor/plugins/liquid/test/file_system_test.rb
@@ -0,0 +1,30 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+class FileSystemTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_default
+ assert_raise(FileSystemError) do
+ BlankFileSystem.new.read_template_file("dummy")
+ end
+ end
+
+ def test_local
+ file_system = Liquid::LocalFileSystem.new("/some/path")
+ assert_equal "/some/path/_mypartial.liquid" , file_system.full_path("mypartial")
+ assert_equal "/some/path/dir/_mypartial.liquid", file_system.full_path("dir/mypartial")
+
+ assert_raise(FileSystemError) do
+ file_system.full_path("../dir/mypartial")
+ end
+
+ assert_raise(FileSystemError) do
+ file_system.full_path("/dir/../../dir/mypartial")
+ end
+
+ assert_raise(FileSystemError) do
+ file_system.full_path("/etc/passwd")
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/filter_test.rb b/vendor/plugins/liquid/test/filter_test.rb
new file mode 100644
index 000000000..c66fe0636
--- /dev/null
+++ b/vendor/plugins/liquid/test/filter_test.rb
@@ -0,0 +1,95 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+module MoneyFilter
+ def money(input)
+ sprintf(' %d$ ', input)
+ end
+
+ def money_with_underscore(input)
+ sprintf(' %d$ ', input)
+ end
+end
+
+module CanadianMoneyFilter
+ def money(input)
+ sprintf(' %d$ CAD ', input)
+ end
+end
+
+class FiltersTest < Test::Unit::TestCase
+ include Liquid
+
+ def setup
+ @context = Context.new
+ end
+
+ def test_local_filter
+ @context['var'] = 1000
+ @context.add_filters(MoneyFilter)
+ assert_equal ' 1000$ ', Variable.new("var | money").render(@context)
+ end
+
+ def test_underscore_in_filter_name
+ @context['var'] = 1000
+ @context.add_filters(MoneyFilter)
+ assert_equal ' 1000$ ', Variable.new("var | money_with_underscore").render(@context)
+ end
+
+ def test_second_filter_overwrites_first
+ @context['var'] = 1000
+ @context.add_filters(MoneyFilter)
+ @context.add_filters(CanadianMoneyFilter)
+ assert_equal ' 1000$ CAD ', Variable.new("var | money").render(@context)
+ end
+
+ def test_size
+ @context['var'] = 'abcd'
+ @context.add_filters(MoneyFilter)
+ assert_equal 4, Variable.new("var | size").render(@context)
+ end
+
+ def test_join
+ @context['var'] = [1,2,3,4]
+ assert_equal "1 2 3 4", Variable.new("var | join").render(@context)
+ end
+
+ def test_sort
+ @context['value'] = 3
+ @context['numbers'] = [2,1,4,3]
+ @context['words'] = ['expected', 'as', 'alphabetic']
+ @context['arrays'] = [['flattened'], ['are']]
+ assert_equal [1,2,3,4], Variable.new("numbers | sort").render(@context)
+ assert_equal ['alphabetic', 'as', 'expected'],
+ Variable.new("words | sort").render(@context)
+ assert_equal [3], Variable.new("value | sort").render(@context)
+ assert_equal ['are', 'flattened'], Variable.new("arrays | sort").render(@context)
+ end
+
+ def test_strip_html
+ @context['var'] = "bla blub"
+ assert_equal "bla blub", Variable.new("var | strip_html").render(@context)
+ end
+
+ def test_capitalize
+ @context['var'] = "blub"
+ assert_equal "Blub", Variable.new("var | capitalize").render(@context)
+ end
+end
+
+class FiltersInTemplate < Test::Unit::TestCase
+ include Liquid
+
+ def test_local_global
+ Template.register_filter(MoneyFilter)
+
+ assert_equal " 1000$ ", Template.parse("{{1000 | money}}").render(nil, nil)
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, :filters => CanadianMoneyFilter)
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, :filters => [CanadianMoneyFilter])
+ end
+
+ def test_local_filter_with_deprecated_syntax
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, CanadianMoneyFilter)
+ assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, [CanadianMoneyFilter])
+ end
+end
diff --git a/vendor/plugins/liquid/test/helper.rb b/vendor/plugins/liquid/test/helper.rb
new file mode 100644
index 000000000..deb818594
--- /dev/null
+++ b/vendor/plugins/liquid/test/helper.rb
@@ -0,0 +1,20 @@
+#!/usr/bin/env ruby
+$LOAD_PATH.unshift(File.dirname(__FILE__)+ '/extra')
+
+require 'test/unit'
+require 'test/unit/assertions'
+require 'caller'
+require 'breakpoint'
+require File.dirname(__FILE__) + '/../lib/liquid'
+
+
+module Test
+ module Unit
+ module Assertions
+ include Liquid
+ def assert_template_result(expected, template, assigns={}, message=nil)
+ assert_equal expected, Template.parse(template).render(assigns)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/html_tag_test.rb b/vendor/plugins/liquid/test/html_tag_test.rb
new file mode 100644
index 000000000..546344e94
--- /dev/null
+++ b/vendor/plugins/liquid/test/html_tag_test.rb
@@ -0,0 +1,31 @@
+require File.dirname(__FILE__) + '/helper'
+
+class HtmlTagTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_html_table
+
+ assert_template_result("\n 1 | 2 | 3 |
\n 4 | 5 | 6 |
\n",
+ '{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
+ 'numbers' => [1,2,3,4,5,6])
+
+ assert_template_result("\n
\n",
+ '{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
+ 'numbers' => [])
+ end
+
+ def test_html_table_with_different_cols
+ assert_template_result("\n 1 | 2 | 3 | 4 | 5 |
\n 6 |
\n",
+ '{% tablerow n in numbers cols:5%} {{n}} {% endtablerow %}',
+ 'numbers' => [1,2,3,4,5,6])
+
+ end
+
+ def test_html_col_counter
+ assert_template_result("\n1 | 2 |
\n1 | 2 |
\n1 | 2 |
\n",
+ '{% tablerow n in numbers cols:2%}{{tablerowloop.col}}{% endtablerow %}',
+ 'numbers' => [1,2,3,4,5,6])
+
+ end
+
+end
diff --git a/vendor/plugins/liquid/test/if_else_test.rb b/vendor/plugins/liquid/test/if_else_test.rb
new file mode 100644
index 000000000..8e52d00ad
--- /dev/null
+++ b/vendor/plugins/liquid/test/if_else_test.rb
@@ -0,0 +1,150 @@
+require File.dirname(__FILE__) + '/helper'
+
+class IfElseTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_if
+ assert_template_result(' ',' {% if false %} this text should not go into the output {% endif %} ')
+ assert_template_result(' this text should go into the output ',
+ ' {% if true %} this text should go into the output {% endif %} ')
+ assert_template_result(' you rock ?','{% if false %} you suck {% endif %} {% if true %} you rock {% endif %}?')
+ end
+
+ def test_if_else
+ assert_template_result(' YES ','{% if false %} NO {% else %} YES {% endif %}')
+ assert_template_result(' YES ','{% if true %} YES {% else %} NO {% endif %}')
+ assert_template_result(' YES ','{% if "foo" %} YES {% else %} NO {% endif %}')
+ end
+
+ def test_if_boolean
+ assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => true)
+ end
+
+ def test_if_or
+ assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => true, 'b' => true)
+ assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => true, 'b' => false)
+ assert_template_result(' YES ','{% if a or b %} YES {% endif %}', 'a' => false, 'b' => true)
+ assert_template_result('', '{% if a or b %} YES {% endif %}', 'a' => false, 'b' => false)
+
+ assert_template_result(' YES ','{% if a or b or c %} YES {% endif %}', 'a' => false, 'b' => false, 'c' => true)
+ assert_template_result('', '{% if a or b or c %} YES {% endif %}', 'a' => false, 'b' => false, 'c' => false)
+ end
+
+ def test_if_or_with_operators
+ assert_template_result(' YES ','{% if a == true or b == true %} YES {% endif %}', 'a' => true, 'b' => true)
+ assert_template_result(' YES ','{% if a == true or b == false %} YES {% endif %}', 'a' => true, 'b' => true)
+ assert_template_result('','{% if a == false or b == false %} YES {% endif %}', 'a' => true, 'b' => true)
+ end
+
+ def test_if_and
+ assert_template_result(' YES ','{% if true and true %} YES {% endif %}')
+ assert_template_result('','{% if false and true %} YES {% endif %}')
+ assert_template_result('','{% if false and true %} YES {% endif %}')
+ end
+
+
+ def test_hash_miss_generates_false
+ assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => {})
+ end
+
+ def test_if_from_variable
+ assert_template_result('','{% if var %} NO {% endif %}', 'var' => false)
+ assert_template_result('','{% if var %} NO {% endif %}', 'var' => nil)
+ assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => {'bar' => false})
+ assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => {})
+ assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => nil)
+ assert_template_result('','{% if foo.bar %} NO {% endif %}', 'foo' => true)
+
+ assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => "text")
+ assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => true)
+ assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => 1)
+ assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => {})
+ assert_template_result(' YES ','{% if var %} YES {% endif %}', 'var' => [])
+ assert_template_result(' YES ','{% if "foo" %} YES {% endif %}')
+ assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => true})
+ assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => "text"})
+ assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => 1 })
+ assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => {} })
+ assert_template_result(' YES ','{% if foo.bar %} YES {% endif %}', 'foo' => {'bar' => [] })
+
+ assert_template_result(' YES ','{% if var %} NO {% else %} YES {% endif %}', 'var' => false)
+ assert_template_result(' YES ','{% if var %} NO {% else %} YES {% endif %}', 'var' => nil)
+ assert_template_result(' YES ','{% if var %} YES {% else %} NO {% endif %}', 'var' => true)
+ assert_template_result(' YES ','{% if "foo" %} YES {% else %} NO {% endif %}', 'var' => "text")
+
+ assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => {'bar' => false})
+ assert_template_result(' YES ','{% if foo.bar %} YES {% else %} NO {% endif %}', 'foo' => {'bar' => true})
+ assert_template_result(' YES ','{% if foo.bar %} YES {% else %} NO {% endif %}', 'foo' => {'bar' => "text"})
+ assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => {'notbar' => true})
+ assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'foo' => {})
+ assert_template_result(' YES ','{% if foo.bar %} NO {% else %} YES {% endif %}', 'notfoo' => {'bar' => true})
+ end
+
+ def test_nested_if
+ assert_template_result('', '{% if false %}{% if false %} NO {% endif %}{% endif %}')
+ assert_template_result('', '{% if false %}{% if true %} NO {% endif %}{% endif %}')
+ assert_template_result('', '{% if true %}{% if false %} NO {% endif %}{% endif %}')
+ assert_template_result(' YES ', '{% if true %}{% if true %} YES {% endif %}{% endif %}')
+
+ assert_template_result(' YES ', '{% if true %}{% if true %} YES {% else %} NO {% endif %}{% else %} NO {% endif %}')
+ assert_template_result(' YES ', '{% if true %}{% if false %} NO {% else %} YES {% endif %}{% else %} NO {% endif %}')
+ assert_template_result(' YES ', '{% if false %}{% if true %} NO {% else %} NONO {% endif %}{% else %} YES {% endif %}')
+
+ end
+
+ def test_comparisons_on_null
+ assert_template_result('','{% if null < 10 %} NO {% endif %}')
+ assert_template_result('','{% if null <= 10 %} NO {% endif %}')
+ assert_template_result('','{% if null >= 10 %} NO {% endif %}')
+ assert_template_result('','{% if null > 10 %} NO {% endif %}')
+
+ assert_template_result('','{% if 10 < null %} NO {% endif %}')
+ assert_template_result('','{% if 10 <= null %} NO {% endif %}')
+ assert_template_result('','{% if 10 >= null %} NO {% endif %}')
+ assert_template_result('','{% if 10 > null %} NO {% endif %}')
+ end
+
+ def test_else_if
+ assert_template_result('0','{% if 0 == 0 %}0{% elsif 1 == 1%}1{% else %}2{% endif %}')
+ assert_template_result('1','{% if 0 != 0 %}0{% elsif 1 == 1%}1{% else %}2{% endif %}')
+ assert_template_result('2','{% if 0 != 0 %}0{% elsif 1 != 1%}1{% else %}2{% endif %}')
+
+ assert_template_result('elsif','{% if false %}if{% elsif true %}elsif{% endif %}')
+ end
+
+ def test_with_filtered_expressions
+ assert_template_result('yes','{% if "BLAH"|downcase == "blah" %}yes{% endif %}')
+ assert_template_result('yes','{% if "FOO BAR"|truncatewords:1,"--" == "FOO--" %}yes{% endif %}')
+ assert_template_result('yes','{% if "FOO BAR"|truncatewords:1,"--"|downcase == "foo--" %}yes{% endif %}')
+ assert_template_result('yes','{% if "foo--" == "FOO BAR"|truncatewords:1,"--"|downcase %}yes{% endif %}')
+ # array transformation, to make sure we aren't converting arrays to strings somewhere along the way:
+ assert_template_result('yes','{% if values|sort == sorted %}yes{% endif %}', 'values' => %w{foo bar baz}, 'sorted' => %w{bar baz foo})
+ end
+
+ def test_allow_no_spaces_in_filtered_expressions
+ assert_template_result('','{% if "foo--" == "FOO BAR" |truncatewords:1,"--"|downcase %}yes{% endif %}')
+ assert_template_result('','{% if "foo--" == "FOO BAR"| truncatewords:1,"--"|downcase %}yes{% endif %}')
+ assert_template_result('','{% if "foo--" == "FOO BAR"|truncatewords :1,"--"|downcase %}yes{% endif %}')
+ assert_template_result('','{% if "foo--" == "FOO BAR"|truncatewords: 1,"--"|downcase %}yes{% endif %}')
+ assert_template_result('','{% if "foo--" == "FOO BAR"|truncatewords:1 ,"--"|downcase %}yes{% endif %}')
+ assert_template_result('','{% if "foo--" == "FOO BAR"|truncatewords:1, "--"|downcase %}yes{% endif %}')
+ assert_template_result('','{% if "foo--" == "FOO BAR"|truncatewords:1,"--" |downcase %}yes{% endif %}')
+ end
+
+ def test_syntax_error_no_variable
+ assert_raise(SyntaxError){ assert_template_result('', '{% if jerry == 1 %}')}
+ end
+
+ def test_syntax_error_no_expression
+ assert_raise(SyntaxError) { assert_template_result('', '{% if %}') }
+ end
+
+ def test_if_with_custom_condition
+ Condition.operators['contains'] = :[]
+
+ assert_template_result('yes', %({% if 'bob' contains 'o' %}yes{% endif %}))
+ assert_template_result('no', %({% if 'bob' contains 'f' %}yes{% else %}no{% endif %}))
+ ensure
+ Condition.operators.delete 'contains'
+ end
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/include_tag_test.rb b/vendor/plugins/liquid/test/include_tag_test.rb
new file mode 100644
index 000000000..06f3b3053
--- /dev/null
+++ b/vendor/plugins/liquid/test/include_tag_test.rb
@@ -0,0 +1,115 @@
+require File.dirname(__FILE__) + '/helper'
+
+class TestFileSystem
+ def read_template_file(template_path)
+ case template_path
+ when "product"
+ "Product: {{ product.title }} "
+
+ when "locale_variables"
+ "Locale: {{echo1}} {{echo2}}"
+
+ when "variant"
+ "Variant: {{ variant.title }}"
+
+ when "nested_template"
+ "{% include 'header' %} {% include 'body' %} {% include 'footer' %}"
+
+ when "body"
+ "body {% include 'body_detail' %}"
+
+ when "nested_product_template"
+ "Product: {{ nested_product_template.title }} {%include 'details'%} "
+
+ when "recursively_nested_template"
+ "-{% include 'recursively_nested_template' %}"
+
+ else
+ template_path
+ end
+ end
+end
+
+class IncludeTagTest < Test::Unit::TestCase
+ include Liquid
+
+ def setup
+ Liquid::Template.file_system = TestFileSystem.new
+ end
+
+
+ def test_include_tag_with
+ assert_equal "Product: Draft 151cm ",
+ Template.parse("{% include 'product' with products[0] %}").render( "products" => [ {'title' => 'Draft 151cm'}, {'title' => 'Element 155cm'} ] )
+ end
+
+ def test_include_tag_with_default_name
+ assert_equal "Product: Draft 151cm ",
+ Template.parse("{% include 'product' %}").render( "product" => {'title' => 'Draft 151cm'} )
+ end
+
+ def test_include_tag_for
+
+ assert_equal "Product: Draft 151cm Product: Element 155cm ",
+ Template.parse("{% include 'product' for products %}").render( "products" => [ {'title' => 'Draft 151cm'}, {'title' => 'Element 155cm'} ] )
+ end
+
+ def test_include_tag_with_local_variables
+ assert_equal "Locale: test123 ",
+ Template.parse("{% include 'locale_variables' echo1: 'test123' %}").render
+ end
+
+ def test_include_tag_with_multiple_local_variables
+ assert_equal "Locale: test123 test321",
+ Template.parse("{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}").render
+ end
+
+ def test_include_tag_with_multiple_local_variables_from_context
+ assert_equal "Locale: test123 test321",
+ Template.parse("{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}").render('echo1' => 'test123', 'more_echos' => { "echo2" => 'test321'})
+ end
+
+ def test_nested_include_tag
+ assert_equal "body body_detail",
+ Template.parse("{% include 'body' %}").render
+
+ assert_equal "header body body_detail footer",
+ Template.parse("{% include 'nested_template' %}").render
+ end
+
+ def test_nested_include_with_variable
+
+ assert_equal "Product: Draft 151cm details ",
+ Template.parse("{% include 'nested_product_template' with product %}").render("product" => {"title" => 'Draft 151cm'})
+
+ assert_equal "Product: Draft 151cm details Product: Element 155cm details ",
+ Template.parse("{% include 'nested_product_template' for products %}").render("products" => [{"title" => 'Draft 151cm'}, {"title" => 'Element 155cm'}])
+
+ end
+
+ def test_recursively_included_template_does_not_produce_endless_loop
+
+ infinite_file_system = Class.new do
+ def read_template_file(template_path)
+ "-{% include 'loop' %}"
+ end
+ end
+
+ Liquid::Template.file_system = infinite_file_system.new
+
+ assert_raise(Liquid::StackLevelError) do
+ Template.parse("{% include 'loop' %}").render!
+ end
+
+ end
+
+ def test_dynamically_choosen_template
+
+ assert_equal "Test123", Template.parse("{% include template %}").render("template" => 'Test123')
+ assert_equal "Test321", Template.parse("{% include template %}").render("template" => 'Test321')
+
+ assert_equal "Product: Draft 151cm ", Template.parse("{% include template for product %}").render("template" => 'product', 'product' => { 'title' => 'Draft 151cm'})
+
+ end
+
+end
diff --git a/vendor/plugins/liquid/test/module_ex_test.rb b/vendor/plugins/liquid/test/module_ex_test.rb
new file mode 100644
index 000000000..40d7e1c4f
--- /dev/null
+++ b/vendor/plugins/liquid/test/module_ex_test.rb
@@ -0,0 +1,89 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+class TestClassA
+ liquid_methods :allowedA, :chainedB
+ def allowedA
+ 'allowedA'
+ end
+ def restrictedA
+ 'restrictedA'
+ end
+ def chainedB
+ TestClassB.new
+ end
+end
+
+class TestClassB
+ liquid_methods :allowedB, :chainedC
+ def allowedB
+ 'allowedB'
+ end
+ def chainedC
+ TestClassC.new
+ end
+end
+
+class TestClassC
+ liquid_methods :allowedC
+ def allowedC
+ 'allowedC'
+ end
+end
+
+class TestClassC::LiquidDropClass
+ def another_allowedC
+ 'another_allowedC'
+ end
+end
+
+class ModuleExTest < Test::Unit::TestCase
+ include Liquid
+
+ def setup
+ @a = TestClassA.new
+ @b = TestClassB.new
+ @c = TestClassC.new
+ end
+
+ def test_should_create_LiquidDropClass
+ assert TestClassA::LiquidDropClass
+ assert TestClassB::LiquidDropClass
+ assert TestClassC::LiquidDropClass
+ end
+
+ def test_should_respond_to_liquid
+ assert @a.respond_to?(:to_liquid)
+ assert @b.respond_to?(:to_liquid)
+ assert @c.respond_to?(:to_liquid)
+ end
+
+ def test_should_return_LiquidDropClass_object
+ assert @a.to_liquid.is_a?(TestClassA::LiquidDropClass)
+ assert @b.to_liquid.is_a?(TestClassB::LiquidDropClass)
+ assert @c.to_liquid.is_a?(TestClassC::LiquidDropClass)
+ end
+
+ def test_should_respond_to_liquid_methods
+ assert @a.to_liquid.respond_to?(:allowedA)
+ assert @a.to_liquid.respond_to?(:chainedB)
+ assert @b.to_liquid.respond_to?(:allowedB)
+ assert @b.to_liquid.respond_to?(:chainedC)
+ assert @c.to_liquid.respond_to?(:allowedC)
+ assert @c.to_liquid.respond_to?(:another_allowedC)
+ end
+
+ def test_should_not_respond_to_restricted_methods
+ assert ! @a.to_liquid.respond_to?(:restricted)
+ end
+
+ def test_should_use_regular_objects_as_drops
+ assert_equal 'allowedA', Liquid::Template.parse("{{ a.allowedA }}").render('a'=>@a)
+ assert_equal 'allowedB', Liquid::Template.parse("{{ a.chainedB.allowedB }}").render('a'=>@a)
+ assert_equal 'allowedC', Liquid::Template.parse("{{ a.chainedB.chainedC.allowedC }}").render('a'=>@a)
+ assert_equal 'another_allowedC', Liquid::Template.parse("{{ a.chainedB.chainedC.another_allowedC }}").render('a'=>@a)
+ assert_equal '', Liquid::Template.parse("{{ a.restricted }}").render('a'=>@a)
+ assert_equal '', Liquid::Template.parse("{{ a.unknown }}").render('a'=>@a)
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/output_test.rb b/vendor/plugins/liquid/test/output_test.rb
new file mode 100644
index 000000000..96d909035
--- /dev/null
+++ b/vendor/plugins/liquid/test/output_test.rb
@@ -0,0 +1,121 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+module FunnyFilter
+
+ def make_funny(input)
+ 'LOL'
+ end
+
+ def cite_funny(input)
+ "LOL: #{input}"
+ end
+
+ def add_smiley(input, smiley = ":-)")
+ "#{input} #{smiley}"
+ end
+
+ def add_tag(input, tag = "p", id = "foo")
+ %|<#{tag} id="#{id}">#{input}#{tag}>|
+ end
+
+ def paragraph(input)
+ "#{input}
"
+ end
+
+ def link_to(name, url)
+ %|#{name}|
+ end
+end
+
+
+class OutputTest < Test::Unit::TestCase
+ include Liquid
+
+ def setup
+ @assigns = {
+ 'best_cars' => 'bmw',
+ 'car' => {'bmw' => 'good', 'gm' => 'bad'}
+ }
+
+ end
+
+ def test_variable
+ text = %| {{best_cars}} |
+
+ expected = %| bmw |
+ assert_equal expected, Template.parse(text).render(@assigns)
+ end
+
+ def test_variable_traversing
+ text = %| {{car.bmw}} {{car.gm}} {{car.bmw}} |
+
+ expected = %| good bad good |
+ assert_equal expected, Template.parse(text).render(@assigns)
+ end
+
+ def test_variable_piping
+ text = %( {{ car.gm | make_funny }} )
+ expected = %| LOL |
+
+ assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
+ end
+
+ def test_variable_piping_with_input
+ text = %( {{ car.gm | cite_funny }} )
+ expected = %| LOL: bad |
+
+ assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
+ end
+
+ def test_variable_piping_with_args
+ text = %! {{ car.gm | add_smiley : ':-(' }} !
+ expected = %| bad :-( |
+
+ assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
+ end
+
+ def test_variable_piping_with_no_args
+ text = %! {{ car.gm | add_smiley }} !
+ expected = %| bad :-) |
+
+ assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
+ end
+
+ def test_multiple_variable_piping_with_args
+ text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} !
+ expected = %| bad :-( :-( |
+
+ assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
+ end
+
+ def test_variable_piping_with_args
+ text = %! {{ car.gm | add_tag : 'span', 'bar'}} !
+ expected = %| bad |
+
+ assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
+ end
+
+ def test_variable_piping_with_variable_args
+ text = %! {{ car.gm | add_tag : 'span', car.bmw}} !
+ expected = %| bad |
+
+ assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
+ end
+
+ def test_multiple_pipings
+ text = %( {{ best_cars | cite_funny | paragraph }} )
+ expected = %| LOL: bmw
|
+
+ assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
+ end
+
+ def test_link_to
+ text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} )
+ expected = %| Typo |
+
+ assert_equal expected, Template.parse(text).render(@assigns, :filters => [FunnyFilter])
+ end
+
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/parsing_quirks_test.rb b/vendor/plugins/liquid/test/parsing_quirks_test.rb
new file mode 100644
index 000000000..5cebd522b
--- /dev/null
+++ b/vendor/plugins/liquid/test/parsing_quirks_test.rb
@@ -0,0 +1,41 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+class ParsingQuirksTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_error_with_css
+ text = %| div { font-weight: bold; } |
+ template = Template.parse(text)
+
+ assert_equal text, template.render
+ assert_equal [String], template.root.nodelist.collect {|i| i.class}
+ end
+
+ def test_raise_on_single_close_bracet
+ assert_raise(SyntaxError) do
+ Template.parse("text {{method} oh nos!")
+ end
+ end
+
+ def test_raise_on_label_and_no_close_bracets
+ assert_raise(SyntaxError) do
+ Template.parse("TEST {{ ")
+ end
+ end
+
+ def test_raise_on_label_and_no_close_bracets_percent
+ assert_raise(SyntaxError) do
+ Template.parse("TEST {% ")
+ end
+ end
+
+ def test_error_on_empty_filter
+ assert_nothing_raised do
+ Template.parse("{{test |a|b|}}")
+ Template.parse("{{test}}")
+ Template.parse("{{|test|}}")
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/regexp_test.rb b/vendor/plugins/liquid/test/regexp_test.rb
new file mode 100644
index 000000000..719324cdf
--- /dev/null
+++ b/vendor/plugins/liquid/test/regexp_test.rb
@@ -0,0 +1,40 @@
+require File.dirname(__FILE__) + '/helper'
+
+class RegexpTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_empty
+ assert_equal [], ''.scan(QuotedFragment)
+ end
+
+ def test_quote
+ assert_equal ['"arg 1"'], '"arg 1"'.scan(QuotedFragment)
+ end
+
+
+ def test_words
+ assert_equal ['arg1', 'arg2'], 'arg1 arg2'.scan(QuotedFragment)
+ end
+
+ def test_quoted_words
+ assert_equal ['arg1', 'arg2', '"arg 3"'], 'arg1 arg2 "arg 3"'.scan(QuotedFragment)
+ end
+
+ def test_quoted_words
+ assert_equal ['arg1', 'arg2', "'arg 3'"], 'arg1 arg2 \'arg 3\''.scan(QuotedFragment)
+ end
+
+ def test_quoted_words_in_the_middle
+ assert_equal ['arg1', 'arg2', '"arg 3"', 'arg4'], 'arg1 arg2 "arg 3" arg4 '.scan(QuotedFragment)
+ end
+
+ def test_variable_parser
+ assert_equal ['var'], 'var'.scan(VariableParser)
+ assert_equal ['var', 'method'], 'var.method'.scan(VariableParser)
+ assert_equal ['var', '[method]'], 'var[method]'.scan(VariableParser)
+ assert_equal ['var', '[method]', '[0]'], 'var[method][0]'.scan(VariableParser)
+ assert_equal ['var', '["method"]', '[0]'], 'var["method"][0]'.scan(VariableParser)
+ assert_equal ['var', '[method]', '[0]', 'method'], 'var[method][0].method'.scan(VariableParser)
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/security_test.rb b/vendor/plugins/liquid/test/security_test.rb
new file mode 100644
index 000000000..1ab0d6fa4
--- /dev/null
+++ b/vendor/plugins/liquid/test/security_test.rb
@@ -0,0 +1,41 @@
+require File.dirname(__FILE__) + '/helper'
+
+module SecurityFilter
+ def add_one(input)
+ "#{input} + 1"
+ end
+end
+
+class SecurityTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_no_instance_eval
+ text = %( {{ '1+1' | instance_eval }} )
+ expected = %| 1+1 |
+
+ assert_equal expected, Template.parse(text).render(@assigns)
+ end
+
+ def test_no_existing_instance_eval
+ text = %( {{ '1+1' | __instance_eval__ }} )
+ expected = %| 1+1 |
+
+ assert_equal expected, Template.parse(text).render(@assigns)
+ end
+
+
+ def test_no_instance_eval_after_mixing_in_new_filter
+ text = %( {{ '1+1' | instance_eval }} )
+ expected = %| 1+1 |
+
+ assert_equal expected, Template.parse(text).render(@assigns)
+ end
+
+
+ def test_no_instance_eval_later_in_chain
+ text = %( {{ '1+1' | add_one | instance_eval }} )
+ expected = %| 1+1 + 1 |
+
+ assert_equal expected, Template.parse(text).render(@assigns, :filters => SecurityFilter)
+ end
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/standard_filter_test.rb b/vendor/plugins/liquid/test/standard_filter_test.rb
new file mode 100644
index 000000000..0b9ee5b71
--- /dev/null
+++ b/vendor/plugins/liquid/test/standard_filter_test.rb
@@ -0,0 +1,161 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+
+class Filters
+ include Liquid::StandardFilters
+end
+
+
+class StandardFiltersTest < Test::Unit::TestCase
+ include Liquid
+
+ def setup
+ @filters = Filters.new
+ end
+
+ def test_size
+ assert_equal 3, @filters.size([1,2,3])
+ assert_equal 0, @filters.size([])
+ assert_equal 0, @filters.size(nil)
+ end
+
+ def test_downcase
+ assert_equal 'testing', @filters.downcase("Testing")
+ assert_equal '', @filters.downcase(nil)
+ end
+
+ def test_upcase
+ assert_equal 'TESTING', @filters.upcase("Testing")
+ assert_equal '', @filters.upcase(nil)
+ end
+
+ def test_upcase
+ assert_equal 'TESTING', @filters.upcase("Testing")
+ assert_equal '', @filters.upcase(nil)
+ end
+
+ def test_truncate
+ assert_equal '1234...', @filters.truncate('1234567890', 7)
+ assert_equal '1234567890', @filters.truncate('1234567890', 20)
+ assert_equal '...', @filters.truncate('1234567890', 0)
+ assert_equal '1234567890', @filters.truncate('1234567890')
+ end
+
+ def test_escape
+ assert_equal '<strong>', @filters.escape('')
+ assert_equal '<strong>', @filters.h('')
+ end
+
+ def test_truncatewords
+ assert_equal 'one two three', @filters.truncatewords('one two three', 4)
+ assert_equal 'one two...', @filters.truncatewords('one two three', 2)
+ assert_equal 'one two three', @filters.truncatewords('one two three')
+ assert_equal 'Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13”...', @filters.truncatewords('Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13” x 16” x 10.5” high) with cover.', 15)
+ end
+
+ def test_strip_html
+ assert_equal 'test', @filters.strip_html("test
")
+ assert_equal 'test', @filters.strip_html("test
")
+ assert_equal '', @filters.strip_html(nil)
+ end
+
+ def test_join
+ assert_equal '1 2 3 4', @filters.join([1,2,3,4])
+ assert_equal '1 - 2 - 3 - 4', @filters.join([1,2,3,4], ' - ')
+ end
+
+ def test_sort
+ assert_equal [1,2,3,4], @filters.sort([4,3,2,1])
+ assert_equal [{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], @filters.sort([{"a" => 4}, {"a" => 3}, {"a" => 1}, {"a" => 2}], "a")
+ end
+
+ def test_map
+ assert_equal [1,2,3,4], @filters.map([{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], 'a')
+ assert_template_result 'abc', "{{ ary | map:'foo' | map:'bar' }}",
+ 'ary' => [{'foo' => {'bar' => 'a'}}, {'foo' => {'bar' => 'b'}}, {'foo' => {'bar' => 'c'}}]
+ end
+
+ def test_date
+ assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B")
+ assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B")
+ assert_equal 'July', @filters.date(Time.parse("2006-07-05 10:00:00"), "%B")
+
+ assert_equal 'May', @filters.date("2006-05-05 10:00:00", "%B")
+ assert_equal 'June', @filters.date("2006-06-05 10:00:00", "%B")
+ assert_equal 'July', @filters.date("2006-07-05 10:00:00", "%B")
+
+ assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
+ assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
+ assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", "")
+ assert_equal '2006-07-05 10:00:00', @filters.date("2006-07-05 10:00:00", nil)
+
+ assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
+
+ assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
+
+ assert_equal nil, @filters.date(nil, "%B")
+ end
+
+
+ def test_first_last
+ assert_equal 1, @filters.first([1,2,3])
+ assert_equal 3, @filters.last([1,2,3])
+ assert_equal nil, @filters.first([])
+ assert_equal nil, @filters.last([])
+ end
+
+ def test_replace
+ assert_equal 'b b b b', @filters.replace("a a a a", 'a', 'b')
+ assert_equal 'b a a a', @filters.replace_first("a a a a", 'a', 'b')
+ assert_template_result 'b a a a', "{{ 'a a a a' | replace_first: 'a', 'b' }}"
+ end
+
+ def test_remove
+ assert_equal ' ', @filters.remove("a a a a", 'a')
+ assert_equal 'a a a', @filters.remove_first("a a a a", 'a ')
+ assert_template_result 'a a a', "{{ 'a a a a' | remove_first: 'a ' }}"
+ end
+
+ def test_strip_newlines
+ assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\nb\nc"
+ end
+
+ def test_newlines_to_br
+ assert_template_result "a
\nb
\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc"
+ end
+
+ def test_plus
+ assert_template_result "2", "{{ 1 | plus:1 }}"
+ assert_template_result "11", "{{ '1' | plus:'1' }}"
+ end
+
+ def test_minus
+ assert_template_result "4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1
+ end
+
+ def test_times
+ assert_template_result "12", "{{ 3 | times:4 }}"
+ assert_template_result "foofoofoofoo", "{{ 'foo' | times:4 }}"
+ end
+
+ def test_append
+ assigns = {'a' => 'bc', 'b' => 'd' }
+ assert_template_result('bcd',"{{ a | append: 'd'}}",assigns)
+ assert_template_result('bcd',"{{ a | append: b}}",assigns)
+ end
+
+ def test_prepend
+ assigns = {'a' => 'bc', 'b' => 'a' }
+ assert_template_result('abc',"{{ a | prepend: 'a'}}",assigns)
+ assert_template_result('abc',"{{ a | prepend: b}}",assigns)
+ end
+
+ def test_divided_by
+ assert_template_result "4", "{{ 12 | divided_by:3 }}"
+ assert_template_result "4", "{{ 14 | divided_by:3 }}"
+ assert_template_result "5", "{{ 15 | divided_by:3 }}"
+ end
+
+end
+
diff --git a/vendor/plugins/liquid/test/standard_tag_test.rb b/vendor/plugins/liquid/test/standard_tag_test.rb
new file mode 100644
index 000000000..9e095cca5
--- /dev/null
+++ b/vendor/plugins/liquid/test/standard_tag_test.rb
@@ -0,0 +1,404 @@
+require File.dirname(__FILE__) + '/helper'
+
+
+class StandardTagTest < Test::Unit::TestCase
+ include Liquid
+
+
+ def test_tag
+ tag = Tag.new('tag', [], [])
+ assert_equal 'liquid::tag', tag.name
+ assert_equal '', tag.render(Context.new)
+ end
+
+ def test_no_transform
+ assert_template_result('this text should come out of the template without change...',
+ 'this text should come out of the template without change...')
+ assert_template_result('blah','blah')
+ assert_template_result('','')
+ assert_template_result('|,.:','|,.:')
+ assert_template_result('','')
+
+ text = %|this shouldnt see any transformation either but has multiple lines
+ as you can clearly see here ...|
+ assert_template_result(text,text)
+ end
+
+ def test_has_a_block_which_does_nothing
+ assert_template_result(%|the comment block should be removed .. right?|,
+ %|the comment block should be removed {%comment%} be gone.. {%endcomment%} .. right?|)
+
+ assert_template_result('','{%comment%}{%endcomment%}')
+ assert_template_result('','{%comment%}{% endcomment %}')
+ assert_template_result('','{% comment %}{%endcomment%}')
+ assert_template_result('','{% comment %}{% endcomment %}')
+ assert_template_result('','{%comment%}comment{%endcomment%}')
+ assert_template_result('','{% comment %}comment{% endcomment %}')
+
+ assert_template_result('foobar','foo{%comment%}comment{%endcomment%}bar')
+ assert_template_result('foobar','foo{% comment %}comment{% endcomment %}bar')
+ assert_template_result('foobar','foo{%comment%} comment {%endcomment%}bar')
+ assert_template_result('foobar','foo{% comment %} comment {% endcomment %}bar')
+
+ assert_template_result('foo bar','foo {%comment%} {%endcomment%} bar')
+ assert_template_result('foo bar','foo {%comment%}comment{%endcomment%} bar')
+ assert_template_result('foo bar','foo {%comment%} comment {%endcomment%} bar')
+
+ assert_template_result('foobar','foo{%comment%}
+ {%endcomment%}bar')
+ end
+
+ def test_for
+ assert_template_result(' yo yo yo yo ','{%for item in array%} yo {%endfor%}','array' => [1,2,3,4])
+ assert_template_result('yoyo','{%for item in array%}yo{%endfor%}','array' => [1,2])
+ assert_template_result(' yo ','{%for item in array%} yo {%endfor%}','array' => [1])
+ assert_template_result('','{%for item in array%}{%endfor%}','array' => [1,2])
+ expected = < [1,2,3])
+ end
+
+ def test_for_with_range
+ assert_template_result(' 1 2 3 ','{%for item in (1..3) %} {{item}} {%endfor%}')
+ end
+
+ def test_for_with_variable
+ assert_template_result(' 1 2 3 ','{%for item in array%} {{item}} {%endfor%}','array' => [1,2,3])
+ assert_template_result('123','{%for item in array%}{{item}}{%endfor%}','array' => [1,2,3])
+ assert_template_result('123','{% for item in array %}{{item}}{% endfor %}','array' => [1,2,3])
+ assert_template_result('abcd','{%for item in array%}{{item}}{%endfor%}','array' => ['a','b','c','d'])
+ assert_template_result('a b c','{%for item in array%}{{item}}{%endfor%}','array' => ['a',' ','b',' ','c'])
+ assert_template_result('abc','{%for item in array%}{{item}}{%endfor%}','array' => ['a','','b','','c'])
+ end
+
+ def test_for_helpers
+ assigns = {'array' => [1,2,3] }
+ assert_template_result(' 1/3 2/3 3/3 ','{%for item in array%} {{forloop.index}}/{{forloop.length}} {%endfor%}',assigns)
+ assert_template_result(' 1 2 3 ','{%for item in array%} {{forloop.index}} {%endfor%}',assigns)
+ assert_template_result(' 0 1 2 ','{%for item in array%} {{forloop.index0}} {%endfor%}',assigns)
+ assert_template_result(' 2 1 0 ','{%for item in array%} {{forloop.rindex0}} {%endfor%}',assigns)
+ assert_template_result(' 3 2 1 ','{%for item in array%} {{forloop.rindex}} {%endfor%}',assigns)
+ assert_template_result(' true false false ','{%for item in array%} {{forloop.first}} {%endfor%}',assigns)
+ assert_template_result(' false false true ','{%for item in array%} {{forloop.last}} {%endfor%}',assigns)
+ end
+
+ def test_for_and_if
+ assigns = {'array' => [1,2,3] }
+ assert_template_result('+--', '{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}', assigns)
+ end
+
+ def test_for_with_filtered_expressions
+ assert_template_result('abc','{% for letter in letters|sort %}{{ letter }}{% endfor %}', 'letters' => %w{c b a})
+ end
+
+ def test_limiting
+ assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
+ assert_template_result('12','{%for i in array limit:2 %}{{ i }}{%endfor%}',assigns)
+ assert_template_result('1234','{%for i in array limit:4 %}{{ i }}{%endfor%}',assigns)
+ assert_template_result('3456','{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}',assigns)
+ assert_template_result('3456','{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}',assigns)
+ end
+
+ def test_dynamic_variable_limiting
+ assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
+ assigns['limit'] = 2
+ assigns['offset'] = 2
+ assert_template_result('34','{%for i in array limit: limit offset: offset %}{{ i }}{%endfor%}',assigns)
+ end
+
+ def test_nested_for
+ assigns = {'array' => [[1,2],[3,4],[5,6]] }
+ assert_template_result('123456','{%for item in array%}{%for i in item%}{{ i }}{%endfor%}{%endfor%}',assigns)
+ end
+
+ def test_offset_only
+ assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
+ assert_template_result('890','{%for i in array offset:7 %}{{ i }}{%endfor%}',assigns)
+ end
+
+ def test_pause_resume
+ assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}}
+ markup = <<-MKUP
+ {%for i in array.items limit: 3 %}{{i}}{%endfor%}
+ next
+ {%for i in array.items offset:continue limit: 3 %}{{i}}{%endfor%}
+ next
+ {%for i in array.items offset:continue limit: 3 %}{{i}}{%endfor%}
+ MKUP
+ expected = <<-XPCTD
+ 123
+ next
+ 456
+ next
+ 789
+ XPCTD
+ assert_template_result(expected,markup,assigns)
+ end
+
+ def test_pause_resume_limit
+ assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}}
+ markup = <<-MKUP
+ {%for i in array.items limit:3 %}{{i}}{%endfor%}
+ next
+ {%for i in array.items offset:continue limit:3 %}{{i}}{%endfor%}
+ next
+ {%for i in array.items offset:continue limit:1 %}{{i}}{%endfor%}
+ MKUP
+ expected = <<-XPCTD
+ 123
+ next
+ 456
+ next
+ 7
+ XPCTD
+ assert_template_result(expected,markup,assigns)
+ end
+
+ def test_pause_resume_BIG_limit
+ assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}}
+ markup = <<-MKUP
+ {%for i in array.items limit:3 %}{{i}}{%endfor%}
+ next
+ {%for i in array.items offset:continue limit:3 %}{{i}}{%endfor%}
+ next
+ {%for i in array.items offset:continue limit:1000 %}{{i}}{%endfor%}
+ MKUP
+ expected = <<-XPCTD
+ 123
+ next
+ 456
+ next
+ 7890
+ XPCTD
+ assert_template_result(expected,markup,assigns)
+ end
+
+
+ def test_pause_resume_BIG_offset
+ assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,0]}}
+ markup = %q({%for i in array.items limit:3 %}{{i}}{%endfor%}
+ next
+ {%for i in array.items offset:continue limit:3 %}{{i}}{%endfor%}
+ next
+ {%for i in array.items offset:continue limit:3 offset:1000 %}{{i}}{%endfor%})
+ expected = %q(123
+ next
+ 456
+ next
+ )
+ assert_template_result(expected,markup,assigns)
+ end
+
+ def test_assign
+ assigns = {'var' => 'content' }
+ assert_template_result('var2: var2:content','var2:{{var2}} {%assign var2 = var%} var2:{{var2}}',assigns)
+
+ end
+
+ def test_hyphenated_assign
+ assigns = {'a-b' => '1' }
+ assert_template_result('a-b:1 a-b:2','a-b:{{a-b}} {%assign a-b = 2 %}a-b:{{a-b}}',assigns)
+
+ end
+
+ def test_assign_with_colon_and_spaces
+ assigns = {'var' => {'a:b c' => {'paged' => '1' }}}
+ assert_template_result('var2: 1','{%assign var2 = var["a:b c"].paged %}var2: {{var2}}',assigns)
+ end
+
+ def test_capture
+ assigns = {'var' => 'content' }
+ assert_template_result('content foo content foo ','{{ var2 }}{% capture var2 %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}', assigns)
+ end
+
+ def test_capture_detects_bad_syntax
+ assert_raise(SyntaxError) do
+ assert_template_result('content foo content foo ','{{ var2 }}{% capture %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}', {'var' => 'content' })
+ end
+ end
+
+ def test_case
+ assigns = {'condition' => 2 }
+ assert_template_result(' its 2 ','{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}', assigns)
+
+ assigns = {'condition' => 1 }
+ assert_template_result(' its 1 ','{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}', assigns)
+
+ assigns = {'condition' => 3 }
+ assert_template_result('','{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}', assigns)
+
+ assigns = {'condition' => "string here" }
+ assert_template_result(' hit ','{% case condition %}{% when "string here" %} hit {% endcase %}', assigns)
+
+ assigns = {'condition' => "bad string here" }
+ assert_template_result('','{% case condition %}{% when "string here" %} hit {% endcase %}', assigns)
+ end
+
+ def test_case_with_else
+
+ assigns = {'condition' => 5 }
+ assert_template_result(' hit ','{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}', assigns)
+
+ assigns = {'condition' => 6 }
+ assert_template_result(' else ','{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}', assigns)
+
+ assigns = {'condition' => 6 }
+ assert_template_result(' else ','{% case condition %} {% when 5 %} hit {% else %} else {% endcase %}', assigns)
+
+
+ end
+
+ def test_case_on_size
+ assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [])
+ assert_template_result('1' , '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1])
+ assert_template_result('2' , '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1])
+ assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1])
+ assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1, 1])
+ assert_template_result('', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% endcase %}', 'a' => [1, 1, 1, 1, 1])
+ end
+
+ def test_case_on_size_with_else
+ assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [])
+ assert_template_result('1', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1])
+ assert_template_result('2', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1])
+ assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1, 1])
+ assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1, 1, 1])
+ assert_template_result('else', '{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}', 'a' => [1, 1, 1, 1, 1])
+ end
+
+ def test_case_on_length_with_else
+ assert_template_result('else', '{% case a.empty? %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {})
+ assert_template_result('false', '{% case false %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {})
+ assert_template_result('true', '{% case true %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {})
+ assert_template_result('else', '{% case NULL %}{% when true %}true{% when false %}false{% else %}else{% endcase %}', {})
+ end
+
+ def test_assign_from_case
+ # Example from the shopify forums
+ code = %q({% case collection.handle %}{% when 'menswear-jackets' %}{% assign ptitle = 'menswear' %}{% when 'menswear-t-shirts' %}{% assign ptitle = 'menswear' %}{% else %}{% assign ptitle = 'womenswear' %}{% endcase %}{{ ptitle }})
+ template = Liquid::Template.parse(code)
+ assert_equal "menswear", template.render("collection" => {'handle' => 'menswear-jackets'})
+ assert_equal "menswear", template.render("collection" => {'handle' => 'menswear-t-shirts'})
+ assert_equal "womenswear", template.render("collection" => {'handle' => 'x'})
+ assert_equal "womenswear", template.render("collection" => {'handle' => 'y'})
+ assert_equal "womenswear", template.render("collection" => {'handle' => 'z'})
+ end
+
+ def test_case_when_or
+ code = '{% case condition %}{% when 1 or 2 or 3 %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}'
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 1 })
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 2 })
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 3 })
+ assert_template_result(' its 4 ', code, {'condition' => 4 })
+ assert_template_result('', code, {'condition' => 5 })
+
+ code = '{% case condition %}{% when 1 or "string" or null %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}'
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 1 })
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 'string' })
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => nil })
+ assert_template_result('', code, {'condition' => 'something else' })
+ end
+
+ def test_case_when_comma
+ code = '{% case condition %}{% when 1, 2, 3 %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}'
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 1 })
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 2 })
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 3 })
+ assert_template_result(' its 4 ', code, {'condition' => 4 })
+ assert_template_result('', code, {'condition' => 5 })
+
+ code = '{% case condition %}{% when 1, "string", null %} its 1 or 2 or 3 {% when 4 %} its 4 {% endcase %}'
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 1 })
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => 'string' })
+ assert_template_result(' its 1 or 2 or 3 ', code, {'condition' => nil })
+ assert_template_result('', code, {'condition' => 'something else' })
+ end
+
+ def test_assign
+ assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{{a}}' ).render
+ end
+
+ def test_assign_is_global
+ assert_equal 'variable', Liquid::Template.parse( '{%for i in (1..2) %}{% assign a = "variable"%}{% endfor %}{{a}}' ).render
+ end
+
+ def test_case_detects_bad_syntax
+ assert_raise(SyntaxError) do
+ assert_template_result('', '{% case false %}{% when %}true{% endcase %}', {})
+ end
+
+ assert_raise(SyntaxError) do
+ assert_template_result('', '{% case false %}{% huh %}true{% endcase %}', {})
+ end
+
+ end
+
+
+
+ def test_cycle
+
+ assert_template_result('one','{%cycle "one", "two"%}')
+ assert_template_result('one two','{%cycle "one", "two"%} {%cycle "one", "two"%}')
+
+ assert_template_result('one two one','{%cycle "one", "two"%} {%cycle "one", "two"%} {%cycle "one", "two"%}')
+
+ assert_template_result('text-align: left text-align: right','{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}')
+
+ end
+
+ def test_multiple_cycles
+ assert_template_result('1 2 1 1 2 3 1','{%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%}')
+ end
+
+ def test_multiple_named_cycles
+ assert_template_result('one one two two one one','{%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %}')
+ end
+
+ def test_multiple_named_cycles_with_names_from_context
+ assigns = {"var1" => 1, "var2" => 2 }
+ assert_template_result('one one two two one one','{%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %}', assigns)
+ end
+
+ def test_size_of_array
+ assigns = {"array" => [1,2,3,4]}
+ assert_template_result('array has 4 elements', "array has {{ array.size }} elements", assigns)
+ end
+
+ def test_size_of_hash
+ assigns = {"hash" => {:a => 1, :b => 2, :c=> 3, :d => 4}}
+ assert_template_result('hash has 4 elements', "hash has {{ hash.size }} elements", assigns)
+ end
+
+ def test_illegal_symbols
+ assert_template_result('', '{% if true == empty %}?{% endif %}', {})
+ assert_template_result('', '{% if true == null %}?{% endif %}', {})
+ assert_template_result('', '{% if empty == true %}?{% endif %}', {})
+ assert_template_result('', '{% if null == true %}?{% endif %}', {})
+ end
+
+ def test_for_reversed
+ assigns = {'array' => [ 1, 2, 3] }
+ assert_template_result('321','{%for item in array reversed %}{{item}}{%endfor%}',assigns)
+ end
+
+
+ def test_ifchanged
+ assigns = {'array' => [ 1, 1, 2, 2, 3, 3] }
+ assert_template_result('123','{%for item in array%}{%ifchanged%}{{item}}{% endifchanged %}{%endfor%}',assigns)
+
+ assigns = {'array' => [ 1, 1, 1, 1] }
+ assert_template_result('1','{%for item in array%}{%ifchanged%}{{item}}{% endifchanged %}{%endfor%}',assigns)
+ end
+end
diff --git a/vendor/plugins/liquid/test/statements_test.rb b/vendor/plugins/liquid/test/statements_test.rb
new file mode 100644
index 000000000..63a41040e
--- /dev/null
+++ b/vendor/plugins/liquid/test/statements_test.rb
@@ -0,0 +1,137 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+class StatementsTest < Test::Unit::TestCase
+ include Liquid
+
+
+ def test_true_eql_true
+ text = %| {% if true == true %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render
+ end
+
+ def test_true_not_eql_true
+ text = %| {% if true != true %} true {% else %} false {% endif %} |
+ expected = %| false |
+ assert_equal expected, Template.parse(text).render
+ end
+
+ def test_true_lq_true
+ text = %| {% if 0 > 0 %} true {% else %} false {% endif %} |
+ expected = %| false |
+ assert_equal expected, Template.parse(text).render
+ end
+
+ def test_one_lq_zero
+ text = %| {% if 1 > 0 %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render
+ end
+
+ def test_zero_lq_one
+ text = %| {% if 0 < 1 %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render
+ end
+
+ def test_zero_lq_or_equal_one
+ text = %| {% if 0 <= 0 %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render
+ end
+
+ def test_zero_lq_or_equal_one_involving_nil
+ text = %| {% if null <= 0 %} true {% else %} false {% endif %} |
+ expected = %| false |
+ assert_equal expected, Template.parse(text).render
+
+
+ text = %| {% if 0 <= null %} true {% else %} false {% endif %} |
+ expected = %| false |
+ assert_equal expected, Template.parse(text).render
+ end
+
+ def test_zero_lqq_or_equal_one
+ text = %| {% if 0 >= 0 %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render
+ end
+
+ def test_strings
+ text = %| {% if 'test' == 'test' %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render
+ end
+
+ def test_strings_not_equal
+ text = %| {% if 'test' != 'test' %} true {% else %} false {% endif %} |
+ expected = %| false |
+ assert_equal expected, Template.parse(text).render
+ end
+
+ def test_var_strings_equal
+ text = %| {% if var == "hello there!" %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render('var' => 'hello there!')
+ end
+
+ def test_var_strings_are_not_equal
+ text = %| {% if "hello there!" == var %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render('var' => 'hello there!')
+ end
+
+ def test_var_and_long_string_are_equal
+ text = %| {% if var == 'hello there!' %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render('var' => 'hello there!')
+ end
+
+
+ def test_var_and_long_string_are_equal_backwards
+ text = %| {% if 'hello there!' == var %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render('var' => 'hello there!')
+ end
+
+ #def test_is_nil
+ # text = %| {% if var != nil %} true {% else %} false {% end %} |
+ # @template.assigns = { 'var' => 'hello there!'}
+ # expected = %| true |
+ # assert_equal expected, @template.parse(text)
+ #end
+
+ def test_is_collection_empty
+ text = %| {% if array == empty %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render('array' => [])
+ end
+
+ def test_is_not_collection_empty
+ text = %| {% if array == empty %} true {% else %} false {% endif %} |
+ expected = %| false |
+ assert_equal expected, Template.parse(text).render('array' => [1,2,3])
+ end
+
+ def test_nil
+ text = %| {% if var == nil %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render('var' => nil)
+
+ text = %| {% if var == null %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render('var' => nil)
+ end
+
+ def test_not_nil
+ text = %| {% if var != nil %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render('var' => 1 )
+
+ text = %| {% if var != null %} true {% else %} false {% endif %} |
+ expected = %| true |
+ assert_equal expected, Template.parse(text).render('var' => 1 )
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/strainer_test.rb b/vendor/plugins/liquid/test/strainer_test.rb
new file mode 100644
index 000000000..540888cee
--- /dev/null
+++ b/vendor/plugins/liquid/test/strainer_test.rb
@@ -0,0 +1,21 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+class StrainerTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_strainer
+ strainer = Strainer.create(nil)
+ assert_equal false, strainer.respond_to?('__test__')
+ assert_equal false, strainer.respond_to?('test')
+ assert_equal false, strainer.respond_to?('instance_eval')
+ assert_equal false, strainer.respond_to?('__send__')
+ assert_equal true, strainer.respond_to?('size') # from the standard lib
+ end
+
+ def test_should_respond_to_two_parameters
+ strainer = Strainer.create(nil)
+ assert_equal true, strainer.respond_to?('size', false)
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/template_test.rb b/vendor/plugins/liquid/test/template_test.rb
new file mode 100644
index 000000000..d96d274d3
--- /dev/null
+++ b/vendor/plugins/liquid/test/template_test.rb
@@ -0,0 +1,26 @@
+require File.dirname(__FILE__) + '/helper'
+
+class TemplateTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_tokenize_strings
+ assert_equal [' '], Template.new.send(:tokenize, ' ')
+ assert_equal ['hello world'], Template.new.send(:tokenize, 'hello world')
+ end
+
+ def test_tokenize_variables
+ assert_equal ['{{funk}}'], Template.new.send(:tokenize, '{{funk}}')
+ assert_equal [' ', '{{funk}}', ' '], Template.new.send(:tokenize, ' {{funk}} ')
+ assert_equal [' ', '{{funk}}', ' ', '{{so}}', ' ', '{{brother}}', ' '], Template.new.send(:tokenize, ' {{funk}} {{so}} {{brother}} ')
+ assert_equal [' ', '{{ funk }}', ' '], Template.new.send(:tokenize, ' {{ funk }} ')
+ end
+
+ def test_tokenize_blocks
+ assert_equal ['{%comment%}'], Template.new.send(:tokenize, '{%comment%}')
+ assert_equal [' ', '{%comment%}', ' '], Template.new.send(:tokenize, ' {%comment%} ')
+
+ assert_equal [' ', '{%comment%}', ' ', '{%endcomment%}', ' '], Template.new.send(:tokenize, ' {%comment%} {%endcomment%} ')
+ assert_equal [' ', '{% comment %}', ' ', '{% endcomment %}', ' '], Template.new.send(:tokenize, " {% comment %} {% endcomment %} ")
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/test_helper.rb b/vendor/plugins/liquid/test/test_helper.rb
new file mode 100644
index 000000000..deb818594
--- /dev/null
+++ b/vendor/plugins/liquid/test/test_helper.rb
@@ -0,0 +1,20 @@
+#!/usr/bin/env ruby
+$LOAD_PATH.unshift(File.dirname(__FILE__)+ '/extra')
+
+require 'test/unit'
+require 'test/unit/assertions'
+require 'caller'
+require 'breakpoint'
+require File.dirname(__FILE__) + '/../lib/liquid'
+
+
+module Test
+ module Unit
+ module Assertions
+ include Liquid
+ def assert_template_result(expected, template, assigns={}, message=nil)
+ assert_equal expected, Template.parse(template).render(assigns)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/unless_else_test.rb b/vendor/plugins/liquid/test/unless_else_test.rb
new file mode 100644
index 000000000..1c420ba8c
--- /dev/null
+++ b/vendor/plugins/liquid/test/unless_else_test.rb
@@ -0,0 +1,27 @@
+require File.dirname(__FILE__) + '/helper'
+
+class UnlessElseTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_unless
+ assert_template_result(' ',' {% unless true %} this text should not go into the output {% endunless %} ')
+ assert_template_result(' this text should go into the output ',
+ ' {% unless false %} this text should go into the output {% endunless %} ')
+ assert_template_result(' you rock ?','{% unless true %} you suck {% endunless %} {% unless false %} you rock {% endunless %}?')
+ end
+
+ def test_unless_else
+ assert_template_result(' YES ','{% unless true %} NO {% else %} YES {% endunless %}')
+ assert_template_result(' YES ','{% unless false %} YES {% else %} NO {% endunless %}')
+ assert_template_result(' YES ','{% unless "foo" %} NO {% else %} YES {% endunless %}')
+ end
+
+ def test_unless_in_loop
+ assert_template_result '23', '{% for i in choices %}{% unless i %}{{ forloop.index }}{% endunless %}{% endfor %}', 'choices' => [1, nil, false]
+ end
+
+ def test_unless_else_in_loop
+ assert_template_result ' TRUE 2 3 ', '{% for i in choices %}{% unless i %} {{ forloop.index }} {% else %} TRUE {% endunless %}{% endfor %}', 'choices' => [1, nil, false]
+ end
+
+end
\ No newline at end of file
diff --git a/vendor/plugins/liquid/test/variable_test.rb b/vendor/plugins/liquid/test/variable_test.rb
new file mode 100644
index 000000000..058383ac3
--- /dev/null
+++ b/vendor/plugins/liquid/test/variable_test.rb
@@ -0,0 +1,135 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__) + '/helper'
+
+class VariableTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_variable
+ var = Variable.new('hello')
+ assert_equal 'hello', var.name
+ end
+
+ def test_filters
+ var = Variable.new('hello | textileze')
+ assert_equal 'hello', var.name
+ assert_equal [[:textileze,[]]], var.filters
+
+ var = Variable.new('hello | textileze | paragraph')
+ assert_equal 'hello', var.name
+ assert_equal [[:textileze,[]], [:paragraph,[]]], var.filters
+
+ var = Variable.new(%! hello | strftime: '%Y'!)
+ assert_equal 'hello', var.name
+ assert_equal [[:strftime,["'%Y'"]]], var.filters
+
+ var = Variable.new(%! 'typo' | link_to: 'Typo', true !)
+ assert_equal %!'typo'!, var.name
+ assert_equal [[:link_to,["'Typo'", "true"]]], var.filters
+
+ var = Variable.new(%! 'typo' | link_to: 'Typo', false !)
+ assert_equal %!'typo'!, var.name
+ assert_equal [[:link_to,["'Typo'", "false"]]], var.filters
+
+ var = Variable.new(%! 'foo' | repeat: 3 !)
+ assert_equal %!'foo'!, var.name
+ assert_equal [[:repeat,["3"]]], var.filters
+
+ var = Variable.new(%! 'foo' | repeat: 3, 3 !)
+ assert_equal %!'foo'!, var.name
+ assert_equal [[:repeat,["3","3"]]], var.filters
+
+ var = Variable.new(%! 'foo' | repeat: 3, 3, 3 !)
+ assert_equal %!'foo'!, var.name
+ assert_equal [[:repeat,["3","3","3"]]], var.filters
+
+ var = Variable.new(%! hello | strftime: '%Y, okay?'!)
+ assert_equal 'hello', var.name
+ assert_equal [[:strftime,["'%Y, okay?'"]]], var.filters
+
+ var = Variable.new(%! hello | things: "%Y, okay?", 'the other one'!)
+ assert_equal 'hello', var.name
+ assert_equal [[:things,["\"%Y, okay?\"","'the other one'"]]], var.filters
+ end
+
+ def test_filter_with_date_parameter
+
+ var = Variable.new(%! '2006-06-06' | date: "%m/%d/%Y"!)
+ assert_equal "'2006-06-06'", var.name
+ assert_equal [[:date,["\"%m/%d/%Y\""]]], var.filters
+
+ end
+
+ def test_filters_without_whitespace
+ var = Variable.new('hello | textileze | paragraph')
+ assert_equal 'hello', var.name
+ assert_equal [[:textileze,[]], [:paragraph,[]]], var.filters
+
+ var = Variable.new('hello|textileze|paragraph')
+ assert_equal 'hello', var.name
+ assert_equal [[:textileze,[]], [:paragraph,[]]], var.filters
+ end
+
+ def test_symbol
+ var = Variable.new("http://disney.com/logo.gif | image: 'med' ")
+ assert_equal 'http://disney.com/logo.gif', var.name
+ assert_equal [[:image,["'med'"]]], var.filters
+ end
+
+ def test_string_single_quoted
+ var = Variable.new(%| "hello" |)
+ assert_equal '"hello"', var.name
+ end
+
+ def test_string_double_quoted
+ var = Variable.new(%| 'hello' |)
+ assert_equal "'hello'", var.name
+ end
+
+ def test_integer
+ var = Variable.new(%| 1000 |)
+ assert_equal "1000", var.name
+ end
+
+ def test_float
+ var = Variable.new(%| 1000.01 |)
+ assert_equal "1000.01", var.name
+ end
+
+ def test_string_with_special_chars
+ var = Variable.new(%| 'hello! $!@.;"ddasd" ' |)
+ assert_equal %|'hello! $!@.;"ddasd" '|, var.name
+ end
+
+ def test_string_dot
+ var = Variable.new(%| test.test |)
+ assert_equal 'test.test', var.name
+ end
+end
+
+
+class VariableResolutionTest < Test::Unit::TestCase
+ include Liquid
+
+ def test_simple_variable
+ template = Template.parse(%|{{test}}|)
+ assert_equal 'worked', template.render('test' => 'worked')
+ assert_equal 'worked wonderfully', template.render('test' => 'worked wonderfully')
+ end
+
+ def test_simple_with_whitespaces
+ template = Template.parse(%| {{ test }} |)
+ assert_equal ' worked ', template.render('test' => 'worked')
+ assert_equal ' worked wonderfully ', template.render('test' => 'worked wonderfully')
+ end
+
+ def test_ignore_unknown
+ template = Template.parse(%|{{ test }}|)
+ assert_equal '', template.render
+ end
+
+ def test_hash_scoping
+ template = Template.parse(%|{{ test.test }}|)
+ assert_equal 'worked', template.render('test' => {'test' => 'worked'})
+ end
+
+end
\ No newline at end of file