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 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