representative 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Mike Williams
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,97 @@
1
+ Representative
2
+ ==============
3
+
4
+ "Representative" makes it easier to create XML representations of your Ruby objects.
5
+ It works best when you want the XML to roughly follow the object structure,
6
+ but still have complete control of the result.
7
+
8
+ Example
9
+ -------
10
+
11
+ Given a Ruby data-structure:
12
+
13
+ books = [
14
+ OpenStruct.new(
15
+ :title => "Sailing for old dogs",
16
+ :authors => ["Jim Watson"],
17
+ :published => OpenStruct.new(
18
+ :by => "Credulous Print",
19
+ :year => 1994
20
+ )
21
+ ),
22
+ OpenStruct.new(
23
+ :title => "On the horizon",
24
+ :authors => ["Zoe Primpton", "Stan Ford"],
25
+ :published => OpenStruct.new(
26
+ :by => "McGraw-Hill",
27
+ :year => 2005
28
+ )
29
+ ),
30
+ OpenStruct.new(
31
+ :title => "The Little Blue Book of VHS Programming",
32
+ :authors => ["Henry Nelson"],
33
+ :rating => "****"
34
+ )
35
+ ]
36
+
37
+ Representative::Xml can be used to generate XML, in a declarative style:
38
+
39
+ xml = Builder::XmlMarkup.new(:indent => 2)
40
+ representative = Representative::Xml.new(xml)
41
+
42
+ representative.list_of!(:books, books) do |_book|
43
+ _book.title
44
+ _book.list_of!(:authors)
45
+ _book.published do |_published|
46
+ _published.by
47
+ _published.year
48
+ end
49
+ end
50
+
51
+ puts xml.target!
52
+
53
+ The resulting XML looks like this:
54
+
55
+ <books type="array">
56
+ <book>
57
+ <title>Sailing for old dogs</title>
58
+ <authors type="array">
59
+ <author>Jim Watson</author>
60
+ </authors>
61
+ <published>
62
+ <by>Credulous Print</by>
63
+ <year>1994</year>
64
+ </published>
65
+ </book>
66
+ <book>
67
+ <title>On the horizon</title>
68
+ <authors type="array">
69
+ <author>Zoe Primpton</author>
70
+ <author>Stan Ford</author>
71
+ </authors>
72
+ <published>
73
+ <by>McGraw-Hill</by>
74
+ <year>2005</year>
75
+ </published>
76
+ </book>
77
+ <book>
78
+ <title>The Little Blue Book of VHS Programming</title>
79
+ <authors type="array">
80
+ <author>Henry Nelson</author>
81
+ </authors>
82
+ <published/>
83
+ </book>
84
+ </books>
85
+
86
+ Notice that:
87
+
88
+ - Representative generates elements for each object-attribute you name (and not the ones you don't).
89
+ - The structure of the XML mirrors the structure described by the nested Ruby blocks.
90
+ - Using **list_of!** for a collection attribute generates an "array" element, which plays nicely
91
+ with most Ruby XML-to-hash converters.
92
+ - Where a named object-attribute is nil, you get an empty element.
93
+
94
+ Copyright
95
+ ---------
96
+
97
+ Copyright (c) 2009 Mike Williams. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ require "rubygems"
2
+ require "rake"
3
+ require "rake/clean"
4
+
5
+ task :default => :spec
6
+
7
+ def with_gem(gem_name, lib = gem_name)
8
+ begin
9
+ require(lib)
10
+ rescue LoadError
11
+ $stderr.puts "WARNING: can't load #{lib}. Install it with: sudo gem install #{gem_name}"
12
+ return false
13
+ end
14
+ yield
15
+ end
16
+
17
+ with_gem "jeweler" do
18
+
19
+ Jeweler::Tasks.new do |gem|
20
+ gem.name = "representative"
21
+ gem.summary = "Builds XML representations of your Ruby objects"
22
+ gem.email = "mdub@dogbiscuit.org"
23
+ gem.homepage = "http://github.com/mdub/representative"
24
+ gem.authors = ["Mike Williams"]
25
+ gem.add_dependency("activesupport", ">= 2.2.2")
26
+ end
27
+
28
+ Jeweler::GemcutterTasks.new
29
+
30
+ end
31
+
32
+ require "spec/rake/spectask"
33
+
34
+ Spec::Rake::SpecTask.new(:spec) do |spec|
35
+ spec.libs << 'lib' << 'spec'
36
+ spec.spec_files = FileList['spec/**/*_spec.rb']
37
+ end
38
+
39
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
40
+ spec.libs << 'lib' << 'spec'
41
+ spec.spec_files = FileList['spec/**/*_spec.rb']
42
+ spec.rcov = true
43
+ end
44
+
45
+ # with_gem "yard" do
46
+ #
47
+ # YARD::Rake::YardocTask.new(:yardoc) do |t|
48
+ # t.files = FileList['lib/**/*.rb']
49
+ # end
50
+ # CLEAN << "doc"
51
+ #
52
+ # end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,43 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ require "rubygems"
4
+ require "representative/xml"
5
+ require "ostruct"
6
+
7
+ books = [
8
+ OpenStruct.new(
9
+ :title => "Sailing for old dogs",
10
+ :authors => ["Jim Watson"],
11
+ :published => OpenStruct.new(
12
+ :by => "Credulous Print",
13
+ :year => 1994
14
+ )
15
+ ),
16
+ OpenStruct.new(
17
+ :title => "On the horizon",
18
+ :authors => ["Zoe Primpton", "Stan Ford"],
19
+ :published => OpenStruct.new(
20
+ :by => "McGraw-Hill",
21
+ :year => 2005
22
+ )
23
+ ),
24
+ OpenStruct.new(
25
+ :title => "The Little Blue Book of VHS Programming",
26
+ :authors => ["Henry Nelson"],
27
+ :rating => "****"
28
+ )
29
+ ]
30
+
31
+ xml = Builder::XmlMarkup.new(:indent => 2)
32
+ representative = Representative::Xml.new(xml)
33
+
34
+ representative.list_of!(:books, books) do |_book|
35
+ _book.title
36
+ _book.list_of!(:authors)
37
+ _book.published do |_published|
38
+ _published.by
39
+ _published.year
40
+ end
41
+ end
42
+
43
+ puts xml.target!
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "representative"
@@ -0,0 +1,99 @@
1
+ require "builder"
2
+ require "active_support"
3
+
4
+ module Representative
5
+
6
+ class Xml < BlankSlate
7
+
8
+ def initialize(xml_builder, subject = nil)
9
+ @xml = xml_builder
10
+ @subject = subject
11
+ yield self if block_given?
12
+ end
13
+
14
+ def method_missing(name, *args, &block)
15
+ if name.to_s =~ /!$/
16
+ super
17
+ else
18
+ property!(name, *args, &block)
19
+ end
20
+ end
21
+
22
+ def subject!
23
+ @subject
24
+ end
25
+
26
+ def property!(property_name, *args, &block)
27
+
28
+ attributes = args.extract_options!
29
+ value_generator = args.empty? ? property_name : args.shift
30
+ raise ArgumentError, "too many arguments" unless args.empty?
31
+
32
+ element_name = property_name.to_s.dasherize
33
+
34
+ value = resolve(value_generator)
35
+ resolved_attributes = resolve_attributes(attributes, value)
36
+
37
+ element!(element_name, value, resolved_attributes, &block)
38
+
39
+ end
40
+
41
+ def element!(element_name, value, options, &block)
42
+ text = content_generator = nil
43
+ if block && value
44
+ content_generator = Proc.new do
45
+ block.call(Representative::Xml.new(@xml, value))
46
+ end
47
+ else
48
+ text = value
49
+ end
50
+ tag_args = [text, options].compact
51
+ @xml.tag!(element_name, *tag_args, &content_generator)
52
+ end
53
+
54
+ def list_of!(property_name, *args, &block)
55
+
56
+ options = args.extract_options!
57
+ value_generator = args.empty? ? property_name : args.shift
58
+ raise ArgumentError, "too many arguments" unless args.empty?
59
+
60
+ list_name = property_name.to_s.dasherize
61
+ list_attributes = options[:list_attributes] || {}
62
+ item_name = options[:item_name] || list_name.singularize
63
+ item_attributes = options[:item_attributes] || {}
64
+
65
+ items = resolve(value_generator)
66
+ resolved_list_attributes = resolve_attributes(list_attributes, items)
67
+
68
+ @xml.tag!(list_name, resolved_list_attributes.merge(:type => "array")) do
69
+ items.each do |item|
70
+ resolved_item_attributes = resolve_attributes(item_attributes, item)
71
+ element!(item_name, item, resolved_item_attributes, &block)
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ private
78
+
79
+ def resolve(value_generator, subject = @subject)
80
+ if value_generator.respond_to?(:to_proc)
81
+ value_generator.to_proc.call(subject) if subject
82
+ else
83
+ value_generator
84
+ end
85
+ end
86
+
87
+ def resolve_attributes(attributes, subject)
88
+ if attributes
89
+ attributes.inject({}) do |resolved, (k,v)|
90
+ resolved_value = resolve(v, subject)
91
+ resolved[k.to_s.dasherize] = resolved_value unless resolved_value.nil?
92
+ resolved
93
+ end
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ end
@@ -0,0 +1 @@
1
+ require "representative/xml"
@@ -0,0 +1,56 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{representative}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Mike Williams"]
12
+ s.date = %q{2009-10-14}
13
+ s.email = %q{mdub@dogbiscuit.org}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.markdown"
17
+ ]
18
+ s.files = [
19
+ ".document",
20
+ ".gitignore",
21
+ "LICENSE",
22
+ "README.markdown",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "examples/xml_demo.rb",
26
+ "init.rb",
27
+ "lib/representative.rb",
28
+ "lib/representative/xml.rb",
29
+ "representative.gemspec",
30
+ "spec/representative/xml_spec.rb",
31
+ "spec/spec_helper.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/mdub/representative}
34
+ s.rdoc_options = ["--charset=UTF-8"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.3.5}
37
+ s.summary = %q{Builds XML representations of your Ruby objects}
38
+ s.test_files = [
39
+ "spec/representative/xml_spec.rb",
40
+ "spec/spec_helper.rb",
41
+ "examples/xml_demo.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.2.2"])
50
+ else
51
+ s.add_dependency(%q<activesupport>, [">= 2.2.2"])
52
+ end
53
+ else
54
+ s.add_dependency(%q<activesupport>, [">= 2.2.2"])
55
+ end
56
+ end
@@ -0,0 +1,210 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ require "representative/xml"
4
+
5
+ require "rexml/document"
6
+
7
+ describe Representative::Xml do
8
+
9
+ before do
10
+ @xml = Builder::XmlMarkup.new
11
+ end
12
+
13
+ def represent
14
+ @xml_representative
15
+ end
16
+
17
+ def resulting_xml
18
+ @xml.target!
19
+ end
20
+
21
+ describe "for some 'subject'" do
22
+
23
+ before do
24
+ @subject = OpenStruct.new(:name => "Fred", :width => 200)
25
+ @xml_representative = Representative::Xml.new(@xml, @subject)
26
+ end
27
+
28
+ describe "calling a method" do
29
+
30
+ it "generates an element with content extracted from the subject" do
31
+ represent.name
32
+ resulting_xml.should == %(<name>Fred</name>)
33
+ end
34
+
35
+ it "dasherizes the method name" do
36
+ @subject.full_name = "Fredrick"
37
+ represent.full_name
38
+ resulting_xml.should == %(<full-name>Fredrick</full-name>)
39
+ end
40
+
41
+ describe "with an attribute" do
42
+
43
+ it "generates attributes on the element" do
44
+ represent.name(:lang => "fr")
45
+ resulting_xml.should == %(<name lang="fr">Fred</name>)
46
+ end
47
+
48
+ it "dasherizes the attribute name" do
49
+ represent.name(:sourced_from => "phonebook")
50
+ resulting_xml.should == %(<name sourced-from="phonebook">Fred</name>)
51
+ end
52
+
53
+ describe "whose value supports #to_proc" do
54
+
55
+ it "calls the Proc on the subject to generate a value" do
56
+ represent.name(:rev => :reverse)
57
+ resulting_xml.should == %(<name rev="derF">Fred</name>)
58
+ end
59
+
60
+ end
61
+
62
+ describe "with value nil" do
63
+
64
+ it "omits the attribute" do
65
+ represent.name(:lang => nil)
66
+ resulting_xml.should == %(<name>Fred</name>)
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+ describe "with a non-Hash argument" do
74
+
75
+ it "generates an element with explicitly provided content" do
76
+ represent.name("Bloggs")
77
+ resulting_xml.should == %(<name>Bloggs</name>)
78
+ end
79
+
80
+ describe "AND a Hash argument" do
81
+
82
+ it "generates attributes on the element" do
83
+ represent.name("Bloggs", :lang => "fr")
84
+ resulting_xml.should == %(<name lang="fr">Bloggs</name>)
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+
91
+ describe "with an argument that supports #to_proc" do
92
+
93
+ it "calls the Proc on the subject to generate a value" do
94
+ represent.name(:width)
95
+ resulting_xml.should == %(<name>200</name>)
96
+ end
97
+
98
+ end
99
+
100
+ describe "with a nil argument" do
101
+
102
+ it "builds an empty element" do
103
+ represent.name(nil)
104
+ resulting_xml.should == %(<name/>)
105
+ end
106
+
107
+ describe "and a Hash entry value that supports #to_proc" do
108
+
109
+ it "omits the attribute" do
110
+ represent.name(nil, :size => :size)
111
+ resulting_xml.should == %(<name/>)
112
+ end
113
+
114
+ end
115
+
116
+ describe "and a block" do
117
+
118
+ it "doesn't call the block" do
119
+ represent.name(nil) do |name|
120
+ name.foo
121
+ end
122
+ resulting_xml.should == %(<name/>)
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+
129
+ describe "with a block" do
130
+
131
+ it "generates nested elements" do
132
+ @subject.vehicle = OpenStruct.new(:year => "1959", :make => "Chevrolet")
133
+ represent.vehicle do |vehicle|
134
+ vehicle.year
135
+ vehicle.make
136
+ end
137
+ resulting_xml.should == %(<vehicle><year>1959</year><make>Chevrolet</make></vehicle>)
138
+ end
139
+
140
+ end
141
+
142
+ end
143
+
144
+ describe "#list_of!" do
145
+
146
+ before do
147
+ @subject.nick_names = ["Freddie", "Knucklenose"]
148
+ end
149
+
150
+ it "generates an array element" do
151
+ represent.list_of!(:nick_names)
152
+ resulting_xml.should == %(<nick-names type="array"><nick-name>Freddie</nick-name><nick-name>Knucklenose</nick-name></nick-names>)
153
+ end
154
+
155
+ describe "with :list_attributes" do
156
+
157
+ it "attaches attributes to the array element" do
158
+ represent.list_of!(:nick_names, :list_attributes => {:color => "blue", :size => :size})
159
+ array_element_attributes = REXML::Document.new(resulting_xml).root.attributes
160
+ array_element_attributes["type"].should == "array"
161
+ array_element_attributes["color"].should == "blue"
162
+ array_element_attributes["size"].should == "2"
163
+ array_element_attributes.size.should == 3
164
+ end
165
+
166
+ end
167
+
168
+ describe "with :item_attributes" do
169
+
170
+ it "attaches attributes to each item element" do
171
+ represent.list_of!(:nick_names, :item_attributes => {:length => :size})
172
+ resulting_xml.should == %(<nick-names type="array"><nick-name length="7">Freddie</nick-name><nick-name length="11">Knucklenose</nick-name></nick-names>)
173
+ end
174
+
175
+ end
176
+
177
+ describe "with an explicit :item_name" do
178
+ it "uses the name provided" do
179
+ represent.list_of!(:nick_names, :item_name => :nick)
180
+ resulting_xml.should == %(<nick-names type="array"><nick>Freddie</nick><nick>Knucklenose</nick></nick-names>)
181
+ end
182
+ end
183
+
184
+ describe "with a block" do
185
+
186
+ it "generates a nested element for each list element" do
187
+ represent.list_of!(:nick_names) do |nick_name|
188
+ nick_name.length
189
+ end
190
+ resulting_xml.should == %(<nick-names type="array"><nick-name><length>7</length></nick-name><nick-name><length>11</length></nick-name></nick-names>)
191
+ end
192
+
193
+ end
194
+
195
+ describe "with :item_attributes AND block" do
196
+
197
+ it "generates attributes and nested elements" do
198
+ represent.list_of!(:nick_names, :item_attributes => {:length => :size}) do |nick_name|
199
+ nick_name.reverse
200
+ end
201
+ resulting_xml.should == %(<nick-names type="array"><nick-name length="7"><reverse>eidderF</reverse></nick-name><nick-name length="11"><reverse>esonelkcunK</reverse></nick-name></nick-names>)
202
+ end
203
+
204
+ end
205
+
206
+ end
207
+
208
+ end
209
+
210
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+
6
+ require "rubygems"
7
+
8
+ Spec::Runner.configure do |config|
9
+
10
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: representative
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mike Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-14 00:00:00 +11:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.2.2
24
+ version:
25
+ description:
26
+ email: mdub@dogbiscuit.org
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.markdown
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.markdown
39
+ - Rakefile
40
+ - VERSION
41
+ - examples/xml_demo.rb
42
+ - init.rb
43
+ - lib/representative.rb
44
+ - lib/representative/xml.rb
45
+ - representative.gemspec
46
+ - spec/representative/xml_spec.rb
47
+ - spec/spec_helper.rb
48
+ has_rdoc: true
49
+ homepage: http://github.com/mdub/representative
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options:
54
+ - --charset=UTF-8
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ version:
69
+ requirements: []
70
+
71
+ rubyforge_project:
72
+ rubygems_version: 1.3.5
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: Builds XML representations of your Ruby objects
76
+ test_files:
77
+ - spec/representative/xml_spec.rb
78
+ - spec/spec_helper.rb
79
+ - examples/xml_demo.rb