diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 777e91e..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,26 +0,0 @@ -# How to contribute - -## Things we will merge - -* Bugfixes -* Performance improvements -* Features which are likely to be useful to the majority of Liquid users - -## Things we won't merge - -* Code which introduces considerable performance degrations -* Code which touches performance critical parts of Liquid and comes without benchmarks -* Features which are not important for most people (we want to keep the core Liquid code small and tidy) -* Features which can easily be implemented on top of Liquid (for example as a custom filter or custom filesystem) -* Code which comes without tests -* Code which breaks existing tests - -## Workflow - -* Fork the Liquid repository -* Create a new branch in your fork -* If it makes sense, add tests for your code and run a performance benchmark -* Make sure all tests pass -* Create a pull request -* In the description, ping one of [@boourns](https://github.com/boourns), [@fw42](https://github.com/fw42), [@camilo](https://github.com/camilo), [@dylanahsmith](https://github.com/dylanahsmith), or [@arthurnn](https://github.com/arthurnn) and ask for a code review. - diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 13933d9..0000000 --- a/Gemfile +++ /dev/null @@ -1,8 +0,0 @@ -source 'https://rubygems.org' - -gemspec -gem 'stackprof', platforms: :mri_21 - -group :test do - gem 'spy', '0.4.1' -end diff --git a/History.md b/History.md deleted file mode 100644 index 9e18ade..0000000 --- a/History.md +++ /dev/null @@ -1,147 +0,0 @@ -# Liquid Version History - -## 3.0.0 / not yet released / branch "master" - -* ... -* Optimize variable parsing to avoid repeated regex evaluation during template rendering #383 [Jason Hiltz-Laforge, jasonhl] -* Optimize checking for block interrupts to reduce object allocation #380 [Jason Hiltz-Laforge, jasonhl] -* Properly set context rethrow_errors on render! #349 [Thierry Joyal, tjoyal] -* Fix broken rendering of variables which are equal to false, see #345 [Florian Weingarten, fw42] -* Remove ActionView template handler [Dylan Thacker-Smith, dylanahsmith] -* Freeze lots of string literals for new Ruby 2.1 optimization, see #297 [Florian Weingarten, fw42] -* Allow newlines in tags and variables, see #324 [Dylan Thacker-Smith, dylanahsmith] -* Tag#parse is called after initialize, which now takes options instead of tokens as the 3rd argument. See #321 [Dylan Thacker-Smith, dylanahsmith] -* Raise `Liquid::ArgumentError` instead of `::ArgumentError` when filter has wrong number of arguments #309 [Bogdan Gusiev, bogdan] -* Add a to_s default for liquid drops, see #306 [Adam Doeler, releod] -* Add strip, lstrip, and rstrip to standard filters [Florian Weingarten, fw42] -* Make if, for & case tags return complete and consistent nodelists, see #250 [Nick Jones, dntj] -* Prevent arbitrary method invocation on condition objects, see #274 [Dylan Thacker-Smith, dylanahsmith] -* Don't call to_sym when creating conditions for security reasons, see #273 [Bouke van der Bijl, bouk] -* Fix resource counting bug with respond_to?(:length), see #263 [Florian Weingarten, fw42] -* Allow specifying custom patterns for template filenames, see #284 [Andrei Gladkyi, agladkyi] -* Allow drops to optimize loading a slice of elements, see #282 [Tom Burns, boourns] -* Support for passing variables to snippets in subdirs, see #271 [Joost Hietbrink, joost] -* Add a class cache to avoid runtime extend calls, see #249 [James Tucker, raggi] -* Remove some legacy Ruby 1.8 compatibility code, see #276 [Florian Weingarten, fw42] -* Add default filter to standard filters, see #267 [Derrick Reimer, djreimer] -* Add optional strict parsing and warn parsing, see #235 [Tristan Hume, trishume] -* Add I18n syntax error translation, see #241 [Simon Hørup Eskildsen, Sirupsen] -* Make sort filter work on enumerable drops, see #239 [Florian Weingarten, fw42] -* Fix clashing method names in enumerable drops, see #238 [Florian Weingarten, fw42] -* Make map filter work on enumerable drops, see #233 [Florian Weingarten, fw42] -* Improved whitespace stripping for blank blocks, related to #216 [Florian Weingarten, fw42] - -## 2.6.0 / 2013-11-25 / branch "2.6-stable" - -IMPORTANT: Liquid 2.6 is going to be the last version of Liquid which maintains explicit Ruby 1.8 compatability. -The following releases will only be tested against Ruby 1.9 and Ruby 2.0 and are likely to break on Ruby 1.8. - -* Bugfix for #106: fix example servlet [gnowoel] -* Bugfix for #97: strip_html filter supports multi-line tags [Jo Liss, joliss] -* Bugfix for #114: strip_html filter supports style tags [James Allardice, jamesallardice] -* Bugfix for #117: 'now' support for date filter in Ruby 1.9 [Notre Dame Webgroup, ndwebgroup] -* Bugfix for #166: truncate filter on UTF-8 strings with Ruby 1.8 [Florian Weingarten, fw42] -* Bugfix for #204: 'raw' parsing bug [Florian Weingarten, fw42] -* Bugfix for #150: 'for' parsing bug [Peter Schröder, phoet] -* Bugfix for #126: Strip CRLF in strip_newline [Peter Schröder, phoet] -* Bugfix for #174, "can't convert Fixnum into String" for "replace" [wǒ_is神仙, jsw0528] -* Allow a Liquid::Drop to be passed into Template#render [Daniel Huckstep, darkhelmet] -* Resource limits [Florian Weingarten, fw42] -* Add reverse filter [Jay Strybis, unreal] -* Add utf-8 support -* Use array instead of Hash to keep the registered filters [Tasos Stathopoulos, astathopoulos] -* Cache tokenized partial templates [Tom Burns, boourns] -* Avoid warnings in Ruby 1.9.3 [Marcus Stollsteimer, stomar] -* Better documentation for 'include' tag (closes #163) [Peter Schröder, phoet] -* Use of BigDecimal on filters to have better precision (closes #155) [Arthur Nogueira Neves, arthurnn] - -## 2.5.4 / 2013-11-11 / branch "2.5-stable" - -* Fix "can't convert Fixnum into String" for "replace", see #173, [wǒ_is神仙, jsw0528] - -## 2.5.3 / 2013-10-09 - -* #232, #234, #237: Fix map filter bugs [Florian Weingarten, fw42] - -## 2.5.2 / 2013-09-03 / deleted - -Yanked from rubygems, as it contained too many changes that broke compatibility. Those changes will be on following major releases. - -## 2.5.1 / 2013-07-24 - -* #230: Fix security issue with map filter, Use invoke_drop in map filter [Florian Weingarten, fw42] - -## 2.5.0 / 2013-03-06 - -* Prevent Object methods from being called on drops -* Avoid symbol injection from liquid -* Added break and continue statements -* Fix filter parser for args without space separators -* Add support for filter keyword arguments - - -## 2.4.0 / 2012-08-03 - -* Performance improvements -* Allow filters in `assign` -* Add `modulo` filter -* Ruby 1.8, 1.9, and Rubinius compatibility fixes -* Add support for `quoted['references']` in `tablerow` -* Add support for Enumerable to `tablerow` -* `strip_html` filter removes html comments - - -## 2.3.0 / 2011-10-16 - -* Several speed/memory improvements -* Numerous bug fixes -* Added support for MRI 1.9, Rubinius, and JRuby -* Added support for integer drop parameters -* Added epoch support to `date` filter -* New `raw` tag that suppresses parsing -* Added `else` option to `for` tag -* New `increment` tag -* New `split` filter - - -## 2.2.1 / 2010-08-23 - -* Added support for literal tags - - -## 2.2.0 / 2010-08-22 - -* Compatible with Ruby 1.8.7, 1.9.1 and 1.9.2-p0 -* Merged some changed made by the community - - -## 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/MIT-LICENSE b/MIT-LICENSE deleted file mode 100644 index 926b04e..0000000 --- a/MIT-LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -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. diff --git a/README.md b/README.md index a872c78..a3d40b2 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,3 @@ -[![Build Status](https://secure.travis-ci.org/Shopify/liquid.png?branch=master)](http://travis-ci.org/Shopify/liquid) -[![Inline docs](http://inch-ci.org/github/Shopify/liquid.png)](http://inch-ci.org/github/Shopify/liquid) +#### Liquid Docs -# Liquid template engine - -* [Contributing guidelines](CONTRIBUTING.md) -* [Version history](History.md) -* [Liquid documentation from Shopify](http://docs.shopify.com/themes/liquid-basics) -* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki) -* [Website](http://liquidmarkup.org/) - -## Introduction - -Liquid is a template engine which was written with 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 separate 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 you should 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 (PHP) 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 templating engine. - -## What does it look like? - -```html - -``` - -## How to 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. - -```ruby -@template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template -@template.render('name' => 'tobi') # => "hi tobi" -``` - -### Error Modes - -Setting the error mode of Liquid lets you specify how strictly you want your templates to be interpreted. -Normally the parser is very lax and will accept almost anything without error. Unfortunately this can make -it very hard to debug and can lead to unexpected behaviour. - -Liquid also comes with a stricter parser that can be used when editing templates to give better error messages -when templates are invalid. You can enable this new parser like this: - -```ruby -Liquid::Template.error_mode = :strict # Raises a SyntaxError when invalid syntax is used -Liquid::Template.error_mode = :warn # Adds errors to template.errors but continues as normal -Liquid::Template.error_mode = :lax # The default mode, accepts almost anything. -``` - -If you want to set the error mode only on specific templates you can pass `:error_mode` as an option to `parse`: -```ruby -Liquid::Template.parse(source, :error_mode => :strict) -``` -This is useful for doing things like enabling strict mode only in the theme editor. - -It is recommended that you enable `:strict` or `:warn` mode on new apps to stop invalid templates from being created. -It is also recommended that you use it in the template editors of existing apps to give editors better error messages. +Home of the Liquid Docs \ No newline at end of file diff --git a/Rakefile b/Rakefile deleted file mode 100755 index 6296027..0000000 --- a/Rakefile +++ /dev/null @@ -1,77 +0,0 @@ -require 'rake' -require 'rake/testtask' -$LOAD_PATH.unshift File.expand_path("../lib", __FILE__) -require "liquid/version" - -task :default => 'test' - -desc 'run test suite with default parser' -Rake::TestTask.new(:base_test) do |t| - t.libs << '.' << 'lib' << 'test' - t.test_files = FileList['test/{integration,unit}/**/*_test.rb'] - t.verbose = false -end - -desc 'run test suite with warn error mode' -task :warn_test do - ENV['LIQUID_PARSER_MODE'] = 'warn' - Rake::Task['base_test'].invoke -end - -desc 'runs test suite with both strict and lax parsers' -task :test do - ENV['LIQUID_PARSER_MODE'] = 'lax' - Rake::Task['base_test'].invoke - ENV['LIQUID_PARSER_MODE'] = 'strict' - Rake::Task['base_test'].reenable - Rake::Task['base_test'].invoke -end - -task :gem => :build -task :build do - system "gem build liquid.gemspec" -end - -task :install => :build do - system "gem install liquid-#{Liquid::VERSION}.gem" -end - -task :release => :build do - system "git tag -a v#{Liquid::VERSION} -m 'Tagging #{Liquid::VERSION}'" - system "git push --tags" - system "gem push liquid-#{Liquid::VERSION}.gem" - system "rm liquid-#{Liquid::VERSION}.gem" -end - -namespace :benchmark do - - desc "Run the liquid benchmark with lax parsing" - task :run do - ruby "./performance/benchmark.rb lax" - end - - desc "Run the liquid benchmark with strict parsing" - task :strict do - ruby "./performance/benchmark.rb strict" - end -end - - -namespace :profile do - - desc "Run the liquid profile/performance coverage" - task :run do - ruby "./performance/profile.rb" - end - - desc "Run the liquid profile/performance coverage with strict parsing" - task :strict do - ruby "./performance/profile.rb strict" - end - -end - -desc "Run example" -task :example do - ruby "-w -d -Ilib example/server/server.rb" -end diff --git a/example/server/example_servlet.rb b/example/server/example_servlet.rb deleted file mode 100644 index 16f3bff..0000000 --- a/example/server/example_servlet.rb +++ /dev/null @@ -1,41 +0,0 @@ -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, 'description' => description, '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 - - def description - "List of Products ~ This is a list of products with price and description." - end - -end diff --git a/example/server/liquid_servlet.rb b/example/server/liquid_servlet.rb deleted file mode 100644 index 794d66b..0000000 --- a/example/server/liquid_servlet.rb +++ /dev/null @@ -1,28 +0,0 @@ -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+)\z/ - @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 diff --git a/example/server/server.rb b/example/server/server.rb deleted file mode 100644 index 78cb7ff..0000000 --- a/example/server/server.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'webrick' -require 'rexml/document' - -DIR = File.expand_path(File.dirname(__FILE__)) - -require DIR + '/../../lib/liquid' -require DIR + '/liquid_servlet' -require DIR + '/example_servlet' - -# Setup webrick -server = WEBrick::HTTPServer.new( :Port => ARGV[1] || 3000 ) -server.mount('/', Servlet) -trap("INT"){ server.shutdown } -server.start diff --git a/example/server/templates/index.liquid b/example/server/templates/index.liquid deleted file mode 100644 index 4872aa8..0000000 --- a/example/server/templates/index.liquid +++ /dev/null @@ -1,6 +0,0 @@ -

Hello world!

- -

It is {{date}}

- - -

Check out the Products screen

diff --git a/example/server/templates/products.liquid b/example/server/templates/products.liquid deleted file mode 100644 index 374b029..0000000 --- a/example/server/templates/products.liquid +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - products - - - - - - - - - - -

{{ description | split: '~' | first }}

- -

{{ description | split: '~' | last }}

- -

There are currently {{products | count}} products in the {{section}} catalog

