From 56f1aa9b4aa8501c820d6723d6089fdad06e4ee9 Mon Sep 17 00:00:00 2001 From: Cody Fauser Date: Sat, 24 Jan 2009 14:00:12 +0800 Subject: [PATCH 01/15] Fix LiquidView for Rails 2.2. Fix local assigns for all versions of Rails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tobias Lütke --- CHANGELOG | 2 ++ lib/extras/liquid_view.rb | 43 ++++++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a6a44b1..2fb88bc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Fix LiquidView for Rails 2.2. Fix local assigns for all versions of Rails + * Fixed gem install rake task * Improve Error encapsulation in liquid by maintaining a own set of exceptions instead of relying on ruby build ins diff --git a/lib/extras/liquid_view.rb b/lib/extras/liquid_view.rb index 5cce750..6b983be 100644 --- a/lib/extras/liquid_view.rb +++ b/lib/extras/liquid_view.rb @@ -5,32 +5,43 @@ # # ActionView::Base::register_template_handler :liquid, LiquidView class LiquidView + PROTECTED_ASSIGNS = %w( template_root response _session template_class action_name request_origin session template + _response url _request _cookies variables_added _flash params _headers request cookies + ignore_missing_templates flash _params logger before_filter_chain_aborted headers ) + PROTECTED_INSTANCE_VARIABLES = %w( @_request @controller @_first_render @_memoized__pick_template @view_paths + @helpers @assigns_added @template @_render_stack @template_format @assigns ) + + def self.call(template) + "LiquidView.new(self).render(template, local_assigns)" + end - def initialize(action_view) - @action_view = action_view + def initialize(view) + @view = view end - - def render(template, local_assigns_for_rails_less_than_2_1_0 = nil) - @action_view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8' - assigns = @action_view.assigns.dup + def render(template, local_assigns = nil) + @view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8' - # template is a Template object in Rails >=2.1.0, a source string previously. - if template.respond_to? :source - source = template.source - local_assigns = template.locals + # Rails 2.2 Template has source, but not locals + if template.respond_to?(:source) && !template.respond_to?(:locals) + assigns = (@view.instance_variables - PROTECTED_INSTANCE_VARIABLES).inject({}) do |hash, ivar| + hash[ivar[1..-1]] = @view.instance_variable_get(ivar) + hash + end else - source = template - local_assigns = local_assigns_for_rails_less_than_2_1_0 + assigns = @view.assigns.reject{ |k,v| PROTECTED_ASSIGNS.include?(k) } end - - if content_for_layout = @action_view.instance_variable_get("@content_for_layout") + + source = template.respond_to?(:source) ? template.source : template + local_assigns = (template.respond_to?(:locals) ? template.locals : local_assigns) || {} + + if content_for_layout = @view.instance_variable_get("@content_for_layout") assigns['content_for_layout'] = content_for_layout end - assigns.merge!(local_assigns) + assigns.merge!(local_assigns.stringify_keys) liquid = Liquid::Template.parse(source) - liquid.render(assigns, :filters => [@action_view.controller.master_helper_module], :registers => {:action_view => @action_view, :controller => @action_view.controller}) + liquid.render(assigns, :filters => [@view.controller.master_helper_module], :registers => {:action_view => @view, :controller => @view.controller}) end def compilable? From daadb1a2d609fe853c24a287314931bbc4d4b458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ku=C5=BAma?= Date: Mon, 13 Apr 2009 20:46:14 +0800 Subject: [PATCH 02/15] Ruby 1.9.1 bugfixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tobias Lütke --- CHANGELOG | 14 ++++++++------ lib/liquid/strainer.rb | 35 +++++++++++++++++------------------ lib/liquid/variable.rb | 8 +++----- liquid.gemspec | 4 ++-- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2fb88bc..02920ab 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Ruby 1.9.1 bugfixes + * Fix LiquidView for Rails 2.2. Fix local assigns for all versions of Rails * Fixed gem install rake task @@ -5,7 +7,7 @@ * 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. +* 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 @@ -24,17 +26,17 @@ * 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] + {{ '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 + end t = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {% endfor %} ' ) t.render('product' => ProductDrop.new ) diff --git a/lib/liquid/strainer.rb b/lib/liquid/strainer.rb index a857b6c..9fa769b 100644 --- a/lib/liquid/strainer.rb +++ b/lib/liquid/strainer.rb @@ -1,52 +1,51 @@ require 'set' module Liquid - - + parent_object = if defined? BlankObject BlankObject else Object end - # Strainer is the parent class for the filters system. - # New filters are mixed into the strainer class which is then instanciated for each liquid template render run. + # Strainer is the parent class for the filters system. + # New filters are mixed into the strainer class which is then instanciated for each liquid template render run. # - # One of the strainer's responsibilities is to keep malicious method calls out + # One of the strainer's responsibilities is to keep malicious method calls out class Strainer < parent_object #:nodoc: - INTERNAL_METHOD = /^__/ - @@required_methods = Set.new([:__send__, :__id__, :respond_to?, :extend, :methods, :class]) - + INTERNAL_METHOD = /^__/ + @@required_methods = Set.new([:__send__, :respond_to?, :extend, :methods, :class, :object_id]) + @@filters = {} - + def initialize(context) @context = context end - + def self.global_filter(filter) raise ArgumentError, "Passed filter is not a module" unless filter.is_a?(Module) @@filters[filter.name] = filter end - + def self.create(context) strainer = Strainer.new(context) @@filters.each { |k,m| strainer.extend(m) } strainer end - + def respond_to?(method, include_private = false) method_name = method.to_s return false if method_name =~ INTERNAL_METHOD return false if @@required_methods.include?(method_name) super end - - # remove all standard methods from the bucket so circumvent security - # problems - instance_methods.each do |m| - unless @@required_methods.include?(m.to_sym) + + # remove all standard methods from the bucket so circumvent security + # problems + instance_methods.each do |m| + unless @@required_methods.include?(m.to_sym) undef_method m end - end + end end end diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb index cf58518..fb39ed7 100644 --- a/lib/liquid/variable.rb +++ b/lib/liquid/variable.rb @@ -21,7 +21,7 @@ module Liquid @name = match[1] if markup.match(/#{FilterSeparator}\s*(.*)/) filters = Regexp.last_match(1).split(/#{FilterSeparator}/) - filters.each do |f| + filters.each do |f| if matches = f.match(/\s*(\w+)/) filtername = matches[1] filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*(#{QuotedFragment})/).flatten @@ -34,8 +34,7 @@ module Liquid def render(context) return '' if @name.nil? - output = context[@name] - @filters.inject(output) do |output, filter| + @filters.inject(context[@name]) do |output, filter| filterargs = filter[1].to_a.collect do |a| context[a] end @@ -45,7 +44,6 @@ module Liquid raise FilterNotFound, "Error - filter '#{filter[0]}' in '#{@markup.strip}' could not be found." end end - output end end -end \ No newline at end of file +end diff --git a/liquid.gemspec b/liquid.gemspec index cd57fa6..f7569ef 100644 --- a/liquid.gemspec +++ b/liquid.gemspec @@ -1,10 +1,10 @@ Gem::Specification.new do |s| s.name = %q{liquid} - s.version = "1.9.0" + s.version = "2.0.1" s.specification_version = 2 if s.respond_to? :specification_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Tobias Luetke"] - s.date = %q{2008-06-23} + s.date = %q{2009-04-13} s.description = %q{A secure non evaling end user template engine with aesthetic markup.} s.email = %q{tobi@leetsoft.com} s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"] From 37580976db0ced024dd7dfc4bef57ac19f5c0faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ku=C5=BAma?= Date: Mon, 13 Apr 2009 21:14:52 +0800 Subject: [PATCH 03/15] regenerated gemspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tobias Lütke --- liquid.gemspec | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/liquid.gemspec b/liquid.gemspec index f7569ef..773c797 100644 --- a/liquid.gemspec +++ b/liquid.gemspec @@ -1,7 +1,7 @@ Gem::Specification.new do |s| s.name = %q{liquid} s.version = "2.0.1" - s.specification_version = 2 if s.respond_to? :specification_version= + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Tobias Luetke"] s.date = %q{2009-04-13} @@ -14,6 +14,16 @@ Gem::Specification.new do |s| s.rdoc_options = ["--main", "README.txt"] s.require_paths = ["lib"] s.rubyforge_project = %q{liquid} - s.rubygems_version = %q{1.2.0} + s.rubygems_version = %q{1.3.1} s.summary = %q{A secure non evaling end user template engine with aesthetic markup.} + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 2 + + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + else + end + else + end end From 8d278648455094d82e42a72c1c783d0929ae1b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ku=C5=BAma?= Date: Tue, 14 Apr 2009 16:05:03 +0800 Subject: [PATCH 04/15] Ruby 1.9.1 bugfixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tobias Lütke --- lib/liquid/condition.rb | 63 +++++------ lib/liquid/context.rb | 2 +- lib/liquid/drop.rb | 35 +++--- test/context_test.rb | 21 ++-- test/standard_tag_test.rb | 232 +++++++++++++++++++------------------- 5 files changed, 175 insertions(+), 178 deletions(-) diff --git a/lib/liquid/condition.rb b/lib/liquid/condition.rb index ac30f74..69224ab 100644 --- a/lib/liquid/condition.rb +++ b/lib/liquid/condition.rb @@ -3,7 +3,7 @@ module Liquid # # Example: # - # c = Condition.new('1', '==', '1') + # c = Condition.new('1', '==', '1') # c.evaluate #=> true # class Condition #:nodoc: @@ -17,33 +17,33 @@ module Liquid '<=' => :<=, 'contains' => lambda { |cond, left, right| left.include?(right) }, } - + def self.operators @@operators end attr_reader :attachment attr_accessor :left, :operator, :right - + def initialize(left = nil, operator = nil, right = nil) @left, @operator, @right = left, operator, right @child_relation = nil @child_condition = nil end - + def evaluate(context = Context.new) - result = interpret_condition(left, right, operator, context) - + result = interpret_condition(left, right, operator, context) + case @child_relation - when :or + when :or result || @child_condition.evaluate(context) - when :and + when :and result && @child_condition.evaluate(context) else result - end - end - + end + end + def or(condition) @child_relation, @child_condition = :or, condition end @@ -51,25 +51,25 @@ module Liquid def and(condition) @child_relation, @child_condition = :and, condition end - + def attach(attachment) @attachment = attachment end - + def else? false - end - + 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) + return right.send(left.to_s) else return nil end @@ -77,47 +77,44 @@ module Liquid if right.is_a?(Symbol) if left.respond_to?(right) - return left.send(right.to_s) + return left.send(right.to_s) else return nil end end - left == right - 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 + # 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 + 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) + elsif left.respond_to?(operation) and right.respond_to?(operation) left.send(operation, right) else nil end - end - end + end + end class ElseCondition < Condition - - def else? + def else? true end - + def evaluate(context) true end end -end \ No newline at end of file +end diff --git a/lib/liquid/context.rb b/lib/liquid/context.rb index d1ca109..603cd55 100644 --- a/lib/liquid/context.rb +++ b/lib/liquid/context.rb @@ -224,7 +224,7 @@ module Liquid object end - + def filtered_variable(markup) Variable.new(markup).render(self) end diff --git a/lib/liquid/drop.rb b/lib/liquid/drop.rb index 7b531b5..3accfd8 100644 --- a/lib/liquid/drop.rb +++ b/lib/liquid/drop.rb @@ -1,9 +1,9 @@ module Liquid - + # A drop in liquid is a class which allows you to to export DOM like things to liquid - # Methods of drops are callable. - # The main use for liquid drops is the implement lazy loaded objects. - # If you would like to make data available to the web designers which you don't want loaded unless needed then + # Methods of drops are callable. + # The main use for liquid drops is the implement lazy loaded objects. + # If you would like to make data available to the web designers which you don't want loaded unless needed then # a drop is a great way to do that # # Example: @@ -13,38 +13,39 @@ module Liquid # 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 + # tmpl = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {%endfor%} ' ) + # tmpl.render('product' => ProductDrop.new ) # will invoke top_sales query. + # + # Your drop can either implement the methods sans any parameters or implement the before_method(name) method which is a # catch all class Drop attr_writer :context - # Catch all for the method + # Catch all for the method def before_method(method) nil end - + # called by liquid to invoke a drop - def invoke_drop(method) - if self.class.public_instance_methods.include?(method.to_s) - send(method.to_sym) - else + def invoke_drop(method) + # for backward compatibility with Ruby 1.8 + methods = self.class.public_instance_methods.map { |m| m.to_s } + if methods.include?(method.to_s) + send(method.to_sym) + else before_method(method) end end - + def has_key?(name) true end - + def to_liquid self end alias :[] :invoke_drop end - end diff --git a/test/context_test.rb b/test/context_test.rb index e998e29..5c23376 100644 --- a/test/context_test.rb +++ b/test/context_test.rb @@ -190,9 +190,10 @@ class ContextTest < Test::Unit::TestCase end context = Context.new(@template) - methods = context.strainer.methods + methods_before = context.strainer.methods.map { |method| method.to_s } context.add_filters(filter) - assert_equal (methods + ['hi']).sort, context.strainer.methods.sort + methods_after = context.strainer.methods.map { |method| method.to_s } + assert_equal (methods_before + ["hi"]).sort, methods_after.sort end def test_add_item_in_outer_scope @@ -289,11 +290,9 @@ class ContextTest < Test::Unit::TestCase 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'] @@ -417,25 +416,25 @@ class ContextTest < Test::Unit::TestCase end def test_lambda_as_variable - @context['dynamic'] = lambda { 'Hello' } + @context['dynamic'] = proc { 'Hello' } assert_equal 'Hello', @context['dynamic'] end def test_nested_lambda_as_variable - @context['dynamic'] = { "lambda" => lambda { 'Hello' } } + @context['dynamic'] = { "lambda" => proc { 'Hello' } } assert_equal 'Hello', @context['dynamic.lambda'] end def test_array_containing_lambda_as_variable - @context['dynamic'] = [1,2, lambda { 'Hello' } ,4,5] + @context['dynamic'] = [1,2, proc { 'Hello' } ,4,5] assert_equal 'Hello', @context['dynamic[2]'] end def test_lambda_is_called_once - @context['callcount'] = lambda { @global ||= 0; @global += 1; @global.to_s } + @context['callcount'] = proc { @global ||= 0; @global += 1; @global.to_s } assert_equal '1', @context['callcount'] assert_equal '1', @context['callcount'] @@ -445,7 +444,7 @@ class ContextTest < Test::Unit::TestCase end def test_nested_lambda_is_called_once - @context['callcount'] = { "lambda" => lambda { @global ||= 0; @global += 1; @global.to_s } } + @context['callcount'] = { "lambda" => proc { @global ||= 0; @global += 1; @global.to_s } } assert_equal '1', @context['callcount.lambda'] assert_equal '1', @context['callcount.lambda'] @@ -455,7 +454,7 @@ class ContextTest < Test::Unit::TestCase end def test_lambda_in_array_is_called_once - @context['callcount'] = [1,2, lambda { @global ||= 0; @global += 1; @global.to_s } ,4,5] + @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]'] @@ -467,7 +466,7 @@ class ContextTest < Test::Unit::TestCase def test_access_to_context_from_proc @context.registers[:magic] = 345392 - @context['magic'] = lambda { @context.registers[:magic] } + @context['magic'] = proc { @context.registers[:magic] } assert_equal 345392, @context['magic'] end diff --git a/test/standard_tag_test.rb b/test/standard_tag_test.rb index 2ce32e2..9e095cc 100644 --- a/test/standard_tag_test.rb +++ b/test/standard_tag_test.rb @@ -3,14 +3,14 @@ require File.dirname(__FILE__) + '/helper' class StandardTagTest < Test::Unit::TestCase include Liquid - - - def test_tag + + + def test_tag tag = Tag.new('tag', [], []) - assert_equal 'liquid::tag', tag.name - assert_equal '', tag.render(Context.new) + assert_equal 'liquid::tag', tag.name + assert_equal '', tag.render(Context.new) end - + def test_no_transform assert_template_result('this text should come out of the template without change...', 'this text should come out of the template without change...') @@ -18,60 +18,60 @@ class StandardTagTest < Test::Unit::TestCase assert_template_result('','') assert_template_result('|,.:','|,.:') assert_template_result('','') - + text = %|this shouldnt see any transformation either but has multiple lines as you can clearly see here ...| assert_template_result(text,text) end - + def test_has_a_block_which_does_nothing assert_template_result(%|the comment block should be removed .. right?|, %|the comment block should be removed {%comment%} be gone.. {%endcomment%} .. right?|) - + assert_template_result('','{%comment%}{%endcomment%}') assert_template_result('','{%comment%}{% endcomment %}') assert_template_result('','{% comment %}{%endcomment%}') assert_template_result('','{% comment %}{% endcomment %}') assert_template_result('','{%comment%}comment{%endcomment%}') assert_template_result('','{% comment %}comment{% endcomment %}') - + assert_template_result('foobar','foo{%comment%}comment{%endcomment%}bar') assert_template_result('foobar','foo{% comment %}comment{% endcomment %}bar') assert_template_result('foobar','foo{%comment%} comment {%endcomment%}bar') assert_template_result('foobar','foo{% comment %} comment {% endcomment %}bar') - + assert_template_result('foo bar','foo {%comment%} {%endcomment%} bar') assert_template_result('foo bar','foo {%comment%}comment{%endcomment%} bar') assert_template_result('foo bar','foo {%comment%} comment {%endcomment%} bar') - + assert_template_result('foobar','foo{%comment%} {%endcomment%}bar') end - + def test_for assert_template_result(' yo yo yo yo ','{%for item in array%} yo {%endfor%}','array' => [1,2,3,4]) assert_template_result('yoyo','{%for item in array%}yo{%endfor%}','array' => [1,2]) assert_template_result(' yo ','{%for item in array%} yo {%endfor%}','array' => [1]) assert_template_result('','{%for item in array%}{%endfor%}','array' => [1,2]) expected = < [1,2,3]) end - + def test_for_with_range - assert_template_result(' 1 2 3 ','{%for item in (1..3) %} {{item}} {%endfor%}') + assert_template_result(' 1 2 3 ','{%for item in (1..3) %} {{item}} {%endfor%}') end def test_for_with_variable @@ -82,7 +82,7 @@ HERE 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) @@ -93,41 +93,41 @@ HERE assert_template_result(' true false false ','{%for item in array%} {{forloop.first}} {%endfor%}',assigns) assert_template_result(' false false true ','{%for item in array%} {{forloop.last}} {%endfor%}',assigns) end - + def test_for_and_if assigns = {'array' => [1,2,3] } assert_template_result('+--', '{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}', assigns) end - + def test_for_with_filtered_expressions assert_template_result('abc','{% for letter in letters|sort %}{{ letter }}{% endfor %}', 'letters' => %w{c b a}) end - + def test_limiting assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]} assert_template_result('12','{%for i in array limit:2 %}{{ i }}{%endfor%}',assigns) assert_template_result('1234','{%for i in array limit:4 %}{{ i }}{%endfor%}',assigns) assert_template_result('3456','{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}',assigns) - assert_template_result('3456','{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}',assigns) + 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) + 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 @@ -146,7 +146,7 @@ HERE 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 @@ -165,7 +165,7 @@ HERE 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 @@ -184,8 +184,8 @@ HERE 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%} @@ -200,19 +200,19 @@ HERE ) assert_template_result(expected,markup,assigns) end - + def test_assign assigns = {'var' => 'content' } assert_template_result('var2: var2:content','var2:{{var2}} {%assign var2 = var%} var2:{{var2}}',assigns) - + end def test_hyphenated_assign assigns = {'a-b' => '1' } assert_template_result('a-b:1 a-b:2','a-b:{{a-b}} {%assign a-b = 2 %}a-b:{{a-b}}',assigns) - + end - + def test_assign_with_colon_and_spaces assigns = {'var' => {'a:b c' => {'paged' => '1' }}} assert_template_result('var2: 1','{%assign var2 = var["a:b c"].paged %}var2: {{var2}}',assigns) @@ -230,62 +230,62 @@ HERE end def test_case - assigns = {'condition' => 2 } + 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) + 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) + 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) + 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) + 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) - + 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) - + 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 - + 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]) + 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 %}', {}) + 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 + + 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) @@ -303,12 +303,12 @@ HERE 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' }) + assert_template_result('', code, {'condition' => 'something else' }) end def test_case_when_comma @@ -318,82 +318,82 @@ HERE 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' }) + assert_template_result('', code, {'condition' => 'something else' }) end - + def test_assign - assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{{a}}' ).render + assert_equal 'variable', Liquid::Template.parse( '{% assign a = "variable"%}{{a}}' ).render end - + def test_assign_is_global - assert_equal 'variable', Liquid::Template.parse( '{%for i in (1..2) %}{% assign a = "variable"%}{% endfor %}{{a}}' ).render - end - + assert_equal 'variable', Liquid::Template.parse( '{%for i in (1..2) %}{% assign a = "variable"%}{% endfor %}{{a}}' ).render + end + def test_case_detects_bad_syntax assert_raise(SyntaxError) do - assert_template_result('', '{% case false %}{% when %}true{% endcase %}', {}) + assert_template_result('', '{% case false %}{% when %}true{% endcase %}', {}) end assert_raise(SyntaxError) do - assert_template_result('', '{% case false %}{% huh %}true{% endcase %}', {}) + assert_template_result('', '{% case false %}{% huh %}true{% endcase %}', {}) end - + end - - - + + + def test_cycle - assert_template_result('one','{%cycle "one", "two"%}') - assert_template_result('one two','{%cycle "one", "two"%} {%cycle "one", "two"%}') - - assert_template_result('one two one','{%cycle "one", "two"%} {%cycle "one", "two"%} {%cycle "one", "two"%}') - - assert_template_result('text-align: left text-align: right','{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}') - + assert_template_result('one','{%cycle "one", "two"%}') + assert_template_result('one two','{%cycle "one", "two"%} {%cycle "one", "two"%}') + + assert_template_result('one two one','{%cycle "one", "two"%} {%cycle "one", "two"%} {%cycle "one", "two"%}') + + assert_template_result('text-align: left text-align: right','{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}') + end - + def test_multiple_cycles - assert_template_result('1 2 1 1 2 3 1','{%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%}') + 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" %}') + 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) + 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) + assert_template_result('array has 4 elements', "array has {{ array.size }} elements", assigns) end def test_size_of_hash assigns = {"hash" => {:a => 1, :b => 2, :c=> 3, :d => 4}} - assert_template_result('hash has 4 elements', "hash has {{ hash.size }} elements", assigns) - end - - def test_illegal_symbols - assert_template_result('', '{% if true == empty %}?{% endif %}', {}) - assert_template_result('', '{% if true == null %}?{% endif %}', {}) - assert_template_result('', '{% if empty == true %}?{% endif %}', {}) - assert_template_result('', '{% if null == true %}?{% endif %}', {}) - end - - def test_for_reversed - assigns = {'array' => [ 1, 2, 3] } - assert_template_result('321','{%for item in array reversed %}{{item}}{%endfor%}',assigns) + assert_template_result('hash has 4 elements', "hash has {{ hash.size }} elements", assigns) end - + def test_illegal_symbols + assert_template_result('', '{% if true == empty %}?{% endif %}', {}) + assert_template_result('', '{% if true == null %}?{% endif %}', {}) + assert_template_result('', '{% if empty == true %}?{% endif %}', {}) + assert_template_result('', '{% if null == true %}?{% endif %}', {}) + end + + def test_for_reversed + assigns = {'array' => [ 1, 2, 3] } + assert_template_result('321','{%for item in array reversed %}{{item}}{%endfor%}',assigns) + end + + def test_ifchanged assigns = {'array' => [ 1, 1, 2, 2, 3, 3] } assert_template_result('123','{%for item in array%}{%ifchanged%}{{item}}{% endifchanged %}{%endfor%}',assigns) From ed1b542abf73d1d7c1885ee158410c6575a95668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ku=C5=BAma?= Date: Tue, 14 Apr 2009 16:30:08 +0800 Subject: [PATCH 05/15] All tests pass on Ruby 1.9.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tobias Lütke --- lib/liquid/block.rb | 46 ++++++++--------- lib/liquid/document.rb | 14 ++--- lib/liquid/htmltags.rb | 54 ++++++++++---------- lib/liquid/module_ex.rb | 8 +-- lib/liquid/strainer.rb | 2 +- lib/liquid/tag.rb | 16 +++--- lib/liquid/tags/capture.rb | 18 +++---- lib/liquid/template.rb | 102 ++++++++++++++++++------------------- 8 files changed, 130 insertions(+), 130 deletions(-) diff --git a/lib/liquid/block.rb b/lib/liquid/block.rb index e28cb4e..2d4e293 100644 --- a/lib/liquid/block.rb +++ b/lib/liquid/block.rb @@ -1,19 +1,19 @@ module Liquid - + class Block < Tag def parse(tokens) @nodelist ||= [] @nodelist.clear - while token = tokens.shift + while token = tokens.shift case token - when /^#{TagStart}/ + when /^#{TagStart}/ if token =~ /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/ # if we found the proper block delimitor just end parsing here and let the outer block - # proceed + # proceed if block_delimiter == $1 end_tag return @@ -23,10 +23,10 @@ module Liquid if tag = Template.tags[$1] @nodelist << tag.new($1, $2, tokens) else - # this tag is not registered with the system + # 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 + end else raise SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} " end @@ -37,19 +37,19 @@ module Liquid else @nodelist << token end - end - - # Make sure that its ok to end parsing in the current block. - # Effectively this method will throw and exception unless the current block is - # of type Document + end + + # Make sure that its ok to end parsing in the current block. + # Effectively this method will throw and exception unless the current block is + # of type Document assert_missing_delimitation! - end - - def end_tag + end + + def end_tag end def unknown_tag(tag, params, tokens) - case tag + case tag when 'else' raise SyntaxError, "#{block_name} tag does not expect else tag" when 'end' @@ -61,7 +61,7 @@ module Liquid def block_delimiter "end#{block_name}" - end + end def block_name @tag_name @@ -77,7 +77,7 @@ module Liquid def render(context) render_all(@nodelist, context) end - + protected def assert_missing_delimitation! @@ -86,12 +86,12 @@ module Liquid def render_all(list, context) list.collect do |token| - begin + begin token.respond_to?(:render) ? token.render(context) : token - rescue Exception => e + rescue Exception => e context.handle_error(e) - end - end + end + end end - end -end \ No newline at end of file + end +end diff --git a/lib/liquid/document.rb b/lib/liquid/document.rb index abffbde..bf95478 100644 --- a/lib/liquid/document.rb +++ b/lib/liquid/document.rb @@ -1,17 +1,17 @@ module Liquid - class Document < Block + class Document < Block # we don't need markup to open this block def initialize(tokens) parse(tokens) - end - - # There isn't a real delimter + end + + # There isn't a real delimter def block_delimiter [] end - + # Document blocks don't need to be terminated since they are not actually opened def assert_missing_delimitation! - end + end end -end \ No newline at end of file +end diff --git a/lib/liquid/htmltags.rb b/lib/liquid/htmltags.rb index 8b88e49..c6db036 100644 --- a/lib/liquid/htmltags.rb +++ b/lib/liquid/htmltags.rb @@ -1,7 +1,7 @@ module Liquid - class TableRow < Block - Syntax = /(\w+)\s+in\s+(#{VariableSignature}+)/ - + class TableRow < Block + Syntax = /(\w+)\s+in\s+(#{VariableSignature}+)/ + def initialize(tag_name, markup, tokens) if markup =~ Syntax @variable_name = $1 @@ -13,62 +13,62 @@ module Liquid else raise SyntaxError.new("Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3") end - - super + + super end - - def render(context) + + def render(context) collection = context[@collection_name] or return '' - + if @attributes['limit'] or @attributes['offset'] limit = context[@attributes['limit']] || -1 offset = context[@attributes['offset']] || 0 collection = collection[offset.to_i..(limit.to_i + offset.to_i - 1)] end - + length = collection.length - + cols = context[@attributes['cols']].to_i row = 1 col = 0 result = ["\n"] - context.stack do + context.stack do collection.each_with_index do |item, index| context[@variable_name] = item context['tablerowloop'] = { 'length' => length, - 'index' => index + 1, - 'index0' => index, - 'col' => col + 1, - 'col0' => col, - 'index0' => index, + 'index' => index + 1, + 'index0' => index, + 'col' => col + 1, + 'col0' => col, + 'index0' => index, 'rindex' => length - index, 'rindex0' => length - index -1, 'first' => (index == 0), 'last' => (index == length - 1), 'col_first' => (col == 0), 'col_last' => (col == cols - 1) - } - - + } + + col += 1 - + result << [""] + render_all(@nodelist, context) + [''] - if col == cols and not (index == length - 1) + if col == cols and not (index == length - 1) col = 0 row += 1 - result << ["\n"] + result << ["\n"] end - + end end result + ["\n"] - end + end end - - Template.register_tag('tablerow', TableRow) -end \ No newline at end of file + + Template.register_tag('tablerow', TableRow) +end diff --git a/lib/liquid/module_ex.rb b/lib/liquid/module_ex.rb index e9d7b7b..c504ea1 100644 --- a/lib/liquid/module_ex.rb +++ b/lib/liquid/module_ex.rb @@ -18,7 +18,7 @@ # end # end # -# if you want to extend the drop to other methods you can defines more methods +# if you want to extend the drop to other methods you can defines more methods # in the class ::LiquidDropClass # # class SomeClass::LiquidDropClass @@ -37,11 +37,11 @@ # 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 +# 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 @@ -58,5 +58,5 @@ class Module end end end - + end diff --git a/lib/liquid/strainer.rb b/lib/liquid/strainer.rb index 9fa769b..b6dad26 100644 --- a/lib/liquid/strainer.rb +++ b/lib/liquid/strainer.rb @@ -14,7 +14,7 @@ module Liquid # One of the strainer's responsibilities is to keep malicious method calls out class Strainer < parent_object #:nodoc: INTERNAL_METHOD = /^__/ - @@required_methods = Set.new([:__send__, :respond_to?, :extend, :methods, :class, :object_id]) + @@required_methods = Set.new([:__id__, :__send__, :respond_to?, :extend, :methods, :class, :object_id]) @@filters = {} diff --git a/lib/liquid/tag.rb b/lib/liquid/tag.rb index e0bf35d..2750064 100644 --- a/lib/liquid/tag.rb +++ b/lib/liquid/tag.rb @@ -1,26 +1,26 @@ module Liquid - + class Tag attr_accessor :nodelist - + def initialize(tag_name, markup, tokens) @tag_name = tag_name @markup = markup parse(tokens) end - + def parse(tokens) end - + def name self.class.name.downcase end - + def render(context) '' - end + end end - + end - \ No newline at end of file + diff --git a/lib/liquid/tags/capture.rb b/lib/liquid/tags/capture.rb index f4f6f3c..92b142f 100644 --- a/lib/liquid/tags/capture.rb +++ b/lib/liquid/tags/capture.rb @@ -1,5 +1,5 @@ module Liquid - + # Capture stores the result of a block into a variable without rendering it inplace. # # {% capture heading %} @@ -8,28 +8,28 @@ module Liquid # ... #

