class Markaby::Builder
The Markaby::Builder class is the central gear in the system. When using from Ruby code, this is the only class you need to instantiate directly.
mab = Markaby::Builder.new mab.html do head { title "Boats.com" } body do h1 "Boats.com has great deals" ul do li "$49 for a canoe" li "$39 for a raft" li "$29 for a huge boot that floats and can fit 5 people" end end end puts mab.to_s
Attributes
Public Class Methods
# File lib/markaby/builder.rb, line 40 def self.ignore_helpers(*helpers) ignored_helpers.concat helpers end
# File lib/markaby/builder.rb, line 36 def self.ignored_helpers @@ignored_helpers ||= [] end
Create a Markaby builder object. Pass in a
hash of variable assignments to assigns
which will be
available as instance variables inside tag construction blocks. If an
object is passed in to helpers
, its methods will be available
from those same blocks.
Pass in a block
to new and the block will be evaluated.
mab = Markaby::Builder.new { html do body do h1 "Matching Mole" end end }
# File lib/markaby/builder.rb, line 61 def initialize(assigns = {}, helpers = nil, &block) @streams = [[]] @assigns = assigns @elements = {} @@default.each do |k, v| instance_variable_set("@#{k}", @assigns[k] || v) end if helpers.nil? @helpers = nil else @helpers = helpers.dup for iv in helpers.instance_variables instance_variable_set(iv, helpers.instance_variable_get(iv)) end end unless assigns.nil? || assigns.empty? for iv, val in assigns instance_variable_set("@#{iv}", val) unless @helpers.nil? @helpers.instance_variable_set("@#{iv}", val) end end end @builder = ::Builder::XmlMarkup.new(:indent => @indent, :target => @streams.last) class << @builder attr_accessor :target, :level end if block text(capture(&block)) end end
# File lib/markaby/builder.rb, line 32 def self.set(option, value) @@default[option] = value end
Public Instance Methods
Emulate ERB to satisfy helpers like form_for
.
# File lib/markaby/builder.rb, line 112 def _erbout; self end
Captures the HTML code built inside the block
. This is done
by creating a new stream for the builder object, running the block and
passing back its stream as a string.
>> Markaby::Builder.new.capture { h1 "TEST"; h2 "CAPTURE ME" } => "<h1>TITLE</h1>\n<h2>CAPTURE ME</h2>\n"
# File lib/markaby/builder.rb, line 120 def capture(&block) @streams.push(builder.target = []) @builder.level += 1 str = instance_eval(&block) str = @streams.last.join if @streams.last.any? @streams.pop @builder.level -= 1 builder.target = @streams.last str end
Content_for will store the given block in an instance variable for later use in another template or in the layout.
The name of the instance variable is content_for_<name> to stay consistent with @content_for_layout which is used by ActionView's layouts.
Example:
content_for("header") do h1 "Half Shark and Half Lion" end
If used several times, the variable will contain all the parts concatenated.
# File lib/markaby/rails.rb, line 40 def content_for(name, &block) @helpers.assigns["content_for_#{name}"] = eval("@content_for_#{name} = (@content_for_#{name} || '') + capture(&block)") end
Builds a head tag. Adds a meta
tag inside with Content-Type
set to text/html; charset=utf-8
.
# File lib/markaby/builder.rb, line 230 def head(*args, &block) tag!(:head, *args) do tag!(:meta, "http-equiv" => "Content-Type", "content" => "text/html; charset=utf-8") if @output_meta_tag instance_eval(&block) end end
Every HTML tag method goes through an #html_tag call. So, calling
div
is equivalent to calling html_tag(:div)
. All
HTML tags in Markaby's list are given generated wrappers for this
method.
If the @auto_validation setting is on, this method will check for many common mistakes which could lead to invalid XHTML.
# File lib/markaby/builder.rb, line 202 def html_tag(sym, *args, &block) if @auto_validation and @tagset.self_closing.include?(sym) and block raise InvalidXhtmlError, "the `\#{sym}' element is self-closing, please remove the block" end if args.empty? and block.nil? and not NO_PROXY.include?(sym) return CssProxy.new do |args, block| if @tagset.forms.include?(sym) and args.last.respond_to?(:to_hash) and args.last[:id] args.last[:name] ||= args.last[:id] end tag!(sym, *args, &block) end end if not @tagset.self_closing.include?(sym) and args.first.respond_to?(:to_hash) block ||= proc{} end tag!(sym, *args, &block) end
This method is used to intercept calls to helper methods and instance variables. Here is the order of interception:
-
If
sym
is a helper method, the helper method is called and output to the stream. -
If
sym
is a Builder::XmlMarkup method, it is passed on to the builder object. -
If
sym
is also the name of an instance variable, the value of the instance variable is returned. -
If
sym
has come this far and notagset
is found,sym
and its arguments are passed to tag! -
If a tagset is found, though,
NoMethodError
is raised.
#method_missing used to be the lynchpin in Markaby, but it's no longer used to handle HTML tags. See #html_tag for that.
# File lib/markaby/builder.rb, line 177 def method_missing(sym, *args, &block) if @helpers.respond_to?(sym, true) && !self.class.ignored_helpers.include?(sym) r = @helpers.send(sym, *args, &block) if @output_helpers and r.respond_to? :to_str fragment { @builder << r } else r end elsif ::Builder::XmlMarkup.instance_methods.include?(sym.to_s) @builder.__send__(sym, *args, &block) elsif instance_variables.include?("@#{sym}") instance_variable_get("@#{sym}") elsif @tagset.nil? tag!(sym, *args, &block) else raise NoMethodError, "no such method `#{sym}'" end end
Create a tag named tag
. Other than the first argument which is
the tag name, the arguments are the same as the tags implemented via
method_missing.
# File lib/markaby/builder.rb, line 133 def tag!(tag, *args, &block) ele_id = nil if @auto_validation and @tagset if !@tagset.tagset.has_key?(tag) raise InvalidXhtmlError, "no element `#{tag}' for #{tagset.doctype}" elsif args.last.respond_to?(:to_hash) attrs = args.last.to_hash attrs.each do |k, v| atname = k.to_s.downcase.intern unless k =~ /:/ or @tagset.tagset[tag].include? atname raise InvalidXhtmlError, "no attribute `#{k}' on #{tag} elements" end if atname == :id ele_id = v.to_s if @elements.has_key? ele_id raise InvalidXhtmlError, "id `#{ele_id}' already used (id's must be unique)." end end end end end if block str = capture &block block = proc { text(str) } end f = fragment { @builder.method_missing(tag, *args, &block) } @elements[ele_id] = f if ele_id f end
Write a string
to the HTML stream without escaping it.
# File lib/markaby/builder.rb, line 104 def text(string) @builder << "#{string}" nil end
Returns a string containing the HTML stream. Internally, the stream is stored as an Array.
# File lib/markaby/builder.rb, line 99 def to_s @streams.last.to_s end
Builds an html tag with XHTML 1.0 Strict doctype instead.
# File lib/markaby/builder.rb, line 246 def xhtml_strict(&block) self.tagset = Markaby::XHTMLStrict xhtml_html &block end
Builds an html tag. An XML 1.0 instruction and an XHTML 1.0 Transitional
doctype are prepended. Also assumes :xmlns =>
"http://www.w3.org/1999/xhtml", :lang =>
"en"
.
# File lib/markaby/builder.rb, line 240 def xhtml_transitional(&block) self.tagset = Markaby::XHTMLTransitional xhtml_html &block end
Private Instance Methods
# File lib/markaby/builder.rb, line 259 def fragment stream = @streams.last f1 = stream.length yield f2 = stream.length - f1 Fragment.new(stream, f1, f2) end
# File lib/markaby/builder.rb, line 253 def xhtml_html(&block) instruct! if @output_xml_instruction declare!(:DOCTYPE, :html, :PUBLIC, *tagset.doctype) tag!(:html, :xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en", :lang => "en", &block) end