nelumba 0.0.13

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 (95) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +9 -0
  3. data/Gemfile +20 -0
  4. data/README.md +242 -0
  5. data/Rakefile +7 -0
  6. data/assets/lotus_logo_purple.png +0 -0
  7. data/assets/lotus_logo_purple.svg +262 -0
  8. data/lib/nelumba.rb +47 -0
  9. data/lib/nelumba/activity.rb +250 -0
  10. data/lib/nelumba/application.rb +11 -0
  11. data/lib/nelumba/article.rb +11 -0
  12. data/lib/nelumba/atom/account.rb +50 -0
  13. data/lib/nelumba/atom/address.rb +56 -0
  14. data/lib/nelumba/atom/author.rb +176 -0
  15. data/lib/nelumba/atom/category.rb +41 -0
  16. data/lib/nelumba/atom/comment.rb +96 -0
  17. data/lib/nelumba/atom/entry.rb +216 -0
  18. data/lib/nelumba/atom/feed.rb +198 -0
  19. data/lib/nelumba/atom/generator.rb +40 -0
  20. data/lib/nelumba/atom/link.rb +79 -0
  21. data/lib/nelumba/atom/name.rb +57 -0
  22. data/lib/nelumba/atom/organization.rb +62 -0
  23. data/lib/nelumba/atom/person.rb +179 -0
  24. data/lib/nelumba/atom/portable_contacts.rb +117 -0
  25. data/lib/nelumba/atom/source.rb +179 -0
  26. data/lib/nelumba/atom/thread.rb +60 -0
  27. data/lib/nelumba/audio.rb +39 -0
  28. data/lib/nelumba/badge.rb +11 -0
  29. data/lib/nelumba/binary.rb +52 -0
  30. data/lib/nelumba/bookmark.rb +30 -0
  31. data/lib/nelumba/category.rb +49 -0
  32. data/lib/nelumba/collection.rb +34 -0
  33. data/lib/nelumba/comment.rb +47 -0
  34. data/lib/nelumba/crypto.rb +144 -0
  35. data/lib/nelumba/device.rb +11 -0
  36. data/lib/nelumba/discover.rb +362 -0
  37. data/lib/nelumba/event.rb +57 -0
  38. data/lib/nelumba/feed.rb +173 -0
  39. data/lib/nelumba/file.rb +43 -0
  40. data/lib/nelumba/generator.rb +53 -0
  41. data/lib/nelumba/group.rb +11 -0
  42. data/lib/nelumba/identity.rb +63 -0
  43. data/lib/nelumba/image.rb +30 -0
  44. data/lib/nelumba/link.rb +56 -0
  45. data/lib/nelumba/note.rb +34 -0
  46. data/lib/nelumba/notification.rb +229 -0
  47. data/lib/nelumba/object.rb +251 -0
  48. data/lib/nelumba/person.rb +306 -0
  49. data/lib/nelumba/place.rb +34 -0
  50. data/lib/nelumba/product.rb +30 -0
  51. data/lib/nelumba/publisher.rb +44 -0
  52. data/lib/nelumba/question.rb +30 -0
  53. data/lib/nelumba/review.rb +30 -0
  54. data/lib/nelumba/service.rb +11 -0
  55. data/lib/nelumba/subscription.rb +117 -0
  56. data/lib/nelumba/version.rb +3 -0
  57. data/lib/nelumba/video.rb +43 -0
  58. data/nelumba.gemspec +28 -0
  59. data/spec/activity_spec.rb +116 -0
  60. data/spec/application_spec.rb +136 -0
  61. data/spec/article_spec.rb +136 -0
  62. data/spec/atom/comment_spec.rb +455 -0
  63. data/spec/atom/feed_spec.rb +684 -0
  64. data/spec/audio_spec.rb +164 -0
  65. data/spec/badge_spec.rb +136 -0
  66. data/spec/binary_spec.rb +218 -0
  67. data/spec/bookmark.rb +150 -0
  68. data/spec/collection_spec.rb +152 -0
  69. data/spec/comment_spec.rb +128 -0
  70. data/spec/crypto_spec.rb +126 -0
  71. data/spec/device_spec.rb +136 -0
  72. data/spec/event_spec.rb +239 -0
  73. data/spec/feed_spec.rb +252 -0
  74. data/spec/file_spec.rb +190 -0
  75. data/spec/group_spec.rb +136 -0
  76. data/spec/helper.rb +10 -0
  77. data/spec/identity_spec.rb +67 -0
  78. data/spec/image_spec.rb +150 -0
  79. data/spec/link_spec.rb +30 -0
  80. data/spec/note_spec.rb +163 -0
  81. data/spec/notification_spec.rb +89 -0
  82. data/spec/person_spec.rb +244 -0
  83. data/spec/place_spec.rb +162 -0
  84. data/spec/product_spec.rb +150 -0
  85. data/spec/question_spec.rb +156 -0
  86. data/spec/review_spec.rb +149 -0
  87. data/spec/service_spec.rb +136 -0
  88. data/spec/video_spec.rb +164 -0
  89. data/test/example_feed.atom +393 -0
  90. data/test/example_feed_empty_author.atom +336 -0
  91. data/test/example_feed_false_connected.atom +359 -0
  92. data/test/example_feed_link_without_href.atom +134 -0
  93. data/test/example_page.html +4 -0
  94. data/test/mime_type_bug_feed.atom +874 -0
  95. metadata +288 -0
