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