{{ monkeys }}

# - # Capture is useful for saving content for use later in your template, such as + # Capture is useful for saving content for use later in your template, such as # in a sidebar or footer. # class Capture < Block Syntax = /(\w+)/ - def initialize(tag_name, markup, tokens) + def initialize(tag_name, markup, tokens) if markup =~ Syntax @to = $1 else raise SyntaxError.new("Syntax Error in 'capture' - Valid syntax: capture [var]") end - - super + + super end def render(context) output = super - context[@to] = output.to_s + context[@to] = output.join '' end - end - + end + Template.register_tag('capture', Capture) -end \ No newline at end of file +end diff --git a/lib/liquid/template.rb b/lib/liquid/template.rb index 03b3da1..c0f189c 100644 --- a/lib/liquid/template.rb +++ b/lib/liquid/template.rb @@ -1,120 +1,120 @@ 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. + # 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. + # 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: # - # Example: - # # template = Liquid::Template.parse(source) # template.render('user_name' => 'bob') # class Template attr_accessor :root @@file_system = BlankFileSystem.new - - class <Template object from liquid source code def parse(source) template = Template.new template.parse(source) template - end + end end # creates a new Template from an array of tokens. Use Template.parse instead def initialize end - - # Parse source code. - # Returns self for easy chaining + + # Parse source code. + # Returns self for easy chaining def parse(source) @root = Document.new(tokenize(source)) self end - - def registers + + def registers @registers ||= {} end - + def assigns @assigns ||= {} end - + def errors @errors ||= [] end - + # Render takes a hash with local variables. # - # if you use the same filters over and over again consider registering them globally + # 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 + # * registers : hash with register variables. Those can be accessed from + # filters and tags and might be useful to integrate liquid more with its host application # def render(*args) - return '' if @root.nil? + return '' if @root.nil? context = case args.first when Liquid::Context args.shift when Hash - self.assigns.merge!(args.shift) + self.assigns.merge!(args.shift) Context.new(assigns, registers, @rethrow_errors) when nil Context.new(assigns, registers, @rethrow_errors) else raise ArgumentError, "Expect Hash or Liquid::Context as parameter" end - + case args.last when Hash options = args.pop - + if options[:registers].is_a?(Hash) - self.registers.merge!(options[:registers]) + self.registers.merge!(options[:registers]) end if options[:filters] context.add_filters(options[:filters]) - end - + end + when Module - context.add_filters(args.pop) + context.add_filters(args.pop) when Array - context.add_filters(args.pop) + context.add_filters(args.pop) end - + begin # render the nodelist. # for performance reasons we get a array back here. join will make a string out of it @@ -123,24 +123,24 @@ module Liquid @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) + 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.shift if tokens[0] and tokens[0].empty? tokens end - - end + + end end From 678fdfdb8a314885db624c64476d70cb4c43490a Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Sat, 6 Jun 2009 15:47:52 +0100 Subject: [PATCH 06/15] Add test case for presetting assigns --- test/variable_test.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/variable_test.rb b/test/variable_test.rb index 058383a..bc4d41a 100644 --- a/test/variable_test.rb +++ b/test/variable_test.rb @@ -131,5 +131,11 @@ class VariableResolutionTest < Test::Unit::TestCase template = Template.parse(%|{{ test.test }}|) assert_equal 'worked', template.render('test' => {'test' => 'worked'}) end + + def test_preset_assigns + template = Template.parse(%|{{ test }}|) + template.assigns['test'] = 'worked' + assert_equal 'worked', template.render + end end \ No newline at end of file From 09c0b3b3916d44e3a31062161019247d9ef451b4 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Sat, 6 Jun 2009 15:59:53 +0100 Subject: [PATCH 07/15] Allow template to be re-used without persisting assigns --- lib/liquid/template.rb | 7 ++++--- test/variable_test.rb | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/liquid/template.rb b/lib/liquid/template.rb index c0f189c..fae8ae3 100644 --- a/lib/liquid/template.rb +++ b/lib/liquid/template.rb @@ -89,10 +89,11 @@ module Liquid when Liquid::Context args.shift when Hash - self.assigns.merge!(args.shift) - Context.new(assigns, registers, @rethrow_errors) + a = args.shift + assigns.each { |k,v| a[k] = v unless a.has_key?(k) } + Context.new(a, registers, @rethrow_errors) when nil - Context.new(assigns, registers, @rethrow_errors) + Context.new(assigns.dup, registers, @rethrow_errors) else raise ArgumentError, "Expect Hash or Liquid::Context as parameter" end diff --git a/test/variable_test.rb b/test/variable_test.rb index bc4d41a..20968ba 100644 --- a/test/variable_test.rb +++ b/test/variable_test.rb @@ -138,4 +138,23 @@ class VariableResolutionTest < Test::Unit::TestCase 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 + end \ No newline at end of file From cfe3e6f3be197da14a1976fccb83cc1a33bb3add Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Sat, 6 Jun 2009 16:04:22 +0100 Subject: [PATCH 08/15] Allow Hash with default value or default proc to be used --- lib/liquid/context.rb | 16 ++++++---------- test/variable_test.rb | 12 ++++++++++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/liquid/context.rb b/lib/liquid/context.rb index 603cd55..0ba943b 100644 --- a/lib/liquid/context.rb +++ b/lib/liquid/context.rb @@ -159,16 +159,12 @@ module Liquid # fetches an object starting at the local scope and then moving up # the hierachy def find_variable(key) - @scopes.each do |scope| - if scope.has_key?(key) - variable = scope[key] - variable = scope[key] = variable.call(self) if variable.is_a?(Proc) - variable = variable.to_liquid - variable.context = self if variable.respond_to?(:context=) - return variable - end - end - nil + scope = @scopes[0..-2].find { |s| s.has_key?(key) } || @scopes.last + variable = scope[key] + variable = scope[key] = variable.call(self) if variable.is_a?(Proc) + variable = variable.to_liquid + variable.context = self if variable.respond_to?(:context=) + return variable end # resolves namespaced queries gracefully. diff --git a/test/variable_test.rb b/test/variable_test.rb index 20968ba..2ce846b 100644 --- a/test/variable_test.rb +++ b/test/variable_test.rb @@ -157,4 +157,16 @@ class VariableResolutionTest < Test::Unit::TestCase 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 + end \ No newline at end of file From f29b9335c51a30b98fadc412983c8ab09766d69f Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Sat, 6 Jun 2009 16:20:34 +0100 Subject: [PATCH 09/15] Allow question-mark at end of variable name only --- lib/liquid.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/liquid.rb b/lib/liquid.rb index 679d1c2..2d6caf7 100644 --- a/lib/liquid.rb +++ b/lib/liquid.rb @@ -29,7 +29,7 @@ module Liquid TagStart = /\{\%/ TagEnd = /\%\}/ VariableSignature = /\(?[\w\-\.\[\]]\)?/ - VariableSegment = /[\w\-]\??/ + VariableSegment = /[\w\-]/ VariableStart = /\{\{/ VariableEnd = /\}\}/ VariableIncompleteEnd = /\}\}?/ @@ -44,7 +44,7 @@ module Liquid AnyStartingTag = /\{\{|\{\%/ PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/ TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/ - VariableParser = /\[[^\]]+\]|#{VariableSegment}+/ + VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/ end require 'liquid/drop' From 01c25a11a3923d9f3a8a00b821c0aba2b586da23 Mon Sep 17 00:00:00 2001 From: Brian Candler Date: Sat, 6 Jun 2009 16:32:20 +0100 Subject: [PATCH 10/15] Raise FilterNotFound on use of non-existent filter --- lib/liquid/context.rb | 2 +- test/context_test.rb | 4 +++- test/security_test.rb | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/liquid/context.rb b/lib/liquid/context.rb index 0ba943b..b757ce9 100644 --- a/lib/liquid/context.rb +++ b/lib/liquid/context.rb @@ -56,7 +56,7 @@ module Liquid if strainer.respond_to?(method) strainer.__send__(method, *args) else - args.first + raise FilterNotFound, "Filter '#{method}' not found" end end diff --git a/test/context_test.rb b/test/context_test.rb index 5c23376..a57432b 100644 --- a/test/context_test.rb +++ b/test/context_test.rb @@ -156,7 +156,9 @@ class ContextTest < Test::Unit::TestCase assert_equal 'hi? hi!', context.invoke(:hi, 'hi?') context = Context.new(@template) - assert_equal 'hi?', context.invoke(:hi, 'hi?') + assert_raises(FilterNotFound) { + context.invoke(:hi, 'hi?') + } context.add_filters(filter) assert_equal 'hi? hi!', context.invoke(:hi, 'hi?') diff --git a/test/security_test.rb b/test/security_test.rb index 1ab0d6f..7bd600b 100644 --- a/test/security_test.rb +++ b/test/security_test.rb @@ -11,14 +11,14 @@ class SecurityTest < Test::Unit::TestCase def test_no_instance_eval text = %( {{ '1+1' | instance_eval }} ) - expected = %| 1+1 | + expected = %! Liquid error: Error - filter 'instance_eval' in ''1+1' | instance_eval' could not be found. ! assert_equal expected, Template.parse(text).render(@assigns) end def test_no_existing_instance_eval text = %( {{ '1+1' | __instance_eval__ }} ) - expected = %| 1+1 | + expected = %! Liquid error: Error - filter '__instance_eval__' in ''1+1' | __instance_eval__' could not be found. ! assert_equal expected, Template.parse(text).render(@assigns) end @@ -26,7 +26,7 @@ class SecurityTest < Test::Unit::TestCase def test_no_instance_eval_after_mixing_in_new_filter text = %( {{ '1+1' | instance_eval }} ) - expected = %| 1+1 | + expected = %! Liquid error: Error - filter 'instance_eval' in ''1+1' | instance_eval' could not be found. ! assert_equal expected, Template.parse(text).render(@assigns) end @@ -34,7 +34,7 @@ class SecurityTest < Test::Unit::TestCase def test_no_instance_eval_later_in_chain text = %( {{ '1+1' | add_one | instance_eval }} ) - expected = %| 1+1 + 1 | + expected = %! Liquid error: Error - filter 'instance_eval' in ''1+1' | add_one | instance_eval' could not be found. ! assert_equal expected, Template.parse(text).render(@assigns, :filters => SecurityFilter) end From f65ce254c70e007a99d0ee4399cfab4dfbaa3561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=BCtke?= Date: Sun, 14 Jun 2009 17:32:50 -0400 Subject: [PATCH 11/15] added full profiler suite to liquid based on real-world code form Shopify and vision --- benchmark/shopify.rb | 88 ++ benchmark/shopify/comment_form.rb | 33 + benchmark/shopify/database.rb | 44 + benchmark/shopify/json_filter.rb | 7 + benchmark/shopify/liquid.rb | 18 + benchmark/shopify/money_filter.rb | 18 + benchmark/shopify/paginate.rb | 93 +++ benchmark/shopify/shop_filter.rb | 98 +++ benchmark/shopify/tag_filter.rb | 25 + benchmark/shopify/vision.database.yml | 945 ++++++++++++++++++++++ benchmark/shopify/weight_filter.rb | 11 + benchmark/tests/dropify/article.liquid | 74 ++ benchmark/tests/dropify/blog.liquid | 33 + benchmark/tests/dropify/cart.liquid | 66 ++ benchmark/tests/dropify/collection.liquid | 22 + benchmark/tests/dropify/index.liquid | 47 ++ benchmark/tests/dropify/page.liquid | 8 + benchmark/tests/dropify/product.liquid | 68 ++ benchmark/tests/dropify/theme.liquid | 105 +++ 19 files changed, 1803 insertions(+) create mode 100644 benchmark/shopify.rb create mode 100644 benchmark/shopify/comment_form.rb create mode 100644 benchmark/shopify/database.rb create mode 100644 benchmark/shopify/json_filter.rb create mode 100644 benchmark/shopify/liquid.rb create mode 100644 benchmark/shopify/money_filter.rb create mode 100644 benchmark/shopify/paginate.rb create mode 100644 benchmark/shopify/shop_filter.rb create mode 100644 benchmark/shopify/tag_filter.rb create mode 100644 benchmark/shopify/vision.database.yml create mode 100644 benchmark/shopify/weight_filter.rb create mode 100644 benchmark/tests/dropify/article.liquid create mode 100644 benchmark/tests/dropify/blog.liquid create mode 100644 benchmark/tests/dropify/cart.liquid create mode 100644 benchmark/tests/dropify/collection.liquid create mode 100644 benchmark/tests/dropify/index.liquid create mode 100644 benchmark/tests/dropify/page.liquid create mode 100644 benchmark/tests/dropify/product.liquid create mode 100644 benchmark/tests/dropify/theme.liquid diff --git a/benchmark/shopify.rb b/benchmark/shopify.rb new file mode 100644 index 0000000..e5dc3a3 --- /dev/null +++ b/benchmark/shopify.rb @@ -0,0 +1,88 @@ +# 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 'rubygems' +require 'active_support' +require 'yaml' +require 'digest/md5' + +require 'shopify/liquid' +require 'shopify/database.rb' + +require "ruby-prof" rescue fail("install ruby-prof extension/gem") + +class ThemeProfiler + + # Load all templates into memory, do this now so that + # we don't profile IO. + def initialize + @tests = Dir['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 profile + RubyProf.measure_mode = RubyProf::WALL_TIME + + # 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 + html = nil + page_template = File.basename(template_name, File.extname(template_name)) + + # Profile compiling and rendering both + RubyProf.resume { html = compile_and_render(liquid, layout, assigns, page_template) } + + # return the result and the MD5 of the content, this can be used to detect regressions between liquid version + $stdout.puts "* rendered template %s, content: %s" % [template_name, Digest::MD5.hexdigest(html)] + + # Uncomment to dump html files to /tmp so that you can inspect for errors + #File.open("/tmp/#{File.basename(template_name)}.html", "w+") { |fp| fp < 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/benchmark/shopify/database.rb b/benchmark/shopify/database.rb new file mode 100644 index 0000000..4326a3a --- /dev/null +++ b/benchmark/shopify/database.rb @@ -0,0 +1,44 @@ +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 +end \ No newline at end of file diff --git a/benchmark/shopify/json_filter.rb b/benchmark/shopify/json_filter.rb new file mode 100644 index 0000000..76ff3d2 --- /dev/null +++ b/benchmark/shopify/json_filter.rb @@ -0,0 +1,7 @@ +module JsonFilter + + def json(object) + object.reject {|k,v| k == "collections" }.to_json + end + +end \ No newline at end of file diff --git a/benchmark/shopify/liquid.rb b/benchmark/shopify/liquid.rb new file mode 100644 index 0000000..86863c6 --- /dev/null +++ b/benchmark/shopify/liquid.rb @@ -0,0 +1,18 @@ +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/benchmark/shopify/money_filter.rb b/benchmark/shopify/money_filter.rb new file mode 100644 index 0000000..8f1f00e --- /dev/null +++ b/benchmark/shopify/money_filter.rb @@ -0,0 +1,18 @@ +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 \ No newline at end of file diff --git a/benchmark/shopify/paginate.rb b/benchmark/shopify/paginate.rb new file mode 100644 index 0000000..5972a74 --- /dev/null +++ b/benchmark/shopify/paginate.rb @@ -0,0 +1,93 @@ +class Paginate < Liquid::Block + Syntax = /(#{Liquid::QuotedFragment})\s*(by\s*(\d+))?/ + + def initialize(tag_name, markup, tokens) + @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 + + super + 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 \ No newline at end of file diff --git a/benchmark/shopify/shop_filter.rb b/benchmark/shopify/shop_filter.rb new file mode 100644 index 0000000..90d2e39 --- /dev/null +++ b/benchmark/shopify/shop_filter.rb @@ -0,0 +1,98 @@ +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/#{vendor_title.to_handle}" + end + + def url_for_type(type_title) + "/collections/#{type_title.to_handle}" + end + + def product_img_url(url, style = 'small') + + unless url =~ /^products\/([\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', 'small', 'thumb', 'icon' + "/files/shops/random_number/products/#{$1}_#{style}.#{$2}" + else + raise ArgumentError, 'valid parameters for filter "size" are: original, grande, large, medium, 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 + +end diff --git a/benchmark/shopify/tag_filter.rb b/benchmark/shopify/tag_filter.rb new file mode 100644 index 0000000..93de0a5 --- /dev/null +++ b/benchmark/shopify/tag_filter.rb @@ -0,0 +1,25 @@ +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/benchmark/shopify/vision.database.yml b/benchmark/shopify/vision.database.yml new file mode 100644 index 0000000..199d257 --- /dev/null +++ b/benchmark/shopify/vision.database.yml @@ -0,0 +1,945 @@ +# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +# 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/benchmark/shopify/weight_filter.rb b/benchmark/shopify/weight_filter.rb new file mode 100644 index 0000000..cf5c8bc --- /dev/null +++ b/benchmark/shopify/weight_filter.rb @@ -0,0 +1,11 @@ +module WeightFilter + + def weight(grams) + sprintf("%.2f", grams / 1000) + end + + def weight_with_unit(grams) + "#{weight(grams)} kg" + end + +end \ No newline at end of file diff --git a/benchmark/tests/dropify/article.liquid b/benchmark/tests/dropify/article.liquid new file mode 100644 index 0000000..504eb39 --- /dev/null +++ b/benchmark/tests/dropify/article.liquid @@ -0,0 +1,74 @@ +
+

