xommelier 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/Gemfile +6 -0
- data/README.md +19 -0
- data/Rakefile +20 -0
- data/Termfile +38 -0
- data/console +7 -0
- data/examples/atom.rb +4 -4
- data/lib/xommelier/atom/category.rb +1 -1
- data/lib/xommelier/atom/entry.rb +1 -1
- data/lib/xommelier/atom/generator.rb +1 -1
- data/lib/xommelier/atom/link.rb +1 -1
- data/lib/xommelier/atom/person.rb +1 -1
- data/lib/xommelier/atom/thread.rb +38 -0
- data/lib/xommelier/atom.rb +4 -20
- data/lib/xommelier/core_ext/{integer.rb → numeric.rb} +1 -1
- data/lib/xommelier/core_ext/symbol.rb +9 -0
- data/lib/xommelier/core_ext/uri.rb +11 -5
- data/lib/xommelier/core_ext.rb +2 -1
- data/lib/xommelier/version.rb +1 -1
- data/lib/xommelier/xml/class_methods.rb +1 -0
- data/lib/xommelier/xml/element/serialization.rb +148 -0
- data/lib/xommelier/xml/element/structure.rb +236 -0
- data/lib/xommelier/xml/element.rb +23 -93
- data/lib/xommelier/xml/namespace.rb +0 -1
- data/lib/xommelier/xml.rb +1 -2
- data/spec/fixtures/feed.atom +39 -0
- data/spec/fixtures/simple_feed.atom +17 -0
- data/spec/lib/xommelier/atom/entry_spec.rb +18 -0
- data/spec/lib/xommelier/xml/element/serialization_spec.rb +36 -0
- data/spec/lib/xommelier/xml/element/structure_spec.rb +27 -0
- data/spec/lib/xommelier/xml/element_spec.rb +21 -38
- data/spec/lib/xommelier/xml_spec.rb +2 -4
- data/spec/lib/xommelier_spec.rb +1 -1
- data/spec/namespaced_module.rb +0 -3
- data/spec/spec_helper.rb +5 -3
- data/xommelier.gemspec +2 -2
- metadata +62 -49
- data/lib/xommelier/xml/attribute.rb +0 -8
- data/lib/xommelier/xml/element/dsl.rb +0 -51
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/Gemfile
CHANGED
@@ -10,6 +10,8 @@ group :development, :test do
|
|
10
10
|
gem 'guard'
|
11
11
|
gem 'guard-rspec'
|
12
12
|
gem 'guard-bundler'
|
13
|
+
gem 'ruby_gntp'
|
14
|
+
gem 'rb-fsevent'
|
13
15
|
end
|
14
16
|
|
15
17
|
group :development do
|
@@ -21,3 +23,7 @@ group :development do
|
|
21
23
|
gem 'ripl-rails', require: 'ripl/rails'
|
22
24
|
gem 'ripl-rocket', require: 'ripl/rocket'
|
23
25
|
end
|
26
|
+
|
27
|
+
group :documentation do
|
28
|
+
gem 'yard'
|
29
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Xommelier is an XML Sommelier
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
Xommelier is an XML Object Mapper. You could describe some namespace (e.g. Atom) in ruby DSL and use it for parsing XML to Ruby objects or for building XML from Ruby objects.
|
6
|
+
|
7
|
+
Look into {Xommelier::Atom} module for implementation of http://www.w3.org/2005/Atom namespace
|
8
|
+
|
9
|
+
Xommelier is work in progress.
|
10
|
+
|
11
|
+
## Examples
|
12
|
+
|
13
|
+
You can see example code in examples/ folder.
|
14
|
+
|
15
|
+
## Future tasks
|
16
|
+
|
17
|
+
* Implementation that support more than one namespace in XML class
|
18
|
+
* Converting XML Schema, RelaxNG, RelaxNG Compact and DTD into Xommelier Ruby DSL
|
19
|
+
* ActiveRecord-like automatic loading of XML Schema, RelaxNG, RelaxNG Compact and DTD without needing to write it down into ruby code
|
data/Rakefile
CHANGED
@@ -1 +1,21 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'yard'
|
5
|
+
YARD::Rake::YardocTask.new(:doc)
|
6
|
+
rescue LoadError
|
7
|
+
task :doc do
|
8
|
+
abort "YARD is not available. In order to run yardoc, you must: gem install yard"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
RSpec::Core::RakeTask.new(:spec)
|
15
|
+
rescue LoadError
|
16
|
+
task :spec do
|
17
|
+
abort "RSpec is not available. In order to run specs, you must: gem install rspec"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
task default: :spec
|
data/Termfile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# IDE for rails application development
|
2
|
+
|
3
|
+
#setup 'bundle'
|
4
|
+
|
5
|
+
def title(title)
|
6
|
+
run "echo -e \"\\033];#{title}\\007\""
|
7
|
+
end
|
8
|
+
|
9
|
+
title 'Editor'
|
10
|
+
run 'vim Gemfile'
|
11
|
+
|
12
|
+
tab(:bash) do
|
13
|
+
title('Shell')
|
14
|
+
end
|
15
|
+
#tab(:log) do
|
16
|
+
#title 'Logs'
|
17
|
+
#run 'tail -f log/development.log'
|
18
|
+
#end
|
19
|
+
tab :guard do
|
20
|
+
title 'Guard'
|
21
|
+
run 'guard'
|
22
|
+
end
|
23
|
+
tab(:console) do
|
24
|
+
title 'Console'
|
25
|
+
run './console'
|
26
|
+
end
|
27
|
+
|
28
|
+
#window do
|
29
|
+
#tab "echo 'first tab'", "echo 'of window'"
|
30
|
+
#tab "named tab", :settings => "Ocean" do
|
31
|
+
#run "echo 'named tab'"
|
32
|
+
#run "ls"
|
33
|
+
#end
|
34
|
+
#end
|
35
|
+
|
36
|
+
#window "autotest" do
|
37
|
+
#tab "echo 'window and tab without options'"
|
38
|
+
#end
|
data/console
ADDED
data/examples/atom.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'xommelier/atom'
|
2
|
-
require '
|
2
|
+
require 'active_support/core_ext'
|
3
3
|
|
4
4
|
# Reading a feed
|
5
|
-
feed = Xommelier::Atom::Feed.parse(open('
|
5
|
+
feed = Xommelier::Atom::Feed.parse(open('spec/fixtures/feed.atom'))
|
6
6
|
puts feed.id, feed.title, feed.updated
|
7
7
|
|
8
|
-
feed.
|
8
|
+
feed.entries do |entry|
|
9
9
|
puts feed.id, feed.title, feed.published, feed.updated
|
10
10
|
puts feed.content || feed.summary
|
11
11
|
end
|
@@ -21,7 +21,7 @@ feed.title = 'Example.com blog'
|
|
21
21
|
entry.title = "Example.com blog entry #{i}"
|
22
22
|
entry.updated = (5 - i).days.ago
|
23
23
|
|
24
|
-
feed << entry
|
24
|
+
feed.entries << entry
|
25
25
|
end
|
26
26
|
|
27
27
|
puts feed.to_xml
|
data/lib/xommelier/atom/entry.rb
CHANGED
data/lib/xommelier/atom/link.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'xommelier/atom'
|
2
|
+
|
3
|
+
module Xommelier
|
4
|
+
module Atom
|
5
|
+
module Thread
|
6
|
+
include Xommelier::Xml
|
7
|
+
|
8
|
+
xmlns 'http://purl.org/syndication/thread/1.0', as: :thr
|
9
|
+
|
10
|
+
class InReplyTo < Xml::Element
|
11
|
+
element_name 'in-reply-to'
|
12
|
+
#method_name :in_reply_to
|
13
|
+
|
14
|
+
attribute :href
|
15
|
+
|
16
|
+
may do
|
17
|
+
attribute :href, type: Uri
|
18
|
+
attribute :type, type: String
|
19
|
+
attribute :source
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Extends Atom elements
|
25
|
+
class Entry
|
26
|
+
may do
|
27
|
+
element :in_reply_to, ns: Thread.xmlns
|
28
|
+
element :total, type: Integer, ns: Thread.xmlns
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Link
|
33
|
+
may do
|
34
|
+
attribute :count, type: Integer, ns: Thread.xmlns
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/xommelier/atom.rb
CHANGED
@@ -5,7 +5,9 @@ module Xommelier
|
|
5
5
|
include Xommelier::Xml
|
6
6
|
|
7
7
|
xmlns 'http://www.w3.org/2005/Atom', as: :atom
|
8
|
+
#roots :Feed, :Entry
|
8
9
|
|
10
|
+
# Elements
|
9
11
|
autoload :Link, 'xommelier/atom/link'
|
10
12
|
autoload :Person, 'xommelier/atom/person'
|
11
13
|
autoload :Category, 'xommelier/atom/category'
|
@@ -14,25 +16,7 @@ module Xommelier
|
|
14
16
|
autoload :Feed, 'xommelier/atom/feed'
|
15
17
|
autoload :Entry, 'xommelier/atom/entry'
|
16
18
|
|
17
|
-
#
|
18
|
-
|
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
|
19
|
+
# Extensions
|
20
|
+
autoload :Thread, 'xommelier/atom/thread'
|
37
21
|
end
|
38
22
|
end
|
@@ -1,12 +1,18 @@
|
|
1
|
+
require 'xommelier/core_ext/string'
|
1
2
|
require 'uri'
|
2
3
|
|
3
4
|
class URI::Generic
|
4
|
-
def self.from_xommelier(value)
|
5
|
-
URI.parse value
|
6
|
-
end
|
7
|
-
|
8
5
|
def to_xommelier
|
9
6
|
to_s
|
10
7
|
end
|
11
8
|
end
|
12
|
-
|
9
|
+
|
10
|
+
class Uri < String
|
11
|
+
def self.from_xommelier(value)
|
12
|
+
if value.is_a?(URI::Generic)
|
13
|
+
value
|
14
|
+
else
|
15
|
+
URI.parse(value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/xommelier/core_ext.rb
CHANGED
@@ -3,7 +3,8 @@ require 'xommelier'
|
|
3
3
|
require 'xommelier/core_ext/boolean'
|
4
4
|
require 'xommelier/core_ext/date'
|
5
5
|
require 'xommelier/core_ext/float'
|
6
|
-
require 'xommelier/core_ext/
|
6
|
+
require 'xommelier/core_ext/numeric'
|
7
7
|
require 'xommelier/core_ext/string'
|
8
|
+
require 'xommelier/core_ext/symbol'
|
8
9
|
require 'xommelier/core_ext/time'
|
9
10
|
require 'xommelier/core_ext/uri'
|
data/lib/xommelier/version.rb
CHANGED
@@ -10,6 +10,7 @@ module Xommelier
|
|
10
10
|
# Defines namespace used in formats
|
11
11
|
def xmlns(uri = nil, options = {}, &block)
|
12
12
|
if uri
|
13
|
+
options[:module] ||= self
|
13
14
|
instance_variable_set :@_xmlns, Xommelier::Xml::Namespace.new(uri, options, &block)
|
14
15
|
end
|
15
16
|
instance_variable_get :@_xmlns
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'xommelier/xml/element'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module Xommelier
|
6
|
+
module Xml
|
7
|
+
class Element
|
8
|
+
module Serialization
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
SERIALIZATION_OPTIONS = {
|
12
|
+
encoding: 'utf-8'
|
13
|
+
}
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def from_xml(xml, options = {})
|
17
|
+
new.tap do |doc|
|
18
|
+
doc.options = options
|
19
|
+
doc.from_xml(xml, options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
alias_method :parse, :from_xml
|
23
|
+
alias_method :from_xommelier, :from_xml
|
24
|
+
|
25
|
+
def element_xpath(xmldoc = nil, name = nil)
|
26
|
+
"#{xmlns_xpath(xmldoc)}:#{name || element_name}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def xmlns_xpath(xml_document = nil)
|
30
|
+
if xml_document
|
31
|
+
xml_document.namespaces.key(xmlns.uri)
|
32
|
+
else
|
33
|
+
xmlns.as
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def from_xml(xml, options = {})
|
39
|
+
case xml
|
40
|
+
when IO, String
|
41
|
+
xml = Nokogiri::XML(xml)
|
42
|
+
end
|
43
|
+
@_xml_node = options.delete(:node) { xml.at_xpath(element_xpath(xml.document, element_name)) }
|
44
|
+
|
45
|
+
if text? && @_xml_node.text?
|
46
|
+
self.text = @_xml_node.text
|
47
|
+
end
|
48
|
+
|
49
|
+
self.class.attributes.each do |name, options|
|
50
|
+
send(name, @_xml_node[name])
|
51
|
+
end
|
52
|
+
|
53
|
+
self.class.elements.each do |name, options|
|
54
|
+
deserialize_element(name, options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
alias_method :from_xommelier, :from_xml
|
58
|
+
|
59
|
+
def to_xml(options = {})
|
60
|
+
options = SERIALIZATION_OPTIONS.merge(options)
|
61
|
+
element_name = options.delete(:element_name) { self.element_name }
|
62
|
+
if options[:builder] # Non-root element
|
63
|
+
builder = options.delete(:builder)
|
64
|
+
attribute_values = {}
|
65
|
+
else # Root element
|
66
|
+
builder = Nokogiri::XML::Builder.new(options)
|
67
|
+
attribute_values = {xmlns: xmlns.to_s}
|
68
|
+
end
|
69
|
+
attributes.each do |name, value|
|
70
|
+
serialize_attribute(name, value, attribute_values)
|
71
|
+
end
|
72
|
+
builder.send(element_name, attribute_values) do |xml|
|
73
|
+
elements.each do |name, value|
|
74
|
+
serialize_element(name, value, xml)
|
75
|
+
end
|
76
|
+
if respond_to?(:text)
|
77
|
+
xml.text @text
|
78
|
+
end
|
79
|
+
end
|
80
|
+
builder.to_xml
|
81
|
+
end
|
82
|
+
alias_method :to_xommelier, :to_xml
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
def element_xpath(xmldoc = self.xml_document, name = nil)
|
87
|
+
self.class.element_xpath(xmldoc, name)
|
88
|
+
end
|
89
|
+
|
90
|
+
def xml_document
|
91
|
+
@_xml_node.document
|
92
|
+
end
|
93
|
+
|
94
|
+
def xmlns_xpath(xml_document = self.xml_document)
|
95
|
+
self.class.xmlns_xpath(xml_document)
|
96
|
+
end
|
97
|
+
|
98
|
+
def serialize_attribute(name, value, attributes)
|
99
|
+
attributes[name] = value.to_xommelier
|
100
|
+
end
|
101
|
+
|
102
|
+
def deserialize_element(name, options = nil)
|
103
|
+
options ||= self.element_options(name)
|
104
|
+
type = options[:type]
|
105
|
+
xpath = if type < Xommelier::Xml::Element
|
106
|
+
type.element_xpath(xml_document, name)
|
107
|
+
else
|
108
|
+
element_xpath(xml_document, name)
|
109
|
+
end
|
110
|
+
nodes = @_xml_node.xpath("./#{xpath}")
|
111
|
+
if nodes.any?
|
112
|
+
case options[:count]
|
113
|
+
when :any, :many
|
114
|
+
children = nodes.map { |node| typecast_element(type, node, options) }
|
115
|
+
send options[:plural], children
|
116
|
+
else
|
117
|
+
send(name, typecast_element(type, nodes[0], options))
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def typecast_element(type, node, options)
|
123
|
+
if type < Xommelier::Xml::Element
|
124
|
+
type.from_xommelier(xml_document, options.merge(node: node))
|
125
|
+
else
|
126
|
+
type.from_xommelier(node.text)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def serialize_element(name, value, xml, options = nil)
|
131
|
+
options ||= self.element_options(name)
|
132
|
+
case options[:count]
|
133
|
+
when :any, :many
|
134
|
+
single_element = options.merge(count: :one)
|
135
|
+
value.each { |item| serialize_element(name, item, xml, single_element) }
|
136
|
+
else
|
137
|
+
case value
|
138
|
+
when Xommelier::Xml::Element
|
139
|
+
value.to_xommelier(builder: xml, element_name: name)
|
140
|
+
else
|
141
|
+
xml.send(name) { xml.text value.to_xommelier }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|