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

- old
+ new

@@ -2,82 +2,135 @@ # # Describes elements that can contain other elements # # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru> # + # TODO: single_child class Container < Base - switch :deffered_render + switch :deffered_render do |_| + # avoid changing deffered_render after any child added + if @children.is_a?(Array) + @children.empty? + else + true + end + end - after_initialize do - @children = deffered_render? ? [] : empty_html + attr_reader :children + attr_writer :extract_children + section :children + + def extract_children? + @extract_children == true end - def self.child(*args, &block) - create_args = args.last.is_a?(Array) ? args.pop : [] - klass = args.pop - klass.is_a?(Class) && klass = klass.name - unless klass.is_a?(String) - args.push(klass) - klass = 'WrapIt::Base' + after_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 - args.select! { |n| n.is_a?(Symbol) } - args.size > 0 || fail(ArgumentError, 'No valid method names given') - args.each do |method| - define_method method 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 = create_args.clone - options = helper_args.extract_options! - options[:helper_name] = method - options.merge!(default_args.extract_options!) - helper_args += default_args + [options] - add_children(klass, block, *helper_args, &helper_block) + end + + # + # Defines child elements helper for creation of child items. + # + # @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 = + if args.first.is_a?(String) || args.first.is_a?(Class) + args.shift + else + 'WrapIt::Base' end + child_class = child_class.name if child_class.is_a?(Class) + @helpers ||= [] + @helpers << name + 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 -# protected + 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] } + ] + end after_capture do if deffered_render? - html = Hash[@children.map { |c| [c.object_id, capture { c.render }] }] - if omit_content? - @content = html.values.reduce(empty_html) { |a, e| a << e } - else - safe = html_safe?(@content) - @content = @content + html = Hash[children.map { |c| [c.object_id, capture { c.render }] }] + unless omit_content? || extract_children? + safe = html_safe?(self[:content]) + self[:content] = self[:content] .split(CONTENT_SPLIT_REGEXP) .reduce(empty_html) do |a, e| match = CONTENT_REPLACE_REGEXP.match(e) - safe || e = html_safe(e) - a << match.nil? ? e : html[match[:obj_id].to_i(16)] + safe && e = html_safe(e) + str = match.nil? ? e : html.delete(match[:obj_id].to_i(16)) + a << (str || empty_html) end end - else - omit_content? && @content = @children + # finally add all elements, not captured from markup + html.each do |id, str| + obj = ObjectSpace._id2ref(id) + obj.nil? || self[obj.render_to] << str + end end end private CONTENT_SPLIT_REGEXP = /(<!-- WrapIt::Container\(\h+\) -->)/ CONTENT_REPLACE_REGEXP = /\A<!-- WrapIt::Container\((?<obj_id>\h+)\) -->\z/ - def add_children(helper_class, class_block, *args, &helper_block) + def add_children(name, helper_class, class_block, *args, &helper_block) + options = args.extract_options! + section = options.delete(:section) || :children + args << options item = Object .const_get(helper_class) .new(@template, *args, &helper_block) + item.instance_variable_set(:@render_to, section) + item.instance_variable_set(:@parent, self) + item.define_singleton_method(:render_to) { @render_to } + item.define_singleton_method(:render_to=) do |value| + self.class.sections.include?(value) && @render_to = value + end + item.define_singleton_method(:parent) { @parent } class_block.nil? || instance_exec(item, &class_block) - item = item.render unless deffered_render? - @children << item if deffered_render? || omit_content? - if omit_content? + deffered_render? && @children << item + if !deffered_render? && (omit_content? || extract_children?) + self[section] << capture { item.render } + end + if omit_content? || extract_children? empty_html else if deffered_render? html_safe("<!-- WrapIt::Container(#{item.object_id.to_s(16)}) -->") else - item + item.render end end end end end