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.
- data/BLURB +149 -0
- data/CHANGES +177 -0
- data/LEGAL +36 -0
- data/LICENSE +56 -0
- data/Rakefile +93 -0
- data/THANKS +96 -0
- data/bin/rcov +560 -0
- data/doc/readme_for_api +41 -0
- data/doc/readme_for_emacs +64 -0
- data/doc/readme_for_rake +62 -0
- data/doc/readme_for_vim +47 -0
- data/editor-extensions/rcov.el +131 -0
- data/editor-extensions/rcov.vim +38 -0
- data/ext/rcovrt/1.8/callsite.c +242 -0
- data/ext/rcovrt/1.8/rcovrt.c +331 -0
- data/ext/rcovrt/1.9/callsite.c +258 -0
- data/ext/rcovrt/1.9/rcovrt.c +315 -0
- data/ext/rcovrt/extconf.rb +23 -0
- data/lib/rcov.rb +991 -0
- data/lib/rcov/lowlevel.rb +145 -0
- data/lib/rcov/rcovtask.rb +156 -0
- data/lib/rcov/report.rb +71 -0
- data/lib/rcov/rexml_extensions.rb +44 -0
- data/lib/rcov/version.rb +11 -0
- data/lib/rcov/xx.rb +754 -0
- data/setup.rb +1588 -0
- data/test/assets/sample_01.rb +7 -0
- data/test/assets/sample_02.rb +5 -0
- data/test/assets/sample_03.rb +20 -0
- data/test/assets/sample_04.rb +10 -0
- data/test/assets/sample_05-new.rb +17 -0
- data/test/assets/sample_05-old.rb +13 -0
- data/test/assets/sample_05.rb +17 -0
- data/test/call_site_analyzer_test.rb +171 -0
- data/test/code_coverage_analyzer_test.rb +188 -0
- data/test/file_statistics_test.rb +471 -0
- data/test/functional_test.rb +89 -0
- data/test/turn_off_rcovrt.rb +4 -0
- metadata +99 -0
@@ -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
|
+
|
data/lib/rcov/report.rb
ADDED
@@ -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
|
data/lib/rcov/version.rb
ADDED
@@ -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
|
data/lib/rcov/xx.rb
ADDED
@@ -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
|