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.
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