mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Unvendor'd Builder. Now requires the Builder gem as a dependency
This commit is contained in:
parent
9029efce41
commit
f1fe71d754
10 changed files with 3 additions and 1041 deletions
|
@ -1,5 +1,7 @@
|
|||
*Edge*
|
||||
|
||||
* Unvendor'd Builder. Now requires the Builder gem as a dependency
|
||||
|
||||
* Unvendor'd TZInfo. Now requires the TZInfo gem as a dependency
|
||||
|
||||
* YAML serialization for OrderedHash. #3608 [Gregor Schmidt]
|
||||
|
|
|
@ -8,6 +8,7 @@ Gem::Specification.new do |s|
|
|||
|
||||
s.add_dependency('i18n', '~> 0.3.0')
|
||||
s.add_dependency('tzinfo', '~> 0.3.16')
|
||||
s.add_dependency('builder', '~> 2.1.2')
|
||||
|
||||
s.files = Dir['CHANGELOG', 'README', 'lib/**/*']
|
||||
s.require_path = 'lib'
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#--
|
||||
# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
|
||||
# All rights reserved.
|
||||
|
||||
# Permission is granted for use, copying, modification, distribution,
|
||||
# and distribution of modified versions of this work as long as the
|
||||
# above copyright notice is included.
|
||||
#++
|
||||
|
||||
######################################################################
|
||||
# BlankSlate provides an abstract base class with no predefined
|
||||
# methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
|
||||
# BlankSlate is useful as a base class when writing classes that
|
||||
# depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
|
||||
#
|
||||
class BlankSlate
|
||||
class << self
|
||||
|
||||
# Hide the method named +name+ in the BlankSlate class. Don't
|
||||
# hide +instance_eval+ or any method beginning with "__".
|
||||
def hide(name)
|
||||
if instance_methods.include?(name.to_s) and
|
||||
name !~ /^(__|instance_eval)/
|
||||
@hidden_methods ||= {}
|
||||
@hidden_methods[name.to_sym] = instance_method(name)
|
||||
undef_method name
|
||||
end
|
||||
end
|
||||
|
||||
def find_hidden_method(name)
|
||||
@hidden_methods ||= {}
|
||||
@hidden_methods[name] || superclass.find_hidden_method(name)
|
||||
end
|
||||
|
||||
# Redefine a previously hidden method so that it may be called on a blank
|
||||
# slate object.
|
||||
def reveal(name)
|
||||
bound_method = nil
|
||||
unbound_method = find_hidden_method(name)
|
||||
fail "Don't know how to reveal method '#{name}'" unless unbound_method
|
||||
define_method(name) do |*args|
|
||||
bound_method ||= unbound_method.bind(self)
|
||||
bound_method.call(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
instance_methods.each { |m| hide(m) }
|
||||
end
|
||||
|
||||
######################################################################
|
||||
# Since Ruby is very dynamic, methods added to the ancestors of
|
||||
# BlankSlate <em>after BlankSlate is defined</em> will show up in the
|
||||
# list of available BlankSlate methods. We handle this by defining a
|
||||
# hook in the Object and Kernel classes that will hide any method
|
||||
# defined after BlankSlate has been loaded.
|
||||
#
|
||||
module Kernel
|
||||
class << self
|
||||
alias_method :blank_slate_method_added, :method_added
|
||||
|
||||
# Detect method additions to Kernel and remove them in the
|
||||
# BlankSlate class.
|
||||
def method_added(name)
|
||||
result = blank_slate_method_added(name)
|
||||
return result if self != Kernel
|
||||
BlankSlate.hide(name)
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
######################################################################
|
||||
# Same as above, except in Object.
|
||||
#
|
||||
class Object
|
||||
class << self
|
||||
alias_method :blank_slate_method_added, :method_added
|
||||
|
||||
# Detect method additions to Object and remove them in the
|
||||
# BlankSlate class.
|
||||
def method_added(name)
|
||||
result = blank_slate_method_added(name)
|
||||
return result if self != Object
|
||||
BlankSlate.hide(name)
|
||||
result
|
||||
end
|
||||
|
||||
def find_hidden_method(name)
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
######################################################################
|
||||
# Also, modules included into Object need to be scanned and have their
|
||||
# instance methods removed from blank slate. In theory, modules
|
||||
# included into Kernel would have to be removed as well, but a
|
||||
# "feature" of Ruby prevents late includes into modules from being
|
||||
# exposed in the first place.
|
||||
#
|
||||
class Module
|
||||
alias blankslate_original_append_features append_features
|
||||
def append_features(mod)
|
||||
result = blankslate_original_append_features(mod)
|
||||
return result if mod != Object
|
||||
instance_methods.each do |name|
|
||||
BlankSlate.hide(name)
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
#--
|
||||
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
|
||||
# All rights reserved.
|
||||
|
||||
# Permission is granted for use, copying, modification, distribution,
|
||||
# and distribution of modified versions of this work as long as the
|
||||
# above copyright notice is included.
|
||||
#++
|
||||
|
||||
require 'builder/xmlmarkup'
|
||||
require 'builder/xmlevents'
|
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#--
|
||||
# Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org).
|
||||
# All rights reserved.
|
||||
|
||||
# Permission is granted for use, copying, modification, distribution,
|
||||
# and distribution of modified versions of this work as long as the
|
||||
# above copyright notice is included.
|
||||
#++
|
||||
|
||||
require 'blankslate'
|
||||
|
||||
######################################################################
|
||||
# BlankSlate has been promoted to a top level name and is now
|
||||
# available as a standalone gem. We make the name available in the
|
||||
# Builder namespace for compatibility.
|
||||
#
|
||||
module Builder
|
||||
BlankSlate = ::BlankSlate
|
||||
end
|
|
@ -1,250 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#--
|
||||
# Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
|
||||
# Copyright 2005 by Scott Barron (scott@elitists.net).
|
||||
# All rights reserved.
|
||||
#
|
||||
# Permission is granted for use, copying, modification, distribution,
|
||||
# and distribution of modified versions of this work as long as the
|
||||
# above copyright notice is included.
|
||||
#
|
||||
# Much of this is taken from Jim's work in xmlbase.rb and xmlmarkup.rb.
|
||||
# Documentation has also been copied and pasted and modified to reflect
|
||||
# that we're building CSS here instead of XML. Jim is conducting the
|
||||
# orchestra here and I'm just off in the corner playing a flute.
|
||||
#++
|
||||
|
||||
# Provide a flexible and easy to use Builder for creating Cascading
|
||||
# Style Sheets (CSS).
|
||||
|
||||
|
||||
require 'builder/blankslate'
|
||||
|
||||
module Builder
|
||||
|
||||
# Create a Cascading Style Sheet (CSS) using Ruby.
|
||||
#
|
||||
# Example usage:
|
||||
#
|
||||
# css = Builder::CSS.new
|
||||
#
|
||||
# text_color = '#7F7F7F'
|
||||
# preferred_fonts = 'Helvetica, Arial, sans_serif'
|
||||
#
|
||||
# css.comment! 'This is our stylesheet'
|
||||
# css.body {
|
||||
# background_color '#FAFAFA'
|
||||
# font_size 'small'
|
||||
# font_family preferred_fonts
|
||||
# color text_color
|
||||
# }
|
||||
#
|
||||
# css.id!('navbar') {
|
||||
# width '500px'
|
||||
# }
|
||||
#
|
||||
# css.class!('navitem') {
|
||||
# color 'red'
|
||||
# }
|
||||
#
|
||||
# css.a :hover {
|
||||
# text_decoration 'underline'
|
||||
# }
|
||||
#
|
||||
# css.div(:id => 'menu') {
|
||||
# background 'green'
|
||||
# }
|
||||
#
|
||||
# css.div(:class => 'foo') {
|
||||
# background 'red'
|
||||
# }
|
||||
#
|
||||
# This will yield the following stylesheet:
|
||||
#
|
||||
# /* This is our stylesheet */
|
||||
# body {
|
||||
# background_color: #FAFAFA;
|
||||
# font_size: small;
|
||||
# font_family: Helvetica, Arial, sans_serif;
|
||||
# color: #7F7F7F;
|
||||
# }
|
||||
#
|
||||
# #navbar {
|
||||
# width: 500px;
|
||||
# }
|
||||
#
|
||||
# .navitem {
|
||||
# color: red;
|
||||
# }
|
||||
#
|
||||
# a:hover {
|
||||
# text_decoration: underline;
|
||||
# }
|
||||
#
|
||||
# div#menu {
|
||||
# background: green;
|
||||
# }
|
||||
#
|
||||
# div.foo {
|
||||
# background: red;
|
||||
# }
|
||||
#
|
||||
class CSS < BlankSlate
|
||||
|
||||
# Create a CSS builder.
|
||||
#
|
||||
# out:: Object receiving the markup.1 +out+ must respond to
|
||||
# <tt><<</tt>.
|
||||
# indent:: Number of spaces used for indentation (0 implies no
|
||||
# indentation and no line breaks).
|
||||
#
|
||||
def initialize(indent=2)
|
||||
@indent = indent
|
||||
@target = []
|
||||
@parts = []
|
||||
@library = {}
|
||||
end
|
||||
|
||||
def +(part)
|
||||
_join_with_op! '+'
|
||||
self
|
||||
end
|
||||
|
||||
def >>(part)
|
||||
_join_with_op! ''
|
||||
self
|
||||
end
|
||||
|
||||
def >(part)
|
||||
_join_with_op! '>'
|
||||
self
|
||||
end
|
||||
|
||||
def |(part)
|
||||
_join_with_op! ','
|
||||
self
|
||||
end
|
||||
|
||||
# Return the target of the builder
|
||||
def target!
|
||||
@target * ''
|
||||
end
|
||||
|
||||
# Create a comment string in the output.
|
||||
def comment!(comment_text)
|
||||
@target << "/* #{comment_text} */\n"
|
||||
end
|
||||
|
||||
def id!(arg, &block)
|
||||
_start_container('#'+arg.to_s, nil, block_given?)
|
||||
_css_block(block) if block
|
||||
_unify_block
|
||||
self
|
||||
end
|
||||
|
||||
def class!(arg, &block)
|
||||
_start_container('.'+arg.to_s, nil, block_given?)
|
||||
_css_block(block) if block
|
||||
_unify_block
|
||||
self
|
||||
end
|
||||
|
||||
def store!(sym, &block)
|
||||
@library[sym] = block.to_proc
|
||||
end
|
||||
|
||||
def group!(*args, &block)
|
||||
args.each do |arg|
|
||||
if arg.is_a?(Symbol)
|
||||
instance_eval(&@library[arg])
|
||||
else
|
||||
instance_eval(&arg)
|
||||
end
|
||||
_text ', ' unless arg == args.last
|
||||
end
|
||||
if block
|
||||
_css_block(block)
|
||||
_unify_block
|
||||
end
|
||||
end
|
||||
|
||||
def method_missing(sym, *args, &block)
|
||||
sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
|
||||
if block
|
||||
_start_container(sym, args.first)
|
||||
_css_block(block)
|
||||
_unify_block
|
||||
elsif @in_block
|
||||
_indent
|
||||
_css_line(sym, *args)
|
||||
_newline
|
||||
return self
|
||||
else
|
||||
_start_container(sym, args.first, false)
|
||||
_unify_block
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# "Cargo culted" from Jim who also "cargo culted" it. See xmlbase.rb.
|
||||
def nil?
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
def _unify_block
|
||||
@target << @parts * ''
|
||||
@parts = []
|
||||
end
|
||||
|
||||
def _join_with_op!(op)
|
||||
rhs, lhs = @target.pop, @target.pop
|
||||
@target << "#{lhs} #{op} #{rhs}"
|
||||
end
|
||||
|
||||
def _text(text)
|
||||
@parts << text
|
||||
end
|
||||
|
||||
def _css_block(block)
|
||||
_newline
|
||||
_nested_structures(block)
|
||||
_end_container
|
||||
_end_block
|
||||
end
|
||||
|
||||
def _end_block
|
||||
_newline
|
||||
_newline
|
||||
end
|
||||
|
||||
def _newline
|
||||
_text "\n"
|
||||
end
|
||||
|
||||
def _indent
|
||||
_text ' ' * @indent
|
||||
end
|
||||
|
||||
def _nested_structures(block)
|
||||
@in_block = true
|
||||
self.instance_eval(&block)
|
||||
@in_block = false
|
||||
end
|
||||
|
||||
def _start_container(sym, atts = {}, with_bracket = true)
|
||||
selector = sym.to_s
|
||||
selector << ".#{atts[:class]}" if atts && atts[:class]
|
||||
selector << '#' + "#{atts[:id]}" if atts && atts[:id]
|
||||
@parts << "#{selector}#{with_bracket ? ' {' : ''}"
|
||||
end
|
||||
|
||||
def _end_container
|
||||
@parts << "}"
|
||||
end
|
||||
|
||||
def _css_line(sym, *args)
|
||||
_text("#{sym.to_s.gsub('_','-')}: #{args * ' '};")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,115 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# The XChar library is provided courtesy of Sam Ruby (See
|
||||
# http://intertwingly.net/stories/2005/09/28/xchar.rb)
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# If the Builder::XChar module is not currently defined, fail on any
|
||||
# name clashes in standard library classes.
|
||||
|
||||
module Builder
|
||||
def self.check_for_name_collision(klass, method_name, defined_constant=nil)
|
||||
if klass.instance_methods.include?(method_name.to_s)
|
||||
fail RuntimeError,
|
||||
"Name Collision: Method '#{method_name}' is already defined in #{klass}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ! defined?(Builder::XChar)
|
||||
Builder.check_for_name_collision(String, "to_xs")
|
||||
Builder.check_for_name_collision(Fixnum, "xchr")
|
||||
end
|
||||
|
||||
######################################################################
|
||||
module Builder
|
||||
|
||||
####################################################################
|
||||
# XML Character converter, from Sam Ruby:
|
||||
# (see http://intertwingly.net/stories/2005/09/28/xchar.rb).
|
||||
#
|
||||
module XChar # :nodoc:
|
||||
|
||||
# See
|
||||
# http://intertwingly.net/stories/2004/04/14/i18n.html#CleaningWindows
|
||||
# for details.
|
||||
CP1252 = { # :nodoc:
|
||||
128 => 8364, # euro sign
|
||||
130 => 8218, # single low-9 quotation mark
|
||||
131 => 402, # latin small letter f with hook
|
||||
132 => 8222, # double low-9 quotation mark
|
||||
133 => 8230, # horizontal ellipsis
|
||||
134 => 8224, # dagger
|
||||
135 => 8225, # double dagger
|
||||
136 => 710, # modifier letter circumflex accent
|
||||
137 => 8240, # per mille sign
|
||||
138 => 352, # latin capital letter s with caron
|
||||
139 => 8249, # single left-pointing angle quotation mark
|
||||
140 => 338, # latin capital ligature oe
|
||||
142 => 381, # latin capital letter z with caron
|
||||
145 => 8216, # left single quotation mark
|
||||
146 => 8217, # right single quotation mark
|
||||
147 => 8220, # left double quotation mark
|
||||
148 => 8221, # right double quotation mark
|
||||
149 => 8226, # bullet
|
||||
150 => 8211, # en dash
|
||||
151 => 8212, # em dash
|
||||
152 => 732, # small tilde
|
||||
153 => 8482, # trade mark sign
|
||||
154 => 353, # latin small letter s with caron
|
||||
155 => 8250, # single right-pointing angle quotation mark
|
||||
156 => 339, # latin small ligature oe
|
||||
158 => 382, # latin small letter z with caron
|
||||
159 => 376, # latin capital letter y with diaeresis
|
||||
}
|
||||
|
||||
# See http://www.w3.org/TR/REC-xml/#dt-chardata for details.
|
||||
PREDEFINED = {
|
||||
38 => '&', # ampersand
|
||||
60 => '<', # left angle bracket
|
||||
62 => '>', # right angle bracket
|
||||
}
|
||||
|
||||
# See http://www.w3.org/TR/REC-xml/#charsets for details.
|
||||
VALID = [
|
||||
0x9, 0xA, 0xD,
|
||||
(0x20..0xD7FF),
|
||||
(0xE000..0xFFFD),
|
||||
(0x10000..0x10FFFF)
|
||||
]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
######################################################################
|
||||
# Enhance the Fixnum class with a XML escaped character conversion.
|
||||
#
|
||||
class Fixnum
|
||||
XChar = Builder::XChar if ! defined?(XChar)
|
||||
|
||||
# XML escaped version of chr
|
||||
def xchr
|
||||
n = XChar::CP1252[self] || self
|
||||
case n when *XChar::VALID
|
||||
XChar::PREDEFINED[n] or (n<128 ? n.chr : "&##{n};")
|
||||
else
|
||||
'*'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
######################################################################
|
||||
# Enhance the String class with a XML escaped character version of
|
||||
# to_s.
|
||||
#
|
||||
class String
|
||||
# XML escaped version of to_s
|
||||
def to_xs
|
||||
unpack('U*').map {|n| n.xchr}.join # ASCII, UTF-8
|
||||
rescue
|
||||
unpack('C*').map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
|
||||
end
|
||||
end
|
|
@ -1,139 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'builder/blankslate'
|
||||
|
||||
module Builder
|
||||
|
||||
# Generic error for builder
|
||||
class IllegalBlockError < RuntimeError; end
|
||||
|
||||
# XmlBase is a base class for building XML builders. See
|
||||
# Builder::XmlMarkup and Builder::XmlEvents for examples.
|
||||
class XmlBase < BlankSlate
|
||||
|
||||
# Create an XML markup builder.
|
||||
#
|
||||
# out:: Object receiving the markup. +out+ must respond to
|
||||
# <tt><<</tt>.
|
||||
# indent:: Number of spaces used for indentation (0 implies no
|
||||
# indentation and no line breaks).
|
||||
# initial:: Level of initial indentation.
|
||||
#
|
||||
def initialize(indent=0, initial=0)
|
||||
@indent = indent
|
||||
@level = initial
|
||||
end
|
||||
|
||||
# Create a tag named +sym+. Other than the first argument which
|
||||
# is the tag name, the arguments are the same as the tags
|
||||
# implemented via <tt>method_missing</tt>.
|
||||
def tag!(sym, *args, &block)
|
||||
method_missing(sym.to_sym, *args, &block)
|
||||
end
|
||||
|
||||
# Create XML markup based on the name of the method. This method
|
||||
# is never invoked directly, but is called for each markup method
|
||||
# in the markup block.
|
||||
def method_missing(sym, *args, &block)
|
||||
text = nil
|
||||
attrs = nil
|
||||
sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
|
||||
args.each do |arg|
|
||||
case arg
|
||||
when Hash
|
||||
attrs ||= {}
|
||||
attrs.merge!(arg)
|
||||
else
|
||||
text ||= ''
|
||||
text << arg.to_s
|
||||
end
|
||||
end
|
||||
if block
|
||||
unless text.nil?
|
||||
raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
|
||||
end
|
||||
_indent
|
||||
_start_tag(sym, attrs)
|
||||
_newline
|
||||
_nested_structures(block)
|
||||
_indent
|
||||
_end_tag(sym)
|
||||
_newline
|
||||
elsif text.nil?
|
||||
_indent
|
||||
_start_tag(sym, attrs, true)
|
||||
_newline
|
||||
else
|
||||
_indent
|
||||
_start_tag(sym, attrs)
|
||||
text! text
|
||||
_end_tag(sym)
|
||||
_newline
|
||||
end
|
||||
@target
|
||||
end
|
||||
|
||||
# Append text to the output target. Escape any markup. May be
|
||||
# used within the markup brackets as:
|
||||
#
|
||||
# builder.p { |b| b.br; b.text! "HI" } #=> <p><br/>HI</p>
|
||||
def text!(text)
|
||||
_text(_escape(text))
|
||||
end
|
||||
|
||||
# Append text to the output target without escaping any markup.
|
||||
# May be used within the markup brackets as:
|
||||
#
|
||||
# builder.p { |x| x << "<br/>HI" } #=> <p><br/>HI</p>
|
||||
#
|
||||
# This is useful when using non-builder enabled software that
|
||||
# generates strings. Just insert the string directly into the
|
||||
# builder without changing the inserted markup.
|
||||
#
|
||||
# It is also useful for stacking builder objects. Builders only
|
||||
# use <tt><<</tt> to append to the target, so by supporting this
|
||||
# method/operation builders can use other builders as their
|
||||
# targets.
|
||||
def <<(text)
|
||||
_text(text)
|
||||
end
|
||||
|
||||
# For some reason, nil? is sent to the XmlMarkup object. If nil?
|
||||
# is not defined and method_missing is invoked, some strange kind
|
||||
# of recursion happens. Since nil? won't ever be an XML tag, it
|
||||
# is pretty safe to define it here. (Note: this is an example of
|
||||
# cargo cult programming,
|
||||
# cf. http://fishbowl.pastiche.org/2004/10/13/cargo_cult_programming).
|
||||
def nil?
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
require 'builder/xchar'
|
||||
def _escape(text)
|
||||
text.to_xs
|
||||
end
|
||||
|
||||
def _escape_quote(text)
|
||||
_escape(text).gsub(%r{"}, '"') # " WART
|
||||
end
|
||||
|
||||
def _newline
|
||||
return if @indent == 0
|
||||
text! "\n"
|
||||
end
|
||||
|
||||
def _indent
|
||||
return if @indent == 0 || @level == 0
|
||||
text!(" " * (@level * @indent))
|
||||
end
|
||||
|
||||
def _nested_structures(block)
|
||||
@level += 1
|
||||
block.call(self)
|
||||
ensure
|
||||
@level -= 1
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,63 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
#--
|
||||
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
|
||||
# All rights reserved.
|
||||
|
||||
# Permission is granted for use, copying, modification, distribution,
|
||||
# and distribution of modified versions of this work as long as the
|
||||
# above copyright notice is included.
|
||||
#++
|
||||
|
||||
require 'builder/xmlmarkup'
|
||||
|
||||
module Builder
|
||||
|
||||
# Create a series of SAX-like XML events (e.g. start_tag, end_tag)
|
||||
# from the markup code. XmlEvent objects are used in a way similar
|
||||
# to XmlMarkup objects, except that a series of events are generated
|
||||
# and passed to a handler rather than generating character-based
|
||||
# markup.
|
||||
#
|
||||
# Usage:
|
||||
# xe = Builder::XmlEvents.new(handler)
|
||||
# xe.title("HI") # Sends start_tag/end_tag/text messages to the handler.
|
||||
#
|
||||
# Indentation may also be selected by providing value for the
|
||||
# indentation size and initial indentation level.
|
||||
#
|
||||
# xe = Builder::XmlEvents.new(handler, indent_size, initial_indent_level)
|
||||
#
|
||||
# == XML Event Handler
|
||||
#
|
||||
# The handler object must expect the following events.
|
||||
#
|
||||
# [<tt>start_tag(tag, attrs)</tt>]
|
||||
# Announces that a new tag has been found. +tag+ is the name of
|
||||
# the tag and +attrs+ is a hash of attributes for the tag.
|
||||
#
|
||||
# [<tt>end_tag(tag)</tt>]
|
||||
# Announces that an end tag for +tag+ has been found.
|
||||
#
|
||||
# [<tt>text(text)</tt>]
|
||||
# Announces that a string of characters (+text+) has been found.
|
||||
# A series of characters may be broken up into more than one
|
||||
# +text+ call, so the client cannot assume that a single
|
||||
# callback contains all the text data.
|
||||
#
|
||||
class XmlEvents < XmlMarkup
|
||||
def text!(text)
|
||||
@target.text(text)
|
||||
end
|
||||
|
||||
def _start_tag(sym, attrs, end_too=false)
|
||||
@target.start_tag(sym, attrs)
|
||||
_end_tag(sym) if end_too
|
||||
end
|
||||
|
||||
def _end_tag(sym)
|
||||
@target.end_tag(sym)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,328 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#--
|
||||
# Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
|
||||
# All rights reserved.
|
||||
|
||||
# Permission is granted for use, copying, modification, distribution,
|
||||
# and distribution of modified versions of this work as long as the
|
||||
# above copyright notice is included.
|
||||
#++
|
||||
|
||||
# Provide a flexible and easy to use Builder for creating XML markup.
|
||||
# See XmlBuilder for usage details.
|
||||
|
||||
require 'builder/xmlbase'
|
||||
|
||||
module Builder
|
||||
|
||||
# Create XML markup easily. All (well, almost all) methods sent to
|
||||
# an XmlMarkup object will be translated to the equivalent XML
|
||||
# markup. Any method with a block will be treated as an XML markup
|
||||
# tag with nested markup in the block.
|
||||
#
|
||||
# Examples will demonstrate this easier than words. In the
|
||||
# following, +xm+ is an +XmlMarkup+ object.
|
||||
#
|
||||
# xm.em("emphasized") # => <em>emphasized</em>
|
||||
# xm.em { xmm.b("emp & bold") } # => <em><b>emph & bold</b></em>
|
||||
# xm.a("A Link", "href"=>"http://onestepback.org")
|
||||
# # => <a href="http://onestepback.org">A Link</a>
|
||||
# xm.div { br } # => <div><br/></div>
|
||||
# xm.target("name"=>"compile", "option"=>"fast")
|
||||
# # => <target option="fast" name="compile"\>
|
||||
# # NOTE: order of attributes is not specified.
|
||||
#
|
||||
# xm.instruct! # <?xml version="1.0" encoding="UTF-8"?>
|
||||
# xm.html { # <html>
|
||||
# xm.head { # <head>
|
||||
# xm.title("History") # <title>History</title>
|
||||
# } # </head>
|
||||
# xm.body { # <body>
|
||||
# xm.comment! "HI" # <! -- HI -->
|
||||
# xm.h1("Header") # <h1>Header</h1>
|
||||
# xm.p("paragraph") # <p>paragraph</p>
|
||||
# } # </body>
|
||||
# } # </html>
|
||||
#
|
||||
# == Notes:
|
||||
#
|
||||
# * The order that attributes are inserted in markup tags is
|
||||
# undefined.
|
||||
#
|
||||
# * Sometimes you wish to insert text without enclosing tags. Use
|
||||
# the <tt>text!</tt> method to accomplish this.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# xm.div { # <div>
|
||||
# xm.text! "line"; xm.br # line<br/>
|
||||
# xm.text! "another line"; xmbr # another line<br/>
|
||||
# } # </div>
|
||||
#
|
||||
# * The special XML characters <, >, and & are converted to <,
|
||||
# > and & automatically. Use the <tt><<</tt> operation to
|
||||
# insert text without modification.
|
||||
#
|
||||
# * Sometimes tags use special characters not allowed in ruby
|
||||
# identifiers. Use the <tt>tag!</tt> method to handle these
|
||||
# cases.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# xml.tag!("SOAP:Envelope") { ... }
|
||||
#
|
||||
# will produce ...
|
||||
#
|
||||
# <SOAP:Envelope> ... </SOAP:Envelope>"
|
||||
#
|
||||
# <tt>tag!</tt> will also take text and attribute arguments (after
|
||||
# the tag name) like normal markup methods. (But see the next
|
||||
# bullet item for a better way to handle XML namespaces).
|
||||
#
|
||||
# * Direct support for XML namespaces is now available. If the
|
||||
# first argument to a tag call is a symbol, it will be joined to
|
||||
# the tag to produce a namespace:tag combination. It is easier to
|
||||
# show this than describe it.
|
||||
#
|
||||
# xml.SOAP :Envelope do ... end
|
||||
#
|
||||
# Just put a space before the colon in a namespace to produce the
|
||||
# right form for builder (e.g. "<tt>SOAP:Envelope</tt>" =>
|
||||
# "<tt>xml.SOAP :Envelope</tt>")
|
||||
#
|
||||
# * XmlMarkup builds the markup in any object (called a _target_)
|
||||
# that accepts the <tt><<</tt> method. If no target is given,
|
||||
# then XmlMarkup defaults to a string target.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# xm = Builder::XmlMarkup.new
|
||||
# result = xm.title("yada")
|
||||
# # result is a string containing the markup.
|
||||
#
|
||||
# buffer = ""
|
||||
# xm = Builder::XmlMarkup.new(buffer)
|
||||
# # The markup is appended to buffer (using <<)
|
||||
#
|
||||
# xm = Builder::XmlMarkup.new(STDOUT)
|
||||
# # The markup is written to STDOUT (using <<)
|
||||
#
|
||||
# xm = Builder::XmlMarkup.new
|
||||
# x2 = Builder::XmlMarkup.new(:target=>xm)
|
||||
# # Markup written to +x2+ will be send to +xm+.
|
||||
#
|
||||
# * Indentation is enabled by providing the number of spaces to
|
||||
# indent for each level as a second argument to XmlBuilder.new.
|
||||
# Initial indentation may be specified using a third parameter.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# xm = Builder.new(:indent=>2)
|
||||
# # xm will produce nicely formatted and indented XML.
|
||||
#
|
||||
# xm = Builder.new(:indent=>2, :margin=>4)
|
||||
# # xm will produce nicely formatted and indented XML with 2
|
||||
# # spaces per indent and an over all indentation level of 4.
|
||||
#
|
||||
# builder = Builder::XmlMarkup.new(:target=>$stdout, :indent=>2)
|
||||
# builder.name { |b| b.first("Jim"); b.last("Weirich) }
|
||||
# # prints:
|
||||
# # <name>
|
||||
# # <first>Jim</first>
|
||||
# # <last>Weirich</last>
|
||||
# # </name>
|
||||
#
|
||||
# * The instance_eval implementation which forces self to refer to
|
||||
# the message receiver as self is now obsolete. We now use normal
|
||||
# block calls to execute the markup block. This means that all
|
||||
# markup methods must now be explicitly send to the xml builder.
|
||||
# For instance, instead of
|
||||
#
|
||||
# xml.div { strong("text") }
|
||||
#
|
||||
# you need to write:
|
||||
#
|
||||
# xml.div { xml.strong("text") }
|
||||
#
|
||||
# Although more verbose, the subtle change in semantics within the
|
||||
# block was found to be prone to error. To make this change a
|
||||
# little less cumbersome, the markup block now gets the markup
|
||||
# object sent as an argument, allowing you to use a shorter alias
|
||||
# within the block.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# xml_builder = Builder::XmlMarkup.new
|
||||
# xml_builder.div { |xml|
|
||||
# xml.stong("text")
|
||||
# }
|
||||
#
|
||||
class XmlMarkup < XmlBase
|
||||
|
||||
# Create an XML markup builder. Parameters are specified by an
|
||||
# option hash.
|
||||
#
|
||||
# :target=><em>target_object</em>::
|
||||
# Object receiving the markup. +out+ must respond to the
|
||||
# <tt><<</tt> operator. The default is a plain string target.
|
||||
#
|
||||
# :indent=><em>indentation</em>::
|
||||
# Number of spaces used for indentation. The default is no
|
||||
# indentation and no line breaks.
|
||||
#
|
||||
# :margin=><em>initial_indentation_level</em>::
|
||||
# Amount of initial indentation (specified in levels, not
|
||||
# spaces).
|
||||
#
|
||||
# :escape_attrs=><b>OBSOLETE</em>::
|
||||
# The :escape_attrs option is no longer supported by builder
|
||||
# (and will be quietly ignored). String attribute values are
|
||||
# now automatically escaped. If you need unescaped attribute
|
||||
# values (perhaps you are using entities in the attribute
|
||||
# values), then give the value as a Symbol. This allows much
|
||||
# finer control over escaping attribute values.
|
||||
#
|
||||
def initialize(options={})
|
||||
indent = options[:indent] || 0
|
||||
margin = options[:margin] || 0
|
||||
super(indent, margin)
|
||||
@target = options[:target] || ""
|
||||
end
|
||||
|
||||
# Return the target of the builder.
|
||||
def target!
|
||||
@target
|
||||
end
|
||||
|
||||
def comment!(comment_text)
|
||||
_ensure_no_block block_given?
|
||||
_special("<!-- ", " -->", comment_text, nil)
|
||||
end
|
||||
|
||||
# Insert an XML declaration into the XML markup.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# xml.declare! :ELEMENT, :blah, "yada"
|
||||
# # => <!ELEMENT blah "yada">
|
||||
def declare!(inst, *args, &block)
|
||||
_indent
|
||||
@target << "<!#{inst}"
|
||||
args.each do |arg|
|
||||
case arg
|
||||
when String
|
||||
@target << %{ "#{arg}"} # " WART
|
||||
when Symbol
|
||||
@target << " #{arg}"
|
||||
end
|
||||
end
|
||||
if block_given?
|
||||
@target << " ["
|
||||
_newline
|
||||
_nested_structures(block)
|
||||
@target << "]"
|
||||
end
|
||||
@target << ">"
|
||||
_newline
|
||||
end
|
||||
|
||||
# Insert a processing instruction into the XML markup. E.g.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# xml.instruct!
|
||||
# #=> <?xml version="1.0" encoding="UTF-8"?>
|
||||
# xml.instruct! :aaa, :bbb=>"ccc"
|
||||
# #=> <?aaa bbb="ccc"?>
|
||||
#
|
||||
def instruct!(directive_tag=:xml, attrs={})
|
||||
_ensure_no_block block_given?
|
||||
if directive_tag == :xml
|
||||
a = { :version=>"1.0", :encoding=>"UTF-8" }
|
||||
attrs = a.merge attrs
|
||||
end
|
||||
_special(
|
||||
"<?#{directive_tag}",
|
||||
"?>",
|
||||
nil,
|
||||
attrs,
|
||||
[:version, :encoding, :standalone])
|
||||
end
|
||||
|
||||
# Insert a CDATA section into the XML markup.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# xml.cdata!("text to be included in cdata")
|
||||
# #=> <![CDATA[text to be included in cdata]]>
|
||||
#
|
||||
def cdata!(text)
|
||||
_ensure_no_block block_given?
|
||||
_special("<![CDATA[", "]]>", text, nil)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# NOTE: All private methods of a builder object are prefixed when
|
||||
# a "_" character to avoid possible conflict with XML tag names.
|
||||
|
||||
# Insert text directly in to the builder's target.
|
||||
def _text(text)
|
||||
@target << text
|
||||
end
|
||||
|
||||
# Insert special instruction.
|
||||
def _special(open, close, data=nil, attrs=nil, order=[])
|
||||
_indent
|
||||
@target << open
|
||||
@target << data if data
|
||||
_insert_attributes(attrs, order) if attrs
|
||||
@target << close
|
||||
_newline
|
||||
end
|
||||
|
||||
# Start an XML tag. If <tt>end_too</tt> is true, then the start
|
||||
# tag is also the end tag (e.g. <br/>
|
||||
def _start_tag(sym, attrs, end_too=false)
|
||||
@target << "<#{sym}"
|
||||
_insert_attributes(attrs)
|
||||
@target << "/" if end_too
|
||||
@target << ">"
|
||||
end
|
||||
|
||||
# Insert an ending tag.
|
||||
def _end_tag(sym)
|
||||
@target << "</#{sym}>"
|
||||
end
|
||||
|
||||
# Insert the attributes (given in the hash).
|
||||
def _insert_attributes(attrs, order=[])
|
||||
return if attrs.nil?
|
||||
order.each do |k|
|
||||
v = attrs[k]
|
||||
@target << %{ #{k}="#{_attr_value(v)}"} if v # " WART
|
||||
end
|
||||
attrs.each do |k, v|
|
||||
@target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k) # " WART
|
||||
end
|
||||
end
|
||||
|
||||
def _attr_value(value)
|
||||
case value
|
||||
when Symbol
|
||||
value.to_s
|
||||
else
|
||||
_escape_quote(value.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def _ensure_no_block(got_block)
|
||||
if got_block
|
||||
fail IllegalBlockError,
|
||||
"Blocks are not allowed on XML instructions"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in a new issue