{{ 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 %} + \ No newline at end of file diff --git a/benchmark/tests/dropify/blog.liquid b/benchmark/tests/dropify/blog.liquid new file mode 100644 index 0000000..3c36a32 --- /dev/null +++ b/benchmark/tests/dropify/blog.liquid @@ -0,0 +1,33 @@ +
+

{{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 %} + +
\ No newline at end of file diff --git a/benchmark/tests/dropify/cart.liquid b/benchmark/tests/dropify/cart.liquid new file mode 100644 index 0000000..f206ee5 --- /dev/null +++ b/benchmark/tests/dropify/cart.liquid @@ -0,0 +1,66 @@ + + +
+ + {% 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 %} + +
\ No newline at end of file diff --git a/benchmark/tests/dropify/collection.liquid b/benchmark/tests/dropify/collection.liquid new file mode 100644 index 0000000..0679d1e --- /dev/null +++ b/benchmark/tests/dropify/collection.liquid @@ -0,0 +1,22 @@ +{% 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 %} \ No newline at end of file diff --git a/benchmark/tests/dropify/index.liquid b/benchmark/tests/dropify/index.liquid new file mode 100644 index 0000000..569896d --- /dev/null +++ b/benchmark/tests/dropify/index.liquid @@ -0,0 +1,47 @@ +
+

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/benchmark/tests/dropify/page.liquid b/benchmark/tests/dropify/page.liquid new file mode 100644 index 0000000..dcd6399 --- /dev/null +++ b/benchmark/tests/dropify/page.liquid @@ -0,0 +1,8 @@ +
+

{{page.title}}

+ +
+ {{page.content}} +
+ +
\ No newline at end of file diff --git a/benchmark/tests/dropify/product.liquid b/benchmark/tests/dropify/product.liquid new file mode 100644 index 0000000..12d8ea0 --- /dev/null +++ b/benchmark/tests/dropify/product.liquid @@ -0,0 +1,68 @@ +
+ +
+ {% 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/benchmark/tests/dropify/theme.liquid b/benchmark/tests/dropify/theme.liquid new file mode 100644 index 0000000..4c127cf --- /dev/null +++ b/benchmark/tests/dropify/theme.liquid @@ -0,0 +1,105 @@ + + + + + + {{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 %} + + + +
+ +

+ + + +
+
+ +
+ + + + + From 31a5606d42c0a9b39d9946146fc460b304396fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=BCtke?= Date: Sun, 14 Jun 2009 17:42:28 -0400 Subject: [PATCH 12/15] fixed a bug with mock paginate tag --- benchmark/shopify.rb | 5 +---- benchmark/shopify/database.rb | 1 + benchmark/shopify/paginate.rb | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/benchmark/shopify.rb b/benchmark/shopify.rb index e5dc3a3..b336868 100644 --- a/benchmark/shopify.rb +++ b/benchmark/shopify.rb @@ -10,7 +10,6 @@ require 'rubygems' require 'active_support' require 'yaml' require 'digest/md5' - require 'shopify/liquid' require 'shopify/database.rb' @@ -50,7 +49,7 @@ class ThemeProfiler $stdout.puts "* rendered template %s, content: %s" % [template_name, Digest::MD5.hexdigest(html)] # Uncomment to dump html files to /tmp so that you can inspect for errors - #File.open("/tmp/#{File.basename(template_name)}.html", "w+") { |fp| fp < Date: Sun, 14 Jun 2009 18:04:07 -0400 Subject: [PATCH 13/15] added rake profile for easier invokation --- Rakefile | 7 + performance/shopify.rb | 92 ++ performance/shopify/comment_form.rb | 33 + performance/shopify/database.rb | 45 + performance/shopify/json_filter.rb | 7 + performance/shopify/liquid.rb | 18 + performance/shopify/money_filter.rb | 18 + performance/shopify/paginate.rb | 93 ++ performance/shopify/shop_filter.rb | 98 ++ performance/shopify/tag_filter.rb | 25 + performance/shopify/vision.database.yml | 945 ++++++++++++++++++++ performance/shopify/weight_filter.rb | 11 + performance/tests/dropify/article.liquid | 74 ++ performance/tests/dropify/blog.liquid | 33 + performance/tests/dropify/cart.liquid | 66 ++ performance/tests/dropify/collection.liquid | 22 + performance/tests/dropify/index.liquid | 47 + performance/tests/dropify/page.liquid | 8 + performance/tests/dropify/product.liquid | 68 ++ performance/tests/dropify/theme.liquid | 105 +++ 20 files changed, 1815 insertions(+) create mode 100644 performance/shopify.rb create mode 100644 performance/shopify/comment_form.rb create mode 100644 performance/shopify/database.rb create mode 100644 performance/shopify/json_filter.rb create mode 100644 performance/shopify/liquid.rb create mode 100644 performance/shopify/money_filter.rb create mode 100644 performance/shopify/paginate.rb create mode 100644 performance/shopify/shop_filter.rb create mode 100644 performance/shopify/tag_filter.rb create mode 100644 performance/shopify/vision.database.yml create mode 100644 performance/shopify/weight_filter.rb create mode 100644 performance/tests/dropify/article.liquid create mode 100644 performance/tests/dropify/blog.liquid create mode 100644 performance/tests/dropify/cart.liquid create mode 100644 performance/tests/dropify/collection.liquid create mode 100644 performance/tests/dropify/index.liquid create mode 100644 performance/tests/dropify/page.liquid create mode 100644 performance/tests/dropify/product.liquid create mode 100644 performance/tests/dropify/theme.liquid diff --git a/Rakefile b/Rakefile index a2057ac..a3dd972 100755 --- a/Rakefile +++ b/Rakefile @@ -21,4 +21,11 @@ Hoe.new(PKG_NAME, PKG_VERSION) do |p| p.author = "Tobias Luetke" p.email = "tobi@leetsoft.com" p.url = "http://www.liquidmarkup.org" +end + +desc "Run the liquid profile/perforamce coverage" +task :profile do + + ruby "performance/shopify.rb" + end \ No newline at end of file diff --git a/performance/shopify.rb b/performance/shopify.rb new file mode 100644 index 0000000..bd1f634 --- /dev/null +++ b/performance/shopify.rb @@ -0,0 +1,92 @@ +# 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 'rubygems' +require 'active_support' +require 'yaml' +require 'digest/md5' +require File.dirname(__FILE__) + '/shopify/liquid' +require File.dirname(__FILE__) + '/shopify/database.rb' + +require "ruby-prof" rescue fail("install ruby-prof extension/gem") + +class ThemeProfiler + + # 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 profile + RubyProf.measure_mode = RubyProf::WALL_TIME + + # 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 + html = nil + page_template = File.basename(template_name, File.extname(template_name)) + + # Profile compiling and rendering both + RubyProf.resume { html = compile_and_render(liquid, layout, assigns, page_template) } + + # return the result and the MD5 of the content, this can be used to detect regressions between liquid version + $stdout.puts "* rendered template %s, content: %s" % [template_name, Digest::MD5.hexdigest(html)] + + # Uncomment to dump html files to /tmp so that you can inspect for errors + # File.open("/tmp/#{File.basename(template_name)}.html", "w+") { |fp| fp < 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 new file mode 100644 index 0000000..a4db6f6 --- /dev/null +++ b/performance/shopify/database.rb @@ -0,0 +1,45 @@ +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 \ No newline at end of file diff --git a/performance/shopify/json_filter.rb b/performance/shopify/json_filter.rb new file mode 100644 index 0000000..76ff3d2 --- /dev/null +++ b/performance/shopify/json_filter.rb @@ -0,0 +1,7 @@ +module JsonFilter + + def json(object) + object.reject {|k,v| k == "collections" }.to_json + end + +end \ No newline at end of file diff --git a/performance/shopify/liquid.rb b/performance/shopify/liquid.rb new file mode 100644 index 0000000..86863c6 --- /dev/null +++ b/performance/shopify/liquid.rb @@ -0,0 +1,18 @@ +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 new file mode 100644 index 0000000..8f1f00e --- /dev/null +++ b/performance/shopify/money_filter.rb @@ -0,0 +1,18 @@ +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 \ No newline at end of file diff --git a/performance/shopify/paginate.rb b/performance/shopify/paginate.rb new file mode 100644 index 0000000..4dcc00d --- /dev/null +++ b/performance/shopify/paginate.rb @@ -0,0 +1,93 @@ +class Paginate < Liquid::Block + Syntax = /(#{Liquid::QuotedFragment})\s*(by\s*(\d+))?/ + + def initialize(tag_name, markup, tokens) + @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 + + super + 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 \ No newline at end of file diff --git a/performance/shopify/shop_filter.rb b/performance/shopify/shop_filter.rb new file mode 100644 index 0000000..90d2e39 --- /dev/null +++ b/performance/shopify/shop_filter.rb @@ -0,0 +1,98 @@ +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/#{vendor_title.to_handle}" + end + + def url_for_type(type_title) + "/collections/#{type_title.to_handle}" + end + + def product_img_url(url, style = 'small') + + unless url =~ /^products\/([\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', 'small', 'thumb', 'icon' + "/files/shops/random_number/products/#{$1}_#{style}.#{$2}" + else + raise ArgumentError, 'valid parameters for filter "size" are: original, grande, large, medium, 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 + +end diff --git a/performance/shopify/tag_filter.rb b/performance/shopify/tag_filter.rb new file mode 100644 index 0000000..93de0a5 --- /dev/null +++ b/performance/shopify/tag_filter.rb @@ -0,0 +1,25 @@ +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 new file mode 100644 index 0000000..199d257 --- /dev/null +++ b/performance/shopify/vision.database.yml @@ -0,0 +1,945 @@ +# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +# 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 new file mode 100644 index 0000000..cf5c8bc --- /dev/null +++ b/performance/shopify/weight_filter.rb @@ -0,0 +1,11 @@ +module WeightFilter + + def weight(grams) + sprintf("%.2f", grams / 1000) + end + + def weight_with_unit(grams) + "#{weight(grams)} kg" + end + +end \ No newline at end of file diff --git a/performance/tests/dropify/article.liquid b/performance/tests/dropify/article.liquid new file mode 100644 index 0000000..504eb39 --- /dev/null +++ b/performance/tests/dropify/article.liquid @@ -0,0 +1,74 @@ +
+

{{ 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 %} + \ No newline at end of file diff --git a/performance/tests/dropify/blog.liquid b/performance/tests/dropify/blog.liquid new file mode 100644 index 0000000..3c36a32 --- /dev/null +++ b/performance/tests/dropify/blog.liquid @@ -0,0 +1,33 @@ +
+

{{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 %} + +
\ No newline at end of file diff --git a/performance/tests/dropify/cart.liquid b/performance/tests/dropify/cart.liquid new file mode 100644 index 0000000..f206ee5 --- /dev/null +++ b/performance/tests/dropify/cart.liquid @@ -0,0 +1,66 @@ + + +
+ + {% 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 %} + +
\ No newline at end of file diff --git a/performance/tests/dropify/collection.liquid b/performance/tests/dropify/collection.liquid new file mode 100644 index 0000000..0679d1e --- /dev/null +++ b/performance/tests/dropify/collection.liquid @@ -0,0 +1,22 @@ +{% 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 %} \ No newline at end of file diff --git a/performance/tests/dropify/index.liquid b/performance/tests/dropify/index.liquid new file mode 100644 index 0000000..569896d --- /dev/null +++ b/performance/tests/dropify/index.liquid @@ -0,0 +1,47 @@ +
+

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 new file mode 100644 index 0000000..dcd6399 --- /dev/null +++ b/performance/tests/dropify/page.liquid @@ -0,0 +1,8 @@ +
+

{{page.title}}

+ +
+ {{page.content}} +
+ +
\ No newline at end of file diff --git a/performance/tests/dropify/product.liquid b/performance/tests/dropify/product.liquid new file mode 100644 index 0000000..12d8ea0 --- /dev/null +++ b/performance/tests/dropify/product.liquid @@ -0,0 +1,68 @@ +
+ +
+ {% 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 new file mode 100644 index 0000000..4c127cf --- /dev/null +++ b/performance/tests/dropify/theme.liquid @@ -0,0 +1,105 @@ + + + + + + {{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 %} + + + +
+ +

+ + + +
+
+ +
+ + + + + From 5e0ad75ff514fbdcc548cd54fa5f1943d07d569b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=BCtke?= Date: Sun, 14 Jun 2009 18:05:18 -0400 Subject: [PATCH 14/15] remove benchmark dir --- benchmark/shopify.rb | 85 -- benchmark/shopify/comment_form.rb | 33 - benchmark/shopify/database.rb | 45 -- benchmark/shopify/json_filter.rb | 7 - benchmark/shopify/liquid.rb | 18 - benchmark/shopify/money_filter.rb | 18 - benchmark/shopify/paginate.rb | 93 --- benchmark/shopify/shop_filter.rb | 98 --- benchmark/shopify/tag_filter.rb | 25 - benchmark/shopify/vision.database.yml | 945 ---------------------- benchmark/shopify/weight_filter.rb | 11 - benchmark/tests/dropify/article.liquid | 74 -- benchmark/tests/dropify/blog.liquid | 33 - benchmark/tests/dropify/cart.liquid | 66 -- benchmark/tests/dropify/collection.liquid | 22 - benchmark/tests/dropify/index.liquid | 47 -- benchmark/tests/dropify/page.liquid | 8 - benchmark/tests/dropify/product.liquid | 68 -- benchmark/tests/dropify/theme.liquid | 105 --- 19 files changed, 1801 deletions(-) delete mode 100644 benchmark/shopify.rb delete mode 100644 benchmark/shopify/comment_form.rb delete mode 100644 benchmark/shopify/database.rb delete mode 100644 benchmark/shopify/json_filter.rb delete mode 100644 benchmark/shopify/liquid.rb delete mode 100644 benchmark/shopify/money_filter.rb delete mode 100644 benchmark/shopify/paginate.rb delete mode 100644 benchmark/shopify/shop_filter.rb delete mode 100644 benchmark/shopify/tag_filter.rb delete mode 100644 benchmark/shopify/vision.database.yml delete mode 100644 benchmark/shopify/weight_filter.rb delete mode 100644 benchmark/tests/dropify/article.liquid delete mode 100644 benchmark/tests/dropify/blog.liquid delete mode 100644 benchmark/tests/dropify/cart.liquid delete mode 100644 benchmark/tests/dropify/collection.liquid delete mode 100644 benchmark/tests/dropify/index.liquid delete mode 100644 benchmark/tests/dropify/page.liquid delete mode 100644 benchmark/tests/dropify/product.liquid delete mode 100644 benchmark/tests/dropify/theme.liquid diff --git a/benchmark/shopify.rb b/benchmark/shopify.rb deleted file mode 100644 index b336868..0000000 --- a/benchmark/shopify.rb +++ /dev/null @@ -1,85 +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 'rubygems' -require 'active_support' -require 'yaml' -require 'digest/md5' -require 'shopify/liquid' -require 'shopify/database.rb' - -require "ruby-prof" rescue fail("install ruby-prof extension/gem") - -class ThemeProfiler - - # Load all templates into memory, do this now so that - # we don't profile IO. - def initialize - @tests = Dir['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 profile - RubyProf.measure_mode = RubyProf::WALL_TIME - - # 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 - html = nil - page_template = File.basename(template_name, File.extname(template_name)) - - # Profile compiling and rendering both - RubyProf.resume { html = compile_and_render(liquid, layout, assigns, page_template) } - - # return the result and the MD5 of the content, this can be used to detect regressions between liquid version - $stdout.puts "* rendered template %s, content: %s" % [template_name, Digest::MD5.hexdigest(html)] - - # Uncomment to dump html files to /tmp so that you can inspect for errors - # File.open("/tmp/#{File.basename(template_name)}.html", "w+") { |fp| fp < 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/benchmark/shopify/database.rb b/benchmark/shopify/database.rb deleted file mode 100644 index a4db6f6..0000000 --- a/benchmark/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 \ No newline at end of file diff --git a/benchmark/shopify/json_filter.rb b/benchmark/shopify/json_filter.rb deleted file mode 100644 index 76ff3d2..0000000 --- a/benchmark/shopify/json_filter.rb +++ /dev/null @@ -1,7 +0,0 @@ -module JsonFilter - - def json(object) - object.reject {|k,v| k == "collections" }.to_json - end - -end \ No newline at end of file diff --git a/benchmark/shopify/liquid.rb b/benchmark/shopify/liquid.rb deleted file mode 100644 index 86863c6..0000000 --- a/benchmark/shopify/liquid.rb +++ /dev/null @@ -1,18 +0,0 @@ -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/benchmark/shopify/money_filter.rb b/benchmark/shopify/money_filter.rb deleted file mode 100644 index 8f1f00e..0000000 --- a/benchmark/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 \ No newline at end of file diff --git a/benchmark/shopify/paginate.rb b/benchmark/shopify/paginate.rb deleted file mode 100644 index 4dcc00d..0000000 --- a/benchmark/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, tokens) - @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 - - super - 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 \ No newline at end of file diff --git a/benchmark/shopify/shop_filter.rb b/benchmark/shopify/shop_filter.rb deleted file mode 100644 index 90d2e39..0000000 --- a/benchmark/shopify/shop_filter.rb +++ /dev/null @@ -1,98 +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/#{vendor_title.to_handle}" - end - - def url_for_type(type_title) - "/collections/#{type_title.to_handle}" - end - - def product_img_url(url, style = 'small') - - unless url =~ /^products\/([\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', 'small', 'thumb', 'icon' - "/files/shops/random_number/products/#{$1}_#{style}.#{$2}" - else - raise ArgumentError, 'valid parameters for filter "size" are: original, grande, large, medium, 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 - -end diff --git a/benchmark/shopify/tag_filter.rb b/benchmark/shopify/tag_filter.rb deleted file mode 100644 index 93de0a5..0000000 --- a/benchmark/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/benchmark/shopify/vision.database.yml b/benchmark/shopify/vision.database.yml deleted file mode 100644 index 199d257..0000000 --- a/benchmark/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/benchmark/shopify/weight_filter.rb b/benchmark/shopify/weight_filter.rb deleted file mode 100644 index cf5c8bc..0000000 --- a/benchmark/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 \ No newline at end of file diff --git a/benchmark/tests/dropify/article.liquid b/benchmark/tests/dropify/article.liquid deleted file mode 100644 index 504eb39..0000000 --- a/benchmark/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 %} - \ No newline at end of file diff --git a/benchmark/tests/dropify/blog.liquid b/benchmark/tests/dropify/blog.liquid deleted file mode 100644 index 3c36a32..0000000 --- a/benchmark/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 %} - -
\ No newline at end of file diff --git a/benchmark/tests/dropify/cart.liquid b/benchmark/tests/dropify/cart.liquid deleted file mode 100644 index f206ee5..0000000 --- a/benchmark/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 %} - -
\ No newline at end of file diff --git a/benchmark/tests/dropify/collection.liquid b/benchmark/tests/dropify/collection.liquid deleted file mode 100644 index 0679d1e..0000000 --- a/benchmark/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 %} \ No newline at end of file diff --git a/benchmark/tests/dropify/index.liquid b/benchmark/tests/dropify/index.liquid deleted file mode 100644 index 569896d..0000000 --- a/benchmark/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/benchmark/tests/dropify/page.liquid b/benchmark/tests/dropify/page.liquid deleted file mode 100644 index dcd6399..0000000 --- a/benchmark/tests/dropify/page.liquid +++ /dev/null @@ -1,8 +0,0 @@ -
-

{{page.title}}

- -
- {{page.content}} -
- -
\ No newline at end of file diff --git a/benchmark/tests/dropify/product.liquid b/benchmark/tests/dropify/product.liquid deleted file mode 100644 index 12d8ea0..0000000 --- a/benchmark/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/benchmark/tests/dropify/theme.liquid b/benchmark/tests/dropify/theme.liquid deleted file mode 100644 index 4c127cf..0000000 --- a/benchmark/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 %} - - - -
- -

- - - -
-
- -
- - - - - From 37e913f755a5b30dae68accbe252612aac7998ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=BCtke?= Date: Sun, 14 Jun 2009 18:09:40 -0400 Subject: [PATCH 15/15] added 3 more themes to add veriety to the profiler run --- performance/tests/ripen/article.liquid | 74 +++++++++++ performance/tests/ripen/blog.liquid | 13 ++ performance/tests/ripen/cart.liquid | 54 ++++++++ performance/tests/ripen/collection.liquid | 29 +++++ performance/tests/ripen/index.liquid | 32 +++++ performance/tests/ripen/page.liquid | 4 + performance/tests/ripen/product.liquid | 75 +++++++++++ performance/tests/ripen/theme.liquid | 85 +++++++++++++ performance/tests/tribble/404.liquid | 56 ++++++++ performance/tests/tribble/article.liquid | 98 ++++++++++++++ performance/tests/tribble/blog.liquid | 41 ++++++ performance/tests/tribble/cart.liquid | 134 ++++++++++++++++++++ performance/tests/tribble/collection.liquid | 70 ++++++++++ performance/tests/tribble/index.liquid | 94 ++++++++++++++ performance/tests/tribble/page.liquid | 56 ++++++++ performance/tests/tribble/product.liquid | 116 +++++++++++++++++ performance/tests/tribble/search.liquid | 51 ++++++++ performance/tests/tribble/theme.liquid | 90 +++++++++++++ performance/tests/vogue/article.liquid | 66 ++++++++++ performance/tests/vogue/blog.liquid | 32 +++++ performance/tests/vogue/cart.liquid | 58 +++++++++ performance/tests/vogue/collection.liquid | 19 +++ performance/tests/vogue/index.liquid | 22 ++++ performance/tests/vogue/page.liquid | 3 + performance/tests/vogue/product.liquid | 62 +++++++++ performance/tests/vogue/theme.liquid | 122 ++++++++++++++++++ 26 files changed, 1556 insertions(+) create mode 100644 performance/tests/ripen/article.liquid create mode 100644 performance/tests/ripen/blog.liquid create mode 100644 performance/tests/ripen/cart.liquid create mode 100644 performance/tests/ripen/collection.liquid create mode 100644 performance/tests/ripen/index.liquid create mode 100644 performance/tests/ripen/page.liquid create mode 100644 performance/tests/ripen/product.liquid create mode 100644 performance/tests/ripen/theme.liquid create mode 100644 performance/tests/tribble/404.liquid create mode 100644 performance/tests/tribble/article.liquid create mode 100644 performance/tests/tribble/blog.liquid create mode 100644 performance/tests/tribble/cart.liquid create mode 100644 performance/tests/tribble/collection.liquid create mode 100644 performance/tests/tribble/index.liquid create mode 100644 performance/tests/tribble/page.liquid create mode 100644 performance/tests/tribble/product.liquid create mode 100644 performance/tests/tribble/search.liquid create mode 100644 performance/tests/tribble/theme.liquid create mode 100644 performance/tests/vogue/article.liquid create mode 100644 performance/tests/vogue/blog.liquid create mode 100644 performance/tests/vogue/cart.liquid create mode 100644 performance/tests/vogue/collection.liquid create mode 100644 performance/tests/vogue/index.liquid create mode 100644 performance/tests/vogue/page.liquid create mode 100644 performance/tests/vogue/product.liquid create mode 100644 performance/tests/vogue/theme.liquid diff --git a/performance/tests/ripen/article.liquid b/performance/tests/ripen/article.liquid new file mode 100644 index 0000000..3efc26b --- /dev/null +++ b/performance/tests/ripen/article.liquid @@ -0,0 +1,74 @@ +
+

{{ 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 %} + \ No newline at end of file diff --git a/performance/tests/ripen/blog.liquid b/performance/tests/ripen/blog.liquid new file mode 100644 index 0000000..aee318a --- /dev/null +++ b/performance/tests/ripen/blog.liquid @@ -0,0 +1,13 @@ +
+

{{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 %} +
\ No newline at end of file diff --git a/performance/tests/ripen/cart.liquid b/performance/tests/ripen/cart.liquid new file mode 100644 index 0000000..560358e --- /dev/null +++ b/performance/tests/ripen/cart.liquid @@ -0,0 +1,54 @@ + + +
+ + {% 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 %} + +
\ No newline at end of file diff --git a/performance/tests/ripen/collection.liquid b/performance/tests/ripen/collection.liquid new file mode 100644 index 0000000..7ebbbfd --- /dev/null +++ b/performance/tests/ripen/collection.liquid @@ -0,0 +1,29 @@ +
+ +{% 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 %} +
\ No newline at end of file diff --git a/performance/tests/ripen/index.liquid b/performance/tests/ripen/index.liquid new file mode 100644 index 0000000..61eaa67 --- /dev/null +++ b/performance/tests/ripen/index.liquid @@ -0,0 +1,32 @@ +
+

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 %} +
+
\ No newline at end of file diff --git a/performance/tests/ripen/page.liquid b/performance/tests/ripen/page.liquid new file mode 100644 index 0000000..6f1147a --- /dev/null +++ b/performance/tests/ripen/page.liquid @@ -0,0 +1,4 @@ +
+

{{page.title}}

+ {{ page.content }} +
\ No newline at end of file diff --git a/performance/tests/ripen/product.liquid b/performance/tests/ripen/product.liquid new file mode 100644 index 0000000..53c775a --- /dev/null +++ b/performance/tests/ripen/product.liquid @@ -0,0 +1,75 @@ +
+

{{ 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 new file mode 100644 index 0000000..6861c22 --- /dev/null +++ b/performance/tests/ripen/theme.liquid @@ -0,0 +1,85 @@ + + + + {{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 %} + + +
+ +
+
+
+ + + + + \ No newline at end of file diff --git a/performance/tests/tribble/404.liquid b/performance/tests/tribble/404.liquid new file mode 100644 index 0000000..0fd3f8b --- /dev/null +++ b/performance/tests/tribble/404.liquid @@ -0,0 +1,56 @@ +
+ +
+
+

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 new file mode 100644 index 0000000..86df4a7 --- /dev/null +++ b/performance/tests/tribble/article.liquid @@ -0,0 +1,98 @@ + +
+
+ +
+

{{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.

    +
  • +
+
+
\ No newline at end of file diff --git a/performance/tests/tribble/blog.liquid b/performance/tests/tribble/blog.liquid new file mode 100644 index 0000000..5277df6 --- /dev/null +++ b/performance/tests/tribble/blog.liquid @@ -0,0 +1,41 @@ +
+
+

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.

    +
  • +
+
+
\ No newline at end of file diff --git a/performance/tests/tribble/cart.liquid b/performance/tests/tribble/cart.liquid new file mode 100644 index 0000000..fb46fab --- /dev/null +++ b/performance/tests/tribble/cart.liquid @@ -0,0 +1,134 @@ + + +
. + {% 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 new file mode 100644 index 0000000..defdf20 --- /dev/null +++ b/performance/tests/tribble/collection.liquid @@ -0,0 +1,70 @@ +
+

{{ 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 new file mode 100644 index 0000000..04594bd --- /dev/null +++ b/performance/tests/tribble/index.liquid @@ -0,0 +1,94 @@ +
+
+

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 new file mode 100644 index 0000000..402e1fc --- /dev/null +++ b/performance/tests/tribble/page.liquid @@ -0,0 +1,56 @@ +
+ +
+
+

{{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 new file mode 100644 index 0000000..0a94bc5 --- /dev/null +++ b/performance/tests/tribble/product.liquid @@ -0,0 +1,116 @@ +
+

{{ 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.

    +
  • +
+
+ +
+ + + + + \ No newline at end of file diff --git a/performance/tests/tribble/search.liquid b/performance/tests/tribble/search.liquid new file mode 100644 index 0000000..cda6d96 --- /dev/null +++ b/performance/tests/tribble/search.liquid @@ -0,0 +1,51 @@ + + + + +
+

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 %} \ No newline at end of file diff --git a/performance/tests/tribble/theme.liquid b/performance/tests/tribble/theme.liquid new file mode 100644 index 0000000..5176db4 --- /dev/null +++ b/performance/tests/tribble/theme.liquid @@ -0,0 +1,90 @@ + + + + {{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 }} + + + +
+ + + \ No newline at end of file diff --git a/performance/tests/vogue/article.liquid b/performance/tests/vogue/article.liquid new file mode 100644 index 0000000..a2dea57 --- /dev/null +++ b/performance/tests/vogue/article.liquid @@ -0,0 +1,66 @@ +
+

{{ 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 %} \ No newline at end of file diff --git a/performance/tests/vogue/blog.liquid b/performance/tests/vogue/blog.liquid new file mode 100644 index 0000000..63eee80 --- /dev/null +++ b/performance/tests/vogue/blog.liquid @@ -0,0 +1,32 @@ +
+

{{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 %} + + +
+
\ No newline at end of file diff --git a/performance/tests/vogue/cart.liquid b/performance/tests/vogue/cart.liquid new file mode 100644 index 0000000..c6a5e17 --- /dev/null +++ b/performance/tests/vogue/cart.liquid @@ -0,0 +1,58 @@ +

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 %} \ No newline at end of file diff --git a/performance/tests/vogue/collection.liquid b/performance/tests/vogue/collection.liquid new file mode 100644 index 0000000..f17a3b9 --- /dev/null +++ b/performance/tests/vogue/collection.liquid @@ -0,0 +1,19 @@ +{% 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 %} \ No newline at end of file diff --git a/performance/tests/vogue/index.liquid b/performance/tests/vogue/index.liquid new file mode 100644 index 0000000..d94fb95 --- /dev/null +++ b/performance/tests/vogue/index.liquid @@ -0,0 +1,22 @@ +
+ {% 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 %} + \ No newline at end of file diff --git a/performance/tests/vogue/page.liquid b/performance/tests/vogue/page.liquid new file mode 100644 index 0000000..95084e0 --- /dev/null +++ b/performance/tests/vogue/page.liquid @@ -0,0 +1,3 @@ +

{{ page.title }}

+ {{ page.content }} + \ No newline at end of file diff --git a/performance/tests/vogue/product.liquid b/performance/tests/vogue/product.liquid new file mode 100644 index 0000000..3019bdb --- /dev/null +++ b/performance/tests/vogue/product.liquid @@ -0,0 +1,62 @@ +
+ {% 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 new file mode 100644 index 0000000..4d21c1d --- /dev/null +++ b/performance/tests/vogue/theme.liquid @@ -0,0 +1,122 @@ + + + + +{{ 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 %} +
+
+ + + + + \ No newline at end of file