@@ -0,0 +1,40 @@
1
+ require 'nelumba/generator'
2
+
3
+ module Nelumba
4
+ require 'atom'
5
+
6
+ module Atom
7
+ # This class represents an OStatus Generator object.
8
+ class Generator < ::Atom::Generator
9
+ require 'open-uri'
10
+
11
+ attribute :'xml:base'
12
+ attribute :'xml:lang'
13
+ attribute :version
14
+ attribute :uri
15
+
16
+ def self.from_canonical(obj)
17
+ hash = obj.to_hash
18
+ if hash[:base]
19
+ hash[:xml_base] = hash[:base]
20
+ end
21
+ if hash[:lang]
22
+ hash[:xml_lang] = hash[:lang]
23
+ end
24
+ hash.delete :base
25
+ hash.delete :lang
26
+ self.new(hash)
27
+ end
28
+
29
+ def to_canonical
30
+ Nelumba::Generator.new(:base => self.xml_base,
31
+ :lang => self.xml_lang,
32
+ :version => self.version,
33
+ :name => self.name,
34
+ :uri => self.uri)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+
@@ -0,0 +1,79 @@
1
+ module Nelumba
2
+ require 'atom'
3
+
4
+ module Atom
5
+
6
+ # This represents an Atom parser/generator for <link> tags.
7
+ class Link < ::Atom::Link
8
+ include ::Atom::Xml::Parseable
9
+
10
+ attribute :rel, :type, :length, :hreflang, :title, :text
11
+
12
+ uri_attribute :href
13
+
14
+ # Create a link.
15
+ #
16
+ # +o+:: An XML::Reader containing a link element or a Hash of attributes.
17
+ #
18
+ def initialize(o)
19
+ case o
20
+ when XML::Reader
21
+ if current_node_is?(o, 'link')
22
+ self.text = o.read_string
23
+ parse(o, :once => true)
24
+ else
25
+ raise ArgumentError, "Link created with node other than atom:link: #{o.name}"
26
+ end
27
+ when Hash
28
+ [:href, :rel, :type, :length, :hreflang, :title].each do |attr|
29
+ self.send("#{attr}=", o[attr])
30
+ end
31
+ else
32
+ raise ArgumentError, "Don't know how to handle #{o}"
33
+ end
34
+ end
35
+
36
+ remove_method :length=
37
+ def length=(v)
38
+ @length = v.to_i
39
+ end
40
+
41
+ # Returns the href of the link, or the content of the link if no href is
42
+ # provided.
43
+ def href
44
+ @href || self.text
45
+ end
46
+
47
+ # Reports the href of the link.
48
+ def to_s
49
+ self.href
50
+ end
51
+
52
+ # Two links are equal when their href is exactly the same regardless of
53
+ # case.
54
+ def ==(o)
55
+ o.respond_to?(:href) && o.href == self.href
56
+ end
57
+
58
+ # This will fetch the URL referenced by the link.
59
+ #
60
+ # If the URL contains a valid feed, a Feed will be returned, otherwise,
61
+ # the body of the response will be returned.
62
+ #
63
+ # TODO: Handle redirects.
64
+ #
65
+ def fetch(options = {})
66
+ begin
67
+ ::Atom::Feed.load_feed(URI.parse(self.href), options)
68
+ rescue ArgumentError
69
+ Net::HTTP.get_response(URI.parse(self.href)).body
70
+ end
71
+ end
72
+
73
+ # :nodoc:
74
+ def inspect
75
+ "<Nelumba::Link href:'#{href}' type:'#{type}'>"
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,57 @@
1
+ module Nelumba
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 Nelumba
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,179 @@
1
+ require 'nelumba/activity'
2
+ require 'nelumba/atom/name'
3
+ require 'nelumba/atom/address'
4
+ require 'nelumba/atom/account'
5
+ require 'nelumba/atom/organization'
6
+
7
+ module Nelumba
8
+ require 'atom'
9
+
10
+ module Atom
11
+ # Holds information about the author of the Feed.
12
+ class Person < ::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 => Nelumba::Atom::Organization
35
+ element 'poco:address', :class => Nelumba::Atom::Address
36
+ element 'poco:account', :class => Nelumba::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
+ Nelumba::Atom::Name.new(reader)
64
+ else
65
+ nil
66
+ end
67
+ end
68
+
69
+ def to_xml(*args)
70
+ x = super(true)
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 Nelumba::Activity that parses the fields
91
+ # having an activity prefix.
92
+ def activity
93
+ Nelumba::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"] = Nelumba::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"] = Nelumba::Atom::Organization.new(hash[:organization])
114
+ end
115
+ hash.delete :organization
116
+ elsif k == :address
117
+ if hash[:address]
118
+ hash[:"poco_address"] = Nelumba::Atom::Address.new(hash[:address])
119
+ end
120
+ hash.delete :address
121
+ elsif k == :account
122
+ if hash[:account]
123
+ hash[:"poco_account"] = Nelumba::Atom::Account.new(hash[:account])
124
+ end
125
+ hash.delete :account
126
+ elsif k == :uid
127
+ if hash[:uid]
128
+ hash[:"poco_id"] = hash[:uid]
129
+ end
130
+ hash.delete :uid
131
+ elsif (k != :uri) && (k != :name) && (k != :email) && (k != :pronoun)
132
+ hash[:"poco_#{to_k}"] = hash[k]
133
+ hash.delete k
134
+ end
135
+ end
136
+
137
+ # Remove any blank entries
138
+ hash.keys.each do |key|
139
+ if hash[key].nil? || hash[key] == ""
140
+ hash.delete key
141
+ end
142
+ end
143
+
144
+ self.new(hash)
145
+ end
146
+
147
+ def to_canonical
148
+ organization = self.poco_organization
149
+ organization = organization.to_canonical if organization
150
+
151
+ address = self.poco_address
152
+ address = address.to_canonical if address
153
+
154
+ account = self.poco_account
155
+ account = account.to_canonical if account
156
+
157
+ ext_name = self.poco_name
158
+ ext_name = ext_name.to_canonical if ext_name
159
+ Nelumba::Person.new(:uid => self.poco_id,
160
+ :extended_name => ext_name,
161
+ :organization => organization,
162
+ :address => address,
163
+ :account => account,
164
+ :gender => self.poco_gender,
165
+ :note => self.poco_note,
166
+ :nickname => self.poco_nickname,
167
+ :display_name => self.poco_displayName,
168
+ :preferred_username => self.poco_preferredUsername,
169
+ :updated => self.poco_updated,
170
+ :published => self.poco_published,
171
+ :birthday => self.poco_birthday,
172
+ :anniversary => self.poco_anniversary,
173
+ :uri => self.uri,
174
+ :email => self.email,
175
+ :name => self.name)
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,117 @@
1
+ module Nelumba
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 Nelumba::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
+ Nelumba::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