representative 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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