representative 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +15 -12
- data/examples/xml_demo.rb +9 -9
- data/lib/representative/version.rb +1 -1
- data/lib/representative/xml.rb +33 -39
- data/spec/representative/xml_spec.rb +62 -48
- metadata +4 -4
data/README.markdown
CHANGED
@@ -37,15 +37,18 @@ Given a Ruby data-structure:
|
|
37
37
|
Representative::Xml can be used to generate XML, in a declarative style:
|
38
38
|
|
39
39
|
xml = Builder::XmlMarkup.new(:indent => 2)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
40
|
+
|
41
|
+
Representative::Xml.new(xml) do |r|
|
42
|
+
|
43
|
+
r.list_of :books, books do
|
44
|
+
r.element :title
|
45
|
+
r.list_of :authors
|
46
|
+
r.element :published do
|
47
|
+
r.element :by
|
48
|
+
r.element :year
|
49
|
+
end
|
48
50
|
end
|
51
|
+
|
49
52
|
end
|
50
53
|
|
51
54
|
puts xml.target!
|
@@ -85,18 +88,18 @@ The resulting XML looks like this:
|
|
85
88
|
|
86
89
|
Notice that:
|
87
90
|
|
88
|
-
- Representative generates elements for each object-attribute you name (and not the ones you don't).
|
89
91
|
- The structure of the XML mirrors the structure described by the nested Ruby blocks.
|
90
|
-
-
|
92
|
+
- Representative walks the object-graph for you.
|
93
|
+
- Using `list_of` for a collection attribute generates an "array" element, which plays nicely
|
91
94
|
with most Ruby XML-to-hash converters.
|
92
95
|
- Where a named object-attribute is nil, you get an empty element.
|
93
96
|
|
94
97
|
Installation
|
95
98
|
------------
|
96
99
|
|
97
|
-
Representative is packaged as a Gem
|
100
|
+
Representative is packaged as a Gem. Install with:
|
98
101
|
|
99
|
-
|
102
|
+
gem install representative
|
100
103
|
|
101
104
|
Copyright
|
102
105
|
---------
|
data/examples/xml_demo.rb
CHANGED
@@ -29,15 +29,15 @@ books = [
|
|
29
29
|
]
|
30
30
|
|
31
31
|
xml = Builder::XmlMarkup.new(:indent => 2)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
32
|
+
Representative::Xml.new(xml) do |r|
|
33
|
+
r.list_of :books, books do
|
34
|
+
r.element :title
|
35
|
+
r.list_of :authors
|
36
|
+
r.element :published do
|
37
|
+
r.element :by
|
38
|
+
r.element :year
|
39
|
+
end
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
puts xml.target!
|
43
|
+
puts xml.target!
|
data/lib/representative/xml.rb
CHANGED
@@ -15,61 +15,40 @@ module Representative
|
|
15
15
|
yield self if block_given?
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
def represent(subject)
|
19
|
+
@subjects.push(subject)
|
20
|
+
begin
|
21
|
+
yield subject
|
22
|
+
ensure
|
23
|
+
@subjects.pop
|
23
24
|
end
|
24
25
|
end
|
25
|
-
|
26
|
-
def subject
|
26
|
+
|
27
|
+
def subject
|
27
28
|
@subjects.last
|
28
29
|
end
|
29
30
|
|
30
|
-
def
|
31
|
+
def element(name, *args, &block)
|
31
32
|
|
32
33
|
element_attributes = args.extract_options!
|
33
34
|
value_generator = if args.empty?
|
34
35
|
lambda do |subject|
|
35
|
-
@inspector.get_value(subject,
|
36
|
+
@inspector.get_value(subject, name)
|
36
37
|
end
|
37
38
|
else
|
38
39
|
args.shift
|
39
40
|
end
|
40
41
|
raise ArgumentError, "too many arguments" unless args.empty?
|
41
42
|
|
42
|
-
element_name = subject_attribute_name.to_s.dasherize
|
43
|
-
|
44
43
|
value = resolve_value(value_generator)
|
45
44
|
resolved_element_attributes = resolve_element_attributes(element_attributes, value)
|
46
|
-
resolved_element_attributes.merge!(@inspector.get_metadata(subject
|
45
|
+
resolved_element_attributes.merge!(@inspector.get_metadata(subject, name))
|
47
46
|
|
48
|
-
|
47
|
+
emit_element(name, value, resolved_element_attributes, &block)
|
49
48
|
|
50
49
|
end
|
51
50
|
|
52
|
-
def
|
53
|
-
content = content_generator = nil
|
54
|
-
if block && subject
|
55
|
-
unless block == Representative::EMPTY
|
56
|
-
content_generator = Proc.new do
|
57
|
-
@subjects.push(subject)
|
58
|
-
begin
|
59
|
-
block.call(self)
|
60
|
-
ensure
|
61
|
-
@subjects.pop
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
else
|
66
|
-
content = subject
|
67
|
-
end
|
68
|
-
tag_args = [content, options].compact
|
69
|
-
@xml.tag!(element_name, *tag_args, &content_generator)
|
70
|
-
end
|
71
|
-
|
72
|
-
def list_of!(attribute_name, *args, &block)
|
51
|
+
def list_of(attribute_name, *args, &block)
|
73
52
|
|
74
53
|
options = args.extract_options!
|
75
54
|
value_generator = args.empty? ? attribute_name : args.shift
|
@@ -90,19 +69,34 @@ module Representative
|
|
90
69
|
@xml.tag!(list_name, resolved_list_element_attributes.merge(:type => "array")) do
|
91
70
|
items.each do |item|
|
92
71
|
resolved_item_element_attributes = resolve_element_attributes(item_element_attributes, item)
|
93
|
-
|
72
|
+
emit_element(item_name, item, resolved_item_element_attributes, &block)
|
94
73
|
end
|
95
74
|
end
|
96
75
|
|
97
76
|
end
|
98
77
|
|
99
|
-
def empty
|
100
|
-
|
78
|
+
def empty
|
79
|
+
Representative::EMPTY
|
101
80
|
end
|
102
|
-
|
81
|
+
|
103
82
|
private
|
104
83
|
|
105
|
-
def
|
84
|
+
def emit_element(name, subject, options, &content_block)
|
85
|
+
content = content_generator = nil
|
86
|
+
if subject && content_block
|
87
|
+
unless content_block == Representative::EMPTY
|
88
|
+
content_generator = Proc.new do
|
89
|
+
represent(subject, &content_block)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
content = subject
|
94
|
+
end
|
95
|
+
tag_args = [content, options].compact
|
96
|
+
@xml.tag!(name.to_s.dasherize, *tag_args, &content_generator)
|
97
|
+
end
|
98
|
+
|
99
|
+
def resolve_value(value_generator, subject = subject)
|
106
100
|
if value_generator == :self
|
107
101
|
subject
|
108
102
|
elsif value_generator.respond_to?(:to_proc)
|
@@ -10,8 +10,8 @@ describe Representative::Xml do
|
|
10
10
|
@xml = Builder::XmlMarkup.new
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
@
|
13
|
+
def r
|
14
|
+
@representative ||= Representative::Xml.new(@xml, @subject)
|
15
15
|
end
|
16
16
|
|
17
17
|
def resulting_xml
|
@@ -22,38 +22,37 @@ describe Representative::Xml do
|
|
22
22
|
|
23
23
|
before do
|
24
24
|
@subject = OpenStruct.new(:name => "Fred", :width => 200, :vehicle => OpenStruct.new(:year => "1959", :make => "Chevrolet"))
|
25
|
-
@xml_representative = Representative::Xml.new(@xml, @subject)
|
26
25
|
end
|
27
26
|
|
28
|
-
describe "
|
27
|
+
describe "#element" do
|
29
28
|
|
30
29
|
it "generates an element with content extracted from the subject" do
|
31
|
-
|
30
|
+
r.element :name
|
32
31
|
resulting_xml.should == %(<name>Fred</name>)
|
33
32
|
end
|
34
33
|
|
35
|
-
it "dasherizes the
|
34
|
+
it "dasherizes the property name" do
|
36
35
|
@subject.full_name = "Fredrick"
|
37
|
-
|
36
|
+
r.element :full_name
|
38
37
|
resulting_xml.should == %(<full-name>Fredrick</full-name>)
|
39
38
|
end
|
40
39
|
|
41
|
-
describe "with
|
40
|
+
describe "with attributes" do
|
42
41
|
|
43
42
|
it "generates attributes on the element" do
|
44
|
-
|
43
|
+
r.element :name, :lang => "fr"
|
45
44
|
resulting_xml.should == %(<name lang="fr">Fred</name>)
|
46
45
|
end
|
47
46
|
|
48
47
|
it "dasherizes the attribute name" do
|
49
|
-
|
48
|
+
r.element :name, :sourced_from => "phonebook"
|
50
49
|
resulting_xml.should == %(<name sourced-from="phonebook">Fred</name>)
|
51
50
|
end
|
52
51
|
|
53
52
|
describe "whose value supports #to_proc" do
|
54
53
|
|
55
54
|
it "calls the Proc on the subject to generate a value" do
|
56
|
-
|
55
|
+
r.element :name, :rev => :reverse
|
57
56
|
resulting_xml.should == %(<name rev="derF">Fred</name>)
|
58
57
|
end
|
59
58
|
|
@@ -62,7 +61,7 @@ describe Representative::Xml do
|
|
62
61
|
describe "with value nil" do
|
63
62
|
|
64
63
|
it "omits the attribute" do
|
65
|
-
|
64
|
+
r.element :name, :lang => nil
|
66
65
|
resulting_xml.should == %(<name>Fred</name>)
|
67
66
|
end
|
68
67
|
|
@@ -70,17 +69,17 @@ describe Representative::Xml do
|
|
70
69
|
|
71
70
|
end
|
72
71
|
|
73
|
-
describe "with
|
72
|
+
describe "with an explicit value" do
|
74
73
|
|
75
74
|
it "generates an element with explicitly provided content" do
|
76
|
-
|
75
|
+
r.element :name, "Bloggs"
|
77
76
|
resulting_xml.should == %(<name>Bloggs</name>)
|
78
77
|
end
|
79
78
|
|
80
|
-
describe "AND
|
79
|
+
describe "AND attributes" do
|
81
80
|
|
82
81
|
it "generates attributes on the element" do
|
83
|
-
|
82
|
+
r.element :name, "Bloggs", :lang => "fr"
|
84
83
|
resulting_xml.should == %(<name lang="fr">Bloggs</name>)
|
85
84
|
end
|
86
85
|
|
@@ -88,37 +87,37 @@ describe Representative::Xml do
|
|
88
87
|
|
89
88
|
end
|
90
89
|
|
91
|
-
describe "with
|
90
|
+
describe "with a value argument that supports #to_proc" do
|
92
91
|
|
93
92
|
it "calls the Proc on the subject to generate a value" do
|
94
|
-
|
93
|
+
r.element :name, :width
|
95
94
|
resulting_xml.should == %(<name>200</name>)
|
96
95
|
end
|
97
96
|
|
98
97
|
end
|
99
98
|
|
100
|
-
describe "with argument :self" do
|
99
|
+
describe "with value argument :self" do
|
101
100
|
|
102
101
|
it "doesn't alter the subject" do
|
103
|
-
|
104
|
-
|
102
|
+
r.element :info, :self do
|
103
|
+
r.element :name
|
105
104
|
end
|
106
105
|
resulting_xml.should == %(<info><name>Fred</name></info>)
|
107
106
|
end
|
108
107
|
|
109
108
|
end
|
110
109
|
|
111
|
-
describe "with
|
110
|
+
describe "with value argument nil" do
|
112
111
|
|
113
112
|
it "builds an empty element" do
|
114
|
-
|
113
|
+
r.element :name, nil
|
115
114
|
resulting_xml.should == %(<name/>)
|
116
115
|
end
|
117
116
|
|
118
|
-
describe "and
|
117
|
+
describe "and attributes" do
|
119
118
|
|
120
|
-
it "omits the
|
121
|
-
|
119
|
+
it "omits the attributes" do
|
120
|
+
r.element :name, nil, :size => :size
|
122
121
|
resulting_xml.should == %(<name/>)
|
123
122
|
end
|
124
123
|
|
@@ -127,8 +126,8 @@ describe Representative::Xml do
|
|
127
126
|
describe "and a block" do
|
128
127
|
|
129
128
|
it "doesn't call the block" do
|
130
|
-
|
131
|
-
|
129
|
+
r.element :name, nil do
|
130
|
+
raise "hell"
|
132
131
|
end
|
133
132
|
resulting_xml.should == %(<name/>)
|
134
133
|
end
|
@@ -140,41 +139,47 @@ describe Representative::Xml do
|
|
140
139
|
describe "with a block" do
|
141
140
|
|
142
141
|
it "generates nested elements" do
|
143
|
-
|
144
|
-
|
145
|
-
|
142
|
+
r.element :vehicle do
|
143
|
+
r.element :year
|
144
|
+
r.element :make
|
146
145
|
end
|
147
146
|
resulting_xml.should == %(<vehicle><year>1959</year><make>Chevrolet</make></vehicle>)
|
148
147
|
end
|
149
148
|
|
149
|
+
it "yields each new subject" do
|
150
|
+
r.element :vehicle do |vehicle|
|
151
|
+
vehicle.should == @subject.vehicle
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
150
155
|
end
|
151
|
-
|
152
|
-
end
|
153
156
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
157
|
+
describe "with an EMPTY block" do
|
158
|
+
|
159
|
+
it "generates an empty element" do
|
160
|
+
r.element :vehicle, :year => :year, &r.empty
|
161
|
+
resulting_xml.should == %(<vehicle year="1959"/>)
|
162
|
+
end
|
163
|
+
|
159
164
|
end
|
160
165
|
|
161
166
|
end
|
162
167
|
|
163
|
-
describe "#list_of
|
168
|
+
describe "#list_of" do
|
164
169
|
|
165
170
|
before do
|
166
171
|
@subject.nick_names = ["Freddie", "Knucklenose"]
|
167
172
|
end
|
168
173
|
|
169
174
|
it "generates an array element" do
|
170
|
-
|
175
|
+
r.list_of(:nick_names)
|
171
176
|
resulting_xml.should == %(<nick-names type="array"><nick-name>Freddie</nick-name><nick-name>Knucklenose</nick-name></nick-names>)
|
172
177
|
end
|
173
178
|
|
174
179
|
describe "with :list_attributes" do
|
175
180
|
|
176
181
|
it "attaches attributes to the array element" do
|
177
|
-
|
182
|
+
r.list_of(:nick_names, :list_attributes => {:color => "blue", :size => :size})
|
178
183
|
array_element_attributes = REXML::Document.new(resulting_xml).root.attributes
|
179
184
|
array_element_attributes["type"].should == "array"
|
180
185
|
array_element_attributes["color"].should == "blue"
|
@@ -187,7 +192,7 @@ describe Representative::Xml do
|
|
187
192
|
describe "with :item_attributes" do
|
188
193
|
|
189
194
|
it "attaches attributes to each item element" do
|
190
|
-
|
195
|
+
r.list_of(:nick_names, :item_attributes => {:length => :size})
|
191
196
|
resulting_xml.should == %(<nick-names type="array"><nick-name length="7">Freddie</nick-name><nick-name length="11">Knucklenose</nick-name></nick-names>)
|
192
197
|
end
|
193
198
|
|
@@ -195,7 +200,7 @@ describe Representative::Xml do
|
|
195
200
|
|
196
201
|
describe "with an explicit :item_name" do
|
197
202
|
it "uses the name provided" do
|
198
|
-
|
203
|
+
r.list_of(:nick_names, :item_name => :nick)
|
199
204
|
resulting_xml.should == %(<nick-names type="array"><nick>Freddie</nick><nick>Knucklenose</nick></nick-names>)
|
200
205
|
end
|
201
206
|
end
|
@@ -203,7 +208,7 @@ describe Representative::Xml do
|
|
203
208
|
describe "with an argument that resolves to nil" do
|
204
209
|
|
205
210
|
it "omits the attribute" do
|
206
|
-
|
211
|
+
r.list_of(:flags)
|
207
212
|
resulting_xml.should == %(<flags/>)
|
208
213
|
end
|
209
214
|
|
@@ -212,19 +217,28 @@ describe Representative::Xml do
|
|
212
217
|
describe "with a block" do
|
213
218
|
|
214
219
|
it "generates a nested element for each list element" do
|
215
|
-
|
216
|
-
|
220
|
+
r.list_of(:nick_names) do
|
221
|
+
r.element :length
|
217
222
|
end
|
218
223
|
resulting_xml.should == %(<nick-names type="array"><nick-name><length>7</length></nick-name><nick-name><length>11</length></nick-name></nick-names>)
|
219
224
|
end
|
220
225
|
|
221
226
|
end
|
222
227
|
|
228
|
+
describe "with an EMPTY block" do
|
229
|
+
|
230
|
+
it "generates empty elements for each list element" do
|
231
|
+
r.list_of(:nick_names, :item_attributes => {:value => :to_s}, &r.empty)
|
232
|
+
resulting_xml.should == %(<nick-names type="array"><nick-name value="Freddie"/><nick-name value="Knucklenose"/></nick-names>)
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
223
237
|
describe "with :item_attributes AND block" do
|
224
238
|
|
225
239
|
it "generates attributes and nested elements" do
|
226
|
-
|
227
|
-
|
240
|
+
r.list_of(:nick_names, :item_attributes => {:length => :size}) do
|
241
|
+
r.element :reverse
|
228
242
|
end
|
229
243
|
resulting_xml.should == %(<nick-names type="array"><nick-name length="7"><reverse>eidderF</reverse></nick-name><nick-name length="11"><reverse>esonelkcunK</reverse></nick-name></nick-names>)
|
230
244
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Mike Williams
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-07-02 00:00:00 +10:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|