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 CHANGED
@@ -1,6 +1,8 @@
1
- require "rubygems"
2
1
  require "rake"
3
- require "rake/clean"
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
+
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Representative
2
- VERSION = "0.2.5".freeze
2
+ VERSION = "0.3.0".freeze
3
3
  end
@@ -1,14 +1,13 @@
1
1
  require "active_support/core_ext"
2
- require "active_support/core_ext"
3
2
  require "builder"
4
- require "representative/base"
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 < Base
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