pivotal-erector 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.txt +5 -5
- data/VERSION.yml +2 -2
- data/bin/{erect → erector} +0 -0
- data/lib/erector/erect.rb +1 -1
- data/lib/erector/erected.rb +1 -1
- data/lib/erector/rails/extensions/action_controller.rb +25 -7
- data/lib/erector/rails/extensions/{widget.rb → rails_widget/helpers.rb} +2 -9
- data/lib/erector/rails/extensions/{widget/2.2.0/widget.rb → rails_widget.rb} +4 -2
- data/lib/erector/rails/rails_version.rb +6 -0
- data/lib/erector/rails/template_handlers/action_view_template_handler.rb +43 -11
- data/lib/erector/rails.rb +2 -1
- data/lib/erector/version.rb +1 -1
- data/lib/erector/widget.rb +213 -73
- data/lib/erector/widgets/table.rb +3 -3
- data/spec/erector/indentation_spec.rb +39 -24
- data/spec/erector/widget_spec.rb +197 -64
- data/spec/erector/widgets/table_spec.rb +3 -3
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +1 -4
- data/spec/spec_suite.rb +6 -12
- metadata +18 -30
- data/lib/erector/rails/extensions/action_controller/1.2.5/action_controller.rb +0 -17
- data/lib/erector/rails/extensions/action_controller/2.2.0/action_controller.rb +0 -26
- data/lib/erector/rails/extensions/widget/1.2.5/widget.rb +0 -18
- data/lib/erector/rails/supported_rails_versions.rb +0 -14
- data/lib/erector/rails/template_handlers/1.2.5/action_view_template_handler.rb +0 -32
- data/lib/erector/rails/template_handlers/2.0.0/action_view_template_handler.rb +0 -36
- data/lib/erector/rails/template_handlers/2.1.0/action_view_template_handler.rb +0 -31
- data/lib/erector/rails/template_handlers/2.2.0/action_view_template_handler.rb +0 -46
- data/spec/erect/erect_spec.rb +0 -145
- data/spec/erect/erected_spec.rb +0 -80
- data/spec/erect/rhtml_parser_spec.rb +0 -318
    
        data/README.txt
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            = Erector
         | 
| 2 2 |  | 
| 3 3 | 
             
            * http://erector.rubyforge.org
         | 
| 4 | 
            -
            * mailto:erector | 
| 4 | 
            +
            * mailto:erector@googlegroups.com
         | 
| 5 5 | 
             
            * http://www.pivotaltracker.com/projects/482
         | 
| 6 6 |  | 
| 7 7 | 
             
            == DESCRIPTION
         | 
| @@ -18,20 +18,20 @@ project site at http://erector.rubyforge.org for more documentation. | |
| 18 18 | 
             
                require 'erector'
         | 
| 19 19 |  | 
| 20 20 | 
             
                class Hello < Erector::Widget
         | 
| 21 | 
            -
                  def  | 
| 21 | 
            +
                  def content
         | 
| 22 22 | 
             
                    html do
         | 
| 23 23 | 
             
                      head do
         | 
| 24 24 | 
             
                        title "Hello"
         | 
| 25 25 | 
             
                      end
         | 
| 26 26 | 
             
                      body do
         | 
| 27 27 | 
             
                        text "Hello, "
         | 
| 28 | 
            -
                        b " | 
| 28 | 
            +
                        b "#{target}!", :class => 'big'
         | 
| 29 29 | 
             
                      end
         | 
| 30 30 | 
             
                    end
         | 
| 31 31 | 
             
                  end
         | 
| 32 32 | 
             
                end
         | 
| 33 33 |  | 
| 34 | 
            -
                Hello.new.to_s
         | 
| 34 | 
            +
                Hello.new(:target => 'world').to_s
         | 
| 35 35 | 
             
                => "<html><head><title>Hello</title></head><body>Hello, <b class=\"big\">world!</b></body></html>"
         | 
| 36 36 |  | 
| 37 37 | 
             
            == REQUIREMENTS
         | 
| @@ -59,7 +59,7 @@ When installing this way, erector is automatically available to your Rails code | |
| 59 59 |  | 
| 60 60 | 
             
            (The MIT License)
         | 
| 61 61 |  | 
| 62 | 
            -
            Copyright (c) 2007- | 
| 62 | 
            +
            Copyright (c) 2007-2009 Pivotal Labs
         | 
