hpricot 0.6-jruby

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. data/CHANGELOG +62 -0
  2. data/COPYING +18 -0
  3. data/README +284 -0
  4. data/Rakefile +211 -0
  5. data/ext/hpricot_scan/HpricotScanService.java +1340 -0
  6. data/ext/hpricot_scan/extconf.rb +6 -0
  7. data/ext/hpricot_scan/hpricot_common.rl +76 -0
  8. data/ext/hpricot_scan/hpricot_scan.c +5976 -0
  9. data/ext/hpricot_scan/hpricot_scan.h +79 -0
  10. data/ext/hpricot_scan/hpricot_scan.java.rl +363 -0
  11. data/ext/hpricot_scan/hpricot_scan.rl +273 -0
  12. data/extras/mingw-rbconfig.rb +176 -0
  13. data/lib/hpricot.rb +26 -0
  14. data/lib/hpricot/blankslate.rb +63 -0
  15. data/lib/hpricot/builder.rb +200 -0
  16. data/lib/hpricot/elements.rb +510 -0
  17. data/lib/hpricot/htmlinfo.rb +672 -0
  18. data/lib/hpricot/inspect.rb +107 -0
  19. data/lib/hpricot/modules.rb +37 -0
  20. data/lib/hpricot/parse.rb +297 -0
  21. data/lib/hpricot/tag.rb +228 -0
  22. data/lib/hpricot/tags.rb +164 -0
  23. data/lib/hpricot/traverse.rb +821 -0
  24. data/lib/hpricot/xchar.rb +94 -0
  25. data/lib/i686-linux/hpricot_scan.jar +0 -0
  26. data/test/files/basic.xhtml +17 -0
  27. data/test/files/boingboing.html +2266 -0
  28. data/test/files/cy0.html +3653 -0
  29. data/test/files/immob.html +400 -0
  30. data/test/files/pace_application.html +1320 -0
  31. data/test/files/tenderlove.html +16 -0
  32. data/test/files/uswebgen.html +220 -0
  33. data/test/files/utf8.html +1054 -0
  34. data/test/files/week9.html +1723 -0
  35. data/test/files/why.xml +19 -0
  36. data/test/load_files.rb +7 -0
  37. data/test/test_alter.rb +65 -0
  38. data/test/test_builder.rb +24 -0
  39. data/test/test_parser.rb +379 -0
  40. data/test/test_paths.rb +16 -0
  41. data/test/test_preserved.rb +66 -0
  42. data/test/test_xml.rb +28 -0
  43. metadata +98 -0
