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,57 @@
|
|
1
|
+
module Lotus
|
2
|
+
require 'atom'
|
3
|
+
|
4
|
+
module Atom
|
5
|
+
# This class represents an PortableContacts Name object.
|
6
|
+
class Name
|
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 :familyName
|
16
|
+
element :givenName
|
17
|
+
element :middleName
|
18
|
+
element :honorificPrefix
|
19
|
+
element :honorificSuffix
|
20
|
+
|
21
|
+
def initialize(o = {})
|
22
|
+
case o
|
23
|
+
when XML::Reader
|
24
|
+
parse(o, :test => true)
|
25
|
+
when Hash
|
26
|
+
o.each do |k, v|
|
27
|
+
if k.to_s.include? '_'
|
28
|
+
k = k.to_s.gsub(/_(.)/){"#{$1.upcase}"}.intern
|
29
|
+
end
|
30
|
+
self.send("#{k.to_s}=", v)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
raise ArgumentError, "Got #{o.class} but expected a Hash or XML::Reader"
|
34
|
+
end
|
35
|
+
|
36
|
+
yield(self) if block_given?
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_hash
|
40
|
+
{
|
41
|
+
:formatted => self.formatted,
|
42
|
+
:family_name => self.familyName,
|
43
|
+
:given_name => self.givenName,
|
44
|
+
:middle_name => self.middleName,
|
45
|
+
:honorific_prefix => self.honorificPrefix,
|
46
|
+
:honorific_suffix => self.honorificSuffix
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_canonical
|
51
|
+
to_hash
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Lotus
|
2
|
+
require 'atom'
|
3
|
+
|
4
|
+
module Atom
|
5
|
+
# This class represents an PortableContacts Organization object.
|
6
|
+
class Organization
|
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 :name
|
15
|
+
element :department
|
16
|
+
element :title
|
17
|
+
element :type
|
18
|
+
element :startDate, :class => Date, :content_only => true
|
19
|
+
element :endDate, :class => Date, :content_only => true
|
20
|
+
element :location
|
21
|
+
element :description
|
22
|
+
|
23
|
+
def initialize(o = {})
|
24
|
+
case o
|
25
|
+
when XML::Reader
|
26
|
+
o.read
|
27
|
+
parse(o)
|
28
|
+
when Hash
|
29
|
+
o.each do |k, v|
|
30
|
+
if k.to_s.include? '_'
|
31
|
+
k = k.to_s.gsub(/_(.)/){"#{$1.upcase}"}.intern
|
32
|
+
end
|
33
|
+
self.send("#{k.to_s}=", v)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
raise ArgumentError, "Got #{o.class} but expected a Hash or XML::Reader"
|
37
|
+
end
|
38
|
+
|
39
|
+
yield(self) if block_given?
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_hash
|
43
|
+
{
|
44
|
+
:name => self.name,
|
45
|
+
:department => self.department,
|
46
|
+
:title => self.title,
|
47
|
+
:type => self.type,
|
48
|
+
:start_date => self.startDate,
|
49
|
+
:end_date => self.endDate,
|
50
|
+
:location => self.location,
|
51
|
+
:description => self.description
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_canonical
|
56
|
+
to_hash
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Lotus
|
2
|
+
module Atom
|
3
|
+
# Holds information about the extended contact information
|
4
|
+
# in the Feed given in the Portable Contacts specification.
|
5
|
+
class PortableContacts
|
6
|
+
# Instantiates a Lotus::PortableContacts object from either
|
7
|
+
# a given root that contains all <poco:*> tags as an ratom Person
|
8
|
+
# or a Hash containing the properties.
|
9
|
+
def initialize(parent)
|
10
|
+
if parent.is_a? Hash
|
11
|
+
@options = parent
|
12
|
+
else
|
13
|
+
@parent = parent
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def id; get_prop(:id); end
|
18
|
+
def id= value; set_prop(:id, value); end
|
19
|
+
|
20
|
+
def name; get_prop(:name); end
|
21
|
+
def name= value; set_prop(:name, value); end
|
22
|
+
|
23
|
+
def nickname; get_prop(:nickname); end
|
24
|
+
def nickname= value; set_prop(:nickname, value); end
|
25
|
+
|
26
|
+
def gender; get_prop(:gender); end
|
27
|
+
def gender= value; set_prop(:gender, value); end
|
28
|
+
|
29
|
+
def note; get_prop(:note); end
|
30
|
+
def note= value; set_prop(:note, value); end
|
31
|
+
|
32
|
+
def display_name; get_prop(:display_name, 'displayName'); end
|
33
|
+
def display_name= value; set_prop(:display_name, value, 'displayName'); end
|
34
|
+
|
35
|
+
def preferred_username
|
36
|
+
get_prop(:preferred_username, 'preferredUsername')
|
37
|
+
end
|
38
|
+
|
39
|
+
def preferred_username= value
|
40
|
+
set_prop(:preferred_username, value, 'preferredUsername')
|
41
|
+
end
|
42
|
+
|
43
|
+
def updated; get_datetime(:updated); end
|
44
|
+
def published; get_datetime(:published); end
|
45
|
+
|
46
|
+
def birthday; get_date(:birthday); end
|
47
|
+
def anniversary; get_date(:anniversary); end
|
48
|
+
|
49
|
+
# Returns a boolean that indicates that a bi-directional connection
|
50
|
+
# has been established between the user and the contact, if it is
|
51
|
+
# able to assert this.
|
52
|
+
def connected
|
53
|
+
return @options[:connected] unless @options.nil?
|
54
|
+
str = @parent.poco_connected
|
55
|
+
|
56
|
+
if str == "true"
|
57
|
+
true
|
58
|
+
elsif str == "false"
|
59
|
+
false
|
60
|
+
else
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def get_prop name, xmlName = name
|
68
|
+
@options ? @options[name] : @parent.send("poco_#{xmlName}")
|
69
|
+
end
|
70
|
+
|
71
|
+
def set_prop name, value, xmlName = name
|
72
|
+
if @options
|
73
|
+
@options[name] = value
|
74
|
+
else
|
75
|
+
@parent.send("poco_#{xmlName}=", value)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_datetime x
|
80
|
+
if @options
|
81
|
+
dt = @options[x]
|
82
|
+
DateTime.parse(dt) if dt
|
83
|
+
else
|
84
|
+
@parent.send("poco_#{x}")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_date x
|
89
|
+
if @options
|
90
|
+
d = @options[x]
|
91
|
+
Date.parse(d) if d
|
92
|
+
else
|
93
|
+
@parent.send("poco_#{x}")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_canonical
|
98
|
+
Lotus::PortableContacts.new(:id => self.id,
|
99
|
+
:name => self.name,
|
100
|
+
:gender => self.gender,
|
101
|
+
:note => self.note,
|
102
|
+
:nickname => self.nickname,
|
103
|
+
:display_name => self.display_name,
|
104
|
+
:preferred_username => self.preferred_username,
|
105
|
+
:updated => self.updated,
|
106
|
+
:published => self.published,
|
107
|
+
:birthday => self.birthday,
|
108
|
+
:anniversary => self.anniversary,
|
109
|
+
|
110
|
+
:name => self.name,
|
111
|
+
:organization => self.organization,
|
112
|
+
:account => self.account,
|
113
|
+
:address => self.address)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'lotus/author'
|
2
|
+
require 'lotus/category'
|
3
|
+
require 'lotus/generator'
|
4
|
+
|
5
|
+
require 'lotus/atom/generator'
|
6
|
+
require 'lotus/atom/category'
|
7
|
+
require 'lotus/atom/author'
|
8
|
+
require 'lotus/atom/link'
|
9
|
+
|
10
|
+
module Lotus
|
11
|
+
require 'atom'
|
12
|
+
|
13
|
+
module Atom
|
14
|
+
# This class represents an OStatus Feed object.
|
15
|
+
class Source < ::Atom::Source
|
16
|
+
require 'open-uri'
|
17
|
+
|
18
|
+
include ::Atom::SimpleExtensions
|
19
|
+
|
20
|
+
# The XML namespace that identifies the conforming specification.
|
21
|
+
ACTIVITY_NAMESPACE = 'http://activitystrea.ms/spec/1.0/'
|
22
|
+
|
23
|
+
# The XML namespace the specifies this content.
|
24
|
+
POCO_NAMESPACE = 'http://portablecontacts.net/spec/1.0'
|
25
|
+
|
26
|
+
namespace ::Atom::NAMESPACE
|
27
|
+
|
28
|
+
add_extension_namespace :poco, POCO_NAMESPACE
|
29
|
+
add_extension_namespace :activity, ACTIVITY_NAMESPACE
|
30
|
+
element :id, :rights, :icon, :logo
|
31
|
+
element :generator, :class => Lotus::Atom::Generator
|
32
|
+
element :title, :class => ::Atom::Content
|
33
|
+
element :subtitle, :class => ::Atom::Content
|
34
|
+
element :published, :class => Time, :content_only => true
|
35
|
+
element :updated, :class => Time, :content_only => true
|
36
|
+
elements :links, :class => ::Atom::Link
|
37
|
+
elements :authors, :class => Lotus::Atom::Author
|
38
|
+
elements :contributors, :class => Lotus::Atom::Author
|
39
|
+
elements :categories, :class => Lotus::Atom::Category
|
40
|
+
|
41
|
+
# Creates an Atom generator for the given Lotus::Feed.
|
42
|
+
def self.from_canonical(obj)
|
43
|
+
hash = obj.to_hash
|
44
|
+
hash.delete :entries
|
45
|
+
|
46
|
+
# Ensure that the generator is encoded.
|
47
|
+
if hash[:generator]
|
48
|
+
hash[:generator] = Lotus::Atom::Generator.from_canonical(hash[:generator])
|
49
|
+
end
|
50
|
+
|
51
|
+
hash[:links] ||= []
|
52
|
+
|
53
|
+
if hash[:salmon_url]
|
54
|
+
hash[:links] << ::Atom::Link.new(:rel => "salmon", :href => hash[:salmon_url])
|
55
|
+
end
|
56
|
+
hash.delete :salmon_url
|
57
|
+
|
58
|
+
if hash[:url]
|
59
|
+
hash[:links] << ::Atom::Link.new(:rel => "self", :href => hash[:url])
|
60
|
+
end
|
61
|
+
hash.delete :url
|
62
|
+
|
63
|
+
hash[:hubs].each {|h|
|
64
|
+
hash[:links] << ::Atom::Link.new(:rel => "hub", :href => h)
|
65
|
+
}
|
66
|
+
hash.delete :hubs
|
67
|
+
|
68
|
+
hash[:authors].map! {|a|
|
69
|
+
Lotus::Atom::Author.from_canonical(a)
|
70
|
+
}
|
71
|
+
|
72
|
+
hash[:contributors].map! {|a|
|
73
|
+
Lotus::Atom::Author.from_canonical(a)
|
74
|
+
}
|
75
|
+
|
76
|
+
hash[:categories].map! {|c|
|
77
|
+
Lotus::Atom::Category.from_canonical(c)
|
78
|
+
}
|
79
|
+
|
80
|
+
# title/subtitle content type
|
81
|
+
node = XML::Node.new("title")
|
82
|
+
node['type'] = hash[:title_type] if hash[:title_type]
|
83
|
+
node << hash[:title]
|
84
|
+
|
85
|
+
xml = XML::Reader.string(node.to_s)
|
86
|
+
xml.read
|
87
|
+
hash[:title] = ::Atom::Content.parse(xml)
|
88
|
+
hash.delete :title_type
|
89
|
+
|
90
|
+
if hash[:subtitle]
|
91
|
+
node = XML::Node.new("subtitle")
|
92
|
+
node['type'] = hash[:subtitle_type] if hash[:subtitle_type]
|
93
|
+
node << hash[:subtitle]
|
94
|
+
|
95
|
+
xml = XML::Reader.string(node.to_s)
|
96
|
+
xml.read
|
97
|
+
hash[:subtitle] = ::Atom::Content.parse(xml)
|
98
|
+
else
|
99
|
+
hash.delete :subtitle
|
100
|
+
end
|
101
|
+
hash.delete :subtitle_type
|
102
|
+
|
103
|
+
self.new(hash)
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_canonical
|
107
|
+
generator = nil
|
108
|
+
generator = self.generator.to_canonical if self.generator
|
109
|
+
|
110
|
+
salmon_url = nil
|
111
|
+
if self.link('salmon').any?
|
112
|
+
salmon_url = self.link('salmon').first.href
|
113
|
+
end
|
114
|
+
|
115
|
+
url = self.url
|
116
|
+
|
117
|
+
categories = self.categories.map(&:to_canonical)
|
118
|
+
|
119
|
+
Lotus::Feed.new(:title => self.title,
|
120
|
+
:title_type => self.title ? self.title.type : nil,
|
121
|
+
:subtitle => self.subtitle,
|
122
|
+
:subtitle_type => self.subtitle ? self.subtitle.type : nil,
|
123
|
+
:id => self.id,
|
124
|
+
:url => url,
|
125
|
+
:categories => categories,
|
126
|
+
:icon => self.icon,
|
127
|
+
:logo => self.logo,
|
128
|
+
:rights => self.rights,
|
129
|
+
:published => self.published,
|
130
|
+
:updated => self.updated,
|
131
|
+
:authors => self.authors.map(&:to_canonical),
|
132
|
+
:contributors => self.contributors.map(&:to_canonical),
|
133
|
+
:hubs => self.hubs,
|
134
|
+
:salmon_url => salmon_url,
|
135
|
+
:generator => generator)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Returns an array of ::Atom::Link instances for all link tags
|
139
|
+
# that have a rel equal to that given by attribute.
|
140
|
+
#
|
141
|
+
# For example:
|
142
|
+
# link(:hub).first.href -- Gets the first link tag with rel="hub" and
|
143
|
+
# returns the contents of the href attribute.
|
144
|
+
#
|
145
|
+
def link(attribute)
|
146
|
+
links.find_all { |l| l.rel == attribute.to_s }
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns an array of URLs for each hub link tag.
|
150
|
+
def hubs
|
151
|
+
link('hub').map { |link| link.href }
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns a string of the url for this feed.
|
155
|
+
def url
|
156
|
+
if links.alternate
|
157
|
+
links.alternate.href
|
158
|
+
elsif links.self
|
159
|
+
links.self.href
|
160
|
+
else
|
161
|
+
links.map.each do |l|
|
162
|
+
l.href
|
163
|
+
end.compact.first
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Lotus
|
2
|
+
module Atom
|
3
|
+
# This will parse the Thread Atom extension
|
4
|
+
class Thread
|
5
|
+
require 'xml/libxml'
|
6
|
+
require 'atom/xml/parser.rb'
|
7
|
+
|
8
|
+
include ::Atom::Xml::Parseable
|
9
|
+
attribute :ref, :type, :source
|
10
|
+
uri_attribute :href
|
11
|
+
|
12
|
+
def initialize(o)
|
13
|
+
case o
|
14
|
+
when XML::Reader
|
15
|
+
if current_node_is?(o, 'in-reply-to')
|
16
|
+
parse(o, :once => true)
|
17
|
+
else
|
18
|
+
raise ArgumentError, "Thread created with node other than thr:in-reply-to: #{o.name}"
|
19
|
+
end
|
20
|
+
when Hash
|
21
|
+
[:href, :ref, :type, :source].each do |attr|
|
22
|
+
self.send("#{attr}=", o[attr])
|
23
|
+
end
|
24
|
+
else
|
25
|
+
raise ArgumentError, "Don't know how to handle #{o}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def length=(v)
|
30
|
+
@length = v.to_i
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
self.href
|
35
|
+
end
|
36
|
+
|
37
|
+
def ==(o)
|
38
|
+
o.respond_to?(:href) && o.href == self.href
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.from_canonical(obj)
|
42
|
+
self.new(obj.to_hash)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_canonical
|
46
|
+
Lotus::Activity.new(:url => self.href,
|
47
|
+
:id => self.ref)
|
48
|
+
end
|
49
|
+
|
50
|
+
def info
|
51
|
+
{
|
52
|
+
:ref => self.ref,
|
53
|
+
:type => self.type,
|
54
|
+
:source => self.source,
|
55
|
+
:href => self.href
|
56
|
+
}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|