representative 0.2.0 → 0.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.
@@ -1,3 +1,3 @@
1
1
  module Representative
2
- VERSION = "0.2.0".freeze
2
+ VERSION = "0.2.1".freeze
3
3
  end
@@ -6,8 +6,14 @@ require "representative/object_inspector"
6
6
 
7
7
  module Representative
8
8
 
9
- class Xml < BlankSlate
10
-
9
+ # Easily generate XML while traversing an object-graph.
10
+ #
11
+ class Xml
12
+
13
+ # Create an XML-generating Representative. The first argument should be an instance of
14
+ # Builder::XmlMarkup (or something that implements it's interface). The second argument
15
+ # if any, is the initial #subject of representation.
16
+ #
11
17
  def initialize(xml_builder, subject = nil, options = {})
12
18
  @xml = xml_builder
13
19
  @subjects = [subject]
@@ -15,7 +21,18 @@ module Representative
15
21
  yield self if block_given?
16
22
  end
17
23
 
18
- def represent(subject)
24
+ # Return the current "subject" of representation.
25
+ #
26
+ # This object will provide element values where they haven't been
27
+ # explicitly provided.
28
+ #
29
+ def subject
30
+ @subjects.last
31
+ end
32
+
33
+ # Evaluate a block with a specified object as #subject.
34
+ #
35
+ def representing(subject)
19
36
  @subjects.push(subject)
20
37
  begin
21
38
  yield subject
@@ -23,14 +40,42 @@ module Representative
23
40
  @subjects.pop
24
41
  end
25
42
  end
26
-
27
- def subject
28
- @subjects.last
29
- end
30
-
43
+
44
+ # Generate an element.
45
+ #
46
+ # With two arguments, it generates an element with the specified text content.
47
+ #
48
+ # r.element :size, 42
49
+ # # => <size>42</size>
50
+ #
51
+ # More commonly, though, the second argument is omitted, in which case the
52
+ # element content is assumed to be the named property of the current #subject.
53
+ #
54
+ # r.representing my_shoe do
55
+ # r.element :size
56
+ # end
57
+ # # => <size>9</size>
58
+ #
59
+ # If a block is attached, nested elements can be generated. The element "value"
60
+ # (whether explicitly provided, or derived from the current subject) becomes the
61
+ # subject during evaluation of the block.
62
+ #
63
+ # r.element :book, book do
64
+ # r.title
65
+ # r.author
66
+ # end
67
+ # # => <book><title>Whatever</title><author>Whoever</author></book>
68
+ #
69
+ # Providing a final Hash argument specifies element attributes.
70
+ #
71
+ # r.element :size, :type => "integer"
72
+ # # => <size type="integer">9</size>
73
+ #
31
74
  def element(name, *args, &block)
32
75
 
33
- element_attributes = args.extract_options!
76
+ attributes = args.extract_options!
77
+ attributes = attributes.merge(@inspector.get_metadata(subject, name))
78
+
34
79
  value_generator = if args.empty?
35
80
  lambda do |subject|
36
81
  @inspector.get_value(subject, name)
@@ -38,64 +83,75 @@ module Representative
38
83
  else
39
84
  args.shift
40
85
  end
86
+
41
87
  raise ArgumentError, "too many arguments" unless args.empty?
42
88
 
43
89
  value = resolve_value(value_generator)
44
- resolved_element_attributes = resolve_element_attributes(element_attributes, value)
45
- resolved_element_attributes.merge!(@inspector.get_metadata(subject, name))
90
+ return @xml.tag!(name) if value.nil?
91
+
92
+ representing(value) do
93
+
94
+ content_string = subject.to_s unless block
95
+ content_block = unless block.nil? || block == Representative::EMPTY
96
+ Proc.new do
97
+ block.call(subject)
98
+ end
99
+ end
100
+
101
+ resolved_attributes = resolve_attributes(attributes)
102
+ tag_args = [content_string, resolved_attributes].compact
46
103
 
