lotus 0.0.12
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 +6 -0
- data/.travis.yml +9 -0
- data/Gemfile +16 -0
- data/README.md +233 -0
- data/Rakefile +7 -0
- data/lib/lotus.rb +232 -0
- data/lib/lotus/activity.rb +134 -0
- data/lib/lotus/atom/account.rb +50 -0
- data/lib/lotus/atom/address.rb +56 -0
- data/lib/lotus/atom/author.rb +167 -0
- data/lib/lotus/atom/category.rb +41 -0
- data/lib/lotus/atom/entry.rb +159 -0
- data/lib/lotus/atom/feed.rb +174 -0
- data/lib/lotus/atom/generator.rb +40 -0
- data/lib/lotus/atom/link.rb +79 -0
- data/lib/lotus/atom/name.rb +57 -0
- data/lib/lotus/atom/organization.rb +62 -0
- data/lib/lotus/atom/portable_contacts.rb +117 -0
- data/lib/lotus/atom/source.rb +168 -0
- data/lib/lotus/atom/thread.rb +60 -0
- data/lib/lotus/author.rb +177 -0
- data/lib/lotus/category.rb +45 -0
- data/lib/lotus/crypto.rb +146 -0
- data/lib/lotus/feed.rb +190 -0
- data/lib/lotus/generator.rb +53 -0
- data/lib/lotus/identity.rb +59 -0
- data/lib/lotus/link.rb +56 -0
- data/lib/lotus/notification.rb +220 -0
- data/lib/lotus/publisher.rb +40 -0
- data/lib/lotus/subscription.rb +117 -0
- data/lib/lotus/version.rb +3 -0
- data/lotus.gemspec +27 -0
- data/spec/activity_spec.rb +84 -0
- data/spec/atom/feed_spec.rb +681 -0
- data/spec/author_spec.rb +150 -0
- data/spec/crypto_spec.rb +138 -0
- data/spec/feed_spec.rb +252 -0
- data/spec/helper.rb +8 -0
- data/spec/identity_spec.rb +67 -0
- data/spec/link_spec.rb +30 -0
- data/spec/notification_spec.rb +77 -0
- data/test/example_feed.atom +393 -0
- data/test/example_feed_empty_author.atom +336 -0
- data/test/example_feed_false_connected.atom +359 -0
- data/test/example_feed_link_without_href.atom +134 -0
- data/test/example_page.html +4 -0
- data/test/mime_type_bug_feed.atom +874 -0
- metadata +204 -0
data/lib/lotus/author.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'lotus/activity'
|
2
|
+
|
3
|
+
module Lotus
|
4
|
+
require 'atom'
|
5
|
+
|
6
|
+
# Holds information about the author of the Feed.
|
7
|
+
class Author
|
8
|
+
require 'date'
|
9
|
+
|
10
|
+
# Holds the id that represents this contact.
|
11
|
+
attr_reader :id
|
12
|
+
|
13
|
+
# Holds the nickname of this contact.
|
14
|
+
attr_reader :nickname
|
15
|
+
|
16
|
+
# Holds a hash representing information about the name of this contact.
|
17
|
+
#
|
18
|
+
# contains one or more of the following:
|
19
|
+
# :formatted => The full name of the contact
|
20
|
+
# :family_name => The family name. "Last name" in Western contexts.
|
21
|
+
# :given_name => The given name. "First name" in Western contexts.
|
22
|
+
# :middle_name => The middle name.
|
23
|
+
# :honorific_prefix => "Title" in Western contexts. (e.g. "Mr." "Mrs.")
|
24
|
+
# :honorific_suffix => "Suffix" in Western contexts. (e.g. "Esq.")
|
25
|
+
attr_reader :extended_name
|
26
|
+
|
27
|
+
# The uri that uniquely identifies the author.
|
28
|
+
attr_reader :uri
|
29
|
+
|
30
|
+
# The email address of the author.
|
31
|
+
attr_reader :email
|
32
|
+
|
33
|
+
# The name of the author
|
34
|
+
attr_reader :name
|
35
|
+
|
36
|
+
# Holds a hash representing the address of the contact.
|
37
|
+
#
|
38
|
+
# contains one or more of the following:
|
39
|
+
# :formatted => A formatted representating of the address. May
|
40
|
+
# contain newlines.
|
41
|
+
# :street_address => The full street address. May contain newlines.
|
42
|
+
# :locality => The city or locality component.
|
43
|
+
# :region => The state or region component.
|
44
|
+
# :postal_code => The zipcode or postal code component.
|
45
|
+
# :country => The country name component.
|
46
|
+
attr_reader :address
|
47
|
+
|
48
|
+
# Holds a hash representing an organization for this contact.
|
49
|
+
#
|
50
|
+
# contains one or more of the following:
|
51
|
+
# :name => The name of the organization (e.g. company, school,
|
52
|
+
# etc) This field is required. Will be used for sorting.
|
53
|
+
# :department => The department within the organization.
|
54
|
+
# :title => The title or role within the organization.
|
55
|
+
# :type => The type of organization. Canonical values include
|
56
|
+
# "job" or "school"
|
57
|
+
# :start_date => A DateTime representing when the contact joined
|
58
|
+
# the organization.
|
59
|
+
# :end_date => A DateTime representing when the contact left the
|
60
|
+
# organization.
|
61
|
+
# :location => The physical location of this organization.
|
62
|
+
# :description => A free-text description of the role this contact
|
63
|
+
# played in this organization.
|
64
|
+
attr_reader :organization
|
65
|
+
|
66
|
+
# Holds a hash representing information about an account held by this
|
67
|
+
# contact.
|
68
|
+
#
|
69
|
+
# contains one or more of the following:
|
70
|
+
# :domain => The top-most authoriative domain for this account. (e.g.
|
71
|
+
# "twitter.com") This is the primary field. Is required.
|
72
|
+
# Used for sorting.
|
73
|
+
# :username => An alphanumeric username, typically chosen by the user.
|
74
|
+
# :userid => A user id, typically assigned, that uniquely refers to
|
75
|
+
# the user.
|
76
|
+
attr_reader :account
|
77
|
+
|
78
|
+
# Holds the gender of this contact.
|
79
|
+
attr_reader :gender
|
80
|
+
|
81
|
+
# Holds a note for this contact.
|
82
|
+
attr_reader :note
|
83
|
+
|
84
|
+
# Holds the display name for this contact.
|
85
|
+
attr_reader :display_name
|
86
|
+
|
87
|
+
# Holds the preferred username of this contact.
|
88
|
+
attr_reader :preferred_username
|
89
|
+
|
90
|
+
# Holds a DateTime that represents when this contact was last modified.
|
91
|
+
attr_reader :updated
|
92
|
+
|
93
|
+
# Holds a DateTime that represents when this contact was originally
|
94
|
+
# published.
|
95
|
+
attr_reader :published
|
96
|
+
|
97
|
+
# Holds a DateTime representing this contact's birthday.
|
98
|
+
attr_reader :birthday
|
99
|
+
|
100
|
+
# Holds a DateTime representing a contact's anniversary.
|
101
|
+
attr_reader :anniversary
|
102
|
+
|
103
|
+
# Creates a representating of an author.
|
104
|
+
#
|
105
|
+
# options:
|
106
|
+
# name => The name of the author. Defaults: "anonymous"
|
107
|
+
# id => The identifier that uniquely identifies the
|
108
|
+
# contact.
|
109
|
+
# nickname => The nickname of the contact.
|
110
|
+
# gender => The gender of the contact.
|
111
|
+
# note => A note for this contact.
|
112
|
+
# display_name => The display name for this contact.
|
113
|
+
# preferred_username => The preferred username for this contact.
|
114
|
+
# updated => A DateTime representing when this contact was
|
115
|
+
# last updated.
|
116
|
+
# published => A DateTime representing when this contact was
|
117
|
+
# originally created.
|
118
|
+
# birthday => A DateTime representing a birthday for this
|
119
|
+
# contact.
|
120
|
+
# anniversary => A DateTime representing an anniversary for this
|
121
|
+
# contact.
|
122
|
+
# extended_name => A Hash representing the name of the contact.
|
123
|
+
# organization => A Hash representing the organization of which the
|
124
|
+
# contact belongs.
|
125
|
+
# account => A Hash describing the authorative account for the
|
126
|
+
# author.
|
127
|
+
# address => A Hash describing the address of the contact.
|
128
|
+
# uri => The uri that uniquely identifies this author.
|
129
|
+
# email => The email of the author.
|
130
|
+
def initialize(options = {})
|
131
|
+
@uri = options[:uri]
|
132
|
+
@name = options[:name] || "anonymous"
|
133
|
+
@email = options[:email]
|
134
|
+
|
135
|
+
@id = options[:id]
|
136
|
+
@name = options[:name]
|
137
|
+
@gender = options[:gender]
|
138
|
+
@note = options[:note]
|
139
|
+
@nickname = options[:nickname]
|
140
|
+
@display_name = options[:display_name]
|
141
|
+
@preferred_username = options[:preferred_username]
|
142
|
+
@updated = options[:updated]
|
143
|
+
@published = options[:published]
|
144
|
+
@birthday = options[:birthday]
|
145
|
+
@anniversary = options[:anniversary]
|
146
|
+
|
147
|
+
@extended_name = options[:extended_name]
|
148
|
+
@organization = options[:organization]
|
149
|
+
@account = options[:account]
|
150
|
+
@address = options[:address]
|
151
|
+
end
|
152
|
+
|
153
|
+
def to_hash
|
154
|
+
{
|
155
|
+
:uri => self.uri,
|
156
|
+
:email => self.email,
|
157
|
+
:name => self.name,
|
158
|
+
|
159
|
+
:id => self.id,
|
160
|
+
:gender => self.gender,
|
161
|
+
:note => self.note,
|
162
|
+
:nickname => self.nickname,
|
163
|
+
:display_name => self.display_name,
|
164
|
+
:preferred_username => self.preferred_username,
|
165
|
+
:updated => self.updated,
|
166
|
+
:published => self.published,
|
167
|
+
:birthday => self.birthday,
|
168
|
+
:anniversary => self.anniversary,
|
169
|
+
|
170
|
+
:extended_name => self.extended_name,
|
171
|
+
:organization => self.organization,
|
172
|
+
:account => self.account,
|
173
|
+
:address => self.address
|
174
|
+
}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Lotus
|
2
|
+
# This element conveys information about a category associated with an entry
|
3
|
+
# or feed. There is no defined meaning to the content according to the Atom
|
4
|
+
# specification.
|
5
|
+
class Category
|
6
|
+
# Holds the base URI for relative URIs contained in scheme.
|
7
|
+
attr_reader :base
|
8
|
+
|
9
|
+
# Holds the language of term and label, when it exists. The language
|
10
|
+
# should be specified as RFC 3066 as either 2 or 3 letter codes.
|
11
|
+
# For example: 'en' for English or more specifically 'en-us'
|
12
|
+
attr_reader :lang
|
13
|
+
|
14
|
+
# Holds the optional scheme used for categorization.
|
15
|
+
attr_reader :scheme
|
16
|
+
|
17
|
+
# Holds the string identifying the category to which the entry or
|
18
|
+
# feed belongs.
|
19
|
+
attr_reader :term
|
20
|
+
|
21
|
+
# Holds the string that provides a human-readable label for display in
|
22
|
+
# end-user applications. The content of this field is language sensitive.
|
23
|
+
attr_reader :label
|
24
|
+
|
25
|
+
# Create a Category to apply to a feed or entry.
|
26
|
+
def initialize(options = {})
|
27
|
+
@base = options[:base]
|
28
|
+
@lang = options[:lang]
|
29
|
+
@scheme = options[:scheme]
|
30
|
+
@term = options[:term]
|
31
|
+
@label = options[:label]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Yields a Hash that represents this category.
|
35
|
+
def to_hash
|
36
|
+
{
|
37
|
+
:base => @base,
|
38
|
+
:lang => @lang,
|
39
|
+
:scheme => @scheme,
|
40
|
+
:term => @term,
|
41
|
+
:label => @label
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/lotus/crypto.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
module Lotus
|
2
|
+
module Crypto
|
3
|
+
require 'openssl'
|
4
|
+
require 'rsa'
|
5
|
+
require 'base64'
|
6
|
+
|
7
|
+
KeyPair = Struct.new(:public_key, :private_key)
|
8
|
+
|
9
|
+
# Generate a new RSA keypair with the given bitlength.
|
10
|
+
def self.new_keypair(bits = 2048)
|
11
|
+
keypair = KeyPair.new
|
12
|
+
|
13
|
+
key = RSA::KeyPair.generate(bits)
|
14
|
+
|
15
|
+
public_key = key.public_key
|
16
|
+
m = public_key.modulus
|
17
|
+
e = public_key.exponent
|
18
|
+
|
19
|
+
modulus = ""
|
20
|
+
until m == 0 do
|
21
|
+
modulus << [m % 256].pack("C")
|
22
|
+
m >>= 8
|
23
|
+
end
|
24
|
+
modulus.reverse!
|
25
|
+
|
26
|
+
exponent = ""
|
27
|
+
until e == 0 do
|
28
|
+
exponent << [e % 256].pack("C")
|
29
|
+
e >>= 8
|
30
|
+
end
|
31
|
+
exponent.reverse!
|
32
|
+
|
33
|
+
keypair.public_key = "RSA.#{Base64::urlsafe_encode64(modulus)}.#{Base64::urlsafe_encode64(exponent)}"
|
34
|
+
|
35
|
+
tmp_private_key = key.private_key
|
36
|
+
m = tmp_private_key.modulus
|
37
|
+
e = tmp_private_key.exponent
|
38
|
+
|
39
|
+
modulus = ""
|
40
|
+
until m == 0 do
|
41
|
+
modulus << [m % 256].pack("C")
|
42
|
+
m >>= 8
|
43
|
+
end
|
44
|
+
modulus.reverse!
|
45
|
+
|
46
|
+
exponent = ""
|
47
|
+
until e == 0 do
|
48
|
+
exponent << [e % 256].pack("C")
|
49
|
+
e >>= 8
|
50
|
+
end
|
51
|
+
exponent.reverse!
|
52
|
+
|
53
|
+
keypair.private_key = "RSA.#{Base64::urlsafe_encode64(modulus)}.#{Base64::urlsafe_encode64(exponent)}"
|
54
|
+
|
55
|
+
keypair
|
56
|
+
end
|
57
|
+
|
58
|
+
# Creates an EMSA signature for the given plaintext and key.
|
59
|
+
def self.emsa_sign(text, private_key)
|
60
|
+
private_key = generate_key(private_key) unless private_key.is_a? RSA::Key
|
61
|
+
|
62
|
+
signature = self.emsa_signature(text, private_key)
|
63
|
+
|
64
|
+
self.decrypt(private_key, signature)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Verifies an existing EMSA signature.
|
68
|
+
def self.emsa_verify(text, signature, public_key)
|
69
|
+
# RSA encryption is needed to compare the signatures
|
70
|
+
public_key = generate_key(public_key) unless public_key.is_a? RSA::Key
|
71
|
+
|
72
|
+
# Get signature to check
|
73
|
+
emsa = self.emsa_signature(text, public_key)
|
74
|
+
|
75
|
+
# Get signature in payload
|
76
|
+
emsa_signature = self.encrypt(public_key, signature)
|
77
|
+
|
78
|
+
# RSA gem drops leading 0s since it does math upon an Integer
|
79
|
+
# As a workaround, I check for what I expect the second byte to be (\x01)
|
80
|
+
# This workaround will also handle seeing a \x00 first if the RSA gem is
|
81
|
+
# fixed.
|
82
|
+
if emsa_signature.getbyte(0) == 1
|
83
|
+
emsa_signature = "\x00#{emsa_signature}"
|
84
|
+
end
|
85
|
+
|
86
|
+
# Does the signature match?
|
87
|
+
# Return the result.
|
88
|
+
emsa_signature == emsa
|
89
|
+
end
|
90
|
+
|
91
|
+
# Decrypts the given data with the given private key.
|
92
|
+
def self.decrypt(private_key, data)
|
93
|
+
private_key = generate_key(private_key) unless private_key.is_a? RSA::Key
|
94
|
+
keypair = generate_keypair(nil, private_key)
|
95
|
+
keypair.decrypt(data)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Encrypts the given data with the given public key.
|
99
|
+
def self.encrypt(public_key, data)
|
100
|
+
public_key = generate_key(public_key) unless public_key.is_a? RSA::Key
|
101
|
+
keypair = generate_keypair(public_key, nil)
|
102
|
+
keypair.encrypt(data)
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# :nodoc:
|
108
|
+
def self.emsa_signature(text, key)
|
109
|
+
modulus_byte_count = key.modulus.size
|
110
|
+
|
111
|
+
plaintext = Digest::SHA2.new(256).digest(text)
|
112
|
+
|
113
|
+
prefix = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"
|
114
|
+
padding_count = modulus_byte_count - prefix.bytes.count - plaintext.bytes.count - 3
|
115
|
+
|
116
|
+
padding = ""
|
117
|
+
padding_count.times do
|
118
|
+
padding = padding + "\xff"
|
119
|
+
end
|
120
|
+
|
121
|
+
"\x00\x01#{padding}\x00#{prefix}#{plaintext}".force_encoding('binary')
|
122
|
+
end
|
123
|
+
|
124
|
+
# :nodoc:
|
125
|
+
def self.generate_key(key_string)
|
126
|
+
return nil unless key_string
|
127
|
+
|
128
|
+
key_string.match /^RSA\.(.*?)\.(.*)$/
|
129
|
+
|
130
|
+
modulus = decode_key($1)
|
131
|
+
exponent = decode_key($2)
|
132
|
+
|
133
|
+
RSA::Key.new(modulus, exponent)
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.generate_keypair(public_key, private_key)
|
137
|
+
RSA::KeyPair.new(private_key, public_key)
|
138
|
+
end
|
139
|
+
|
140
|
+
# :nodoc:
|
141
|
+
def self.decode_key(encoded_key_part)
|
142
|
+
modulus = Base64::urlsafe_decode64(encoded_key_part)
|
143
|
+
modulus.bytes.inject(0) {|num, byte| (num << 8) | byte }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/lib/lotus/feed.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'lotus/activity'
|
2
|
+
require 'lotus/author'
|
3
|
+
|
4
|
+
module Lotus
|
5
|
+
require 'atom'
|
6
|
+
|
7
|
+
# This class represents a Lotus::Feed object.
|
8
|
+
class Feed
|
9
|
+
require 'open-uri'
|
10
|
+
require 'date'
|
11
|
+
|
12
|
+
# Holds the id that uniquely represents this feed.
|
13
|
+
attr_reader :id
|
14
|
+
|
15
|
+
# Holds the url that represents this feed.
|
16
|
+
attr_reader :url
|
17
|
+
|
18
|
+
# Holds the list of categories for this feed as Lotus::Category.
|
19
|
+
attr_reader :categories
|
20
|
+
|
21
|
+
# Holds human-readable information about the content rights of the entries
|
22
|
+
# in the feed without an explicit rights field of their own. SHOULD NOT be
|
23
|
+
# machine interpreted.
|
24
|
+
attr_reader :rights
|
25
|
+
|
26
|
+
# Holds the title for this feed.
|
27
|
+
attr_reader :title
|
28
|
+
|
29
|
+
# Holds the content-type for the title.
|
30
|
+
attr_reader :title_type
|
31
|
+
|
32
|
+
# Holds the subtitle for this feed.
|
33
|
+
attr_reader :subtitle
|
34
|
+
|
35
|
+
# Holds the content-type for the subtitle.
|
36
|
+
attr_reader :subtitle_type
|
37
|
+
|
38
|
+
# Holds the URL for the icon representing this feed.
|
39
|
+
attr_reader :icon
|
40
|
+
|
41
|
+
# Holds the URL for the logo representing this feed.
|
42
|
+
attr_reader :logo
|
43
|
+
|
44
|
+
# Holds the generator for this content as an Lotus::Generator.
|
45
|
+
attr_reader :generator
|
46
|
+
|
47
|
+
# Holds the list of contributors, if any, that are involved in this feed
|
48
|
+
# as Lotus::Author.
|
49
|
+
attr_reader :contributors
|
50
|
+
|
51
|
+
# Holds the DateTime when this feed was originally created.
|
52
|
+
attr_reader :published
|
53
|
+
|
54
|
+
# Holds the DateTime when this feed was last modified.
|
55
|
+
attr_reader :updated
|
56
|
+
|
57
|
+
# Holds the list of authors as Lotus::Author responsible for this feed.
|
58
|
+
attr_reader :authors
|
59
|
+
|
60
|
+
# Holds the list of entries as Lotus::Activity contained within this feed.
|
61
|
+
attr_reader :entries
|
62
|
+
|
63
|
+
# Holds the list of hubs that are available to manage subscriptions to this
|
64
|
+
# feed.
|
65
|
+
attr_reader :hubs
|
66
|
+
|
67
|
+
# Holds the salmon url that handles notifications for this feed.
|
68
|
+
attr_reader :salmon_url
|
69
|
+
|
70
|
+
# Holds links to other resources as an array of Lotus::Link
|
71
|
+
attr_reader :links
|
72
|
+
|
73
|
+
# Creates a new representation of a feed.
|
74
|
+
#
|
75
|
+
# options:
|
76
|
+
# id => The unique identifier for this feed.
|
77
|
+
# url => The url that represents this feed.
|
78
|
+
# title => The title for this feed. Defaults: "Untitled"
|
79
|
+
# title_type => The content type for the title.
|
80
|
+
# subtitle => The subtitle for this feed.
|
81
|
+
# subtitle_type => The content type for the subtitle.
|
82
|
+
# authors => The list of Lotus::Author's for this feed.
|
83
|
+
# Defaults: []
|
84
|
+
# contributors => The list of Lotus::Author's that contributed to this
|
85
|
+
# feed. Defaults: []
|
86
|
+
# entries => The list of Lotus::Activity's for this feed.
|
87
|
+
# Defaults: []
|
88
|
+
# icon => The url of the icon that represents this feed. It
|
89
|
+
# should have an aspect ratio of 1 horizontal to 1
|
90
|
+
# vertical and optimized for presentation at a
|
91
|
+
# small size.
|
92
|
+
# logo => The url of the logo that represents this feed. It
|
93
|
+
# should have an aspect ratio of 2 horizontal to 1
|
94
|
+
# vertical.
|
95
|
+
# categories => An array of Lotus::Category's that describe how to
|
96
|
+
# categorize and describe the content of the feed.
|
97
|
+
# Defaults: []
|
98
|
+
# rights => A String depicting the rights of entries without
|
99
|
+
# explicit rights of their own. SHOULD NOT be machine
|
100
|
+
# interpreted.
|
101
|
+
# updated => The DateTime representing when this feed was last
|
102
|
+
# modified.
|
103
|
+
# published => The DateTime representing when this feed was originally
|
104
|
+
# published.
|
105
|
+
# salmon_url => The url of the salmon endpoint, if one exists, for this
|
106
|
+
# feed.
|
107
|
+
# links => An array of Lotus::Link that adds relations to other
|
108
|
+
# resources.
|
109
|
+
# generator => A Lotus::Generator representing the agent
|
110
|
+
# responsible for generating this feed.
|
111
|
+
#
|
112
|
+
# Usage:
|
113
|
+
#
|
114
|
+
# author = Lotus::Author.new(:name => "Kelly")
|
115
|
+
#
|
116
|
+
# feed = Lotus::Feed.new(:title => "My Feed",
|
117
|
+
# :id => "1",
|
118
|
+
# :url => "http://example.com/feeds/1",
|
119
|
+
# :authors => [author])
|
120
|
+
def initialize(options = {})
|
121
|
+
@id = options[:id]
|
122
|
+
@url = options[:url]
|
123
|
+
@icon = options[:icon]
|
124
|
+
@logo = options[:logo]
|
125
|
+
@rights = options[:rights]
|
126
|
+
@title = options[:title] || "Untitled"
|
127
|
+
@title_type = options[:title_type]
|
128
|
+
@subtitle = options[:subtitle]
|
129
|
+
@subtitle_type = options[:subtitle_type]
|
130
|
+
@authors = options[:authors] || []
|
131
|
+
@categories = options[:categories] || []
|
132
|
+
@contributors = options[:contributors] || []
|
133
|
+
@entries = options[:entries] || []
|
134
|
+
@updated = options[:updated]
|
135
|
+
@published = options[:published]
|
136
|
+
@salmon_url = options[:salmon_url]
|
137
|
+
@hubs = options[:hubs] || []
|
138
|
+
@generator = options[:generator]
|
139
|
+
end
|
140
|
+
|
141
|
+
# Yields a Lotus::Link to this feed.
|
142
|
+
#
|
143
|
+
# options: Can override Lotus::Link properties, such as rel.
|
144
|
+
#
|
145
|
+
# Usage:
|
146
|
+
#
|
147
|
+
# feed = Lotus::Feed.new(:title => "Foo", :url => "http://example.com")
|
148
|
+
# feed.to_link(:rel => "alternate", :title => "Foo's Feed")
|
149
|
+
#
|
150
|
+
# Generates a link with:
|
151
|
+
# <Lotus::Link rel="alternate" title="Foo's Feed" url="http://example.com">
|
152
|
+
def to_link(options = {})
|
153
|
+
options = { :title => self.title,
|
154
|
+
:href => self.url }.merge(options)
|
155
|
+
|
156
|
+
Lotus::Link.new(options)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns a hash of the properties of the feed.
|
160
|
+
def to_hash
|
161
|
+
{
|
162
|
+
:id => self.id,
|
163
|
+
:url => self.url,
|
164
|
+
:hubs => self.hubs.dup,
|
165
|
+
:icon => self.icon,
|
166
|
+
:logo => self.logo,
|
167
|
+
:rights => self.rights,
|
168
|
+
:title => self.title,
|
169
|
+
:title_type => self.title_type,
|
170
|
+
:subtitle => self.subtitle,
|
171
|
+
:subtitle_type => self.subtitle_type,
|
172
|
+
:authors => self.authors.dup,
|
173
|
+
:categories => self.categories.dup,
|
174
|
+
:contributors => self.contributors.dup,
|
175
|
+
:entries => self.entries.dup,
|
176
|
+
:updated => self.updated,
|
177
|
+
:salmon_url => self.salmon_url,
|
178
|
+
:published => self.published,
|
179
|
+
:generator => self.generator
|
180
|
+
}
|
181
|
+
end
|
182
|
+
|
183
|
+
# Returns a string containing an Atom representation of the feed.
|
184
|
+
def to_atom
|
185
|
+
require 'lotus/atom/feed'
|
186
|
+
|
187
|
+
Lotus::Atom::Feed.from_canonical(self).to_xml
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|