xommelier 0.0.1
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/.gitignore +4 -0
- data/.rspec +1 -0
- data/Gemfile +23 -0
- data/Guardfile +10 -0
- data/Rakefile +1 -0
- data/examples/atom.rb +27 -0
- data/examples/xml.rb +17 -0
- data/lib/xommelier.rb +9 -0
- data/lib/xommelier/atom.rb +38 -0
- data/lib/xommelier/atom/category.rb +14 -0
- data/lib/xommelier/atom/entry.rb +28 -0
- data/lib/xommelier/atom/feed.rb +29 -0
- data/lib/xommelier/atom/generator.rb +12 -0
- data/lib/xommelier/atom/link.rb +17 -0
- data/lib/xommelier/atom/person.rb +14 -0
- data/lib/xommelier/atom/source.rb +14 -0
- data/lib/xommelier/collection.rb +46 -0
- data/lib/xommelier/core_ext.rb +9 -0
- data/lib/xommelier/core_ext/boolean.rb +24 -0
- data/lib/xommelier/core_ext/date.rb +11 -0
- data/lib/xommelier/core_ext/float.rb +14 -0
- data/lib/xommelier/core_ext/integer.rb +14 -0
- data/lib/xommelier/core_ext/string.rb +9 -0
- data/lib/xommelier/core_ext/time.rb +11 -0
- data/lib/xommelier/core_ext/uri.rb +12 -0
- data/lib/xommelier/version.rb +3 -0
- data/lib/xommelier/xml.rb +19 -0
- data/lib/xommelier/xml/attribute.rb +8 -0
- data/lib/xommelier/xml/class_methods.rb +19 -0
- data/lib/xommelier/xml/element.rb +142 -0
- data/lib/xommelier/xml/element/dsl.rb +51 -0
- data/lib/xommelier/xml/namespace.rb +68 -0
- data/spec/lib/xommelier/xml/element_spec.rb +91 -0
- data/spec/lib/xommelier/xml_spec.rb +11 -0
- data/spec/lib/xommelier_spec.rb +5 -0
- data/spec/namespaced_module.rb +32 -0
- data/spec/spec_helper.rb +11 -0
- data/xommelier.gemspec +24 -0
- metadata +124 -0
data/.gitignore
ADDED
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
source 'http://rubygems.org'
|
|
2
|
+
|
|
3
|
+
# Specify your gem's dependencies in xommelier.gemspec
|
|
4
|
+
gemspec
|
|
5
|
+
|
|
6
|
+
group :development, :test do
|
|
7
|
+
gem 'rb-inotify'
|
|
8
|
+
gem 'libnotify'
|
|
9
|
+
gem 'rspec'
|
|
10
|
+
gem 'guard'
|
|
11
|
+
gem 'guard-rspec'
|
|
12
|
+
gem 'guard-bundler'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
group :development do
|
|
16
|
+
gem 'ripl'
|
|
17
|
+
gem 'ripl-auto_indent', require: 'ripl/auto_indent'
|
|
18
|
+
gem 'ripl-color_error', require: 'ripl/color_error'
|
|
19
|
+
gem 'ripl-color_result', require: 'ripl/color_result'
|
|
20
|
+
gem 'ripl-multi_line', require: 'ripl/multi_line'
|
|
21
|
+
gem 'ripl-rails', require: 'ripl/rails'
|
|
22
|
+
gem 'ripl-rocket', require: 'ripl/rocket'
|
|
23
|
+
end
|
data/Guardfile
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/examples/atom.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'xommelier/atom'
|
|
2
|
+
require 'open-uri'
|
|
3
|
+
|
|
4
|
+
# Reading a feed
|
|
5
|
+
feed = Xommelier::Atom::Feed.parse(open('http://example.com/blog.atom'))
|
|
6
|
+
puts feed.id, feed.title, feed.updated
|
|
7
|
+
|
|
8
|
+
feed.each(:entry) do |entry|
|
|
9
|
+
puts feed.id, feed.title, feed.published, feed.updated
|
|
10
|
+
puts feed.content || feed.summary
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Building a feed
|
|
14
|
+
feed = Xommelier::Atom::Feed.new
|
|
15
|
+
feed.id = 'http://example.com/blog'
|
|
16
|
+
feed.title = 'Example.com blog'
|
|
17
|
+
|
|
18
|
+
5.times do |i|
|
|
19
|
+
entry = Xommelier::Atom::Entry.new
|
|
20
|
+
entry.id = "http://example.com/blog/#{i}"
|
|
21
|
+
entry.title = "Example.com blog entry #{i}"
|
|
22
|
+
entry.updated = (5 - i).days.ago
|
|
23
|
+
|
|
24
|
+
feed << entry
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
puts feed.to_xml
|
data/examples/xml.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'xommelier'
|
|
2
|
+
|
|
3
|
+
class Author < Xommelier::Xml::Element
|
|
4
|
+
attribute :id
|
|
5
|
+
|
|
6
|
+
element :given_name
|
|
7
|
+
element :family_name
|
|
8
|
+
element :born_on, type: Date
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Book < Xommelier::Xml::Element
|
|
12
|
+
attribute :isdn
|
|
13
|
+
|
|
14
|
+
element :author, type: Author
|
|
15
|
+
element :title
|
|
16
|
+
element :published_on, type: Date
|
|
17
|
+
end
|
data/lib/xommelier.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'xommelier/xml'
|
|
2
|
+
|
|
3
|
+
module Xommelier
|
|
4
|
+
module Atom
|
|
5
|
+
include Xommelier::Xml
|
|
6
|
+
|
|
7
|
+
xmlns 'http://www.w3.org/2005/Atom', as: :atom
|
|
8
|
+
|
|
9
|
+
autoload :Link, 'xommelier/atom/link'
|
|
10
|
+
autoload :Person, 'xommelier/atom/person'
|
|
11
|
+
autoload :Category, 'xommelier/atom/category'
|
|
12
|
+
autoload :Generator, 'xommelier/atom/generator'
|
|
13
|
+
autoload :Source, 'xommelier/atom/source'
|
|
14
|
+
autoload :Feed, 'xommelier/atom/feed'
|
|
15
|
+
autoload :Entry, 'xommelier/atom/entry'
|
|
16
|
+
|
|
17
|
+
#namespace 'http://purl.org/syndication/thread/1.0', as: :thr do
|
|
18
|
+
#element :in_reply_to, as: 'in-reply-to' do
|
|
19
|
+
#attribute :ref
|
|
20
|
+
|
|
21
|
+
#may do
|
|
22
|
+
#attribute :href, type: Uri
|
|
23
|
+
#attribute :type, type: String
|
|
24
|
+
#attribute :source
|
|
25
|
+
#end
|
|
26
|
+
#end
|
|
27
|
+
|
|
28
|
+
#ns.atom.element :entry do
|
|
29
|
+
#element :in_reply_to, ns: ns.thr
|
|
30
|
+
#element :total, type: Integer, ns: ns.thr
|
|
31
|
+
#end
|
|
32
|
+
|
|
33
|
+
#ns.atom.element :link, values: [:replies] do
|
|
34
|
+
#ns.thr.attribute :count, type: Integer
|
|
35
|
+
#end
|
|
36
|
+
#end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'xommelier/atom'
|
|
2
|
+
|
|
3
|
+
module Xommelier
|
|
4
|
+
module Atom
|
|
5
|
+
class Entry < Xml::Element
|
|
6
|
+
root
|
|
7
|
+
|
|
8
|
+
element :id, unique: true
|
|
9
|
+
element :title
|
|
10
|
+
element :updated, type: Time
|
|
11
|
+
|
|
12
|
+
may do
|
|
13
|
+
element :content
|
|
14
|
+
element :published, type: Time
|
|
15
|
+
element :rights
|
|
16
|
+
element :source
|
|
17
|
+
element :summary
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
any do
|
|
21
|
+
element :author, type: Person
|
|
22
|
+
element :category, type: Category
|
|
23
|
+
element :contributor, type: Person
|
|
24
|
+
element :link
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'xommelier/atom'
|
|
2
|
+
|
|
3
|
+
module Xommelier
|
|
4
|
+
module Atom
|
|
5
|
+
class Feed < Xml::Element
|
|
6
|
+
root
|
|
7
|
+
|
|
8
|
+
element :id, unique: true
|
|
9
|
+
element :title
|
|
10
|
+
element :updated, type: Time
|
|
11
|
+
|
|
12
|
+
may do
|
|
13
|
+
element :generator, type: Generator
|
|
14
|
+
element :icon
|
|
15
|
+
element :logo
|
|
16
|
+
element :rights
|
|
17
|
+
element :subtitle, type: String
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
any do
|
|
21
|
+
element :author, type: Person
|
|
22
|
+
element :category, type: Category
|
|
23
|
+
element :contributor, type: Person
|
|
24
|
+
element :entry, type: Entry
|
|
25
|
+
element :link, type: Link
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'xommelier/atom'
|
|
2
|
+
|
|
3
|
+
module Xommelier
|
|
4
|
+
module Atom
|
|
5
|
+
class Link < Xml::Element
|
|
6
|
+
attribute :href, type: URI
|
|
7
|
+
|
|
8
|
+
may do
|
|
9
|
+
attribute :rel#, type: Enum(:alternate, :related, :self, :enclosure, :via)
|
|
10
|
+
attribute :type
|
|
11
|
+
attribute :hreflang
|
|
12
|
+
attribute :title
|
|
13
|
+
attribute :length
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'xommelier/atom'
|
|
2
|
+
|
|
3
|
+
module Xommelier
|
|
4
|
+
module Atom
|
|
5
|
+
class Source < Xml::Element
|
|
6
|
+
may do
|
|
7
|
+
element :generator, :icon, :id, :logo, :rights, :subtitle, :title, :updated
|
|
8
|
+
end
|
|
9
|
+
any do
|
|
10
|
+
element :author, :category, :contributor, :link
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'xommelier'
|
|
2
|
+
|
|
3
|
+
module Xommelier
|
|
4
|
+
class Collection < Hash
|
|
5
|
+
def initialize(klass)
|
|
6
|
+
@klass = klass
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def respond_to?(name)
|
|
10
|
+
key?(name) || super(name)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def find_and_append(name, options = {}, &block)
|
|
14
|
+
item = self[name]
|
|
15
|
+
item.options = options
|
|
16
|
+
if block_given?
|
|
17
|
+
item.scoped(&block)
|
|
18
|
+
end
|
|
19
|
+
item
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def method_missing(name, options = {}, &block)
|
|
23
|
+
if key?(name)
|
|
24
|
+
find_and_append(name, options, &block)
|
|
25
|
+
else
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def find_or_create(name, options = {}, &block)
|
|
31
|
+
if key?(name)
|
|
32
|
+
find_and_append(name, options, &block)
|
|
33
|
+
else
|
|
34
|
+
@klass.new(name, options, &block)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def inspect
|
|
39
|
+
values.map { |value| value.inspect }.inspect
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def pretty_inspect
|
|
43
|
+
values.map { |value| value.inspect }.pretty_inspect
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require 'xommelier'
|
|
2
|
+
|
|
3
|
+
require 'xommelier/core_ext/boolean'
|
|
4
|
+
require 'xommelier/core_ext/date'
|
|
5
|
+
require 'xommelier/core_ext/float'
|
|
6
|
+
require 'xommelier/core_ext/integer'
|
|
7
|
+
require 'xommelier/core_ext/string'
|
|
8
|
+
require 'xommelier/core_ext/time'
|
|
9
|
+
require 'xommelier/core_ext/uri'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'xommelier'
|
|
2
|
+
|
|
3
|
+
class Boolean
|
|
4
|
+
BOOLEAN_MAP = {
|
|
5
|
+
true => true, 'true' => true, 'TRUE' => true, '1' => true, '1.0' => true, 1 => true, 1.0 => true,
|
|
6
|
+
false => false, 'false' => false, 'FALSE' => false, '0' => false, '0.0' => false, 0 => false, 0.0 => false
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
def self.from_xommelier(value)
|
|
10
|
+
BOOLEAN_MAP[value]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class TrueClass
|
|
15
|
+
def to_xommelier
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class FalseClass
|
|
21
|
+
def to_xommelier
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'xommelier'
|
|
2
|
+
require 'xommelier/xml/class_methods'
|
|
3
|
+
require 'active_support/concern'
|
|
4
|
+
|
|
5
|
+
module Xommelier
|
|
6
|
+
module Xml
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
Infinity = 1.0 / 0
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
instance_variable_set :@_xmlns, nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
autoload :Namespace, 'xommelier/xml/namespace'
|
|
16
|
+
autoload :Element, 'xommelier/xml/element'
|
|
17
|
+
autoload :Attribute, 'xommelier/xml/attribute'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'xommelier/xml'
|
|
2
|
+
|
|
3
|
+
module Xommelier
|
|
4
|
+
module Xml
|
|
5
|
+
module ClassMethods
|
|
6
|
+
def ns
|
|
7
|
+
Xommelier::Xml::Namespace.registry
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Defines namespace used in formats
|
|
11
|
+
def xmlns(uri = nil, options = {}, &block)
|
|
12
|
+
if uri
|
|
13
|
+
instance_variable_set :@_xmlns, Xommelier::Xml::Namespace.new(uri, options, &block)
|
|
14
|
+
end
|
|
15
|
+
instance_variable_get :@_xmlns
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require 'xommelier/xml'
|
|
2
|
+
require 'xommelier/xml/element/dsl'
|
|
3
|
+
require 'nokogiri'
|
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
|
5
|
+
require 'active_support/core_ext/array/extract_options'
|
|
6
|
+
require 'active_support/core_ext/class/attribute'
|
|
7
|
+
|
|
8
|
+
module Xommelier
|
|
9
|
+
module Xml
|
|
10
|
+
DEFAULT_OPTIONS = {
|
|
11
|
+
type: String
|
|
12
|
+
}
|
|
13
|
+
DEFAULT_ELEMENT_OPTIONS = DEFAULT_OPTIONS.merge(
|
|
14
|
+
count: :one
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
class Element
|
|
18
|
+
extend Xommelier::Xml::Element::DSL
|
|
19
|
+
|
|
20
|
+
class_attribute :elements, :attributes
|
|
21
|
+
self.elements = {}
|
|
22
|
+
self.attributes = {}
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
def xmlns(value = nil)
|
|
26
|
+
if value
|
|
27
|
+
@xmlns = case ns
|
|
28
|
+
when Module
|
|
29
|
+
ns.xmlns
|
|
30
|
+
else
|
|
31
|
+
ns
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
@xmlns ||= find_namespace
|
|
35
|
+
end
|
|
36
|
+
alias xmlns= xmlns
|
|
37
|
+
|
|
38
|
+
def element_name(element_name = nil)
|
|
39
|
+
@element_name = element_name if element_name
|
|
40
|
+
@element_name ||= find_element_name
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def containing_module
|
|
46
|
+
@containing_module ||= ("::#{name.gsub(/::[^:]+$/, '')}").constantize
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def find_namespace
|
|
50
|
+
containing_module.xmlns
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def find_element_name
|
|
54
|
+
name.demodulize.tableize.dasherize
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def initialize(contents = {}, options = {})
|
|
59
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
|
60
|
+
|
|
61
|
+
@elements = {}
|
|
62
|
+
@attributes = {}
|
|
63
|
+
@text = nil
|
|
64
|
+
|
|
65
|
+
case contents
|
|
66
|
+
when Hash
|
|
67
|
+
contents.each do |name, value|
|
|
68
|
+
send("#{name}=", value)
|
|
69
|
+
end
|
|
70
|
+
else
|
|
71
|
+
send(:text=, contents)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def to_xml(options = {})
|
|
76
|
+
element_name = options.delete(:element_name) { self.element_name }
|
|
77
|
+
if options[:builder] # Non-root element
|
|
78
|
+
builder = options.delete(:builder)
|
|
79
|
+
attribute_values = {}
|
|
80
|
+
else # Root element
|
|
81
|
+
builder = Nokogiri::XML::Builder.new(options)
|
|
82
|
+
attribute_values = {xmlns: xmlns.to_s}
|
|
83
|
+
end
|
|
84
|
+
attributes.each do |name, value|
|
|
85
|
+
serialize_attribute(name, value, attribute_values)
|
|
86
|
+
end
|
|
87
|
+
builder.send(element_name, attribute_values) do |xml|
|
|
88
|
+
if respond_to?(:text)
|
|
89
|
+
xml.text @text
|
|
90
|
+
else
|
|
91
|
+
elements.each do |name, value|
|
|
92
|
+
serialize_element(name, value, xml)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
builder.to_xml
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
protected
|
|
100
|
+
|
|
101
|
+
def element_options(name)
|
|
102
|
+
self.class.elements[name]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def attribute_options(name)
|
|
106
|
+
self.class.attributes[name]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def element_name
|
|
110
|
+
self.class.element_name
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def xmlns
|
|
114
|
+
self.class.xmlns
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def serialize_attribute(name, value, attributes)
|
|
118
|
+
attribute_options = self.attribute_options(name)
|
|
119
|
+
attribute_class = attribute_options[:type]
|
|
120
|
+
value = attribute_class.new(value) unless value.is_a?(attribute_class)
|
|
121
|
+
attributes[name] = value.to_xommelier
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def serialize_element(name, value, xml, element_options = nil)
|
|
125
|
+
element_options ||= self.element_options(name)
|
|
126
|
+
if element_options[:count] == :many
|
|
127
|
+
single_element = element_options.merge(count: :one)
|
|
128
|
+
value.each { |item| serialize_element(name, item, xml, single_element) }
|
|
129
|
+
else
|
|
130
|
+
element_class = element_options[:type]
|
|
131
|
+
value = element_class.new(value) unless value.is_a?(element_class)
|
|
132
|
+
case value
|
|
133
|
+
when Xommelier::Xml::Element
|
|
134
|
+
value.to_xml(builder: xml, element_name: name)
|
|
135
|
+
else
|
|
136
|
+
xml.send(name) { xml.text value.to_xommelier }
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'xommelier/xml/element'
|
|
2
|
+
|
|
3
|
+
module Xommelier
|
|
4
|
+
module Xml
|
|
5
|
+
class Element
|
|
6
|
+
module DSL
|
|
7
|
+
def element(name, options = {})
|
|
8
|
+
self.elements[name] = DEFAULT_ELEMENT_OPTIONS.merge(options)
|
|
9
|
+
define_element_accessors(name)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def attribute(name, options = {})
|
|
13
|
+
self.attributes[name] = DEFAULT_OPTIONS.merge(options)
|
|
14
|
+
define_attribute_accessors(name)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def text(options = {})
|
|
18
|
+
define_text_accessors
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def define_text_accessors
|
|
24
|
+
define_method(:text) { @text ||= '' }
|
|
25
|
+
define_method(:text=) { |value| @text = value }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def define_element_accessors(name)
|
|
29
|
+
case elements[name][:count]
|
|
30
|
+
when :one, :may
|
|
31
|
+
define_method(name) { @elements[name] }
|
|
32
|
+
define_method("#{name}=") { |value| @elements[name] = value }
|
|
33
|
+
when :many
|
|
34
|
+
define_method(name) {
|
|
35
|
+
@elements[name] ||= []
|
|
36
|
+
}
|
|
37
|
+
define_method("#{name}=") do |value|
|
|
38
|
+
@elements[name] ||= []
|
|
39
|
+
@elements[name] += Array(value)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def define_attribute_accessors(name)
|
|
45
|
+
define_method(name) { @attributes[name] }
|
|
46
|
+
define_method("#{name}=") { |value| @attributes[name] = value }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'xommelier/xml'
|
|
2
|
+
require 'active_support/core_ext/array/extract_options'
|
|
3
|
+
|
|
4
|
+
module Xommelier
|
|
5
|
+
module Xml
|
|
6
|
+
class Namespace
|
|
7
|
+
class << self
|
|
8
|
+
def registry
|
|
9
|
+
@registry ||= Xommelier::Collection.new(self)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_reader :uri, :options, :as, :elements, :attributes
|
|
14
|
+
alias to_s uri
|
|
15
|
+
|
|
16
|
+
def initialize(uri, options = {}, &block)
|
|
17
|
+
@uri = uri
|
|
18
|
+
@options = {}
|
|
19
|
+
@elements = Xommelier::Collection.new(Xommelier::Xml::Element)
|
|
20
|
+
@attributes = Xommelier::Collection.new(Xommelier::Xml::Attribute)
|
|
21
|
+
@as = options.delete(:as)
|
|
22
|
+
|
|
23
|
+
Xommelier::Xml::Namespace.registry[as] = self
|
|
24
|
+
|
|
25
|
+
self.options = options
|
|
26
|
+
scoped(&block) if block_given?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def ns
|
|
30
|
+
Xommelier::Xml::Namespace.registry
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def options=(options)
|
|
34
|
+
options.delete(:elements) { [] }.each do |name|
|
|
35
|
+
element(name)
|
|
36
|
+
end
|
|
37
|
+
options.delete(:attributes) { [] }.each do |name|
|
|
38
|
+
attribute(name)
|
|
39
|
+
end
|
|
40
|
+
@options.merge!(options)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def scoped(&block)
|
|
44
|
+
instance_exec(&block)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def attribute(*names, &block)
|
|
48
|
+
options = names.extract_options!
|
|
49
|
+
names.map do |name|
|
|
50
|
+
options[:ns] ||= self
|
|
51
|
+
attributes.find_or_create(name, options, &block)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def element(*names, &block)
|
|
56
|
+
options = names.extract_options!
|
|
57
|
+
names.map do |name|
|
|
58
|
+
options[:ns] ||= self
|
|
59
|
+
elements.find_or_create(name, options, &block)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def inspect
|
|
64
|
+
%(xmlns:#{as}="#{uri}")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Xommelier::Xml::Element do
|
|
4
|
+
describe 'class' do
|
|
5
|
+
subject { NamespacedModule::EmptyRoot }
|
|
6
|
+
|
|
7
|
+
it { should respond_to(:xmlns) }
|
|
8
|
+
it { subject.xmlns.to_s.should == 'http://example.org/' }
|
|
9
|
+
|
|
10
|
+
it { should respond_to(:element_name) }
|
|
11
|
+
it { subject.element_name.should == 'empty-root' }
|
|
12
|
+
|
|
13
|
+
it { should respond_to(:elements) }
|
|
14
|
+
it { should respond_to(:attributes) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe 'DSL' do
|
|
18
|
+
subject { NamespacedModule::RootWithSimpleSubelement }
|
|
19
|
+
|
|
20
|
+
it { should respond_to(:element) }
|
|
21
|
+
it 'defines subelement' do
|
|
22
|
+
NamespacedModule::RootWithSimpleSubelement.elements.should have_key(:some)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it { should respond_to(:attribute) }
|
|
26
|
+
it 'defines attribute' do
|
|
27
|
+
NamespacedModule::RootWithAttribute.attributes.should have_key(:another)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it { should respond_to(:text) }
|
|
31
|
+
it 'defines as containing text' do
|
|
32
|
+
NamespacedModule::RootWithText.new.should respond_to(:text)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe 'instance' do
|
|
37
|
+
subject { NamespacedModule::EmptyRoot.new }
|
|
38
|
+
|
|
39
|
+
it { should respond_to(:to_xml) }
|
|
40
|
+
it { subject.to_xml.should == %(<?xml version="1.0"?>\n<empty-root xmlns="http://example.org/"/>\n) }
|
|
41
|
+
|
|
42
|
+
describe 'with text' do
|
|
43
|
+
subject { NamespacedModule::RootWithText.new('Text') }
|
|
44
|
+
|
|
45
|
+
it { should respond_to(:text) }
|
|
46
|
+
it { subject.text.should == 'Text' }
|
|
47
|
+
it { subject.to_xml.should == %(<?xml version="1.0"?>\n<root-with-text xmlns="http://example.org/">Text</root-with-text>\n) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe 'with simple subelements' do
|
|
51
|
+
subject { NamespacedModule::RootWithSimpleSubelement.new(some: 'Text') }
|
|
52
|
+
|
|
53
|
+
it { should respond_to(:some) }
|
|
54
|
+
it { subject.some.should == 'Text' }
|
|
55
|
+
it { subject.to_xml.should == %(<?xml version="1.0"?>\n<root-with-simple-subelement xmlns="http://example.org/">\n <some>Text</some>\n</root-with-simple-subelement>\n) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe 'with many simple subelements' do
|
|
59
|
+
subject do
|
|
60
|
+
NamespacedModule::RootWithManySimpleSubelements.new(foo: ['bar', 'baz'])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it { should respond_to(:foo) }
|
|
64
|
+
it { subject.foo.should == ['bar', 'baz'] }
|
|
65
|
+
it { subject.to_xml.should == %(<?xml version="1.0"?>\n<root-with-many-simple-subelements xmlns="http://example.org/">\n <foo>bar</foo>\n <foo>baz</foo>\n</root-with-many-simple-subelements>\n) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe 'with attribute' do
|
|
69
|
+
subject { NamespacedModule::RootWithAttribute.new(another: 'Difference') }
|
|
70
|
+
|
|
71
|
+
it { should respond_to(:another) }
|
|
72
|
+
it { subject.another.should == 'Difference' }
|
|
73
|
+
it { subject.to_xml.should == %(<?xml version="1.0"?>\n<root-with-attribute xmlns="http://example.org/" another="Difference"/>\n) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe 'with subelements' do
|
|
77
|
+
subject do
|
|
78
|
+
NamespacedModule::RootWithSubelement.new(
|
|
79
|
+
one: Date.new(2011, 8, 15),
|
|
80
|
+
two: '2',
|
|
81
|
+
some: 'Text',
|
|
82
|
+
another: {some: 'Text'}
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it { should respond_to(:one) }
|
|
87
|
+
it { subject.another.should == {some: 'Text'} }
|
|
88
|
+
it { subject.to_xml.should == %(<?xml version="1.0"?>\n<root-with-subelement xmlns="http://example.org/" one="2011-08-15" two="2">\n <some>Text</some>\n <another>\n <some>Text</some>\n </another>\n</root-with-subelement>\n) }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module NamespacedModule
|
|
2
|
+
include Xommelier::Xml
|
|
3
|
+
|
|
4
|
+
xmlns 'http://example.org/'
|
|
5
|
+
|
|
6
|
+
class EmptyRoot < Xommelier::Xml::Element
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class RootWithText < Xommelier::Xml::Element
|
|
10
|
+
text
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class RootWithSimpleSubelement < Xommelier::Xml::Element
|
|
14
|
+
element :some
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class RootWithAttribute < Xommelier::Xml::Element
|
|
18
|
+
attribute :one, type: Date
|
|
19
|
+
attribute :another
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class RootWithManySimpleSubelements < Xommelier::Xml::Element
|
|
23
|
+
element :foo, count: :many
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class RootWithSubelement < Xommelier::Xml::Element
|
|
27
|
+
attribute :one, type: Date
|
|
28
|
+
attribute :two
|
|
29
|
+
element :some
|
|
30
|
+
element :another, type: RootWithSimpleSubelement
|
|
31
|
+
end
|
|
32
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
root = File.expand_path('../..', __FILE__)
|
|
2
|
+
$:<< File.join(root, 'lib')
|
|
3
|
+
require 'rspec'
|
|
4
|
+
require 'xommelier'
|
|
5
|
+
require 'namespaced_module'
|
|
6
|
+
|
|
7
|
+
Dir[File.join(root, 'spec/support/**/*.rb')].each {|f| require f}
|
|
8
|
+
|
|
9
|
+
RSpec.configure do |config|
|
|
10
|
+
config.mock_with :rspec
|
|
11
|
+
end
|
data/xommelier.gemspec
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "xommelier/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "xommelier"
|
|
7
|
+
s.version = Xommelier::VERSION
|
|
8
|
+
s.authors = ["Alexander Semyonov"]
|
|
9
|
+
s.email = ["al@semyonov.us"]
|
|
10
|
+
s.homepage = "http://github.com/alsemyonov/xommelier"
|
|
11
|
+
s.summary = %q{XML-Object Mapper}
|
|
12
|
+
s.description = %q{XML-Object Mapper with many built-in XML formats supported}
|
|
13
|
+
|
|
14
|
+
s.rubyforge_project = "xommelier"
|
|
15
|
+
|
|
16
|
+
s.files = `git ls-files`.split("\n")
|
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
19
|
+
s.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
s.add_dependency 'nokogiri', '~> 1.5.0'
|
|
22
|
+
s.add_dependency 'activesupport', '~> 3.0.0'
|
|
23
|
+
s.add_dependency 'activemodel', '~> 3.0.0'
|
|
24
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: xommelier
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Alexander Semyonov
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
|
|
13
|
+
date: 2011-08-15 00:00:00 Z
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: nokogiri
|
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
18
|
+
none: false
|
|
19
|
+
requirements:
|
|
20
|
+
- - ~>
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 1.5.0
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: *id001
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: activesupport
|
|
28
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
29
|
+
none: false
|
|
30
|
+
requirements:
|
|
31
|
+
- - ~>
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 3.0.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: *id002
|
|
37
|
+
- !ruby/object:Gem::Dependency
|
|
38
|
+
name: activemodel
|
|
39
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
40
|
+
none: false
|
|
41
|
+
requirements:
|
|
42
|
+
- - ~>
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: 3.0.0
|
|
45
|
+
type: :runtime
|
|
46
|
+
prerelease: false
|
|
47
|
+
version_requirements: *id003
|
|
48
|
+
description: XML-Object Mapper with many built-in XML formats supported
|
|
49
|
+
email:
|
|
50
|
+
- al@semyonov.us
|
|
51
|
+
executables: []
|
|
52
|
+
|
|
53
|
+
extensions: []
|
|
54
|
+
|
|
55
|
+
extra_rdoc_files: []
|
|
56
|
+
|
|
57
|
+
files:
|
|
58
|
+
- .gitignore
|
|
59
|
+
- .rspec
|
|
60
|
+
- Gemfile
|
|
61
|
+
- Guardfile
|
|
62
|
+
- Rakefile
|
|
63
|
+
- examples/atom.rb
|
|
64
|
+
- examples/xml.rb
|
|
65
|
+
- lib/xommelier.rb
|
|
66
|
+
- lib/xommelier/atom.rb
|
|
67
|
+
- lib/xommelier/atom/category.rb
|
|
68
|
+
- lib/xommelier/atom/entry.rb
|
|
69
|
+
- lib/xommelier/atom/feed.rb
|
|
70
|
+
- lib/xommelier/atom/generator.rb
|
|
71
|
+
- lib/xommelier/atom/link.rb
|
|
72
|
+
- lib/xommelier/atom/person.rb
|
|
73
|
+
- lib/xommelier/atom/source.rb
|
|
74
|
+
- lib/xommelier/collection.rb
|
|
75
|
+
- lib/xommelier/core_ext.rb
|
|
76
|
+
- lib/xommelier/core_ext/boolean.rb
|
|
77
|
+
- lib/xommelier/core_ext/date.rb
|
|
78
|
+
- lib/xommelier/core_ext/float.rb
|
|
79
|
+
- lib/xommelier/core_ext/integer.rb
|
|
80
|
+
- lib/xommelier/core_ext/string.rb
|
|
81
|
+
- lib/xommelier/core_ext/time.rb
|
|
82
|
+
- lib/xommelier/core_ext/uri.rb
|
|
83
|
+
- lib/xommelier/version.rb
|
|
84
|
+
- lib/xommelier/xml.rb
|
|
85
|
+
- lib/xommelier/xml/attribute.rb
|
|
86
|
+
- lib/xommelier/xml/class_methods.rb
|
|
87
|
+
- lib/xommelier/xml/element.rb
|
|
88
|
+
- lib/xommelier/xml/element/dsl.rb
|
|
89
|
+
- lib/xommelier/xml/namespace.rb
|
|
90
|
+
- spec/lib/xommelier/xml/element_spec.rb
|
|
91
|
+
- spec/lib/xommelier/xml_spec.rb
|
|
92
|
+
- spec/lib/xommelier_spec.rb
|
|
93
|
+
- spec/namespaced_module.rb
|
|
94
|
+
- spec/spec_helper.rb
|
|
95
|
+
- xommelier.gemspec
|
|
96
|
+
homepage: http://github.com/alsemyonov/xommelier
|
|
97
|
+
licenses: []
|
|
98
|
+
|
|
99
|
+
post_install_message:
|
|
100
|
+
rdoc_options: []
|
|
101
|
+
|
|
102
|
+
require_paths:
|
|
103
|
+
- lib
|
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
105
|
+
none: false
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: "0"
|
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
|
+
none: false
|
|
112
|
+
requirements:
|
|
113
|
+
- - ">="
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: "0"
|
|
116
|
+
requirements: []
|
|
117
|
+
|
|
118
|
+
rubyforge_project: xommelier
|
|
119
|
+
rubygems_version: 1.8.5
|
|
120
|
+
signing_key:
|
|
121
|
+
specification_version: 3
|
|
122
|
+
summary: XML-Object Mapper
|
|
123
|
+
test_files: []
|
|
124
|
+
|