fortitude 0.0.4-java → 0.0.5-java
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.
- checksums.yaml +4 -4
- data/.travis.yml +19 -19
- data/CHANGES.md +31 -0
- data/Gemfile +1 -0
- data/README-erector.md +1 -1
- data/lib/fortitude/erector.rb +32 -0
- data/lib/fortitude/method_templates/tag_method_template.rb.smpl +6 -6
- data/lib/fortitude/method_templates/text_method_template.rb.smpl +3 -3
- data/lib/fortitude/rails/railtie.rb +6 -4
- data/lib/fortitude/rails/renderer.rb +7 -4
- data/lib/fortitude/rails/template_handler.rb +18 -3
- data/lib/fortitude/rendering_context.rb +20 -2
- data/lib/fortitude/tags/render_widget_placeholder.rb +19 -0
- data/lib/fortitude/tags/tag.rb +18 -4
- data/lib/fortitude/tags/tag_return_value.rb +1 -1
- data/lib/fortitude/tags/tag_store.rb +4 -0
- data/lib/fortitude/tilt/fortitude_template.rb +6 -128
- data/lib/fortitude/version.rb +1 -1
- data/lib/fortitude/widget.rb +2 -0
- data/lib/fortitude/widget/files.rb +162 -0
- data/lib/fortitude/widget/integration.rb +5 -3
- data/lib/fortitude/widget/modules_and_subclasses.rb +17 -0
- data/lib/fortitude/widget/rendering.rb +12 -5
- data/lib/fortitude/widget/start_and_end_comments.rb +4 -2
- data/lib/fortitude/widget/tags.rb +6 -1
- data/lib/fortitude/widget/widget_class_inheritable_attributes.rb +7 -0
- data/spec/helpers/rails_server.rb +4 -0
- data/spec/rails/basic_rails_system_spec.rb +4 -0
- data/spec/rails/erector_coexistence_system_spec.rb +33 -0
- data/spec/rails/rendering_context_system_spec.rb +19 -3
- data/spec/rails/rendering_system_spec.rb +6 -0
- data/spec/rails/templates/basic_rails_system_spec/app/controllers/basic_rails_system_spec_controller.rb +5 -0
- data/spec/rails/templates/basic_rails_system_spec/app/views/basic_rails_system_spec/double_render_one.rb +5 -0
- data/spec/rails/templates/basic_rails_system_spec/app/views/basic_rails_system_spec/double_render_three.rb +5 -0
- data/spec/rails/templates/basic_rails_system_spec/app/views/basic_rails_system_spec/double_render_two.rb +9 -0
- data/spec/rails/templates/erector_coexistence_system_spec/app/controllers/erector_coexistence_system_spec_controller.rb +19 -0
- data/spec/rails/templates/erector_coexistence_system_spec/app/v/views/erector_coexistence_system_spec/erector_widget_in_app_v_views.rb +7 -0
- data/spec/rails/templates/erector_coexistence_system_spec/app/v/views/erector_coexistence_system_spec/fortitude_widget_in_app_v_views.rb +7 -0
- data/spec/rails/templates/erector_coexistence_system_spec/app/views/erector_coexistence_system_spec/erector_widget_in_app_views.rb +7 -0
- data/spec/rails/templates/erector_coexistence_system_spec/app/views/erector_coexistence_system_spec/fortitude_widget_in_app_views.rb +7 -0
- data/spec/rails/templates/erector_coexistence_system_spec/config/application.rb +25 -0
- data/spec/rails/templates/rendering_context_system_spec/app/controllers/rendering_context_system_spec_controller.rb +2 -2
- data/spec/rails/templates/rendering_context_system_spec/app/views/rendering_context_system_spec/_current_element_nesting_intermediate.html.erb +3 -0
- data/spec/rails/templates/rendering_context_system_spec/app/views/rendering_context_system_spec/current_element_nesting_child.rb +13 -0
- data/spec/rails/templates/rendering_context_system_spec/app/views/rendering_context_system_spec/current_element_nesting_toplevel.rb +9 -0
- data/spec/rails/templates/rendering_context_system_spec/app/views/rendering_context_system_spec/start_end_widget_through_partials.rb +2 -2
- data/spec/system/convenience_methods_system_spec.rb +22 -0
- data/spec/system/inline_system_spec.rb +2 -2
- data/spec/system/record_tag_emission_system_spec.rb +71 -0
- data/spec/system/rendering_context_system_spec.rb +21 -0
- data/spec/system/setting_inheritance_system_spec.rb +52 -0
- data/spec/system/tag_return_value_system_spec.rb +7 -0
- data/spec/system/tilt_system_spec.rb +13 -18
- data/spec/system/widget_class_from_spec.rb +240 -0
- data/spec/system/widget_method_system_spec.rb +52 -0
- metadata +37 -2
| @@ -5,50 +5,17 @@ require 'fortitude/doctypes' | |
| 5 5 |  | 
| 6 6 | 
             
            module Fortitude
         | 
