Class: Jig::XML

Inherits:
Jig
  • Object
show all
Defined in:
lib/jig/xml.rb

Constant Summary collapse

Newlines =

These elements will have newlines inserted into the default constructions to increase the readability of the generated XML.

[:html, :head, :body, :title, :div, :p, :table, :script, :form]
Encode =

:nodoc:

Entities =

:nodoc:

Encode.keys.join
ATTRS =

:nodoc:

Gap::ATTRS
ATTRS_GAP =
Jig::ALGap.new(ATTRS)
Element_Cache =

ATTRS_GAP = Gap.new(ATTRS) { |h| h && h.map { |k,v| Jig::XML.attribute(k, v) } } # :nodoc:

{}
Empty_Element_Cache =

:nodoc:

{}
Cache =

:nodoc:

{}

Constants inherited from Jig

Before, DEFAULT_GAP, GAP, Replace, VERSION

Instance Attribute Summary

Attributes inherited from Jig

#contents, #rawgaps

Class Method Summary collapse

Methods inherited from Jig

#+, #==, #===, #=~, #after, #after!, #before, #before!, #closed?, #concat, #eql?, #fill!, #filln!, #freeze, #gaps, #has_gap?, #index, #initialize, #initialize_copy, #inspect, interpolate, #join, #mult, null, #null?, #open?, #plug, #plug!, #plug_gap!, #plugn, #plugn!, #push, #push_jig, #slice, #split, #syntax, #to_jig, #to_s

Constructor Details

This class inherits a constructor from Jig

Class Method Details

._anonymous(tag) ⇒ Object

Construct a generic XML element with four gaps:

  • :__a filters a hash into an XML attribute list

  • :_ which is a default gap

  • tag which acts a placeholder for the element’s opening and closing tag

Jig::XML._element(:tag) # => #<Jig: [“<”, :tag, :“__a{}”, “>n”, :_, “</”, :tag, “>n”]>



201
202
203
204
# File 'lib/jig/xml.rb', line 201

def _anonymous(tag) # :nodoc:
  whitespace = Newlines.include?(tag.to_sym) && "\n" || ""
  new("<", tag.to_sym, ATTRS_GAP, ">#{whitespace}", GAP, "</", tag.to_sym, ">\n")
end

._element(tag) ⇒ Object

Construct a generic XML element with two gaps:

  • :__a filters a hash into an XML attribute list

  • :_ which is a default gap

Jig::XML._element(‘div’) # => #<Jig: [“<div”, :“__a{}”, “>n”, :_, “</div>n”]>



189
190
191
192
193
194
# File 'lib/jig/xml.rb', line 189

def _element(tag) 
  Element_Cache[tag] ||= begin
    whitespace = Newlines.include?(tag.to_sym) && "\n" || ""
    new("<#{tag}".freeze, ATTRS_GAP, ">#{whitespace}".freeze, GAP, "</#{tag}>\n".freeze).freeze
  end
end

._element!(tag) ⇒ Object

Construct an XML empty element with one gap:

  • :__a filters a hash into an XML attribute list

  • :_ which is a default gap

Jig::XML._element!(‘br’) # => #<Jig: [“<br”, :“__a{}”, “/>”]>



211
212
213
214
215
# File 'lib/jig/xml.rb', line 211

def _element!(tag) # :nodoc:
  Empty_Element_Cache[tag] ||= begin
    new("<#{tag}".freeze, ATTRS_GAP, "/>\n".freeze).freeze
  end
end

.anonymous(tag = 'div', *args) ⇒ Object

Construct an anonymous XML element. The single argument provides a name for a gap that replaces the XML start and end tags. Use plug to replace the gaps with an actual tag.

a = anonymous(:heading)       # => #<Jig: ["<", :heading, ">", :___, "</", :heading, ">\n"]>
b = a.plug(:heading, 'h1')    # => #<Jig: ["<", "h1", ">", :___, "</", "h1", ">\n"]>
b.plug('contents')            # => #<Jig: ["<", "h1", ">", "contents", "</", "h1", ">\n"]>


258
259
260
261
262
263
# File 'lib/jig/xml.rb', line 258

