eim_xml 0.0.3

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,135 @@
1
+ require "eim_xml"
2
+ require "eim_xml/formatter"
3
+
4
+ module EimXML::XHTML
5
+ module DocType
6
+ XHTML_MATHML = %[<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd">]
7
+ end
8
+
9
+ class Base_ < EimXML::Element
10
+ end
11
+
12
+ class HTML < Base_
13
+ attr_accessor :prefix
14
+ module NameSpace
15
+ XHTML = "http://www.w3.org/1999/xhtml"
16
+ end
17
+
18
+ def initialize(attributes={})
19
+ super(:html, attributes)
20
+ end
21
+
22
+ def write_to(out="")
23
+ out << @prefix << "\n" if @prefix
24
+ super
25
+ end
26
+ end
27
+
28
+ class Simple_ < Base_
29
+ def initialize(attributes={})
30
+ super(self.class.name[/.*::(.*)/, 1].downcase.to_sym, attributes)
31
+ end
32
+ end
33
+
34
+ class PreserveSpace_ < Simple_; end
35
+
36
+ class HEAD < Simple_; end
37
+ class META < Simple_; end
38
+ class LINK < Simple_; end
39
+ class STYLE < PreserveSpace_; end
40
+ class SCRIPT < PreserveSpace_; end
41
+ class TITLE < Simple_; end
42
+ class BODY < Simple_; end
43
+ class PRE < PreserveSpace_; end
44
+ class FORM < Simple_
45
+ def initialize(attributes={})
46
+ if attributes
47
+ if s = attributes.delete(:session)
48
+ name = attributes.delete(:session_name) || "token"
49
+ require "digest/sha1"
50
+ token = s[name] ||= Digest::SHA1.hexdigest("#{$$}#{Time.now}#{rand}")
51
+ end
52
+ end
53
+ super
54
+ add(HIDDEN.new(:name=>name, :value=>token)) if token
55
+ end
56
+ end
57
+ class H1 < PreserveSpace_; end
58
+ class H2 < PreserveSpace_; end
59
+ class H3 < PreserveSpace_; end
60
+ class H4 < PreserveSpace_; end
61
+ class H5 < PreserveSpace_; end
62
+ class H6 < PreserveSpace_; end
63
+ class P < PreserveSpace_; end
64
+ class A < PreserveSpace_; end
65
+ class EM < PreserveSpace_; end
66
+ class STRONG < PreserveSpace_; end
67
+ class DIV < Simple_; end
68
+ class SPAN < PreserveSpace_; end
69
+ class UL < Simple_; end
70
+ class OL < Simple_; end
71
+ class LI < PreserveSpace_; end
72
+ class DL < Simple_; end
73
+ class DT < PreserveSpace_; end
74
+ class DD < PreserveSpace_; end
75
+ class TABLE < Simple_; end
76
+ class CAPTION < PreserveSpace_; end
77
+ class TR < Simple_; end
78
+ class TH < PreserveSpace_; end
79
+ class TD < PreserveSpace_; end
80
+ class BR < Simple_; end
81
+ class HR < Simple_; end
82
+
83
+ module Hn
84
+ def self.new(level, attr={}, &proc)
85
+ raise ArgumentError unless 1<=level && level<=6
86
+ klass = EimXML::XHTML.const_get("H#{level}")
87
+ klass.new(attr, &proc)
88
+ end
89
+ end
90
+
91
+ class TEXTAREA < PreserveSpace_; end
92
+
93
+ class INPUT < Base_
94
+ def initialize(opt={})
95
+ super(:input, opt)
96
+ end
97
+ end
98
+
99
+ class BUTTON < Base_
100
+ def initialize(opt={})
101
+ super(:button, opt)
102
+ end
103
+ end
104
+
105
+ class SUBMIT < BUTTON
106
+ def initialize(opt={})
107
+ super(opt.merge(:type=>:submit))
108
+ end
109
+ end
110
+
111
+ class HIDDEN < INPUT
112
+ def initialize(opt={})
113
+ super(opt.merge(:type=>:hidden))
114
+ end
115
+ end
116
+
117
+ class TEXT < INPUT
118
+ def initialize(opt={})
119
+ super(opt.merge(:type=>:text))
120
+ end
121
+ end
122
+
123
+ class PASSWORD < INPUT
124
+ def initialize(opt={})
125
+ super(opt.merge(:type=>:password))
126
+ end
127
+ end
128
+
129
+ PRESERVE_SPACES = [PreserveSpace_]
130
+ class Formatter < EimXML::Formatter
131
+ def self.write(element, opt={})
132
+ EimXML::Formatter.write(element, opt.merge(:preservers=>PRESERVE_SPACES))
133
+ end
134
+ end
135
+ end
data/lib/eim_xml.rb ADDED
@@ -0,0 +1,210 @@
1
+ # Easy IMplementation of XML
2
+ #
3
+ # Copyright (C) 2006, KURODA Hiraku <hiraku@hinet.mydns.jp>
4
+ # You can redistribute it and/or modify it under GPL2.
5
+ #
6
+
7
+ module EimXML
8
+ XML_DECLARATION = %[<?xml version="1.0"?>]
9
+
10
+ class PCString
11
+ attr_reader :encoded_string, :src
12
+ alias to_s encoded_string
13
+
14
+ def self.encode(s)
15
+ s.to_s.gsub(/[&\"\'<>]/) do |m|
16
+ case m
17
+ when "&"
18
+ "&amp;"
19
+ when '"'
20
+ "&quot;"
21
+ when "'"
22
+ "&apos;"
23
+ when "<"
24
+ "&lt;"
25
+ when ">"
26
+ "&gt;"
27
+ end
28
+ end
29
+ end
30
+
31
+ def self.[](obj)
32
+ obj.is_a?(PCString) ? obj : PCString.new(obj)
33
+ end
34
+
35
+ def initialize(s, encoded=false)
36
+ @src = s
37
+ @encoded_string = encoded ? s : PCString.encode(s)
38
+ end
39
+
40
+ def ==(other)
41
+ other.is_a?(PCString) ? @encoded_string==other.encoded_string : self==PCString.new(other)
42
+ end
43
+
44
+ def write_to(out="")
45
+ out << encoded_string
46
+ end
47
+ end
48
+
49
+ class Comment
50
+ def initialize(text)
51
+ raise ArgumentError, "Can not include '--'" if text =~ /--/
52
+ @text = text
53
+ end
54
+
55
+ def write_to(out="")
56
+ out << "<!-- #{@text} -->"
57
+ end
58
+ end
59
+
60
+ class Element
61
+ attr_reader :name, :attributes, :contents
62
+
63
+ NEST = " "
64
+
65
+ def initialize(name, attributes={})
66
+ @name = name.to_sym
67
+ @attributes = Hash.new
68
+ @contents = Array.new
69
+
70
+ attributes.each do |k, v|
71
+ @attributes[k.to_sym] = v
72
+ end
73
+
74
+ yield(self) if block_given?
75
+ end
76
+
77
+ def name=(new_name)
78
+ @name = new_name.to_sym
79
+ end
80
+ protected :name=
81
+
82
+ def add(v)
83
+ case v
84
+ when nil
85
+ when Array
86
+ v.each{|i| self.add(i)}
87
+ else
88
+ @contents << v
89
+ end
90
+ self
91
+ end
92
+ alias << add
93
+
94
+ def name_and_attributes(out="")
95
+ out << "#{@name}"
96
+ @attributes.each do |k, v|
97
+ next unless v
98
+ out << " #{k}='#{PCString===v ? v : PCString.encode(v.to_s)}'"
99
+ end
100
+ end
101
+
102
+ def write_to(out = "")
103
+ out << "<"
104
+ name_and_attributes(out)
105
+
106
+ if @contents.empty?
107
+ out << " />"
108
+ else
109
+ out << ">"
110
+ @contents.each do |c|
111
+ case c
112
+ when Element
113
+ c.write_to(out)
114
+ when PCString
115
+ out << c.to_s
116
+ else
117
+ out << PCString.encode(c.to_s)
118
+ end
119
+ end
120
+ out << "</#{@name}>"
121
+ end
122
+ out
123
+ end
124
+ alias :to_s :write_to
125
+ alias :inspect :to_s
126
+
127
+ def ==(xml)
128
+ return false unless xml.is_a?(Element)
129
+ @name==xml.name && @attributes==xml.attributes && @contents==xml.contents
130
+ end
131
+
132
+ def add_attribute(key, value)
133
+ @attributes[key.to_sym] = value
134
+ end
135
+ alias []= add_attribute
136
+
137
+ def [](key)
138
+ if key.is_a?(Fixnum)
139
+ @contents[key]
140
+ else
141
+ @attributes[key.to_sym]
142
+ end
143
+ end
144
+
145
+ def del_attribute(key)
146
+ @attributes.delete(key.to_sym)
147
+ end
148
+
149
+ def pcstring_contents
150
+ @contents.select{|c| c.is_a?(String)||c.is_a?(PCString)}.map{|c| c.is_a?(String) ? PCString.new(c) : c}
151
+ end
152
+
153
+ def match(obj, attr=nil)
154
+ return match(Element.new(obj, attr)) if attr
155
+ return obj=~@name.to_s if obj.is_a?(Regexp)
156
+ return @name==obj if obj.is_a?(Symbol)
157
+ return is_a?(obj) if obj.is_a?(Module)
158
+
159
+ raise ArgumentError unless obj.is_a?(Element)
160
+
161
+ return false unless @name==obj.name
162
+
163
+ obj.attributes.all? do |k, v|
164
+ (v.nil? && !@attributes.include?(k)) ||
165
+ (@attributes.include?(k) && (v.is_a?(Regexp) ? v =~ @attributes[k] : PCString[v] == PCString[@attributes[k]]))
166
+ end and obj.contents.all? do |i|
167
+ case i
168
+ when Element
169
+ has_element?(i)
170
+ when String
171
+ pcstring_contents.include?(PCString.new(i))
172
+ when PCString
173
+ pcstring_contents.include?(i)
174
+ when Regexp
175
+ @contents.any?{|c| c.is_a?(String) and i=~c}
176
+ end
177
+ end
178
+ end
179
+ alias :=~ :match
180
+
181
+ def has?(obj, attr=nil)
182
+ return has?(Element.new(obj, attr)) if attr
183
+
184
+ @contents.any? do |i|
185
+ if i.is_a?(Element)
186
+ i.match(obj) || i.has?(obj)
187
+ else
188
+ obj.is_a?(Module) && i.is_a?(obj)
189
+ end
190
+ end
191
+ end
192
+ alias has_element? has?
193
+ alias include? has?
194
+
195
+ def find(obj, dst=Element.new(:found))
196
+ return find(Element.new(obj, dst)) if dst.is_a?(Hash)
197
+
198
+ dst << self if match(obj)
199
+ @contents.each do |i|
200
+ case
201
+ when i.is_a?(Element)
202
+ i.find(obj, dst)
203
+ when obj.is_a?(Module) && i.is_a?(obj)
204
+ dst << i
205
+ end
206
+ end
207
+ dst
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,29 @@
1
+ # Test for eim_xml/assertions.rb
2
+ #
3
+ # Copyright (C) 2006, KURODA Hiraku <hiraku@hinet.mydns.jp>
4
+ # You can redistribute it and/or modify it under GPL2.
5
+
6
+ require "test/unit"
7
+ require "eim_xml/assertions"
8
+
9
+ class EimXMLAssertionsTest < Test::Unit::TestCase
10
+ include EimXML
11
+ include EimXML::Assertions
12
+
13
+ def test_assert_has
14
+ e = Element.new(:tag) do |e|
15
+ e <<= Element.new(:sub)
16
+ end
17
+
18
+ assert_nothing_raised do
19
+ assert_has(:sub, e)
20
+ end
21
+
22
+ a = assert_raises(Test::Unit::AssertionFailedError) do
23
+ assert_has(:no, e)
24
+ end
25
+ assert(!a.backtrace.any?{ |i|
26
+ i=~/eim_xml\/assertions\.rb/
27
+ })
28
+ end
29
+ end
data/spec/dsl_spec.rb ADDED
@@ -0,0 +1,217 @@
1
+ require "eim_xml/dsl"
2
+
3
+ module Module.new::M
4
+ include EimXML
5
+ EDSL = EimXML::DSL
6
+
7
+ describe EimXML::DSL do
8
+ it "scope is in instance of DSL" do
9
+ outer = inner = nil
10
+ e3 = e2 = nil
11
+ block_executed = false
12
+ e = EDSL.element(:out, :k1=>"v1") do
13
+ outer = self
14
+ e2 = element(:in, :k2=>"v2") do
15
+ block_executed = true
16
+ inner = self
17
+ e3 = element(:deep)
18
+ end
19
+ end
20
+
21
+ block_executed.should == true
22
+ outer.should be_kind_of(EDSL)
23
+ inner.should be_kind_of(EDSL)
24
+ outer.should be_equal(inner)
25
+
26
+ e.name.should == :out
27
+ e[:k1].should == "v1"
28
+ e[0].name.should == :in
29
+ e[0][:k2].should == "v2"
30
+ e[0][0].name.should == :deep
31
+ e2.should be_equal(e[0])
32
+ e3.should be_equal(e[0][0])
33
+ end
34
+
35
+ it "#comment" do
36
+ Comment.should_receive(:new).with("comment").and_return(:success)
37
+ EDSL.comment("comment").should == :success
38
+ end
39
+
40
+ it "#import_variables" do
41
+ d = EDSL.new
42
+ o = Object.new
43
+ o.instance_variable_set("@v1", 1)
44
+ o.instance_variable_set("@v2", "2")
45
+ o.instance_variable_set("@_v3", :t)
46
+ o.instance_variable_set("@__v4", 4)
47
+ o.instance_variable_set("@_container", :t)
48
+ orig_c = d.instance_variable_get("@_container")
49
+
50
+ d.import_variables(o).should be_equal(d)
51
+
52
+ d.instance_variable_get("@_container").should == orig_c
53
+ d.instance_variables.map(&:to_s).sort.should == ["@v1", "@v2", "@__v4"].sort
54
+ d.instance_variable_get("@v1").should == 1
55
+ d.instance_variable_get("@v2").should == "2"
56
+ d.instance_variable_get("@__v4").should == 4
57
+ end
58
+
59
+ describe "#_push" do
60
+ before do
61
+ m = Module.new
62
+ class m::D < EimXML::DSL
63
+ def call_push(c)
64
+ _push(c) do
65
+ element(:e)
66
+ end
67
+ end
68
+
69
+ def exec
70
+ element(:e) do
71
+ element(:f)
72
+ end
73
+ end
74
+ end
75
+ @D = m::D
76
+ end
77
+
78
+ it "should return given container" do
79
+ a = []
80
+ @D.new.call_push(a).should be_equal(a)
81
+ a.should == [EimXML::Element.new(:e)]
82
+
83
+ @D.new.exec.should == EimXML::Element.new(:e).add(EimXML::Element.new(:f))
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "Subclass of BaseDSL" do
89
+ class DSL1 < EimXML::BaseDSL
90
+ register([EimXML::Element, "call"])
91
+ register(Hash)
92
+ register(String, Array, Object)
93
+ end
94
+
95
+ it "register" do
96
+ lambda{EDSL.call(:dummy)}.should raise_error(NoMethodError)
97
+ lambda{BaseDSL.call(:dummy)}.should raise_error(NoMethodError)
98
+ lambda{DSL1.element(:dummy)}.should raise_error(NoMethodError)
99
+ DSL1.call(:dummy).should be_kind_of(Element)
100
+ DSL1.hash.should be_kind_of(Hash)
101
+ DSL1.string.should be_kind_of(String)
102
+ DSL1.array.should be_kind_of(Array)
103
+ DSL1.object.should be_kind_of(Object)
104
+ end
105
+ end
106
+
107
+ describe EimXML::OpenDSL do
108
+ it "scope of block is one of outside" do
109
+ @scope_checker_variable = 1
110
+ block_executed = false
111
+ d = OpenDSL.new do |d|
112
+ block_executed = true
113
+ d.should be_kind_of(OpenDSL)
114
+ d.container.should be_nil
115
+ d.element(:base, :key1=>"v1") do
116
+ @scope_checker_variable.should == 1
117
+ self.should_not be_kind_of(Element)
118
+ d.container.should be_kind_of(Element)
119
+ d.container.should == Element.new(:base, :key1=>"v1")
120
+ d.element(:sub, :key2=>"v2") do
121
+ d.container.should be_kind_of(Element)
122
+ d.container.should == Element.new(:sub, :key2=>"v2")
123
+ end
124
+ d.element(:sub2).should == Element.new(:sub2)
125
+ end
126
+ end
127
+ block_executed.should be_true
128
+ end
129
+
130
+ it "DSL methods return element" do
131
+ d = OpenDSL.new
132
+ d.container.should be_nil
133
+ r = d.element(:base, :key1=>"v1") do
134
+ d.element(:sub, :key2=>"v2")
135
+ end
136
+ r.should == EDSL.element(:base, :key1=>"v1") do
137
+ element(:sub, :key2=>"v2")
138
+ end
139
+ end
140
+
141
+ it "DSL method's block given instance of OpenDSL" do
142
+ e = OpenDSL.new.element(:base) do |d|
143
+ d.should be_kind_of(OpenDSL)
144
+ d.container.name.should == :base
145
+ d.element(:sub) do |d2|
146
+ d2.should be_equal(d)
147
+ end
148
+ end
149
+
150
+ e.should == EDSL.element(:base) do
151
+ element(:sub)
152
+ end
153
+ end
154
+
155
+ it "ensure reset container when error raised" do
156
+ OpenDSL.new do |d|
157
+ begin
158
+ d.element(:base) do
159
+ begin
160
+ d.element(:sub) do
161
+ raise "OK"
162
+ end
163
+ rescue RuntimeError => e
164
+ raise unless e.message=="OK"
165
+ d.container.name.should == :base
166
+ raise
167
+ end
168
+ end
169
+ rescue RuntimeError => e
170
+ raise unless e.message=="OK"
171
+ d.container.should == nil
172
+ end
173
+ end
174
+ end
175
+
176
+ it "respond to add" do
177
+ r = OpenDSL.new.element(:base) do |d|
178
+ d.add "text"
179
+ d.element(:sub) do
180
+ s = Element.new(:sub)
181
+ s.add("sub text")
182
+ d.add("sub text").should == s
183
+ end
184
+ end
185
+
186
+ r.should == EDSL.element(:base) do
187
+ add "text"
188
+ element(:sub) do
189
+ add "sub text"
190
+ end
191
+ end
192
+ end
193
+
194
+ it "respond to <<" do
195
+ r = OpenDSL.new.element(:base) do |d|
196
+ b = Element.new(:base)
197
+ b << "text" << "next"
198
+ (d << "text" << "next").should == b
199
+ end
200
+ r.should == EDSL.element(:base) do
201
+ add "text"
202
+ add "next"
203
+ end
204
+ end
205
+
206
+ it "can call directly element method" do
207
+ r = OpenDSL.element(:base) do |d|
208
+ d.element(:sub)
209
+ d.element(:sub2)
210
+ end
211
+ r.should == EDSL.element(:base) do
212
+ element(:sub)
213
+ element(:sub2)
214
+ end
215
+ end
216
+ end
217
+ end