| 7 7 | 
             
              module Tilt
         | 
| 8 | 
            -
                class CannotDetermineTemplateClassError < StandardError
         | 
| 9 | 
            -
                  attr_reader :tried_class_names
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                  def initialize(tried_class_names)
         | 
| 12 | 
            -
                    @tried_class_names = tried_class_names
         | 
| 13 | 
            -
                    super %{Due to the way Tilt is designed, Fortitude unfortunately has to guess, given a template,
         | 
| 14 | 
            -
            what the actual class name of the widget it contains is. Despite our best efforts, we were unable to
         | 
| 15 | 
            -
            guess what the name of the class involved is.
         | 
| 16 | 
            -
             | 
| 17 | 
            -
            Given the source of the template, we tried the following class names:
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            #{tried_class_names.join("\n")}
         | 
| 20 | 
            -
             | 
| 21 | 
            -
            You can correct this problem either by passing a :fortitude_class option to Tilt, giving
         | 
| 22 | 
            -
            either the name of the widget class or the actual widget Class object, or by adding a
         | 
| 23 | 
            -
            comment to the source of the template, like so:
         | 
| 24 | 
            -
             | 
| 25 | 
            -
            #!fortitude_tilt_class: Foo::Bar::MyWidget}
         | 
| 26 | 
            -
                  end
         | 
| 27 | 
            -
                end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                class NotATemplateClassError < StandardError
         | 
| 30 | 
            -
                  attr_reader :class_name, :actual_object
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  def initialize(class_name, actual_object)
         | 
| 33 | 
            -
                    @class_name = class_name
         | 
| 34 | 
            -
                    @actual_object = actual_object
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                    super %{You explicitly told Fortitude's Tilt support (either with a comment in your template, or
         | 
| 37 | 
            -
            with a :fortitude_class option) that the class we should render for this template is
         | 
| 38 | 
            -
            #{class_name.inspect}, but that turns out to be #{actual_object.inspect},
         | 
| 39 | 
            -
            which is not a Class that inherits from Fortitude::Widget.}
         | 
| 40 | 
            -
                  end
         | 
| 41 | 
            -
                end
         | 
| 42 | 
            -
             | 
| 43 8 | 
             
                class FortitudeTemplate < ::Tilt::Template
         | 
| 44 9 | 
             
                  def prepare
         | 
| 45 10 | 
             
                    ::Object.class_eval(data)
         | 
| 46 | 
            -
                    @fortitude_class = explicit_fortitude_class || first_class_that_is_a_widget_class(all_possible_class_names)
         | 
| 47 11 |  | 
| 48 12 | 
             
                    # 2014-06-19 ageweke -- Earlier versions of Tilt try to instantiate the engine with an empty tempate as a way
         | 
| 49 13 | 
             
                    # of making sure it can be created, so we have to support this case.
         | 
| 50 | 
            -
                    if  | 
| 51 | 
            -
                       | 
| 14 | 
            +
                    if data.strip.length > 0
         | 
| 15 | 
            +
                      @fortitude_class = ::Fortitude::Widget.widget_class_from_source(
         | 
| 16 | 
            +
                        data,
         | 
| 17 | 
            +
                        :magic_comment_text => 'fortitude_tilt_class',
         | 
| 18 | 
            +
                        :class_names_to_try => Array(options[:fortitude_class]) + Array(options[:class_names_to_try]))
         | 