- - {% if cool_products %} - Cool products :) - {% else %} - Uncool products :( - {% endif %} - - - - - diff --git a/lib/liquid.rb b/lib/liquid.rb deleted file mode 100644 index bb81745..0000000 --- a/lib/liquid.rb +++ /dev/null @@ -1,69 +0,0 @@ -# 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. - -module Liquid - FilterSeparator = /\|/ - ArgumentSeparator = ','.freeze - FilterArgumentSeparator = ':'.freeze - VariableAttributeSeparator = '.'.freeze - TagStart = /\{\%/ - TagEnd = /\%\}/ - VariableSignature = /\(?[\w\-\.\[\]]\)?/ - VariableSegment = /[\w\-]/ - VariableStart = /\{\{/ - VariableEnd = /\}\}/ - VariableIncompleteEnd = /\}\}?/ - QuotedString = /"[^"]*"|'[^']*'/ - QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o - TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/o - AnyStartingTag = /\{\{|\{\%/ - PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om - TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om - VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o - - singleton_class.send(:attr_accessor, :cache_classes) - self.cache_classes = true -end - -require "liquid/version" -require 'liquid/lexer' -require 'liquid/parser' -require 'liquid/i18n' -require 'liquid/drop' -require 'liquid/extensions' -require 'liquid/errors' -require 'liquid/interrupts' -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/standardfilters' -require 'liquid/condition' -require 'liquid/module_ex' -require 'liquid/utils' - -# Load all the tags of the standard library -# -Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f } diff --git a/lib/liquid/block.rb b/lib/liquid/block.rb deleted file mode 100644 index 7717f0b..0000000 --- a/lib/liquid/block.rb +++ /dev/null @@ -1,157 +0,0 @@ -module Liquid - class Block < Tag - FullToken = /\A#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om - ContentOfVariable = /\A#{VariableStart}(.*)#{VariableEnd}\z/om - TAGSTART = "{%".freeze - VARSTART = "{{".freeze - - def initialize(tag_name, markup, options) - super - @block_delimiter = "end#{tag_name}" - end - - def blank? - @blank - end - - def parse(tokens) - @blank = true - @nodelist ||= [] - @nodelist.clear - - # All child tags of the current block. - @children = [] - - while token = tokens.shift - unless token.empty? - case - when token.start_with?(TAGSTART) - if token =~ FullToken - - # if we found the proper block delimiter 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] - new_tag = tag.parse($1, $2, tokens, @options) - @blank &&= new_tag.blank? - @nodelist << new_tag - @children << new_tag - 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.new(options[:locale].t("errors.syntax.tag_termination".freeze, :token => token, :tag_end => TagEnd.inspect)) - end - when token.start_with?(VARSTART) - new_var = create_variable(token) - @nodelist << new_var - @children << new_var - @blank = false - else - @nodelist << token - @blank &&= (token =~ /\A\s*\z/) - end - end - end - - # Make sure that it's ok to end parsing in the current block. - # Effectively this method will throw an exception unless the current block is - # of type Document - assert_missing_delimitation! - end - - # warnings of this block and all sub-tags - def warnings - all_warnings = [] - all_warnings.concat(@warnings) if @warnings - - (@children || []).each do |node| - all_warnings.concat(node.warnings || []) - end - - all_warnings - end - - def end_tag - end - - def unknown_tag(tag, params, tokens) - case tag - when 'else'.freeze - raise SyntaxError.new(options[:locale].t("errors.syntax.unexpected_else".freeze, - :block_name => block_name)) - when 'end'.freeze - raise SyntaxError.new(options[:locale].t("errors.syntax.invalid_delimiter".freeze, - :block_name => block_name, - :block_delimiter => @block_delimiter)) - else - raise SyntaxError.new(options[:locale].t("errors.syntax.unknown_tag".freeze, :tag => tag)) - end - end - - def block_name - @tag_name - end - - def create_variable(token) - token.scan(ContentOfVariable) do |content| - return Variable.new(content.first, @options) - end - raise SyntaxError.new(options[:locale].t("errors.syntax.variable_termination".freeze, :token => token, :tag_end => VariableEnd.inspect)) - end - - def render(context) - render_all(@nodelist, context) - end - - protected - - def assert_missing_delimitation! - raise SyntaxError.new(options[:locale].t("errors.syntax.tag_never_closed".freeze, :block_name => block_name)) - end - - def render_all(list, context) - output = [] - context.resource_limits[:render_length_current] = 0 - context.resource_limits[:render_score_current] += list.length - - list.each do |token| - # Break out if we have any unhanded interrupts. - break if context.has_interrupt? - - begin - # If we get an Interrupt that means the block must stop processing. An - # Interrupt is any command that stops block execution such as {% break %} - # or {% continue %} - if token.is_a? Continue or token.is_a? Break - context.push_interrupt(token.interrupt) - break - end - - token_output = (token.respond_to?(:render) ? token.render(context) : token) - context.increment_used_resources(:render_length_current, token_output) - if context.resource_limits_reached? - context.resource_limits[:reached] = true - raise MemoryError.new("Memory limits exceeded".freeze) - end - unless token.is_a?(Block) && token.blank? - output << token_output - end - rescue MemoryError => e - raise e - rescue ::StandardError => e - output << (context.handle_error(e)) - end - end - - output.join - end - end -end diff --git a/lib/liquid/condition.rb b/lib/liquid/condition.rb deleted file mode 100644 index bf025d9..0000000 --- a/lib/liquid/condition.rb +++ /dev/null @@ -1,120 +0,0 @@ -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 = { - '=='.freeze => lambda { |cond, left, right| cond.send(:equal_variables, left, right) }, - '!='.freeze => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) }, - '<>'.freeze => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) }, - '<'.freeze => :<, - '>'.freeze => :>, - '>='.freeze => :>=, - '<='.freeze => :<=, - 'contains'.freeze => lambda { |cond, left, right| left && right ? left.include?(right) : false } - } - - 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/lib/liquid/context.rb b/lib/liquid/context.rb deleted file mode 100644 index 4e74f56..0000000 --- a/lib/liquid/context.rb +++ /dev/null @@ -1,304 +0,0 @@ -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, :errors, :registers, :environments, :resource_limits - - attr_accessor :rethrow_errors - - SQUARE_BRACKETED = /\A\[(.*)\]\z/m - - def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = {}) - @environments = [environments].flatten - @scopes = [(outer_scope || {})] - @registers = registers - @errors = [] - @rethrow_errors = rethrow_errors - @resource_limits = (resource_limits || {}).merge!({ :render_score_current => 0, :assign_score_current => 0 }) - squash_instance_assigns_with_environments - - @interrupts = [] - @filters = [] - @parsed_variables = Hash.new{ |cache, markup| cache[markup] = variable_parse(markup) } - end - - def increment_used_resources(key, obj) - @resource_limits[key] += if obj.kind_of?(String) || obj.kind_of?(Array) || obj.kind_of?(Hash) - obj.length - else - 1 - end - end - - def resource_limits_reached? - (@resource_limits[:render_length_limit] && @resource_limits[:render_length_current] > @resource_limits[:render_length_limit]) || - (@resource_limits[:render_score_limit] && @resource_limits[:render_score_current] > @resource_limits[:render_score_limit] ) || - (@resource_limits[:assign_score_limit] && @resource_limits[:assign_score_current] > @resource_limits[:assign_score_limit] ) - end - - def strainer - @strainer ||= Strainer.create(self, @filters) - end - - # Adds filters to this context. - # - # Note that 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.add_known_filter(f) - end - - # If strainer is already setup then there's no choice but to use a runtime - # extend call. If strainer is not yet created, we can utilize strainers - # cached class based API, which avoids busting the method cache. - if @strainer - filters.each do |f| - strainer.extend(f) - end - else - @filters.concat filters - end - end - - # are there any not handled interrupts? - def has_interrupt? - !@interrupts.empty? - end - - # push an interrupt to the stack. this interrupt is considered not handled. - def push_interrupt(e) - @interrupts.push(e) - end - - # pop an interrupt from the stack - def pop_interrupt - @interrupts.pop - 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) - strainer.invoke(method, *args) - end - - # Push new local scope on the stack. use Context#stack instead - def push(new_scope={}) - @scopes.unshift(new_scope) - raise StackLevelError, "Nesting too deep".freeze if @scopes.length > 100 - 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(new_scope={}) - push(new_scope) - yield - ensure - pop - end - - def clear_instance_assigns - @scopes[0] = {} - 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 - LITERALS = { - nil => nil, 'nil'.freeze => nil, 'null'.freeze => nil, ''.freeze => nil, - 'true'.freeze => true, - 'false'.freeze => false, - 'blank'.freeze => :blank?, - 'empty'.freeze => :empty? - } - - # 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) - if LITERALS.key?(key) - LITERALS[key] - else - case key - when /\A'(.*)'\z/m # Single quoted strings - $1 - when /\A"(.*)"\z/m # Double quoted strings - $1 - when /\A(-?\d+)\z/ # Integer and floats - $1.to_i - when /\A\((\S+)\.\.(\S+)\)\z/ # Ranges - (resolve($1).to_i..resolve($2).to_i) - when /\A(-?\d[\d\.]+)\z/ # Floats - $1.to_f - else - variable(key) - end - end - end - - # Fetches an object starting at the local scope and then moving up the hierachy - def find_variable(key) - - # This was changed from find() to find_index() because this is a very hot - # path and find_index() is optimized in MRI to reduce object allocation - index = @scopes.find_index { |s| s.has_key?(key) } - scope = @scopes[index] if index - - variable = nil - - if scope.nil? - @environments.each do |e| - variable = lookup_and_evaluate(e, key) - unless variable.nil? - scope = e - break - end - end - end - - scope ||= @environments.last || @scopes.last - variable ||= lookup_and_evaluate(scope, key) - - variable = variable.to_liquid - variable.context = self if variable.respond_to?(:context=) - - return variable - end - - def variable_parse(markup) - parts = markup.scan(VariableParser) - needs_resolution = false - if parts.first =~ SQUARE_BRACKETED - needs_resolution = true - parts[0] = $1 - end - {:first => parts.shift, :needs_resolution => needs_resolution, :rest => parts} - 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 = @parsed_variables[markup] - - first_part = parts[:first] - if parts[:needs_resolution] - first_part = resolve(parts[:first]) - end - - if object = find_variable(first_part) - - parts[:rest].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 = lookup_and_evaluate(object, part) - 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'.freeze, 'first'.freeze, 'last'.freeze].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 # variable - - def lookup_and_evaluate(obj, key) - if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=) - obj[key] = (value.arity == 0) ? value.call : value.call(self) - else - value - end - end # lookup_and_evaluate - - def squash_instance_assigns_with_environments - @scopes.last.each_key do |k| - @environments.each do |env| - if env.has_key?(k) - scopes.last[k] = lookup_and_evaluate(env, k) - break - end - end - end - end # squash_instance_assigns_with_environments - end # Context - -end # Liquid diff --git a/lib/liquid/document.rb b/lib/liquid/document.rb deleted file mode 100644 index 1969a3a..0000000 --- a/lib/liquid/document.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Liquid - class Document < Block - def self.parse(tokens, options={}) - # we don't need markup to open this block - super(nil, nil, tokens, options) - end - - # There isn't a real delimiter - 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/lib/liquid/drop.rb b/lib/liquid/drop.rb deleted file mode 100644 index e4880c4..0000000 --- a/lib/liquid/drop.rb +++ /dev/null @@ -1,77 +0,0 @@ -require 'set' - -module Liquid - - # A drop in liquid is a class which allows you to export DOM like things to liquid. - # Methods of drops are callable. - # The main use for liquid drops is to 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 - - EMPTY_STRING = ''.freeze - - # Catch all for the method - def before_method(method) - nil - end - - # called by liquid to invoke a drop - def invoke_drop(method_or_key) - if method_or_key && method_or_key != EMPTY_STRING && self.class.invokable?(method_or_key) - send(method_or_key) - else - before_method(method_or_key) - end - end - - def has_key?(name) - true - end - - def inspect - self.class.to_s - end - - def to_liquid - self - end - - def to_s - self.class.name - end - - alias :[] :invoke_drop - - private - - # Check for method existence without invoking respond_to?, which creates symbols - def self.invokable?(method_name) - unless @invokable_methods - blacklist = Liquid::Drop.public_instance_methods + [:each] - if include?(Enumerable) - blacklist += Enumerable.public_instance_methods - blacklist -= [:sort, :count, :first, :min, :max, :include?] - end - whitelist = [:to_liquid] + (public_instance_methods - blacklist) - @invokable_methods = Set.new(whitelist.map(&:to_s)) - end - @invokable_methods.include?(method_name.to_s) - end - end -end diff --git a/lib/liquid/errors.rb b/lib/liquid/errors.rb deleted file mode 100644 index 85cb373..0000000 --- a/lib/liquid/errors.rb +++ /dev/null @@ -1,12 +0,0 @@ -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 - class MemoryError < Error; end -end diff --git a/lib/liquid/extensions.rb b/lib/liquid/extensions.rb deleted file mode 100644 index 24fc8e3..0000000 --- a/lib/liquid/extensions.rb +++ /dev/null @@ -1,62 +0,0 @@ -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 - -class TrueClass - def to_liquid # :nodoc: - self - end -end - -class FalseClass - def to_liquid # :nodoc: - self - end -end - -class NilClass - def to_liquid # :nodoc: - self - end -end diff --git a/lib/liquid/file_system.rb b/lib/liquid/file_system.rb deleted file mode 100644 index f95f644..0000000 --- a/lib/liquid/file_system.rb +++ /dev/null @@ -1,73 +0,0 @@ -module Liquid - # A Liquid file system is a 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, context) - 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" - # - # Optionally in the second argument you can specify a custom pattern for template filenames. - # The Kernel::sprintf format specification is used. - # Default pattern is "_%s.liquid". - # - # Example: - # - # file_system = Liquid::LocalFileSystem.new("/some/path", "%s.html") - # - # file_system.full_path("index") # => "/some/path/index.html" - # - class LocalFileSystem - attr_accessor :root - - def initialize(root, pattern = "_%s.liquid".freeze) - @root = root - @pattern = pattern - end - - def read_template_file(template_path, context) - 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[^.\/][a-zA-Z0-9_\/]+\z/ - - full_path = if template_path.include?('/'.freeze) - File.join(root, File.dirname(template_path), @pattern % File.basename(template_path)) - else - File.join(root, @pattern % template_path) - end - - raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path) =~ /\A#{File.expand_path(root)}/ - - full_path - end - end -end diff --git a/lib/liquid/i18n.rb b/lib/liquid/i18n.rb deleted file mode 100644 index 3a59b1f..0000000 --- a/lib/liquid/i18n.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'yaml' - -module Liquid - class I18n - DEFAULT_LOCALE = File.join(File.expand_path(File.dirname(__FILE__)), "locales", "en.yml") - - class TranslationError < StandardError - end - - attr_reader :path - - def initialize(path = DEFAULT_LOCALE) - @path = path - end - - def translate(name, vars = {}) - interpolate(deep_fetch_translation(name), vars) - end - alias_method :t, :translate - - def locale - @locale ||= YAML.load_file(@path) - end - - private - def interpolate(name, vars) - name.gsub(/%\{(\w+)\}/) { - # raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym] - "#{vars[$1.to_sym]}" - } - end - - def deep_fetch_translation(name) - name.split('.'.freeze).reduce(locale) do |level, cur| - level[cur] or raise TranslationError, "Translation for #{name} does not exist in locale #{path}" - end - end - end -end diff --git a/lib/liquid/interrupts.rb b/lib/liquid/interrupts.rb deleted file mode 100644 index 6ff01ac..0000000 --- a/lib/liquid/interrupts.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Liquid - - # An interrupt is any command that breaks processing of a block (ex: a for loop). - class Interrupt - attr_reader :message - - def initialize(message=nil) - @message = message || "interrupt".freeze - end - end - - # Interrupt that is thrown whenever a {% break %} is called. - class BreakInterrupt < Interrupt; end - - # Interrupt that is thrown whenever a {% continue %} is called. - class ContinueInterrupt < Interrupt; end -end diff --git a/lib/liquid/lexer.rb b/lib/liquid/lexer.rb deleted file mode 100644 index 0ecd8e0..0000000 --- a/lib/liquid/lexer.rb +++ /dev/null @@ -1,51 +0,0 @@ -require "strscan" -module Liquid - class Lexer - SPECIALS = { - '|'.freeze => :pipe, - '.'.freeze => :dot, - ':'.freeze => :colon, - ','.freeze => :comma, - '['.freeze => :open_square, - ']'.freeze => :close_square, - '('.freeze => :open_round, - ')'.freeze => :close_round - } - IDENTIFIER = /[\w\-?!]+/ - SINGLE_STRING_LITERAL = /'[^\']*'/ - DOUBLE_STRING_LITERAL = /"[^\"]*"/ - NUMBER_LITERAL = /-?\d+(\.\d+)?/ - DOTDOT = /\.\./ - COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains/ - - def initialize(input) - @ss = StringScanner.new(input.rstrip) - end - - def tokenize - @output = [] - - while !@ss.eos? - @ss.skip(/\s*/) - tok = case - when t = @ss.scan(COMPARISON_OPERATOR) then [:comparison, t] - when t = @ss.scan(SINGLE_STRING_LITERAL) then [:string, t] - when t = @ss.scan(DOUBLE_STRING_LITERAL) then [:string, t] - when t = @ss.scan(NUMBER_LITERAL) then [:number, t] - when t = @ss.scan(IDENTIFIER) then [:id, t] - when t = @ss.scan(DOTDOT) then [:dotdot, t] - else - c = @ss.getch - if s = SPECIALS[c] - [s,c] - else - raise SyntaxError, "Unexpected character #{c}" - end - end - @output << tok - end - - @output << [:end_of_string] - end - end -end diff --git a/lib/liquid/locales/en.yml b/lib/liquid/locales/en.yml deleted file mode 100644 index 09f0ad8..0000000 --- a/lib/liquid/locales/en.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- - errors: - syntax: - assign: "Syntax Error in 'assign' - Valid syntax: assign [var] = [source]" - capture: "Syntax Error in 'capture' - Valid syntax: capture [var]" - case: "Syntax Error in 'case' - Valid syntax: case [condition]" - case_invalid_when: "Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %}" - case_invalid_else: "Syntax Error in tag 'case' - Valid else condition: {% else %} (no parameters) " - cycle: "Syntax Error in 'cycle' - Valid syntax: cycle [name :] var [, var2, var3 ...]" - for: "Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]" - for_invalid_in: "For loops require an 'in' clause" - for_invalid_attribute: "Invalid attribute in for loop. Valid attributes are limit and offset" - if: "Syntax Error in tag 'if' - Valid syntax: if [expression]" - include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]" - unknown_tag: "Unknown tag '%{tag}'" - invalid_delimiter: "'end' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}" - unexpected_else: "%{block_name} tag does not expect else tag" - tag_termination: "Tag '%{token}' was not properly terminated with regexp: %{tag_end}" - variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}" - tag_never_closed: "'%{block_name}' tag was never closed" - meta_syntax_error: "Liquid syntax error: #{e.message}" - table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3" diff --git a/lib/liquid/module_ex.rb b/lib/liquid/module_ex.rb deleted file mode 100644 index 40fa3a4..0000000 --- a/lib/liquid/module_ex.rb +++ /dev/null @@ -1,62 +0,0 @@ -# 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 used 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/lib/liquid/parser.rb b/lib/liquid/parser.rb deleted file mode 100644 index d2006f3..0000000 --- a/lib/liquid/parser.rb +++ /dev/null @@ -1,90 +0,0 @@ -module Liquid - class Parser - def initialize(input) - l = Lexer.new(input) - @tokens = l.tokenize - @p = 0 # pointer to current location - end - - def jump(point) - @p = point - end - - def consume(type = nil) - token = @tokens[@p] - if type && token[0] != type - raise SyntaxError, "Expected #{type} but found #{@tokens[@p].first}" - end - @p += 1 - token[1] - end - - # Only consumes the token if it matches the type - # Returns the token's contents if it was consumed - # or false otherwise. - def consume?(type) - token = @tokens[@p] - return false unless token && token[0] == type - @p += 1 - token[1] - end - - # Like consume? Except for an :id token of a certain name - def id?(str) - token = @tokens[@p] - return false unless token && token[0] == :id - return false unless token[1] == str - @p += 1 - token[1] - end - - def look(type, ahead = 0) - tok = @tokens[@p + ahead] - return false unless tok - tok[0] == type - end - - def expression - token = @tokens[@p] - if token[0] == :id - variable_signature - elsif [:string, :number].include? token[0] - consume - elsif token.first == :open_round - consume - first = expression - consume(:dotdot) - last = expression - consume(:close_round) - "(#{first}..#{last})" - else - raise SyntaxError, "#{token} is not a valid expression" - end - end - - def argument - str = "" - # might be a keyword argument (identifier: expression) - if look(:id) && look(:colon, 1) - str << consume << consume << ' '.freeze - end - - str << expression - str - end - - def variable_signature - str = consume(:id) - if look(:open_square) - str << consume - str << expression - str << consume(:close_square) - end - if look(:dot) - str << consume - str << variable_signature - end - str - end - end -end diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb deleted file mode 100644 index a244cc5..0000000 --- a/lib/liquid/standardfilters.rb +++ /dev/null @@ -1,316 +0,0 @@ -require 'cgi' -require 'bigdecimal' - -module Liquid - - module StandardFilters - HTML_ESCAPE = { - '&'.freeze => '&'.freeze, - '>'.freeze => '>'.freeze, - '<'.freeze => '<'.freeze, - '"'.freeze => '"'.freeze, - "'".freeze => '''.freeze - } - HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/ - - # Return the size of an array or of an string - def size(input) - input.respond_to?(:size) ? input.size : 0 - end - - # convert an input string to DOWNCASE - def downcase(input) - input.to_s.downcase - end - - # convert an 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 - - def escape_once(input) - input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE) - end - - alias_method :h, :escape - - # Truncate a string down to x characters - def truncate(input, length = 50, truncate_string = "...".freeze) - 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 = "...".freeze) - 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(" ".freeze) + truncate_string : input - end - - # Split input string into an array of substrings separated by given pattern. - # - # Example: - #
{{ post | split '//' | first }}
- # - def split(input, pattern) - input.split(pattern) - end - - def strip(input) - input.to_s.strip - end - - def lstrip(input) - input.to_s.lstrip - end - - def rstrip(input) - input.to_s.rstrip - end - - def strip_html(input) - empty = ''.freeze - input.to_s.gsub(//m, empty).gsub(//m, empty).gsub(//m, empty).gsub(/<.*?>/m, empty) - end - - # Remove all newlines from the string - def strip_newlines(input) - input.to_s.gsub(/\r?\n/, ''.freeze) - end - - # Join elements of the array with certain character between them - def join(input, glue = ' '.freeze) - [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 = flatten_if_necessary(input) - if property.nil? - ary.sort - elsif ary.first.respond_to?('[]'.freeze) 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 - - # Reverse the elements of an array - def reverse(input) - ary = [input].flatten - ary.reverse - end - - # map/collect on a given property - def map(input, property) - flatten_if_necessary(input).map do |e| - e = e.call if e.is_a?(Proc) - - if property == "to_liquid".freeze - e - elsif e.respond_to?(:[]) - e[property] - end - end - end - - # Replace occurrences of a string with another - def replace(input, string, replacement = ''.freeze) - input.to_s.gsub(string, replacement.to_s) - end - - # Replace the first occurrences of a string with another - def replace_first(input, string, replacement = ''.freeze) - input.to_s.sub(string, replacement.to_s) - end - - # remove a substring - def remove(input, string) - input.to_s.gsub(string, ''.freeze) - end - - # remove the first occurrences of a substring - def remove_first(input, string) - input.to_s.sub(string, ''.freeze) - 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".freeze) - end - - # Reformat a date using Ruby's core Time#strftime( string ) -> string - # - # %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 - Number of seconds since 1970-01-01 00:00:00 UTC. - # %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 - # - # See also: http://www.ruby-doc.org/core/Time.html#method-i-strftime - def date(input, format) - return input if format.to_s.empty? - - return input unless date = to_date(input) - - date.strftime(format.to_s) - 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) - apply_operation(input, operand, :+) - end - - # subtraction - def minus(input, operand) - apply_operation(input, operand, :-) - end - - # multiplication - def times(input, operand) - apply_operation(input, operand, :*) - end - - # division - def divided_by(input, operand) - apply_operation(input, operand, :/) - end - - def modulo(input, operand) - apply_operation(input, operand, :%) - end - - def round(input, n = 0) - result = to_number(input).round(to_number(n)) - result = result.to_f if result.is_a?(BigDecimal) - result = result.to_i if n == 0 - result - end - - def ceil(input) - to_number(input).ceil.to_i - end - - def floor(input) - to_number(input).floor.to_i - end - - def default(input, default_value = "".freeze) - is_blank = input.respond_to?(:empty?) ? input.empty? : !input - is_blank ? default_value : input - end - - private - - def flatten_if_necessary(input) - ary = if input.is_a?(Array) - input.flatten - elsif input.is_a?(Enumerable) && !input.is_a?(Hash) - input - else - [input].flatten - end - ary.map{ |e| e.respond_to?(:to_liquid) ? e.to_liquid : e } - end - - def to_number(obj) - case obj - when Float - BigDecimal.new(obj.to_s) - when Numeric - obj - when String - (obj.strip =~ /\A\d+\.\d+\z/) ? BigDecimal.new(obj) : obj.to_i - else - 0 - end - end - - def to_date(obj) - return obj if obj.respond_to?(:strftime) - - case obj - when 'now'.freeze, 'today'.freeze - Time.now - when /\A\d+\z/, Integer - Time.at(obj.to_i) - when String - Time.parse(obj) - else - nil - end - rescue ArgumentError - nil - end - - def apply_operation(input, operand, operation) - result = to_number(input).send(operation, to_number(operand)) - result.is_a?(BigDecimal) ? result.to_f : result - end - end - - Template.register_filter(StandardFilters) -end diff --git a/lib/liquid/strainer.rb b/lib/liquid/strainer.rb deleted file mode 100644 index e11549e..0000000 --- a/lib/liquid/strainer.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'set' - -module Liquid - - # Strainer is the parent class for the filters system. - # New filters are mixed into the strainer class which is then instantiated for each liquid template render run. - # - # The Strainer only allows method calls defined in filters given to it via Strainer.global_filter, - # Context#add_filters or Template.register_filter - class Strainer #:nodoc: - @@filters = [] - @@known_filters = Set.new - @@known_methods = Set.new - @@strainer_class_cache = Hash.new do |hash, filters| - hash[filters] = Class.new(Strainer) do - filters.each { |f| include f } - end - end - - def initialize(context) - @context = context - end - - def self.global_filter(filter) - raise ArgumentError, "Passed filter is not a module" unless filter.is_a?(Module) - add_known_filter(filter) - @@filters << filter unless @@filters.include?(filter) - end - - def self.add_known_filter(filter) - unless @@known_filters.include?(filter) - @@method_blacklist ||= Set.new(Strainer.instance_methods.map(&:to_s)) - new_methods = filter.instance_methods.map(&:to_s) - new_methods.reject!{ |m| @@method_blacklist.include?(m) } - @@known_methods.merge(new_methods) - @@known_filters.add(filter) - end - end - - def self.strainer_class_cache - @@strainer_class_cache - end - - def self.create(context, filters = []) - filters = @@filters + filters - strainer_class_cache[filters].new(context) - end - - def invoke(method, *args) - if invokable?(method) - send(method, *args) - else - args.first - end - rescue ::ArgumentError => e - raise Liquid::ArgumentError.new(e.message) - end - - def invokable?(method) - @@known_methods.include?(method.to_s) && respond_to?(method) - end - end -end diff --git a/lib/liquid/tag.rb b/lib/liquid/tag.rb deleted file mode 100644 index e894e35..0000000 --- a/lib/liquid/tag.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Liquid - class Tag - attr_accessor :options - attr_reader :nodelist, :warnings - - class << self - def parse(tag_name, markup, tokens, options) - tag = new(tag_name, markup, options) - tag.parse(tokens) - tag - end - - private :new - end - - def initialize(tag_name, markup, options) - @tag_name = tag_name - @markup = markup - @options = options - end - - def parse(tokens) - end - - def name - self.class.name.downcase - end - - def render(context) - ''.freeze - end - - def blank? - false - end - - def parse_with_selected_parser(markup) - case @options[:error_mode] || Template.error_mode - when :strict then strict_parse_with_error_context(markup) - when :lax then lax_parse(markup) - when :warn - begin - return strict_parse_with_error_context(markup) - rescue SyntaxError => e - @warnings ||= [] - @warnings << e - return lax_parse(markup) - end - end - end - - private - - def strict_parse_with_error_context(markup) - strict_parse(markup) - rescue SyntaxError => e - e.message << " in \"#{markup.strip}\"" - raise e - end - end -end diff --git a/lib/liquid/tags/assign.rb b/lib/liquid/tags/assign.rb deleted file mode 100644 index fa852a0..0000000 --- a/lib/liquid/tags/assign.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Liquid - - # Assign sets a variable in your template. - # - # {% assign foo = 'monkey' %} - # - # You can then use the variable later in the page. - # - # {{ foo }} - # - class Assign < Tag - Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om - - def initialize(tag_name, markup, options) - super - if markup =~ Syntax - @to = $1 - @from = Variable.new($2) - else - raise SyntaxError.new options[:locale].t("errors.syntax.assign".freeze) - end - end - - def render(context) - val = @from.render(context) - context.scopes.last[@to] = val - context.increment_used_resources(:assign_score_current, val) - ''.freeze - end - - def blank? - true - end - end - - Template.register_tag('assign'.freeze, Assign) -end diff --git a/lib/liquid/tags/break.rb b/lib/liquid/tags/break.rb deleted file mode 100644 index 411a3b1..0000000 --- a/lib/liquid/tags/break.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Liquid - - # Break tag to be used to break out of a for loop. - # - # == Basic Usage: - # {% for item in collection %} - # {% if item.condition %} - # {% break %} - # {% endif %} - # {% endfor %} - # - class Break < Tag - - def interrupt - BreakInterrupt.new - end - - end - - Template.register_tag('break'.freeze, Break) -end diff --git a/lib/liquid/tags/capture.rb b/lib/liquid/tags/capture.rb deleted file mode 100644 index 3ec0d67..0000000 --- a/lib/liquid/tags/capture.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Liquid - # Capture stores the result of a block into a variable without rendering it inplace. - # - # {% capture heading %} - # Monkeys! - # {% endcapture %} - # ... - #

{{ heading }}

- # - # 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, options) - super - if markup =~ Syntax - @to = $1 - else - raise SyntaxError.new(options[:locale].t("errors.syntax.capture")) - end - end - - def render(context) - output = super - context.scopes.last[@to] = output - context.increment_used_resources(:assign_score_current, output) - ''.freeze - end - - def blank? - true - end - end - - Template.register_tag('capture'.freeze, Capture) -end diff --git a/lib/liquid/tags/case.rb b/lib/liquid/tags/case.rb deleted file mode 100644 index 1cc48d8..0000000 --- a/lib/liquid/tags/case.rb +++ /dev/null @@ -1,79 +0,0 @@ -module Liquid - class Case < Block - Syntax = /(#{QuotedFragment})/o - WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om - - def initialize(tag_name, markup, options) - super - @blocks = [] - - if markup =~ Syntax - @left = $1 - else - raise SyntaxError.new(options[:locale].t("errors.syntax.case".freeze)) - end - end - - def nodelist - @blocks.map(&:attachment).flatten - end - - def unknown_tag(tag, markup, tokens) - @nodelist = [] - case tag - when 'when'.freeze - record_when_condition(markup) - when 'else'.freeze - record_else_condition(markup) - else - super - end - end - - def render(context) - context.stack do - execute_else_block = true - - output = '' - @blocks.each do |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 - end - output - 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(options[:locale].t("errors.syntax.case_invalid_when".freeze)) - end - - markup = $2 - - block = Condition.new(@left, '=='.freeze, $1) - block.attach(@nodelist) - @blocks.push(block) - end - end - - def record_else_condition(markup) - if not markup.strip.empty? - raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_else".freeze)) - end - - block = ElseCondition.new - block.attach(@nodelist) - @blocks << block - end - end - - Template.register_tag('case'.freeze, Case) -end diff --git a/lib/liquid/tags/comment.rb b/lib/liquid/tags/comment.rb deleted file mode 100644 index 3e6bb69..0000000 --- a/lib/liquid/tags/comment.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Liquid - class Comment < Block - def render(context) - ''.freeze - end - - def unknown_tag(tag, markup, tokens) - end - - def blank? - true - end - end - - Template.register_tag('comment'.freeze, Comment) -end diff --git a/lib/liquid/tags/continue.rb b/lib/liquid/tags/continue.rb deleted file mode 100644 index 9c81ec2..0000000 --- a/lib/liquid/tags/continue.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Liquid - # Continue tag to be used to break out of a for loop. - # - # == Basic Usage: - # {% for item in collection %} - # {% if item.condition %} - # {% continue %} - # {% endif %} - # {% endfor %} - # - class Continue < Tag - def interrupt - ContinueInterrupt.new - end - end - - Template.register_tag('continue'.freeze, Continue) -end diff --git a/lib/liquid/tags/cycle.rb b/lib/liquid/tags/cycle.rb deleted file mode 100644 index 744565e..0000000 --- a/lib/liquid/tags/cycle.rb +++ /dev/null @@ -1,57 +0,0 @@ -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 = /\A#{QuotedFragment}+/o - NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om - - def initialize(tag_name, markup, options) - super - 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(options[:locale].t("errors.syntax.cycle".freeze)) - end - 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*(#{QuotedFragment})\s*/o - $1 ? $1 : nil - end.compact - end - end - - Template.register_tag('cycle', Cycle) -end diff --git a/lib/liquid/tags/decrement.rb b/lib/liquid/tags/decrement.rb deleted file mode 100644 index d604750..0000000 --- a/lib/liquid/tags/decrement.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Liquid - - # decrement is used in a place where one needs to insert a counter - # into a template, and needs the counter to survive across - # multiple instantiations of the template. - # NOTE: decrement is a pre-decrement, --i, - # while increment is post: i++. - # - # (To achieve the survival, the application must keep the context) - # - # if the variable does not exist, it is created with value 0. - - # Hello: {% decrement variable %} - # - # gives you: - # - # Hello: -1 - # Hello: -2 - # Hello: -3 - # - class Decrement < Tag - def initialize(tag_name, markup, options) - super - @variable = markup.strip - end - - def render(context) - value = context.environments.first[@variable] ||= 0 - value = value - 1 - context.environments.first[@variable] = value - value.to_s - end - - private - end - - Template.register_tag('decrement'.freeze, Decrement) -end diff --git a/lib/liquid/tags/for.rb b/lib/liquid/tags/for.rb deleted file mode 100644 index 6d29b64..0000000 --- a/lib/liquid/tags/for.rb +++ /dev/null @@ -1,175 +0,0 @@ -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 }} - #
- # {% else %} - # There is nothing in the collection. - # {% 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 = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o - - def initialize(tag_name, markup, options) - super - parse_with_selected_parser(markup) - @nodelist = @for_block = [] - end - - def nodelist - if @else_block - @for_block + @else_block - else - @for_block - end - end - - def unknown_tag(tag, markup, tokens) - return super unless tag == 'else'.freeze - @nodelist = @else_block = [] - end - - def render(context) - context.registers[:for] ||= Hash.new(0) - - collection = context[@collection_name] - collection = collection.to_a if collection.is_a?(Range) - - # Maintains Ruby 1.8.7 String#each behaviour on 1.9 - return render_else(context) unless iterable?(collection) - - from = if @attributes['offset'.freeze] == 'continue'.freeze - context.registers[:for][@name].to_i - else - context[@attributes['offset'.freeze]].to_i - end - - limit = context[@attributes['limit'.freeze]] - to = limit ? limit.to_i + from : nil - - segment = Utils.slice_collection(collection, from, to) - - return render_else(context) 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'.freeze] = { - 'name'.freeze => @name, - 'length'.freeze => length, - 'index'.freeze => index + 1, - 'index0'.freeze => index, - 'rindex'.freeze => length - index, - 'rindex0'.freeze => length - index - 1, - 'first'.freeze => (index == 0), - 'last'.freeze => (index == length - 1) - } - - result << render_all(@for_block, context) - - # Handle any interrupts if they exist. - if context.has_interrupt? - interrupt = context.pop_interrupt - break if interrupt.is_a? BreakInterrupt - next if interrupt.is_a? ContinueInterrupt - end - end - end - result - end - - protected - - def lax_parse(markup) - 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(options[:locale].t("errors.syntax.for".freeze)) - end - end - - def strict_parse(markup) - p = Parser.new(markup) - @variable_name = p.consume(:id) - raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_in".freeze)) unless p.id?('in'.freeze) - @collection_name = p.expression - @name = "#{@variable_name}-#{@collection_name}" - @reversed = p.id?('reversed'.freeze) - - @attributes = {} - while p.look(:id) && p.look(:colon, 1) - unless attribute = p.id?('limit'.freeze) || p.id?('offset'.freeze) - raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_attribute".freeze)) - end - p.consume - val = p.expression - @attributes[attribute] = val - end - p.consume(:end_of_string) - end - - private - - def render_else(context) - return @else_block ? [render_all(@else_block, context)] : ''.freeze - end - - def iterable?(collection) - collection.respond_to?(:each) || Utils.non_blank_string?(collection) - end - end - - Template.register_tag('for'.freeze, For) -end diff --git a/lib/liquid/tags/if.rb b/lib/liquid/tags/if.rb deleted file mode 100644 index 60bd634..0000000 --- a/lib/liquid/tags/if.rb +++ /dev/null @@ -1,106 +0,0 @@ -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 - Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o - ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o - BOOLEAN_OPERATORS = %w(and or) - - def initialize(tag_name, markup, options) - super - @blocks = [] - push_block('if'.freeze, markup) - end - - def nodelist - @blocks.map(&:attachment).flatten - end - - def unknown_tag(tag, markup, tokens) - if ['elsif'.freeze, 'else'.freeze].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 - ''.freeze - end - end - - private - - def push_block(tag, markup) - block = if tag == 'else'.freeze - ElseCondition.new - else - parse_with_selected_parser(markup) - end - - @blocks.push(block) - @nodelist = block.attach(Array.new) - end - - def lax_parse(markup) - expressions = markup.scan(ExpressionsAndOperators).reverse - raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.shift =~ Syntax - - condition = Condition.new($1, $2, $3) - - while not expressions.empty? - operator = (expressions.shift).to_s.strip - - raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.shift.to_s =~ Syntax - - new_condition = Condition.new($1, $2, $3) - raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless BOOLEAN_OPERATORS.include?(operator) - new_condition.send(operator, condition) - condition = new_condition - end - - condition - end - - def strict_parse(markup) - p = Parser.new(markup) - - condition = parse_comparison(p) - - while op = (p.id?('and'.freeze) || p.id?('or'.freeze)) - new_cond = parse_comparison(p) - new_cond.send(op, condition) - condition = new_cond - end - p.consume(:end_of_string) - - condition - end - - def parse_comparison(p) - a = p.expression - if op = p.consume?(:comparison) - b = p.expression - Condition.new(a, op, b) - else - Condition.new(a) - end - end - end - - Template.register_tag('if'.freeze, If) -end diff --git a/lib/liquid/tags/ifchanged.rb b/lib/liquid/tags/ifchanged.rb deleted file mode 100644 index 03a9fb0..0000000 --- a/lib/liquid/tags/ifchanged.rb +++ /dev/null @@ -1,20 +0,0 @@ -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 - ''.freeze - end - end - end - end - - Template.register_tag('ifchanged'.freeze, Ifchanged) -end diff --git a/lib/liquid/tags/include.rb b/lib/liquid/tags/include.rb deleted file mode 100644 index d4b2ee0..0000000 --- a/lib/liquid/tags/include.rb +++ /dev/null @@ -1,94 +0,0 @@ -module Liquid - - # Include allows templates to relate with other templates - # - # Simply include another template: - # - # {% include 'product' %} - # - # Include a template with a local variable: - # - # {% include 'product' with products[0] %} - # - # Include a template for a collection: - # - # {% include 'product' for products %} - # - class Include < Tag - Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o - - def initialize(tag_name, markup, options) - super - - if markup =~ Syntax - - @template_name = $1 - @variable_name = $3 - @attributes = {} - - markup.scan(TagAttributes) do |key, value| - @attributes[key] = value - end - - else - raise SyntaxError.new(options[:locale].t("errors.syntax.include".freeze)) - end - end - - def parse(tokens) - end - - def render(context) - partial = load_cached_partial(context) - variable = context[@variable_name || @template_name[1..-2]] - - context.stack do - @attributes.each do |key, value| - context[key] = context[value] - end - - context_variable_name = @template_name[1..-2].split('/'.freeze).last - if variable.is_a?(Array) - variable.collect do |var| - context[context_variable_name] = var - partial.render(context) - end - else - context[context_variable_name] = variable - partial.render(context) - end - end - end - - private - def load_cached_partial(context) - cached_partials = context.registers[:cached_partials] || {} - template_name = context[@template_name] - - if cached = cached_partials[template_name] - return cached - end - source = read_template_from_file_system(context) - partial = Liquid::Template.parse(source) - cached_partials[template_name] = partial - context.registers[:cached_partials] = cached_partials - partial - end - - def read_template_from_file_system(context) - file_system = context.registers[:file_system] || Liquid::Template.file_system - - # make read_template_file call backwards-compatible. - case file_system.method(:read_template_file).arity - when 1 - file_system.read_template_file(context[@template_name]) - when 2 - file_system.read_template_file(context[@template_name], context) - else - raise ArgumentError, "file_system.read_template_file expects two parameters: (template_name, context)" - end - end - end - - Template.register_tag('include'.freeze, Include) -end diff --git a/lib/liquid/tags/increment.rb b/lib/liquid/tags/increment.rb deleted file mode 100644 index baa0cbb..0000000 --- a/lib/liquid/tags/increment.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Liquid - # increment is used in a place where one needs to insert a counter - # into a template, and needs the counter to survive across - # multiple instantiations of the template. - # (To achieve the survival, the application must keep the context) - # - # if the variable does not exist, it is created with value 0. - # - # Hello: {% increment variable %} - # - # gives you: - # - # Hello: 0 - # Hello: 1 - # Hello: 2 - # - class Increment < Tag - def initialize(tag_name, markup, options) - super - @variable = markup.strip - end - - def render(context) - value = context.environments.first[@variable] ||= 0 - context.environments.first[@variable] = value + 1 - value.to_s - end - end - - Template.register_tag('increment'.freeze, Increment) -end diff --git a/lib/liquid/tags/raw.rb b/lib/liquid/tags/raw.rb deleted file mode 100644 index 68e4c21..0000000 --- a/lib/liquid/tags/raw.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Liquid - class Raw < Block - FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om - - def parse(tokens) - @nodelist ||= [] - @nodelist.clear - while token = tokens.shift - if token =~ FullTokenPossiblyInvalid - @nodelist << $1 if $1 != "".freeze - if @block_delimiter == $2 - end_tag - return - end - end - @nodelist << token if not token.empty? - end - end - end - - Template.register_tag('raw'.freeze, Raw) -end diff --git a/lib/liquid/tags/table_row.rb b/lib/liquid/tags/table_row.rb deleted file mode 100644 index 25457a3..0000000 --- a/lib/liquid/tags/table_row.rb +++ /dev/null @@ -1,73 +0,0 @@ -module Liquid - class TableRow < Block - Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o - - def initialize(tag_name, markup, options) - super - if markup =~ Syntax - @variable_name = $1 - @collection_name = $2 - @attributes = {} - markup.scan(TagAttributes) do |key, value| - @attributes[key] = value - end - else - raise SyntaxError.new(options[:locale].t("errors.syntax.table_row".freeze)) - end - end - - def render(context) - collection = context[@collection_name] or return ''.freeze - - from = @attributes['offset'.freeze] ? context[@attributes['offset'.freeze]].to_i : 0 - to = @attributes['limit'.freeze] ? from + context[@attributes['limit'.freeze]].to_i : nil - - collection = Utils.slice_collection(collection, from, to) - - length = collection.length - - cols = context[@attributes['cols'.freeze]].to_i - - row = 1 - col = 0 - - result = "\n" - context.stack do - - collection.each_with_index do |item, index| - context[@variable_name] = item - context['tablerowloop'.freeze] = { - 'length'.freeze => length, - 'index'.freeze => index + 1, - 'index0'.freeze => index, - 'col'.freeze => col + 1, - 'col0'.freeze => col, - 'index0'.freeze => index, - 'rindex'.freeze => length - index, - 'rindex0'.freeze => length - index - 1, - 'first'.freeze => (index == 0), - 'last'.freeze => (index == length - 1), - 'col_first'.freeze => (col == 0), - 'col_last'.freeze => (col == cols - 1) - } - - - col += 1 - - result << "" << render_all(@nodelist, context) << '' - - if col == cols and (index != length - 1) - col = 0 - row += 1 - result << "\n" - end - - end - end - result << "\n" - result - end - end - - Template.register_tag('tablerow'.freeze, TableRow) -end diff --git a/lib/liquid/tags/unless.rb b/lib/liquid/tags/unless.rb deleted file mode 100644 index eb8a731..0000000 --- a/lib/liquid/tags/unless.rb +++ /dev/null @@ -1,31 +0,0 @@ -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 ) - first_block = @blocks.first - unless first_block.evaluate(context) - return render_all(first_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 - - ''.freeze - end - end - end - - Template.register_tag('unless'.freeze, Unless) -end diff --git a/lib/liquid/template.rb b/lib/liquid/template.rb deleted file mode 100644 index 34172db..0000000 --- a/lib/liquid/template.rb +++ /dev/null @@ -1,208 +0,0 @@ -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 - DEFAULT_OPTIONS = { - :locale => I18n.new - } - - attr_accessor :root, :resource_limits - @@file_system = BlankFileSystem.new - - class TagRegistry - def initialize - @tags = {} - @cache = {} - end - - def [](tag_name) - return nil unless @tags.has_key?(tag_name) - return @cache[tag_name] if Liquid.cache_classes - - lookup_class(@tags[tag_name]).tap { |o| @cache[tag_name] = o } - end - - def []=(tag_name, klass) - @tags[tag_name] = klass.name - @cache[tag_name] = klass - end - - def delete(tag_name) - @tags.delete(tag_name) - @cache.delete(tag_name) - end - - private - - def lookup_class(name) - name.split("::").reject(&:empty?).reduce(Object) { |scope, const| scope.const_get(const) } - end - end - - class << self - # Sets how strict the parser should be. - # :lax acts like liquid 2.5 and silently ignores malformed tags in most cases. - # :warn is the default and will give deprecation warnings when invalid syntax is used. - # :strict will enforce correct syntax. - attr_writer :error_mode - - 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 ||= TagRegistry.new - end - - def error_mode - @error_mode || :lax - 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, options = {}) - template = Template.new - template.parse(source, options) - end - end - - # creates a new Template from an array of tokens. Use Template.parse instead - def initialize - @resource_limits = {} - end - - # Parse source code. - # Returns self for easy chaining - def parse(source, options = {}) - @root = Document.parse(tokenize(source), DEFAULT_OPTIONS.merge(options)) - @warnings = nil - self - end - - def warnings - return [] unless @root - @warnings ||= @root.warnings - end - - def registers - @registers ||= {} - end - - def assigns - @assigns ||= {} - end - - def instance_assigns - @instance_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 ''.freeze if @root.nil? - - context = case args.first - when Liquid::Context - c = args.shift - c.rethrow_errors = true if @rethrow_errors - c - when Liquid::Drop - drop = args.shift - drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits) - when Hash - Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits) - when nil - Context.new(assigns, instance_assigns, registers, @rethrow_errors, @resource_limits) - else - raise ArgumentError, "Expected 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 an array back here. join will make a string out of it. - result = @root.render(context) - result.respond_to?(:join) ? result.join : result - rescue Liquid::MemoryError => e - context.handle_error(e) - 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/lib/liquid/utils.rb b/lib/liquid/utils.rb deleted file mode 100644 index cfa675a..0000000 --- a/lib/liquid/utils.rb +++ /dev/null @@ -1,39 +0,0 @@ -module Liquid - module Utils - - def self.slice_collection(collection, from, to) - if (from != 0 || to != nil) && collection.respond_to?(:load_slice) - collection.load_slice(from, to) - else - slice_collection_using_each(collection, from, to) - end - end - - def self.non_blank_string?(collection) - collection.is_a?(String) && collection != ''.freeze - end - - def self.slice_collection_using_each(collection, from, to) - segments = [] - index = 0 - - # Maintains Ruby 1.8.7 String#each behaviour on 1.9 - return [collection] if non_blank_string?(collection) - - collection.each do |item| - - if to && to <= index - break - end - - if from <= index - segments << item - end - - index += 1 - end - - segments - end - end -end diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb deleted file mode 100644 index 6a35122..0000000 --- a/lib/liquid/variable.rb +++ /dev/null @@ -1,108 +0,0 @@ -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 - FilterParser = /(?:#{FilterSeparator}|(?:\s*(?:#{QuotedFragment}|#{ArgumentSeparator})\s*)+)/o - EasyParse = /\A *(\w+(?:\.\w+)*) *\z/ - attr_accessor :filters, :name, :warnings - - def initialize(markup, options = {}) - @markup = markup - @name = nil - @options = options || {} - - case @options[:error_mode] || Template.error_mode - when :strict then strict_parse(markup) - when :lax then lax_parse(markup) - when :warn - begin - strict_parse(markup) - rescue SyntaxError => e - @warnings ||= [] - @warnings << e - lax_parse(markup) - end - end - end - - def lax_parse(markup) - @filters = [] - if markup =~ /\s*(#{QuotedFragment})(.*)/om - @name = Regexp.last_match(1) - if Regexp.last_match(2) =~ /#{FilterSeparator}\s*(.*)/om - filters = Regexp.last_match(1).scan(FilterParser) - filters.each do |f| - if f =~ /\s*(\w+)/ - filtername = Regexp.last_match(1) - filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o).flatten - @filters << [filtername, filterargs] - end - end - end - end - end - - def strict_parse(markup) - # Very simple valid cases - if markup =~ EasyParse - @name = $1 - @filters = [] - return - end - - @filters = [] - p = Parser.new(markup) - # Could be just filters with no input - @name = p.look(:pipe) ? ''.freeze : p.expression - while p.consume?(:pipe) - filtername = p.consume(:id) - filterargs = p.consume?(:colon) ? parse_filterargs(p) : [] - @filters << [filtername, filterargs] - end - p.consume(:end_of_string) - rescue SyntaxError => e - e.message << " in \"{{#{markup}}}\"" - raise e - end - - def parse_filterargs(p) - # first argument - filterargs = [p.argument] - # followed by comma separated others - while p.consume?(:comma) - filterargs << p.argument - end - filterargs - end - - def render(context) - return ''.freeze if @name.nil? - @filters.inject(context[@name]) do |output, filter| - filterargs = [] - keyword_args = {} - filter[1].to_a.each do |a| - if matches = a.match(/\A#{TagAttributes}\z/o) - keyword_args[matches[1]] = context[matches[2]] - else - filterargs << context[a] - end - end - filterargs << keyword_args unless keyword_args.empty? - 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/lib/liquid/version.rb b/lib/liquid/version.rb deleted file mode 100644 index d27c6d3..0000000 --- a/lib/liquid/version.rb +++ /dev/null @@ -1,4 +0,0 @@ -# encoding: utf-8 -module Liquid - VERSION = "3.0.0" -end diff --git a/liquid.gemspec b/liquid.gemspec deleted file mode 100644 index 0cd2f86..0000000 --- a/liquid.gemspec +++ /dev/null @@ -1,28 +0,0 @@ -# encoding: utf-8 -lib = File.expand_path('../lib/', __FILE__) -$:.unshift lib unless $:.include?(lib) - -require "liquid/version" - -Gem::Specification.new do |s| - s.name = "liquid" - s.version = Liquid::VERSION - s.platform = Gem::Platform::RUBY - s.summary = "A secure, non-evaling end user template engine with aesthetic markup." - s.authors = ["Tobias Luetke"] - s.email = ["tobi@leetsoft.com"] - s.homepage = "http://www.liquidmarkup.org" - s.license = "MIT" - #s.description = "A secure, non-evaling end user template engine with aesthetic markup." - - s.required_rubygems_version = ">= 1.3.7" - - s.test_files = Dir.glob("{test}/**/*") - s.files = Dir.glob("{lib}/**/*") + %w(MIT-LICENSE README.md) - - s.extra_rdoc_files = ["History.md", "README.md"] - - s.require_path = "lib" - - s.add_development_dependency 'rake' -end diff --git a/performance/benchmark.rb b/performance/benchmark.rb deleted file mode 100644 index cc990eb..0000000 --- a/performance/benchmark.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'benchmark' -require File.dirname(__FILE__) + '/theme_runner' - -Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first -profiler = ThemeRunner.new - -Benchmark.bmbm do |x| - x.report("parse:") { 100.times { profiler.compile } } - x.report("parse & run:") { 100.times { profiler.run } } -end - diff --git a/performance/profile.rb b/performance/profile.rb deleted file mode 100644 index 57b6187..0000000 --- a/performance/profile.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'stackprof' rescue fail("install stackprof extension/gem") -require File.dirname(__FILE__) + '/theme_runner' - -Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first -profiler = ThemeRunner.new -profiler.run - -[:cpu, :object].each do |profile_type| - puts "Profiling in #{profile_type.to_s} mode..." - results = StackProf.run(mode: profile_type) do - 100.times do - profiler.run - end - end - StackProf::Report.new(results).print_text(false, 20) - File.write(ENV['FILENAME'] + "." + profile_type.to_s, Marshal.dump(results)) if ENV['FILENAME'] -end diff --git a/performance/shopify/comment_form.rb b/performance/shopify/comment_form.rb deleted file mode 100644 index 30226dd..0000000 --- a/performance/shopify/comment_form.rb +++ /dev/null @@ -1,33 +0,0 @@ -class CommentForm < Liquid::Block - Syntax = /(#{Liquid::VariableSignature}+)/ - - def initialize(tag_name, markup, options) - super - - if markup =~ Syntax - @variable_name = $1 - @attributes = {} - else - raise SyntaxError.new("Syntax Error in 'comment_form' - Valid syntax: comment_form [article]") - end - end - - def render(context) - article = context[@variable_name] - - context.stack do - context['form'] = { - 'posted_successfully?' => context.registers[:posted_successfully], - 'errors' => context['comment.errors'], - 'author' => context['comment.author'], - 'email' => context['comment.email'], - 'body' => context['comment.body'] - } - wrap_in_form(article, render_all(@nodelist, context)) - end - end - - def wrap_in_form(article, input) - %Q{
\n#{input}\n
} - end -end diff --git a/performance/shopify/database.rb b/performance/shopify/database.rb deleted file mode 100644 index 147ca05..0000000 --- a/performance/shopify/database.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'yaml' - -module Database - # Load the standard vision toolkit database and re-arrage it to be simply exportable - # to liquid as assigns. All this is based on Shopify - def self.tables - @tables ||= begin - db = YAML.load_file(File.dirname(__FILE__) + '/vision.database.yml') - - # From vision source - db['products'].each do |product| - collections = db['collections'].find_all do |collection| - collection['products'].any? { |p| p['id'].to_i == product['id'].to_i } - end - product['collections'] = collections - end - - # key the tables by handles, as this is how liquid expects it. - db = db.inject({}) do |assigns, (key, values)| - assigns[key] = values.inject({}) { |h, v| h[v['handle']] = v; h; } - assigns - end - - # Some standard direct accessors so that the specialized templates - # render correctly - db['collection'] = db['collections'].values.first - db['product'] = db['products'].values.first - db['blog'] = db['blogs'].values.first - db['article'] = db['blog']['articles'].first - - db['cart'] = { - 'total_price' => db['line_items'].values.inject(0) { |sum, item| sum += item['line_price'] * item['quantity'] }, - 'item_count' => db['line_items'].values.inject(0) { |sum, item| sum += item['quantity'] }, - 'items' => db['line_items'].values - } - - db - end - end -end - -if __FILE__ == $0 - p Database.tables['collections']['frontpage'].keys - #p Database.tables['blog']['articles'] -end diff --git a/performance/shopify/json_filter.rb b/performance/shopify/json_filter.rb deleted file mode 100644 index d3f02d0..0000000 --- a/performance/shopify/json_filter.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'json' - -module JsonFilter - - def json(object) - JSON.dump(object.reject {|k,v| k == "collections" }) - end - -end diff --git a/performance/shopify/liquid.rb b/performance/shopify/liquid.rb deleted file mode 100644 index 29be34a..0000000 --- a/performance/shopify/liquid.rb +++ /dev/null @@ -1,19 +0,0 @@ -$:.unshift File.dirname(__FILE__) + '/../../lib' -require File.dirname(__FILE__) + '/../../lib/liquid' - -require File.dirname(__FILE__) + '/comment_form' -require File.dirname(__FILE__) + '/paginate' -require File.dirname(__FILE__) + '/json_filter' -require File.dirname(__FILE__) + '/money_filter' -require File.dirname(__FILE__) + '/shop_filter' -require File.dirname(__FILE__) + '/tag_filter' -require File.dirname(__FILE__) + '/weight_filter' - -Liquid::Template.register_tag 'paginate', Paginate -Liquid::Template.register_tag 'form', CommentForm - -Liquid::Template.register_filter JsonFilter -Liquid::Template.register_filter MoneyFilter -Liquid::Template.register_filter WeightFilter -Liquid::Template.register_filter ShopFilter -Liquid::Template.register_filter TagFilter diff --git a/performance/shopify/money_filter.rb b/performance/shopify/money_filter.rb deleted file mode 100644 index b200831..0000000 --- a/performance/shopify/money_filter.rb +++ /dev/null @@ -1,18 +0,0 @@ -module MoneyFilter - - def money_with_currency(money) - return '' if money.nil? - sprintf("$ %.2f USD", money/100.0) - end - - def money(money) - return '' if money.nil? - sprintf("$ %.2f", money/100.0) - end - - private - - def currency - ShopDrop.new.currency - end -end diff --git a/performance/shopify/paginate.rb b/performance/shopify/paginate.rb deleted file mode 100644 index 23c92c8..0000000 --- a/performance/shopify/paginate.rb +++ /dev/null @@ -1,93 +0,0 @@ -class Paginate < Liquid::Block - Syntax = /(#{Liquid::QuotedFragment})\s*(by\s*(\d+))?/ - - def initialize(tag_name, markup, options) - super - - @nodelist = [] - - if markup =~ Syntax - @collection_name = $1 - @page_size = if $2 - $3.to_i - else - 20 - end - - @attributes = { 'window_size' => 3 } - markup.scan(Liquid::TagAttributes) do |key, value| - @attributes[key] = value - end - else - raise SyntaxError.new("Syntax Error in tag 'paginate' - Valid syntax: paginate [collection] by number") - end - end - - def render(context) - @context = context - - context.stack do - current_page = context['current_page'].to_i - - pagination = { - 'page_size' => @page_size, - 'current_page' => 5, - 'current_offset' => @page_size * 5 - } - - context['paginate'] = pagination - - collection_size = context[@collection_name].size - - raise ArgumentError.new("Cannot paginate array '#{@collection_name}'. Not found.") if collection_size.nil? - - page_count = (collection_size.to_f / @page_size.to_f).to_f.ceil + 1 - - pagination['items'] = collection_size - pagination['pages'] = page_count -1 - pagination['previous'] = link('« Previous', current_page-1 ) unless 1 >= current_page - pagination['next'] = link('Next »', current_page+1 ) unless page_count <= current_page+1 - pagination['parts'] = [] - - hellip_break = false - - if page_count > 2 - 1.upto(page_count-1) do |page| - - if current_page == page - pagination['parts'] << no_link(page) - elsif page == 1 - pagination['parts'] << link(page, page) - elsif page == page_count -1 - pagination['parts'] << link(page, page) - elsif page <= current_page - @attributes['window_size'] or page >= current_page + @attributes['window_size'] - next if hellip_break - pagination['parts'] << no_link('…') - hellip_break = true - next - else - pagination['parts'] << link(page, page) - end - - hellip_break = false - end - end - - render_all(@nodelist, context) - end - end - - private - - def no_link(title) - { 'title' => title, 'is_link' => false} - end - - def link(title, page) - { 'title' => title, 'url' => current_url + "?page=#{page}", 'is_link' => true} - end - - def current_url - "/collections/frontpage" - end -end diff --git a/performance/shopify/shop_filter.rb b/performance/shopify/shop_filter.rb deleted file mode 100644 index 27b6b57..0000000 --- a/performance/shopify/shop_filter.rb +++ /dev/null @@ -1,110 +0,0 @@ -module ShopFilter - - def asset_url(input) - "/files/1/[shop_id]/[shop_id]/assets/#{input}" - end - - def global_asset_url(input) - "/global/#{input}" - end - - def shopify_asset_url(input) - "/shopify/#{input}" - end - - def script_tag(url) - %() - end - - def stylesheet_tag(url, media="all") - %() - end - - def link_to(link, url, title="") - %|#{link}| - end - - def img_tag(url, alt="") - %|#{alt}| - end - - def link_to_vendor(vendor) - if vendor - link_to vendor, url_for_vendor(vendor), vendor - else - 'Unknown Vendor' - end - end - - def link_to_type(type) - if type - link_to type, url_for_type(type), type - else - 'Unknown Vendor' - end - end - - def url_for_vendor(vendor_title) - "/collections/#{to_handle(vendor_title)}" - end - - def url_for_type(type_title) - "/collections/#{to_handle(type_title)}" - end - - def product_img_url(url, style = 'small') - - unless url =~ /\Aproducts\/([\w\-\_]+)\.(\w{2,4})/ - raise ArgumentError, 'filter "size" can only be called on product images' - end - - case style - when 'original' - return '/files/shops/random_number/' + url - when 'grande', 'large', 'medium', 'compact', 'small', 'thumb', 'icon' - "/files/shops/random_number/products/#{$1}_#{style}.#{$2}" - else - raise ArgumentError, 'valid parameters for filter "size" are: original, grande, large, medium, compact, small, thumb and icon ' - end - end - - def default_pagination(paginate) - - html = [] - html << %(#{link_to(paginate['previous']['title'], paginate['previous']['url'])}) if paginate['previous'] - - for part in paginate['parts'] - - if part['is_link'] - html << %(#{link_to(part['title'], part['url'])}) - elsif part['title'].to_i == paginate['current_page'].to_i - html << %(#{part['title']}) - else - html << %(#{part['title']}) - end - - end - - html << %(#{link_to(paginate['next']['title'], paginate['next']['url'])}) if paginate['next'] - html.join(' ') - end - - # Accepts a number, and two words - one for singular, one for plural - # Returns the singular word if input equals 1, otherwise plural - def pluralize(input, singular, plural) - input == 1 ? singular : plural - end - - private - - def to_handle(str) - result = str.dup - result.downcase! - result.delete!("'\"()[]") - result.gsub!(/\W+/, '-') - result.gsub!(/-+\z/, '') if result[-1] == '-' - result.gsub!(/\A-+/, '') if result[0] == '-' - result - end - -end diff --git a/performance/shopify/tag_filter.rb b/performance/shopify/tag_filter.rb deleted file mode 100644 index ed6f8b4..0000000 --- a/performance/shopify/tag_filter.rb +++ /dev/null @@ -1,25 +0,0 @@ -module TagFilter - - def link_to_tag(label, tag) - "#{label}" - end - - def highlight_active_tag(tag, css_class='active') - if @context['current_tags'].include?(tag) - "#{tag}" - else - tag - end - end - - def link_to_add_tag(label, tag) - tags = (@context['current_tags'] + [tag]).uniq - "#{label}" - end - - def link_to_remove_tag(label, tag) - tags = (@context['current_tags'] - [tag]).uniq - "#{label}" - end - -end diff --git a/performance/shopify/vision.database.yml b/performance/shopify/vision.database.yml deleted file mode 100644 index 9f38c30..0000000 --- a/performance/shopify/vision.database.yml +++ /dev/null @@ -1,945 +0,0 @@ -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Variants -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -product_variants: - - &product-1-var-1 - id: 1 - title: 151cm / Normal - price: 19900 - weight: 1000 - compare_at_price: 49900 - available: true - inventory_quantity: 5 - option1: 151cm - option2: Normal - option3: - - &product-1-var-2 - id: 2 - title: 155cm / Normal - price: 31900 - weight: 1000 - compare_at_price: 50900 - available: true - inventory_quantity: 2 - option1: 155cm - option2: Normal - option3: - - &product-2-var-1 - id: 3 - title: 162cm - price: 29900 - weight: 1000 - compare_at_price: 52900 - available: true - inventory_quantity: 3 - option1: 162cm - option2: - option3: - - &product-3-var-1 - id: 4 - title: 159cm - price: 19900 - weight: 1000 - compare_at_price: - available: true - inventory_quantity: 4 - option1: 159cm - option2: - option3: - - &product-4-var-1 - id: 5 - title: 159cm - price: 19900 - weight: 1000 - compare_at_price: 32900 - available: true - inventory_quantity: 6 - option1: 159cm - option2: - option3: - - &product-1-var-3 - id: 6 - title: 158cm / Wide - price: 23900 - weight: 1000 - compare_at_price: 99900 - available: false - inventory_quantity: 0 - option1: 158cm - option2: Wide - option3: - - &product-3-var-2 - id: 7 - title: 162cm - price: 19900 - weight: 1000 - compare_at_price: - available: false - inventory_quantity: 0 - option1: 162cm - option2: - option3: - - &product-3-var-3 - id: 8 - title: 165cm - price: 22900 - weight: 1000 - compare_at_price: - available: true - inventory_quantity: 4 - option1: 165cm - option2: - option3: - - &product-5-var-1 - id: 9 - title: black / 42 - price: 11900 - weight: 500 - compare_at_price: 22900 - available: true - inventory_quantity: 1 - option1: black - option2: 42 - option3: - - &product-5-var-2 - id: 10 - title: beige / 42 - price: 11900 - weight: 500 - compare_at_price: 22900 - available: true - inventory_quantity: 3 - option1: beige - option2: 42 - option3: - - &product-5-var-3 - id: 11 - title: white / 42 - price: 13900 - weight: 500 - compare_at_price: 24900 - available: true - inventory_quantity: 1 - option1: white - option2: 42 - option3: - - &product-5-var-4 - id: 12 - title: black / 44 - price: 11900 - weight: 500 - compare_at_price: 22900 - available: true - inventory_quantity: 2 - option1: black - option2: 44 - option3: - - &product-5-var-5 - id: 13 - title: beige / 44 - price: 11900 - weight: 500 - compare_at_price: 22900 - available: false - inventory_quantity: 0 - option1: beige - option2: 44 - option3: - - &product-5-var-6 - id: 14 - title: white / 44 - price: 13900 - weight: 500 - compare_at_price: 24900 - available: false - inventory_quantity: 0 - option1: white - option2: 44 - option3: - - &product-6-var-1 - id: 15 - title: red - price: 2179500 - weight: 200000 - compare_at_price: - available: true - inventory_quantity: 0 - option1: red - option2: - option3: - - &product-7-var-1 - id: 16 - title: black / small - price: 1900 - weight: 200 - compare_at_price: - available: true - inventory_quantity: 20 - option1: black - option2: small - option3: - - &product-7-var-2 - id: 17 - title: black / medium - price: 1900 - weight: 200 - compare_at_price: - available: false - inventory_quantity: 0 - option1: black - option2: medium - option3: - - &product-7-var-3 - id: 18 - title: black / large - price: 1900 - weight: 200 - compare_at_price: - available: true - inventory_quantity: 10 - option1: black - option2: large - option3: - - &product-7-var-4 - id: 19 - title: black / extra large - price: 1900 - weight: 200 - compare_at_price: - available: false - inventory_quantity: 0 - option1: black - option2: extra large - option3: - - &product-8-var-1 - id: 20 - title: brown / small - price: 5900 - weight: 400 - compare_at_price: 6900 - available: true - inventory_quantity: 5 - option1: brown - option2: small - option3: - - &product-8-var-2 - id: 21 - title: brown / medium - price: 5900 - weight: 400 - compare_at_price: 6900 - available: false - inventory_quantity: 0 - option1: brown - option2: medium - option3: - - &product-8-var-3 - id: 22 - title: brown / large - price: 5900 - weight: 400 - compare_at_price: 6900 - available: true - inventory_quantity: 10 - option1: brown - option2: large - option3: - - &product-8-var-4 - id: 23 - title: black / small - price: 5900 - weight: 400 - compare_at_price: 6900 - available: true - inventory_quantity: 10 - option1: black - option2: small - option3: - - &product-8-var-5 - id: 24 - title: black / medium - price: 5900 - weight: 400 - compare_at_price: 6900 - available: true - inventory_quantity: 10 - option1: black - option2: medium - option3: - - &product-8-var-6 - id: 25 - title: black / large - price: 5900 - weight: 400 - compare_at_price: 6900 - available: false - inventory_quantity: 0 - option1: black - option2: large - option3: - - &product-9-var-1 - id: 26 - title: Body Only - price: 499995 - weight: 2000 - compare_at_price: - available: true - inventory_quantity: 3 - option1: Body Only - option2: - option3: - - &product-9-var-2 - id: 27 - title: Kit with 18-55mm VR lens - price: 523995 - weight: 2000 - compare_at_price: - available: true - inventory_quantity: 2 - option1: Kit with 18-55mm VR lens - option2: - option3: - - &product-9-var-3 - id: 28 - title: Kit with 18-200 VR lens - price: 552500 - weight: 2000 - compare_at_price: - available: true - inventory_quantity: 3 - option1: Kit with 18-200 VR lens - option2: - option3: - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Products -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -products: - - &product-1 - id: 1 - title: Arbor Draft - handle: arbor-draft - type: Snowboards - vendor: Arbor - price: 23900 - price_max: 31900 - price_min: 23900 - price_varies: true - available: true - tags: - - season2005 - - pro - - intermediate - - wooden - - freestyle - options: - - Length - - Style - compare_at_price: 49900 - compare_at_price_max: 50900 - compare_at_price_min: 49900 - compare_at_price_varies: true - url: /products/arbor-draft - featured_image: products/arbor_draft.jpg - images: - - products/arbor_draft.jpg - description: - The Arbor Draft snowboard wouldn't exist if Polynesians hadn't figured out how to surf hundreds of years ago. But the Draft does exist, and it's here to bring your urban and park riding to a new level. The board's freaky Tiki design pays homage to culture that inspired snowboarding. It's designed to spin with ease, land smoothly, lock hook-free onto rails, and take the abuse of a pavement pounding or twelve. The Draft will pop off kickers with authority and carve solidly across the pipe. The Draft features targeted Koa wood die cuts inlayed into the deck that enhance the flex pattern. Now bow down to riding's ancestors. - variants: - - *product-1-var-1 - - *product-1-var-2 - - *product-1-var-3 - - &product-2 - id: 2 - title: Arbor Element - handle: arbor-element - type: Snowboards - vendor: Arbor - price: 29900 - price_max: 29900 - price_min: 29900 - price_varies: false - available: true - tags: - - season2005 - - pro - - wooden - - freestyle - options: - - Length - compare_at_price: 52900 - compare_at_price_max: 52900 - compare_at_price_min: 52900 - compare_at_price_varies: false - url: /products/arbor-element - featured_image: products/element58.jpg - images: - - products/element58.jpg - description: - The Element is a technically advanced all-mountain board for riders who readily transition from one terrain, snow condition, or riding style to another. Its balanced design provides the versatility needed for the true ride-it-all experience. The Element is exceedingly lively, freely initiates, and holds a tight edge at speed. Its structural real-wood topsheet is made with book-matched Koa. - variants: - - *product-2-var-1 - - - &product-3 - id: 3 - title: Comic ~ Pastel - handle: comic-pastel - type: Snowboards - vendor: Technine - price: 19900 - price_max: 22900 - price_min: 19900 - tags: - - season2006 - - beginner - - intermediate - - freestyle - - purple - options: - - Length - price_varies: true - available: true - compare_at_price: - compare_at_price_max: 0 - compare_at_price_min: 0 - compare_at_price_varies: false - url: /products/comic-pastel - featured_image: products/technine1.jpg - images: - - products/technine1.jpg - - products/technine2.jpg - - products/technine_detail.jpg - description: - 2005 Technine Comic Series Description The Comic series was developed to be the ultimate progressive freestyle board in the Technine line. Dependable edge control and a perfect flex pattern for jumping in the park or out of bounds. Landins and progression will come easy with this board and it will help your riding progress to the next level. Street rails, park jibs, backcountry booters and park jumps, this board will do it all. - variants: - - *product-3-var-1 - - *product-3-var-2 - - *product-3-var-3 - - - &product-4 - id: 4 - title: Comic ~ Orange - handle: comic-orange - type: Snowboards - vendor: Technine - price: 19900 - price_max: 19900 - price_min: 19900 - price_varies: false - available: true - tags: - - season2006 - - beginner - - intermediate - - freestyle - - orange - options: - - Length - compare_at_price: 32900 - compare_at_price_max: 32900 - compare_at_price_min: 32900 - compare_at_price_varies: false - url: /products/comic-orange - featured_image: products/technine3.jpg - images: - - products/technine3.jpg - - products/technine4.jpg - description: - 2005 Technine Comic Series Description The Comic series was developed to be the ultimate progressive freestyle board in the Technine line. Dependable edge control and a perfect flex pattern for jumping in the park or out of bounds. Landins and progression will come easy with this board and it will help your riding progress to the next level. Street rails, park jibs, backcountry booters and park jumps, this board will do it all. - variants: - - *product-4-var-1 - - - &product-5 - id: 5 - title: Burton Boots - handle: burton-boots - type: Boots - vendor: Burton - price: 11900 - price_max: 11900 - price_min: 11900 - price_varies: false - available: true - tags: - - season2006 - - beginner - - intermediate - - boots - options: - - Color - - Shoe Size - compare_at_price: 22900 - compare_at_price_max: 22900 - compare_at_price_min: 22900 - compare_at_price_varies: false - url: /products/burton-boots - featured_image: products/burton.jpg - images: - - products/burton.jpg - description: - The Burton boots are particularly well on snowboards. The very best thing about them is that the according picture is cubic. This makes testing in a Vision testing environment very easy. - variants: - - *product-5-var-1 - - *product-5-var-2 - - *product-5-var-3 - - *product-5-var-4 - - *product-5-var-5 - - *product-5-var-6 - - - &product-6 - id: 6 - title: Superbike 1198 S - handle: superbike - type: Superbike - vendor: Ducati - price: 2179500 - price_max: 2179500 - price_min: 2179500 - price_varies: false - available: true - tags: - - ducati - - superbike - - bike - - street - - racing - - performance - options: - - Color - compare_at_price: - compare_at_price_max: 0 - compare_at_price_min: 0 - compare_at_price_varies: false - url: /products/superbike - featured_image: products/ducati.jpg - images: - - products/ducati.jpg - description: -

