module WrapIt # # Base class for all HTML helper classes # # @example Prevent user from changing element tag # class Helper < WrapIt::Base # after_initialize { @tag = 'table' } # end # @example Including some simple HTML into content # class Helper < WrapIt::Base # after_initialize do # @icon = optioins.delete(:icon) # end # after_capture do # unless @icon.nil? # @content = html_safe("") + @content # end # end # @author Alexey Ovchinnikov # class Base # Documentation includes # # @!parse extend Arguments::ClassMethods # @!parse extend Enums::ClassMethods # @!parse extend HTML::ClassMethods # @!parse extend Sections::ClassMethods # @!parse extend Switches::ClassMethods # # include appropriate functionality from modules include DerivedAttributes include Callbacks callback :initialize, :capture, :render include Arguments include Sections include HTML include Switches include Enums include Renderer @omit_content = false attr_reader :helper_name option :helper_name option :tag section :content, :render_arguments, :render_block place :content, :after, :begin place :render_block, :after, :begin place :render_arguments, :after, :begin def initialize(template, *args, &block) @template, @block = template, block add_default_classes run_callbacks :initialize do capture_arguments!(args, &block) # TODO: uncomment following after html_attr implementation finished #html_attr.merge!(args.extract_options!) self.html_attr = args.extract_options! # TODO: find convenient way to save unprocessed arguments @arguments = args end end def tag @tag ||= (self.class.get_derived(:@default_tag) || 'div').to_s end def tag=(value) value.is_a?(Symbol) && value = value.to_s value.is_a?(String) && @tag = value end def helper_name=(value) value.is_a?(String) && value = value.to_sym value.is_a?(Symbol) && @helper_name = value end def omit_content? self.class.get_derived(:@omit_content) == true end # # Renders element to template # # @overload render([content, ...]) # @param content [String] additional content that will be appended # to element content # @yield [element] Runs block after capturing element content and before # rendering it. Returned value appended to content. # @yieldparam element [Base] rendering element. # @yieldreturn [String, nil] content to append to HTML # # @return [String] rendered HTML for element def render(*args, &render_block) # return cached copy if it available return @rendered unless @rendered.nil? capture_sections # add to content string args and block result if its present args.flatten.each { |a| self[:render_arguments] << a if a.is_a? String } if block_given? result = instance_exec(self, &render_block) || empty_html result.is_a?(String) && self[:render_block] << result end do_render do_wrap if @template.output_buffer.nil? # when render called from code, just return content as a String @rendered else # in template context, write content to templates buffer concat(@rendered) empty_html end end # # Wraps element with another. # # You can provide wrapper directly or specify wrapper class as first # argument. In this case wrapper will created with specified set of # arguments and options. If wrapper class ommited, WrapIt::Base will # be used. # # If block present, it will be called when wrapper will rendered. # # @overload wrap(wrapper) # @param wrapper [Base] wrapper instance. # # @overload wrap(wrapper_class, [arg, ...], options = {}) # @param wrapper_class [Class] WrapIt::Base subclass for wrapper. # @param arg [String, Symbol] wrapper creation arguments. # @param options [Hash] wrapper creation options. # # @overload wrap([arg, ...], options = {}) # @param arg [String, Symbol] wrapper creation arguments. # @param options [Hash] wrapper creation options. # # @return [void] def wrap(*args, &block) if args.first.is_a?(Base) @wrapper = args.shift else wrapper_class = args.first.is_a?(Class) ? args.shift : Base @wrapper = wrapper_class.new(@template, *args, &block) end end def unwrap @wrapper = nil end protected # # Defines or gets default tag name for element. This tag can be changed # soon. Without parameters returns current default_tag value. # @param name [] Tag name. Converted to `String`. # @param override [Boolean] Whether to override default tag value if it # allready exists. # # @return [String] new default_tag value. def self.default_tag(name = nil, override = true) return @default_tag if name.nil? name.is_a?(String) || name.is_a?(Symbol) || fail(ArgumentError, 'Tag name should be a String or Symbol') override ? @default_tag = name.to_s : @default_tag ||= name.to_s end def self.omit_content @omit_content = true end def capture_sections run_callbacks :capture do unless @block.nil? captured = capture(self, &@block) || empty_html omit_content? || self[:content] << captured end end end def render_sections(*sections) opts = sections.extract_options! sections.empty? && sections = self.class.sections if opts.key?(:except) opts[:except].is_a?(Array) || opts[:except] = [opts[:except]] sections.reject! { |s| opts[:except].include?(s) } end # glew sections self.class.placement .select { |s| sections.include?(s) } .reduce(empty_html) do |a, e| a << self[e] self[e] = empty_html a end end private def do_render # cleanup options from empty values html_attr.select! do |k, v| !v.nil? && (!v.respond_to?(:empty?) || !v.empty?) end @rendered = render_sections run_callbacks :render do options = html_attr.merge(class: html_class.to_html, data: html_data) @rendered = content_tag(tag, @rendered, options) end end def do_wrap @wrapper.is_a?(Base) && @rendered = capture do @wrapper.render(html_safe(@rendered)) end end end end