representative 0.2.5 → 0.3.0
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/Rakefile +4 -9
- data/lib/representative/abstract_xml.rb +119 -0
- data/lib/representative/json.rb +14 -2
- data/lib/representative/nokogiri.rb +61 -0
- data/lib/representative/tilt_integration.rb +56 -0
- data/lib/representative/version.rb +1 -1
- data/lib/representative/xml.rb +10 -116
- data/spec/representative/json_spec.rb +49 -0
- data/spec/representative/nokogiri_spec.rb +74 -0
- data/spec/representative/tilt_integration_spec.rb +136 -0
- data/spec/representative/xml_behaviour.rb +270 -0
- data/spec/representative/xml_spec.rb +3 -264
- data/spec/spec_helper.rb +0 -3
- metadata +65 -41
data/Rakefile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
require "rubygems"
|
2
1
|
require "rake"
|
3
|
-
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
|
5
|
+
Bundler::GemHelper.install_tasks
|
4
6
|
|
5
7
|
require "spec/rake/spectask"
|
6
8
|
|
@@ -19,10 +21,3 @@ Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
19
21
|
spec.rcov = true
|
20
22
|
end
|
21
23
|
|
22
|
-
require "yard"
|
23
|
-
|
24
|
-
YARD::Rake::YardocTask.new(:yardoc) do |t|
|
25
|
-
t.files = FileList['lib/**/*.rb']
|
26
|
-
end
|
27
|
-
|
28
|
-
CLEAN << "doc"
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require "representative/base"
|
2
|
+
|
3
|
+
module Representative
|
4
|
+
|
5
|
+
class AbstractXml < Base
|
6
|
+
|
7
|
+
# Generate an element.
|
8
|
+
#
|
9
|
+
# With two arguments, it generates an element with the specified text content.
|
10
|
+
#
|
11
|
+
# r.element :size, 42
|
12
|
+
# # => <size>42</size>
|
13
|
+
#
|
14
|
+
# More commonly, though, the second argument is omitted, in which case the
|
15
|
+
# element content is assumed to be the named property of the current #subject.
|
16
|
+
#
|
17
|
+
# r.representing my_shoe do
|
18
|
+
# r.element :size
|
19
|
+
# end
|
20
|
+
# # => <size>9</size>
|
21
|
+
#
|
22
|
+
# If a block is attached, nested elements can be generated. The element "value"
|
23
|
+
# (whether explicitly provided, or derived from the current subject) becomes the
|
24
|
+
# subject during evaluation of the block.
|
25
|
+
#
|
26
|
+
# r.element :book, book do
|
27
|
+
# r.title
|
28
|
+
# r.author
|
29
|
+
# end
|
30
|
+
# # => <book><title>Whatever</title><author>Whoever</author></book>
|
31
|
+
#
|
32
|
+
# Providing a final Hash argument specifies element attributes.
|
33
|
+
#
|
34
|
+
# r.element :size, :type => "integer"
|
35
|
+
# # => <size type="integer">9</size>
|
36
|
+
#
|
37
|
+
def element(name, *args, &block)
|
38
|
+
|
39
|
+
metadata = @inspector.get_metadata(current_subject, name)
|
40
|
+
attributes = args.extract_options!.merge(metadata)
|
41
|
+
|
42
|
+
subject_of_element = if args.empty?
|
43
|
+
lambda do |subject|
|
44
|
+
@inspector.get_value(current_subject, name)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
args.shift
|
48
|
+
end
|
49
|
+
|
50
|
+
raise ArgumentError, "too many arguments" unless args.empty?
|
51
|
+
|
52
|
+
representing(subject_of_element) do
|
53
|
+
|
54
|
+
resolved_attributes = resolve_attributes(attributes)
|
55
|
+
content_string = content_block = nil
|
56
|
+
|
57
|
+
unless current_subject.nil?
|
58
|
+
if block
|
59
|
+
unless block == Representative::EMPTY
|
60
|
+
content_block = Proc.new { block.call(current_subject) }
|
61
|
+
end
|
62
|
+
else
|
63
|
+
content_string = current_subject.to_s
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
generate_element(name.to_s.dasherize, resolved_attributes, content_string, &content_block)
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
# Generate a list of elements from Enumerable data.
|
74
|
+
#
|
75
|
+
# r.list_of :books, my_books do
|
76
|
+
# r.element :title
|
77
|
+
# end
|
78
|
+
# # => <books type="array">
|
79
|
+
# # <book><title>Sailing for old dogs</title></book>
|
80
|
+
# # <book><title>On the horizon</title></book>
|
81
|
+
# # <book><title>The Little Blue Book of VHS Programming</title></book>
|
82
|
+
# # </books>
|
83
|
+
#
|
84
|
+
# Like #element, the value can be explicit, but is more commonly extracted
|
85
|
+
# by name from the current #subject.
|
86
|
+
#
|
87
|
+
def list_of(name, *args, &block)
|
88
|
+
|
89
|
+
options = args.extract_options!
|
90
|
+
list_subject = args.empty? ? name : args.shift
|
91
|
+
raise ArgumentError, "too many arguments" unless args.empty?
|
92
|
+
|
93
|
+
list_attributes = options[:list_attributes] || {}
|
94
|
+
item_name = options[:item_name] || name.to_s.singularize
|
95
|
+
item_attributes = options[:item_attributes] || {}
|
96
|
+
|
97
|
+
items = resolve_value(list_subject)
|
98
|
+
element(name, items, list_attributes.merge(:type => proc{"array"})) do
|
99
|
+
items.each do |item|
|
100
|
+
element(item_name, item, item_attributes, &block)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
# Return a magic value that, when passed to #element as a block, forces
|
107
|
+
# generation of an empty element.
|
108
|
+
#
|
109
|
+
# r.element(:link, :rel => "me", :href => "http://dogbiscuit.org", &r.empty)
|
110
|
+
# # => <link rel="parent" href="http://dogbiscuit.org"/>
|
111
|
+
#
|
112
|
+
def empty
|
113
|
+
Representative::EMPTY
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
data/lib/representative/json.rb
CHANGED
@@ -5,15 +5,20 @@ require "json"
|
|
5
5
|
module Representative
|
6
6
|
|
7
7
|
class Json < Base
|
8
|
+
|
9
|
+
DEFAULT_ATTRIBUTE_PREFIX = "@".freeze
|
8
10
|
|
9
11
|
def initialize(subject = nil, options = {})
|
10
12
|
super(subject, options)
|
11
13
|
@buffer = ""
|
12
14
|
@indent_level = 0
|
15
|
+
@attribute_prefix = options[:attribute_prefix] || DEFAULT_ATTRIBUTE_PREFIX
|
13
16
|
now_at :beginning_of_buffer
|
14
17
|
yield self if block_given?
|
15
18
|
end
|
16
19
|
|
20
|
+
attr_reader :attribute_prefix
|
21
|
+
|
17
22
|
def element(name, *args, &block)
|
18
23
|
|
19
24
|
metadata = @inspector.get_metadata(current_subject, name)
|
@@ -30,10 +35,14 @@ module Representative
|
|
30
35
|
raise ArgumentError, "too many arguments" unless args.empty?
|
31
36
|
|
32
37
|
label(name)
|
33
|
-
value(subject_of_element, &block)
|
38
|
+
value(subject_of_element, attributes, &block)
|
34
39
|
|
35
40
|
end
|
36
41
|
|
42
|
+
def attribute(name, value_generator = name)
|
43
|
+
element(attribute_prefix + name.to_s, value_generator)
|
44
|
+
end
|
45
|
+
|
37
46
|
def list_of(name, *args, &block)
|
38
47
|
list_subject = args.empty? ? name : args.shift
|
39
48
|
items = resolve_value(list_subject)
|
@@ -46,10 +55,13 @@ module Representative
|
|
46
55
|
end
|
47
56
|
end
|
48
57
|
|
49
|
-
def value(subject)
|
58
|
+
def value(subject, attributes = {})
|
50
59
|
representing(subject) do
|
51
60
|
if block_given? && !current_subject.nil?
|
52
61
|
inside "{", "}" do
|
62
|
+
attributes.each do |name, value_generator|
|
63
|
+
attribute(name, value_generator)
|
64
|
+
end
|
53
65
|
yield current_subject
|
54
66
|
end
|
55
67
|
else
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "active_support/core_ext"
|
2
|
+
require "nokogiri"
|
3
|
+
require "representative/abstract_xml"
|
4
|
+
require "representative/empty"
|
5
|
+
|
6
|
+
module Representative
|
7
|
+
|
8
|
+
# Easily generate XML while traversing an object-graph.
|
9
|
+
#
|
10
|
+
class Nokogiri < AbstractXml
|
11
|
+
|
12
|
+
def initialize(subject = nil, options = {})
|
13
|
+
super(subject, options)
|
14
|
+
@doc = ::Nokogiri::XML::Document.new
|
15
|
+
@current_element = @doc
|
16
|
+
yield self if block_given?
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :doc, :current_element
|
20
|
+
|
21
|
+
# Serialize the generated document as XML
|
22
|
+
#
|
23
|
+
def to_xml(*args)
|
24
|
+
doc.to_xml(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Generate a comment
|
28
|
+
#
|
29
|
+
def comment(text)
|
30
|
+
comment_node = ::Nokogiri::XML::Comment.new(doc, " #{text} ")
|
31
|
+
current_element.add_child(comment_node)
|
32
|
+
end
|
33
|
+
|
34
|
+
def attribute(name, value_generator = name)
|
35
|
+
attribute_name = name.to_s.dasherize
|
36
|
+
value = resolve_value(value_generator)
|
37
|
+
unless value.nil?
|
38
|
+
current_element[attribute_name] = value.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def generate_element(name, resolved_attributes, content_string)
|
45
|
+
tag_args = [content_string, resolved_attributes].compact
|
46
|
+
new_element = doc.create_element(name, *tag_args)
|
47
|
+
current_element.add_child(new_element)
|
48
|
+
if block_given?
|
49
|
+
old_element = @current_element
|
50
|
+
begin
|
51
|
+
@current_element = new_element
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
@current_element = old_element
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "tilt"
|
2
|
+
|
3
|
+
module Representative
|
4
|
+
module Tilt
|
5
|
+
|
6
|
+
class NokogiriTemplate < ::Tilt::Template
|
7
|
+
|
8
|
+
def initialize_engine
|
9
|
+
return if defined?(Representative::Nokogiri)
|
10
|
+
require_template_library 'representative/nokogiri'
|
11
|
+
end
|
12
|
+
|
13
|
+
def prepare
|
14
|
+
end
|
15
|
+
|
16
|
+
def evaluate(scope, locals, &block)
|
17
|
+
r = Representative::Nokogiri.new
|
18
|
+
locals[:r] = r
|
19
|
+
super(scope, locals, &block)
|
20
|
+
r.to_xml
|
21
|
+
end
|
22
|
+
|
23
|
+
def precompiled_template(locals)
|
24
|
+
data.to_str
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class JsonTemplate < ::Tilt::Template
|
30
|
+
|
31
|
+
def initialize_engine
|
32
|
+
return if defined?(Representative::Json)
|
33
|
+
require_template_library 'representative/json'
|
34
|
+
end
|
35
|
+
|
36
|
+
def prepare
|
37
|
+
end
|
38
|
+
|
39
|
+
def evaluate(scope, locals, &block)
|
40
|
+
r = Representative::Json.new
|
41
|
+
locals[:r] = r
|
42
|
+
super(scope, locals, &block)
|
43
|
+
r.to_json
|
44
|
+
end
|
45
|
+
|
46
|
+
def precompiled_template(locals)
|
47
|
+
data.to_str
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Tilt.register 'xml.rep', Representative::Tilt::NokogiriTemplate
|
56
|
+
Tilt.register 'json.rep', Representative::Tilt::JsonTemplate
|
data/lib/representative/xml.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
require "active_support/core_ext"
|
2
|
-
require "active_support/core_ext"
|
3
2
|
require "builder"
|
4
|
-
require "representative/
|
3
|
+
require "representative/abstract_xml"
|
5
4
|
require "representative/empty"
|
6
5
|
|
7
6
|
module Representative
|
8
7
|
|
9
8
|
# Easily generate XML while traversing an object-graph.
|
10
9
|
#
|
11
|
-
class Xml <
|
10
|
+
class Xml < AbstractXml
|
12
11
|
|
13
12
|
# Create an XML-generating Representative. The first argument should be an instance of
|
14
13
|
# Builder::XmlMarkup (or something that implements it's interface). The second argument
|
@@ -20,124 +19,19 @@ module Representative
|
|
20
19
|
yield self if block_given?
|
21
20
|
end
|
22
21
|
|
23
|
-
# Generate an element.
|
24
|
-
#
|
25
|
-
# With two arguments, it generates an element with the specified text content.
|
26
|
-
#
|
27
|
-
# r.element :size, 42
|
28
|
-
# # => <size>42</size>
|
29
|
-
#
|
30
|
-
# More commonly, though, the second argument is omitted, in which case the
|
31
|
-
# element content is assumed to be the named property of the current #subject.
|
32
|
-
#
|
33
|
-
# r.representing my_shoe do
|
34
|
-
# r.element :size
|
35
|
-
# end
|
36
|
-
# # => <size>9</size>
|
37
|
-
#
|
38
|
-
# If a block is attached, nested elements can be generated. The element "value"
|
39
|
-
# (whether explicitly provided, or derived from the current subject) becomes the
|
40
|
-
# subject during evaluation of the block.
|
41
|
-
#
|
42
|
-
# r.element :book, book do
|
43
|
-
# r.title
|
44
|
-
# r.author
|
45
|
-
# end
|
46
|
-
# # => <book><title>Whatever</title><author>Whoever</author></book>
|
47
|
-
#
|
48
|
-
# Providing a final Hash argument specifies element attributes.
|
49
|
-
#
|
50
|
-
# r.element :size, :type => "integer"
|
51
|
-
# # => <size type="integer">9</size>
|
52
|
-
#
|
53
|
-
def element(name, *args, &block)
|
54
|
-
|
55
|
-
metadata = @inspector.get_metadata(current_subject, name)
|
56
|
-
attributes = args.extract_options!.merge(metadata)
|
57
|
-
|
58
|
-
subject_of_element = if args.empty?
|
59
|
-
lambda do |subject|
|
60
|
-
@inspector.get_value(current_subject, name)
|
61
|
-
end
|
62
|
-
else
|
63
|
-
args.shift
|
64
|
-
end
|
65
|
-
|
66
|
-
raise ArgumentError, "too many arguments" unless args.empty?
|
67
|
-
|
68
|
-
representing(subject_of_element) do
|
69
|
-
|
70
|
-
content_string = content_block = nil
|
71
|
-
|
72
|
-
unless current_subject.nil?
|
73
|
-
if block
|
74
|
-
unless block == Representative::EMPTY
|
75
|
-
content_block = Proc.new do
|
76
|
-
block.call(current_subject)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
else
|
80
|
-
content_string = current_subject.to_s
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
resolved_attributes = resolve_attributes(attributes)
|
85
|
-
tag_args = [content_string, resolved_attributes].compact
|
86
|
-
|
87
|
-
@xml.tag!(name.to_s.dasherize, *tag_args, &content_block)
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
end
|
92
|
-
|
93
|
-
# Generate a list of elements from Enumerable data.
|
94
|
-
#
|
95
|
-
# r.list_of :books, my_books do
|
96
|
-
# r.element :title
|
97
|
-
# end
|
98
|
-
# # => <books type="array">
|
99
|
-
# # <book><title>Sailing for old dogs</title></book>
|
100
|
-
# # <book><title>On the horizon</title></book>
|
101
|
-
# # <book><title>The Little Blue Book of VHS Programming</title></book>
|
102
|
-
# # </books>
|
103
|
-
#
|
104
|
-
# Like #element, the value can be explicit, but is more commonly extracted
|
105
|
-
# by name from the current #subject.
|
106
|
-
#
|
107
|
-
def list_of(name, *args, &block)
|
108
|
-
|
109
|
-
options = args.extract_options!
|
110
|
-
list_subject = args.empty? ? name : args.shift
|
111
|
-
raise ArgumentError, "too many arguments" unless args.empty?
|
112
|
-
|
113
|
-
list_attributes = options[:list_attributes] || {}
|
114
|
-
item_name = options[:item_name] || name.to_s.singularize
|
115
|
-
item_attributes = options[:item_attributes] || {}
|
116
|
-
|
117
|
-
items = resolve_value(list_subject)
|
118
|
-
element(name, items, list_attributes.merge(:type => proc{"array"})) do
|
119
|
-
items.each do |item|
|
120
|
-
element(item_name, item, item_attributes, &block)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
end
|
125
|
-
|
126
|
-
# Return a magic value that, when passed to #element as a block, forces
|
127
|
-
# generation of an empty element.
|
128
|
-
#
|
129
|
-
# r.element(:link, :rel => "me", :href => "http://dogbiscuit.org", &r.empty)
|
130
|
-
# # => <link rel="parent" href="http://dogbiscuit.org"/>
|
131
|
-
#
|
132
|
-
def empty
|
133
|
-
Representative::EMPTY
|
134
|
-
end
|
135
|
-
|
136
22
|
# Generate a comment
|
23
|
+
#
|
137
24
|
def comment(text)
|
138
25
|
@xml.comment!(text)
|
139
26
|
end
|
140
27
|
|
28
|
+
protected
|
29
|
+
|
30
|
+
def generate_element(name, resolved_attributes, content_string, &content_block)
|
31
|
+
tag_args = [content_string, resolved_attributes].compact
|
32
|
+
@xml.tag!(name, *tag_args, &content_block)
|
33
|
+
end
|
34
|
+
|
141
35
|
end
|
142
36
|
|
143
37
|
end
|