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.
- data/.gitignore +6 -0
- data/.travis.yml +9 -0
- data/Gemfile +20 -0
- data/README.md +242 -0
- data/Rakefile +7 -0
- data/assets/lotus_logo_purple.png +0 -0
- data/assets/lotus_logo_purple.svg +262 -0
- data/lib/nelumba.rb +47 -0
- data/lib/nelumba/activity.rb +250 -0
- data/lib/nelumba/application.rb +11 -0
- data/lib/nelumba/article.rb +11 -0
- data/lib/nelumba/atom/account.rb +50 -0
- data/lib/nelumba/atom/address.rb +56 -0
- data/lib/nelumba/atom/author.rb +176 -0
- data/lib/nelumba/atom/category.rb +41 -0
- data/lib/nelumba/atom/comment.rb +96 -0
- data/lib/nelumba/atom/entry.rb +216 -0
- data/lib/nelumba/atom/feed.rb +198 -0
- data/lib/nelumba/atom/generator.rb +40 -0
- data/lib/nelumba/atom/link.rb +79 -0
- data/lib/nelumba/atom/name.rb +57 -0
- data/lib/nelumba/atom/organization.rb +62 -0
- data/lib/nelumba/atom/person.rb +179 -0
- data/lib/nelumba/atom/portable_contacts.rb +117 -0
- data/lib/nelumba/atom/source.rb +179 -0
- data/lib/nelumba/atom/thread.rb +60 -0
- data/lib/nelumba/audio.rb +39 -0
- data/lib/nelumba/badge.rb +11 -0
- data/lib/nelumba/binary.rb +52 -0
- data/lib/nelumba/bookmark.rb +30 -0
- data/lib/nelumba/category.rb +49 -0
- data/lib/nelumba/collection.rb +34 -0
- data/lib/nelumba/comment.rb +47 -0
- data/lib/nelumba/crypto.rb +144 -0
- data/lib/nelumba/device.rb +11 -0
- data/lib/nelumba/discover.rb +362 -0
- data/lib/nelumba/event.rb +57 -0
- data/lib/nelumba/feed.rb +173 -0
- data/lib/nelumba/file.rb +43 -0
- data/lib/nelumba/generator.rb +53 -0
- data/lib/nelumba/group.rb +11 -0
- data/lib/nelumba/identity.rb +63 -0
- data/lib/nelumba/image.rb +30 -0
- data/lib/nelumba/link.rb +56 -0
- data/lib/nelumba/note.rb +34 -0
- data/lib/nelumba/notification.rb +229 -0
- data/lib/nelumba/object.rb +251 -0
- data/lib/nelumba/person.rb +306 -0
- data/lib/nelumba/place.rb +34 -0
- data/lib/nelumba/product.rb +30 -0
- data/lib/nelumba/publisher.rb +44 -0
- data/lib/nelumba/question.rb +30 -0
- data/lib/nelumba/review.rb +30 -0
- data/lib/nelumba/service.rb +11 -0
- data/lib/nelumba/subscription.rb +117 -0
- data/lib/nelumba/version.rb +3 -0
- data/lib/nelumba/video.rb +43 -0
- data/nelumba.gemspec +28 -0
- data/spec/activity_spec.rb +116 -0
- data/spec/application_spec.rb +136 -0
- data/spec/article_spec.rb +136 -0
- data/spec/atom/comment_spec.rb +455 -0
- data/spec/atom/feed_spec.rb +684 -0
- data/spec/audio_spec.rb +164 -0
- data/spec/badge_spec.rb +136 -0
- data/spec/binary_spec.rb +218 -0
- data/spec/bookmark.rb +150 -0
- data/spec/collection_spec.rb +152 -0
- data/spec/comment_spec.rb +128 -0
- data/spec/crypto_spec.rb +126 -0
- data/spec/device_spec.rb +136 -0
- data/spec/event_spec.rb +239 -0
- data/spec/feed_spec.rb +252 -0
- data/spec/file_spec.rb +190 -0
- data/spec/group_spec.rb +136 -0
- data/spec/helper.rb +10 -0
- data/spec/identity_spec.rb +67 -0
- data/spec/image_spec.rb +150 -0
- data/spec/link_spec.rb +30 -0
- data/spec/note_spec.rb +163 -0
- data/spec/notification_spec.rb +89 -0
- data/spec/person_spec.rb +244 -0
- data/spec/place_spec.rb +162 -0
- data/spec/product_spec.rb +150 -0
- data/spec/question_spec.rb +156 -0
- data/spec/review_spec.rb +149 -0
- data/spec/service_spec.rb +136 -0
- data/spec/video_spec.rb +164 -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 +288 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'nelumba/person'
|
2
|
+
require 'nelumba/category'
|
3
|
+
require 'nelumba/generator'
|
4
|
+
|
5
|
+
require 'nelumba/atom/generator'
|
6
|
+
require 'nelumba/atom/category'
|
7
|
+
require 'nelumba/atom/person'
|
8
|
+
require 'nelumba/atom/link'
|
9
|
+
|
10
|
+
module Nelumba
|
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 => Nelumba::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 => Nelumba::Atom::Author
|
38
|
+
elements :contributors, :class => Nelumba::Atom::Person
|
39
|
+
elements :categories, :class => Nelumba::Atom::Category
|
40
|
+
|
41
|
+
# Creates an Atom generator for the given Nelumba::Feed.
|
42
|
+
def self.from_canonical(obj)
|
43
|
+
hash = obj.to_hash
|
44
|
+
hash.delete :items
|
45
|
+
hash.delete :author
|
46
|
+
hash.delete :total_items
|
47
|
+
hash.delete :display_name
|
48
|
+
hash.delete :content
|
49
|
+
hash.delete :summary
|
50
|
+
|
51
|
+
hash.delete :text
|
52
|
+
hash.delete :html
|
53
|
+
|
54
|
+
# Ensure that the generator is encoded.
|
55
|
+
if hash[:generator]
|
56
|
+
hash[:generator] = Nelumba::Atom::Generator.from_canonical(hash[:generator])
|
57
|
+
end
|
58
|
+
|
59
|
+
hash[:links] ||= []
|
60
|
+
|
61
|
+
if hash[:salmon_url]
|
62
|
+
hash[:links] << ::Atom::Link.new(:rel => "salmon", :href => hash[:salmon_url])
|
63
|
+
end
|
64
|
+
hash.delete :salmon_url
|
65
|
+
|
66
|
+
if hash[:url]
|
67
|
+
hash[:links] << ::Atom::Link.new(:rel => "self", :href => hash[:url])
|
68
|
+
end
|
69
|
+
hash.delete :url
|
70
|
+
|
71
|
+
hash[:hubs].each {|h|
|
72
|
+
hash[:links] << ::Atom::Link.new(:rel => "hub", :href => h)
|
73
|
+
}
|
74
|
+
hash.delete :hubs
|
75
|
+
|
76
|
+
hash[:authors].map! {|a|
|
77
|
+
Nelumba::Atom::Author.from_canonical(a)
|
78
|
+
}
|
79
|
+
|
80
|
+
hash[:contributors].map! {|a|
|
81
|
+
Nelumba::Atom::Person.from_canonical(a)
|
82
|
+
}
|
83
|
+
|
84
|
+
hash[:categories].map! {|c|
|
85
|
+
Nelumba::Atom::Category.from_canonical(c)
|
86
|
+
}
|
87
|
+
|
88
|
+
# title/subtitle content type
|
89
|
+
node = XML::Node.new("title")
|
90
|
+
node['type'] = hash[:title_type] if hash[:title_type]
|
91
|
+
node << hash[:title]
|
92
|
+
|
93
|
+
xml = XML::Reader.string(node.to_s)
|
94
|
+
xml.read
|
95
|
+
hash[:title] = ::Atom::Content.parse(xml)
|
96
|
+
hash.delete :title_type
|
97
|
+
|
98
|
+
hash[:id] = hash[:uid]
|
99
|
+
hash.delete :uid
|
100
|
+
|
101
|
+
if hash[:subtitle]
|
102
|
+
node = XML::Node.new("subtitle")
|
103
|
+
node['type'] = hash[:subtitle_type] if hash[:subtitle_type]
|
104
|
+
node << hash[:subtitle]
|
105
|
+
|
106
|
+
xml = XML::Reader.string(node.to_s)
|
107
|
+
xml.read
|
108
|
+
hash[:subtitle] = ::Atom::Content.parse(xml)
|
109
|
+
else
|
110
|
+
hash.delete :subtitle
|
111
|
+
end
|
112
|
+
hash.delete :subtitle_type
|
113
|
+
|
114
|
+
self.new(hash)
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_canonical
|
118
|
+
generator = nil
|
119
|
+
generator = self.generator.to_canonical if self.generator
|
120
|
+
|
121
|
+
salmon_url = nil
|
122
|
+
if self.link('salmon').any?
|
123
|
+
salmon_url = self.link('salmon').first.href
|
124
|
+
end
|
125
|
+
|
126
|
+
url = self.url
|
127
|
+
|
128
|
+
categories = self.categories.map(&:to_canonical)
|
129
|
+
|
130
|
+
Nelumba::Feed.new(:title => self.title,
|
131
|
+
:title_type => self.title ? self.title.type : nil,
|
132
|
+
:subtitle => self.subtitle,
|
133
|
+
:subtitle_type => self.subtitle ? self.subtitle.type : nil,
|
134
|
+
:id => self.id,
|
135
|
+
:url => url,
|
136
|
+
:categories => categories,
|
137
|
+
:icon => self.icon,
|
138
|
+
:logo => self.logo,
|
139
|
+
:rights => self.rights,
|
140
|
+
:published => self.published,
|
141
|
+
:updated => self.updated,
|
142
|
+
:authors => self.authors.map(&:to_canonical),
|
143
|
+
:contributors => self.contributors.map(&:to_canonical),
|
144
|
+
:hubs => self.hubs,
|
145
|
+
:salmon_url => salmon_url,
|
146
|
+
:generator => generator)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns an array of ::Atom::Link instances for all link tags
|
150
|
+
# that have a rel equal to that given by attribute.
|
151
|
+
#
|
152
|
+
# For example:
|
153
|
+
# link(:hub).first.href -- Gets the first link tag with rel="hub" and
|
154
|
+
# returns the contents of the href attribute.
|
155
|
+
#
|
156
|
+
def link(attribute)
|
157
|
+
links.find_all { |l| l.rel == attribute.to_s }
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns an array of URLs for each hub link tag.
|
161
|
+
def hubs
|
162
|
+
link('hub').map { |link| link.href }
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns a string of the url for this feed.
|
166
|
+
def url
|
167
|
+
if links.alternate
|
168
|
+
links.alternate.href
|
169
|
+
elsif links.self
|
170
|
+
links.self.href
|
171
|
+
else
|
172
|
+
links.map.each do |l|
|
173
|
+
l.href
|
174
|
+
end.compact.first
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Nelumba
|
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
|
+
Nelumba::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
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Nelumba
|
2
|
+
class Audio
|
3
|
+
include Nelumba::Object
|
4
|
+
|
5
|
+
# A fragment of HTML markup that, when embedded within another HTML page,
|
6
|
+
# provides an interactive user-interface for viewing or listening to the
|
7
|
+
# audio stream.
|
8
|
+
attr_reader :embed_code
|
9
|
+
|
10
|
+
# A MediaLink to the audio content itself.
|
11
|
+
attr_reader :stream
|
12
|
+
|
13
|
+
# Creates a new Audio activity object.
|
14
|
+
def initialize(options = {})
|
15
|
+
super options
|
16
|
+
|
17
|
+
@embed_code = options[:embed_code]
|
18
|
+
@stream = options[:stream]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns a hash of all relevant fields.
|
22
|
+
def to_hash
|
23
|
+
{
|
24
|
+
:embed_code => @embed_code,
|
25
|
+
:stream => @stream
|
26
|
+
}.merge(super)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns a hash of all relevant fields with JSON activity streams
|
30
|
+
# conventions.
|
31
|
+
def to_json_hash
|
32
|
+
{
|
33
|
+
:objectType => "audio",
|
34
|
+
:embedCode => @embed_code,
|
35
|
+
:stream => @stream
|
36
|
+
}.merge(super)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Nelumba
|
2
|
+
class Binary
|
3
|
+
include Nelumba::Object
|
4
|
+
|
5
|
+
attr_reader :data
|
6
|
+
attr_reader :length
|
7
|
+
|
8
|
+
attr_reader :md5
|
9
|
+
attr_reader :compression
|
10
|
+
|
11
|
+
attr_reader :file_url
|
12
|
+
attr_reader :mime_type
|
13
|
+
|
14
|
+
def initialize(options = {}, &blk)
|
15
|
+
init(options, &blk)
|
16
|
+
end
|
17
|
+
|
18
|
+
def init(options = {}, &blk)
|
19
|
+
super(options, &blk)
|
20
|
+
|
21
|
+
@data = options[:data]
|
22
|
+
@compression = options[:compression]
|
23
|
+
@md5 = options[:md5]
|
24
|
+
@file_url = options[:file_url]
|
25
|
+
@mime_type = options[:mime_type]
|
26
|
+
@length = options[:length]
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_hash
|
30
|
+
{
|
31
|
+
:data => @data,
|
32
|
+
:compression => @compression,
|
33
|
+
:md5 => @md5,
|
34
|
+
:file_url => @file_url,
|
35
|
+
:mime_type => @mime_type,
|
36
|
+
:length => @length
|
37
|
+
}.merge(super)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_json_hash
|
41
|
+
{
|
42
|
+
:objectType => "binary",
|
43
|
+
:data => @data,
|
44
|
+
:compression => @compression,
|
45
|
+
:md5 => @md5,
|
46
|
+
:fileUrl => @file_url,
|
47
|
+
:mimeType => @mime_type,
|
48
|
+
:length => @length
|
49
|
+
}.merge(super)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Nelumba
|
2
|
+
class Bookmark
|
3
|
+
include Nelumba::Object
|
4
|
+
|
5
|
+
attr_reader :target_url
|
6
|
+
|
7
|
+
def initialize(options = {}, &blk)
|
8
|
+
init(options, &blk)
|
9
|
+
end
|
10
|
+
|
11
|
+
def init(options = {}, &blk)
|
12
|
+
super(options, &blk)
|
13
|
+
|
14
|
+
@target_url = options[:target_url]
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_hash
|
18
|
+
{
|
19
|
+
:target_url => @target_url
|
20
|
+
}.merge(super)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_json_hash
|
24
|
+
{
|
25
|
+
:objectType => "bookmark",
|
26
|
+
:targetUrl => @target_url
|
27
|
+
}.merge(super)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Nelumba
|
2
|
+
# This element conveys information about a category associated with an entry
|
3
|
+
# or feed. There is no defined meaning to the content according to the Atom
|
4
|
+
# specification.
|
5
|
+
class Category
|
6
|
+
# Holds the base URI for relative URIs contained in scheme.
|
7
|
+
attr_reader :base
|
8
|
+
|
9
|
+
# Holds the language of term and label, when it exists. The language
|
10
|
+
# should be specified as RFC 3066 as either 2 or 3 letter codes.
|
11
|
+
# For example: 'en' for English or more specifically 'en-us'
|
12
|
+
attr_reader :lang
|
13
|
+
|
14
|
+
# Holds the optional scheme used for categorization.
|
15
|
+
attr_reader :scheme
|
16
|
+
|
17
|
+
# Holds the string identifying the category to which the entry or
|
18
|
+
# feed belongs.
|
19
|
+
attr_reader :term
|
20
|
+
|
21
|
+
# Holds the string that provides a human-readable label for display in
|
22
|
+
# end-user applications. The content of this field is language sensitive.
|
23
|
+
attr_reader :label
|
24
|
+
|
25
|
+
# Create a Category to apply to a feed or entry.
|
26
|
+
def initialize(options = {})
|
27
|
+
init(options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def init(options = {})
|
31
|
+
@base = options[:base]
|
32
|
+
@lang = options[:lang]
|
33
|
+
@scheme = options[:scheme]
|
34
|
+
@term = options[:term]
|
35
|
+
@label = options[:label]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Yields a Hash that represents this category.
|
39
|
+
def to_hash
|
40
|
+
{
|
41
|
+
:base => @base,
|
42
|
+
:lang => @lang,
|
43
|
+
:scheme => @scheme,
|
44
|
+
:term => @term,
|
45
|
+
:label => @label
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Nelumba
|
2
|
+
class Collection
|
3
|
+
include Nelumba::Object
|
4
|
+
|
5
|
+
attr_reader :items
|
6
|
+
attr_reader :total_items
|
7
|
+
|
8
|
+
def initialize(options = {}, &blk)
|
9
|
+
init(options, &blk)
|
10
|
+
end
|
11
|
+
|
12
|
+
def init(options = {}, &blk)
|
13
|
+
super(options, &blk)
|
14
|
+
|
15
|
+
@items = options[:items] || []
|
16
|
+
@total_items = options[:total_items] || @items.count
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
{
|
21
|
+
:items => (self.items || []).dup,
|
22
|
+
:total_items => self.total_items || self.items.count
|
23
|
+
}.merge(super)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_json_hash
|
27
|
+
{
|
28
|
+
:objectType => "collection",
|
29
|
+
:items => (self.items || []).dup,
|
30
|
+
:totalItems => self.total_items || self.items.count
|
31
|
+
}.merge(super)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|