| 63 63 |  | 
| 64 64 | 
             
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 65 65 | 
             
            a copy of this software and associated documentation files (the
         | 
    
        data/VERSION.yml
    CHANGED
    
    
    
        data/bin/{erect → erector}
    RENAMED
    
    | 
            File without changes
         | 
    
        data/lib/erector/erect.rb
    CHANGED
    
    | @@ -11,7 +11,7 @@ module Erector | |
| 11 11 | 
             
                  @output_dir = nil
         | 
| 12 12 |  | 
| 13 13 | 
             
                  opts = OptionParser.new do |opts|
         | 
| 14 | 
            -
                    opts.banner = "Usage:  | 
| 14 | 
            +
                    opts.banner = "Usage: erector [options] [file|dir]*"
         | 
| 15 15 |  | 
| 16 16 | 
             
                    opts.separator "Converts from html/rhtml files to erector widgets, or from erector widgets to html files"
         | 
| 17 17 | 
             
                    opts.separator ""
         | 
    
        data/lib/erector/erected.rb
    CHANGED
    
    
| @@ -1,8 +1,26 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
            else
         | 
| 7 | 
            -
             | 
| 1 | 
            +
            ActionController::Base.class_eval do
         | 
| 2 | 
            +
              def render_widget(widget_class, assigns=nil)
         | 
| 3 | 
            +
                @__widget_class = widget_class
         | 
| 4 | 
            +
                if assigns
         | 
| 5 | 
            +
                  @__widget_assigns = assigns
         | 
| 6 | 
            +
                else
         | 
| 7 | 
            +
                  @__widget_assigns = {}
         | 
| 8 | 
            +
                  variables = instance_variable_names
         | 
| 9 | 
            +
                  variables -= protected_instance_variables
         | 
| 10 | 
            +
                  variables.each do |name|
         | 
| 11 | 
            +
                    @__widget_assigns[name.sub('@', "")] = instance_variable_get(name)
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
                response.template.send(:_evaluate_assigns_and_ivars)
         | 
| 15 | 
            +
                render :inline => "<% @__widget_class.new(@__widget_assigns).to_s(:output => output_buffer, :helpers => self) %>"
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def render_with_erector_widget(*options, &block)
         | 
| 19 | 
            +
                if options.first.is_a?(Hash) && widget = options.first.delete(:widget)
         | 
| 20 | 
            +
                  render_widget widget, @assigns, &block
         | 
| 21 | 
            +
                else
         | 
| 22 | 
            +
                  render_without_erector_widget *options, &block
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
              alias_method_chain :render, :erector_widget
         | 
| 8 26 | 
             
            end
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            module Erector
         | 
| 2 | 
            -
              Widget | 
| 2 | 
            +
              class RailsWidget < Widget
         | 
| 3 3 | 
             
                include ActionController::UrlWriter
         | 
| 4 4 |  | 
| 5 5 | 
             
                # helpers returning raw text
         | 
| @@ -105,13 +105,6 @@ module Erector | |
| 105 105 |  | 
| 106 106 | 
             
                def pluralize(*args)
         | 
| 107 107 | 
             
                  helpers.pluralize(*args)
         | 
| 108 | 
            -
                end | 
| 108 | 
            +
                end
         | 
| 109 109 | 
             
              end
         | 
| 110 110 | 
             
            end
         | 
| 111 | 
            -
             | 
| 112 | 
            -
            dir = File.dirname(__FILE__)
         | 
| 113 | 
            -
            if ActionView::Base.instance_methods.include?("output_buffer")
         | 
| 114 | 
            -
              require "#{dir}/widget/2.2.0/widget"
         | 
| 115 | 
            -
            else
         | 
| 116 | 
            -
              require "#{dir}/widget/1.2.5/widget"
         | 
| 117 | 
            -
            end
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            module Erector
         | 
| 2 | 
            -
              Widget | 
| 2 | 
            +
              class RailsWidget < Widget
         | 
| 3 3 | 
             
                def output
         | 
| 4 4 | 
             
                  process_output_buffer || @output
         | 
| 5 5 | 
             
                end
         | 
| @@ -7,7 +7,7 @@ module Erector | |
| 7 7 | 
             
                def capture_with_helpers(&block)
         | 
| 8 8 | 
             
                  helpers ? helpers.capture(&block) : capture_without_helpers(&block)
         | 
| 9 9 | 
             
                end
         | 
| 10 | 
            -
             | 
| 10 | 
            +
             | 
| 11 11 | 
             
                alias_method_chain :capture, :helpers
         | 
| 12 12 |  | 
| 13 13 | 
             
                # This is here to force #helpers.capture to return the output
         | 
| @@ -34,3 +34,5 @@ module Erector | |
| 34 34 | 
             
                end
         | 
| 35 35 | 
             
              end
         | 
| 36 36 | 
             
            end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            require "#{File.dirname(__FILE__)}/rails_widget/helpers"
         | 
| @@ -1,14 +1,46 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
                   | 
| 6 | 
            -
             | 
| 7 | 
            -
                   | 
| 1 | 
            +
            module ActionView #:nodoc:
         | 
| 2 | 
            +
              module TemplateHandlers #:nodoc:
         | 
| 3 | 
            +
                class Erector < TemplateHandler
         | 
| 4 | 
            +
                  include Compilable
         | 
| 5 | 
            +
                  def self.line_offset
         | 
| 6 | 
            +
                    2
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  ActionView::Template.instance_eval do
         | 
| 10 | 
            +
                    register_template_handler :rb, ActionView::TemplateHandlers::Erector
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def compile(template)
         | 
| 14 | 
            +
                    relative_path_parts = template.path.split('/')
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    is_partial = relative_path_parts.last =~ /^_/
         | 
| 17 | 
            +
                    require_dependency File.expand_path(template.filename)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    widget_class_parts = relative_path_parts.inject(['Views']) do |class_parts, node|
         | 
| 20 | 
            +
                      class_parts << node.gsub(/^_/, "").gsub(/(\.html)?\.rb$/, '').camelize
         | 
| 21 | 
            +
                      class_parts
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                    widget_class_name = widget_class_parts.join("::")
         | 
| 24 | 
            +
                    render_method = is_partial ? 'render_partial' : 'content'
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    erb_template = <<-ERB
         | 
| 27 | 
            +
                    <%
         | 
| 28 | 
            +
                      assigns = instance_variables.inject({}) do |hash, name|
         | 
| 29 | 
            +
                        hash[name.sub('@', "")] = instance_variable_get(name)
         | 
| 30 | 
            +
                        hash
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                      widget = #{widget_class_name}.new(assigns)
         | 
| 34 | 
            +
                      widget.to_s(:output => output_buffer, :helpers => self, :content_method_name => :#{render_method})
         | 
| 35 | 
            +
                    %>
         | 
| 36 | 
            +
                    ERB
         | 
| 37 | 
            +
                    ::ERB.new(
         | 
| 38 | 
            +
                      erb_template,
         | 
| 39 | 
            +
                      nil,
         | 
| 40 | 
            +
                      ::ActionView::TemplateHandlers::ERB.erb_trim_mode,
         | 
| 41 | 
            +
                      "@output_buffer"
         | 
| 42 | 
            +
                    ).src
         | 
| 43 | 
            +
                  end
         | 
| 8 44 | 
             
                end
         | 
| 9 | 
            -
              else
         | 
| 10 | 
            -
                require File.expand_path("#{dir}/2.0.0/action_view_template_handler")
         | 
| 11 45 | 
             
              end
         | 
| 12 | 
            -
            else
         | 
| 13 | 
            -
              require File.expand_path("#{dir}/1.2.5/action_view_template_handler")
         | 
| 14 46 | 
             
            end
         | 
    
        data/lib/erector/rails.rb
    CHANGED
    
    | @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            dir = File.dirname(__FILE__)
         | 
| 2 2 | 
             
            require "action_controller"
         | 
| 3 | 
            -
            require "#{dir}/rails/ | 
| 3 | 
            +
            require "#{dir}/rails/rails_version"
         | 
| 4 | 
            +
            require "#{dir}/rails/extensions/rails_widget"
         | 
| 4 5 | 
             
            require "#{dir}/rails/extensions/action_controller"
         | 
| 5 6 | 
             
            require "#{dir}/rails/extensions/action_view"
         | 
| 6 7 | 
             
            require "#{dir}/rails/template_handlers/action_view_template_handler"
         | 
    
        data/lib/erector/version.rb
    CHANGED
    
    | @@ -4,7 +4,7 @@ module Erector | |
| 4 4 | 
             
              if !Erector.const_defined?(:VERSION)
         | 
| 5 5 | 
             
                dir = File.dirname(__FILE__)
         | 
| 6 6 | 
             
                version = YAML.load_file(File.expand_path("#{dir}/../../VERSION.yml"))
         | 
| 7 | 
            -
                VERSION = "#{version[ | 
| 7 | 
            +
                VERSION = "#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
         | 
| 8 8 | 
             
              end
         | 
| 9 9 | 
             
            end
         | 
| 10 10 |  | 
    
        data/lib/erector/widget.rb
    CHANGED
    
    | @@ -3,17 +3,27 @@ module Erector | |
| 3 3 | 
             
              # A Widget is the center of the Erector universe. 
         | 
| 4 4 | 
             
              #
         | 
| 5 5 | 
             
              # To create a widget, extend Erector::Widget and implement 
         | 
| 6 | 
            -
              # the + | 
| 6 | 
            +
              # the +content+ method. Inside this method you may call any of the tag methods like +span+ or +p+ to emit HTML/XML
         | 
| 7 7 | 
             
              # tags. 
         | 
| 8 8 | 
             
              # 
         | 
| 9 9 | 
             
              # You can also define a widget on the fly by passing a block to +new+. This block will get executed when the widget's
         | 
| 10 | 
            -
              # + | 
| 10 | 
            +
              # +content+ method is called.
         | 
| 11 11 | 
             
              #
         | 
| 12 12 | 
             
              # To render a widget from the outside, instantiate it and call its +to_s+ method.
         | 
| 13 | 
            +
              #
         | 
| 14 | 
            +
              # A widget's +new+ method optionally accepts an options hash. Entries in this hash are converted to instance
         | 
| 15 | 
            +
              # variables, and +attr_reader+ accessors are defined for each.
         | 
| 16 | 
            +
              #
         | 
| 17 | 
            +
              # TODO: You can add runtime input checking via the +needs+ macro. If any of the variables named via 
         | 
| 18 | 
            +
              # +needs+ are absent, an exception is thrown. Optional variables are specified with +wants+. If a variable appears
         | 
| 19 | 
            +
              # in the options hash that is in neither the +needs+ nor +wants+ lists, then that too provokes an exception. 
         | 
| 20 | 
            +
              # This mechanism is meant to ameliorate development-time confusion about exactly what parameters are supported
         | 
| 21 | 
            +
              # by a given widget, avoiding confusing runtime NilClass errors.
         | 
| 13 22 | 
             
              # 
         | 
| 14 | 
            -
              # To call one widget from another, inside the parent widget's  | 
| 15 | 
            -
              #  | 
| 16 | 
            -
              # is used, which gives better performance than using +capture+ or +to_s+.
         | 
| 23 | 
            +
              # To call one widget from another, inside the parent widget's +content+ method, instantiate the child widget and call
         | 
| 24 | 
            +
              # the +widget+ method. This assures that the same output stream
         | 
| 25 | 
            +
              # is used, which gives better performance than using +capture+ or +to_s+. It also preserves the indentation and
         | 
| 26 | 
            +
              # helpers of the enclosing class.
         | 
| 17 27 | 
             
              # 
         | 
| 18 28 | 
             
              # In this documentation we've tried to keep the distinction clear between methods that *emit* text and those that
         | 
| 19 29 | 
             
              # *return* text. "Emit" means that it writes to the output stream; "return" means that it returns a string
         | 
| @@ -64,13 +74,64 @@ module Erector | |
| 64 74 | 
             
                      raise ArgumentError, "You must provide either an instance or a block"
         | 
| 65 75 | 
             
                    end
         | 
| 66 76 | 
             
                  end
         | 
| 67 | 
            -
             | 
| 77 | 
            +
                  
         | 
| 68 78 | 
             
                  protected
         | 
| 69 79 | 
             
                  def after_initialize_parts
         | 
| 70 80 | 
             
                    @after_initialize_parts ||= []
         | 
| 71 81 | 
             
                  end
         | 
| 72 82 | 
             
                end
         | 
| 73 83 |  | 
| 84 | 
            +
                # Class method by which widget classes can declare that they need certain parameters.
         | 
| 85 | 
            +
                # If needed parameters are not passed in to #new, then an exception will be thrown
         | 
| 86 | 
            +
                # (with a hopefully useful message about which parameters are missing). This is intended
         | 
| 87 | 
            +
                # to catch silly bugs like passing in a parameter called 'name' to a widget that expects
         | 
| 88 | 
            +
                # a parameter called 'title'. Every variable declared in 'needs' will get an attr_reader
         | 
| 89 | 
            +
                # accessor declared for it.
         | 
| 90 | 
            +
                #
         | 
| 91 | 
            +
                # You can also declare default values for parameters using hash syntax. You can put #needs
         | 
| 92 | 
            +
                # declarations on multiple lines or on the same line; the only caveat is that if there are
         | 
| 93 | 
            +
                # default values, they all have to be at the end of the line (so they go into the magic
         | 
| 94 | 
            +
                # hash parameter).
         | 
| 95 | 
            +
                #
         | 
| 96 | 
            +
                # If a widget has no #needs declaration then it will accept any combination of parameters
         | 
| 97 | 
            +
                # (and make accessors for them) just like normal. In that case there will be no 'attr_reader's
         | 
| 98 | 
            +
                # declared.
         | 
| 99 | 
            +
                # If a widget wants to declare that it 
         | 
| 100 | 
            +
                # takes no parameters, use the special incantation "needs nil" (and don't declare any other
         | 
| 101 | 
            +
                # needs, or kittens will cry). 
         | 
| 102 | 
            +
                #
         | 
| 103 | 
            +
                # Usage:
         | 
| 104 | 
            +
                #    class FancyForm < Erector::Widget
         | 
| 105 | 
            +
                #      needs :title, :show_okay => true, :show_cancel => false
         | 
| 106 | 
            +
                #      ...
         | 
| 107 | 
            +
                #    end
         | 
| 108 | 
            +
                # 
         | 
| 109 | 
            +
                # That means that 
         | 
| 110 | 
            +
                #   FancyForm.new(:title => 'Login')
         | 
| 111 | 
            +
                # will succeed, as will 
         | 
| 112 | 
            +
                #   FancyForm.new(:title => 'Login', :show_cancel => true)
         | 
| 113 | 
            +
                # but 
         | 
| 114 | 
            +
                #   FancyForm.new(:name => 'Login')
         | 
| 115 | 
            +
                # will fail.
         | 
| 116 | 
            +
                #
         | 
| 117 | 
            +
                def self.needs(*args)
         | 
| 118 | 
            +
                  args.each do |arg|
         | 
| 119 | 
            +
                    (@needs ||= []) << (arg.nil? ? nil : (arg.is_a? Hash) ? arg : arg.to_sym)
         | 
| 120 | 
            +
                  end
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                protected
         | 
| 124 | 
            +
                def self.get_needs
         | 
| 125 | 
            +
                  @needs ||= []
         | 
| 126 | 
            +
                  parent = self.ancestors[1]
         | 
| 127 | 
            +
                  if parent.respond_to? :get_needs
         | 
| 128 | 
            +
                    parent.get_needs + @needs
         | 
| 129 | 
            +
                  else
         | 
| 130 | 
            +
                    @needs
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                public
         | 
| 74 135 | 
             
                @@prettyprint_default = false
         | 
| 75 136 | 
             
                def prettyprint_default
         | 
| 76 137 | 
             
                  @@prettyprint_default
         | 
| @@ -87,95 +148,163 @@ module Erector | |
| 87 148 |  | 
| 88 149 | 
             
                SPACES_PER_INDENT = 2
         | 
| 89 150 |  | 
| 90 | 
            -
                attr_reader :helpers, :assigns, :block, :parent, :output
         | 
| 91 | 
            -
                attr_accessor :enable_prettyprint
         | 
| 151 | 
            +
                attr_reader :helpers, :assigns, :block, :parent, :output, :prettyprint, :indentation
         | 
| 92 152 |  | 
| 93 | 
            -
                def initialize( | 
| 153 | 
            +
                def initialize(assigns={}, &block)
         | 
| 154 | 
            +
                  unless assigns.is_a? Hash
         | 
| 155 | 
            +
                    raise "Erector's API has changed. Now you should pass only an options hash into Widget.new; the rest come in via to_s, or by using #widget."
         | 
| 156 | 
            +
                  end
         | 
| 157 | 
            +
                  if (respond_to? :render) &&
         | 
| 158 | 
            +
                    !self.method(:render).to_s.include?("(RailsWidget)")
         | 
| 159 | 
            +
                    raise "Erector's API has changed. You should rename #{self.class}#render to #content."
         | 
| 160 | 
            +
                  end
         | 
| 94 161 | 
             
                  @assigns = assigns
         | 
| 95 162 | 
             
                  assign_locals(assigns)
         | 
| 96 | 
            -
                  @helpers = helpers
         | 
| 97 163 | 
             
                  @parent = block ? eval("self", block.binding) : nil
         | 
| 98 | 
            -
                  @output = output
         | 
| 99 164 | 
             
                  @block = block
         | 
| 100 | 
            -
                  @at_start_of_line = true
         | 
| 101 | 
            -
                  @indent = 0
         | 
| 102 | 
            -
                  @enable_prettyprint = prettyprint_default
         | 
| 103 165 | 
             
                  self.class.after_initialize self
         | 
| 104 166 | 
             
                end
         | 
| 105 167 |  | 
| 106 168 | 
             
            #-- methods for other classes to call, left public for ease of testing and documentation
         | 
| 107 169 | 
             
            #++
         | 
| 108 170 |  | 
| 171 | 
            +
                protected
         | 
| 172 | 
            +
                def context(output, prettyprint = false, indentation = 0, helpers = nil)
         | 
| 173 | 
            +
                  #TODO: pass in options hash, maybe, instead of parameters
         | 
| 174 | 
            +
                  original_output = @output
         | 
| 175 | 
            +
                  original_indendation = @indentation
         | 
| 176 | 
            +
                  original_helpers = @helpers
         | 
| 177 | 
            +
                  original_prettyprint = @prettyprint
         | 
| 178 | 
            +
                  @output = output
         | 
| 179 | 
            +
                  @at_start_of_line = true
         | 
| 180 | 
            +
                  raise "indentation must be a number, not #{indentation.inspect}" unless indentation.is_a? Fixnum
         | 
| 181 | 
            +
                  @indentation = indentation
         | 
| 182 | 
            +
                  @helpers = helpers
         | 
| 183 | 
            +
                  @prettyprint = prettyprint
         | 
| 184 | 
            +
                  yield
         | 
| 185 | 
            +
                ensure
         | 
| 186 | 
            +
                  @output = original_output
         | 
| 187 | 
            +
                  @indentation = original_indendation
         | 
| 188 | 
            +
                  @helpers = original_helpers
         | 
| 189 | 
            +
                  @prettyprint = original_prettyprint
         | 
| 190 | 
            +
                end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                public
         | 
| 109 193 | 
             
                def assign_locals(local_assigns)
         | 
| 110 | 
            -
                   | 
| 111 | 
            -
             | 
| 194 | 
            +
                  needed = self.class.get_needs.map{|need| need.is_a?(Hash) ? need.keys : need}.flatten
         | 
| 195 | 
            +
                  assigned = []
         | 
| 196 | 
            +
                  local_assigns.each do |name, value|
         | 
| 197 | 
            +
                    unless needed.empty? || needed.include?(name)
         | 
| 198 | 
            +
                      raise "Unknown parameter '#{name}'. #{self.class.name} only accepts #{needed.join(', ')}"
         | 
| 199 | 
            +
                    end
         | 
| 200 | 
            +
                    assign_local(name, value)
         | 
| 201 | 
            +
                    assigned << name
         | 
| 202 | 
            +
                  end
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                  # set variables with default values
         | 
| 205 | 
            +
                  self.class.get_needs.select{|var| var.is_a? Hash}.each do |hash|
         | 
| 206 | 
            +
                    hash.each_pair do |name, value|
         | 
| 207 | 
            +
                      unless assigned.include?(name)
         | 
| 208 | 
            +
                        assign_local(name, value)
         | 
| 209 | 
            +
                        assigned << name
         | 
| 210 | 
            +
                      end
         | 
| 211 | 
            +
                    end
         | 
| 212 | 
            +
                  end
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                  missing = needed - assigned
         | 
| 215 | 
            +
                  unless missing.empty? || missing == [nil]
         | 
| 216 | 
            +
                    raise "Missing parameter#{missing.size == 1 ? '' : 's'}: #{missing.join(', ')}"
         | 
| 217 | 
            +
                  end
         | 
| 218 | 
            +
                end
         | 
| 219 | 
            +
                
         | 
| 220 | 
            +
                def assign_local(name, value)
         | 
| 221 | 
            +
                  instance_variable_set("@#{name}", value)
         | 
| 222 | 
            +
                  if any_are_needed?
         | 
| 112 223 | 
             
                    metaclass.module_eval do
         | 
| 113 224 | 
             
                      attr_reader name
         | 
| 114 225 | 
             
                    end
         | 
| 115 226 | 
             
                  end
         | 
| 116 227 | 
             
                end
         | 
| 117 228 |  | 
| 118 | 
            -
                 | 
| 119 | 
            -
             | 
| 120 | 
            -
                # (either in terms of how it is enabled, or in terms of
         | 
| 121 | 
            -
                # what decisions Erector makes about where to add whitespace).
         | 
| 122 | 
            -
                # This flag should be set prior to any rendering being done
         | 
| 123 | 
            -
                # (for example, calls to to_s or to_pretty).
         | 
| 124 | 
            -
                def enable_prettyprint(enable)
         | 
| 125 | 
            -
                  self.enable_prettyprint = enable
         | 
| 126 | 
            -
                  self
         | 
| 229 | 
            +
                def any_are_needed?
         | 
| 230 | 
            +
                  !self.class.get_needs.empty?
         | 
| 127 231 | 
             
                end
         | 
| 128 | 
            -
             | 
| 232 | 
            +
                
         | 
| 129 233 | 
             
                # Render (like to_s) but adding newlines and indentation.
         | 
| 234 | 
            +
                # This is a convenience method; you may just want to call to_s(:prettyprint => true)
         | 
| 235 | 
            +
                # so you can pass in other rendering options as well.  
         | 
| 130 236 | 
             
                def to_pretty
         | 
| 131 | 
            -
                   | 
| 237 | 
            +
                  to_s(:prettyprint => true)
         | 
| 132 238 | 
             
                end
         | 
| 133 239 |  | 
| 134 | 
            -
                # Entry point for rendering a widget (and all its children). This method creates a new output string,
         | 
| 135 | 
            -
                # calls this widget's # | 
| 240 | 
            +
                # Entry point for rendering a widget (and all its children). This method creates a new output string (if necessary),
         | 
| 241 | 
            +
                # calls this widget's #content method and returns the string.
         | 
| 242 | 
            +
                #
         | 
| 243 | 
            +
                # Options:
         | 
| 244 | 
            +
                # output:: the string to output to. Default: a new empty string
         | 
| 245 | 
            +
                # prettyprint:: whether Erector should add newlines and indentation. Default: the value of prettyprint_default (which is false by default). 
         | 
| 246 | 
            +
                # indentation:: the amount of spaces to indent. Ignored unless prettyprint is true.
         | 
| 247 | 
            +
                # helpers:: a helpers object containing utility methods. Usually this is a Rails view object.
         | 
| 248 | 
            +
                # content_method_name:: in case you want to call a method other than #content, pass its name in here.
         | 
| 136 249 | 
             
                #
         | 
| 137 | 
            -
                #  | 
| 138 | 
            -
                #  | 
| 139 | 
            -
                #  | 
| 140 | 
            -
                 | 
| 141 | 
            -
             | 
| 142 | 
            -
                  #  | 
| 143 | 
            -
                   | 
| 144 | 
            -
                   | 
| 145 | 
            -
             | 
| 146 | 
            -
             | 
| 250 | 
            +
                # Note: Prettyprinting is an experimental feature and is subject to change
         | 
| 251 | 
            +
                # (either in terms of how it is enabled, or in terms of
         | 
| 252 | 
            +
                # what decisions Erector makes about where to add whitespace).
         | 
| 253 | 
            +
                def to_s(options = {}, &blk)
         | 
| 254 | 
            +
                  
         | 
| 255 | 
            +
                  raise "Erector::Widget#to_s now takes an options hash, not a symbol. Try calling \"to_s(:content_method_name=> :#{options})\"" if options.is_a? Symbol
         | 
| 256 | 
            +
                  
         | 
| 257 | 
            +
                  options = {
         | 
| 258 | 
            +
                    :output => "",
         | 
| 259 | 
            +
                    :prettyprint => prettyprint_default,
         | 
| 260 | 
            +
                    :indentation => 0,
         | 
| 261 | 
            +
                    :helpers => nil,
         | 
| 262 | 
            +
                    :content_method_name => :content,
         | 
| 263 | 
            +
                  }.merge(options)
         | 
| 264 | 
            +
                  context(options[:output], options[:prettyprint], options[:indentation], options[:helpers]) do
         | 
| 265 | 
            +
                    send(options[:content_method_name], &blk)
         | 
| 266 | 
            +
                    output.to_s
         | 
| 267 | 
            +
                  end
         | 
| 147 268 | 
             
                end
         | 
| 148 269 |  | 
| 149 270 | 
             
                alias_method :inspect, :to_s
         | 
| 150 | 
            -
             | 
| 271 | 
            +
                
         | 
| 151 272 | 
             
                # Template method which must be overridden by all widget subclasses. Inside this method you call the magic
         | 
| 152 273 | 
             
                # #element methods which emit HTML and text to the output string.
         | 
| 153 | 
            -
                def  | 
| 274 | 
            +
                def content
         | 
| 154 275 | 
             
                  if @block
         | 
| 155 276 | 
             
                    instance_eval(&@block)
         | 
| 156 277 | 
             
                  end
         | 
| 157 278 | 
             
                end
         | 
| 158 279 |  | 
| 159 | 
            -
                # To call one widget from another, inside the parent widget's  | 
| 160 | 
            -
                # its + | 
| 280 | 
            +
                # To call one widget from another, inside the parent widget's +content+ method, instantiate the child widget and call 
         | 
| 281 | 
            +
                # its +write_via+ method, passing in +self+. This assures that the same output string
         | 
| 161 282 | 
             
                # is used, which gives better performance than using +capture+ or +to_s+.
         | 
| 162 | 
            -
                 | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 166 | 
            -
             | 
| 167 | 
            -
                    @output = output_or_widget
         | 
| 283 | 
            +
                # You can also use the +widget+ method.
         | 
| 284 | 
            +
                def write_via(parent)
         | 
| 285 | 
            +
                  @parent = parent
         | 
| 286 | 
            +
                  context(parent.output, parent.prettyprint, parent.indentation, parent.helpers) do
         | 
| 287 | 
            +
                    content
         | 
| 168 288 | 
             
                  end
         | 
| 169 | 
            -
                  render
         | 
| 170 289 | 
             
                end
         | 
| 171 290 |  | 
| 172 | 
            -
                #  | 
| 173 | 
            -
                # a  | 
| 174 | 
            -
                #  | 
| 175 | 
            -
                #  | 
| 176 | 
            -
                 | 
| 177 | 
            -
             | 
| 178 | 
            -
             | 
| 291 | 
            +
                # Emits a (nested) widget onto the current widget's output stream. Accepts either
         | 
| 292 | 
            +
                # a class or an instance. If the first argument is a class, then the second argument
         | 
| 293 | 
            +
                # is a hash used to populate its instance variables. If the first argument is an 
         | 
| 294 | 
            +
                # instance then the hash must be unspecified (or empty).
         | 
| 295 | 
            +
                #
         | 
| 296 | 
            +
                # The sub-widget will have access to the methods of the parent class, via some method_missing
         | 
| 297 | 
            +
                # magic and a "parent" pointer.
         | 
| 298 | 
            +
                def widget(target, assigns={}, &block)
         | 
| 299 | 
            +
                  child = if target.is_a? Class
         | 
| 300 | 
            +
                    target.new(assigns, &block)
         | 
| 301 | 
            +
                  else
         | 
| 302 | 
            +
                    unless assigns.empty?
         | 
| 303 | 
            +
                      raise "Unexpected second parameter. Did you mean to pass in variables when you instantiated the #{target.class.to_s}?"
         | 
| 304 | 
            +
                    end
         | 
| 305 | 
            +
                    target
         | 
| 306 | 
            +
                  end
         | 
| 307 | 
            +
                  child.write_via(self)
         | 
| 179 308 | 
             
                end
         | 
| 180 309 |  | 
| 181 310 | 
             
                # (Should we make this hidden?)
         | 
| @@ -227,7 +356,7 @@ module Erector | |
| 227 356 | 
             
                # Emits an open tag, comprising '<', tag name, optional attributes, and '>'
         | 
| 228 357 | 
             
                def open_tag(tag_name, attributes={})
         | 
| 229 358 | 
             
                  indent_for_open_tag(tag_name)
         | 
| 230 | 
            -
                  @ | 
| 359 | 
            +
                  @indentation += SPACES_PER_INDENT
         | 
| 231 360 |  | 
| 232 361 | 
             
                  output.concat "<#{tag_name}#{format_attributes(attributes)}>"
         | 
| 233 362 | 
             
                  @at_start_of_line = false
         | 
| @@ -239,7 +368,11 @@ module Erector | |
| 239 368 | 
             
                # If another kind of object is passed in, the result of calling
         | 
| 240 369 | 
             
                # its to_s method will be treated as a string would be.
         | 
| 241 370 | 
             
                def text(value)
         | 
| 242 | 
            -
                   | 
| 371 | 
            +
                  if value.is_a? Widget
         | 
| 372 | 
            +
                    widget value
         | 
| 373 | 
            +
                  else
         | 
| 374 | 
            +
                    output.concat(value.html_escape)
         | 
| 375 | 
            +
                  end
         | 
| 243 376 | 
             
                  @at_start_of_line = false
         | 
| 244 377 | 
             
                  nil
         | 
| 245 378 | 
             
                end
         | 
| @@ -279,20 +412,19 @@ module Erector | |
| 279 412 |  | 
| 280 413 | 
             
                # Emits a close tag, consisting of '<', tag name, and '>'
         | 
| 281 414 | 
             
                def close_tag(tag_name)
         | 
| 282 | 
            -
                  @ | 
| 415 | 
            +
                  @indentation -= SPACES_PER_INDENT
         | 
| 283 416 | 
             
                  indent()
         | 
| 284 417 |  | 
| 285 418 | 
             
                  output.concat("</#{tag_name}>")
         | 
| 286 419 |  | 
| 287 420 | 
             
                  if newliney(tag_name)
         | 
| 288 | 
            -
                     | 
| 289 | 
            -
                    @at_start_of_line = true
         | 
| 421 | 
            +
                    _newline
         | 
| 290 422 | 
             
                  end
         | 
| 291 423 | 
             
                end
         | 
| 292 424 |  | 
| 293 425 | 
             
                # Emits the result of joining the elements in array with the separator.
         | 
| 294 426 | 
             
                # The array elements and separator can be Erector::Widget objects,
         | 
| 295 | 
            -
                # which are rendered, or strings, which are  | 
| 427 | 
            +
                # which are rendered, or strings, which are html-escaped and output.
         | 
| 296 428 | 
             
                def join(array, separator)
         | 
| 297 429 | 
             
                  first = true
         | 
| 298 430 | 
             
                  array.each do |widget_or_text|
         | 
| @@ -311,7 +443,7 @@ module Erector | |
| 311 443 |  | 
| 312 444 | 
             
                # Creates a whole new output string, executes the block, then converts the output string to a string and
         | 
| 313 445 | 
             
                # emits it as raw text. If at all possible you should avoid this method since it hurts performance,
         | 
| 314 | 
            -
                # and use  | 
| 446 | 
            +
                # and use +content+ or +write_via+ instead.
         | 
| 315 447 | 
             
                def capture(&block)
         | 
| 316 448 | 
             
                  begin
         | 
| 317 449 | 
             
                    original_output = output
         | 
| @@ -382,10 +514,11 @@ module Erector | |
| 382 514 | 
             
                  rawtext "\n"
         | 
| 383 515 | 
             
                end
         | 
| 384 516 |  | 
| 385 | 
            -
                # Convenience method to emit a css file link, which looks like this:  | 
| 517 | 
            +
                # Convenience method to emit a css file link, which looks like this: 
         | 
| 518 | 
            +
                # <link href="erector.css" rel="stylesheet" type="text/css" />
         | 
| 386 519 | 
             
                # The parameter is the full contents of the href attribute, including any ".css" extension. 
         | 
| 387 520 | 
             
                #
         | 
| 388 | 
            -
                # If you want to emit raw CSS inline, use the # | 
| 521 | 
            +
                # If you want to emit raw CSS inline, use the #style method instead.
         | 
| 389 522 | 
             
                def css(href)
         | 
| 390 523 | 
             
                  link :rel => 'stylesheet', :type => 'text/css', :href => href
         | 
| 391 524 | 
             
                end
         | 
| @@ -396,7 +529,7 @@ module Erector | |
| 396 529 | 
             
                end
         | 
| 397 530 |  | 
| 398 531 | 
             
                def newliney(tag_name)
         | 
| 399 | 
            -
                  if @ | 
| 532 | 
            +
                  if @prettyprint
         | 
| 400 533 | 
             
                    !NON_NEWLINEY.include?(tag_name)
         | 
| 401 534 | 
             
                  else
         | 
| 402 535 | 
             
                    false
         | 
| @@ -434,6 +567,9 @@ protected | |
| 434 567 | 
             
                  end
         | 
| 435 568 | 
             
                  attributes ||= {}
         | 
| 436 569 | 
             
                  open_tag tag_name, attributes
         | 
| 570 | 
            +
                  if block && value
         | 
| 571 | 
            +
                    raise ArgumentError, "You can't pass both a block and a value to #{tag_name} -- please choose one."
         | 
| 572 | 
            +
                  end
         | 
| 437 573 | 
             
                  if block
         | 
| 438 574 | 
             
                    instance_eval(&block)
         | 
| 439 575 | 
             
                  else
         | 
| @@ -448,23 +584,27 @@ protected | |
| 448 584 | 
             
                  output.concat "<#{tag_name}#{format_attributes(attributes)} />"
         | 
| 449 585 |  | 
| 450 586 | 
             
                  if newliney(tag_name)
         | 
| 451 | 
            -
                     | 
| 452 | 
            -
                    @at_start_of_line = true
         | 
| 587 | 
            +
                    _newline
         | 
| 453 588 | 
             
                  end
         | 
| 454 589 | 
             
                end
         | 
| 590 | 
            +
                
         | 
| 591 | 
            +
                def _newline
         | 
| 592 | 
            +
                  return unless @prettyprint      
         | 
| 593 | 
            +
                  output.concat "\n"
         | 
| 594 | 
            +
                  @at_start_of_line = true
         | 
| 595 | 
            +
                end
         | 
| 455 596 |  | 
| 456 597 | 
             
                def indent_for_open_tag(tag_name)
         | 
| 598 | 
            +
                  return unless @prettyprint      
         | 
| 457 599 | 
             
                  if !@at_start_of_line && newliney(tag_name)
         | 
| 458 | 
            -
                     | 
| 459 | 
            -
                    @at_start_of_line = true
         | 
| 600 | 
            +
                    _newline
         | 
| 460 601 | 
             
                  end
         | 
| 461 | 
            -
             | 
| 462 602 | 
             
                  indent()
         | 
| 463 603 | 
             
                end
         | 
| 464 604 |  | 
| 465 605 | 
             
                def indent()
         | 
| 466 606 | 
             
                  if @at_start_of_line
         | 
| 467 | 
            -
                    output.concat " " * @ | 
| 607 | 
            +
                    output.concat " " * @indentation
         | 
| 468 608 | 
             
                  end
         | 
| 469 609 | 
             
                end
         | 
| 470 610 |  |