lotus 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +9 -0
  3. data/Gemfile +16 -0
  4. data/README.md +233 -0
  5. data/Rakefile +7 -0
  6. data/lib/lotus.rb +232 -0
  7. data/lib/lotus/activity.rb +134 -0
  8. data/lib/lotus/atom/account.rb +50 -0
  9. data/lib/lotus/atom/address.rb +56 -0
  10. data/lib/lotus/atom/author.rb +167 -0
  11. data/lib/lotus/atom/category.rb +41 -0
  12. data/lib/lotus/atom/entry.rb +159 -0
  13. data/lib/lotus/atom/feed.rb +174 -0
  14. data/lib/lotus/atom/generator.rb +40 -0
  15. data/lib/lotus/atom/link.rb +79 -0
  16. data/lib/lotus/atom/name.rb +57 -0
  17. data/lib/lotus/atom/organization.rb +62 -0
  18. data/lib/lotus/atom/portable_contacts.rb +117 -0
  19. data/lib/lotus/atom/source.rb +168 -0
  20. data/lib/lotus/atom/thread.rb +60 -0
  21. data/lib/lotus/author.rb +177 -0
  22. data/lib/lotus/category.rb +45 -0
  23. data/lib/lotus/crypto.rb +146 -0
  24. data/lib/lotus/feed.rb +190 -0
  25. data/lib/lotus/generator.rb +53 -0
  26. data/lib/lotus/identity.rb +59 -0
  27. data/lib/lotus/link.rb +56 -0
  28. data/lib/lotus/notification.rb +220 -0
  29. data/lib/lotus/publisher.rb +40 -0
  30. data/lib/lotus/subscription.rb +117 -0
  31. data/lib/lotus/version.rb +3 -0
  32. data/lotus.gemspec +27 -0
  33. data/spec/activity_spec.rb +84 -0
  34. data/spec/atom/feed_spec.rb +681 -0
  35. data/spec/author_spec.rb +150 -0
  36. data/spec/crypto_spec.rb +138 -0
  37. data/spec/feed_spec.rb +252 -0
  38. data/spec/helper.rb +8 -0
  39. data/spec/identity_spec.rb +67 -0
  40. data/spec/link_spec.rb +30 -0
  41. data/spec/notification_spec.rb +77 -0
  42. data/test/example_feed.atom +393 -0
  43. data/test/example_feed_empty_author.atom +336 -0
  44. data/test/example_feed_false_connected.atom +359 -0
  45. data/test/example_feed_link_without_href.atom +134 -0
  46. data/test/example_page.html +4 -0
  47. data/test/mime_type_bug_feed.atom +874 -0
  48. 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