lib/wrap_it/container.rb in wrap_it-0.2.0 vs lib/wrap_it/container.rb in wrap_it-1.0.0

- old
+ new

@@ -2,43 +2,81 @@ # # Describes elements that can contain other elements # # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru> # - # TODO: single_child + # @todo single_child realization + # @todo refactor code for more clearness class Container < Base switch :deffered_render do |_| # avoid changing deffered_render after any child added if @children.is_a?(Array) @children.empty? else true end end + # list of children elements attr_reader :children + + # children can be extracted from normal template flow and rendered in + # separate section. attr_writer :extract_children + section :children def extract_children? @extract_children == true end - after_initialize do + before_initialize do @children = [] - self.class.extract_from_options.each do |option, name| - args = options.delete(option) - next if args.nil? - args = [args] unless args.is_a?(Array) - self.deffered_render = true - send(name, *args) - end end # - # Defines child elements helper for creation of child items. + # Defines helper for child elements creation. # + # @example simple usage + # class Item < WrapIt::Base + # include TextContainer + # end + # + # class List < WrapIt::Container + # default_tag 'ul' + # child :item, tag: 'li' + # end + # + # list = List.new(template) + # list.item 'list item 1' + # list.item 'list item 2' + # list.render # => '<ul><li>list item 1'</li><li>list item 2</li></ul>' + # + # @example with option + # class Button < WrapIt::Container + # include TextContainer + # html_class 'btn' + # child :icon, tag: 'i', option: true + # end + # + # btn = Button.new(template, 'Home', icon: { class: 'i-home' }) + # btn.render # => '<div class="btn">Home<i class="i-home"></i></div>' + # + # @overload child(name, class_name = nil, [args, ...], opts = {}, &block) + # @param name [Symbol, String] helper method name + # @param class_name [String, Base] class for child elements. If ommited + # WrapIt::Base will be used + # @param args [Object] any arguments that will be passed to child + # element constructor + # @param opts [Hash] options + # @option opts [true, Symbol] :option if specified, child can be created + # via option with same name (if :option is true) or with specified + # name + # @option opts [Symbol] :section section to that this children will be + # rendered. By default children rendered to `children`. Refer to + # {Sections} module for details. + # # @return [String] def self.child(name, *args, &block) name.is_a?(String) && name.to_sym name.is_a?(Symbol) || fail(ArgumentError, 'Wrong child name') child_class = @@ -46,36 +84,40 @@ args.shift else 'WrapIt::Base' end child_class = child_class.name if child_class.is_a?(Class) - @helpers ||= [] - @helpers << name + + opts = args.extract_options! + extract = opts.delete(:option) + args << opts + define_method name do |*helper_args, &helper_block| # We should clone arguments becouse if we have loop in template, # `extract_options!` below works only for first iterration default_args = args.clone options = helper_args.extract_options! options[:helper_name] = name options.merge!(default_args.extract_options!) helper_args += default_args + [options] add_children(name, child_class, block, *helper_args, &helper_block) end - end - def self.extract_from_options(*args) - return @extract_from_options || [] if args.size == 0 - hash = args.extract_options! - args.size.odd? && fail(ArgumentError, 'odd arguments number') - args.each_with_index { |arg, i| i.even? && hash[arg] = args[i + 1] } - @helpers ||= [] - hash.symbolize_keys! - @extract_from_options = Hash[ - hash.select do |k, v| - (v.is_a?(String) || v.is_a?(Symbol)) && @helpers.include?(k) - end.map { |k, v| [k, v.to_sym] } - ] + unless extract.nil? + extract.is_a?(Array) || extract = [extract] + extract.each do |opt_name| + opt_name = name if opt_name == true + option(opt_name) do |_, arguments| + self.deffered_render = true + arguments.is_a?(Array) || arguments = [arguments] + o = arguments.extract_options! + o.merge!(extracted: true) + arguments << o + send name, *arguments + end + end + end end after_capture do if deffered_render? html = Hash[children.map { |c| [c.object_id, capture { c.render }] }] @@ -104,10 +146,11 @@ CONTENT_REPLACE_REGEXP = /\A<!-- WrapIt::Container\((?<obj_id>\h+)\) -->\z/ def add_children(name, helper_class, class_block, *args, &helper_block) options = args.extract_options! section = options.delete(:section) || :children + extracted = options.delete(:extracted) == true args << options item = Object .const_get(helper_class) .new(@template, *args, &helper_block) item.instance_variable_set(:@render_to, section) @@ -118,9 +161,10 @@ end item.define_singleton_method(:parent) { @parent } class_block.nil? || instance_exec(item, &class_block) deffered_render? && @children << item + return if extracted if !deffered_render? && (omit_content? || extract_children?) self[section] << capture { item.render } end if omit_content? || extract_children? empty_html