@@ -0,0 +1,176 @@
1
+
2
+ # This rbconfig.rb corresponds to a Ruby installation for win32 cross-compiled
3
+ # with mingw under i686-linux. It can be used to cross-compile extensions for
4
+ # win32 using said toolchain.
5
+ #
6
+ # This file assumes that a cross-compiled mingw32 build (compatible with the
7
+ # mswin32 builds) is installed under $HOME/ruby-mingw32.
8
+
9
+ module Config
10
+ #RUBY_VERSION == "1.8.5" or
11
+ # raise "ruby lib version (1.8.5) doesn't match executable version (#{RUBY_VERSION})"
12
+
13
+ mingw32 = ENV['MINGW32_RUBY'] || "#{ENV["HOME"]}/ruby-mingw32"
14
+ mingwpre = ENV['MINGW32_PREFIX']
15
+ TOPDIR = File.dirname(__FILE__).chomp!("/lib/ruby/1.8/i386-mingw32")
16
+ DESTDIR = '' unless defined? DESTDIR
17
+ CONFIG = {}
18
+ CONFIG["DESTDIR"] = DESTDIR
19
+ CONFIG["INSTALL"] = "/usr/bin/install -c"
20
+ CONFIG["prefix"] = (TOPDIR || DESTDIR + mingw32)
21
+ CONFIG["EXEEXT"] = ".exe"
22
+ CONFIG["ruby_install_name"] = "ruby"
23
+ CONFIG["RUBY_INSTALL_NAME"] = "ruby"
24
+ CONFIG["RUBY_SO_NAME"] = "msvcrt-ruby18"
25
+ CONFIG["SHELL"] = "/bin/sh"
26
+ CONFIG["PATH_SEPARATOR"] = ":"
27
+ CONFIG["PACKAGE_NAME"] = ""
28
+ CONFIG["PACKAGE_TARNAME"] = ""
29
+ CONFIG["PACKAGE_VERSION"] = ""
30
+ CONFIG["PACKAGE_STRING"] = ""
31
+ CONFIG["PACKAGE_BUGREPORT"] = ""
32
+ CONFIG["exec_prefix"] = "$(prefix)"
33
+ CONFIG["bindir"] = "$(exec_prefix)/bin"
34
+ CONFIG["sbindir"] = "$(exec_prefix)/sbin"
35
+ CONFIG["libexecdir"] = "$(exec_prefix)/libexec"
36
+ CONFIG["datadir"] = "$(prefix)/share"
37
+ CONFIG["sysconfdir"] = "$(prefix)/etc"
38
+ CONFIG["sharedstatedir"] = "$(prefix)/com"
39
+ CONFIG["localstatedir"] = "$(prefix)/var"
40
+ CONFIG["libdir"] = "$(exec_prefix)/lib"
41
+ CONFIG["includedir"] = "$(prefix)/include"
42
+ CONFIG["oldincludedir"] = "/usr/include"
43
+ CONFIG["infodir"] = "$(prefix)/info"
44
+ CONFIG["mandir"] = "$(prefix)/man"
45
+ CONFIG["build_alias"] = "i686-linux"
46
+ CONFIG["host_alias"] = "#{mingwpre}"
47
+ CONFIG["target_alias"] = "i386-mingw32"
48
+ CONFIG["ECHO_C"] = ""
49
+ CONFIG["ECHO_N"] = "-n"
50
+ CONFIG["ECHO_T"] = ""
51
+ CONFIG["LIBS"] = "-lwsock32 "
52
+ CONFIG["MAJOR"] = "1"
53
+ CONFIG["MINOR"] = "8"
54
+ CONFIG["TEENY"] = "4"
55
+ CONFIG["build"] = "i686-pc-linux"
56
+ CONFIG["build_cpu"] = "i686"
57
+ CONFIG["build_vendor"] = "pc"
58
+ CONFIG["build_os"] = "linux"
59
+ CONFIG["host"] = "i586-pc-mingw32msvc"
60
+ CONFIG["host_cpu"] = "i586"
61
+ CONFIG["host_vendor"] = "pc"
62
+ CONFIG["host_os"] = "mingw32msvc"
63
+ CONFIG["target"] = "i386-pc-mingw32"
64
+ CONFIG["target_cpu"] = "i386"
65
+ CONFIG["target_vendor"] = "pc"
66
+ CONFIG["target_os"] = "mingw32"
67
+ CONFIG["CC"] = "#{mingwpre}-gcc"
68
+ CONFIG["CFLAGS"] = "-g -O2 "
69
+ CONFIG["LDFLAGS"] = ""
70
+ CONFIG["CPPFLAGS"] = ""
71
+ CONFIG["OBJEXT"] = "o"
72
+ CONFIG["CPP"] = "#{mingwpre}-gcc -E"
73
+ CONFIG["EGREP"] = "grep -E"
74
+ CONFIG["GNU_LD"] = "yes"
75
+ CONFIG["CPPOUTFILE"] = "-o conftest.i"
76
+ CONFIG["OUTFLAG"] = "-o "
77
+ CONFIG["YACC"] = "bison -y"
78
+ CONFIG["RANLIB"] = "#{mingwpre}-ranlib"
79
+ CONFIG["AR"] = "#{mingwpre}-ar"
80
+ CONFIG["NM"] = "#{mingwpre}-nm"
81
+ CONFIG["WINDRES"] = "#{mingwpre}-windres"
82
+ CONFIG["DLLWRAP"] = "#{mingwpre}-dllwrap"
83
+ CONFIG["OBJDUMP"] = "#{mingwpre}-objdump"
84
+ CONFIG["LN_S"] = "ln -s"
85
+ CONFIG["SET_MAKE"] = ""
86
+ CONFIG["INSTALL_PROGRAM"] = "$(INSTALL)"
87
+ CONFIG["INSTALL_SCRIPT"] = "$(INSTALL)"
88
+ CONFIG["INSTALL_DATA"] = "$(INSTALL) -m 644"
89
+ CONFIG["RM"] = "rm -f"
90
+ CONFIG["CP"] = "cp"
91
+ CONFIG["MAKEDIRS"] = "mkdir -p"
92
+ CONFIG["LIBOBJS"] = " fileblocks$(U).o crypt$(U).o flock$(U).o acosh$(U).o win32$(U).o"
93
+ CONFIG["ALLOCA"] = ""
94
+ CONFIG["DLDFLAGS"] = " -Wl,--enable-auto-import,--export-all"
95
+ CONFIG["ARCH_FLAG"] = ""
96
+ CONFIG["STATIC"] = ""
97
+ CONFIG["CCDLFLAGS"] = ""
98
+ CONFIG["LDSHARED"] = "#{mingwpre}-gcc -shared -s"
99
+ CONFIG["DLEXT"] = "so"
100
+ CONFIG["DLEXT2"] = "dll"
101
+ CONFIG["LIBEXT"] = "a"
102
+ CONFIG["LINK_SO"] = ""
103
+ CONFIG["LIBPATHFLAG"] = " -L\"%s\""
104
+ CONFIG["RPATHFLAG"] = ""
105
+ CONFIG["LIBPATHENV"] = ""
106
+ CONFIG["TRY_LINK"] = ""
107
+ CONFIG["STRIP"] = "strip"
108
+ CONFIG["EXTSTATIC"] = ""
109
+ CONFIG["setup"] = "Setup"
110
+ CONFIG["MINIRUBY"] = "ruby -rfake"
111
+ CONFIG["PREP"] = "fake.rb"
112
+ CONFIG["RUNRUBY"] = "$(MINIRUBY) -I`cd $(srcdir)/lib; pwd`"
113
+ CONFIG["EXTOUT"] = ".ext"
114
+ CONFIG["ARCHFILE"] = ""
115
+ CONFIG["RDOCTARGET"] = ""
116
+ CONFIG["XCFLAGS"] = " -DRUBY_EXPORT"
117
+ CONFIG["XLDFLAGS"] = " -Wl,--stack,0x02000000 -L."
118
+ CONFIG["LIBRUBY_LDSHARED"] = "#{mingwpre}-gcc -shared -s"
119
+ CONFIG["LIBRUBY_DLDFLAGS"] = " -Wl,--enable-auto-import,--export-all -Wl,--out-implib=$(LIBRUBY)"
120
+ CONFIG["rubyw_install_name"] = "rubyw"
121
+ CONFIG["RUBYW_INSTALL_NAME"] = "rubyw"
122
+ CONFIG["LIBRUBY_A"] = "lib$(RUBY_SO_NAME)-static.a"
123
+ CONFIG["LIBRUBY_SO"] = "$(RUBY_SO_NAME).dll"
124
+ CONFIG["LIBRUBY_ALIASES"] = ""
125
+ CONFIG["LIBRUBY"] = "lib$(LIBRUBY_SO).a"
126
+ CONFIG["LIBRUBYARG"] = "$(LIBRUBYARG_SHARED)"
127
+ CONFIG["LIBRUBYARG_STATIC"] = "-l$(RUBY_SO_NAME)-static"
128
+ CONFIG["LIBRUBYARG_SHARED"] = "-l$(RUBY_SO_NAME)"
129
+ CONFIG["SOLIBS"] = "$(LIBS)"
130
+ CONFIG["DLDLIBS"] = ""
131
+ CONFIG["ENABLE_SHARED"] = "yes"
132
+ CONFIG["MAINLIBS"] = ""
133
+ CONFIG["COMMON_LIBS"] = "m"
134
+ CONFIG["COMMON_MACROS"] = ""
135
+ CONFIG["COMMON_HEADERS"] = "windows.h winsock.h"
136
+ CONFIG["EXPORT_PREFIX"] = ""
137
+ CONFIG["MINIOBJS"] = "dmydln.o"
138
+ CONFIG["MAKEFILES"] = "Makefile GNUmakefile"
139
+ CONFIG["arch"] = "i386-mingw32"
140
+ CONFIG["sitearch"] = "i386-msvcrt"
141
+ CONFIG["sitedir"] = "$(prefix)/lib/ruby/site_ruby"
142
+ CONFIG["configure_args"] = "'--host=#{mingwpre}' '--target=i386-mingw32' '--build=i686-linux' '--prefix=#{mingw32}' 'build_alias=i686-linux' 'host_alias=#{mingwpre}' 'target_alias=i386-mingw32'"
143
+ CONFIG["NROFF"] = "/usr/bin/nroff"
144
+ CONFIG["MANTYPE"] = "doc"
145
+ CONFIG["LTLIBOBJS"] = " fileblocks$(U).lo crypt$(U).lo flock$(U).lo acosh$(U).lo win32$(U).lo"
146
+ CONFIG["ruby_version"] = "$(MAJOR).$(MINOR)"
147
+ CONFIG["rubylibdir"] = "$(libdir)/ruby/$(ruby_version)"
148
+ CONFIG["archdir"] = "$(rubylibdir)/$(arch)"
149
+ CONFIG["sitelibdir"] = "$(sitedir)/$(ruby_version)"
150
+ CONFIG["sitearchdir"] = "$(sitelibdir)/$(sitearch)"
151
+ CONFIG["topdir"] = File.dirname(__FILE__)
152
+ MAKEFILE_CONFIG = {}
153
+ CONFIG.each{|k,v| MAKEFILE_CONFIG[k] = v.dup}
154
+ def Config::expand(val, config = CONFIG)
155
+ val.gsub!(/\$\$|\$\(([^()]+)\)|\$\{([^{}]+)\}/) do |var|
156
+ if !(v = $1 || $2)
157
+ '$'
158
+ elsif key = config[v = v[/\A[^:]+(?=(?::(.*?)=(.*))?\z)/]]
159
+ pat, sub = $1, $2
160
+ config[v] = false
161
+ Config::expand(key, config)
162
+ config[v] = key
163
+ key = key.gsub(/#{Regexp.quote(pat)}(?=\s|\z)/n) {sub} if pat
164
+ key
165
+ else
166
+ var
167
+ end
168
+ end
169
+ val
170
+ end
171
+ CONFIG.each_value do |val|
172
+ Config::expand(val)
173
+ end
174
+ end
175
+ RbConfig = Config # compatibility for ruby-1.9
176
+ CROSS_COMPILING = nil unless defined? CROSS_COMPILING
@@ -0,0 +1,26 @@
1
+ # == About hpricot.rb
2
+ #
3
+ # All of Hpricot's various part are loaded when you use <tt>require 'hpricot'</tt>.
4
+ #
5
+ # * hpricot_scan: the scanner (a C extension for Ruby) which turns an HTML stream into tokens.
6
+ # * hpricot/parse.rb: uses the scanner to sort through tokens and give you back a complete document object.
7
+ # * hpricot/tag.rb: sets up objects for the various types of elements in an HTML document.
8
+ # * hpricot/modules.rb: categorizes the various elements using mixins.
9
+ # * hpricot/traverse.rb: methods for searching documents.
10
+ # * hpricot/elements.rb: methods for dealing with a group of elements as an Hpricot::Elements list.
11
+ # * hpricot/inspect.rb: methods for displaying documents in a readable form.
12
+
13
+ # If available, Nikolai's UTF-8 library will ease use of utf-8 documents.
14
+ # See http://git.bitwi.se/ruby-character-encodings.git/.
15
+ begin
16
+ require 'encoding/character/utf-8'
17
+ rescue LoadError
18
+ end
19
+
20
+ require 'hpricot_scan'
21
+ require 'hpricot/tag'
22
+ require 'hpricot/modules'
23
+ require 'hpricot/traverse'
24
+ require 'hpricot/inspect'
25
+ require 'hpricot/parse'
26
+ require 'hpricot/builder'
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ #--
3
+ # Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
4
+ # All rights reserved.
5
+
6
+ # Permission is granted for use, copying, modification, distribution,
7
+ # and distribution of modified versions of this work as long as the
8
+ # above copyright notice is included.
9
+ #++
10
+
11
+ module Hpricot
12
+
13
+ # BlankSlate provides an abstract base class with no predefined
14
+ # methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
15
+ # BlankSlate is useful as a base class when writing classes that
16
+ # depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
17
+ class BlankSlate
18
+ class << self
19
+
20
+ # Hide the method named +name+ in the BlankSlate class. Don't
21
+ # hide +instance_eval+ or any method beginning with "__".
22
+ def hide(name)
23
+ undef_method name if
24
+ instance_methods.include?(name.to_s) and
25
+ name !~ /^(__|instance_eval)/
26
+ end
27
+ end
28
+
29
+ instance_methods.each { |m| hide(m) }
30
+ end
31
+ end
32
+
33
+ # Since Ruby is very dynamic, methods added to the ancestors of
34
+ # BlankSlate <em>after BlankSlate is defined</em> will show up in the
35
+ # list of available BlankSlate methods. We handle this by defining a
36
+ # hook in the Object and Kernel classes that will hide any defined
37
+ module Kernel
38
+ class << self
39
+ alias_method :hpricot_slate_method_added, :method_added
40
+
41
+ # Detect method additions to Kernel and remove them in the
42
+ # BlankSlate class.
43
+ def method_added(name)
44
+ hpricot_slate_method_added(name)
45
+ return if self != Kernel
46
+ Hpricot::BlankSlate.hide(name)
47
+ end
48
+ end
49
+ end
50
+
51
+ class Object
52
+ class << self
53
+ alias_method :hpricot_slate_method_added, :method_added
54
+
55
+ # Detect method additions to Object and remove them in the
56
+ # BlankSlate class.
57
+ def method_added(name)
58
+ hpricot_slate_method_added(name)
59
+ return if self != Object
60
+ Hpricot::BlankSlate.hide(name)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,200 @@
1
+ require 'hpricot/tags'
2
+ require 'hpricot/xchar'
3
+ require 'hpricot/blankslate'
4
+
5
+ module Hpricot
6
+ def self.build(ele = Doc.new, assigns = {}, &blk)
7
+ ele.extend Builder
8
+ assigns.each do |k, v|
9
+ ele.instance_variable_set("@#{k}", v)
10
+ end
11
+ ele.instance_eval &blk
12
+ ele
13
+ end
14
+
15
+ module Builder
16
+
17
+ @@default = {
18
+ :indent => 0,
19
+ :output_helpers => true,
20
+ :output_xml_instruction => true,
21
+ :output_meta_tag => true,
22
+ :auto_validation => true,
23
+ :tagset => Hpricot::XHTMLTransitional,
24
+ :root_attributes => {
25
+ :xmlns => 'http://www.w3.org/1999/xhtml', :'xml:lang' => 'en', :lang => 'en'
26
+ }
27
+ }
28
+
29
+ def self.set(option, value)
30
+ @@default[option] = value
31
+ end
32
+
33
+ # Write a +string+ to the HTML stream, making sure to escape it.
34
+ def text!(string)
35
+ @children << Text.new(Hpricot.xs(string))
36
+ end
37
+
38
+ # Write a +string+ to the HTML stream without escaping it.
39
+ def text(string)
40
+ @children << Text.new(string)
41
+ nil
42
+ end
43
+ alias_method :<<, :text
44
+ alias_method :concat, :text
45
+
46
+ # Create a tag named +tag+. Other than the first argument which is the tag name,
47
+ # the arguments are the same as the tags implemented via method_missing.
48
+ def tag!(tag, *args, &block)
49
+ ele_id = nil
50
+ if @auto_validation and @tagset
51
+ if !@tagset.tagset.has_key?(tag)
52
+ raise InvalidXhtmlError, "no element `#{tag}' for #{tagset.doctype}"
53
+ elsif args.last.respond_to?(:to_hash)
54
+ attrs = args.last.to_hash
55
+
56
+ if @tagset.forms.include?(tag) and attrs[:id]
57
+ attrs[:name] ||= attrs[:id]
58
+ end
59
+
60
+ attrs.each do |k, v|
61
+ atname = k.to_s.downcase.intern
62
+ unless k =~ /:/ or @tagset.tagset[tag].include? atname
63
+ raise InvalidXhtmlError, "no attribute `#{k}' on #{tag} elements"
64
+ end
65
+ if atname == :id
66
+ ele_id = v.to_s
67
+ if @elements.has_key? ele_id
68
+ raise InvalidXhtmlError, "id `#{ele_id}' already used (id's must be unique)."
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ # turn arguments into children or attributes
76
+ childs = []
77
+ attrs = args.grep(Hash)
78
+ childs.concat((args - attrs).map do |x|
79
+ if x.respond_to? :to_html
80
+ Hpricot.make(x.to_html)
81
+ elsif x
82
+ Text.new(Hpricot.xs(x))
83
+ end
84
+ end.flatten)
85
+ attrs = attrs.inject({}) do |hsh, ath|
86
+ ath.each do |k, v|
87
+ hsh[k] = Hpricot.xs(v.to_s) if v
88
+ end
89
+ hsh
90
+ end
91
+
92
+ # create the element itself
93
+ f = Elem.new(STag.new(tag, attrs), childs, ETag.new(tag))
94
+
95
+ # build children from the block
96
+ if block
97
+ build(f, &block)
98
+ end
99
+
100
+ @children << f
101
+ f
102
+ end
103
+
104
+ def build(*a, &b)
105
+ Hpricot.build(*a, &b)
106
+ end
107
+
108
+ # Every HTML tag method goes through an html_tag call. So, calling <tt>div</tt> is equivalent
109
+ # to calling <tt>html_tag(:div)</tt>. All HTML tags in Hpricot's list are given generated wrappers
110
+ # for this method.
111
+ #
112
+ # If the @auto_validation setting is on, this method will check for many common mistakes which
113
+ # could lead to invalid XHTML.
114
+ def html_tag(sym, *args, &block)
115
+ if @auto_validation and @tagset.self_closing.include?(sym) and block
116
+ raise InvalidXhtmlError, "the `#{sym}' element is self-closing, please remove the block"
117
+ elsif args.empty? and block.nil?
118
+ CssProxy.new(self, sym)
119
+ else
120
+ tag!(sym, *args, &block)
121
+ end
122
+ end
123
+
124
+ XHTMLTransitional.tags.each do |k|
125
+ class_eval %{
126
+ def #{k}(*args, &block)
127
+ html_tag(#{k.inspect}, *args, &block)
128
+ end
129
+ }
130
+ end
131
+
132
+ def doctype(target, pub, sys)
133
+ @children << DocType.new(target, pub, sys)
134
+ end
135
+
136
+ remove_method :head
137
+
138
+ # Builds a head tag. Adds a <tt>meta</tt> tag inside with Content-Type
139
+ # set to <tt>text/html; charset=utf-8</tt>.
140
+ def head(*args, &block)
141
+ tag!(:head, *args) do
142
+ tag!(:meta, "http-equiv" => "Content-Type", "content" => "text/html; charset=utf-8") if @output_meta_tag
143
+ instance_eval(&block)
144
+ end
145
+ end
146
+
147
+ # Builds an html tag. An XML 1.0 instruction and an XHTML 1.0 Transitional doctype
148
+ # are prepended. Also assumes <tt>:xmlns => "http://www.w3.org/1999/xhtml",
149
+ # :lang => "en"</tt>.
150
+ def xhtml_transitional(attrs = {}, &block)
151
+ # self.tagset = Hpricot::XHTMLTransitional
152
+ xhtml_html(attrs, &block)
153
+ end
154
+
155
+ # Builds an html tag with XHTML 1.0 Strict doctype instead.
156
+ def xhtml_strict(attrs = {}, &block)
157
+ # self.tagset = Hpricot::XHTMLStrict
158
+ xhtml_html(attrs, &block)
159
+ end
160
+
161
+ private
162
+
163
+ def xhtml_html(attrs = {}, &block)
164
+ instruct! if @output_xml_instruction
165
+ doctype(:html, *@@default[:tagset].doctype)
166
+ tag!(:html, @@default[:root_attributes].merge(attrs), &block)
167
+ end
168
+
169
+ end
170
+
171
+ # Class used by Markaby::Builder to store element options. Methods called
172
+ # against the CssProxy object are added as element classes or IDs.
173
+ #
174
+ # See the README for examples.
175
+ class CssProxy < BlankSlate
176
+
177
+ # Creates a CssProxy object.
178
+ def initialize(builder, sym)
179
+ @builder, @sym, @attrs = builder, sym, {}
180
+ end
181
+
182
+ # Adds attributes to an element. Bang methods set the :id attribute.
183
+ # Other methods add to the :class attribute.
184
+ def method_missing(id_or_class, *args, &block)
185
+ if (idc = id_or_class.to_s) =~ /!$/
186
+ @attrs[:id] = $`
187
+ else
188
+ @attrs[:class] = @attrs[:class].nil? ? idc : "#{@attrs[:class]} #{idc}".strip
189
+ end
190
+
191
+ if block or args.any?
192
+ args.push(@attrs)
193
+ return @builder.tag!(@sym, *args, &block)
194
+ end
195
+
196
+ return self
197
+ end
198
+
199
+ end
200
+ end