nelumba 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
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