| 52 19 | 
             
                    end
         | 
| 53 20 | 
             
                  end
         | 
| 54 21 |  | 
| @@ -74,96 +41,7 @@ which is not a Class that inherits from Fortitude::Widget.} | |
| 74 41 | 
             
                  end
         | 
| 75 42 |  | 
| 76 43 | 
             
                  private
         | 
| 77 | 
            -
                   | 
| 78 | 
            -
                    @fortitude_class
         | 
| 79 | 
            -
                  end
         | 
| 80 | 
            -
             | 
| 81 | 
            -
                  def explicit_fortitude_class
         | 
| 82 | 
            -
                    explicit_fortitude_class_from_option || explicit_fortitude_class_from_comment
         | 
| 83 | 
            -
                  end
         | 
| 84 | 
            -
             | 
| 85 | 
            -
                  def explicit_fortitude_class_from_option
         | 
| 86 | 
            -
                    string_or_class_to_widget_class(options[:fortitude_class])
         | 
| 87 | 
            -
                  end
         | 
| 88 | 
            -
             | 
| 89 | 
            -
                  def explicit_fortitude_class_from_comment
         | 
| 90 | 
            -
                    out = nil
         | 
| 91 | 
            -
                    data.scan(/^\s*\#\s*\!\s*fortitude_tilt_class\s*:\s*(\S+)\s*$/) do |*args|
         | 
| 92 | 
            -
                      out = args.first.first
         | 
| 93 | 
            -
                    end
         | 
| 94 | 
            -
                    string_or_class_to_widget_class(out)
         | 
| 95 | 
            -
                  end
         | 
| 96 | 
            -
             | 
| 97 | 
            -
                  def string_or_class_to_widget_class(string_or_class)
         | 
| 98 | 
            -
                    out = string_or_class
         | 
| 99 | 
            -
             | 
| 100 | 
            -
                    if out.kind_of?(String)
         | 
| 101 | 
            -
                      begin
         | 
| 102 | 
            -
                        out = out.constantize
         | 
| 103 | 
            -
                      rescue NameError => ne
         | 
| 104 | 
            -
                        raise NotATemplateClassError.new(string_or_class, nil)
         | 
| 105 | 
            -
                      end
         | 
| 106 | 
            -
                    end
         | 
| 107 | 
            -
             | 
| 108 | 
            -
                    if out
         | 
| 109 | 
            -
                      raise NotATemplateClassError.new(string_or_class, out) unless is_widget_class?(out)
         | 
| 110 | 
            -
                    end
         | 
| 111 | 
            -
                    out
         | 
| 112 | 
            -
                  end
         | 
| 113 | 
            -
             | 
| 114 | 
            -
                  def all_possible_class_names
         | 
| 115 | 
            -
                    out = [ ]
         | 
| 116 | 
            -
                    module_nesting = [ ]
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                    data.scan(/\bmodule\s+(\S+)/) do |module_name|
         | 
| 119 | 
            -
                      module_nesting << module_name
         | 
| 120 | 
            -
                    end
         | 
| 121 | 
            -
             | 
| 122 | 
            -
                    data.scan(/\bclass\s+(\S+)/) do |*args|
         | 
| 123 | 
            -
                      out << args.first.first
         | 
| 124 | 
            -
                    end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                    out.uniq!
         | 
| 127 | 
            -
             | 
| 128 | 
            -
                    while module_nesting.length > 0
         | 
| 129 | 
            -
                      possible_module_name = module_nesting.join("::")
         | 
| 130 | 
            -
                      out.reverse.each do |class_name|
         | 
| 131 | 
            -
                        out.unshift("#{possible_module_name}::#{class_name}")
         | 
| 132 | 
            -
                      end
         | 
| 133 | 
            -
                      module_nesting.pop
         | 
| 134 | 
            -
                    end
         | 
| 135 | 
            -
             | 
| 136 | 
            -
                    out
         | 
| 137 | 
            -
                  end
         | 
| 138 | 
            -
             | 
| 139 | 
            -
                  def first_class_that_is_a_widget_class(class_names)
         | 
| 140 | 
            -
                    class_names.each do |class_name|
         | 
| 141 | 
            -
                      begin
         | 
| 142 | 
            -
                        klass = "::#{class_name}".constantize
         | 
| 143 | 
            -
                        return klass if is_widget_class?(klass)
         | 
| 144 | 
            -
                      rescue NameError => ne
         | 
| 145 | 
            -
                        # ok, keep going
         | 
| 146 | 
            -
                      end
         | 
| 147 | 
            -
                    end
         | 
| 148 | 
            -
             | 
| 149 | 
            -
                    nil
         | 
| 150 | 
            -
                  end
         | 
| 151 | 
            -
             | 
| 152 | 
            -
                  def is_widget_class?(klass)
         | 
| 153 | 
            -
                    if (! klass)
         | 
| 154 | 
            -
                      false
         | 
| 155 | 
            -
                    elsif (! klass.kind_of?(Class))
         | 
| 156 | 
            -
                      false
         | 
| 157 | 
            -
                    elsif defined?(::BasicObject) && (klass == ::BasicObject)
         | 
| 158 | 
            -
                      false
         | 
| 159 | 
            -
                    elsif klass == Object
         | 
| 160 | 
            -
                      false
         | 
| 161 | 
            -
                    elsif klass == ::Fortitude::Widget
         | 
| 162 | 
            -
                      true
         | 
| 163 | 
            -
                    else
         | 
| 164 | 
            -
                      is_widget_class?(klass.superclass)
         | 
| 165 | 
            -
                    end
         | 
| 166 | 
            -
                  end
         | 
| 44 | 
            +
                  attr_reader :fortitude_class
         | 
| 167 45 | 
             
                end
         | 
| 168 46 | 
             
              end
         | 
| 169 47 | 
             
            end
         | 
    
        data/lib/fortitude/version.rb
    CHANGED
    
    
    
        data/lib/fortitude/widget.rb
    CHANGED
    
    | @@ -14,6 +14,7 @@ require 'fortitude/widget/helpers' | |
| 14 14 | 
             
            require 'fortitude/widget/capturing'
         | 
| 15 15 | 
             
            require 'fortitude/widget/rendering'
         | 
| 16 16 | 
             
            require 'fortitude/widget/temporary_overrides'
         | 
| 17 | 
            +
            require 'fortitude/widget/files'
         | 
| 17 18 |  | 
| 18 19 | 
             
            require 'fortitude/doctypes'
         | 
| 19 20 |  | 
| @@ -38,6 +39,7 @@ module Fortitude | |
| 38 39 | 
             
                include Fortitude::Widget::Capturing
         | 
| 39 40 | 
             
                include Fortitude::Widget::Rendering
         | 
| 40 41 | 
             
                include Fortitude::Widget::TemporaryOverrides
         | 
| 42 | 
            +
                include Fortitude::Widget::Files
         | 
| 41 43 |  | 
| 42 44 | 
             
                if defined?(::Rails)
         | 
| 43 45 | 
             
                  require 'fortitude/rails/widget_methods'
         | 
| @@ -0,0 +1,162 @@ | |
| 1 | 
            +
            require 'active_support'
         | 
| 2 | 
            +
            require 'active_support/concern'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Fortitude
         | 
| 5 | 
            +
              class Widget
         | 
| 6 | 
            +
                module Files
         | 
| 7 | 
            +
                  extend ActiveSupport::Concern
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  class CannotDetermineWidgetClassNameError < StandardError
         | 
| 10 | 
            +
                    attr_reader :tried_class_names, :filename, :magic_comment_texts
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    def initialize(tried_class_names, options = { })
         | 
| 13 | 
            +
                      options.assert_valid_keys(:filename, :magic_comment_texts)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                      @tried_class_names = tried_class_names
         | 
| 16 | 
            +
                      @filename = options[:filename]
         | 
| 17 | 
            +
                      @magic_comment_texts = options[:magic_comment_texts]
         | 
| 18 | 
            +
                      from_what = filename ? "from the file '#{filename}'" : "from some Fortitude source code"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      super %{You asked for a Fortitude widget class #{from_what},
         | 
| 21 | 
            +
            but we couldn't determine the class name of the widget that supposedly is inside.
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            We tried the following class names, in order:
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            #{tried_class_names.join("\n")}
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ...but none of them both existed and were a class that eventually inherits from
         | 
| 28 | 
            +
            ::Fortitude::Widget.
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            You can either pass the class name into this method via the :class_names_to_try option,
         | 
| 31 | 
            +
            or add a "magic comment" to the source code of this widget that looks like this:
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            #!<token>: <class_name>
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            ...where <token> is one of: #{magic_comment_texts.join(", ")}}
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  module ClassMethods
         | 
| 40 | 
            +
                    def widget_class_from_file(filename, options = { })
         | 
| 41 | 
            +
                      options.assert_valid_keys(:root_dirs, :class_names_to_try, :magic_comment_text)
         | 
| 42 | 
            +
                      filename = File.expand_path(filename)
         | 
| 43 | 
            +
                      source = File.read(filename)
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                      class_names_to_try = Array(options[:class_names_to_try])
         | 
| 46 | 
            +
                      root_dirs = Array(options[:root_dirs])
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                      root_dirs.each do |root_dir|
         | 
| 49 | 
            +
                        root_dir = File.expand_path(root_dir)
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                        if filename[0..(root_dir.length - 1)].downcase == root_dir.downcase
         | 
| 52 | 
            +
                          subpath = filename[(root_dir.length + 1)..-1]
         | 
| 53 | 
            +
                          subpath = $1 if subpath =~ /^(.*)\.rb$/i
         | 
| 54 | 
            +
                          class_names_to_try << subpath.camelize if subpath && subpath.length > 1
         | 
| 55 | 
            +
                        end
         | 
| 56 | 
            +
                      end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                      widget_class_from_source(source,
         | 
| 59 | 
            +
                        :class_names_to_try => class_names_to_try, :magic_comment_text => options[:magic_comment_text],
         | 
| 60 | 
            +
                        :filename => filename)
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    def widget_class_from_source(source, options = { })
         | 
| 64 | 
            +
                      options.assert_valid_keys(:class_names_to_try, :magic_comment_text, :filename)
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                      magic_comment_texts = Array(options[:magic_comment_text]) + DEFAULT_MAGIC_COMMENT_TEXTS
         | 
| 67 | 
            +
                      all_class_names =
         | 
| 68 | 
            +
                        magic_comment_class_from(source, magic_comment_texts) +
         | 
| 69 | 
            +
                        Array(options[:class_names_to_try]) +
         | 
| 70 | 
            +
                        scan_source_for_possible_class_names(source)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                      out = widget_class_from_class_names(all_class_names)
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                      unless out
         | 
| 75 | 
            +
                        if options[:filename]
         | 
| 76 | 
            +
                          require options[:filename]
         | 
| 77 | 
            +
                        else
         | 
| 78 | 
            +
                          ::Object.class_eval(source)
         | 
| 79 | 
            +
                        end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                        out = widget_class_from_class_names(all_class_names)
         | 
| 82 | 
            +
                      end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                      out || (
         | 
| 85 | 
            +
                        raise CannotDetermineWidgetClassNameError.new(all_class_names, :magic_comment_texts => magic_comment_texts,
         | 
| 86 | 
            +
                          :filename => options[:filename]))
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                    private
         | 
| 90 | 
            +
                    DEFAULT_MAGIC_COMMENT_TEXTS = %w{fortitude_class}
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    def magic_comment_class_from(source, magic_comment_texts)
         | 
| 93 | 
            +
                      magic_comment_texts = magic_comment_texts.map { |c| c.to_s.strip.downcase }.uniq
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                      out = [ ]
         | 
| 96 | 
            +
                      source.scan(/^\s*\#\s*\!\s*(\S+)\s*:\s*([A-Za-z0-9_:]+)\s*$/) do |(comment_text, class_name)|
         | 
| 97 | 
            +
                        out << class_name if magic_comment_texts.include?(comment_text.strip.downcase)
         | 
| 98 | 
            +
                      end
         | 
| 99 | 
            +
                      out
         | 
| 100 | 
            +
                    end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    def scan_source_for_possible_class_names(source)
         | 
| 103 | 
            +
                      out = [ ]
         | 
| 104 | 
            +
                      module_nesting = [ ]
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                      source.scan(/\bmodule\s+([A-Za-z0-9_:]+)/) do |match_data|
         | 
| 107 | 
            +
                        module_name = match_data[0]
         | 
| 108 | 
            +
                        module_nesting << module_name
         | 
| 109 | 
            +
                      end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                      source.scan(/\bclass\s+([A-Za-z0-9_:]+)/) do |match_data|
         | 
| 112 | 
            +
                        class_name = match_data[0]
         | 
| 113 | 
            +
                        out << class_name
         | 
| 114 | 
            +
                      end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                      out.uniq!
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                      while module_nesting.length > 0
         | 
| 119 | 
            +
                        possible_module_name = module_nesting.join("::")
         | 
| 120 | 
            +
                        out.reverse.each do |class_name|
         | 
| 121 | 
            +
                          out.unshift("#{possible_module_name}::#{class_name}")
         | 
| 122 | 
            +
                        end
         | 
| 123 | 
            +
                        module_nesting.pop
         | 
| 124 | 
            +
                      end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                      out
         | 
| 127 | 
            +
                    end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    def widget_class_from_class_names(class_names)
         | 
| 130 | 
            +
                      out = nil
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                      class_names.each do |class_name|
         | 
| 133 | 
            +
                        klass = begin
         | 
| 134 | 
            +
                          "::#{class_name}".constantize
         | 
| 135 | 
            +
                        rescue NameError => ne
         | 
| 136 | 
            +
                          nil
         | 
| 137 | 
            +
                        end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                        if is_widget_class?(klass)
         | 
| 140 | 
            +
                          out = klass
         | 
| 141 | 
            +
                          break
         | 
| 142 | 
            +
                        end
         | 
| 143 | 
            +
                      end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                      out
         | 
| 146 | 
            +
                    end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                    def is_widget_class?(klass)
         | 
| 149 | 
            +
                      if (! klass)
         | 
| 150 | 
            +
                        false
         | 
| 151 | 
            +
                      elsif (! klass.kind_of?(Class))
         | 
| 152 | 
            +
                        false
         | 
| 153 | 
            +
                      elsif klass == ::Fortitude::Widget
         | 
| 154 | 
            +
                        true
         | 
| 155 | 
            +
                      else
         | 
| 156 | 
            +
                        is_widget_class?(klass.superclass)
         | 
| 157 | 
            +
                      end
         | 
| 158 | 
            +
                    end
         | 
| 159 | 
            +
                  end
         | 
| 160 | 
            +
                end
         | 
| 161 | 
            +
              end
         | 
| 162 | 
            +
            end
         | 
| @@ -18,7 +18,9 @@ module Fortitude | |
| 18 18 | 
             
                    # INTERNAL USE ONLY
         | 
| 19 19 | 
             
                    def rebuild_text_methods!(why, klass = self)
         | 
| 20 20 | 
             
                      rebuilding(:text_methods, why, klass) do
         | 
| 21 | 
            -
                        class_eval(Fortitude::MethodTemplates::SimpleTemplate.template('text_method_template').result( | 
| 21 | 
            +
                        class_eval(Fortitude::MethodTemplates::SimpleTemplate.template('text_method_template').result(
         | 
| 22 | 
            +
                          :format_output => format_output,
         | 
| 23 | 
            +
                          :record_emitting_tag => self._fortitude_record_emitting_tag?))
         | 
| 22 24 | 
             
                        direct_subclasses.each { |s| s.rebuild_text_methods!(why, klass) }
         | 
| 23 25 | 
             
                      end
         | 
| 24 26 | 
             
                    end
         | 
| @@ -40,13 +42,13 @@ module Fortitude | |
| 40 42 |  | 
| 41 43 | 
             
                  included do
         | 
| 42 44 | 
             
                    _fortitude_on_class_inheritable_attribute_change(
         | 
| 43 | 
            -
                      :format_output, :enforce_element_nesting_rules) do |attribute_name, old_value, new_value|
         | 
| 45 | 
            +
                      :format_output, :enforce_element_nesting_rules, :record_tag_emission) do |attribute_name, old_value, new_value|
         | 
| 44 46 | 
             
                      rebuild_text_methods!(:"#{attribute_name}_changed")
         | 
| 45 47 | 
             
                    end
         | 
| 46 48 |  | 
| 47 49 | 
             
                    _fortitude_on_class_inheritable_attribute_change(
         | 
| 48 50 | 
             
                      :format_output, :close_void_tags, :enforce_element_nesting_rules,
         | 
| 49 | 
            -
                      :enforce_attribute_rules, :enforce_id_uniqueness) do |attribute_name, old_value, new_value|
         | 
| 51 | 
            +
                      :enforce_attribute_rules, :enforce_id_uniqueness, :record_tag_emission) do |attribute_name, old_value, new_value|
         | 
| 50 52 | 
             
                      rebuild_tag_methods!(:"#{attribute_name}_changed")
         | 
| 51 53 | 
             
                    end
         | 
| 52 54 |  | 
| @@ -9,6 +9,23 @@ module Fortitude | |
| 9 9 | 
             
                  extend ActiveSupport::Concern
         | 
| 10 10 |  | 
| 11 11 | 
             
                  module ClassMethods
         | 
| 12 | 
            +
                    def all_fortitude_superclasses
         | 
| 13 | 
            +
                      @all_fortitude_superclasses ||= begin
         | 
| 14 | 
            +
                        if self.name == ::Fortitude::Widget.name
         | 
| 15 | 
            +
                          [ ]
         | 
| 16 | 
            +
                        else
         | 
| 17 | 
            +
                          out = [ ]
         | 
| 18 | 
            +
                          klass = superclass
         | 
| 19 | 
            +
                          while true
         | 
| 20 | 
            +
                            out << klass
         | 
| 21 | 
            +
                            break if klass.name == ::Fortitude::Widget.name
         | 
| 22 | 
            +
                            klass = klass.superclass
         | 
| 23 | 
            +
                          end
         | 
| 24 | 
            +
                          out
         | 
| 25 | 
            +
                        end
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
             | 
| 12 29 | 
             
                    # INTERNAL USE ONLY
         | 
| 13 30 | 
             
                    def direct_subclasses
         | 
| 14 31 | 
             
                      @direct_subclasses || [ ]
         | 
| @@ -11,13 +11,13 @@ module Fortitude | |
| 11 11 | 
             
                  # PUBLIC API
         | 
| 12 12 | 
             
                  def render(*args, &block)
         | 
| 13 13 | 
             
                    call_through = lambda do
         | 
| 14 | 
            -
                      @_fortitude_rendering_context. | 
| 14 | 
            +
                      @_fortitude_rendering_context.record_render(args) do
         | 
| 15 15 | 
             
                        tag_rawtext(invoke_helper(:render, *args, &block))
         | 
| 16 16 | 
             
                      end
         | 
| 17 17 | 
             
                    end
         | 
| 18 18 |  | 
| 19 | 
            -
                    if self.class. | 
| 20 | 
            -
                      @_fortitude_rendering_context. | 
| 19 | 
            +
                    if self.class._fortitude_record_emitting_tag? && args[0].kind_of?(Hash) && args[0].has_key?(:partial)
         | 
| 20 | 
            +
                      @_fortitude_rendering_context.emitting_tag!(self, Fortitude::Tags::PartialTagPlaceholder.instance, nil, nil, &call_through)
         | 
| 21 21 | 
             
                    else
         | 
| 22 22 | 
             
                      call_through.call
         | 
| 23 23 | 
             
                    end
         | 
| @@ -50,8 +50,15 @@ module Fortitude | |
| 50 50 | 
             
                  end
         | 
| 51 51 |  | 
| 52 52 | 
             
                  # PUBLIC API
         | 
| 53 | 
            -
                  def widget(w)
         | 
| 54 | 
            -
                    w.render_to | 
| 53 | 
            +
                  def widget(w, hash = nil)
         | 
| 54 | 
            +
                    if w.respond_to?(:render_to)
         | 
| 55 | 
            +
                      w.render_to(@_fortitude_rendering_context)
         | 
| 56 | 
            +
                    elsif w.kind_of?(Class)
         | 
| 57 | 
            +
                      hash ||= { }
         | 
| 58 | 
            +
                      w.new(hash).render_to(@_fortitude_rendering_context)
         | 
| 59 | 
            +
                    else
         | 
| 60 | 
            +
                      raise "You tried to render a widget, but this is not valid: #{w.inspect}(#{hash.inspect})"
         | 
| 61 | 
            +
                    end
         | 
| 55 62 | 
             
                  end
         | 
| 56 63 |  | 
| 57 64 | 
             
                  # PUBLIC API
         | 
| @@ -22,7 +22,9 @@ module Fortitude | |
| 22 22 | 
             
                    if self.class.start_and_end_comments
         | 
| 23 23 | 
             
                      fo = self.class.format_output
         | 
| 24 24 |  | 
| 25 | 
            -
                       | 
| 25 | 
            +
                      class_name = self.class.name
         | 
| 26 | 
            +
                      class_name = "(anonymous widget class)" if (class_name || "") == ""
         | 
| 27 | 
            +
                      comment_text = "BEGIN #{class_name} depth #{widget_nesting_depth}"
         | 
| 26 28 |  | 
| 27 29 | 
             
                      assign_keys = assigns.keys
         | 
| 28 30 | 
             
                      if assign_keys.length > 0
         | 
| @@ -58,7 +60,7 @@ module Fortitude | |
| 58 60 | 
             
                      end
         | 
| 59 61 | 
             
                      tag_comment comment_text
         | 
| 60 62 | 
             
                      yield
         | 
| 61 | 
            -
                      tag_comment "END #{ | 
| 63 | 
            +
                      tag_comment "END #{class_name} depth #{widget_nesting_depth}"
         | 
| 62 64 | 
             
                    else
         | 
| 63 65 | 
             
                      yield
         | 
| 64 66 | 
             
                    end
         | 
| @@ -14,6 +14,7 @@ module Fortitude | |
| 14 14 | 
             
                    class << self
         | 
| 15 15 | 
             
                      # INTERNAL USE ONLY
         | 
| 16 16 | 
             
                      def tags_changed!(tags)
         | 
| 17 | 
            +
                        super
         | 
| 17 18 | 
             
                        rebuild_tag_methods!(:tags_declared, tags)
         | 
| 18 19 | 
             
                      end
         | 
| 19 20 | 
             
                      private :tags_changed!
         | 
| @@ -30,6 +31,10 @@ module Fortitude | |
| 30 31 | 
             
                    end
         | 
| 31 32 | 
             
                  end
         | 
| 32 33 |  | 
| 34 | 
            +
                  def validate_can_enclose!(widget, tag_object)
         | 
| 35 | 
            +
                    # ok, nothing here
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 33 38 | 
             
                  module ClassMethods
         | 
| 34 39 | 
             
                    # INTERNAL USE ONLY
         | 
| 35 40 | 
             
                    def rebuild_tag_methods!(why, which_tags_in = nil, klass = self)
         | 
| @@ -40,7 +45,7 @@ module Fortitude | |
| 40 45 | 
             
                        which_tags.each do |tag_object|
         | 
| 41 46 | 
             
                          tag_object.define_method_on!(tags_module,
         | 
| 42 47 | 
             
                            :enable_formatting => self.format_output,
         | 
| 43 | 
            -
                            : | 
| 48 | 
            +
                            :record_emitting_tag => self._fortitude_record_emitting_tag?,
         | 
| 44 49 | 
             
                            :enforce_attribute_rules => self.enforce_attribute_rules,
         | 
| 45 50 | 
             
                            :enforce_id_uniqueness => self.enforce_id_uniqueness,
         | 
| 46 51 | 
             
                            :close_void_tags => self.close_void_tags)
         |