def anonymous(tag='div', *args)
  attrs = args.last.respond_to?(:fetch) && args.pop || nil
  args.push(lambda{|*x| yield(*x) }) if block_given?
  args.push GAP if args.empty?
  _anonymous(tag).plug(ATTRS => attrs, GAP => args)
end

.attribute(aname, value) ⇒ Object

Prepare aname and value for use as an attribute pair in an XML jig:

  • If value is nil or false, the empty string is returned.

  • If value is a symbol, an attribute gap is returned.

  • If value is a gap, the gap is returned.

  • If value is a proc, method or jig, the construction of the attribute is deferred by wrapping it in a proc inside a jig.

  • Otherwise, aname and value are converted to strings and rendered as an XML attribute pair.

Examples:

attribute('value', :firstname)                  # => Gap.new(:firstname) {...}
attribute('type', 'password')                   # => 'type="password"'
attribute('type', nil)                          # => ''
attribute('lastname', 'Einstein')               # => 'lastname="Einstein"'
a = attribute('lastname', Jig.new('Einstein'))  # => #<Jig: [#<Proc:0x00058624>]>
a.to_s                                          # => 'lastname="Einstein"'
b = attribute('lastname', Jig.new { })          # => #<Jig: [#<Proc:0x000523dc]>
b.to_s                                          # => ''
c = attribute('lastname', Jig.new {""})         # => #<Jig: [#<Proc:0x00055a3c]>
c.to_s                                          # => 'lastname=""'


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/jig/xml.rb', line 135

def attribute(aname, value)
  case value
  when nil, false
    ""
  when Symbol
    AGap.new(value, aname)
    #Gap.new(value) { |fill| attribute(aname, fill) }
  when Gap
    value
  when Proc, Method
    Jig.new { attribute(aname, value.call) }
  when Jig
    Jig.new { attribute(aname, value.to_s) }
  else
    " #{aname}=\"#{value}\""
  end
end

.cdata(*args) ⇒ Object

Construct a CDATA block

Jig::XML.cdata('This data can have < & >')

<![CDATA[
This data can have < & > ]]>


307
308
309
310
311
312
# File 'lib/jig/xml.rb', line 307

def cdata(*args)
  args.push(lambda{|*x| yield(*x) }) if block_given?
  args.push GAP if args.empty?
  jig = (Cache[:cdata] ||= new("<![CDATA[\n".freeze, GAP, " ]]>\n".freeze).freeze)
  jig.plug(GAP, *args)
end

.comment(*args) ⇒ Object

Construct an XML comment element.

Jig::XML.comment("This is a comment")

\<!-- This is a comment -->


319
320
321
322
323
324
325
326
# File 'lib/jig/xml.rb', line 319

def comment(*args)
  #:stopdoc:
  args.push(lambda{|*x| yield(*x) }) if block_given?
  args.push GAP if args.empty?
  jig = (Cache[:comment] ||= new("<!-- ".freeze, GAP, " -->\n".freeze).freeze)
  jig.plug(GAP, *args)
  #:startdoc:
end

.comments(*args) ⇒ Object

Construct a multiline XML comment element.

Jig::XML.comment("first line\nsecond line")

\<!-- 
first line
second line
-->


336
337
338
339
340
341
# File 'lib/jig/xml.rb', line 336

def comments(*args)
  args.push(lambda{|*x| yield(*x) }) if block_given?
  args.push GAP if args.empty?
  args.push "\n"
  comment("\n", *args)
end

.element(tag = 'div', *args) ⇒ Object

Construct a standard XML element with tag as the XML tag and a default gap for the contents of the element.

Jig::XML.element('h1')                    # => #<Jig: ["<h1", ">", :___, "</h1>\n"]>
Jig::XML.element('p', :class => 'body')   # => #<Jig: ["<p", "class=\"body\"", ">", :___, "</h1>\n"]>


269
270
271
272
273
274
# File 'lib/jig/xml.rb', line 269

def element(tag='div', *args)
  attrs = args.last.respond_to?(:fetch) && args.pop || nil
  args.push(lambda(&Proc.new)) if block_given?
  args.push GAP if args.empty?
  _element(tag).plug(ATTRS => attrs, GAP => args)
