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| %> + + + + + +<% end %> +
<%= snippet.name %><%= link_to 'Edit', edit_admin_snippet_path(snippet) %><%= link_to 'Destroy', admin_snippet_path(snippet), :confirm => 'Are you sure?', :method => :delete %>
+ +
+ +<%= 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? + + + +== 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 %} + + + + + 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("\n12\n12\n12\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}| + 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