‘S’ PERFORMANCE

-

Producing 170hp (125kW) and with a dry weight of just 169kg (372.6lb), the new 1198 S now incorporates more World Superbike technology than ever before by taking the 1198 motor and adding top-of-the-range suspension, lightweight chassis components and a true racing-style traction control system designed for road use.

-

The high performance, fully adjustable 43mm Öhlins forks, which sport low friction titanium nitride-treated fork sliders, respond effortlessly to every imperfection in the tarmac. Beyond their advanced engineering solutions, one of the most important characteristics of Öhlins forks is their ability to communicate the condition and quality of the tyre-to-road contact patch, a feature that puts every rider in superior control. The suspension set-up at the rear is complemented with a fully adjustable Öhlins rear shock equipped with a ride enhancing top-out spring and mounted to a single-sided swingarm for outstanding drive and traction. The front-to-rear Öhlins package is completed with a control-enhancing adjustable steering damper.

- variants: - - *product-6-var-1 - - - &product-7 - id: 7 - title: Shopify Shirt - handle: shopify-shirt - type: Shirt - vendor: Shopify - price: 1900 - price_max: 1900 - price_min: 1900 - price_varies: false - available: true - tags: - - shopify - - shirt - - apparel - - tshirt - - clothing - options: - - Color - - Size - compare_at_price: - compare_at_price_max: 0 - compare_at_price_min: 0 - compare_at_price_varies: false - url: /products/shopify-shirt - featured_image: products/shopify_shirt.png - images: - - products/shopify_shirt.png - description: -