end

.element!(tag, *args) ⇒ Object

Construct a standard XML empty element with tag as the XML tag.

Jig::XHTML.element!('br')              # => '<br />'

h = { :name => 'year', :maxsize => 4, :type => :type }

j = Jig::XHTML.element!('input', h)    # => '<input name="year" maxsize="4"/>'
j.plug(:type => 'hidden')              # => '<input name="year" maxsize="4" type="hidden"/>'


284
285
286
287
# File 'lib/jig/xml.rb', line 284

def element!(tag, *args)
  attrs = args.last.respond_to?(:fetch) && args.pop || nil
  _element!(tag).plug(ATTRS => attrs, GAP => nil)
end

.escape(target) ⇒ Object

Returns a new string with <, >, and & converted to their HTML entity codes.



161
162
163
# File 'lib/jig/xml.rb', line 161

def escape(target)
  new(target.to_s.gsub(/[#{Entities}]/) {|m| "&#{Encode[m]};" })
end

.method_missing(symbol, *args, &block) ⇒ Object

Construct an HTML element using the method name as the element tag. If a method ends with ‘!’, the element is constructed as an empty element. If a method ends with ‘?’, the element is constructed as an anonymous element. If a method ends with ‘_’, it is stripped and the result used as a the tag. If a method contains an ‘_’, it is converted to a ‘:’ to provide XML namespace tags. If a method contains an ‘__’, it is converted to a single ‘_’.

Jig::XML.div.to_s # => “<div></div>” Jig::XML.div_.to_s # => “<div></div>” Jig::XML.br!to_s # => <br />“ Jig::XML.heading? # => Jig[”<“, :heading, ”>“, :_, ”</“, :heading, ”>“] Jig::XML.xhtml_h1 # => ”<xhtml:h1></xhtml:h1>“ Jig::XML.xhtml__h1 # => ”<xhtml_h1></xhtml_h1>“



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/jig/xml.rb', line 230

def method_missing(symbol, *args, &block)
  constructor = :element
  text = symbol.to_s
  if text =~ /!\z/
    text.chop!
    constructor = :element!
  elsif text =~ /\?\z/
    text.chop!
    constructor = :anonymous
  end
  if text =~ /_$/    # alternate for clashes with existing methods
    text.chop!
  end
  if text =~ /_/
    # Single _ gets converted to : for XML name spaces
    # Double _ gets converted to single _
    text = text.gsub(/([^_])_([^_])/){|x| "#{$1}:#{$2}"}.gsub(/__/, '_')
  end
  send(constructor, text, *args, &block)
end

.parseObject

In addition to the parsing done by Jig.parse, Jig::XML.parse recognizes and constructs attribute gaps from text of the form: %=attribute,gapname=

Jig.parse("<input%{=type,itype} />").plug(:itype, 'password')   # <input type="password" />


156
157
158
# File 'lib/jig/xml.rb', line 156

def parse(*)
  super
end

.parse_other(delim, stripped) ⇒ Object

Extend Jig.parse to recognize attribute gaps as %=attrname,gapname=. An attribute gap is returned.



167
168
169
170
171
172
173
174
175
176
177
# File 'lib/jig/xml.rb', line 167

def parse_other(delim, stripped)
  if delim == '='
    if stripped =~ /\A(.*),(.*)\z/
      new({ $1 => $2.to_sym})
    else
      raise ArgumentError, "invalid gap syntax: #{quoted}"
    end
  else
    super
  end
end

.xml(*args) ⇒ Object

Construct an XML declaration tag.

Jig::XML.xml                   # => '<?xml version="1.0">'
Jig::XML.xml(:lang => 'jp')    # => '<?xml version="1.0" lang="jp">'


293
294
295
296
297
298
# File 'lib/jig/xml.rb', line 293

def xml(*args)
  attrs = { :version => '1.0' }
  attrs.merge!(args.pop) if args.last.respond_to?(:fetch) 
  args.push(lambda{|*x| yield(*x) }) if block_given?
  new("<?xml", attrs, " ?>\n", *args)
end