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
@@ -0,0 +1,134 @@
|
|
1
|
+
module Lotus
|
2
|
+
# This class represents an Activity object for an Lotus::Entry.
|
3
|
+
class Activity
|
4
|
+
# The object of this activity.
|
5
|
+
attr_reader :object
|
6
|
+
|
7
|
+
# The type of object for this activity.
|
8
|
+
#
|
9
|
+
# The field can be a String for uncommon types. Several are standard:
|
10
|
+
# :article, :audio, :bookmark, :comment, :file, :folder, :group,
|
11
|
+
# :list, :note, :person, :photo, :"photo-album", :place, :playlist,
|
12
|
+
# :product, :review, :service, :status, :video
|
13
|
+
attr_reader :type
|
14
|
+
|
15
|
+
# The action being invoked in this activity.
|
16
|
+
#
|
17
|
+
# The field can be a String for uncommon verbs. Several are standard:
|
18
|
+
# :favorite, :follow, :like, :"make-friend", :join, :play,
|
19
|
+
# :post, :save, :share, :tag, :update
|
20
|
+
attr_reader :verb
|
21
|
+
|
22
|
+
# The target of the action.
|
23
|
+
attr_reader :target
|
24
|
+
|
25
|
+
# Holds a String containing the title of the entry.
|
26
|
+
attr_reader :title
|
27
|
+
|
28
|
+
# Holds an Lotus::Author.
|
29
|
+
attr_reader :actor
|
30
|
+
|
31
|
+
# Holds the content.
|
32
|
+
attr_reader :content
|
33
|
+
|
34
|
+
# Holds the MIME type of the content.
|
35
|
+
attr_reader :content_type
|
36
|
+
|
37
|
+
# Holds the id that uniquely identifies this entry.
|
38
|
+
attr_reader :id
|
39
|
+
|
40
|
+
# Holds the url that represents the entry.
|
41
|
+
attr_reader :url
|
42
|
+
|
43
|
+
# Holds the source of this entry as an Lotus::Feed.
|
44
|
+
attr_reader :source
|
45
|
+
|
46
|
+
# Holds the DateTime of when the entry was published originally.
|
47
|
+
attr_reader :published
|
48
|
+
|
49
|
+
# Holds the DateTime of when the entry was last modified.
|
50
|
+
attr_reader :updated
|
51
|
+
|
52
|
+
# Holds an array of related Lotus::Entry's that this entry is a response
|
53
|
+
# to.
|
54
|
+
attr_reader :in_reply_to
|
55
|
+
|
56
|
+
# Create a new entry with the given content.
|
57
|
+
#
|
58
|
+
# options:
|
59
|
+
# :object => The object of this activity.
|
60
|
+
# :type => The type of object for this activity.
|
61
|
+
# :target => The target of this activity.
|
62
|
+
# :verb => The action of the activity.
|
63
|
+
#
|
64
|
+
# :title => The title of the entry. Defaults: "Untitled"
|
65
|
+
# :actor => An Lotus::Author responsible for generating this entry.
|
66
|
+
# :content => The content of the entry. Defaults: ""
|
67
|
+
# :content_type => The MIME type of the content.
|
68
|
+
# :source => An Lotus::Feed where this Entry originated. This
|
69
|
+
# should be used when an Entry is copied into this feed
|
70
|
+
# from another.
|
71
|
+
# :published => The DateTime depicting when the entry was originally
|
72
|
+
# published.
|
73
|
+
# :updated => The DateTime depicting when the entry was modified.
|
74
|
+
# :url => The canonical url of the entry.
|
75
|
+
# :id => The unique id that identifies this entry.
|
76
|
+
# :activity => The activity this entry represents. Either a single string
|
77
|
+
# denoting what type of object this entry represents, or an
|
78
|
+
# entire Lotus::Activity when a more detailed description is
|
79
|
+
# appropriate.
|
80
|
+
# :in_reply_to => An Lotus::Entry for which this entry is a response.
|
81
|
+
# Or an array of Lotus::Entry's that this entry is a
|
82
|
+
# response to. Use this when this Entry is a reply
|
83
|
+
# to an existing Entry.
|
84
|
+
def initialize(options = {})
|
85
|
+
@object = options[:object]
|
86
|
+
@type = options[:type]
|
87
|
+
@target = options[:target]
|
88
|
+
@verb = options[:verb]
|
89
|
+
|
90
|
+
@title = options[:title] || "Untitled"
|
91
|
+
@actor = options[:actor]
|
92
|
+
@content = options[:content] || ""
|
93
|
+
@content_type = options[:content_type]
|
94
|
+
@source = options[:source]
|
95
|
+
@published = options[:published]
|
96
|
+
@updated = options[:updated]
|
97
|
+
@url = options[:url]
|
98
|
+
@id = options[:id]
|
99
|
+
|
100
|
+
unless options[:in_reply_to].nil? or options[:in_reply_to].is_a?(Array)
|
101
|
+
options[:in_reply_to] = [options[:in_reply_to]]
|
102
|
+
end
|
103
|
+
@in_reply_to = options[:in_reply_to] || []
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns a hash of all relevant fields.
|
107
|
+
def to_hash
|
108
|
+
{
|
109
|
+
:source => self.source,
|
110
|
+
:title => self.title,
|
111
|
+
:content => self.content,
|
112
|
+
:content_type => self.content_type,
|
113
|
+
:published => self.published,
|
114
|
+
:updated => self.updated,
|
115
|
+
:url => self.url,
|
116
|
+
:id => self.id,
|
117
|
+
:in_reply_to => self.in_reply_to.dup,
|
118
|
+
|
119
|
+
:object => self.object,
|
120
|
+
:target => self.target,
|
121
|
+
:actor => self.actor,
|
122
|
+
:verb => self.verb,
|
123
|
+
:type => self.type
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns a string containing the Atom representation of this Activity.
|
128
|
+
def to_atom
|
129
|
+
require 'lotus/atom/entry'
|
130
|
+
|
131
|
+
Lotus::Atom::Entry.from_canonical(self).to_xml
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Lotus
|
2
|
+
require 'atom'
|
3
|
+
|
4
|
+
module Atom
|
5
|
+
# This class represents an PortableContacts Account object.
|
6
|
+
class Account
|
7
|
+
include ::Atom::Xml::Parseable
|
8
|
+
|
9
|
+
# The XML namespace the specifies this content.
|
10
|
+
POCO_NAMESPACE = 'http://portablecontacts.net/spec/1.0'
|
11
|
+
|
12
|
+
namespace POCO_NAMESPACE
|
13
|
+
|
14
|
+
element :domain
|
15
|
+
element :username
|
16
|
+
element :userid
|
17
|
+
|
18
|
+
def initialize(o = {})
|
19
|
+
case o
|
20
|
+
when XML::Reader
|
21
|
+
o.read
|
22
|
+
parse(o)
|
23
|
+
when Hash
|
24
|
+
o.each do |k, v|
|
25
|
+
if k.to_s.include? '_'
|
26
|
+
k = k.to_s.gsub(/_(.)/){"#{$1.upcase}"}.intern
|
27
|
+
end
|
28
|
+
self.send("#{k.to_s}=", v)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Got #{o.class} but expected a Hash or XML::Reader"
|
32
|
+
end
|
33
|
+
|
34
|
+
yield(self) if block_given?
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_hash
|
38
|
+
{
|
39
|
+
:domain => self.domain,
|
40
|
+
:username => self.username,
|
41
|
+
:userid => self.userid
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_canonical
|
46
|
+
to_hash
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Lotus
|
2
|
+
require 'atom'
|
3
|
+
|
4
|
+
module Atom
|
5
|
+
# This class represents an OStatus PortableContacts Address object.
|
6
|
+
class Address
|
7
|
+
include ::Atom::Xml::Parseable
|
8
|
+
|
9
|
+
# The XML namespace the specifies this content.
|
10
|
+
POCO_NAMESPACE = 'http://portablecontacts.net/spec/1.0'
|
11
|
+
|
12
|
+
namespace POCO_NAMESPACE
|
13
|
+
|
14
|
+
element :formatted
|
15
|
+
element :streetAddress
|
16
|
+
element :locality
|
17
|
+
element :region
|
18
|
+
element :postalCode
|
19
|
+
element :country
|
20
|
+
|
21
|
+
def initialize(o = {})
|
22
|
+
case o
|
23
|
+
when XML::Reader
|
24
|
+
o.read
|
25
|
+
parse(o, :test=>true)
|
26
|
+
when Hash
|
27
|
+
o.each do |k, v|
|
28
|
+
if k.to_s.include? '_'
|
29
|
+
k = k.to_s.gsub(/_(.)/){"#{$1.upcase}"}.intern
|
30
|
+
end
|
31
|
+
self.send("#{k.to_s}=", v)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
raise ArgumentError, "Got #{o.class} but expected a Hash or XML::Reader"
|
35
|
+
end
|
36
|
+
|
37
|
+
yield(self) if block_given?
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_hash
|
41
|
+
{
|
42
|
+
:formatted => self.formatted,
|
43
|
+
:street_address => self.streetAddress,
|
44
|
+
:locality => self.locality,
|
45
|
+
:region => self.region,
|
46
|
+
:postal_code => self.postalCode,
|
47
|
+
:country => self.country
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_canonical
|
52
|
+
to_hash
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'lotus/activity'
|
2
|
+
require 'lotus/atom/name'
|
3
|
+
require 'lotus/atom/address'
|
4
|
+
require 'lotus/atom/account'
|
5
|
+
require 'lotus/atom/organization'
|
6
|
+
|
7
|
+
module Lotus
|
8
|
+
require 'atom'
|
9
|
+
|
10
|
+
module Atom
|
11
|
+
# Holds information about the author of the Feed.
|
12
|
+
class Author < ::Atom::Person
|
13
|
+
require 'date'
|
14
|
+
|
15
|
+
include ::Atom::SimpleExtensions
|
16
|
+
|
17
|
+
# The XML namespace the specifies this content.
|
18
|
+
POCO_NAMESPACE = 'http://portablecontacts.net/spec/1.0'
|
19
|
+
|
20
|
+
# The XML namespace that identifies the conforming specification.
|
21
|
+
ACTIVITY_NAMESPACE = 'http://activitystrea.ms/spec/1.0/'
|
22
|
+
|
23
|
+
add_extension_namespace :activity, ACTIVITY_NAMESPACE
|
24
|
+
element 'activity:object-type'
|
25
|
+
|
26
|
+
namespace ::Atom::NAMESPACE
|
27
|
+
element :email
|
28
|
+
element :uri
|
29
|
+
|
30
|
+
elements :links, :class => ::Atom::Link
|
31
|
+
|
32
|
+
add_extension_namespace :poco, POCO_NAMESPACE
|
33
|
+
element 'poco:id'
|
34
|
+
element 'poco:organization', :class => Lotus::Atom::Organization
|
35
|
+
element 'poco:address', :class => Lotus::Atom::Address
|
36
|
+
element 'poco:account', :class => Lotus::Atom::Account
|
37
|
+
element 'poco:displayName'
|
38
|
+
element 'poco:nickname'
|
39
|
+
element 'poco:updated', :class => DateTime, :content_only => true
|
40
|
+
element 'poco:published', :class => DateTime, :content_only => true
|
41
|
+
element 'poco:birthday', :class => Date, :content_only => true
|
42
|
+
element 'poco:anniversary', :class => Date, :content_only => true
|
43
|
+
element 'poco:gender'
|
44
|
+
element 'poco:note'
|
45
|
+
element 'poco:preferredUsername'
|
46
|
+
|
47
|
+
# unfortunately ratom doesn't handle elements with the same local name well.
|
48
|
+
# this is a workaround for that.
|
49
|
+
attr_writer :name, :poco_name
|
50
|
+
|
51
|
+
def name
|
52
|
+
@name or self[::Atom::NAMESPACE, 'name'].first
|
53
|
+
end
|
54
|
+
|
55
|
+
def poco_name
|
56
|
+
return @poco_name if @poco_name
|
57
|
+
name = self[POCO_NAMESPACE, 'name'].first
|
58
|
+
if name
|
59
|
+
name = "<name>#{name}</name>"
|
60
|
+
reader = XML::Reader.string(name)
|
61
|
+
reader.read
|
62
|
+
reader.read
|
63
|
+
Lotus::Atom::Name.new(reader)
|
64
|
+
else
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_xml(*args)
|
70
|
+
x = super(*args)
|
71
|
+
|
72
|
+
if self.name
|
73
|
+
node = XML::Node.new('name')
|
74
|
+
node << self.name
|
75
|
+
x << node
|
76
|
+
end
|
77
|
+
|
78
|
+
if self.poco_name
|
79
|
+
x << self.poco_name.to_xml(true, root_name = 'poco:name')
|
80
|
+
end
|
81
|
+
|
82
|
+
x
|
83
|
+
end
|
84
|
+
|
85
|
+
def initialize *args
|
86
|
+
self.activity_object_type = "http://activitystrea.ms/schema/1.0/person"
|
87
|
+
super(*args)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Gives an instance of an Lotus::Activity that parses the fields
|
91
|
+
# having an activity prefix.
|
92
|
+
def activity
|
93
|
+
Lotus::Activity.new(self)
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.from_canonical(obj)
|
97
|
+
hash = obj.to_hash
|
98
|
+
hash.keys.each do |k|
|
99
|
+
to_k = k
|
100
|
+
if k == :display_name
|
101
|
+
to_k = :displayName
|
102
|
+
elsif k == :preferred_username
|
103
|
+
to_k = :preferredUsername
|
104
|
+
end
|
105
|
+
|
106
|
+
if k == :extended_name
|
107
|
+
if hash[:extended_name]
|
108
|
+
hash[:"poco_name"] = Lotus::Atom::Name.new(hash[:extended_name])
|
109
|
+
end
|
110
|
+
hash.delete :extended_name
|
111
|
+
elsif k == :organization
|
112
|
+
if hash[:organization]
|
113
|
+
hash[:"poco_organization"] = Lotus::Atom::Organization.new(hash[:organization])
|
114
|
+
end
|
115
|
+
hash.delete :organization
|
116
|
+
elsif k == :address
|
117
|
+
if hash[:address]
|
118
|
+
hash[:"poco_address"] = Lotus::Atom::Address.new(hash[:address])
|
119
|
+
end
|
120
|
+
hash.delete :address
|
121
|
+
elsif k == :account
|
122
|
+
if hash[:account]
|
123
|
+
hash[:"poco_account"] = Lotus::Atom::Account.new(hash[:account])
|
124
|
+
end
|
125
|
+
hash.delete :account
|
126
|
+
elsif (k != :uri) && (k != :name) && (k != :email)
|
127
|
+
hash[:"poco_#{to_k}"] = hash[k]
|
128
|
+
hash.delete k
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
self.new(hash)
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_canonical
|
136
|
+
organization = self.poco_organization
|
137
|
+
organization = organization.to_canonical if organization
|
138
|
+
|
139
|
+
address = self.poco_address
|
140
|
+
address = address.to_canonical if address
|
141
|
+
|
142
|
+
account = self.poco_account
|
143
|
+
account = account.to_canonical if account
|
144
|
+
|
145
|
+
ext_name = self.poco_name
|
146
|
+
ext_name = ext_name.to_canonical if ext_name
|
147
|
+
Lotus::Author.new(:id => self.poco_id,
|
148
|
+
:extended_name => ext_name,
|
149
|
+
:organization => organization,
|
150
|
+
:address => address,
|
151
|
+
:account => account,
|
152
|
+
:gender => self.poco_gender,
|
153
|
+
:note => self.poco_note,
|
154
|
+
:nickname => self.poco_nickname,
|
155
|
+
:display_name => self.poco_displayName,
|
156
|
+
:preferred_username => self.poco_preferredUsername,
|
157
|
+
:updated => self.poco_updated,
|
158
|
+
:published => self.poco_published,
|
159
|
+
:birthday => self.poco_birthday,
|
160
|
+
:anniversary => self.poco_anniversary,
|
161
|
+
:uri => self.uri,
|
162
|
+
:email => self.email,
|
163
|
+
:name => self.name)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'lotus/category'
|
2
|
+
|
3
|
+
module Lotus
|
4
|
+
require 'atom'
|
5
|
+
|
6
|
+
module Atom
|
7
|
+
# This class represents an Lotus Category object.
|
8
|
+
class Category < ::Atom::Category
|
9
|
+
require 'open-uri'
|
10
|
+
|
11
|
+
attribute :'xml:base'
|
12
|
+
attribute :'xml:lang'
|
13
|
+
attribute :scheme
|
14
|
+
attribute :term
|
15
|
+
attribute :label
|
16
|
+
|
17
|
+
def self.from_canonical(obj)
|
18
|
+
hash = obj.to_hash
|
19
|
+
if hash[:base]
|
20
|
+
hash[:xml_base] = hash[:base]
|
21
|
+
end
|
22
|
+
if hash[:lang]
|
23
|
+
hash[:xml_lang] = hash[:lang]
|
24
|
+
end
|
25
|
+
hash.delete :base
|
26
|
+
hash.delete :lang
|
27
|
+
self.new(hash)
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_canonical
|
31
|
+
Lotus::Category.new(:base => self.xml_base,
|
32
|
+
:lang => self.xml_lang,
|
33
|
+
:scheme => self.scheme,
|
34
|
+
:lable => self.label,
|
35
|
+
:term => self.term)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|