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