ostatus 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/ostatus/activity.rb +14 -2
- data/lib/ostatus/author.rb +2 -0
- data/lib/ostatus/entry.rb +11 -2
- data/lib/ostatus/feed.rb +33 -0
- data/lib/ostatus/salmon.rb +227 -0
- data/lib/ostatus/thread.rb +45 -0
- data/lib/ostatus/version.rb +1 -1
- data/lib/ostatus.rb +2 -0
- data/ostatus.gemspec +0 -1
- data/spec/activity_spec.rb +3 -2
- data/spec/feed_spec.rb +7 -0
- data/test/example_feed.atom +34 -1
- data/test/example_page.html +4 -0
- metadata +10 -19
data/lib/ostatus/activity.rb
CHANGED
@@ -35,7 +35,13 @@ module OStatus
|
|
35
35
|
@entry[:object]
|
36
36
|
else
|
37
37
|
obj = @entry.activity_verb
|
38
|
-
|
38
|
+
if obj.nil?
|
39
|
+
nil
|
40
|
+
elsif obj.start_with?(SCHEMA_ROOT)
|
41
|
+
obj[SCHEMA_ROOT.size..-1].intern unless obj.nil?
|
42
|
+
else
|
43
|
+
obj
|
44
|
+
end
|
39
45
|
end
|
40
46
|
end
|
41
47
|
|
@@ -48,7 +54,13 @@ module OStatus
|
|
48
54
|
@entry[:object_type]
|
49
55
|
else
|
50
56
|
obj = @entry.activity_object_type
|
51
|
-
|
57
|
+
if obj.nil?
|
58
|
+
nil
|
59
|
+
elsif obj.start_with?(SCHEMA_ROOT)
|
60
|
+
obj[SCHEMA_ROOT.size..-1].intern unless obj.nil?
|
61
|
+
else
|
62
|
+
obj
|
63
|
+
end
|
52
64
|
end
|
53
65
|
end
|
54
66
|
|
data/lib/ostatus/author.rb
CHANGED
data/lib/ostatus/entry.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require_relative 'activity'
|
2
2
|
require_relative 'author'
|
3
|
+
require_relative 'thread'
|
3
4
|
|
4
5
|
module OStatus
|
6
|
+
THREAD_NS = 'http://purl.org/syndication/thread/1.0'
|
5
7
|
|
6
8
|
# Holds information about an individual entry in the Feed.
|
7
9
|
class Entry < Atom::Entry
|
@@ -9,10 +11,18 @@ module OStatus
|
|
9
11
|
|
10
12
|
add_extension_namespace :activity, ACTIVITY_NS
|
11
13
|
element 'activity:object-type'
|
12
|
-
element 'activity:object'
|
14
|
+
element 'activity:object', :class => OStatus::Author
|
13
15
|
element 'activity:verb'
|
14
16
|
element 'activity:target'
|
15
17
|
|
18
|
+
add_extension_namespace :thr, THREAD_NS
|
19
|
+
element 'thr:in-reply-to', :class => OStatus::Thread
|
20
|
+
|
21
|
+
# This is for backwards compatibility with some implementations of Activity
|
22
|
+
# Streams. It should not be used, and in fact is obscured as it is not a
|
23
|
+
# method in OStatus::Activity.
|
24
|
+
element 'activity:actor', :class => OStatus::Author
|
25
|
+
|
16
26
|
namespace Atom::NAMESPACE
|
17
27
|
element :title, :id, :summary
|
18
28
|
element :updated, :published, :class => DateTime, :content_only => true
|
@@ -35,7 +45,6 @@ module OStatus
|
|
35
45
|
self.activity_verb = OStatus::Activity::SCHEMA_ROOT + value.activity_verb.to_s
|
36
46
|
end
|
37
47
|
self.activity_target = value.activity_target if value.target
|
38
|
-
activity_object_type = "HEY"
|
39
48
|
end
|
40
49
|
|
41
50
|
def url
|
data/lib/ostatus/feed.rb
CHANGED
@@ -25,6 +25,11 @@ module OStatus
|
|
25
25
|
|
26
26
|
attr_reader :url
|
27
27
|
|
28
|
+
# Store in reverse order so that the -1 from .index "not found"
|
29
|
+
# will sort properly
|
30
|
+
MIME_ORDER = ['application/atom+xml', 'application/rss+xml',
|
31
|
+
'application/xml'].reverse
|
32
|
+
|
28
33
|
def initialize(str, url, access_token, options)
|
29
34
|
@str = str
|
30
35
|
@url = url
|
@@ -32,6 +37,34 @@ module OStatus
|
|
32
37
|
@options = options
|
33
38
|
|
34
39
|
if str
|
40
|
+
|
41
|
+
if str =~ /<html/
|
42
|
+
doc = LibXML::XML::HTMLParser.string(str).parse
|
43
|
+
links = doc.find(
|
44
|
+
"//*[contains(concat(' ',normalize-space(@rel),' '), 'alternate')]"
|
45
|
+
).map {|el|
|
46
|
+
{:type => el.attributes['type'].to_s,
|
47
|
+
:href => el.attributes['href'].to_s}
|
48
|
+
}.sort {|a, b|
|
49
|
+
MIME_ORDER.index(b[:type]) <=> MIME_ORDER.index(a[:type])
|
50
|
+
}
|
51
|
+
|
52
|
+
# Resolve relative links
|
53
|
+
link = URI::parse(links.first[:href]) rescue URI.new
|
54
|
+
|
55
|
+
unless link.host
|
56
|
+
link.host = URI::parse(@url).host rescue nil
|
57
|
+
end
|
58
|
+
|
59
|
+
unless link.absolute?
|
60
|
+
link.path = File::dirname(URI::parse(@url).path) \
|
61
|
+
+ '/' + link.path rescue nil
|
62
|
+
end
|
63
|
+
|
64
|
+
@url = link.to_s
|
65
|
+
@str = str = open(@url).read
|
66
|
+
end
|
67
|
+
|
35
68
|
super(XML::Reader.string(str))
|
36
69
|
else
|
37
70
|
super(options)
|
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'xml'
|
2
|
+
require 'atom'
|
3
|
+
require 'digest/sha2'
|
4
|
+
require 'rsa'
|
5
|
+
|
6
|
+
module OStatus
|
7
|
+
class Salmon
|
8
|
+
attr_accessor :entry
|
9
|
+
|
10
|
+
# Create a Salmon instance for a particular OStatus::Entry
|
11
|
+
def initialize entry, signature = nil, plaintext = nil
|
12
|
+
@entry = entry
|
13
|
+
@signature = signature
|
14
|
+
@plaintext = plaintext
|
15
|
+
end
|
16
|
+
|
17
|
+
# Creates an entry for following a particular Author.
|
18
|
+
def Salmon.from_follow(user_author, followed_author)
|
19
|
+
entry = OStatus::Entry.new(
|
20
|
+
:author => user_author,
|
21
|
+
:title => "Now following #{followed_author.name}",
|
22
|
+
:content => Atom::Content::Html.new("Now following #{followed_author.name}")
|
23
|
+
)
|
24
|
+
|
25
|
+
entry.activity_verb = :follow
|
26
|
+
entry.activity_object = followed_author
|
27
|
+
|
28
|
+
OStatus::Salmon.new(entry)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creates an entry for unfollowing a particular Author.
|
32
|
+
def Salmon.from_unfollow(user_author, followed_author)
|
33
|
+
entry = OStatus::Entry.new(
|
34
|
+
:author => user_author,
|
35
|
+
:title => "Stopped following #{followed_author.name}",
|
36
|
+
:content => Atom::Content::Html.new("Stopped following #{followed_author.name}")
|
37
|
+
)
|
38
|
+
|
39
|
+
entry.activity_verb = "http://ostatus.org/schema/1.0/unfollow"
|
40
|
+
entry.activity_object = followed_author
|
41
|
+
|
42
|
+
OStatus::Salmon.new(entry)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Will pull a OStatus::Entry from a magic envelope described by the xml.
|
46
|
+
def Salmon.from_xml source
|
47
|
+
if source.is_a?(String)
|
48
|
+
source = XML::Document.string(source,
|
49
|
+
:options => XML::Parser::Options::NOENT)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Retrieve the envelope
|
53
|
+
envelope = source.find('/me:env',
|
54
|
+
'me:http://salmon-protocol.org/ns/magic-env').first
|
55
|
+
|
56
|
+
if envelope.nil?
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
|
60
|
+
data = envelope.find('me:data',
|
61
|
+
'me:http://salmon-protocol.org/ns/magic-env').first
|
62
|
+
if data.nil?
|
63
|
+
return nil
|
64
|
+
end
|
65
|
+
|
66
|
+
data_type = data.attributes["type"]
|
67
|
+
if data_type.nil?
|
68
|
+
data_type = 'application/atom+xml'
|
69
|
+
armored_data_type = ''
|
70
|
+
else
|
71
|
+
armored_data_type = Base64::urlsafe_encode64(data_type)
|
72
|
+
end
|
73
|
+
|
74
|
+
encoding = envelope.find('me:encoding',
|
75
|
+
'me:http://salmon-protocol.org/ns/magic-env').first
|
76
|
+
|
77
|
+
algorithm = envelope.find(
|
78
|
+
'me:alg',
|
79
|
+
'me:http://salmon-protocol.org/ns/magic-env').first
|
80
|
+
|
81
|
+
signature = source.find('me:sig',
|
82
|
+
'me:http://salmon-protocol.org/ns/magic-env').first
|
83
|
+
|
84
|
+
# Parse fields
|
85
|
+
|
86
|
+
if signature.nil?
|
87
|
+
# Well, if we cannot verify, we don't accept
|
88
|
+
return nil
|
89
|
+
else
|
90
|
+
# XXX: Handle key_id attribute
|
91
|
+
signature = signature.content
|
92
|
+
signature = Base64::urlsafe_decode64(signature)
|
93
|
+
end
|
94
|
+
|
95
|
+
if encoding.nil?
|
96
|
+
# When the encoding is omitted, use base64url
|
97
|
+
# Cite: Magic Envelope Draft Spec Section 3.3
|
98
|
+
armored_encoding = ''
|
99
|
+
encoding = 'base64url'
|
100
|
+
else
|
101
|
+
armored_encoding = Base64::urlsafe_encode64(encoding.content)
|
102
|
+
encoding = encoding.content.downcase
|
103
|
+
end
|
104
|
+
|
105
|
+
if algorithm.nil?
|
106
|
+
# When algorithm is omitted, use 'RSA-SHA256'
|
107
|
+
# Cite: Magic Envelope Draft Spec Section 3.3
|
108
|
+
armored_algorithm = ''
|
109
|
+
algorithm = 'rsa-sha256'
|
110
|
+
else
|
111
|
+
armored_algorithm = Base64::urlsafe_encode64(algorithm.content)
|
112
|
+
algorithm = algorithm.content.downcase
|
113
|
+
end
|
114
|
+
|
115
|
+
# Retrieve and decode data payload
|
116
|
+
|
117
|
+
data = data.content
|
118
|
+
armored_data = data
|
119
|
+
|
120
|
+
case encoding
|
121
|
+
when 'base64url'
|
122
|
+
data = Base64::urlsafe_decode64(data)
|
123
|
+
else
|
124
|
+
# Unsupported data encoding
|
125
|
+
return nil
|
126
|
+
end
|
127
|
+
|
128
|
+
# Signature plaintext
|
129
|
+
plaintext = "#{armored_data}.#{armored_data_type}.#{armored_encoding}.#{armored_algorithm}"
|
130
|
+
|
131
|
+
# Interpret data payload
|
132
|
+
payload = XML::Reader.string(data)
|
133
|
+
Salmon.new OStatus::Entry.new(payload), signature, plaintext
|
134
|
+
end
|
135
|
+
|
136
|
+
# Generate the xml for this Salmon notice and sign with the given private
|
137
|
+
# key.
|
138
|
+
def to_xml key
|
139
|
+
# Generate magic envelope
|
140
|
+
magic_envelope = XML::Document.new
|
141
|
+
|
142
|
+
magic_envelope.root = XML::Node.new 'env'
|
143
|
+
|
144
|
+
me_ns = XML::Namespace.new(magic_envelope.root,
|
145
|
+
'me', 'http://salmon-protocol.org/ns/magic-env')
|
146
|
+
|
147
|
+
magic_envelope.root.namespaces.namespace = me_ns
|
148
|
+
|
149
|
+
# Armored Data <me:data>
|
150
|
+
data = @entry.to_xml
|
151
|
+
@plaintext = data
|
152
|
+
data_armored = Base64::urlsafe_encode64(data)
|
153
|
+
elem = XML::Node.new 'data', data_armored, me_ns
|
154
|
+
elem.attributes['type'] = 'application/atom+xml'
|
155
|
+
data_type_armored = 'YXBwbGljYXRpb24vYXRvbSt4bWw='
|
156
|
+
magic_envelope.root << elem
|
157
|
+
|
158
|
+
# Encoding <me:encoding>
|
159
|
+
magic_envelope.root << XML::Node.new('encoding', 'base64url', me_ns)
|
160
|
+
encoding_armored = 'YmFzZTY0dXJs'
|
161
|
+
|
162
|
+
# Signing Algorithm <me:alg>
|
163
|
+
magic_envelope.root << XML::Node.new('alg', 'RSA-SHA256', me_ns)
|
164
|
+
algorithm_armored = 'UlNBLVNIQTI1Ng=='
|
165
|
+
|
166
|
+
# Signature <me:sig>
|
167
|
+
plaintext = "#{data_armored}.#{data_type_armored}.#{encoding_armored}.#{algorithm_armored}"
|
168
|
+
|
169
|
+
# Assign @signature to the signature generated from the plaintext
|
170
|
+
sign(plaintext, key)
|
171
|
+
|
172
|
+
signature_armored = Base64::urlsafe_encode64(@signature)
|
173
|
+
magic_envelope.root << XML::Node.new('sig', signature_armored, me_ns)
|
174
|
+
|
175
|
+
magic_envelope.to_s :indent => true, :encoding => XML::Encoding::UTF_8
|
176
|
+
end
|
177
|
+
|
178
|
+
# Return the EMSA string for this Salmon instance given the size of the
|
179
|
+
# public key modulus.
|
180
|
+
def signature modulus_byte_length
|
181
|
+
plaintext = Digest::SHA2.new(256).digest(@plaintext)
|
182
|
+
|
183
|
+
prefix = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"
|
184
|
+
padding_count = modulus_byte_length - prefix.bytes.count - plaintext.bytes.count - 3
|
185
|
+
|
186
|
+
padding = ""
|
187
|
+
padding_count.times do
|
188
|
+
padding = padding + "\xff"
|
189
|
+
end
|
190
|
+
|
191
|
+
"\x00\x01#{padding}\x00#{prefix}#{plaintext}"
|
192
|
+
end
|
193
|
+
|
194
|
+
def sign message, key
|
195
|
+
@plaintext = message
|
196
|
+
|
197
|
+
modulus_byte_count = key.private_key.modulus.size
|
198
|
+
|
199
|
+
@signature = signature(modulus_byte_count)
|
200
|
+
@signature = key.decrypt(@signature)
|
201
|
+
end
|
202
|
+
|
203
|
+
# Use RSA to verify the signature
|
204
|
+
# key - RSA::KeyPair with the public key to use
|
205
|
+
def verified? key
|
206
|
+
# RSA encryption is needed to compare the signatures
|
207
|
+
|
208
|
+
# Get signature to check
|
209
|
+
emsa = self.signature key.public_key.modulus.size
|
210
|
+
|
211
|
+
# Get signature in payload
|
212
|
+
emsa_signature = key.encrypt(@signature)
|
213
|
+
|
214
|
+
# RSA gem drops leading 0s since it does math upon an Integer
|
215
|
+
# As a workaround, I check for what I expect the second byte to be (\x01)
|
216
|
+
# This workaround will also handle seeing a \x00 first if the RSA gem is
|
217
|
+
# fixed.
|
218
|
+
if emsa_signature.getbyte(0) == 1
|
219
|
+
emsa_signature = "\x00#{emsa_signature}"
|
220
|
+
end
|
221
|
+
|
222
|
+
# Does the signature match?
|
223
|
+
# Return the result.
|
224
|
+
emsa_signature == emsa
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'xml/libxml'
|
2
|
+
require 'atom/xml/parser.rb'
|
3
|
+
|
4
|
+
module OStatus
|
5
|
+
|
6
|
+
# This will parse the Thread Atom extension
|
7
|
+
class Thread
|
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 inspect
|
42
|
+
"<OStatus::Thread href:'#{href}' type:'#{type}'>"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/ostatus/version.rb
CHANGED
data/lib/ostatus.rb
CHANGED
data/ostatus.gemspec
CHANGED
data/spec/activity_spec.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative '../lib/ostatus/feed.rb'
|
2
2
|
require_relative '../lib/ostatus/entry.rb'
|
3
3
|
require_relative '../lib/ostatus/activity.rb'
|
4
|
+
require_relative '../lib/ostatus/author.rb'
|
4
5
|
|
5
6
|
describe OStatus::Activity do
|
6
7
|
before(:each) do
|
@@ -12,8 +13,8 @@ describe OStatus::Activity do
|
|
12
13
|
end
|
13
14
|
|
14
15
|
describe "#object" do
|
15
|
-
it "should give
|
16
|
-
@activity.object.should eql(
|
16
|
+
it "should give an Author containing the content of the activity:object tag" do
|
17
|
+
@activity.object.class.should eql(OStatus::Author)
|
17
18
|
end
|
18
19
|
|
19
20
|
it "should give nil when no activity:object was given" do
|
data/spec/feed_spec.rb
CHANGED
@@ -6,6 +6,13 @@ describe OStatus::Feed do
|
|
6
6
|
@feed = OStatus::Feed.from_url('test/example_feed.atom')
|
7
7
|
end
|
8
8
|
|
9
|
+
describe "#initialize" do
|
10
|
+
it "should detect a feed URI in an HTML page" do
|
11
|
+
@feed = OStatus::Feed.from_url('test/example_page.html')
|
12
|
+
@feed.url.should == 'test/example_feed.atom'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
9
16
|
describe "#atom" do
|
10
17
|
it "should return a String containing the atom information" do
|
11
18
|
@feed.atom.start_with?("<?xml").should eql(true)
|
data/test/example_feed.atom
CHANGED
@@ -71,7 +71,40 @@ bar</poco:note>
|
|
71
71
|
<link href="http://identi.ca/api/statuses/user_timeline/141464.atom" rel="self" type="application/atom+xml"/>
|
72
72
|
<entry>
|
73
73
|
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
74
|
-
<activity:object>
|
74
|
+
<activity:object>
|
75
|
+
<activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
|
76
|
+
<uri>http://identi.ca/user/141464</uri>
|
77
|
+
<name>greenmanspirit</name>
|
78
|
+
<email>foo@example.com</email>
|
79
|
+
<link rel="alternate" type="text/html" href="http://identi.ca/greenmanspirit"/>
|
80
|
+
<link rel="avatar" type="image/jpeg" media:width="480" media:height="480" href="http://avatar.identi.ca/141464-480-20100607212940.jpeg"/>
|
81
|
+
<link rel="avatar" type="image/jpeg" media:width="96" media:height="96" href="http://avatar.identi.ca/141464-96-20100607212940.jpeg"/>
|
82
|
+
<link rel="avatar" type="image/jpeg" media:width="48" media:height="48" href="http://avatar.identi.ca/141464-48-20100607212940.jpeg"/>
|
83
|
+
<link rel="avatar" type="image/jpeg" media:width="24" media:height="24" href="http://avatar.identi.ca/141464-24-20100607212940.jpeg"/>
|
84
|
+
<georss:point>0 0</georss:point>
|
85
|
+
<poco:preferredUsername>greenmanspirit</poco:preferredUsername>
|
86
|
+
<poco:displayName>Adam Hobaugh</poco:displayName>
|
87
|
+
<poco:id>foobar</poco:id>
|
88
|
+
<poco:name>barbaz</poco:name>
|
89
|
+
<poco:nickname>spaz</poco:nickname>
|
90
|
+
<poco:published>2012-02-21T02:15:14+00:00</poco:published>
|
91
|
+
<poco:updated>2013-02-21T02:15:14+00:00</poco:updated>
|
92
|
+
<poco:birthday>2014-02-21</poco:birthday>
|
93
|
+
<poco:anniversary>2015-02-21</poco:anniversary>
|
94
|
+
<poco:gender>male</poco:gender>
|
95
|
+
<poco:note>foo
|
96
|
+
bar</poco:note>
|
97
|
+
<poco:utcOffset>-08:00</poco:utcOffset>
|
98
|
+
<poco:connected>true</poco:connected>
|
99
|
+
<poco:urls>
|
100
|
+
<poco:type>homepage</poco:type>
|
101
|
+
<poco:value>http://adamhobaugh.com</poco:value>
|
102
|
+
<poco:primary>true</poco:primary>
|
103
|
+
</poco:urls>
|
104
|
+
|
105
|
+
<statusnet:profile_info local_id="141464"></statusnet:profile_info>
|
106
|
+
</activity:object>
|
107
|
+
|
75
108
|
<activity:target>Barbaz</activity:target>
|
76
109
|
<id>http://identi.ca/notice/64991641</id>
|
77
110
|
<title>staples come out of the head tomorrow, oh yeah</title>
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 8
|
9
|
+
version: 0.0.8
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Hackers of the Severed Hand
|
@@ -14,11 +14,11 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-
|
17
|
+
date: 2011-05-20 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
21
|
+
name: ratom
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
@@ -30,23 +30,10 @@ dependencies:
|
|
30
30
|
version: "0"
|
31
31
|
type: :runtime
|
32
32
|
version_requirements: *id001
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
|
-
name: ratom
|
35
|
-
prerelease: false
|
36
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
-
none: false
|
38
|
-
requirements:
|
39
|
-
- - ">="
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
segments:
|
42
|
-
- 0
|
43
|
-
version: "0"
|
44
|
-
type: :runtime
|
45
|
-
version_requirements: *id002
|
46
33
|
- !ruby/object:Gem::Dependency
|
47
34
|
name: rspec
|
48
35
|
prerelease: false
|
49
|
-
requirement: &
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
50
37
|
none: false
|
51
38
|
requirements:
|
52
39
|
- - ">="
|
@@ -55,7 +42,7 @@ dependencies:
|
|
55
42
|
- 0
|
56
43
|
version: "0"
|
57
44
|
type: :development
|
58
|
-
version_requirements: *
|
45
|
+
version_requirements: *id002
|
59
46
|
description: This project is to be used to jumpstart OStatus related projects that implement the PubSubHubbub protocols by providing the common fundamentals of Atom parsing and OStatus object creation.
|
60
47
|
email:
|
61
48
|
- hotsh@xomb.org
|
@@ -76,6 +63,8 @@ files:
|
|
76
63
|
- lib/ostatus/entry.rb
|
77
64
|
- lib/ostatus/feed.rb
|
78
65
|
- lib/ostatus/portable_contacts.rb
|
66
|
+
- lib/ostatus/salmon.rb
|
67
|
+
- lib/ostatus/thread.rb
|
79
68
|
- lib/ostatus/version.rb
|
80
69
|
- ostatus.gemspec
|
81
70
|
- spec/activity_spec.rb
|
@@ -87,6 +76,7 @@ files:
|
|
87
76
|
- test/example_feed.atom
|
88
77
|
- test/example_feed_empty_author.atom
|
89
78
|
- test/example_feed_false_connected.atom
|
79
|
+
- test/example_page.html
|
90
80
|
has_rdoc: true
|
91
81
|
homepage: http://github.com/hotsh/ostatus
|
92
82
|
licenses: []
|
@@ -129,3 +119,4 @@ test_files:
|
|
129
119
|
- test/example_feed.atom
|
130
120
|
- test/example_feed_empty_author.atom
|
131
121
|
- test/example_feed_false_connected.atom
|
122
|
+
- test/example_page.html
|