xommelier 0.1.19 → 0.1.20

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.
Files changed (45) hide show
  1. data/Rakefile +2 -2
  2. data/examples/atom.rb +3 -3
  3. data/lib/xommelier.rb +9 -0
  4. data/lib/xommelier/atom/links_extension.rb +12 -2
  5. data/lib/xommelier/atom/source.rb +12 -2
  6. data/lib/xommelier/core_ext/boolean.rb +1 -0
  7. data/lib/xommelier/core_ext/float.rb +1 -1
  8. data/lib/xommelier/core_ext/numeric.rb +1 -1
  9. data/lib/xommelier/core_ext/time.rb +9 -1
  10. data/lib/xommelier/core_ext/uri.rb +6 -2
  11. data/lib/xommelier/open_search.rb +55 -40
  12. data/lib/xommelier/rss.rb +51 -6
  13. data/lib/xommelier/rss/atomic.rb +21 -0
  14. data/lib/xommelier/schemas/dublincore/dc.xsd +118 -0
  15. data/lib/xommelier/schemas/dublincore/dcmitype.xsd +52 -0
  16. data/lib/xommelier/schemas/dublincore/dcterms.xsd +382 -0
  17. data/lib/xommelier/schemas/dublincore/qualifieddc.xsd +40 -0
  18. data/lib/xommelier/schemas/dublincore/simpledc.xsd +39 -0
  19. data/lib/xommelier/schemas/opensearch.xsd +484 -0
  20. data/lib/xommelier/schemas/syndication/creativeCommons.xsd +17 -0
  21. data/lib/xommelier/schemas/syndication/fh.xsd +19 -0
  22. data/lib/xommelier/schemas/syndication/schematron.xsd +254 -0
  23. data/lib/xommelier/schemas/syndication/thr.xsd +39 -0
  24. data/lib/xommelier/schemas/syndication/trackback.xsd +18 -0
  25. data/lib/xommelier/version.rb +1 -1
  26. data/lib/xommelier/xml/element.rb +1 -0
  27. data/lib/xommelier/xml/element/serialization.rb +12 -11
  28. data/lib/xommelier/xml/element/structure.rb +19 -19
  29. data/lib/xommelier/xml/element/structure/property.rb +4 -0
  30. data/spec/fixtures/opensearch.full.xml +23 -0
  31. data/spec/functional/xommelier/atom/feed/building_hash_spec.rb +37 -0
  32. data/spec/functional/xommelier/atom/feed/building_spec.rb +33 -0
  33. data/spec/functional/{atom_feed_parsing_spec.rb → xommelier/atom/feed/parsing_spec.rb} +3 -4
  34. data/spec/functional/{atom_feed_thread_building_spec.rb → xommelier/atom/threading/building_spec.rb} +0 -0
  35. data/spec/functional/xommelier/open_search/description/building_spec.rb +36 -0
  36. data/spec/functional/xommelier/open_search/description/parsing_spec.rb +47 -0
  37. data/spec/functional/{rss_feed_building_spec.rb → xommelier/rss/rss/building_spec.rb} +0 -0
  38. data/spec/functional/{rss_feed_parsing_spec.rb → xommelier/rss/rss/parsing_spec.rb} +0 -0
  39. data/spec/lib/xommelier/rss/email_address_spec.rb +22 -0
  40. data/spec/lib/xommelier/xml/element_spec.rb +2 -2
  41. data/spec/spec_helper.rb +1 -0
  42. data/xommelier.gemspec +8 -8
  43. metadata +34 -14
  44. data/spec/functional/atom_feed_building_spec.rb +0 -31
  45. data/spec/functional/build_nested_document_from_hash_spec.rb +0 -39
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ begin
5
5
  YARD::Rake::YardocTask.new(:doc)
6
6
  rescue LoadError
7
7
  task :doc do
8
- abort "YARD is not available. In order to run yardoc, you must: gem install yard"
8
+ abort 'YARD is not available. In order to run yardoc, you must: gem install yard'
9
9
  end
10
10
  end
11
11
 
@@ -14,7 +14,7 @@ begin
14
14
  RSpec::Core::RakeTask.new(:spec)
15
15
  rescue LoadError
16
16
  task :spec do
17
- abort "RSpec is not available. In order to run specs, you must: gem install rspec"
17
+ abort 'RSpec is not available. In order to run specs, you must: gem install rspec'
18
18
  end
19
19
  end
20
20
 
@@ -7,8 +7,8 @@ feed = Xommelier::Atom::Feed.parse(open('spec/fixtures/feed.atom.xml'))
7
7
  puts feed.id, feed.title, feed.updated
8
8
 
9
9
  feed.entries do |entry|
10
- puts feed.id, feed.title, feed.published, feed.updated
11
- puts feed.content || feed.summary
10
+ puts entry.id, entry.title, entry.published, entry.updated
11
+ puts entry.content || entry.summary
12
12
  end
13
13
 
14
14
  # Building a feed
@@ -19,7 +19,7 @@ feed.complete = Xommelier::Atom::History::Complete.new
19
19
 
20
20
  entry = feed.entry = Xommelier::Atom::Entry.new(
21
21
  id: 'http://example.com/blog/2012/03/05',
22
- title: "Happy Xommelier's day!",
22
+ title: 'Happy Xommelier\'s day!',
23
23
  updated: 5.days.ago
24
24
  ).tap do |entry|
25
25
  entry.link = Xommelier::Atom::Link.new(href: entry.id, rel: 'alternate', type: 'text/html')
@@ -4,6 +4,8 @@ require 'xommelier/core_ext'
4
4
  module Xommelier
5
5
  autoload :Atom, 'xommelier/atom'
6
6
  autoload :OpenSearch, 'xommelier/open_search'
7
+ autoload :OPML, 'xommelier/opml'
8
+ autoload :RSS, 'xommelier/rss'
7
9
 
8
10
  # Standard Xommelier Error
9
11
  class Error < ::StandardError
@@ -15,6 +17,13 @@ module Xommelier
15
17
  super("Cannot validate #{object} because no schema provided for validation.")
16
18
  end
17
19
  end
20
+
21
+ # Typecasting error
22
+ class TypeError < Error
23
+ def initialize(object, type)
24
+ super("Cannot typecast #{object.inspect} to #{type}")
25
+ end
26
+ end
18
27
  end
19
28
 
20
29
  require 'xommelier/xml'
@@ -4,11 +4,21 @@ module Xommelier
4
4
  module Atom
5
5
  module LinksExtension
6
6
  def feed_url
7
- links.find { |link| link.rel == 'self' && link.type == 'application/atom+xml' }.try(:href)
7
+ detect_linked_href(rel: 'self', type: 'application/atom+xml')
8
8
  end
9
9
 
10
10
  def html_url
11
- links.find { |link| link.rel == 'alternate' && link.type == 'text/html' }.try(:href)
11
+ detect_linked_href(rel: 'alternate', type: 'text/html')
12
+ end
13
+
14
+ protected
15
+
16
+ def detect_linked_href(attributes = {})
17
+ links.detect do |link|
18
+ attributes.inject(true) do |result, (attr, value)|
19
+ result && link.send(attr) == value
20
+ end
21
+ end.try(:href)
12
22
  end
13
23
  end
14
24
  end
@@ -4,10 +4,20 @@ module Xommelier
4
4
  module Atom
5
5
  class Source < Xml::Element
6
6
  may do
7
- element :generator, :icon, :id, :logo, :rights, :subtitle, :title, :updated
7
+ element :generator
8
+ element :icon
9
+ element :id
10
+ element :logo
11
+ element :rights
12
+ element :subtitle
13
+ element :title
14
+ element :updated
8
15
  end
9
16
  any do
10
- element :author, :category, :contributor, :link
17
+ element :author
18
+ element :category
19
+ element :contributor
20
+ element :link
11
21
  end
12
22
  end
13
23
  end
@@ -1,6 +1,7 @@
1
1
  require 'xommelier'
2
2
 
3
3
  class Boolean
4
+ #noinspection RubyStringKeysInHashInspection,RubyDuplicatedKeysInHashInspection
4
5
  BOOLEAN_MAP = {
5
6
  true => true, 'true' => true, 'TRUE' => true, '1' => true, '1.0' => true, 1 => true, 1.0 => true,
6
7
  false => false, 'false' => false, 'FALSE' => false, '0' => false, '0.0' => false, 0 => false, 0.0 => false, nil => false, '' => false
@@ -3,7 +3,7 @@ class Float
3
3
  return nil if value.blank?
4
4
  begin
5
5
  Float(value)
6
- rescue ArgumentError => e
6
+ rescue ArgumentError
7
7
  value
8
8
  end
9
9
  end
@@ -3,7 +3,7 @@ class Numeric
3
3
  return nil if value.blank?
4
4
  begin
5
5
  value =~ /\./ ? Float(value) : Integer(value)
6
- rescue ArgumentError => e
6
+ rescue ArgumentError
7
7
  value
8
8
  end
9
9
  end
@@ -2,7 +2,15 @@ require 'time'
2
2
 
3
3
  class Time
4
4
  def self.from_xommelier(value)
5
- Time.xmlschema value
5
+ return if value == nil
6
+ case value
7
+ when String
8
+ Time.xmlschema(value)
9
+ when Time
10
+ value
11
+ else
12
+ raise Xommelier::TypeError.new(value, Time)
13
+ end
6
14
  end
7
15
 
8
16
  def to_xommelier
@@ -9,10 +9,14 @@ end
9
9
 
10
10
  class Uri < String
11
11
  def self.from_xommelier(value)
12
- if value.is_a?(URI::Generic)
12
+ return if value == nil
13
+ case value
14
+ when URI::Generic
13
15
  value
14
- else
16
+ when String
15
17
  URI.parse(value)
18
+ else
19
+ raise Xommelier::TypeError.new(value, Uri)
16
20
  end
17
21
  end
18
22
  end
@@ -8,10 +8,28 @@ module Xommelier
8
8
  include Xml
9
9
 
10
10
  xmlns 'http://a9.com/-/spec/opensearch/1.1/', as: :opensearch
11
+ schema
11
12
 
12
- class Url < Xml::Element
13
- element_name :Url
13
+ # @abstract
14
+ class Element < Xml::Element
15
+ def self.attribute(name, options = {})
16
+ options[:as] ||= name.to_s.camelcase(:lower)
17
+ super
18
+ end
19
+
20
+ def self.element(name, options = {})
21
+ options[:as] ||= name.to_s.camelcase(:upper)
22
+ super
23
+ end
14
24
 
25
+ class << self
26
+ def find_element_name
27
+ name.demodulize
28
+ end
29
+ end
30
+ end
31
+
32
+ class Url < Element
15
33
  attribute :template
16
34
  attribute :type
17
35
 
@@ -22,9 +40,7 @@ module Xommelier
22
40
  end
23
41
  end
24
42
 
25
- class Image < Xml::Element
26
- element_name :Image
27
-
43
+ class Image < Element
28
44
  may do
29
45
  attribute :height, type: Integer
30
46
  attribute :width, type: Integer
@@ -34,49 +50,46 @@ module Xommelier
34
50
  text type: Uri
35
51
  end
36
52
 
37
- class Query < Xml::Element
38
- element_name :Query
39
-
40
- element :role#, type: Enum[:request, :example, :related, :correction, :subset, :superset]
53
+ class Query < Element
54
+ attribute :role#, type: Enum[:request, :example, :related, :correction, :subset, :superset]
41
55
 
42
56
  may do
43
- element :title
44
- element :total_results, type: Integer, as: 'totalResults'
45
- element :search_terms, as: 'searchTerms', default: 'Xommelier'
46
- element :count, type: Integer
47
- element :start_index, type: Boolean, as: 'startIndex'
48
- element :start_page, type: Boolean, as: 'startPage'
49
- element :language
50
- element :input_encoding, as: 'inputEncoding'
51
- element :output_encoding, as: 'outputEncoding'
57
+ attribute :title
58
+ attribute :total_results, type: Integer
59
+ attribute :search_terms, default: 'Xommelier'
60
+ attribute :count, type: Integer
61
+ attribute :start_index, type: Boolean
62
+ attribute :start_page, type: Boolean
63
+ attribute :language
64
+ attribute :input_encoding
65
+ attribute :output_encoding
52
66
  end
53
67
  end
54
68
 
55
- class Description < Xml::Element
56
- element_name :OpenSearchDescription
69
+ class Description < Element
70
+ element_name 'OpenSearchDescription'
57
71
 
58
- element :short_name, as: :ShortName
59
- element :description, as: :Description
72
+ element :short_name
73
+ element :description
60
74
 
61
- many do
62
- element :url, type: Url, as: Url.element_name
63
- end
75
+ element :url, type: Url, count: :many
76
+ element :query, type: Query, count: :any
64
77
 
65
78
  may do
66
- element :tags, as: :Tags
67
- element :contacts, as: :Contacts
68
- element :long_name, as: :LongName
69
- element :developer, as: :Developer
70
- element :attribution, as: :Attribution
71
- element :syndication_right, type: Symbol, as: :SyndicatioeRight, default: :open #, type: Enum[:open, :limited, :private, :closed]
72
- element :adult_content, type: Boolean, as: :AdultContent
73
- element :language, as: :Language, default: '*'
74
- element :input_encoding, as: :InputEncoding, default: 'UTF-8'
75
- element :output_encoding, as: :OutputEncoding, default: 'UTF-8'
79
+ element :tags
80
+ element :contact
81
+ element :long_name
82
+ element :developer
83
+ element :attribution
84
+ element :syndication_right, type: String, default: 'open' #, type: Enum[:open, :limited, :private, :closed]
85
+ element :adult_content, type: Boolean
86
+ element :language, default: '*'
87
+ element :input_encoding, default: 'UTF-8'
88
+ element :output_encoding, default: 'UTF-8'
76
89
  end
77
90
 
78
91
  any do
79
- element :image, type: Image, as: Image.element_name
92
+ element :image, type: Image
80
93
  end
81
94
  end
82
95
  end
@@ -84,10 +97,12 @@ module Xommelier
84
97
  if defined?(Atom)
85
98
  module Atom
86
99
  class Feed
100
+ include LinksExtension
101
+
87
102
  may do
88
- element :totalResults, type: Integer, ns: OpenSearch.xmlns
89
- element :startIndex, type: Integer, ns: OpenSearch.xmlns
90
- element :itemsPerPage, type: Integer, ns: OpenSearch.xmlns
103
+ element :total_results, type: Integer, ns: OpenSearch.xmlns, as: 'totalResults'
104
+ element :start_index, type: Integer, ns: OpenSearch.xmlns, as: 'startIndex'
105
+ element :items_per_page, type: Integer, ns: OpenSearch.xmlns, as: 'itemsPerPage'
91
106
  end
92
107
 
93
108
  any do
@@ -95,7 +110,7 @@ module Xommelier
95
110
  end
96
111
 
97
112
  def search_url
98
- links.find { |link| link.rel == 'search' && link.type == MIME_TYPE }.try(:href)
113
+ detect_linked_href(rel: 'search', type: MIME_TYPE)
99
114
  end
100
115
  end
101
116
  end
@@ -6,9 +6,54 @@ module Xommelier
6
6
  module RSS
7
7
  include Xommelier::Xml
8
8
 
9
- # TODO RSS Email simple type if needed
10
- # class EmailAddress < String
11
- # end
9
+ # The RECOMMENDED format for e-mail addresses in RSS elements is: username@hostname.tld (Real Name)
10
+ # http://www.rssboard.org/rss-profile#data-types-email
11
+ class EmailAddress < String
12
+ def self.from_xommelier(string)
13
+ email = new(string)
14
+ email.name?
15
+ email
16
+ end
17
+
18
+ def name?
19
+ @has_name ||= begin
20
+ address, name = strip.split(/\s+/, 2)
21
+
22
+ if name.present? && name =~ /\(([\w ]+)\)\s?/
23
+ replace(address)
24
+ @name = $1.to_s
25
+ true
26
+ else
27
+ false
28
+ end
29
+ end
30
+ end
31
+
32
+ def address
33
+ to_s
34
+ end
35
+
36
+ def address=(address)
37
+ replace(address)
38
+ end
39
+
40
+ def name
41
+ @name if name?
42
+ end
43
+
44
+ def name=(name)
45
+ @has_name = name.present?
46
+ @name = name
47
+ end
48
+
49
+ def to_xommelier
50
+ if name?
51
+ "#{address} (#{name})"
52
+ else
53
+ address
54
+ end
55
+ end
56
+ end
12
57
 
13
58
  class Element < Xml::Element
14
59
  def self.element(name, options = {})
@@ -125,7 +170,7 @@ module Xommelier
125
170
  element :link, type: Uri
126
171
 
127
172
  # Email address of the author of the item.
128
- element :author#, type: EmailAddress
173
+ element :author, type: EmailAddress
129
174
 
130
175
  # Includes the item in one or more categories.
131
176
  element :category, type: Category, count: :any
@@ -164,10 +209,10 @@ module Xommelier
164
209
  element :copyright
165
210
 
166
211
  # Email address for person responsible for editorial content.
167
- element :managing_editor#, type: EmailAddress
212
+ element :managing_editor, type: EmailAddress
168
213
 
169
214
  # Email address for person responsible for technical issues relating to channel.
170
- element :web_master#, type: EmailAddress
215
+ element :web_master, type: EmailAddress
171
216
 
172
217
  # The publication date for the content in the channel.
173
218
  # All date-times in RSS conform to the Date and Time Specification of RFC 822, with the exception
@@ -0,0 +1,21 @@
1
+ require 'xommelier/rss'
2
+ require 'xommelier/atom'
3
+ require 'xommelier/atom/history'
4
+
5
+ module Xommelier
6
+ module RSS
7
+ class Channel
8
+ may do
9
+ element :id, ns: Atom.xmlns
10
+ element :complete, type: Atom::History::Complete
11
+ element :archive, type: Atom::History::Archive
12
+ end
13
+ element :atom_link, type: Atom::Link, count: :any
14
+ end
15
+
16
+ class Item
17
+ element :id, ns: Atom.xmlns, count: :may
18
+ element :atom_link, type: Atom::Link, count: :any
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,118 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
3
+ xmlns="http://purl.org/dc/elements/1.1/"
4
+ targetNamespace="http://purl.org/dc/elements/1.1/"
5
+ elementFormDefault="qualified"
6
+ attributeFormDefault="unqualified">
7
+
8
+ <xs:annotation>
9
+ <xs:documentation xml:lang="en">
10
+ DCMES 1.1 XML Schema
11
+ XML Schema for http://purl.org/dc/elements/1.1/ namespace
12
+
13
+ Created 2008-02-11
14
+
15
+ Created by
16
+
17
+ Tim Cole (t-cole3@uiuc.edu)
18
+ Tom Habing (thabing@uiuc.edu)
19
+ Jane Hunter (jane@dstc.edu.au)
20
+ Pete Johnston (p.johnston@ukoln.ac.uk),
21
+ Carl Lagoze (lagoze@cs.cornell.edu)
22
+
23
+ This schema declares XML elements for the 15 DC elements from the
24
+ http://purl.org/dc/elements/1.1/ namespace.
25
+
26
+ It defines a complexType SimpleLiteral which permits mixed content
27
+ and makes the xml:lang attribute available. It disallows child elements by
28
+ use of minOcccurs/maxOccurs.
29
+
30
+ However, this complexType does permit the derivation of other complexTypes
31
+ which would permit child elements.
32
+
33
+ All elements are declared as substitutable for the abstract element any,
34
+ which means that the default type for all elements is dc:SimpleLiteral.
35
+
36
+ </xs:documentation>
37
+
38
+ </xs:annotation>
39
+
40
+
41
+ <xs:import namespace="http://www.w3.org/XML/1998/namespace"
42
+ schemaLocation="http://www.w3.org/2001/03/xml.xsd">
43
+ </xs:import>
44
+
45
+ <xs:complexType name="SimpleLiteral">
46
+ <xs:annotation>
47
+ <xs:documentation xml:lang="en">
48
+ This is the default type for all of the DC elements.
49
+ It permits text content only with optional
50
+ xml:lang attribute.
51
+ Text is allowed because mixed="true", but sub-elements
52
+ are disallowed because minOccurs="0" and maxOccurs="0"
53
+ are on the xs:any tag.
54
+
55
+ This complexType allows for restriction or extension permitting
56
+ child elements.
57
+ </xs:documentation>
58
+ </xs:annotation>
59
+
60
+ <xs:complexContent mixed="true">
61
+ <xs:restriction base="xs:anyType">
62
+ <xs:sequence>
63
+ <xs:any processContents="lax" minOccurs="0" maxOccurs="0"/>
64
+ </xs:sequence>
65
+ <xs:attribute ref="xml:lang" use="optional"/>
66
+ </xs:restriction>
67
+ </xs:complexContent>
68
+ </xs:complexType>
69
+
70
+ <xs:element name="any" type="SimpleLiteral" abstract="true"/>
71
+
72
+ <xs:element name="title" substitutionGroup="any"/>
73
+ <xs:element name="creator" substitutionGroup="any"/>
74
+ <xs:element name="subject" substitutionGroup="any"/>
75
+ <xs:element name="description" substitutionGroup="any"/>
76
+ <xs:element name="publisher" substitutionGroup="any"/>
77
+ <xs:element name="contributor" substitutionGroup="any"/>
78
+ <xs:element name="date" substitutionGroup="any"/>
79
+ <xs:element name="type" substitutionGroup="any"/>
80
+ <xs:element name="format" substitutionGroup="any"/>
81
+ <xs:element name="identifier" substitutionGroup="any"/>
82
+ <xs:element name="source" substitutionGroup="any"/>
83
+ <xs:element name="language" substitutionGroup="any"/>
84
+ <xs:element name="relation" substitutionGroup="any"/>
85
+ <xs:element name="coverage" substitutionGroup="any"/>
86
+ <xs:element name="rights" substitutionGroup="any"/>
87
+
88
+ <xs:group name="elementsGroup">
89
+ <xs:annotation>
90
+ <xs:documentation xml:lang="en">
91
+ This group is included as a convenience for schema authors
92
+ who need to refer to all the elements in the
93
+ http://purl.org/dc/elements/1.1/ namespace.
94
+ </xs:documentation>
95
+ </xs:annotation>
96
+
97
+ <xs:sequence>
98
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
99
+ <xs:element ref="any"/>
100
+ </xs:choice>
101
+ </xs:sequence>
102
+ </xs:group>
103
+
104
+ <xs:complexType name="elementContainer">
105
+ <xs:annotation>
106
+ <xs:documentation xml:lang="en">
107
+ This complexType is included as a convenience for schema authors who need to define a root
108
+ or container element for all of the DC elements.
109
+ </xs:documentation>
110
+ </xs:annotation>
111
+
112
+ <xs:choice>
113
+ <xs:group ref="elementsGroup"/>
114
+ </xs:choice>
115
+ </xs:complexType>
116
+
117
+
118
+ </xs:schema>