High Quality Shopify Shirt. Wear your e-commerce solution with pride and attract attention anywhere you go.

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

- variants: - - *product-7-var-1 - - *product-7-var-2 - - *product-7-var-3 - - *product-7-var-4 - - - &product-8 - id: 8 - title: Hooded Sweater - handle: hooded-sweater - type: Sweater - vendor: Stormtech - price: 5900 - price_max: 5900 - price_min: 5900 - price_varies: false - available: true - tags: - - sweater - - hooded - - apparel - - clothing - options: - - Color - - Size - compare_at_price: 6900 - compare_at_price_max: 6900 - compare_at_price_min: 6900 - compare_at_price_varies: false - url: /products/hooded-sweater - featured_image: products/hooded-sweater.jpg - images: - - products/hooded-sweater.jpg - - products/hooded-sweater-b.jpg - description: -

Extra comfortable zip up sweater. Durable quality, ideal for any outdoor activities.

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

- variants: - - *product-8-var-1 - - *product-8-var-2 - - *product-8-var-3 - - *product-8-var-4 - - *product-8-var-5 - - *product-8-var-6 - - - &product-9 - id: 9 - title: D3 Digital SLR Camera - handle: d3 - type: SLR - vendor: Nikon - price: 499995 - price_max: 552500 - price_min: 499995 - price_varies: true - available: true - tags: - - camera - - slr - - nikon - - professional - options: - - Bundle - compare_at_price: - compare_at_price_max: 0 - compare_at_price_min: 0 - compare_at_price_varies: false - url: /products/d3 - featured_image: products/d3.jpg - images: - - products/d3.jpg - - products/d3_2.jpg - - products/d3_3.jpg - description: -

Flagship pro D-SLR with a 12.1-MP FX-format CMOS sensor, blazing 9 fps shooting at full FX resolution and low-noise performance up to 6400 ISO.

-

Nikon's original 12.1-megapixel FX-format (23.9 x 36mm) CMOS sensor: Couple Nikon's exclusive digital image processing system with the 12.1-megapixel FX-format and you'll get breathtakingly rich images while also reducing noise to unprecedented levels with even higher ISOs.

-

Continuous shooting at up to 9 frames per second: At full FX resolution and up to 11fps in the DX crop mode, the D3 offers uncompromised shooting speeds for fast-action and sports photography.

- variants: - - *product-9-var-1 - - *product-9-var-2 - - *product-9-var-3 - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Line Items -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -line_items: - - &line_item-1 - id: 1 - title: 'Arbor Draft' - subtitle: '151cm' - price: 29900 - line_price: 29900 - quantity: 1 - variant: *product-1-var-1 - product: *product-1 - - - &line_item-2 - id: 2 - title: 'Comic ~ Orange' - subtitle: '159cm' - price: 19900 - line_price: 39800 - quantity: 2 - variant: *product-4-var-1 - product: *product-4 - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Link Lists -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -links: - - &link-1 - id: 1 - title: Our Sale - url: /collections/sale - - &link-2 - id: 2 - title: Arbor Stuff - url: /collections/arbor - - &link-3 - id: 3 - title: All our Snowboards - url: /collections/snowboards - - &link-4 - id: 4 - title: Powered by Shopify - url: 'http://shopify.com' - - &link-5 - id: 5 - title: About Us - url: /pages/about-us - - &link-6 - id: 6 - title: Policies - url: /pages/shipping - - &link-7 - id: 7 - title: Contact Us - url: /pages/contact - - &link-8 - id: 8 - title: Our blog - url: /blogs/bigcheese-blog - - &link-9 - id: 9 - title: New Boots - url: /products/burton-boots - - &link-10 - id: 10 - title: Paginated Sale - url: /collections/paginated-sale - - &link-11 - id: 11 - title: Our Paginated blog - url: /blogs/paginated-blog - - &link-12 - id: 12 - title: Catalog - url: /collections/all - - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Link Lists -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -link_lists: - - &link-list-1 - id: 1 - title: 'Main Menu' - handle: 'main-menu' - links: - - *link-12 - - *link-5 - - *link-7 - - *link-8 - - &link-list-2 - id: 1 - title: 'Footer Menu' - handle: 'footer' - links: - - *link-5 - - *link-6 - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Collections -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -collections: - - &collection-1 - id: 1 - title: Frontpage - handle: frontpage - url: /collections/frontpage - products: - - *product-7 - - *product-8 - - *product-9 - - - &collection-2 - id: 2 - title: Arbor - handle: arbor - url: /collections/arbor - products: - - *product-1 - - *product-2 - - - &collection-3 - id: 3 - title: Snowboards - handle: snowboards - url: /collections/snowboards - description: -

This is a description for my Snowboards collection.

- products: - - *product-1 - - *product-2 - - *product-3 - - *product-4 - - - &collection-4 - id: 4 - title: Items On Sale - handle: sale - url: /collections/sale - products: - - *product-1 - - - &collection-5 - id: 5 - title: Paginated Sale - handle: 'paginated-sale' - url: '/collections/paginated-sale' - products: - - *product-1 - - *product-2 - - *product-3 - - *product-4 - products_count: 210 - - - &collection-6 - id: 6 - title: All products - handle: 'all' - url: '/collections/all' - products: - - *product-7 - - *product-8 - - *product-9 - - *product-6 - - *product-1 - - *product-2 - - *product-3 - - *product-4 - - *product-5 - - -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -# Pages and Blogs -# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -pages: - - &page-2 - id: 1 - title: Contact Us - handle: contact - url: /pages/contact - author: Tobi - content: - "

You can contact us via phone under (555) 567-2222.

-

Our retail store is located at Rue d'Avignon 32, Avignon (Provence).

-

Opening Hours:
Monday through Friday: 9am - 6pm
Saturday: 10am - 3pm
Sunday: closed

" - created_at: 2005-04-04 12:00 - - - &page-3 - id: 2 - title: About Us - handle: about-us - url: /pages/about-us - author: Tobi - content: - "

Our company was founded in 1894 and we are since operating out of Avignon from the beautiful Provence.

-

We offer the highest quality products and are proud to serve our customers to their heart's content.

" - created_at: 2005-04-04 12:00 - - - &page-4 - id: 3 - title: Shopping Cart - handle: shopping-cart - url: /pages/shopping-cart - author: Tobi - content: "
  • Your order is safe with us. Our checkout uses industry standard security to protect your information.
  • Your order will be billed immediately upon checkout.
  • ALL SALES ARE FINAL: Defective or damaged product will be exchanged
  • All orders are processed expediently: usually in under 24 hours.
" - created_at: 2005-04-04 12:00 - - - &page-5 - id: 4 - title: Shipping and Handling - handle: shipping - url: /pages/shipping - author: Tobi - content:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

- created_at: 2005-04-04 12:00 - - - &page-6 - id: 5 - title: Frontpage - handle: frontpage - url: /pages/frontpage - author: Tobi - content:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

- created_at: 2005-04-04 12:00 - -blogs: - - id: 1 - handle: news - title: News - url: /blogs/news - articles: - - id: 3 - title: 'Welcome to the new Foo Shop' - author: Daniel - content:

Welcome to your Shopify store! The jaded Pixel crew is really glad you decided to take Shopify for a spin.

To help you get you started with Shopify, here are a couple of tips regarding what you see on this page.

The text you see here is an article. To edit this article, create new articles or create new pages you can go to the Blogs & Pages tab of the administration menu.

The Shopify t-shirt above is a product and selling products is what Shopify is all about. To edit this product, or create new products you can go to the Products Tab in of the administration menu.

While you're looking around be sure to check out the Collections and Navigations tabs and soon you will be well on your way to populating your site.

And of course don't forget to browse the theme gallery to pick a new look for your shop!

Shopify is in beta
If you would like to make comments or suggestions please visit us in the Shopify Forums or drop us an email.

- created_at: 2005-04-04 16:00 - - id: 4 - title: 'Breaking News: Restock on all sales products' - author: Tobi - content: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. - created_at: 2005-04-04 12:00 - articles_count: 2 - - - id: 2 - handle: bigcheese-blog - title: Bigcheese blog - url: /blogs/bigcheese-blog - articles: - - id: 1 - title: 'One thing you probably did not know yet...' - author: Justin - content: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. - created_at: 2005-04-04 16:00 - comments: - - - id: 1 - author: John Smith - email: john@smith.com - content: Wow...great article man. - status: published - created_at: 2009-01-01 12:00 - updated_at: 2009-02-01 12:00 - url: "" - - - id: 2 - author: John Jones - email: john@jones.com - content: I really enjoyed this article. And I love your shop! It's awesome. Shopify rocks! - status: published - created_at: 2009-03-01 12:00 - updated_at: 2009-02-01 12:00 - url: "http://somesite.com/" - - id: 2 - title: Fascinating - author: Tobi - content: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. - created_at: 2005-04-06 12:00 - comments: - articles_count: 2 - comments_enabled?: true - comment_post_url: "" - comments_count: 2 - moderated?: true - - - id: 3 - handle: paginated-blog - title: Paginated blog - url: /blogs/paginated-blog - articles: - - id: 6 - title: 'One thing you probably did not know yet...' - author: Justin - content: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. - created_at: 2005-04-04 16:00 - - - id: 7 - title: Fascinating - author: Tobi - content: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. - created_at: 2005-04-06 12:00 - articles_count: 200 diff --git a/performance/shopify/weight_filter.rb b/performance/shopify/weight_filter.rb deleted file mode 100644 index 3ff7c2c..0000000 --- a/performance/shopify/weight_filter.rb +++ /dev/null @@ -1,11 +0,0 @@ -module WeightFilter - - def weight(grams) - sprintf("%.2f", grams / 1000) - end - - def weight_with_unit(grams) - "#{weight(grams)} kg" - end - -end diff --git a/performance/tests/dropify/article.liquid b/performance/tests/dropify/article.liquid deleted file mode 100644 index dca6caa..0000000 --- a/performance/tests/dropify/article.liquid +++ /dev/null @@ -1,74 +0,0 @@ -
-

{{ article.title }}

-

posted {{ article.created_at | date: "%Y %h" }} by

- -
- {{ article.content }} -
- -
- - -{% if blog.comments_enabled? %} -
-

Comments

- - -
    - {% for comment in article.comments %} -
  • -
    - {{ comment.author }} said on {{ comment.created_at | date: "%B %d, %Y" }}: -
    - -
    - {{ comment.content }} -
    -
  • - {% endfor %} -
- - -
- {% form article %} -

Leave a comment

- - - {% if form.posted_successfully? %} - {% if blog.moderated? %} -
- Successfully posted your comment.
- It will have to be approved by the blog owner first before showing up. -
- {% else %} -
Successfully posted your comment.
- {% endif %} - {% endif %} - - {% if form.errors %} -
Not all the fields have been filled out correctly!
- {% endif %} - -
-
-
- -
-
- -
-
-
- - {% if blog.moderated? %} -

comments have to be approved before showing up

- {% endif %} - - - {% endform %} -
- - -
-{% endif %} - diff --git a/performance/tests/dropify/blog.liquid b/performance/tests/dropify/blog.liquid deleted file mode 100644 index 5a800c4..0000000 --- a/performance/tests/dropify/blog.liquid +++ /dev/null @@ -1,33 +0,0 @@ -
-

{{page.title}}

- - {% paginate blog.articles by 20 %} - - {% for article in blog.articles %} - -
-
-

- {{ article.title }} -

-

Posted on {{ article.created_at | date: "%B %d, '%y" }} by {{ article.author }}.

-
- -
- {{ article.content | strip_html | truncate: 250 }} -
- - {% if blog.comments_enabled? %} -

{{ article.comments_count }} comments

- {% endif %} -
- - {% endfor %} - - - - {% endpaginate %} - -
diff --git a/performance/tests/dropify/cart.liquid b/performance/tests/dropify/cart.liquid deleted file mode 100644 index 8fe26ab..0000000 --- a/performance/tests/dropify/cart.liquid +++ /dev/null @@ -1,66 +0,0 @@ - - -
- - {% if cart.item_count == 0 %} -

Your shopping cart is looking rather empty...

- {% else %} -
- -
- -

You have {{ cart.item_count }} {{ cart.item_count | pluralize: 'product', 'products' }} in here!

- - - -
- -
- -
- - {% if additional_checkout_buttons %} -
-

- or -

- {{ content_for_additional_checkout_buttons }} -
- {% endif %} - -
- - {% endif %} - -
diff --git a/performance/tests/dropify/collection.liquid b/performance/tests/dropify/collection.liquid deleted file mode 100644 index ad1427c..0000000 --- a/performance/tests/dropify/collection.liquid +++ /dev/null @@ -1,22 +0,0 @@ -{% paginate collection.products by 20 %} - -
    - {% for product in collection.products %} -
  • -
    -
    -
    -
    -

    {{product.title}}

    -

    {{ product.description | strip_html | truncatewords: 35 }}

    -

    {{ product.price_min | money }}{% if product.price_varies %} - {{ product.price_max | money }}{% endif %}

    -
    -
  • - {% endfor %} -
- - - -{% endpaginate %} diff --git a/performance/tests/dropify/index.liquid b/performance/tests/dropify/index.liquid deleted file mode 100644 index f1ea137..0000000 --- a/performance/tests/dropify/index.liquid +++ /dev/null @@ -1,47 +0,0 @@ -
-

Featured Items

-{% for product in collections.frontpage.products limit:1 offset:0 %} -
- {{ product.title | escape }} -

{{ product.title }}

-
{{ product.description | strip_html | truncatewords: 18 }}
-

{{ product.price_min | money }}

-
-{% endfor %} -{% for product in collections.frontpage.products offset:1 %} -
- {{ product.title | escape }} -

{{ product.title }}

-

{{ product.price_min | money }}

-
-{% endfor %} -
- -
- {% assign article = pages.frontpage %} - - {% if article.content != "" %} -

{{ article.title }}

-
- {{ article.content }} -
- {% else %} -
- In Admin > Blogs & Pages, create a page with the handle frontpage and it will show up here.
- {{ "Learn more about handles" | link_to: "http://wiki.shopify.com/Handle" }} -
- {% endif %} - -
-
-
- {% for article in blogs.news.articles offset:1 %} -
-

{{ article.title }}

-
- {{ article.content }} -
-
- {% endfor %} -
- diff --git a/performance/tests/dropify/page.liquid b/performance/tests/dropify/page.liquid deleted file mode 100644 index 2ccbc63..0000000 --- a/performance/tests/dropify/page.liquid +++ /dev/null @@ -1,8 +0,0 @@ -
-

{{page.title}}

- -
- {{page.content}} -
- -
diff --git a/performance/tests/dropify/product.liquid b/performance/tests/dropify/product.liquid deleted file mode 100644 index 8870389..0000000 --- a/performance/tests/dropify/product.liquid +++ /dev/null @@ -1,68 +0,0 @@ -
- -
- {% for image in product.images %} - {% if forloop.first %} - - {{product.title | escape }} - - {% else %} - - {{product.title | escape }} - - {% endif %} - {% endfor %} -
- -

{{ product.title }}

- -
    -
  • Vendor: {{ product.vendor | link_to_vendor }}
  • -
  • Type: {{ product.type | link_to_type }}
  • -