47
- emit_element(name, value, resolved_element_attributes, &block)
104
+ @xml.tag!(name.to_s.dasherize, *tag_args, &content_block)
105
+
106
+ end
48
107
 
49
108
  end
50
109
 
51
- def list_of(attribute_name, *args, &block)
110
+ # Generate a list of elements from Enumerable data.
111
+ #
112
+ # r.list_of :books, my_books do
113
+ # r.element :title
114
+ # end
115
+ # # => <books type="array">
116
+ # # <book><title>Sailing for old dogs</title></book>
117
+ # # <book><title>On the horizon</title></book>
118
+ # # <book><title>The Little Blue Book of VHS Programming</title></book>
119
+ # # </books>
120
+ #
121
+ # Like #element, the value can be explicit, but is more commonly extracted
122
+ # by name from the current #subject.
123
+ #
124
+ def list_of(name, *args, &block)
52
125
 
53
126
  options = args.extract_options!
54
- value_generator = args.empty? ? attribute_name : args.shift
127
+ value_generator = args.empty? ? name : args.shift
55
128
  raise ArgumentError, "too many arguments" unless args.empty?
56
129
 
57
- list_name = attribute_name.to_s.dasherize
58
- list_element_attributes = options[:list_attributes] || {}
59
- item_name = options[:item_name] || list_name.singularize
60
- item_element_attributes = options[:item_attributes] || {}
130
+ list_attributes = options[:list_attributes] || {}
131
+ item_name = options[:item_name] || name.to_s.singularize
132
+ item_attributes = options[:item_attributes] || {}
61
133
 
62
134
  items = resolve_value(value_generator)
63
- if items.nil?
64
- return @xml.tag!(list_name)
65
- end
66
-
67
- resolved_list_element_attributes = resolve_element_attributes(list_element_attributes, items)
68
-
69
- @xml.tag!(list_name, resolved_list_element_attributes.merge(:type => "array")) do
135
+ element(name, items, list_attributes.merge(:type => "array")) do
70
136
  items.each do |item|
71
- resolved_item_element_attributes = resolve_element_attributes(item_element_attributes, item)
72
- emit_element(item_name, item, resolved_item_element_attributes, &block)
137
+ element(item_name, item, item_attributes, &block)
73
138
  end
74
139
  end
75
140
 
76
141
  end
77
142
 
143
+ # Return a magic value that, when passed to #element as a block, forces
144
+ # generation of an empty element.
145
+ #
146
+ # r.element(:link, :rel => "me", :href => "http://dogbiscuit.org", &r.empty)
147
+ # # => <link rel="parent" href="http://dogbiscuit.org"/>
148
+ #
78
149
  def empty
79
150
  Representative::EMPTY
80
151
  end
81
152
 
82
153
  private
83
154
 
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
155
  def resolve_value(value_generator, subject = subject)
100
156
  if value_generator == :self
101
157
  subject
@@ -106,9 +162,9 @@ module Representative
106
162
  end
107
163
  end
108
164
 
109
- def resolve_element_attributes(element_attributes, subject)
110
- if element_attributes
111
- element_attributes.inject({}) do |resolved, (name, value_generator)|
165
+ def resolve_attributes(attributes)
166
+ if attributes
167
+ attributes.inject({}) do |resolved, (name, value_generator)|
112
168
  resolved_value = resolve_value(value_generator, subject)
113
169
  resolved[name.to_s.dasherize] = resolved_value unless resolved_value.nil?
114
170
  resolved
@@ -148,8 +148,9 @@ describe Representative::Xml do
148
148
 
149
149
  it "yields each new subject" do
150
150
  r.element :vehicle do |vehicle|
151
- vehicle.should == @subject.vehicle
151
+ r.element :year, vehicle.year
152
152
  end
153
+ resulting_xml.should == %(<vehicle><year>1959</year></vehicle>)
153
154
  end
154
155
 
155
156
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 0
9
- version: 0.2.0
8
+ - 1
9
+ version: 0.2.1
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-07-02 00:00:00 +10:00
17
+ date: 2010-07-04 00:00:00 +10:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency