xommelier 0.1.19 → 0.1.20

Sign up to get free protection for your applications and to get access to all the features.
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>