relevance-rcov 0.8.2.1

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.
@@ -0,0 +1,145 @@
1
+ # rcov Copyright (c) 2004-2006 Mauricio Fernandez <mfp@acm.org>
2
+ #
3
+ # See LEGAL and LICENSE for licensing information.
4
+
5
+ require 'rcov/version'
6
+
7
+ module Rcov
8
+
9
+ # RCOV__ performs the low-level tracing of the execution, gathering code
10
+ # coverage information in the process. The C core made available through the
11
+ # rcovrt extension will be used if possible. Otherwise the functionality
12
+ # will be emulated using set_trace_func, but this is very expensive and
13
+ # will fail if other libraries (e.g. breakpoint) change the trace_func.
14
+ #
15
+ # Do not use this module; it is very low-level and subject to frequent
16
+ # changes. Rcov::CodeCoverageAnalyzer offers a much more convenient and
17
+ # stable interface.
18
+ module RCOV__
19
+ COVER = {}
20
+ CALLSITES = {}
21
+ DEFSITES = {}
22
+ pure_ruby_impl_needed = true
23
+ unless defined? $rcov_do_not_use_rcovrt
24
+ begin
25
+ require 'rcovrt'
26
+ abi = [0,0,0]
27
+ begin
28
+ abi = RCOV__.ABI
29
+ raise if abi[0] != RCOVRT_ABI[0] || abi[1] < RCOVRT_ABI[1]
30
+ pure_ruby_impl_needed = false
31
+ rescue
32
+ $stderr.puts <<-EOF
33
+ The rcovrt extension I found was built for a different version of rcov.
34
+ The required ABI is: #{RCOVRT_ABI.join(".")}
35
+ Your current rcovrt extension is: #{abi.join(".")}
36
+
37
+ Please delete rcovrt.{so,bundle,dll,...} and install the required one.
38
+ EOF
39
+ raise LoadError
40
+ end
41
+ rescue LoadError
42
+ $stderr.puts <<-EOF
43
+
44
+ Since the rcovrt extension couldn't be loaded, rcov will run in pure-Ruby
45
+ mode, which is about two orders of magnitude slower.
46
+
47
+ If you're on win32, you can find a pre-built extension (usable with recent
48
+ One Click Installer and mswin32 builds) at http://eigenclass.org/hiki.rb?rcov .
49
+
50
+ EOF
51
+ end
52
+ end
53
+
54
+ if pure_ruby_impl_needed
55
+ methods = %w[install_coverage_hook remove_coverage_hook reset_coverage
56
+ install_callsite_hook remove_callsite_hook reset_callsite
57
+ generate_coverage_info generate_callsite_info]
58
+ sklass = class << self; self end
59
+ (methods & sklass.instance_methods).each do |meth|
60
+ sklass.class_eval{ remove_method meth }
61
+ end
62
+
63
+ @coverage_hook_activated = @callsite_hook_activated = false
64
+
65
+ def self.install_coverage_hook # :nodoc:
66
+ install_common_hook
67
+ @coverage_hook_activated = true
68
+ end
69
+
70
+ def self.install_callsite_hook # :nodoc:
71
+ install_common_hook
72
+ @callsite_hook_activated = true
73
+ end
74
+
75
+ def self.install_common_hook # :nodoc:
76
+ set_trace_func lambda {|event, file, line, id, binding, klass|
77
+ next unless SCRIPT_LINES__.has_key? file
78
+ case event
79
+ when 'call'
80
+ if @callsite_hook_activated
81
+ receiver = eval("self", binding)
82
+ klass = class << klass; self end unless klass === receiver
83
+ begin
84
+ DEFSITES[[klass.to_s, id.to_s]] = [file, line]
85
+ rescue Exception
86
+ end
87
+ caller_arr = self.format_backtrace_array(caller[1,1])
88
+ begin
89
+ hash = CALLSITES[[klass.to_s, id.to_s]] ||= {}
90
+ hash[caller_arr] ||= 0
91
+ hash[caller_arr] += 1
92
+ #puts "#{event} #{file} #{line} #{klass.inspect} " +
93
+ # "#{klass.object_id} #{id} #{eval('self', binding)}"
94
+ rescue Exception
95
+ end
96
+ end
97
+ when 'c-call', 'c-return', 'class'
98
+ return
99
+ end
100
+ if @coverage_hook_activated
101
+ COVER[file] ||= Array.new(SCRIPT_LINES__[file].size, 0)
102
+ COVER[file][line - 1] ||= 0
103
+ COVER[file][line - 1] += 1
104
+ end
105
+ }
106
+ end
107
+
108
+ def self.remove_coverage_hook # :nodoc:
109
+ @coverage_hook_activated = false
110
+ set_trace_func(nil) if !@callsite_hook_activated
111
+ end
112
+
113
+ def self.remove_callsite_hook # :nodoc:
114
+ @callsite_hook_activated = false
115
+ set_trace_func(nil) if !@coverage_hook_activated
116
+ end
117
+
118
+ def self.reset_coverage # :nodoc:
119
+ COVER.replace({})
120
+ end
121
+
122
+ def self.reset_callsite # :nodoc:
123
+ CALLSITES.replace({})
124
+ DEFSITES.replace({})
125
+ end
126
+
127
+ def self.generate_coverage_info # :nodoc:
128
+ Marshal.load(Marshal.dump(COVER))
129
+ end
130
+
131
+ def self.generate_callsite_info # :nodoc:
132
+ [CALLSITES, DEFSITES]
133
+ end
134
+
135
+ def self.format_backtrace_array(backtrace)
136
+ backtrace.map do |line|
137
+ md = /^([^:]*)(?::(\d+)(?::in `(.*)'))?/.match(line)
138
+ raise "Bad backtrace format" unless md
139
+ [nil, md[3] ? md[3].to_sym : nil, md[1], (md[2] || '').to_i]
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ end
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Define a task library for performing code coverage analysis of unit tests
4
+ # using rcov.
5
+
6
+ require 'rake'
7
+ require 'rake/tasklib'
8
+
9
+ module Rcov
10
+
11
+ # Create a task that runs a set of tests through rcov, generating code
12
+ # coverage reports.
13
+ #
14
+ # Example:
15
+ #
16
+ # require 'rcov/rcovtask'
17
+ #
18
+ # Rcov::RcovTask.new do |t|
19
+ # t.libs << "test"
20
+ # t.test_files = FileList['test/test*.rb']
21
+ # t.verbose = true
22
+ # end
23
+ #
24
+ # If rake is invoked with a "TEST=filename" command line option,
25
+ # then the list of test files will be overridden to include only the
26
+ # filename specified on the command line. This provides an easy way
27
+ # to run just one test.
28
+ #
29
+ # If rake is invoked with a "RCOVOPTS=options" command line option,
30
+ # then the given options are passed to rcov.
31
+ #
32
+ # If rake is invoked with a "RCOVPATH=path/to/rcov" command line option,
33
+ # then the given rcov executable will be used; otherwise the one in your
34
+ # PATH will be used.
35
+ #
36
+ # Examples:
37
+ #
38
+ # rake rcov # run tests normally
39
+ # rake rcov TEST=just_one_file.rb # run just one test file.
40
+ # rake rcov RCOVOPTS="-p" # run in profile mode
41
+ # rake rcov RCOVOPTS="-T" # generate text report
42
+ #
43
+ class RcovTask < Rake::TaskLib
44
+
45
+ # Name of test task. (default is :rcov)
46
+ attr_accessor :name
47
+
48
+ # List of directories to added to $LOAD_PATH before running the
49
+ # tests. (default is 'lib')
50
+ attr_accessor :libs
51
+
52
+ # True if verbose test output desired. (default is false)
53
+ attr_accessor :verbose
54
+
55
+ # Request that the tests be run with the warning flag set.
56
+ # E.g. warning=true implies "ruby -w" used to run the tests.
57
+ attr_accessor :warning
58
+
59
+ # Glob pattern to match test files. (default is 'test/test*.rb')
60
+ attr_accessor :pattern
61
+
62
+ # Array of commandline options to pass to ruby when running the rcov loader.
63
+ attr_accessor :ruby_opts
64
+
65
+ # Array of commandline options to pass to rcov. An explicit
66
+ # RCOVOPTS=opts on the command line will override this. (default
67
+ # is <tt>["--text-report"]</tt>)
68
+ attr_accessor :rcov_opts
69
+
70
+ # Output directory for the XHTML report.
71
+ attr_accessor :output_dir
72
+
73
+ # Explicitly define the list of test files to be included in a
74
+ # test. +list+ is expected to be an array of file names (a
75
+ # FileList is acceptable). If both +pattern+ and +test_files+ are
76
+ # used, then the list of test files is the union of the two.
77
+ def test_files=(list)
78
+ @test_files = list
79
+ end
80
+
81
+ # Create a testing task.
82
+ def initialize(name=:rcov)
83
+ @name = name
84
+ @libs = ["lib"]
85
+ @pattern = nil
86
+ @test_files = nil
87
+ @verbose = false
88
+ @warning = false
89
+ @rcov_opts = ["--text-report"]
90
+ @ruby_opts = []
91
+ @output_dir = "coverage"
92
+ yield self if block_given?
93
+ @pattern = 'test/test*.rb' if @pattern.nil? && @test_files.nil?
94
+ define
95
+ end
96
+
97
+ # Create the tasks defined by this task lib.
98
+ def define
99
+ lib_path = @libs.join(File::PATH_SEPARATOR)
100
+ actual_name = Hash === name ? name.keys.first : name
101
+ unless Rake.application.last_comment
102
+ desc "Analyze code coverage with tests" +
103
+ (@name==:rcov ? "" : " for #{actual_name}")
104
+ end
105
+ task @name do
106
+ run_code = ''
107
+ RakeFileUtils.verbose(@verbose) do
108
+ run_code =
109
+ case rcov_path
110
+ when nil, ''
111
+ "-S rcov"
112
+ else %!"#{rcov_path}"!
113
+ end
114
+ ruby_opts = @ruby_opts.clone
115
+ ruby_opts.push( "-I#{lib_path}" )
116
+ ruby_opts.push run_code
117
+ ruby_opts.push( "-w" ) if @warning
118
+ ruby ruby_opts.join(" ") + " " + option_list +
119
+ %[ -o "#{@output_dir}" ] +
120
+ file_list.collect { |fn| %["#{fn}"] }.join(' ')
121
+ end
122
+ end
123
+
124
+ desc "Remove rcov products for #{actual_name}"
125
+ task paste("clobber_", actual_name) do
126
+ rm_r @output_dir rescue nil
127
+ end
128
+
129
+ clobber_task = paste("clobber_", actual_name)
130
+ task :clobber => [clobber_task]
131
+
132
+ task actual_name => clobber_task
133
+ self
134
+ end
135
+
136
+ def rcov_path # :nodoc:
137
+ ENV['RCOVPATH']
138
+ end
139
+
140
+ def option_list # :nodoc:
141
+ ENV['RCOVOPTS'] || @rcov_opts.join(" ") || ""
142
+ end
143
+
144
+ def file_list # :nodoc:
145
+ if ENV['TEST']
146
+ FileList[ ENV['TEST'] ]
147
+ else
148
+ result = []
149
+ result += @test_files.to_a if @test_files
150
+ result += FileList[ @pattern ].to_a if @pattern
151
+ FileList[result]
152
+ end
153
+ end
154
+ end
155
+ end
156
+
@@ -0,0 +1,71 @@
1
+ # rcov Copyright (c) 2004-2006 Mauricio Fernandez <mfp@acm.org>
2
+ # See LEGAL and LICENSE for additional licensing information.
3
+
4
+ require 'pathname'
5
+
6
+
7
+ module Rcov
8
+
9
+ # Try to fix bugs in the REXML shipped with Ruby 1.8.6
10
+ # They affect Mac OSX 10.5.1 users and motivates endless bug reports.
11
+ begin
12
+ require 'rexml/formatters/transitive'
13
+ require 'rexml/formatter/pretty'
14
+ rescue LoadError
15
+ end
16
+
17
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rexml_extensions' ))
18
+
19
+ if (RUBY_VERSION == "1.8.6" || RUBY_VERSION == "1.8.7") && defined? REXML::Formatters::Transitive
20
+ class REXML::Document
21
+ remove_method :write rescue nil
22
+ def write( output=$stdout, indent=-1, trans=false, ie_hack=false )
23
+ if xml_decl.encoding != "UTF-8" && !output.kind_of?(Output)
24
+ output = Output.new( output, xml_decl.encoding )
25
+ end
26
+ formatter = if indent > -1
27
+ #if trans
28
+ REXML::Formatters::Transitive.new( indent )
29
+ #else
30
+ # REXML::Formatters::Pretty.new( indent, ie_hack )
31
+ #end
32
+ else
33
+ REXML::Formatters::Default.new( ie_hack )
34
+ end
35
+ formatter.write( self, output )
36
+ end
37
+ end
38
+
39
+ class REXML::Formatters::Transitive
40
+ remove_method :write_element rescue nil
41
+ def write_element( node, output )
42
+ output << "<#{node.expanded_name}"
43
+
44
+ node.attributes.each_attribute do |attr|
45
+ output << " "
46
+ attr.write( output )
47
+ end unless node.attributes.empty?
48
+
49
+ if node.children.empty?
50
+ output << "/>"
51
+ else
52
+ output << ">"
53
+ # If compact and all children are text, and if the formatted output
54
+ # is less than the specified width, then try to print everything on
55
+ # one line
56
+ skip = false
57
+ @level += @indentation
58
+ node.children.each { |child|
59
+ write( child, output )
60
+ }
61
+ @level -= @indentation
62
+ output << "</#{node.expanded_name}>"
63
+ end
64
+ output << "\n"
65
+ output << ' '*@level
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,44 @@
1
+ require 'rexml/document'
2
+ require 'rexml/formatters/pretty'
3
+
4
+ module Rcov
5
+ module REXMLExtensions
6
+
7
+ def self.fix_pretty_formatter_wrap
8
+ REXML::Formatters::Pretty.class_eval do
9
+ include PrettyFormatterWrapFix
10
+ end
11
+ end
12
+
13
+ # Fix for this bug: http://clint-hill.com/2008/10/02/a-bug-in-ruby-did-i-just-find-that/
14
+ # Also known from this fun exception:
15
+ #
16
+ # /usr/local/ruby/lib/ruby/1.8/rexml/formatters/pretty.rb:131:in
17
+ # `[]': no implicit conversion from nil to integer (TypeError)
18
+ #
19
+ # This bug was fixed in Ruby with this changeset http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=19487
20
+ # ...which should mean that this bug only affects Ruby 1.8.6. The latest stable version of 1.8.7 (and up) should be fine.
21
+ module PrettyFormatterWrapFix
22
+
23
+ def self.included(base)
24
+ base.class_eval do
25
+ def wrap(string, width)
26
+ # Recursively wrap string at width.
27
+ return string if string.length <= width
28
+ place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
29
+ return string if place.nil?
30
+ return string[0,place] + "\n" + wrap(string[place+1..-1], width)
31
+ end
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ def self.init!
38
+ if RUBY_VERSION == "1.8.6"
39
+ fix_pretty_formatter_wrap
40
+ end
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,11 @@
1
+ # rcov Copyright (c) 2004-2006 Mauricio Fernandez <mfp@acm.org>
2
+ #
3
+ # See LEGAL and LICENSE for licensing information.
4
+
5
+ module Rcov
6
+ VERSION = "0.8.2.1"
7
+ RELEASE_DATE = "2009-03-17"
8
+ RCOVRT_ABI = [2,0,0]
9
+ UPSTREAM_URL = "http://github.com/spicycode/rcov"
10
+
11
+ end
@@ -0,0 +1,754 @@
1
+ # xx can be redistributed and used under the following conditions
2
+ # (just keep the following copyright notice, list of conditions and disclaimer
3
+ # in order to satisfy rcov's "Ruby license" and xx's license simultaneously).
4
+ #
5
+ #ePark Labs Public License version 1
6
+ #Copyright (c) 2005, ePark Labs, Inc. and contributors
7
+ #All rights reserved.
8
+ #
9
+ #Redistribution and use in source and binary forms, with or without modification,
10
+ #are permitted provided that the following conditions are met:
11
+ #
12
+ # 1. Redistributions of source code must retain the above copyright notice, this
13
+ # list of conditions and the following disclaimer.
14
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
15
+ # this list of conditions and the following disclaimer in the documentation
16
+ # and/or other materials provided with the distribution.
17
+ # 3. Neither the name of ePark Labs nor the names of its contributors may be
18
+ # used to endorse or promote products derived from this software without
19
+ # specific prior written permission.
20
+ #
21
+ #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22
+ #ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
+ #WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ #DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
25
+ #ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26
+ #(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27
+ #LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28
+ #ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ #(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30
+ #SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+
32
+ unless defined? $__xx_rb__
33
+
34
+ require "rexml/document"
35
+
36
+
37
+ module XX
38
+
39
+ VERSION = "0.1.0"
40
+
41
+ %w(
42
+ CRAZY_LIKE_A_HELL
43
+ PERMISSIVE
44
+ STRICT
45
+ ANY
46
+ ).each{|c| const_set c, c}
47
+
48
+ class Document
49
+
50
+ attr "doc"
51
+ attr "stack"
52
+ attr "size"
53
+
54
+ def initialize(*a, &b)
55
+ @doc = ::REXML::Document::new(*a, &b)
56
+ @stack = [@doc]
57
+ @size = 0
58
+ end
59
+
60
+ def top
61
+ @stack.last
62
+ end
63
+
64
+ def push(element)
65
+ @stack.push element
66
+ end
67
+
68
+ def pop
69
+ @stack.pop unless @stack.size == 1
70
+ end
71
+
72
+ def tracking_additions
73
+ n = @size
74
+ yield
75
+ return @size - n
76
+ end
77
+
78
+ def to_str(port = "")
79
+ @doc.write port, indent=-1, transitive=false, ie_hack=true
80
+ port
81
+ end
82
+
83
+ alias_method "to_s", "to_str"
84
+
85
+ def pretty(port = '')
86
+ @doc.write port, indent=2, transitive=false, ie_hack=true
87
+ port
88
+ end
89
+
90
+ def create(element)
91
+ push element
92
+ begin
93
+ object = nil
94
+ additions =
95
+ tracking_additions do
96
+ object = yield element if block_given?
97
+ end
98
+ if object and additions.zero?
99
+ self << object
100
+ end
101
+ ensure
102
+ pop
103
+ end
104
+ self << element
105
+ element
106
+
107
+ end
108
+ def << object
109
+
110
+ t, x = top, object
111
+
112
+ if x
113
+ case t
114
+ when ::REXML::Document
115
+
116
+ begin
117
+ t <<
118
+ case x
119
+ when ::REXML::Document
120
+ x.root || ::REXML::Text::new(x.to_s)
121
+ when ::REXML::Element
122
+ x
123
+ when ::REXML::CData
124
+ x
125
+ when ::REXML::Text
126
+ x
127
+ else # string
128
+ ::REXML::Text::new(x.to_s)
129
+ end
130
+ rescue
131
+ if t.respond_to? "root"
132
+ t = t.root
133
+ retry
134
+ else
135
+ raise
136
+ end
137
+ end
138
+
139
+ when ::REXML::Element
140
+ t <<
141
+ case x
142
+ when ::REXML::Document
143
+ x.root || ::REXML::Text::new(x.to_s)
144
+ when ::REXML::Element
145
+ x
146
+ when ::REXML::CData
147
+ #::REXML::Text::new(x.write(""))
148
+ x
149
+ when ::REXML::Text
150
+ x
151
+ else # string
152
+ ::REXML::Text::new(x.to_s)
153
+ end
154
+
155
+ when ::REXML::Text
156
+ t <<
157
+ case x
158
+ when ::REXML::Document
159
+ x.write ""
160
+ when ::REXML::Element
161
+ x.write ""
162
+ when ::REXML::CData
163
+ x.write ""
164
+ when ::REXML::Text
165
+ x.write ""
166
+ else # string
167
+ x.to_s
168
+ end
169
+
170
+ else # other - try anyhow
171
+ t <<
172
+ case x
173
+ when ::REXML::Document
174
+ x.write ""
175
+ when ::REXML::Element
176
+ x.write ""
177
+ when ::REXML::CData
178
+ x.write ""
179
+ when ::REXML::Text
180
+ x.write ""
181
+ else # string
182
+ x.to_s
183
+ end
184
+ end
185
+ end
186
+
187
+ @size += 1
188
+ self
189
+
190
+ end
191
+
192
+ end
193
+
194
+ module Markup
195
+
196
+ class Error < ::StandardError; end
197
+
198
+ module InstanceMethods
199
+
200
+ def method_missing m, *a, &b
201
+
202
+ m = m.to_s
203
+
204
+ tag_method, tag_name = xx_class::xx_tag_method_name m
205
+
206
+ c_method_missing = xx_class::xx_config_for "method_missing", xx_which
207
+ c_tags = xx_class::xx_config_for "tags", xx_which
208
+
209
+ pat =
210
+ case c_method_missing
211
+ when ::XX::CRAZY_LIKE_A_HELL
212
+ %r/.*/
213
+ when ::XX::PERMISSIVE
214
+ %r/_$/o
215
+ when ::XX::STRICT
216
+ %r/_$/o
217
+ else
218
+ super(m.to_sym, *a, &b)
219
+ end
220
+
221
+ super(m.to_sym, *a, &b) unless m =~ pat
222
+
223
+ if c_method_missing == ::XX::STRICT
224
+ super(m.to_sym, *a, &b) unless c_tags.include? tag_name
225
+ end
226
+
227
+ ret, defined = nil
228
+
229
+ begin
230
+ xx_class::xx_define_tmp_method tag_method
231
+ xx_class::xx_define_tag_method tag_method, tag_name
232
+ ret = send tag_method, *a, &b
233
+ defined = true
234
+ ensure
235
+ xx_class::xx_remove_tag_method tag_method unless defined
236
+ end
237
+
238
+ ret
239
+
240
+ end
241
+ def xx_tag_ tag_name, *a, &b
242
+
243
+ tag_method, tag_name = xx_class::xx_tag_method_name tag_name
244
+
245
+ ret, defined = nil
246
+
247
+ begin
248
+ xx_class::xx_define_tmp_method tag_method
249
+ xx_class::xx_define_tag_method tag_method, tag_name
250
+ ret = send tag_method, *a, &b
251
+ defined = true
252
+ ensure
253
+ xx_class::xx_remove_tag_method tag_method unless defined
254
+ end
255
+
256
+ ret
257
+
258
+ end
259
+ alias_method "g_", "xx_tag_"
260
+ def xx_which *argv
261
+
262
+ @xx_which = nil unless defined? @xx_which
263
+ if argv.empty?
264
+ @xx_which
265
+ else
266
+ xx_which = @xx_which
267
+ begin
268
+ @xx_which = argv.shift
269
+ return yield
270
+ ensure
271
+ @xx_which = xx_which
272
+ end
273
+ end
274
+
275
+ end
276
+ def xx_with_doc_in_effect *a, &b
277
+
278
+ @xx_docs ||= []
279
+ doc = ::XX::Document::new(*a)
280
+ ddoc = doc.doc
281
+ begin
282
+ @xx_docs.push doc
283
+ b.call doc if b
284
+
285
+ doctype = xx_config_for "doctype", xx_which
286
+ if doctype
287
+ unless ddoc.doctype
288
+ doctype = ::REXML::DocType::new doctype unless
289
+ ::REXML::DocType === doctype
290
+ ddoc << doctype
291
+ end
292
+ end
293
+
294
+ xmldecl = xx_config_for "xmldecl", xx_which
295
+ if xmldecl
296
+ if ddoc.xml_decl == ::REXML::XMLDecl::default
297
+ xmldecl = ::REXML::XMLDecl::new xmldecl unless
298
+ ::REXML::XMLDecl === xmldecl
299
+ ddoc << xmldecl
300
+ end
301
+ end
302
+
303
+ return doc
304
+ ensure
305
+ @xx_docs.pop
306
+ end
307
+
308
+ end
309
+ def xx_doc
310
+
311
+ @xx_docs.last rescue raise "no xx_doc in effect!"
312
+
313
+ end
314
+ def xx_text_ *objects, &b
315
+
316
+ doc = xx_doc
317
+
318
+ text =
319
+ ::REXML::Text::new("",
320
+ respect_whitespace=true, parent=nil
321
+ )
322
+
323
+ objects.each do |object|
324
+ text << object.to_s if object
325
+ end
326
+
327
+ doc.create text, &b
328
+
329
+ end
330
+ alias_method "text_", "xx_text_"
331
+ alias_method "t_", "xx_text_"
332
+ def xx_markup_ *objects, &b
333
+
334
+ doc = xx_doc
335
+
336
+ doc2 = ::REXML::Document::new ""
337
+
338
+ objects.each do |object|
339
+ (doc2.root ? doc2.root : doc2) << ::REXML::Document::new(object.to_s)
340
+ end
341
+
342
+
343
+ ret = doc.create doc2, &b
344
+ puts doc2.to_s
345
+ STDIN.gets
346
+ ret
347
+
348
+ end
349
+ alias_method "x_", "xx_markup_"
350
+ def xx_any_ *objects, &b
351
+
352
+ doc = xx_doc
353
+ nothing = %r/.^/m
354
+
355
+ text =
356
+ ::REXML::Text::new("",
357
+ respect_whitespace=true, parent=nil, raw=true, entity_filter=nil, illegal=nothing
358
+ )
359
+
360
+ objects.each do |object|
361
+ text << object.to_s if object
362
+ end
363
+
364
+ doc.create text, &b
365
+
366
+ end
367
+ alias_method "h_", "xx_any_"
368
+ remove_method "x_" if instance_methods.include? "x_"
369
+ alias_method "x_", "xx_any_" # supplant for now
370
+ def xx_cdata_ *objects, &b
371
+
372
+ doc = xx_doc
373
+
374
+ cdata = ::REXML::CData::new ""
375
+
376
+ objects.each do |object|
377
+ cdata << object.to_s if object
378
+ end
379
+
380
+ doc.create cdata, &b
381
+
382
+ end
383
+ alias_method "c_", "xx_cdata_"
384
+ def xx_parse_attributes string
385
+
386
+ string = string.to_s
387
+ tokens = string.split %r/,/o
388
+ tokens.map{|t| t.sub!(%r/[^=]+=/){|key_eq| key_eq.chop << " : "}}
389
+ xx_parse_yaml_attributes(tokens.join(','))
390
+
391
+ end
392
+ alias_method "att_", "xx_parse_attributes"
393
+ def xx_parse_yaml_attributes string
394
+
395
+ require "yaml"
396
+ string = string.to_s
397
+ string = "{" << string unless string =~ %r/^\s*[{]/o
398
+ string = string << "}" unless string =~ %r/[}]\s*$/o
399
+ obj = ::YAML::load string
400
+ raise ArgumentError, "<#{ obj.class }> not Hash!" unless Hash === obj
401
+ obj
402
+
403
+ end
404
+ alias_method "at_", "xx_parse_yaml_attributes"
405
+ alias_method "yat_", "xx_parse_yaml_attributes"
406
+ def xx_class
407
+
408
+ @xx_class ||= self.class
409
+
410
+ end
411
+ def xx_tag_method_name *a, &b
412
+
413
+ xx_class.xx_tag_method_name(*a, &b)
414
+
415
+ end
416
+ def xx_define_tmp_method *a, &b
417
+
418
+ xx_class.xx_define_tmp_methodr(*a, &b)
419
+
420
+ end
421
+ def xx_define_tag_method *a, &b
422
+
423
+ xx_class.xx_define_tag_method(*a, &b)
424
+
425
+ end
426
+ def xx_remove_tag_method *a, &b
427
+
428
+ xx_class.xx_tag_remove_method(*a, &b)
429
+
430
+ end
431
+ def xx_ancestors
432
+
433
+ raise Error, "no xx_which in effect" unless xx_which
434
+ xx_class.xx_ancestors xx_which
435
+
436
+ end
437
+ def xx_config
438
+
439
+ xx_class.xx_config
440
+
441
+ end
442
+ def xx_config_for *a, &b
443
+
444
+ xx_class.xx_config_for(*a, &b)
445
+
446
+ end
447
+ def xx_configure *a, &b
448
+
449
+ xx_class.xx_configure(*a, &b)
450
+
451
+ end
452
+
453
+ end
454
+
455
+ module ClassMethods
456
+
457
+ def xx_tag_method_name m
458
+
459
+ m = m.to_s
460
+ tag_method, tag_name = m, m.gsub(%r/_+$/, "")
461
+ [ tag_method, tag_name ]
462
+
463
+ end
464
+ def xx_define_tmp_method m
465
+
466
+ define_method(m){ raise NotImplementedError, m.to_s }
467
+
468
+ end
469
+ def xx_define_tag_method tag_method, tag_name = nil
470
+
471
+ tag_method = tag_method.to_s
472
+ tag_name ||= tag_method.gsub %r/_+$/, ""
473
+
474
+ remove_method tag_method if instance_methods.include? tag_method
475
+ module_eval <<-code, __FILE__, __LINE__+1
476
+ def #{ tag_method } *a, &b
477
+ hashes, nothashes = a.partition{|x| Hash === x}
478
+
479
+ doc = xx_doc
480
+ element = ::REXML::Element::new '#{ tag_name }'
481
+
482
+ hashes.each{|h| h.each{|k,v| element.add_attribute k.to_s, v}}
483
+ nothashes.each{|nh| element << ::REXML::Text::new(nh.to_s)}
484
+
485
+ doc.create element, &b
486
+ end
487
+ code
488
+ tag_method
489
+
490
+ end
491
+ def xx_remove_tag_method tag_method
492
+
493
+ remove_method tag_method rescue nil
494
+
495
+ end
496
+ def xx_ancestors xx_which = self
497
+
498
+ list = []
499
+ ancestors.each do |a|
500
+ list << a if a < xx_which
501
+ end
502
+ xx_which.ancestors.each do |a|
503
+ list << a if a <= Markup
504
+ end
505
+ list
506
+
507
+ end
508
+ def xx_config
509
+
510
+ @@xx_config ||= Hash::new{|h,k| h[k] = {}}
511
+
512
+ end
513
+ def xx_config_for key, xx_which = nil
514
+
515
+ key = key.to_s
516
+ xx_which ||= self
517
+ xx_ancestors(xx_which).each do |a|
518
+ if xx_config[a].has_key? key
519
+ return xx_config[a][key]
520
+ end
521
+ end
522
+ nil
523
+
524
+ end
525
+ def xx_configure key, value, xx_which = nil
526
+
527
+ key = key.to_s
528
+ xx_which ||= self
529
+ xx_config[xx_which][key] = value
530
+
531
+ end
532
+
533
+ end
534
+
535
+ extend ClassMethods
536
+ include InstanceMethods
537
+
538
+ def self::included other, *a, &b
539
+
540
+ ret = super
541
+ other.module_eval do
542
+ include Markup::InstanceMethods
543
+ extend Markup::ClassMethods
544
+ class << self
545
+ define_method("included", Markup::XX_MARKUP_RECURSIVE_INCLUSION_PROC)
546
+ end
547
+ end
548
+ ret
549
+
550
+ end
551
+ XX_MARKUP_RECURSIVE_INCLUSION_PROC = method("included").to_proc
552
+
553
+ xx_configure "method_missing", XX::PERMISSIVE
554
+ xx_configure "tags", []
555
+ xx_configure "doctype", nil
556
+ xx_configure "xmldecl", nil
557
+
558
+ end
559
+
560
+ module XHTML
561
+
562
+ include Markup
563
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd")
564
+
565
+ def xhtml_ which = XHTML, *a, &b
566
+
567
+ xx_which(which) do
568
+ doc = xx_with_doc_in_effect(*a, &b)
569
+ ddoc = doc.doc
570
+ root = ddoc.root
571
+ if root and root.name and root.name =~ %r/^html$/i
572
+ if root.attribute("lang",nil).nil? or root.attribute("lang",nil).to_s.empty?
573
+ root.add_attribute "lang", "en"
574
+ end
575
+ if root.attribute("xml:lang").nil? or root.attribute("xml:lang").to_s.empty?
576
+ root.add_attribute "xml:lang", "en"
577
+ end
578
+ if root.namespace.nil? or root.namespace.to_s.empty?
579
+ root.add_namespace "http://www.w3.org/1999/xhtml"
580
+ end
581
+ end
582
+ doc
583
+ end
584
+
585
+ end
586
+
587
+ module Strict
588
+
589
+ include XHTML
590
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd")
591
+ xx_configure "tags", %w(
592
+ html head body div span DOCTYPE title link meta style p
593
+ h1 h2 h3 h4 h5 h6 strong em abbr acronym address bdo blockquote cite q code
594
+ ins del dfn kbd pre samp var br a base img
595
+ area map object param ul ol li dl dt dd table
596
+ tr td th tbody thead tfoot col colgroup caption form input
597
+ textarea select option optgroup button label fieldset legend script noscript b
598
+ i tt sub sup big small hr
599
+ )
600
+ xx_configure "method_missing", ::XX::STRICT
601
+
602
+ def xhtml_ which = XHTML::Strict, *a, &b
603
+
604
+ super(which, *a, &b)
605
+
606
+ end
607
+
608
+ end
609
+
610
+ module Transitional
611
+
612
+ include XHTML
613
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd")
614
+ def xhtml_ which = XHTML::Transitional, *a, &b
615
+
616
+ super(which, *a, &b)
617
+
618
+ end
619
+
620
+ end
621
+
622
+ end
623
+
624
+ module HTML4
625
+
626
+ include Markup
627
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN")
628
+
629
+ def html4_ which = HTML4, *a, &b
630
+
631
+ xx_which(which){ xx_with_doc_in_effect(*a, &b) }
632
+
633
+ end
634
+
635
+ module Strict
636
+
637
+ include HTML4
638
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN")
639
+ xx_configure "tags", %w(
640
+ html head body div span DOCTYPE title link meta style p
641
+ h1 h2 h3 h4 h5 h6 strong em abbr acronym address bdo blockquote cite q code
642
+ ins del dfn kbd pre samp var br a base img
643
+ area map object param ul ol li dl dt dd table
644
+ tr td th tbody thead tfoot col colgroup caption form input
645
+ textarea select option optgroup button label fieldset legend script noscript b
646
+ i tt sub sup big small hr
647
+ )
648
+ xx_configure "method_missing", ::XX::STRICT
649
+ def html4_ which = HTML4::Strict, *a, &b
650
+
651
+ super(which, *a, &b)
652
+
653
+ end
654
+
655
+ end
656
+
657
+ module Transitional
658
+
659
+ include HTML4
660
+ xx_configure "doctype", %(html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN")
661
+ def html4_ which = HTML4::Transitional, *a, &b
662
+
663
+ super(which, *a, &b)
664
+
665
+ end
666
+
667
+ end
668
+
669
+ end
670
+ HTML = HTML4
671
+
672
+ module XML
673
+
674
+ include Markup
675
+ xx_configure "xmldecl", ::REXML::XMLDecl::new
676
+
677
+ def xml_ *a, &b
678
+
679
+ xx_which(XML){ xx_with_doc_in_effect(*a, &b)}
680
+
681
+ end
682
+
683
+ end
684
+
685
+ end
686
+
687
+ $__xx_rb__ = __FILE__
688
+ end
689
+
690
+
691
+
692
+
693
+
694
+
695
+
696
+
697
+
698
+
699
+ #
700
+ # simple examples - see samples/ dir for more complete examples
701
+ #
702
+
703
+ if __FILE__ == $0
704
+
705
+ class Table < ::Array
706
+ include XX::XHTML::Strict
707
+ include XX::HTML4::Strict
708
+ include XX::XML
709
+
710
+ def doc
711
+ html_{
712
+ head_{ title_{ "xhtml/html4/xml demo" } }
713
+
714
+ div_{
715
+ h_{ "< malformed html & un-escaped symbols" }
716
+ }
717
+
718
+ t_{ "escaped & text > <" }
719
+
720
+ x_{ "<any_valid> xml </any_valid>" }
721
+
722
+ div_(:style => :sweet){
723
+ em_ "this is a table"
724
+
725
+ table_(:width => 42, :height => 42){
726
+ each{|row| tr_{ row.each{|cell| td_ cell } } }
727
+ }
728
+ }
729
+
730
+ script_(:type => :dangerous){ cdata_{ "javascript" } }
731
+ }
732
+ end
733
+ def to_xhtml
734
+ xhtml_{ doc }
735
+ end
736
+ def to_html4
737
+ html4_{ doc }
738
+ end
739
+ def to_xml
740
+ xml_{ doc }
741
+ end
742
+ end
743
+
744
+ table = Table[ %w( 0 1 2 ), %w( a b c ) ]
745
+
746
+ methods = %w( to_xhtml to_html4 to_xml )
747
+
748
+ methods.each do |method|
749
+ 2.times{ puts "-" * 42 }
750
+ puts(table.send(method).pretty)
751
+ puts
752
+ end
753
+
754
+ end