- - {{ product.price_min | money }}{% if product.price_varies %} - {{ product.price_max | money }}{% endif %} - -
-
- - - -
- -
-
-
- -
- {{ product.description }} -
-
- - diff --git a/performance/tests/dropify/theme.liquid b/performance/tests/dropify/theme.liquid deleted file mode 100644 index aca9206..0000000 --- a/performance/tests/dropify/theme.liquid +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - {{shop.name}} - {{page_title}} - - {{ 'textile.css' | global_asset_url | stylesheet_tag }} - {{ 'lightbox/v204/lightbox.css' | global_asset_url | stylesheet_tag }} - - {{ 'prototype/1.6/prototype.js' | global_asset_url | script_tag }} - {{ 'scriptaculous/1.8.2/scriptaculous.js' | global_asset_url | script_tag }} - {{ 'lightbox/v204/lightbox.js' | global_asset_url | script_tag }} - {{ 'option_selection.js' | shopify_asset_url | script_tag }} - - {{ 'layout.css' | asset_url | stylesheet_tag }} - {{ 'shop.js' | asset_url | script_tag }} - - {{ content_for_header }} - - - - -

Skip to navigation.

- - {% if cart.item_count > 0 %} - - {% endif %} - -
- -
-
- -
-
- {{ content_for_layout }} -
-
- -
-
- - - - {% if tags %} - - {% endif %} - - - -
- -

- - - -
-
- -
- - - - - diff --git a/performance/tests/ripen/article.liquid b/performance/tests/ripen/article.liquid deleted file mode 100644 index e1b7e71..0000000 --- a/performance/tests/ripen/article.liquid +++ /dev/null @@ -1,74 +0,0 @@ -
-

{{ article.title }}

-

posted {{ article.created_at | date: "%Y %h" }} by

- -
- {{ article.content }} -
- -
- - -{% if blog.comments_enabled? %} -
-

Comments

- - -
    - {% for comment in article.comments %} -
  • -
    - {{ comment.content }} -
    - -
    - Posted by {{ comment.author }} on {{ comment.created_at | date: "%B %d, %Y" }} -
    -
  • - {% endfor %} -
- - -
- {% form article %} -

Leave a comment

- - - {% if form.posted_successfully? %} - {% if blog.moderated? %} -
- Successfully posted your comment.
- It will have to be approved by the blog owner first before showing up. -
- {% else %} -
Successfully posted your comment.
- {% endif %} - {% endif %} - - {% if form.errors %} -
Not all the fields have been filled out correctly!
- {% endif %} - -
-
-
- -
-
- -
-
-
- - {% if blog.moderated? %} -

comments have to be approved before showing up

- {% endif %} - - - {% endform %} -
- - -
-{% endif %} - diff --git a/performance/tests/ripen/blog.liquid b/performance/tests/ripen/blog.liquid deleted file mode 100644 index 91b71ba..0000000 --- a/performance/tests/ripen/blog.liquid +++ /dev/null @@ -1,13 +0,0 @@ -
-

{{page.title}}

- {% for article in blog.articles %} -

- {{ article.created_at | date: '%d %b' }} - {{ article.title }} -

- {{ article.content }} - {% if blog.comments_enabled? %} -

{{ article.comments_count }} comments

- {% endif %} - {% endfor %} -
diff --git a/performance/tests/ripen/cart.liquid b/performance/tests/ripen/cart.liquid deleted file mode 100644 index db7a221..0000000 --- a/performance/tests/ripen/cart.liquid +++ /dev/null @@ -1,54 +0,0 @@ - - -
- - {% if cart.item_count == 0 %} -

Your shopping cart is empty...

-

Continue shopping

- {% else %} - -

- - - - - - - - - - - {% for item in cart.items %} - - - - - - - - - {% endfor %} -
ProductQtyPriceTotalRemove
{{ item.product.featured_image | product_img_url: 'thumb' | img_tag }}{{ item.title }}{{ item.price | money }}{{item.line_price | money }}Remove
-

-

- Subtotal: {{cart.total_price | money_with_currency }} -

-

- - {% if additional_checkout_buttons %} -
-

- or -

- {{ content_for_additional_checkout_buttons }} -
- {% endif %} - -
- - {% endif %} - -
diff --git a/performance/tests/ripen/collection.liquid b/performance/tests/ripen/collection.liquid deleted file mode 100644 index 31151f3..0000000 --- a/performance/tests/ripen/collection.liquid +++ /dev/null @@ -1,29 +0,0 @@ -
- -{% if collection.description %} -
{{ collection.description }}
-{% endif %} - -{% paginate collection.products by 20 %} - -
    - {% for product in collection.products %} -
  • -
    -
    {{ product.title | escape }}
    -
    -
    -

    {{product.title}}

    -

    {{ product.description | strip_html | truncatewords: 35 }}

    -

    {{ product.price_min | money }}{% if product.price_varies %} - {{ product.price_max | money }}{% endif %}

    -
    -
  • - {% endfor %} -
- - - -{% endpaginate %} -
diff --git a/performance/tests/ripen/index.liquid b/performance/tests/ripen/index.liquid deleted file mode 100644 index 0e8ad56..0000000 --- a/performance/tests/ripen/index.liquid +++ /dev/null @@ -1,32 +0,0 @@ -
-

Featured products...

- - -
- {% assign article = pages.frontpage %} - {% if article.content != "" %} -

{{ article.title }}

- {{ article.content }} - {% else %} - In Admin > Blogs & Pages, create a page with the handle frontpage and it will show up here.
- {{ "Learn more about handles" | link_to: "http://wiki.shopify.com/Handle" }} - {% endif %} -
-
diff --git a/performance/tests/ripen/page.liquid b/performance/tests/ripen/page.liquid deleted file mode 100644 index b3e9702..0000000 --- a/performance/tests/ripen/page.liquid +++ /dev/null @@ -1,4 +0,0 @@ -
-

{{page.title}}

- {{ page.content }} -
diff --git a/performance/tests/ripen/product.liquid b/performance/tests/ripen/product.liquid deleted file mode 100644 index 9a97045..0000000 --- a/performance/tests/ripen/product.liquid +++ /dev/null @@ -1,75 +0,0 @@ -
-

{{ product.title }}

-
-
- {% for image in product.images %} - {% if forloop.first %} - - {{product.title | escape }} - - {% else %} - - {{product.title | escape }} - - {% endif %} - {% endfor %} -
- -
    -
  • Vendor: {{ product.vendor | link_to_vendor }}
  • -
  • Type: {{ product.type | link_to_type }}
  • -
- - {{ product.price_min | money }}{% if product.price_varies %} - {{ product.price_max | money }}{% endif %} - -
- {% if product.available %} - -
- - - -
- -
-
- {% else %} - Sold Out! - {% endif %} -
- -
- {{ product.description }} -
-
-
- - - - diff --git a/performance/tests/ripen/theme.liquid b/performance/tests/ripen/theme.liquid deleted file mode 100644 index 72c2992..0000000 --- a/performance/tests/ripen/theme.liquid +++ /dev/null @@ -1,85 +0,0 @@ - - - - {{shop.name}} - {{page_title}} - - - {{ 'main.css' | asset_url | stylesheet_tag }} - {{ 'shop.js' | asset_url | script_tag }} - - {{ 'mootools.js' | asset_url | script_tag }} - {{ 'slimbox.js' | asset_url | script_tag }} - {{ 'option_selection.js' | shopify_asset_url | script_tag }} - {{ 'slimbox.css' | asset_url | stylesheet_tag }} - - {{ content_for_header }} - - - -

Skip to navigation.

-
-
- -
- {{ content_for_layout }} -
-
- {% if template != 'cart' %} -
-
-
Shopping Cart
-
- {% if cart.item_count != 0 %} - {{ cart.item_count }} {{ cart.item_count | pluralize: 'item', 'items' }} in your cart - {% else %} - Your cart is empty - {% endif %} -
-
-
- {% endif %} - - -
- -
-
-
- - - - - diff --git a/performance/tests/tribble/404.liquid b/performance/tests/tribble/404.liquid deleted file mode 100644 index acf184c..0000000 --- a/performance/tests/tribble/404.liquid +++ /dev/null @@ -1,56 +0,0 @@ -
- -
-
-

Oh no!

-
- Seems like you are looking for something that just isn't here. Try heading back to our main page. Or you can checkout some of our featured products below. -
-
-
- - -

Featured Products

-
    - - {% for product in collections.frontpage.products %} -
  • -
    -
    -
    -
    -

    {{product.title}}

    -

    {{ product.description | truncatewords: 15 }}

    -
    - {{ product.title | escape }} -
    - -
    - - -

    - View Details - - - {% if product.compare_at_price %} - {% if product.price_min != product.compare_at_price %} - {{product.compare_at_price | money}} - - {% endif %} - {% endif %} - - {{product.price_min | money}} - - -

    -
    -
    -
    -
  • - {% endfor %} - -
-
- - - - diff --git a/performance/tests/tribble/article.liquid b/performance/tests/tribble/article.liquid deleted file mode 100644 index cb2453a..0000000 --- a/performance/tests/tribble/article.liquid +++ /dev/null @@ -1,98 +0,0 @@ - -
-
- -
-

{{article.title}}

-
-
{{ article.created_at | date: "%b %d" }}
- {{ article.content }} -
- - -{% if blog.comments_enabled? %} -
-

Comments

- - -
    - {% for comment in article.comments %} -
  • -
    - {{ comment.content }} -
    - -
    - Posted by {{ comment.author }} on {{ comment.created_at | date: "%B %d, %Y" }} -
    -
  • - {% endfor %} -
- - -
- {% form article %} -

Leave a comment

- - - {% if form.posted_successfully? %} - {% if blog.moderated? %} -
- Successfully posted your comment.
- It will have to be approved by the blog owner first before showing up. -
- {% else %} -
Successfully posted your comment.
- {% endif %} - {% endif %} - - {% if form.errors %} -
Not all the fields have been filled out correctly!
- {% endif %} - -
-
-
- -
-
- -
-
-
- - {% if blog.moderated? %} -

comments have to be approved before showing up

- {% endif %} - - - {% endform %} -
- - -
-{% endif %} - - - -
-
- -
-

Why Shop With Us?

-
    -
  • -

    24 Hours

    -

    We're always here to help.

    -
  • -
  • -

    No Spam

    -

    We'll never share your info.

    -
  • -
  • -

    Secure Servers

    -

    Checkout is 256bit encrypted.

    -
  • -
-
-
diff --git a/performance/tests/tribble/blog.liquid b/performance/tests/tribble/blog.liquid deleted file mode 100644 index ba9f52a..0000000 --- a/performance/tests/tribble/blog.liquid +++ /dev/null @@ -1,41 +0,0 @@ -
-
-

Post from our blog...

- {% paginate blog.articles by 20 %} - {% for article in blog.articles %} - -
-

{{ article.title }}

-
-
{{ article.created_at | date: "%b %d" }}
- {{ article.content }} -
-
- - {% endfor %} - -
- {{ paginate | default_pagination }} -
- - {% endpaginate %} -
- -
-

Why Shop With Us?

-
    -
  • -

    24 Hours

    -

    We're always here to help.

    -
  • -
  • -

    No Spam

    -

    We'll never share your info.

    -
  • -
  • -

    Secure Servers

    -

    Checkout is 256bit encrypted.

    -
  • -
-
-
diff --git a/performance/tests/tribble/cart.liquid b/performance/tests/tribble/cart.liquid deleted file mode 100644 index bdc5e83..0000000 --- a/performance/tests/tribble/cart.liquid +++ /dev/null @@ -1,134 +0,0 @@ - - -
. - {% if cart.item_count == 0 %} -

Your cart is currently empty.

- {% else %} - -

Your Cart ({{ cart.item_count }} {{ cart.item_count | pluralize: 'item', 'items' }}, {{cart.total_price | money_with_currency }} total)

- -
- -
- - - - - - - - - - {% for item in cart.items %} - - - - {% endfor %} -
- - - - - - - - -
{{ item.product.featured_image | product_img_url: 'thumb' | img_tag }}

{{ item.title }}

{{item.line_price | money }}Remove
-
- -
- -
- {{ pages.shopping-cart.content }} -
- -

- Order Total: {{cart.total_price | money_with_currency }} -

- -

- -

- - {% if additional_checkout_buttons %} -
-

- or -

- {{ content_for_additional_checkout_buttons }} -
- {% endif %} - -
- -
- -
- - {% endif %} - - - -

Other Products You Might Enjoy

-
    - - {% for product in collections.frontpage.products limit:2 %} -
  • -
    -
    -
    -
    -

    {{product.title}}

    -

    {{ product.description | truncatewords: 15 }}

    -
    - {{ product.title | escape }} -
    - -
    - - -

    - View Details - - - {% if product.compare_at_price %} - {% if product.price_min != product.compare_at_price %} - {{product.compare_at_price | money}} - - {% endif %} - {% endif %} - - {{product.price_min | money}} - - -

    -
    -
    -
    -
  • - {% endfor %} - -
- -
-

Why Shop With Us?

-
    -
  • -

    24 Hours

    -

    We're always here to help.

    -
  • -
  • -

    No Spam

    -

    We'll never share your info.

    -
  • -
  • -

    Secure Servers

    -

    Checkout is 256bit encrypted.

    -
  • -
-
- -
- diff --git a/performance/tests/tribble/collection.liquid b/performance/tests/tribble/collection.liquid deleted file mode 100644 index fd2fd01..0000000 --- a/performance/tests/tribble/collection.liquid +++ /dev/null @@ -1,70 +0,0 @@ -
-

{{ collection.title }}

- {% if collection.description.size > 0 %} -
{{ collection.description }}
- {% endif %} - - {% paginate collection.products by 8 %} - -
    - {% for product in collection.products %} -
  • -
    -
    -
    -
    -

    {{product.title}}

    -

    {{ product.description | truncatewords: 15 }}

    -
    - {{ product.title | escape }} -
    - -
    - - -

    - View Details - - - {% if product.compare_at_price %} - {% if product.price_min != product.compare_at_price %} - {{product.compare_at_price | money}} - - {% endif %} - {% endif %} - - {{product.price_min | money}} - - -

    -
    -
    -
    -
  • - {% endfor %} -
- -
- {{ paginate | default_pagination }} -
- - -
-

Why Shop With Us?

-
    -
  • -

    24 Hours

    -

    We're always here to help.

    -
  • -
  • -

    No Spam

    -

    We'll never share your info.

    -
  • -
  • -

    Secure Servers

    -

    Checkout is 256bit encrypted.

    -
  • -
-
-
- -{% endpaginate %} diff --git a/performance/tests/tribble/index.liquid b/performance/tests/tribble/index.liquid deleted file mode 100644 index 551cf7c..0000000 --- a/performance/tests/tribble/index.liquid +++ /dev/null @@ -1,94 +0,0 @@ -
-
-

Three Great Reasons You Should Shop With Us...

-
    -
  • -

    Free Shipping

    -

    On all orders over $25

    -
  • -
  • -

    Top Quality

    -

    Hand made in our shop

    -
  • -
  • -

    100% Guarantee

    -

    Any time, any reason

    -
  • -
-
-
- -
- -
{{pages.alert.content}}
- -
    - - {% for product in collections.frontpage.products %} -
  • -
    -
    -
    -
    -

    {{product.title}}

    - {{ product.description | truncatewords: 15 }}

    -
    - {{ product.title | escape }} -
    - -
    - - -

    - View Details - - - {% if product.compare_at_price %} - {% if product.price_min != product.compare_at_price %} - {{product.compare_at_price | money}} - - {% endif %} - {% endif %} - - {{product.price_min | money}} - - -

    -
    -
    -
    -
  • - {% endfor %} - -
- -
-
-

Why Shop With Us?

-
    -
  • -

    24 Hours

    -

    We're always here to help.

    -
  • -
  • -

    No Spam

    -

    We'll never share your info.

    -
  • -
  • -

    Save Energy

    -

    We're green, all the way.

    -
  • -
  • -

    Secure Servers

    -

    Checkout is 256bits encrypted.

    -
  • -
-
- -
-

Our Company

- {{pages.about-us.content | truncatewords: 49}} read more

-
-
- -
- diff --git a/performance/tests/tribble/page.liquid b/performance/tests/tribble/page.liquid deleted file mode 100644 index fa01097..0000000 --- a/performance/tests/tribble/page.liquid +++ /dev/null @@ -1,56 +0,0 @@ -
- -
-
-

{{page.title}}

-
- {{page.content}} -
-
-
- - -

Featured Products

-
    - - {% for product in collections.frontpage.products %} -
  • -
    -
    -
    -
    -

    {{product.title}}

    -

    {{ product.description | truncatewords: 15 }}

    -
    - {{ product.title | escape }} -
    - -
    - - -

    - View Details - - - {% if product.compare_at_price %} - {% if product.price_min != product.compare_at_price %} - {{product.compare_at_price | money}} - - {% endif %} - {% endif %} - - {{product.price_min | money}} - - -

    -
    -
    -
    -
  • - {% endfor %} - -
-
- - - - diff --git a/performance/tests/tribble/product.liquid b/performance/tests/tribble/product.liquid deleted file mode 100644 index f0a1bf6..0000000 --- a/performance/tests/tribble/product.liquid +++ /dev/null @@ -1,116 +0,0 @@ -
-

{{ collection.title }} {{ product.title }}

- - -

Product Tags: - {% for tag in product.tags %} - {{ tag }} | - {% endfor %} -

- -
-
-

{{ product.title }}

-
-

{{ product.description }}

-
- - {% if product.available %} -
- -

Product Options:

- - - -
- -
- -
- -
- {% else %} -

Sold out!

-

Sorry, we're all out of this product. Check back often and order when it returns

- {% endif %} -
- -
- {% for image in product.images %} - - {% if forloop.first %} -
- {{product.title | escape }} -
- {% else %} - {% endif %} - {% endfor %} - -
    - {% for image in product.images %} - {% if forloop.first %} - {% else %} - -
  • - - {{product.title | escape }} - -
  • - {% endif %} - {% endfor %} -
-
-
- - - -
-

Why Shop With Us?

-
    -
  • -

    24 Hours

    -

    We're always here to help.

    -
  • -
  • -

    No Spam

    -

    We'll never share your info.

    -
  • -
  • -

    Secure Servers

    -

    Checkout is 256bit encrypted.

    -
  • -
-
- -
- - - - - diff --git a/performance/tests/tribble/search.liquid b/performance/tests/tribble/search.liquid deleted file mode 100644 index f509c74..0000000 --- a/performance/tests/tribble/search.liquid +++ /dev/null @@ -1,51 +0,0 @@ - - - - -
-

Search Results

- {% if search.performed %} - - {% paginate search.results by 10 %} - - {% if search.results == empty %} -
Your search for "{{search.terms | escape}}" did not yield any results
- {% else %} - - -
    - {% for item in search.results %} -
  • -

    {{ item.title | link_to: item.url }}

    -

    {{ item.content | strip_html | truncatewords: 65 | highlight: search.terms }} ... view this item

    -
  • - {% endfor %} -
- {% endif %} - -
- {{ paginate | default_pagination }} -
- - -
-

Why Shop With Us?

-
    -
  • -

    24 Hours

    -

    We're always here to help.

    -
  • -
  • -

    No Spam

    -

    We'll never share your info.

    -
  • -
  • -

    Secure Servers

    -

    Checkout is 256bit encrypted.

    -
  • -
-
-
- -{% endpaginate %} -{% endif %} diff --git a/performance/tests/tribble/theme.liquid b/performance/tests/tribble/theme.liquid deleted file mode 100644 index c353c86..0000000 --- a/performance/tests/tribble/theme.liquid +++ /dev/null @@ -1,90 +0,0 @@ - - - - {{shop.name}} - {{page_title}} - - - {{ 'reset.css' | asset_url | stylesheet_tag }} - {{ 'style.css' | asset_url | stylesheet_tag }} - - {{ 'lightbox.css' | asset_url | stylesheet_tag }} - {{ 'prototype/1.6/prototype.js' | global_asset_url | script_tag }} - {{ 'scriptaculous/1.8.2/scriptaculous.js' | global_asset_url | script_tag }} - {{ 'lightbox.js' | asset_url | script_tag }} - {{ 'option_selection.js' | shopify_asset_url | script_tag }} - - {{ content_for_header }} - - - -
- -
-
-

Shopping Cart

-

- {% if cart.item_count == 0 %} - Your cart is currently empty - {% else %} - {{ cart.item_count }} {{ cart.item_count | pluralize: 'item', 'items' }} - Total: {{cart.total_price | money_with_currency }} - View Cart - {% endif %} -

-
- -
-

{{shop.name}}

-

Tribble: A Shopify Theme

- -
-
- - - - {{ content_for_layout }} - - - -
- - - diff --git a/performance/tests/vogue/article.liquid b/performance/tests/vogue/article.liquid deleted file mode 100644 index a7196fd..0000000 --- a/performance/tests/vogue/article.liquid +++ /dev/null @@ -1,66 +0,0 @@ -
-

{{ article.title }}

-

posted {{ article.created_at | date: "%Y %h" }} by {{ article.author }}

-
- {{ article.content }} -
-
- -{% if blog.comments_enabled? %} -
-

Comments

- - -
    - {% for comment in article.comments %} -
  • -
    - {{ comment.content }} -
    - -
    - Posted by {{ comment.author }} on {{ comment.created_at | date: "%B %d, %Y" }} -
    -
  • - {% endfor %} -
- - - {% form article %} -

Leave a comment

- - - {% if form.posted_successfully? %} - {% if blog.moderated? %} -
- Successfully posted your comment.
- It will have to be approved by the blog owner first before showing up. -
- {% else %} -
Successfully posted your comment.
- {% endif %} - {% endif %} - - {% if form.errors %} -
Not all the fields have been filled out correctly!
- {% endif %} - -
-
-
- -
-
- -
-
-
- - {% if blog.moderated? %} -

comments have to be approved before showing up

- {% endif %} - - - {% endform %} -
-{% endif %} diff --git a/performance/tests/vogue/blog.liquid b/performance/tests/vogue/blog.liquid deleted file mode 100644 index 8ce5583..0000000 --- a/performance/tests/vogue/blog.liquid +++ /dev/null @@ -1,32 +0,0 @@ -
-

{{page.title}}

- -{% paginate blog.articles by 20 %} - - {% for article in blog.articles %} -
-

- {{ article.title }} -

- -

- {% if blog.comments_enabled? %} - {{ article.comments_count }} comments - — - {% endif %} - posted {{ article.created_at | date: "%Y %h" }} by {{ article.author }}

-
- {{ article.content }} -
-
- {% endfor %} - - - -{% endpaginate %} - - -
-
diff --git a/performance/tests/vogue/cart.liquid b/performance/tests/vogue/cart.liquid deleted file mode 100644 index b997bd8..0000000 --- a/performance/tests/vogue/cart.liquid +++ /dev/null @@ -1,58 +0,0 @@ -

Shopping Cart

-{% if cart.item_count == 0 %} -

Your shopping basket is empty. Perhaps a featured item below is of interest...

- - {% tablerow product in collections.frontpage.products cols: 3 limit: 12 %} - - - {% endtablerow %} - -{% else %} - -
- - - - - - - - {% for item in cart.items %} - - - - - - - {% endfor %} -
Item DescriptionPriceQtyDeleteTotal
-
- {{ item.title | escape }} -
-
-

{{ item.title }}

- {{ item.product.description | strip_html | truncate: 120 }} -
-
{{ item.price | money }}{% if item.variant.compare_at_price > item.price %}
{{ item.variant.compare_at_price | money }}{% endif %}
Remove{{ item.line_price | money }}
-
-

Subtotal {{ cart.total_price | money }}

- - - {% if additional_checkout_buttons %} -
-

- or -

- {{ content_for_additional_checkout_buttons }} -
- {% endif %} -
-
{% endif %} diff --git a/performance/tests/vogue/collection.liquid b/performance/tests/vogue/collection.liquid deleted file mode 100644 index e1fa4c0..0000000 --- a/performance/tests/vogue/collection.liquid +++ /dev/null @@ -1,19 +0,0 @@ -{% paginate collection.products by 12 %}{% if collection.products.size == 0 %} - No products found in this collection.{% else %} -

{{ collection.title }}

- {{ collection.description }} - - {% tablerow product in collection.products cols: 3 %} - - - {% endtablerow %} - {% if paginate.pages > 1 %} -
- {{ paginate | default_pagination }} -
{% endif %}{% endif %} -{% endpaginate %} diff --git a/performance/tests/vogue/index.liquid b/performance/tests/vogue/index.liquid deleted file mode 100644 index 1cfc291..0000000 --- a/performance/tests/vogue/index.liquid +++ /dev/null @@ -1,22 +0,0 @@ -
- {% assign article = pages.frontpage %} - {% if article.content != "" %} -

{{ article.title }}

- {{ article.content }} - {% else %} - In Admin > Blogs & Pages, create a page with the handle frontpage and it will show up here.
- {{ "Learn more about handles" | link_to: "http://wiki.shopify.com/Handle" }} - {% endif %} -
- - - {% tablerow product in collections.frontpage.products cols: 3 limit: 12 %} - - - {% endtablerow %} - diff --git a/performance/tests/vogue/page.liquid b/performance/tests/vogue/page.liquid deleted file mode 100644 index 1ea6661..0000000 --- a/performance/tests/vogue/page.liquid +++ /dev/null @@ -1,3 +0,0 @@ -

{{ page.title }}

- {{ page.content }} - diff --git a/performance/tests/vogue/product.liquid b/performance/tests/vogue/product.liquid deleted file mode 100644 index a9a0ddc..0000000 --- a/performance/tests/vogue/product.liquid +++ /dev/null @@ -1,62 +0,0 @@ -
- {% for image in product.images %}{% if forloop.first %}
- {{ product.title | escape }} -
{% else %} -
- {{ product.title | escape }} -
{% endif %}{% endfor %} -
-
-

{{ product.title }}

- {{ product.description }} - - {% if product.available %} -
- -
-
- - -
- - -
- {% else %} -

This product is temporarily unavailable

- {% endif %} - -
- Continue Shopping
- Browse more {{ product.type | link_to_type }} or additional {{ product.vendor | link_to_vendor }} products. -
-
- - - - diff --git a/performance/tests/vogue/theme.liquid b/performance/tests/vogue/theme.liquid deleted file mode 100644 index 914c03f..0000000 --- a/performance/tests/vogue/theme.liquid +++ /dev/null @@ -1,122 +0,0 @@ - - - - -{{ shop.name }} — {{ page_title }} - - -{{ 'stylesheet.css' | asset_url | stylesheet_tag }} - - - -{{ 'mootools.js' | global_asset_url | script_tag }} -{{ 'slimbox.js' | global_asset_url | script_tag }} -{{ 'option_selection.js' | shopify_asset_url | script_tag }} - -{{ content_for_header }} - - - - - - - - -
-
-
{% if template == "search" %} -

Search Results

{% endif %} - {{ content_for_layout }} -
{% if template != "cart" %}{% if template != "product" %} - -
- {% if template == "index" %} - {% if blogs.news.articles.size > 1 %} - Subscribe -

