class HtmlTagBuilder class << self # tag.div -> tag.tag :div def method_missing tag_name, *args, &block tag tag_name, args[0], args[1], &block end # tag :div, { 'class'=>'iform' } do def tag name=nil, opts={}, data=nil if Array === opts # join data given as an array data = opts opts = {} elsif Hash === data # tag.button('btn', href: '/') { 'Foo' } opts = data.merge class: opts data = nil end # covert n._row_foo to n(class: 'row-foo') name = name.to_s if name.to_s[0, 1] == '_' opts ||= {} opts[:class] = name.to_s.sub('_', '').gsub('_', '-') name = :div end # covert tag.a '.foo.bar' to class names # covert tag.a '#id' to id names if (data || block_given?) && opts.is_a?(String) given = opts.dup opts = {} given.sub(/#([\w\-]+)/) { opts[:id] = $1 } klass = given.sub(/^\./, '').gsub('.', ' ') opts[:class] = klass unless klass.blank? end # fix data and opts unless opts is Hash data, opts = opts, {} unless opts.class == Hash if block_given? stack = new data = yield(stack, opts) # if data is pushed to passed node, use that data data = stack.data if stack.data.first end data = data.join('') if data.is_a?(Array) build opts, name, data end # build html node def build attrs, node=nil, text=nil opts = [] attrs.each do |attr_key, attr_value| if attr_value.is_a?(Hash) for data_key, data_value in attr_value __add_opts opts, "#{attr_key}-#{data_key}", data_value end else __add_opts opts, attr_key, attr_value end end opts = opts.first ? ' '+opts.join(' ') : '' return opts unless node empty_tags = ['input', 'img', 'meta', 'link', 'hr', 'br', 'col', 'frame', 'area'] text ||= '' unless empty_tags.include?(node.to_s) out = text ? %{<#{node}#{opts}>#{text}} : %{<#{node}#{opts} />} out.respond_to?(:html_safe) ? out.html_safe : out end # tag.div(class: 'klasa') do -> tag.('klasa') do def call class_name, &block tag(:div, class_name, &block) end def __add_opts opts, key, value unless value.to_s.blank? value = value.join(' ') if value.is_a?(Array) key = key.to_s.gsub(/data_/,'data-') opts.push key+'="'+value.to_s.gsub(/"/,'"')+'"' end end end ### attr_reader :data def initialize @data = [] end # push data to stack def push data @data.push data end # n.('foo') do -> n.div(class: 'foo') do def call class_name, &block @data.push self.class.tag(:div, { class: class_name }, &block) end private # forward to class def method_missing tag_name, *args, &block @data.push self.class.tag(tag_name, args[0], args[1], &block) end end