More news

- - {% endif %} - {% endif %} - - {% if template == "collection" %} -

Collection Tags

-
{% if collection.tags.size == 0 %} - No tags found.{% else %} - {% for tag in collection.tags %}{% if current_tags contains tag %} {{ tag | highlight_active_tag | link_to_remove_tag: tag }}{% else %} {{ tag | highlight_active_tag | link_to_add_tag: tag }}{% endif %}{% unless forloop.last %}, {% endunless %}{% endfor %}{% endif %} -
- {% endif %} - -

Navigation

- - - {% if template != "page" %} -

Featured Products

- - {% endif %} -
{% endif %}{% endif %} -
-
- - - - - diff --git a/performance/theme_runner.rb b/performance/theme_runner.rb deleted file mode 100644 index 9b7a19a..0000000 --- a/performance/theme_runner.rb +++ /dev/null @@ -1,78 +0,0 @@ -# This profiler run simulates Shopify. -# We are looking in the tests directory for liquid files and render them within the designated layout file. -# We will also export a substantial database to liquid which the templates can render values of. -# All this is to make the benchmark as non syntetic as possible. All templates and tests are lifted from -# direct real-world usage and the profiler measures code that looks very similar to the way it looks in -# Shopify which is likely the biggest user of liquid in the world which something to the tune of several -# million Template#render calls a day. - -require File.dirname(__FILE__) + '/shopify/liquid' -require File.dirname(__FILE__) + '/shopify/database.rb' - -class ThemeRunner - class FileSystem - - def initialize(path) - @path = path - end - - # Called by Liquid to retrieve a template file - def read_template_file(template_path, context) - File.read(@path + '/' + template_path + '.liquid') - end - end - - # Load all templates into memory, do this now so that - # we don't profile IO. - def initialize - @tests = Dir[File.dirname(__FILE__) + '/tests/**/*.liquid'].collect do |test| - next if File.basename(test) == 'theme.liquid' - - theme_path = File.dirname(test) + '/theme.liquid' - - [File.read(test), (File.file?(theme_path) ? File.read(theme_path) : nil), test] - end.compact - end - - def compile - # Dup assigns because will make some changes to them - - @tests.each do |liquid, layout, template_name| - - tmpl = Liquid::Template.new - tmpl.parse(liquid) - tmpl = Liquid::Template.new - tmpl.parse(layout) - end - end - - def run - # Dup assigns because will make some changes to them - assigns = Database.tables.dup - - @tests.each do |liquid, layout, template_name| - - # Compute page_tempalte outside of profiler run, uninteresting to profiler - page_template = File.basename(template_name, File.extname(template_name)) - compile_and_render(liquid, layout, assigns, page_template, template_name) - - end - end - - - def compile_and_render(template, layout, assigns, page_template, template_file) - tmpl = Liquid::Template.new - tmpl.assigns['page_title'] = 'Page title' - tmpl.assigns['template'] = page_template - tmpl.registers[:file_system] = ThemeRunner::FileSystem.new(File.dirname(template_file)) - - content_for_layout = tmpl.parse(template).render!(assigns) - - if layout - assigns['content_for_layout'] = content_for_layout - tmpl.parse(layout).render!(assigns) - else - content_for_layout - end - end -end diff --git a/test/fixtures/en_locale.yml b/test/fixtures/en_locale.yml deleted file mode 100644 index 0b113c6..0000000 --- a/test/fixtures/en_locale.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- - simple: "less is more" - whatever: "something %{something}" - errors: - i18n: - undefined_interpolation: "undefined key %{key}" - unknown_translation: "translation '%{name}' wasn't found" - syntax: - oops: "something wasn't right" diff --git a/test/integration/assign_test.rb b/test/integration/assign_test.rb deleted file mode 100644 index f4edb9a..0000000 --- a/test/integration/assign_test.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'test_helper' - -class AssignTest < Test::Unit::TestCase - include Liquid - - def test_assigned_variable - assert_template_result('.foo.', - '{% assign foo = values %}.{{ foo[0] }}.', - 'values' => %w{foo bar baz}) - - assert_template_result('.bar.', - '{% assign foo = values %}.{{ foo[1] }}.', - 'values' => %w{foo bar baz}) - end - - def test_assign_with_filter - assert_template_result('.bar.', - '{% assign foo = values | split: "," %}.{{ foo[1] }}.', - 'values' => "foo,bar,baz") - end - - def test_assign_syntax_error - assert_match_syntax_error(/assign/, - '{% assign foo not values %}.', - 'values' => "foo,bar,baz") - end -end # AssignTest diff --git a/test/integration/blank_test.rb b/test/integration/blank_test.rb deleted file mode 100644 index e70f21c..0000000 --- a/test/integration/blank_test.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'test_helper' - -class FoobarTag < Liquid::Tag - def render(*args) - " " - end - - Liquid::Template.register_tag('foobar', FoobarTag) -end - -class BlankTestFileSystem - def read_template_file(template_path, context) - template_path - end -end - -class BlankTest < Test::Unit::TestCase - include Liquid - N = 10 - - def wrap_in_for(body) - "{% for i in (1..#{N}) %}#{body}{% endfor %}" - end - - def wrap_in_if(body) - "{% if true %}#{body}{% endif %}" - end - - def wrap(body) - wrap_in_for(body) + wrap_in_if(body) - end - - def test_new_tags_are_not_blank_by_default - assert_template_result(" "*N, wrap_in_for("{% foobar %}")) - end - - def test_loops_are_blank - assert_template_result("", wrap_in_for(" ")) - end - - def test_if_else_are_blank - assert_template_result("", "{% if true %} {% elsif false %} {% else %} {% endif %}") - end - - def test_unless_is_blank - assert_template_result("", wrap("{% unless true %} {% endunless %}")) - end - - def test_mark_as_blank_only_during_parsing - assert_template_result(" "*(N+1), wrap(" {% if false %} this never happens, but still, this block is not blank {% endif %}")) - end - - def test_comments_are_blank - assert_template_result("", wrap(" {% comment %} whatever {% endcomment %} ")) - end - - def test_captures_are_blank - assert_template_result("", wrap(" {% capture foo %} whatever {% endcapture %} ")) - end - - def test_nested_blocks_are_blank_but_only_if_all_children_are - assert_template_result("", wrap(wrap(" "))) - assert_template_result("\n but this is not "*(N+1), - wrap(%q{{% if true %} {% comment %} this is blank {% endcomment %} {% endif %} - {% if true %} but this is not {% endif %}})) - end - - def test_assigns_are_blank - assert_template_result("", wrap(' {% assign foo = "bar" %} ')) - end - - def test_whitespace_is_blank - assert_template_result("", wrap(" ")) - assert_template_result("", wrap("\t")) - end - - def test_whitespace_is_not_blank_if_other_stuff_is_present - body = " x " - assert_template_result(body*(N+1), wrap(body)) - end - - def test_increment_is_not_blank - assert_template_result(" 0"*2*(N+1), wrap("{% assign foo = 0 %} {% increment foo %} {% decrement foo %}")) - end - - def test_cycle_is_not_blank - assert_template_result(" "*((N+1)/2)+" ", wrap("{% cycle ' ', ' ' %}")) - end - - def test_raw_is_not_blank - assert_template_result(" "*(N+1), wrap(" {% raw %} {% endraw %}")) - end - - def test_include_is_blank - Liquid::Template.file_system = BlankTestFileSystem.new - assert_template_result "foobar"*(N+1), wrap("{% include 'foobar' %}") - assert_template_result " foobar "*(N+1), wrap("{% include ' foobar ' %}") - assert_template_result " "*(N+1), wrap(" {% include ' ' %} ") - end - - def test_case_is_blank - assert_template_result("", wrap(" {% assign foo = 'bar' %} {% case foo %} {% when 'bar' %} {% when 'whatever' %} {% else %} {% endcase %} ")) - assert_template_result("", wrap(" {% assign foo = 'else' %} {% case foo %} {% when 'bar' %} {% when 'whatever' %} {% else %} {% endcase %} ")) - assert_template_result(" x "*(N+1), wrap(" {% assign foo = 'else' %} {% case foo %} {% when 'bar' %} {% when 'whatever' %} {% else %} x {% endcase %} ")) - end -end diff --git a/test/integration/capture_test.rb b/test/integration/capture_test.rb deleted file mode 100644 index 0bc7327..0000000 --- a/test/integration/capture_test.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'test_helper' - -class CaptureTest < Test::Unit::TestCase - include Liquid - - def test_captures_block_content_in_variable - assert_template_result("test string", "{% capture 'var' %}test string{% endcapture %}{{var}}", {}) - end - - def test_capture_to_variable_from_outer_scope_if_existing - template_source = <<-END_TEMPLATE - {% assign var = '' %} - {% if true %} - {% capture var %}first-block-string{% endcapture %} - {% endif %} - {% if true %} - {% capture var %}test-string{% endcapture %} - {% endif %} - {{var}} - END_TEMPLATE - template = Template.parse(template_source) - rendered = template.render! - assert_equal "test-string", rendered.gsub(/\s/, '') - end - - def test_assigning_from_capture - template_source = <<-END_TEMPLATE - {% assign first = '' %} - {% assign second = '' %} - {% for number in (1..3) %} - {% capture first %}{{number}}{% endcapture %} - {% assign second = first %} - {% endfor %} - {{ first }}-{{ second }} - END_TEMPLATE - template = Template.parse(template_source) - rendered = template.render! - assert_equal "3-3", rendered.gsub(/\s/, '') - end -end # CaptureTest diff --git a/test/integration/context_test.rb b/test/integration/context_test.rb deleted file mode 100644 index 0f4255a..0000000 --- a/test/integration/context_test.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'test_helper' - -class ContextTest < Test::Unit::TestCase - include Liquid - - 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_has_key_will_not_add_an_error_for_missing_keys - Template.error_mode = :strict - - context = Context.new - - context.has_key?('unknown') - - assert_empty context.errors - end -end diff --git a/test/integration/drop_test.rb b/test/integration/drop_test.rb deleted file mode 100644 index 6edebec..0000000 --- a/test/integration/drop_test.rb +++ /dev/null @@ -1,245 +0,0 @@ -require 'test_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 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.to_s - 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 before_method(method) - method - end - - def size - 3 - end - - def first - 1 - end - - def count - 3 - end - - def min - 1 - end - - def max - 3 - end - - def each - yield 1 - yield 2 - yield 3 - end -end - -class RealEnumerableDrop < Liquid::Drop - include Enumerable - - def before_method(method) - method - 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_drop_does_only_respond_to_whitelisted_methods - assert_equal "", Liquid::Template.parse("{{ product.inspect }}").render!('product' => ProductDrop.new) - assert_equal "", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new) - assert_equal "", Liquid::Template.parse("{{ product.whatever }}").render!('product' => ProductDrop.new) - assert_equal "", Liquid::Template.parse('{{ product | map: "inspect" }}').render!('product' => ProductDrop.new) - assert_equal "", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render!('product' => ProductDrop.new) - assert_equal "", Liquid::Template.parse('{{ product | map: "whatever" }}').render!('product' => ProductDrop.new) - end - - def test_drops_respond_to_to_liquid - assert_equal "text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render!('product' => ProductDrop.new) - assert_equal "text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render!('product' => ProductDrop.new) - end - - def test_text_drop - output = Liquid::Template.parse( ' {{ product.texts.text }} ' ).render!('product' => ProductDrop.new) - assert_equal ' text1 ', output - end - - def test_unknown_method - output = Liquid::Template.parse( ' {{ product.catchall.unknown }} ' ).render!('product' => ProductDrop.new) - assert_equal ' method: unknown ', output - end - - def test_integer_argument_drop - output = Liquid::Template.parse( ' {{ product.catchall[8] }} ' ).render!('product' => ProductDrop.new) - assert_equal ' method: 8 ', 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_object_methods_not_allowed - [:dup, :clone, :singleton_class, :eval, :class_eval, :inspect].each do |method| - output = Liquid::Template.parse(" {{ product.#{method} }} ").render!('product' => ProductDrop.new) - assert_equal ' ', output - end - 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 - - def test_enumerable_drop_will_invoke_before_method_for_clashing_method_names - ["select", "each", "map", "cycle"].each do |method| - assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new) - assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new) - assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new) - assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new) - end - end - - def test_some_enumerable_methods_still_get_invoked - [ :count, :max ].each do |method| - assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new) - assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new) - assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new) - assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new) - end - - assert_equal "yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new) - - [ :min, :first ].each do |method| - assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new) - assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new) - assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new) - assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new) - end - end - - def test_empty_string_value_access - assert_equal '', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => '') - end - - def test_nil_value_access - assert_equal '', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => nil) - end - - def test_default_to_s_on_drops - assert_equal 'ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new) - assert_equal 'EnumerableDrop', Liquid::Template.parse('{{ collection }}').render!('collection' => EnumerableDrop.new) - end -end # DropsTest diff --git a/test/integration/error_handling_test.rb b/test/integration/error_handling_test.rb deleted file mode 100644 index 192285d..0000000 --- a/test/integration/error_handling_test.rb +++ /dev/null @@ -1,110 +0,0 @@ -require 'test_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 - - def exception - raise Exception, 'exception' - 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 - Liquid::Template.parse(' {% for a in b %} ... ') - end - end - - def test_unrecognized_operator - with_error_mode(:strict) do - assert_raise(SyntaxError) do - Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ') - end - end - end - - def test_lax_unrecognized_operator - assert_nothing_raised do - template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', :error_mode => :lax) - assert_equal ' Liquid error: Unknown operator =! ', template.render - assert_equal 1, template.errors.size - assert_equal Liquid::ArgumentError, template.errors.first.class - end - end - - def test_strict_error_messages - err = assert_raise(SyntaxError) do - Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', :error_mode => :strict) - end - assert_equal 'Unexpected character = in "1 =! 2"', err.message - - err = assert_raise(SyntaxError) do - Liquid::Template.parse('{{%%%}}', :error_mode => :strict) - end - assert_equal 'Unexpected character % in "{{%%%}}"', err.message - end - - def test_warnings - template = Liquid::Template.parse('{% if ~~~ %}{{%%%}}{% else %}{{ hello. }}{% endif %}', :error_mode => :warn) - assert_equal 3, template.warnings.size - assert_equal 'Unexpected character ~ in "~~~"', template.warnings[0].message - assert_equal 'Unexpected character % in "{{%%%}}"', template.warnings[1].message - assert_equal 'Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message - assert_equal '', template.render - end - - # Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError - def test_exceptions_propagate - assert_raise Exception do - template = Liquid::Template.parse( ' {{ errors.exception }} ' ) - template.render('errors' => ErrorDrop.new) - end - end -end # ErrorHandlingTest diff --git a/test/integration/filter_test.rb b/test/integration/filter_test.rb deleted file mode 100644 index 58c9251..0000000 --- a/test/integration/filter_test.rb +++ /dev/null @@ -1,125 +0,0 @@ -require 'test_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 - -module SubstituteFilter - def substitute(input, params={}) - input.gsub(/%\{(\w+)\}/) { |match| params[$1] } - 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_strip_html_ignore_comments_with_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 - - def test_nonexistent_filter_is_ignored - @context['var'] = 1000 - - assert_equal 1000, Variable.new("var | xyzzy").render(@context) - end - - def test_filter_with_keyword_arguments - @context['surname'] = 'john' - @context.add_filters(SubstituteFilter) - output = Variable.new(%! 'hello %{first_name}, %{last_name}' | substitute: first_name: surname, last_name: 'doe' !).render(@context) - assert_equal 'hello john, doe', output - 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 # FiltersTest diff --git a/test/integration/hash_ordering_test.rb b/test/integration/hash_ordering_test.rb deleted file mode 100644 index 3b77709..0000000 --- a/test/integration/hash_ordering_test.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'test_helper' - -module MoneyFilter - def money(input) - sprintf(' %d$ ', input) - end -end - -module CanadianMoneyFilter - def money(input) - sprintf(' %d$ CAD ', input) - end -end - -class HashOrderingTest < Test::Unit::TestCase - include Liquid - - def test_global_register_order - Template.register_filter(MoneyFilter) - Template.register_filter(CanadianMoneyFilter) - - assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, nil) - end - -end diff --git a/test/integration/output_test.rb b/test/integration/output_test.rb deleted file mode 100644 index 37a8745..0000000 --- a/test/integration/output_test.rb +++ /dev/null @@ -1,116 +0,0 @@ -require 'test_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_multiple_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 # OutputTest diff --git a/test/integration/parsing_quirks_test.rb b/test/integration/parsing_quirks_test.rb deleted file mode 100644 index 044b0b0..0000000 --- a/test/integration/parsing_quirks_test.rb +++ /dev/null @@ -1,87 +0,0 @@ -require 'test_helper' - -class ParsingQuirksTest < Test::Unit::TestCase - include Liquid - - def test_parsing_css - text = " div { font-weight: bold; } " - assert_equal text, Template.parse(text).render! - 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}}") - Template.parse("{{|test}}") - end - with_error_mode(:strict) do - assert_raise(SyntaxError) do - Template.parse("{{test |a|b|}}") - end - end - end - - def test_meaningless_parens_error - with_error_mode(:strict) do - assert_raise(SyntaxError) do - markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false" - Template.parse("{% if #{markup} %} YES {% endif %}") - end - end - end - - def test_unexpected_characters_syntax_error - with_error_mode(:strict) do - assert_raise(SyntaxError) do - markup = "true && false" - Template.parse("{% if #{markup} %} YES {% endif %}") - end - assert_raise(SyntaxError) do - markup = "false || true" - Template.parse("{% if #{markup} %} YES {% endif %}") - end - end - end - - def test_no_error_on_lax_empty_filter - assert_nothing_raised do - Template.parse("{{test |a|b|}}", :error_mode => :lax) - Template.parse("{{test}}", :error_mode => :lax) - Template.parse("{{|test|}}", :error_mode => :lax) - end - end - - def test_meaningless_parens_lax - with_error_mode(:lax) do - assigns = {'b' => 'bar', 'c' => 'baz'} - markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false" - assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}", assigns) - end - end - - def test_unexpected_characters_silently_eat_logic_lax - with_error_mode(:lax) do - markup = "true && false" - assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}") - markup = "false || true" - assert_template_result('',"{% if #{markup} %} YES {% endif %}") - end - end -end # ParsingQuirksTest diff --git a/test/integration/security_test.rb b/test/integration/security_test.rb deleted file mode 100644 index d512583..0000000 --- a/test/integration/security_test.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'test_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 - - def test_does_not_add_filters_to_symbol_table - current_symbols = Symbol.all_symbols - - test = %( {{ "some_string" | a_bad_filter }} ) - - template = Template.parse(test) - assert_equal [], (Symbol.all_symbols - current_symbols) - - template.render! - assert_equal [], (Symbol.all_symbols - current_symbols) - end - - def test_does_not_add_drop_methods_to_symbol_table - current_symbols = Symbol.all_symbols - - assigns = { 'drop' => Drop.new } - assert_equal "", Template.parse("{{ drop.custom_method_1 }}", assigns).render! - assert_equal "", Template.parse("{{ drop.custom_method_2 }}", assigns).render! - assert_equal "", Template.parse("{{ drop.custom_method_3 }}", assigns).render! - - assert_equal [], (Symbol.all_symbols - current_symbols) - end -end # SecurityTest diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb deleted file mode 100644 index 9486ff6..0000000 --- a/test/integration/standard_filter_test.rb +++ /dev/null @@ -1,316 +0,0 @@ -# encoding: utf-8 - -require 'test_helper' - -class Filters - include Liquid::StandardFilters -end - -class TestThing - def initialize - @foo = 0 - end - - def to_s - "woot: #{@foo}" - end - - def [](whatever) - to_s - end - - def to_liquid - @foo += 1 - self - end -end - -class TestDrop < Liquid::Drop - def test - "testfoo" - end -end - -class TestEnumerable < Liquid::Drop - include Enumerable - - def each(&block) - [ { "foo" => 1, "bar" => 2 }, { "foo" => 2, "bar" => 1 }, { "foo" => 3, "bar" => 3 } ].each(&block) - end -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_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') - assert_equal "测试...", @filters.truncate("测试测试测试测试", 5) - end - - def test_split - assert_equal ['12','34'], @filters.split('12~34', '~') - assert_equal ['A? ',' ,Z'], @filters.split('A? ~ ~ ~ ,Z', '~ ~ ~') - assert_equal ['A?Z'], @filters.split('A?Z', '~') - # Regexp works although Liquid does not support. - assert_equal ['A','Z'], @filters.split('AxZ', /x/) - end - - def test_escape - assert_equal '<strong>', @filters.escape('') - assert_equal '<strong>', @filters.h('') - end - - def test_escape_once - assert_equal '<strong>Hulk</strong>', @filters.escape_once('<strong>Hulk') - 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) - assert_equal "测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5) - end - - def test_strip_html - assert_equal 'test', @filters.strip_html("
test
") - assert_equal 'test', @filters.strip_html("
test
") - assert_equal '', @filters.strip_html("") - assert_equal '', @filters.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_numerical_vs_lexicographical_sort - assert_equal [2, 10], @filters.sort([10, 2]) - assert_equal [{"a" => 2}, {"a" => 10}], @filters.sort([{"a" => 10}, {"a" => 2}], "a") - assert_equal ["10", "2"], @filters.sort(["10", "2"]) - assert_equal [{"a" => "10"}, {"a" => "2"}], @filters.sort([{"a" => "10"}, {"a" => "2"}], "a") - end - - def test_reverse - assert_equal [4,3,2,1], @filters.reverse([1,2,3,4]) - 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_map_doesnt_call_arbitrary_stuff - assert_template_result "", '{{ "foo" | map: "__id__" }}' - assert_template_result "", '{{ "foo" | map: "inspect" }}' - end - - def test_map_calls_to_liquid - t = TestThing.new - assert_template_result "woot: 1", '{{ foo | map: "whatever" }}', "foo" => [t] - end - - def test_map_on_hashes - assert_template_result "4217", '{{ thing | map: "foo" | map: "bar" }}', - "thing" => { "foo" => [ { "bar" => 42 }, { "bar" => 17 } ] } - end - - def test_sort_calls_to_liquid - t = TestThing.new - assert_template_result "woot: 1", '{{ foo | sort: "whatever" }}', "foo" => [t] - end - - def test_map_over_proc - drop = TestDrop.new - p = Proc.new{ drop } - templ = '{{ procs | map: "test" }}' - assert_template_result "testfoo", templ, "procs" => [p] - end - - def test_map_works_on_enumerables - assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new - end - - def test_sort_works_on_enumerables - assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new - 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 "#{Date.today.year}", @filters.date('now', '%Y') - assert_equal "#{Date.today.year}", @filters.date('today', '%Y') - - assert_equal nil, @filters.date(nil, "%B") - - assert_equal "07/05/2006", @filters.date(1152098955, "%m/%d/%Y") - assert_equal "07/05/2006", @filters.date("1152098955", "%m/%d/%Y") - 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 '2 2 2 2', @filters.replace('1 1 1 1', '1', 2) - assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2) - assert_template_result '2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}" - 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_pipes_in_string_arguments - assert_template_result 'foobar', "{{ 'foo|bar' | remove: '|' }}" - end - - def test_strip - assert_template_result 'ab c', "{{ source | strip }}", 'source' => " ab c " - assert_template_result 'ab c', "{{ source | strip }}", 'source' => " \tab c \n \t" - end - - def test_lstrip - assert_template_result 'ab c ', "{{ source | lstrip }}", 'source' => " ab c " - assert_template_result "ab c \n \t", "{{ source | lstrip }}", 'source' => " \tab c \n \t" - end - - def test_rstrip - assert_template_result " ab c", "{{ source | rstrip }}", 'source' => " ab c " - assert_template_result " \tab c", "{{ source | rstrip }}", 'source' => " \tab c \n \t" - end - - def test_strip_newlines - assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\nb\nc" - assert_template_result 'abc', "{{ source | strip_newlines }}", 'source' => "a\r\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 "2.0", "{{ '1' | plus:'1.0' }}" - end - - def test_minus - assert_template_result "4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1 - assert_template_result "2.3", "{{ '4.3' | minus:'2' }}" - end - - def test_times - assert_template_result "12", "{{ 3 | times:4 }}" - assert_template_result "0", "{{ 'foo' | times:4 }}" - - assert_template_result "6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}" - - assert_template_result "7.25", "{{ 0.0725 | times:100 }}" - 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 }}" - assert_equal "Liquid error: divided by 0", Template.parse("{{ 5 | divided_by:0 }}").render - - assert_template_result "0.5", "{{ 2.0 | divided_by:4 }}" - end - - def test_modulo - assert_template_result "1", "{{ 3 | modulo:2 }}" - end - - def test_round - assert_template_result "5", "{{ input | round }}", 'input' => 4.6 - assert_template_result "4", "{{ '4.3' | round }}" - assert_template_result "4.56", "{{ input | round: 2 }}", 'input' => 4.5612 - end - - def test_ceil - assert_template_result "5", "{{ input | ceil }}", 'input' => 4.6 - assert_template_result "5", "{{ '4.3' | ceil }}" - end - - def test_floor - assert_template_result "4", "{{ input | floor }}", 'input' => 4.6 - assert_template_result "4", "{{ '4.3' | floor }}" - 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_default - assert_equal "foo", @filters.default("foo", "bar") - assert_equal "bar", @filters.default(nil, "bar") - assert_equal "bar", @filters.default("", "bar") - assert_equal "bar", @filters.default(false, "bar") - assert_equal "bar", @filters.default([], "bar") - assert_equal "bar", @filters.default({}, "bar") - end - - def test_cannot_access_private_methods - assert_template_result('a',"{{ 'a' | to_number }}") - end -end # StandardFiltersTest diff --git a/test/integration/tags/break_tag_test.rb b/test/integration/tags/break_tag_test.rb deleted file mode 100644 index cbe3095..0000000 --- a/test/integration/tags/break_tag_test.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'test_helper' - -class BreakTagTest < Test::Unit::TestCase - include Liquid - - # tests that no weird errors are raised if break is called outside of a - # block - def test_break_with_no_block - assigns = {'i' => 1} - markup = '{% break %}' - expected = '' - - assert_template_result(expected, markup, assigns) - end - -end diff --git a/test/integration/tags/continue_tag_test.rb b/test/integration/tags/continue_tag_test.rb deleted file mode 100644 index 5825a23..0000000 --- a/test/integration/tags/continue_tag_test.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'test_helper' - -class ContinueTagTest < Test::Unit::TestCase - include Liquid - - # tests that no weird errors are raised if continue is called outside of a - # block - def test_continue_with_no_block - assigns = {} - markup = '{% continue %}' - expected = '' - - assert_template_result(expected, markup, assigns) - end - -end diff --git a/test/integration/tags/for_tag_test.rb b/test/integration/tags/for_tag_test.rb deleted file mode 100644 index 120f7fa..0000000 --- a/test/integration/tags/for_tag_test.rb +++ /dev/null @@ -1,375 +0,0 @@ -require 'test_helper' - -class ThingWithValue < Liquid::Drop - def value - 3 - end -end - -class ForTagTest < Test::Unit::TestCase - include Liquid - - 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_reversed - assigns = {'array' => [ 1, 2, 3] } - assert_template_result('321','{%for item in array reversed %}{{item}}{%endfor%}',assigns) - 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_range - assert_template_result(' 1 2 3 ','{%for item in (1..foobar) %} {{item}} {%endfor%}', "foobar" => 3) - end - - def test_for_with_hash_value_range - foobar = { "value" => 3 } - assert_template_result(' 1 2 3 ','{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar) - end - - def test_for_with_drop_value_range - foobar = ThingWithValue.new - assert_template_result(' 1 2 3 ','{%for item in (1..foobar.value) %} {{item}} {%endfor%}', "foobar" => foobar) - 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_else - assert_template_result('+++', '{%for item in array%}+{%else%}-{%endfor%}', 'array'=>[1,2,3]) - assert_template_result('-', '{%for item in array%}+{%else%}-{%endfor%}', 'array'=>[]) - assert_template_result('-', '{%for item in array%}+{%else%}-{%endfor%}', 'array'=>nil) - 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_for_with_break - assigns = {'array' => {'items' => [1,2,3,4,5,6,7,8,9,10]}} - - markup = '{% for i in array.items %}{% break %}{% endfor %}' - expected = "" - assert_template_result(expected,markup,assigns) - - markup = '{% for i in array.items %}{{ i }}{% break %}{% endfor %}' - expected = "1" - assert_template_result(expected,markup,assigns) - - markup = '{% for i in array.items %}{% break %}{{ i }}{% endfor %}' - expected = "" - assert_template_result(expected,markup,assigns) - - markup = '{% for i in array.items %}{{ i }}{% if i > 3 %}{% break %}{% endif %}{% endfor %}' - expected = "1234" - assert_template_result(expected,markup,assigns) - - # tests to ensure it only breaks out of the local for loop - # and not all of them. - assigns = {'array' => [[1,2],[3,4],[5,6]] } - markup = '{% for item in array %}' + - '{% for i in item %}' + - '{% if i == 1 %}' + - '{% break %}' + - '{% endif %}' + - '{{ i }}' + - '{% endfor %}' + - '{% endfor %}' - expected = '3456' - assert_template_result(expected, markup, assigns) - - # test break does nothing when unreached - assigns = {'array' => {'items' => [1,2,3,4,5]}} - markup = '{% for i in array.items %}{% if i == 9999 %}{% break %}{% endif %}{{ i }}{% endfor %}' - expected = '12345' - assert_template_result(expected, markup, assigns) - end - - def test_for_with_continue - assigns = {'array' => {'items' => [1,2,3,4,5]}} - - markup = '{% for i in array.items %}{% continue %}{% endfor %}' - expected = "" - assert_template_result(expected,markup,assigns) - - markup = '{% for i in array.items %}{{ i }}{% continue %}{% endfor %}' - expected = "12345" - assert_template_result(expected,markup,assigns) - - markup = '{% for i in array.items %}{% continue %}{{ i }}{% endfor %}' - expected = "" - assert_template_result(expected,markup,assigns) - - markup = '{% for i in array.items %}{% if i > 3 %}{% continue %}{% endif %}{{ i }}{% endfor %}' - expected = "123" - assert_template_result(expected,markup,assigns) - - markup = '{% for i in array.items %}{% if i == 3 %}{% continue %}{% else %}{{ i }}{% endif %}{% endfor %}' - expected = "1245" - assert_template_result(expected,markup,assigns) - - # tests to ensure it only continues the local for loop and not all of them. - assigns = {'array' => [[1,2],[3,4],[5,6]] } - markup = '{% for item in array %}' + - '{% for i in item %}' + - '{% if i == 1 %}' + - '{% continue %}' + - '{% endif %}' + - '{{ i }}' + - '{% endfor %}' + - '{% endfor %}' - expected = '23456' - assert_template_result(expected, markup, assigns) - - # test continue does nothing when unreached - assigns = {'array' => {'items' => [1,2,3,4,5]}} - markup = '{% for i in array.items %}{% if i == 9999 %}{% continue %}{% endif %}{{ i }}{% endfor %}' - expected = '12345' - assert_template_result(expected, markup, assigns) - end - - def test_for_tag_string - # ruby 1.8.7 "String".each => Enumerator with single "String" element. - # ruby 1.9.3 no longer supports .each on String though we mimic - # the functionality for backwards compatibility - - assert_template_result('test string', - '{%for val in string%}{{val}}{%endfor%}', - 'string' => "test string") - - assert_template_result('test string', - '{%for val in string limit:1%}{{val}}{%endfor%}', - 'string' => "test string") - - assert_template_result('val-string-1-1-0-1-0-true-true-test string', - '{%for val in string%}' + - '{{forloop.name}}-' + - '{{forloop.index}}-' + - '{{forloop.length}}-' + - '{{forloop.index0}}-' + - '{{forloop.rindex}}-' + - '{{forloop.rindex0}}-' + - '{{forloop.first}}-' + - '{{forloop.last}}-' + - '{{val}}{%endfor%}', - 'string' => "test string") - end - - def test_blank_string_not_iterable - assert_template_result('', "{% for char in characters %}I WILL NOT BE OUTPUT{% endfor %}", 'characters' => '') - end - - def test_bad_variable_naming_in_for_loop - assert_raise(Liquid::SyntaxError) do - Liquid::Template.parse('{% for a/b in x %}{% endfor %}') - end - end - - def test_spacing_with_variable_naming_in_for_loop - expected = '12345' - template = '{% for item in items %}{{item}}{% endfor %}' - assigns = {'items' => [1,2,3,4,5]} - assert_template_result(expected, template, assigns) - end - - class LoaderDrop < Liquid::Drop - attr_accessor :each_called, :load_slice_called - - def initialize(data) - @data = data - end - - def each - @each_called = true - @data.each { |el| yield el } - end - - def load_slice(from, to) - @load_slice_called = true - @data[(from..to-1)] - end - end - - def test_iterate_with_each_when_no_limit_applied - loader = LoaderDrop.new([1,2,3,4,5]) - assigns = {'items' => loader} - expected = '12345' - template = '{% for item in items %}{{item}}{% endfor %}' - assert_template_result(expected, template, assigns) - assert loader.each_called - assert !loader.load_slice_called - end - - def test_iterate_with_load_slice_when_limit_applied - loader = LoaderDrop.new([1,2,3,4,5]) - assigns = {'items' => loader} - expected = '1' - template = '{% for item in items limit:1 %}{{item}}{% endfor %}' - assert_template_result(expected, template, assigns) - assert !loader.each_called - assert loader.load_slice_called - end - - def test_iterate_with_load_slice_when_limit_and_offset_applied - loader = LoaderDrop.new([1,2,3,4,5]) - assigns = {'items' => loader} - expected = '34' - template = '{% for item in items offset:2 limit:2 %}{{item}}{% endfor %}' - assert_template_result(expected, template, assigns) - assert !loader.each_called - assert loader.load_slice_called - end - - def test_iterate_with_load_slice_returns_same_results_as_without - loader = LoaderDrop.new([1,2,3,4,5]) - loader_assigns = {'items' => loader} - array_assigns = {'items' => [1,2,3,4,5]} - expected = '34' - template = '{% for item in items offset:2 limit:2 %}{{item}}{% endfor %}' - assert_template_result(expected, template, loader_assigns) - assert_template_result(expected, template, array_assigns) - end -end diff --git a/test/integration/tags/if_else_tag_test.rb b/test/integration/tags/if_else_tag_test.rb deleted file mode 100644 index afd12bd..0000000 --- a/test/integration/tags/if_else_tag_test.rb +++ /dev/null @@ -1,166 +0,0 @@ -require 'test_helper' - -class IfElseTagTest < 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_comparison_of_strings_containing_and_or_or - assert_nothing_raised do - awful_markup = "a == 'and' and b == 'or' and c == 'foo and bar' and d == 'bar or baz' and e == 'foo' and foo and bar" - assigns = {'a' => 'and', 'b' => 'or', 'c' => 'foo and bar', 'd' => 'bar or baz', 'e' => 'foo', 'foo' => true, 'bar' => true} - assert_template_result(' YES ',"{% if #{awful_markup} %} YES {% endif %}", assigns) - end - end - - def test_comparison_of_expressions_starting_with_and_or_or - assigns = {'order' => {'items_count' => 0}, 'android' => {'name' => 'Roy'}} - assert_nothing_raised do - assert_template_result( "YES", - "{% if android.name == 'Roy' %}YES{% endif %}", - assigns) - end - assert_nothing_raised do - assert_template_result( "YES", - "{% if order.items_count == 0 %}YES{% endif %}", - assigns) - end - 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_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 - - def test_operators_are_ignored_unless_isolated - Condition.operators['contains'] = :[] - - assert_template_result('yes', - %({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %})) - end - - def test_operators_are_whitelisted - assert_raise(SyntaxError) do - assert_template_result('', %({% if 1 or throw or or 1 %}yes{% endif %})) - end - end -end diff --git a/test/integration/tags/include_tag_test.rb b/test/integration/tags/include_tag_test.rb deleted file mode 100644 index e4fab37..0000000 --- a/test/integration/tags/include_tag_test.rb +++ /dev/null @@ -1,212 +0,0 @@ -require 'test_helper' - -class TestFileSystem - def read_template_file(template_path, context) - 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' %}" - - when "pick_a_source" - "from TestFileSystem" - - else - template_path - end - end -end - -class OtherFileSystem - def read_template_file(template_path, context) - 'from OtherFileSystem' - end -end - -class CountingFileSystem - attr_reader :count - def read_template_file(template_path, context) - @count ||= 0 - @count += 1 - 'from CountingFileSystem' - end -end - -class CustomInclude < Liquid::Tag - Syntax = /(#{Liquid::QuotedFragment}+)(\s+(?:with|for)\s+(#{Liquid::QuotedFragment}+))?/o - - def initialize(tag_name, markup, tokens) - markup =~ Syntax - @template_name = $1 - super - end - - def parse(tokens) - end - - def render(context) - @template_name[1..-2] - end -end - -class IncludeTagTest < Test::Unit::TestCase - include Liquid - - def setup - Liquid::Template.file_system = TestFileSystem.new - end - - def test_include_tag_looks_for_file_system_in_registers_first - assert_equal 'from OtherFileSystem', - Template.parse("{% include 'pick_a_source' %}").render!({}, :registers => {:file_system => OtherFileSystem.new}) - end - - - def test_include_tag_with - assert_template_result "Product: Draft 151cm ", - "{% include 'product' with products[0] %}", "products" => [ {'title' => 'Draft 151cm'}, {'title' => 'Element 155cm'} ] - end - - def test_include_tag_with_default_name - assert_template_result "Product: Draft 151cm ", - "{% include 'product' %}", "product" => {'title' => 'Draft 151cm'} - end - - def test_include_tag_for - assert_template_result "Product: Draft 151cm Product: Element 155cm ", - "{% include 'product' for products %}", "products" => [ {'title' => 'Draft 151cm'}, {'title' => 'Element 155cm'} ] - end - - def test_include_tag_with_local_variables - assert_template_result "Locale: test123 ", "{% include 'locale_variables' echo1: 'test123' %}" - end - - def test_include_tag_with_multiple_local_variables - assert_template_result "Locale: test123 test321", - "{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}" - end - - def test_include_tag_with_multiple_local_variables_from_context - assert_template_result "Locale: test123 test321", - "{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}", - 'echo1' => 'test123', 'more_echos' => { "echo2" => 'test321'} - end - - def test_nested_include_tag - assert_template_result "body body_detail", "{% include 'body' %}" - - assert_template_result "header body body_detail footer", "{% include 'nested_template' %}" - end - - def test_nested_include_with_variable - assert_template_result "Product: Draft 151cm details ", - "{% include 'nested_product_template' with product %}", "product" => {"title" => 'Draft 151cm'} - - assert_template_result "Product: Draft 151cm details Product: Element 155cm details ", - "{% include 'nested_product_template' for products %}", "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, context) - "-{% 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_backwards_compatability_support_for_overridden_read_template_file - infinite_file_system = Class.new do - def read_template_file(template_path) # testing only one argument here. - "- hi mom" - end - end - - Liquid::Template.file_system = infinite_file_system.new - - Template.parse("{% include 'hi_mom' %}").render! - end - - def test_dynamically_choosen_template - assert_template_result "Test123", "{% include template %}", "template" => 'Test123' - assert_template_result "Test321", "{% include template %}", "template" => 'Test321' - - assert_template_result "Product: Draft 151cm ", "{% include template for product %}", - "template" => 'product', 'product' => { 'title' => 'Draft 151cm'} - end - - def test_include_tag_caches_second_read_of_same_partial - file_system = CountingFileSystem.new - assert_equal 'from CountingFileSystemfrom CountingFileSystem', - Template.parse("{% include 'pick_a_source' %}{% include 'pick_a_source' %}").render!({}, :registers => {:file_system => file_system}) - assert_equal 1, file_system.count - end - - def test_include_tag_doesnt_cache_partials_across_renders - file_system = CountingFileSystem.new - assert_equal 'from CountingFileSystem', - Template.parse("{% include 'pick_a_source' %}").render!({}, :registers => {:file_system => file_system}) - assert_equal 1, file_system.count - - assert_equal 'from CountingFileSystem', - Template.parse("{% include 'pick_a_source' %}").render!({}, :registers => {:file_system => file_system}) - assert_equal 2, file_system.count - end - - def test_include_tag_within_if_statement - assert_template_result "foo_if_true", "{% if true %}{% include 'foo_if_true' %}{% endif %}" - end - - def test_custom_include_tag - original_tag = Liquid::Template.tags['include'] - Liquid::Template.tags['include'] = CustomInclude - begin - assert_equal "custom_foo", - Template.parse("{% include 'custom_foo' %}").render! - ensure - Liquid::Template.tags['include'] = original_tag - end - end - - def test_custom_include_tag_within_if_statement - original_tag = Liquid::Template.tags['include'] - Liquid::Template.tags['include'] = CustomInclude - begin - assert_equal "custom_foo_if_true", - Template.parse("{% if true %}{% include 'custom_foo_if_true' %}{% endif %}").render! - ensure - Liquid::Template.tags['include'] = original_tag - end - end - - def test_does_not_add_error_in_strict_mode_for_missing_variable - Liquid::Template.file_system = TestFileSystem.new - - a = Liquid::Template.parse(' {% include "nested_template" %}') - a.render! - assert_empty a.errors - end -end # IncludeTagTest diff --git a/test/integration/tags/increment_tag_test.rb b/test/integration/tags/increment_tag_test.rb deleted file mode 100644 index 6c673ff..0000000 --- a/test/integration/tags/increment_tag_test.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'test_helper' - -class IncrementTagTest < Test::Unit::TestCase - include Liquid - - def test_inc - assert_template_result('0','{%increment port %}', {}) - assert_template_result('0 1','{%increment port %} {%increment port%}', {}) - assert_template_result('0 0 1 2 1', - '{%increment port %} {%increment starboard%} ' + - '{%increment port %} {%increment port%} ' + - '{%increment starboard %}', {}) - end - - def test_dec - assert_template_result('9','{%decrement port %}', { 'port' => 10}) - assert_template_result('-1 -2','{%decrement port %} {%decrement port%}', {}) - assert_template_result('1 5 2 2 5', - '{%increment port %} {%increment starboard%} ' + - '{%increment port %} {%decrement port%} ' + - '{%decrement starboard %}', { 'port' => 1, 'starboard' => 5 }) - end - -end diff --git a/test/integration/tags/raw_tag_test.rb b/test/integration/tags/raw_tag_test.rb deleted file mode 100644 index 1583ef0..0000000 --- a/test/integration/tags/raw_tag_test.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'test_helper' - -class RawTagTest < Test::Unit::TestCase - include Liquid - - def test_tag_in_raw - assert_template_result '{% comment %} test {% endcomment %}', - '{% raw %}{% comment %} test {% endcomment %}{% endraw %}' - end - - def test_output_in_raw - assert_template_result '{{ test }}', '{% raw %}{{ test }}{% endraw %}' - end - - def test_open_tag_in_raw - assert_template_result ' Foobar {% invalid ', '{% raw %} Foobar {% invalid {% endraw %}' - assert_template_result ' Foobar invalid %} ', '{% raw %} Foobar invalid %} {% endraw %}' - assert_template_result ' Foobar {{ invalid ', '{% raw %} Foobar {{ invalid {% endraw %}' - assert_template_result ' Foobar invalid }} ', '{% raw %} Foobar invalid }} {% endraw %}' - assert_template_result ' Foobar {% invalid {% {% endraw ', '{% raw %} Foobar {% invalid {% {% endraw {% endraw %}' - assert_template_result ' Foobar {% {% {% ', '{% raw %} Foobar {% {% {% {% endraw %}' - assert_template_result ' test {% raw %} {% endraw %}', '{% raw %} test {% raw %} {% {% endraw %}endraw %}' - assert_template_result ' Foobar {{ invalid 1', '{% raw %} Foobar {{ invalid {% endraw %}{{ 1 }}' - end -end diff --git a/test/integration/tags/standard_tag_test.rb b/test/integration/tags/standard_tag_test.rb deleted file mode 100644 index acb9970..0000000 --- a/test/integration/tags/standard_tag_test.rb +++ /dev/null @@ -1,297 +0,0 @@ -require 'test_helper' - -class StandardTagTest < Test::Unit::TestCase - include Liquid - - 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('','{% comment %} 1 {% comment %} 2 {% endcomment %} 3 {% endcomment %}') - - assert_template_result('','{%comment%}{%blabla%}{%endcomment%}') - assert_template_result('','{% comment %}{% blabla %}{% endcomment %}') - assert_template_result('','{%comment%}{% endif %}{%endcomment%}') - assert_template_result('','{% comment %}{% endwhatever %}{% endcomment %}') - assert_template_result('','{% comment %}{% raw %} {{%%%%}} }} { {% endcomment %} {% comment {% endraw %} {% 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_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_template_result 'variable', '{% assign a = "variable"%}{{a}}' - end - - def test_assign_unassigned - assigns = { 'var' => 'content' } - assert_template_result('var2: var2:content', 'var2:{{var2}} {%assign var2 = var%} var2:{{var2}}', assigns) - end - - def test_assign_an_empty_string - assert_template_result '', '{% assign a = ""%}{{a}}' - end - - def test_assign_is_global - assert_template_result 'variable', '{%for i in (1..2) %}{% assign a = "variable"%}{% endfor %}{{a}}' - 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(' two','{%cycle "", "two"%} {%cycle "", "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_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 - - def test_multiline_tag - assert_template_result '0 1 2 3', "0{%\nfor i in (1..3)\n%} {{\ni\n}}{%\nendfor\n%}" - end -end # StandardTagTest diff --git a/test/integration/tags/statements_test.rb b/test/integration/tags/statements_test.rb deleted file mode 100644 index 63b004e..0000000 --- a/test/integration/tags/statements_test.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'test_helper' - -class StatementsTest < Test::Unit::TestCase - include Liquid - - def test_true_eql_true - text = ' {% if true == true %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text - end - - def test_true_not_eql_true - text = ' {% if true != true %} true {% else %} false {% endif %} ' - assert_template_result ' false ', text - end - - def test_true_lq_true - text = ' {% if 0 > 0 %} true {% else %} false {% endif %} ' - assert_template_result ' false ', text - end - - def test_one_lq_zero - text = ' {% if 1 > 0 %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text - end - - def test_zero_lq_one - text = ' {% if 0 < 1 %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text - end - - def test_zero_lq_or_equal_one - text = ' {% if 0 <= 0 %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text - end - - def test_zero_lq_or_equal_one_involving_nil - text = ' {% if null <= 0 %} true {% else %} false {% endif %} ' - assert_template_result ' false ', text - - - text = ' {% if 0 <= null %} true {% else %} false {% endif %} ' - assert_template_result ' false ', text - end - - def test_zero_lqq_or_equal_one - text = ' {% if 0 >= 0 %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text - end - - def test_strings - text = " {% if 'test' == 'test' %} true {% else %} false {% endif %} " - assert_template_result ' true ', text - end - - def test_strings_not_equal - text = " {% if 'test' != 'test' %} true {% else %} false {% endif %} " - assert_template_result ' false ', text - end - - def test_var_strings_equal - text = ' {% if var == "hello there!" %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text, 'var' => 'hello there!' - end - - def test_var_strings_are_not_equal - text = ' {% if "hello there!" == var %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text, 'var' => 'hello there!' - end - - def test_var_and_long_string_are_equal - text = " {% if var == 'hello there!' %} true {% else %} false {% endif %} " - assert_template_result ' true ', text, 'var' => 'hello there!' - end - - - def test_var_and_long_string_are_equal_backwards - text = " {% if 'hello there!' == var %} true {% else %} false {% endif %} " - assert_template_result ' true ', text, '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 %} ' - assert_template_result ' true ', text, 'array' => [] - end - - def test_is_not_collection_empty - text = ' {% if array == empty %} true {% else %} false {% endif %} ' - assert_template_result ' false ', text, 'array' => [1,2,3] - end - - def test_nil - text = ' {% if var == nil %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text, 'var' => nil - - text = ' {% if var == null %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text, 'var' => nil - end - - def test_not_nil - text = ' {% if var != nil %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text, 'var' => 1 - - text = ' {% if var != null %} true {% else %} false {% endif %} ' - assert_template_result ' true ', text, 'var' => 1 - end -end # StatementsTest diff --git a/test/integration/tags/table_row_test.rb b/test/integration/tags/table_row_test.rb deleted file mode 100644 index 79d621b..0000000 --- a/test/integration/tags/table_row_test.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'test_helper' - -class TableRowTest < Test::Unit::TestCase - include Liquid - - class ArrayDrop < Liquid::Drop - include Enumerable - - def initialize(array) - @array = array - end - - def each(&block) - @array.each(&block) - end - end - - def test_table_row - - 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_table_row_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_table_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 - - def test_quoted_fragment - assert_template_result("\n 1 2 3 \n 4 5 6 \n", - "{% tablerow n in collections.frontpage cols:3%} {{n}} {% endtablerow %}", - 'collections' => {'frontpage' => [1,2,3,4,5,6]}) - assert_template_result("\n 1 2 3 \n 4 5 6 \n", - "{% tablerow n in collections['frontpage'] cols:3%} {{n}} {% endtablerow %}", - 'collections' => {'frontpage' => [1,2,3,4,5,6]}) - - end - - def test_enumerable_drop - assert_template_result("\n 1 2 3 \n 4 5 6 \n", - '{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}', - 'numbers' => ArrayDrop.new([1,2,3,4,5,6])) - end - - def test_offset_and_limit - assert_template_result("\n 1 2 3 \n 4 5 6 \n", - '{% tablerow n in numbers cols:3 offset:1 limit:6%} {{n}} {% endtablerow %}', - 'numbers' => [0,1,2,3,4,5,6,7]) - end -end diff --git a/test/integration/tags/unless_else_tag_test.rb b/test/integration/tags/unless_else_tag_test.rb deleted file mode 100644 index 352b6d6..0000000 --- a/test/integration/tags/unless_else_tag_test.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'test_helper' - -class UnlessElseTagTest < 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 # UnlessElseTest diff --git a/test/integration/template_test.rb b/test/integration/template_test.rb deleted file mode 100644 index 54c1d42..0000000 --- a/test/integration/template_test.rb +++ /dev/null @@ -1,156 +0,0 @@ -require 'test_helper' - -class TemplateContextDrop < Liquid::Drop - def before_method(method) - method - end - - def foo - 'fizzbuzz' - end - - def baz - @context.registers['lulz'] - end -end - -class SomethingWithLength - def length - nil - end - - liquid_methods :length -end - -class ErroneousDrop < Liquid::Drop - def bad_method - raise 'ruby error in drop' - end -end - -class TemplateTest < Test::Unit::TestCase - include Liquid - - def test_instance_assigns_persist_on_same_template_object_between_parses - t = Template.new - assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render! - assert_equal 'from instance assigns', t.parse("{{ foo }}").render! - end - - def test_instance_assigns_persist_on_same_template_parsing_between_renders - t = Template.new.parse("{{ foo }}{% assign foo = 'foo' %}{{ foo }}") - assert_equal 'foo', t.render! - assert_equal 'foofoo', t.render! - end - - def test_custom_assigns_do_not_persist_on_same_template - t = Template.new - assert_equal 'from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns') - assert_equal '', t.parse("{{ foo }}").render! - end - - def test_custom_assigns_squash_instance_assigns - t = Template.new - assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render! - assert_equal 'from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns') - end - - def test_persistent_assigns_squash_instance_assigns - t = Template.new - assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render! - t.assigns['foo'] = 'from persistent assigns' - assert_equal 'from persistent assigns', t.parse("{{ foo }}").render! - end - - def test_lambda_is_called_once_from_persistent_assigns_over_multiple_parses_and_renders - t = Template.new - t.assigns['number'] = lambda { @global ||= 0; @global += 1 } - assert_equal '1', t.parse("{{number}}").render! - assert_equal '1', t.parse("{{number}}").render! - assert_equal '1', t.render! - @global = nil - end - - def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders - t = Template.new - assigns = {'number' => lambda { @global ||= 0; @global += 1 }} - assert_equal '1', t.parse("{{number}}").render!(assigns) - assert_equal '1', t.parse("{{number}}").render!(assigns) - assert_equal '1', t.render!(assigns) - @global = nil - end - - def test_resource_limits_works_with_custom_length_method - t = Template.parse("{% assign foo = bar %}") - t.resource_limits = { :render_length_limit => 42 } - assert_equal "", t.render!("bar" => SomethingWithLength.new) - end - - def test_resource_limits_render_length - t = Template.parse("0123456789") - t.resource_limits = { :render_length_limit => 5 } - assert_equal "Liquid error: Memory limits exceeded", t.render() - assert t.resource_limits[:reached] - t.resource_limits = { :render_length_limit => 10 } - assert_equal "0123456789", t.render!() - assert_not_nil t.resource_limits[:render_length_current] - end - - def test_resource_limits_render_score - t = Template.parse("{% for a in (1..10) %} {% for a in (1..10) %} foo {% endfor %} {% endfor %}") - t.resource_limits = { :render_score_limit => 50 } - assert_equal "Liquid error: Memory limits exceeded", t.render() - assert t.resource_limits[:reached] - t = Template.parse("{% for a in (1..100) %} foo {% endfor %}") - t.resource_limits = { :render_score_limit => 50 } - assert_equal "Liquid error: Memory limits exceeded", t.render() - assert t.resource_limits[:reached] - t.resource_limits = { :render_score_limit => 200 } - assert_equal (" foo " * 100), t.render!() - assert_not_nil t.resource_limits[:render_score_current] - end - - def test_resource_limits_assign_score - t = Template.parse("{% assign foo = 42 %}{% assign bar = 23 %}") - t.resource_limits = { :assign_score_limit => 1 } - assert_equal "Liquid error: Memory limits exceeded", t.render() - assert t.resource_limits[:reached] - t.resource_limits = { :assign_score_limit => 2 } - assert_equal "", t.render!() - assert_not_nil t.resource_limits[:assign_score_current] - end - - def test_resource_limits_aborts_rendering_after_first_error - t = Template.parse("{% for a in (1..100) %} foo1 {% endfor %} bar {% for a in (1..100) %} foo2 {% endfor %}") - t.resource_limits = { :render_score_limit => 50 } - assert_equal "Liquid error: Memory limits exceeded", t.render() - assert t.resource_limits[:reached] - end - - def test_resource_limits_hash_in_template_gets_updated_even_if_no_limits_are_set - t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}") - t.render!() - assert t.resource_limits[:assign_score_current] > 0 - assert t.resource_limits[:render_score_current] > 0 - assert t.resource_limits[:render_length_current] > 0 - end - - def test_can_use_drop_as_context - t = Template.new - t.registers['lulz'] = 'haha' - drop = TemplateContextDrop.new - assert_equal 'fizzbuzz', t.parse('{{foo}}').render!(drop) - assert_equal 'bar', t.parse('{{bar}}').render!(drop) - assert_equal 'haha', t.parse("{{baz}}").render!(drop) - end - - def test_render_bang_force_rethrow_errors_on_passed_context - context = Context.new({'drop' => ErroneousDrop.new}) - t = Template.new.parse('{{ drop.bad_method }}') - - e = assert_raises RuntimeError do - t.render!(context) - end - assert_equal 'ruby error in drop', e.message - end -end diff --git a/test/integration/variable_test.rb b/test/integration/variable_test.rb deleted file mode 100644 index 4bc2ba9..0000000 --- a/test/integration/variable_test.rb +++ /dev/null @@ -1,72 +0,0 @@ -require 'test_helper' - -class VariableTest < 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 - - def test_false_renders_as_false - assert_equal 'false', Template.parse("{{ foo }}").render!('foo' => false) - end - - def test_preset_assigns - template = Template.parse(%|{{ test }}|) - template.assigns['test'] = 'worked' - assert_equal 'worked', template.render! - end - - def test_reuse_parsed_template - template = Template.parse(%|{{ greeting }} {{ name }}|) - template.assigns['greeting'] = 'Goodbye' - assert_equal 'Hello Tobi', template.render!('greeting' => 'Hello', 'name' => 'Tobi') - assert_equal 'Hello ', template.render!('greeting' => 'Hello', 'unknown' => 'Tobi') - assert_equal 'Hello Brian', template.render!('greeting' => 'Hello', 'name' => 'Brian') - assert_equal 'Goodbye Brian', template.render!('name' => 'Brian') - assert_equal({'greeting'=>'Goodbye'}, template.assigns) - end - - def test_assigns_not_polluted_from_template - template = Template.parse(%|{{ test }}{% assign test = 'bar' %}{{ test }}|) - template.assigns['test'] = 'baz' - assert_equal 'bazbar', template.render! - assert_equal 'bazbar', template.render! - assert_equal 'foobar', template.render!('test' => 'foo') - assert_equal 'bazbar', template.render! - end - - def test_hash_with_default_proc - template = Template.parse(%|Hello {{ test }}|) - assigns = Hash.new { |h,k| raise "Unknown variable '#{k}'" } - assigns['test'] = 'Tobi' - assert_equal 'Hello Tobi', template.render!(assigns) - assigns.delete('test') - e = assert_raises(RuntimeError) { - template.render!(assigns) - } - assert_equal "Unknown variable 'test'", e.message - end - - def test_multiline_variable - assert_equal 'worked', Template.parse("{{\ntest\n}}").render!('test' => 'worked') - end -end diff --git a/test/test_helper.rb b/test/test_helper.rb deleted file mode 100644 index 3f02d42..0000000 --- a/test/test_helper.rb +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env ruby - -require 'test/unit' -require 'test/unit/assertions' -require 'spy/integration' - -$:.unshift(File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')) -require 'liquid.rb' - -mode = :strict -if env_mode = ENV['LIQUID_PARSER_MODE'] - puts "-- #{env_mode.upcase} ERROR MODE" - mode = env_mode.to_sym -end -Liquid::Template.error_mode = mode - - -module Test - module Unit - class TestCase - def fixture(name) - File.join(File.expand_path(File.dirname(__FILE__)), "fixtures", name) - end - end - - module Assertions - include Liquid - - def assert_template_result(expected, template, assigns = {}, message = nil) - assert_equal expected, Template.parse(template).render!(assigns) - end - - def assert_template_result_matches(expected, template, assigns = {}, message = nil) - return assert_template_result(expected, template, assigns, message) unless expected.is_a? Regexp - - assert_match expected, Template.parse(template).render!(assigns) - end - - def assert_match_syntax_error(match, template, registers = {}) - exception = assert_raise(Liquid::SyntaxError) { - Template.parse(template).render(assigns) - } - assert_match match, exception.message - end - - def with_error_mode(mode) - old_mode = Liquid::Template.error_mode - Liquid::Template.error_mode = mode - yield - Liquid::Template.error_mode = old_mode - end - end # Assertions - end # Unit -end # Test diff --git a/test/unit/block_unit_test.rb b/test/unit/block_unit_test.rb deleted file mode 100644 index ca46474..0000000 --- a/test/unit/block_unit_test.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'test_helper' - -class BlockUnitTest < 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 # VariableTest diff --git a/test/unit/condition_unit_test.rb b/test/unit/condition_unit_test.rb deleted file mode 100644 index c17bcf9..0000000 --- a/test/unit/condition_unit_test.rb +++ /dev/null @@ -1,127 +0,0 @@ -require 'test_helper' - -class ConditionUnitTest < 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' - # negative numbers - assert_evalutes_true '1', '>', '-1' - assert_evalutes_true '-1', '<', '1' - assert_evalutes_true '1.0', '>', '-1.0' - assert_evalutes_true '-1.0', '<', '1.0' - 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_contains_returns_false_for_nil_operands - @context = Liquid::Context.new - assert_evalutes_false "not_assigned", 'contains', '0' - assert_evalutes_false "0", 'contains', 'not_assigned' - 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 - - def test_left_or_right_may_contain_operators - @context = Liquid::Context.new - @context['one'] = @context['another'] = "gnomeslab-and-or-liquid" - - assert_evalutes_true "one", '==', "another" - 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 # ConditionTest diff --git a/test/unit/context_unit_test.rb b/test/unit/context_unit_test.rb deleted file mode 100644 index 0b2d0a4..0000000 --- a/test/unit/context_unit_test.rb +++ /dev/null @@ -1,486 +0,0 @@ -require 'test_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 ContextUnitTest < Test::Unit::TestCase - include Liquid - - def setup - @context = Liquid::Context.new - end - - def teardown - Spy.teardown - 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 - context.add_filters(filter) - assert_equal 'hi? hi!', context.invoke(:hi, 'hi?') - - context = Context.new - assert_equal 'hi?', context.invoke(:hi, 'hi?') - - context.add_filters(filter) - assert_equal 'hi? hi!', context.invoke(:hi, 'hi?') - - end - - def test_only_intended_filters_make_it_there - - filter = Module.new do - def hi(output) - output + ' hi!' - end - end - - context = Context.new - assert_equal "Wookie", context.invoke("hi", "Wookie") - - context.add_filters(filter) - assert_equal "Wookie hi!", context.invoke("hi", "Wookie") - 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 - - def test_use_empty_instead_of_any_in_interrupt_handling_to_avoid_lots_of_unnecessary_object_allocations - mock_any = Spy.on_instance_method(Array, :any?) - mock_empty = Spy.on_instance_method(Array, :empty?) - mock_has_interrupt = Spy.on(@context, :has_interrupt?).and_call_through - - @context.has_interrupt? - - refute mock_any.has_been_called? - assert mock_empty.has_been_called? - end - - def test_variable_lookup_caches_markup - mock_scan = Spy.on_instance_method(String, :scan).and_return(["string"]) - - @context['string'] = 'string' - @context['string'] - @context['string'] - - assert_equal 1, mock_scan.calls.size - end - -end # ContextTest diff --git a/test/unit/file_system_unit_test.rb b/test/unit/file_system_unit_test.rb deleted file mode 100644 index 31bccb2..0000000 --- a/test/unit/file_system_unit_test.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'test_helper' - -class FileSystemUnitTest < Test::Unit::TestCase - include Liquid - - def test_default - assert_raise(FileSystemError) do - BlankFileSystem.new.read_template_file("dummy", {'dummy'=>'smarty'}) - 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 - - def test_custom_template_filename_patterns - file_system = Liquid::LocalFileSystem.new("/some/path", "%s.html") - assert_equal "/some/path/mypartial.html" , file_system.full_path("mypartial") - assert_equal "/some/path/dir/mypartial.html", file_system.full_path("dir/mypartial") - end -end # FileSystemTest diff --git a/test/unit/i18n_unit_test.rb b/test/unit/i18n_unit_test.rb deleted file mode 100644 index cc05400..0000000 --- a/test/unit/i18n_unit_test.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'test_helper' - -class I18nUnitTest < Test::Unit::TestCase - include Liquid - - def setup - @i18n = I18n.new(fixture("en_locale.yml")) - end - - def test_simple_translate_string - assert_equal "less is more", @i18n.translate("simple") - end - - def test_nested_translate_string - assert_equal "something wasn't right", @i18n.translate("errors.syntax.oops") - end - - def test_single_string_interpolation - assert_equal "something different", @i18n.translate("whatever", :something => "different") - end - - # def test_raises_translation_error_on_undefined_interpolation_key - # assert_raise I18n::TranslationError do - # @i18n.translate("whatever", :oopstypos => "yes") - # end - # end - - def test_raises_unknown_translation - assert_raise I18n::TranslationError do - @i18n.translate("doesnt_exist") - end - end - - def test_sets_default_path_to_en - assert_equal I18n::DEFAULT_LOCALE, I18n.new.path - end -end diff --git a/test/unit/lexer_unit_test.rb b/test/unit/lexer_unit_test.rb deleted file mode 100644 index 7ff2bb1..0000000 --- a/test/unit/lexer_unit_test.rb +++ /dev/null @@ -1,48 +0,0 @@ -require 'test_helper' - -class LexerUnitTest < Test::Unit::TestCase - include Liquid - - def test_strings - tokens = Lexer.new(%! 'this is a test""' "wat 'lol'"!).tokenize - assert_equal [[:string,%!'this is a test""'!], [:string, %!"wat 'lol'"!], [:end_of_string]], tokens - end - - def test_integer - tokens = Lexer.new('hi 50').tokenize - assert_equal [[:id,'hi'], [:number, '50'], [:end_of_string]], tokens - end - - def test_float - tokens = Lexer.new('hi 5.0').tokenize - assert_equal [[:id,'hi'], [:number, '5.0'], [:end_of_string]], tokens - end - - def test_comparison - tokens = Lexer.new('== <> contains').tokenize - assert_equal [[:comparison,'=='], [:comparison, '<>'], [:comparison, 'contains'], [:end_of_string]], tokens - end - - def test_specials - tokens = Lexer.new('| .:').tokenize - assert_equal [[:pipe, '|'], [:dot, '.'], [:colon, ':'], [:end_of_string]], tokens - tokens = Lexer.new('[,]').tokenize - assert_equal [[:open_square, '['], [:comma, ','], [:close_square, ']'], [:end_of_string]], tokens - end - - def test_fancy_identifiers - tokens = Lexer.new('hi! five?').tokenize - assert_equal [[:id,'hi!'], [:id, 'five?'], [:end_of_string]], tokens - end - - def test_whitespace - tokens = Lexer.new("five|\n\t ==").tokenize - assert_equal [[:id,'five'], [:pipe, '|'], [:comparison, '=='], [:end_of_string]], tokens - end - - def test_unexpected_character - assert_raises(SyntaxError) do - Lexer.new("%").tokenize - end - end -end diff --git a/test/unit/module_ex_unit_test.rb b/test/unit/module_ex_unit_test.rb deleted file mode 100644 index 5cefb7f..0000000 --- a/test/unit/module_ex_unit_test.rb +++ /dev/null @@ -1,87 +0,0 @@ -require 'test_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 ModuleExUnitTest < 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_template_result 'allowedA', "{{ a.allowedA }}", 'a'=>@a - assert_template_result 'allowedB', "{{ a.chainedB.allowedB }}", 'a'=>@a - assert_template_result 'allowedC', "{{ a.chainedB.chainedC.allowedC }}", 'a'=>@a - assert_template_result 'another_allowedC', "{{ a.chainedB.chainedC.another_allowedC }}", 'a'=>@a - assert_template_result '', "{{ a.restricted }}", 'a'=>@a - assert_template_result '', "{{ a.unknown }}", 'a'=>@a - end -end # ModuleExTest diff --git a/test/unit/parser_unit_test.rb b/test/unit/parser_unit_test.rb deleted file mode 100644 index b10a176..0000000 --- a/test/unit/parser_unit_test.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'test_helper' - -class ParserUnitTest < Test::Unit::TestCase - include Liquid - - def test_consume - p = Parser.new("wat: 7") - assert_equal 'wat', p.consume(:id) - assert_equal ':', p.consume(:colon) - assert_equal '7', p.consume(:number) - end - - def test_jump - p = Parser.new("wat: 7") - p.jump(2) - assert_equal '7', p.consume(:number) - end - - def test_consume? - p = Parser.new("wat: 7") - assert_equal 'wat', p.consume?(:id) - assert_equal false, p.consume?(:dot) - assert_equal ':', p.consume(:colon) - assert_equal '7', p.consume?(:number) - end - - def test_id? - p = Parser.new("wat 6 Peter Hegemon") - assert_equal 'wat', p.id?('wat') - assert_equal false, p.id?('endgame') - assert_equal '6', p.consume(:number) - assert_equal 'Peter', p.id?('Peter') - assert_equal false, p.id?('Achilles') - end - - def test_look - p = Parser.new("wat 6 Peter Hegemon") - assert_equal true, p.look(:id) - assert_equal 'wat', p.consume(:id) - assert_equal false, p.look(:comparison) - assert_equal true, p.look(:number) - assert_equal true, p.look(:id, 1) - assert_equal false, p.look(:number, 1) - end - - def test_expressions - p = Parser.new("hi.there hi[5].! hi.there.bob") - assert_equal 'hi.there', p.expression - assert_equal 'hi[5].!', p.expression - assert_equal 'hi.there.bob', p.expression - - p = Parser.new("567 6.0 'lol' \"wut\"") - assert_equal '567', p.expression - assert_equal '6.0', p.expression - assert_equal "'lol'", p.expression - assert_equal '"wut"', p.expression - end - - def test_ranges - p = Parser.new("(5..7) (1.5..9.6) (young..old) (hi[5].wat..old)") - assert_equal '(5..7)', p.expression - assert_equal '(1.5..9.6)', p.expression - assert_equal '(young..old)', p.expression - assert_equal '(hi[5].wat..old)', p.expression - end - - def test_arguments - p = Parser.new("filter: hi.there[5], keyarg: 7") - assert_equal 'filter', p.consume(:id) - assert_equal ':', p.consume(:colon) - assert_equal 'hi.there[5]', p.argument - assert_equal ',', p.consume(:comma) - assert_equal 'keyarg: 7', p.argument - end - - def test_invalid_expression - assert_raises(SyntaxError) do - p = Parser.new("==") - p.expression - end - end -end diff --git a/test/unit/regexp_unit_test.rb b/test/unit/regexp_unit_test.rb deleted file mode 100644 index 808c28d..0000000 --- a/test/unit/regexp_unit_test.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'test_helper' - -class RegexpUnitTest < 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_tags - assert_equal ['', ''], ' '.scan(QuotedFragment) - assert_equal [''], ''.scan(QuotedFragment) - assert_equal ['', ''], %||.scan(QuotedFragment) - end - - def test_double_quoted_words - assert_equal ['arg1', 'arg2', '"arg 3"'], 'arg1 arg2 "arg 3"'.scan(QuotedFragment) - end - - def test_single_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 # RegexpTest diff --git a/test/unit/strainer_unit_test.rb b/test/unit/strainer_unit_test.rb deleted file mode 100644 index aa0296c..0000000 --- a/test/unit/strainer_unit_test.rb +++ /dev/null @@ -1,70 +0,0 @@ -require 'test_helper' - -class StrainerUnitTest < Test::Unit::TestCase - include Liquid - - module AccessScopeFilters - def public_filter - "public" - end - - def private_filter - "private" - end - private :private_filter - end - - Strainer.global_filter(AccessScopeFilters) - - def test_strainer - strainer = Strainer.create(nil) - assert_equal 5, strainer.invoke('size', 'input') - assert_equal "public", strainer.invoke("public_filter") - end - - def test_stainer_raises_argument_error - strainer = Strainer.create(nil) - assert_raises(Liquid::ArgumentError) do - strainer.invoke("public_filter", 1) - end - end - - def test_strainer_only_invokes_public_filter_methods - strainer = Strainer.create(nil) - assert_equal false, strainer.invokable?('__test__') - assert_equal false, strainer.invokable?('test') - assert_equal false, strainer.invokable?('instance_eval') - assert_equal false, strainer.invokable?('__send__') - assert_equal true, strainer.invokable?('size') # from the standard lib - end - - def test_strainer_returns_nil_if_no_filter_method_found - strainer = Strainer.create(nil) - assert_nil strainer.invoke("private_filter") - assert_nil strainer.invoke("undef_the_filter") - end - - def test_strainer_returns_first_argument_if_no_method_and_arguments_given - strainer = Strainer.create(nil) - assert_equal "password", strainer.invoke("undef_the_method", "password") - end - - def test_strainer_only_allows_methods_defined_in_filters - strainer = Strainer.create(nil) - assert_equal "1 + 1", strainer.invoke("instance_eval", "1 + 1") - assert_equal "puts", strainer.invoke("__send__", "puts", "Hi Mom") - assert_equal "has_method?", strainer.invoke("invoke", "has_method?", "invoke") - end - - def test_strainer_uses_a_class_cache_to_avoid_method_cache_invalidation - a, b = Module.new, Module.new - strainer = Strainer.create(nil, [a,b]) - assert_kind_of Strainer, strainer - assert_kind_of a, strainer - assert_kind_of b, strainer - Strainer.class_variable_get(:@@filters).each do |m| - assert_kind_of m, strainer - end - end - -end # StrainerTest diff --git a/test/unit/tag_unit_test.rb b/test/unit/tag_unit_test.rb deleted file mode 100644 index e9111aa..0000000 --- a/test/unit/tag_unit_test.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'test_helper' - -class TagUnitTest < Test::Unit::TestCase - include Liquid - - def test_tag - tag = Tag.parse('tag', [], [], {}) - assert_equal 'liquid::tag', tag.name - assert_equal '', tag.render(Context.new) - end -end diff --git a/test/unit/tags/case_tag_unit_test.rb b/test/unit/tags/case_tag_unit_test.rb deleted file mode 100644 index ddb8d83..0000000 --- a/test/unit/tags/case_tag_unit_test.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'test_helper' - -class CaseTagUnitTest < Test::Unit::TestCase - include Liquid - - def test_case_nodelist - template = Liquid::Template.parse('{% case var %}{% when true %}WHEN{% else %}ELSE{% endcase %}') - assert_equal ['WHEN', 'ELSE'], template.root.nodelist[0].nodelist - end -end diff --git a/test/unit/tags/for_tag_unit_test.rb b/test/unit/tags/for_tag_unit_test.rb deleted file mode 100644 index 07ac184..0000000 --- a/test/unit/tags/for_tag_unit_test.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'test_helper' - -class ForTagUnitTest < Test::Unit::TestCase - def test_for_nodelist - template = Liquid::Template.parse('{% for item in items %}FOR{% endfor %}') - assert_equal ['FOR'], template.root.nodelist[0].nodelist - end - - def test_for_else_nodelist - template = Liquid::Template.parse('{% for item in items %}FOR{% else %}ELSE{% endfor %}') - assert_equal ['FOR', 'ELSE'], template.root.nodelist[0].nodelist - end -end diff --git a/test/unit/tags/if_tag_unit_test.rb b/test/unit/tags/if_tag_unit_test.rb deleted file mode 100644 index 209e3c2..0000000 --- a/test/unit/tags/if_tag_unit_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'test_helper' - -class IfTagUnitTest < Test::Unit::TestCase - def test_if_nodelist - template = Liquid::Template.parse('{% if true %}IF{% else %}ELSE{% endif %}') - assert_equal ['IF', 'ELSE'], template.root.nodelist[0].nodelist - end -end diff --git a/test/unit/template_unit_test.rb b/test/unit/template_unit_test.rb deleted file mode 100644 index 41e024d..0000000 --- a/test/unit/template_unit_test.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'test_helper' - -class TemplateUnitTest < Test::Unit::TestCase - include Liquid - - def test_sets_default_localization_in_document - t = Template.new - t.parse('') - assert_instance_of I18n, t.root.options[:locale] - end - - def test_sets_default_localization_in_context_with_quick_initialization - t = Template.new - t.parse('{{foo}}', :locale => I18n.new(fixture("en_locale.yml"))) - - assert_instance_of I18n, t.root.options[:locale] - assert_equal fixture("en_locale.yml"), t.root.options[:locale].path - end - - def test_with_cache_classes_tags_returns_the_same_class - original_cache_setting = Liquid.cache_classes - Liquid.cache_classes = true - - original_klass = Class.new - Object.send(:const_set, :CustomTag, original_klass) - Template.register_tag('custom', CustomTag) - - Object.send(:remove_const, :CustomTag) - - new_klass = Class.new - Object.send(:const_set, :CustomTag, new_klass) - - assert Template.tags['custom'].equal?(original_klass) - ensure - Object.send(:remove_const, :CustomTag) - Template.tags.delete('custom') - Liquid.cache_classes = original_cache_setting - end - - def test_without_cache_classes_tags_reloads_the_class - original_cache_setting = Liquid.cache_classes - Liquid.cache_classes = false - - original_klass = Class.new - Object.send(:const_set, :CustomTag, original_klass) - Template.register_tag('custom', CustomTag) - - Object.send(:remove_const, :CustomTag) - - new_klass = Class.new - Object.send(:const_set, :CustomTag, new_klass) - - assert Template.tags['custom'].equal?(new_klass) - ensure - Object.send(:remove_const, :CustomTag) - Template.tags.delete('custom') - Liquid.cache_classes = original_cache_setting - end - - class FakeTag; end - - def test_tags_delete - Template.register_tag('fake', FakeTag) - assert_equal FakeTag, Template.tags['fake'] - - Template.tags.delete('fake') - assert_nil Template.tags['fake'] - end -end diff --git a/test/unit/tokenizer_unit_test.rb b/test/unit/tokenizer_unit_test.rb deleted file mode 100644 index e8f4a0d..0000000 --- a/test/unit/tokenizer_unit_test.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'test_helper' - -class TokenizerTest < Test::Unit::TestCase - def test_tokenize_strings - assert_equal [' '], tokenize(' ') - assert_equal ['hello world'], tokenize('hello world') - end - - def test_tokenize_variables - assert_equal ['{{funk}}'], tokenize('{{funk}}') - assert_equal [' ', '{{funk}}', ' '], tokenize(' {{funk}} ') - assert_equal [' ', '{{funk}}', ' ', '{{so}}', ' ', '{{brother}}', ' '], tokenize(' {{funk}} {{so}} {{brother}} ') - assert_equal [' ', '{{ funk }}', ' '], tokenize(' {{ funk }} ') - end - - def test_tokenize_blocks - assert_equal ['{%comment%}'], tokenize('{%comment%}') - assert_equal [' ', '{%comment%}', ' '], tokenize(' {%comment%} ') - - assert_equal [' ', '{%comment%}', ' ', '{%endcomment%}', ' '], tokenize(' {%comment%} {%endcomment%} ') - assert_equal [' ', '{% comment %}', ' ', '{% endcomment %}', ' '], tokenize(" {% comment %} {% endcomment %} ") - end - - private - - def tokenize(source) - Liquid::Template.new.send(:tokenize, source) - end -end diff --git a/test/unit/variable_unit_test.rb b/test/unit/variable_unit_test.rb deleted file mode 100644 index 4c1cfbc..0000000 --- a/test/unit/variable_unit_test.rb +++ /dev/null @@ -1,136 +0,0 @@ -require 'test_helper' - -class VariableUnitTest < 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 - - var = Variable.new("hello|replace:'foo','bar'|textileze") - assert_equal 'hello', var.name - assert_equal [["replace", ["'foo'", "'bar'"]], ["textileze", []]], var.filters - end - - def test_symbol - var = Variable.new("http://disney.com/logo.gif | image: 'med' ", :error_mode => :lax) - assert_equal "http://disney.com/logo.gif", var.name - assert_equal [["image",["'med'"]]], var.filters - end - - def test_string_to_filter - 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 - - def test_filter_with_keyword_arguments - var = Variable.new(%! hello | things: greeting: "world", farewell: 'goodbye'!) - assert_equal 'hello', var.name - assert_equal [['things',["greeting: \"world\"","farewell: 'goodbye'"]]], var.filters - end - - def test_lax_filter_argument_parsing - var = Variable.new(%! number_of_comments | pluralize: 'comment': 'comments' !, :error_mode => :lax) - assert_equal 'number_of_comments', var.name - assert_equal [['pluralize',["'comment'","'comments'"]]], var.filters - end - - def test_strict_filter_argument_parsing - with_error_mode(:strict) do - assert_raises(SyntaxError) do - Variable.new(%! number_of_comments | pluralize: 'comment': 'comments' !) - end - end - end -end