ostatus 0.0.6 → 0.0.7
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/lib/ostatus/activity.rb +31 -29
- data/lib/ostatus/author.rb +65 -39
- data/lib/ostatus/entry.rb +44 -101
- data/lib/ostatus/feed.rb +56 -111
- data/lib/ostatus/portable_contacts.rb +63 -87
- data/lib/ostatus/version.rb +1 -1
- data/lib/ostatus.rb +0 -2
- data/ostatus.gemspec +1 -2
- data/spec/activity_spec.rb +2 -2
- data/spec/author_spec.rb +1 -1
- data/spec/builder_spec.rb +72 -0
- data/spec/entry_spec.rb +0 -16
- data/test/example_feed.atom +6 -6
- data/test/example_feed_empty_author.atom +1 -1
- data/test/example_feed_false_connected.atom +6 -6
- metadata +8 -19
data/lib/ostatus/activity.rb
CHANGED
@@ -1,53 +1,55 @@
|
|
1
1
|
module OStatus
|
2
|
+
ACTIVITY_NS = 'http://activitystrea.ms/spec/1.0/'
|
2
3
|
|
3
4
|
# This class represents an Activity object for an OStatus entry.
|
4
5
|
class Activity
|
6
|
+
SCHEMA_ROOT = 'http://activitystrea.ms/schema/1.0/'
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
# Nokogiri::XML::Element that serves as the root node of
|
9
|
-
# anything containing the activity tags.
|
10
|
-
def initialize(activity_root)
|
11
|
-
if activity_root.class == Hash
|
12
|
-
@activity_data = activity_root
|
13
|
-
@activity = nil
|
14
|
-
else
|
15
|
-
@activity = activity_root
|
16
|
-
@activity_data = nil
|
17
|
-
end
|
8
|
+
def initialize(entry)
|
9
|
+
@entry = entry
|
18
10
|
end
|
19
11
|
|
20
|
-
def pick_first_node(a)
|
21
|
-
if a.empty?
|
22
|
-
nil
|
23
|
-
else
|
24
|
-
a[0].content
|
25
|
-
end
|
26
|
-
end
|
27
|
-
private :pick_first_node
|
28
|
-
|
29
12
|
# Returns the object field or nil if it does not exist.
|
30
13
|
def object
|
31
|
-
|
32
|
-
|
14
|
+
if @entry.is_a? Hash
|
15
|
+
@entry[:object]
|
16
|
+
else
|
17
|
+
@entry.activity_object
|
18
|
+
end
|
33
19
|
end
|
34
20
|
|
35
21
|
# Returns the target field or nil if it does not exist.
|
36
22
|
def target
|
37
|
-
|
38
|
-
|
23
|
+
if @entry.is_a? Hash
|
24
|
+
@entry[:object]
|
25
|
+
else
|
26
|
+
@entry.activity_target
|
27
|
+
end
|
39
28
|
end
|
40
29
|
|
41
30
|
# Returns the verb field or nil if it does not exist.
|
31
|
+
# :favorite, :follow, :like, :"make-friend", :join, :play,
|
32
|
+
# :post, :save, :share, :tag, :update
|
42
33
|
def verb
|
43
|
-
|
44
|
-
|
34
|
+
if @entry.is_a? Hash
|
35
|
+
@entry[:object]
|
36
|
+
else
|
37
|
+
obj = @entry.activity_verb
|
38
|
+
obj[SCHEMA_ROOT.size..-1].intern unless obj.nil?
|
39
|
+
end
|
45
40
|
end
|
46
41
|
|
47
42
|
# Returns the object-type field or nil if it does not exist.
|
43
|
+
# :article, :audio, :bookmark, :comment, :file, :folder, :group,
|
44
|
+
# :list, :note, :person, :photo, :"photo-album", :place, :playlist,
|
45
|
+
# :product, :review, :service, :status, :video
|
48
46
|
def object_type
|
49
|
-
|
50
|
-
|
47
|
+
if @entry.is_a? Hash
|
48
|
+
@entry[:object_type]
|
49
|
+
else
|
50
|
+
obj = @entry.activity_object_type
|
51
|
+
obj[SCHEMA_ROOT.size..-1].intern unless obj.nil?
|
52
|
+
end
|
51
53
|
end
|
52
54
|
|
53
55
|
# Returns a hash of all relevant fields.
|
data/lib/ostatus/author.rb
CHANGED
@@ -4,59 +4,85 @@ require_relative 'portable_contacts'
|
|
4
4
|
module OStatus
|
5
5
|
|
6
6
|
# Holds information about the author of the Feed.
|
7
|
-
class Author
|
8
|
-
|
9
|
-
# Instantiates an Author object either from a given <author></author> root
|
10
|
-
# passed as an instance of a Nokogiri::XML::Element or a Hash containing
|
11
|
-
# the properties.
|
12
|
-
def initialize(author_node)
|
13
|
-
if author_node.class == Hash
|
14
|
-
@author_data = author_node
|
15
|
-
@author = nil
|
16
|
-
else
|
17
|
-
@author = author_node
|
18
|
-
@author_data = nil
|
19
|
-
end
|
20
|
-
end
|
7
|
+
class Author < Atom::Person
|
8
|
+
include Atom::SimpleExtensions
|
21
9
|
|
22
|
-
|
23
|
-
|
24
|
-
def activity
|
25
|
-
OStatus::Activity.new(@author)
|
26
|
-
end
|
10
|
+
add_extension_namespace :activity, ACTIVITY_NS
|
11
|
+
element 'activity:object-type'
|
27
12
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
13
|
+
namespace Atom::NAMESPACE
|
14
|
+
element :email
|
15
|
+
element :uri
|
16
|
+
|
17
|
+
add_extension_namespace :poco, POCO_NS
|
18
|
+
element 'poco:id'
|
19
|
+
element 'poco:displayName'
|
20
|
+
element 'poco:nickname'
|
21
|
+
element 'poco:updated', :class => DateTime, :content_only => true
|
22
|
+
element 'poco:published', :class => DateTime, :content_only => true
|
23
|
+
element 'poco:birthday', :class => Date, :content_only => true
|
24
|
+
element 'poco:anniversary', :class => Date, :content_only => true
|
25
|
+
element 'poco:gender'
|
26
|
+
element 'poco:note'
|
27
|
+
element 'poco:preferredUsername'
|
28
|
+
element 'poco:connected'
|
29
|
+
|
30
|
+
def initialize *args
|
31
|
+
self.activity_object_type = "http://activitystrea.ms/schema/1.0/person"
|
32
|
+
super(*args)
|
34
33
|
end
|
35
|
-
private :pick_first_node
|
36
34
|
|
37
|
-
#
|
35
|
+
# unfortunately ratom doesn't handle elements with the same local name well.
|
36
|
+
# this is a workaround for that.
|
37
|
+
attr_writer :name, :poco_name
|
38
|
+
|
38
39
|
def name
|
39
|
-
|
40
|
-
|
40
|
+
@name or self[Atom::NAMESPACE, 'name'].first
|
41
|
+
end
|
42
|
+
|
43
|
+
def poco_name
|
44
|
+
@poco_name or self[POCO_NS, 'name'].first
|
41
45
|
end
|
42
46
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
def to_xml(*args)
|
48
|
+
x = super(*args)
|
49
|
+
|
50
|
+
if self.name
|
51
|
+
node = XML::Node.new('name')
|
52
|
+
node << self.name
|
53
|
+
x << node
|
54
|
+
end
|
55
|
+
|
56
|
+
if self.poco_name
|
57
|
+
node = XML::Node.new('poco:name')
|
58
|
+
node << self.poco_name
|
59
|
+
x << node
|
60
|
+
end
|
61
|
+
|
62
|
+
x
|
47
63
|
end
|
48
64
|
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
65
|
+
# Gives an instance of an OStatus::Activity that parses the fields
|
66
|
+
# having an activity prefix.
|
67
|
+
def activity
|
68
|
+
OStatus::Activity.new(self)
|
53
69
|
end
|
54
70
|
|
55
71
|
# Returns an instance of a PortableContacts that further describe the
|
56
72
|
# author's contact information, if it exists.
|
57
73
|
def portable_contacts
|
58
|
-
|
59
|
-
|
74
|
+
PortableContacts.new(self)
|
75
|
+
end
|
76
|
+
|
77
|
+
def portable_contacts= poco
|
78
|
+
[ 'id', 'name', 'nickname', 'updated', 'published', 'birthday',
|
79
|
+
'anniversary', 'gender', 'note', 'connected'].each do |p|
|
80
|
+
v = poco.send(p)
|
81
|
+
self.send("poco_#{p}=", v) if v
|
82
|
+
end
|
83
|
+
|
84
|
+
self.poco_displayName = poco.display_name if poco.display_name
|
85
|
+
self.poco_preferredUsername = poco.preferred_username if poco.preferred_username
|
60
86
|
end
|
61
87
|
end
|
62
88
|
end
|
data/lib/ostatus/entry.rb
CHANGED
@@ -1,131 +1,74 @@
|
|
1
1
|
require_relative 'activity'
|
2
|
+
require_relative 'author'
|
2
3
|
|
3
4
|
module OStatus
|
4
5
|
|
5
6
|
# Holds information about an individual entry in the Feed.
|
6
|
-
class Entry
|
7
|
+
class Entry < Atom::Entry
|
8
|
+
include Atom::SimpleExtensions
|
9
|
+
|
10
|
+
add_extension_namespace :activity, ACTIVITY_NS
|
11
|
+
element 'activity:object-type'
|
12
|
+
element 'activity:object'
|
13
|
+
element 'activity:verb'
|
14
|
+
element 'activity:target'
|
15
|
+
|
16
|
+
namespace Atom::NAMESPACE
|
17
|
+
element :title, :id, :summary
|
18
|
+
element :updated, :published, :class => DateTime, :content_only => true
|
19
|
+
element :source, :class => Atom::Source
|
20
|
+
elements :links, :class => Atom::Link
|
21
|
+
elements :categories, :class => Atom::Category
|
22
|
+
element :content, :class => Atom::Content
|
23
|
+
element :author, :class => OStatus::Author
|
7
24
|
|
8
|
-
# Instantiates an Entry object from either a given <entry></entry> root
|
9
|
-
# passed as an instance of a Nokogiri::XML::Element or a Hash
|
10
|
-
# containing the properties.
|
11
|
-
def initialize(entry_node)
|
12
|
-
if entry_node.class == Hash
|
13
|
-
@entry_data = entry_node
|
14
|
-
@entry = nil
|
15
|
-
else
|
16
|
-
@entry = entry_node
|
17
|
-
@entry_data = nil
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# Gives an instance of an OStatus::Activity that parses the fields
|
22
|
-
# having an activity prefix.
|
23
25
|
def activity
|
24
|
-
Activity.new(
|
26
|
+
Activity.new(self)
|
25
27
|
end
|
26
28
|
|
27
|
-
def
|
28
|
-
if
|
29
|
-
|
30
|
-
else
|
31
|
-
a[0].content
|
29
|
+
def activity= value
|
30
|
+
if value.object_type
|
31
|
+
self.activity_object_type = OStatus::Activity::SCHEMA_ROOT + value.object_type.to_s
|
32
32
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
pick_first_node(@entry.css('title'))
|
40
|
-
end
|
41
|
-
|
42
|
-
# Returns the content of the entry.
|
43
|
-
def content
|
44
|
-
return @entry_data[:content] unless @entry_data == nil
|
45
|
-
pick_first_node(@entry.css('content'))
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns the content-type of the entry.
|
49
|
-
def content_type
|
50
|
-
return @entry_data[:content_type] unless @entry_data == nil
|
51
|
-
content = @entry.css('content')
|
52
|
-
content.empty? ? "" : content[0]['type']
|
53
|
-
end
|
54
|
-
|
55
|
-
# Returns the DateTime that this entry was published.
|
56
|
-
def published
|
57
|
-
return @entry_data[:published] unless @entry_data == nil
|
58
|
-
DateTime.parse(pick_first_node(@entry.css('published')))
|
59
|
-
end
|
60
|
-
|
61
|
-
# Returns the DateTime that this entry was updated.
|
62
|
-
def updated
|
63
|
-
return @entry_data[:updated] unless @entry_data == nil
|
64
|
-
DateTime.parse(pick_first_node(@entry.css('updated')))
|
65
|
-
end
|
66
|
-
|
67
|
-
# Returns the id of the entry.
|
68
|
-
def id
|
69
|
-
return @entry_data[:id] unless @entry_data == nil
|
70
|
-
pick_first_node(@entry.css('id'))
|
33
|
+
self.activity_object = value.activity_object if value.object
|
34
|
+
if value.verb
|
35
|
+
self.activity_verb = OStatus::Activity::SCHEMA_ROOT + value.activity_verb.to_s
|
36
|
+
end
|
37
|
+
self.activity_target = value.activity_target if value.target
|
38
|
+
activity_object_type = "HEY"
|
71
39
|
end
|
72
40
|
|
73
41
|
def url
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
@entry.css('link').each do |node|
|
79
|
-
if node[:href]
|
80
|
-
cur_url = node[:href]
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
links = self.link
|
85
|
-
if links[:alternate]
|
86
|
-
links[:alternate][0][:href].value
|
87
|
-
elsif links[:self]
|
88
|
-
links[:self][0][:href].value
|
42
|
+
if links.alternate
|
43
|
+
links.alternate.href
|
44
|
+
elsif links.self
|
45
|
+
links.self.href
|
89
46
|
else
|
90
|
-
|
47
|
+
links.map.each do |l|
|
48
|
+
l.href
|
49
|
+
end.compact.first
|
91
50
|
end
|
92
51
|
end
|
93
52
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
result = {}
|
98
|
-
|
99
|
-
@entry.css('link').each do |node|
|
100
|
-
if node[:rel] != nil
|
101
|
-
rel = node[:rel].intern
|
102
|
-
if result[rel] == nil
|
103
|
-
result[rel] = []
|
104
|
-
end
|
105
|
-
|
106
|
-
attrs = node.attributes
|
107
|
-
|
108
|
-
map = {}
|
109
|
-
attrs.keys.each do |key|
|
110
|
-
map[key.intern] = attrs[key]
|
111
|
-
end
|
53
|
+
def url= value
|
54
|
+
links << Atom::Link.new(:rel => "alternate", :href => value)
|
55
|
+
end
|
112
56
|
|
113
|
-
|
114
|
-
|
115
|
-
|
57
|
+
def link
|
58
|
+
links.group_by { |l| l.rel.intern }
|
59
|
+
end
|
116
60
|
|
117
|
-
|
61
|
+
def link= options
|
62
|
+
links.clear << Atom::Link.new(options)
|
118
63
|
end
|
119
64
|
|
120
65
|
# Returns a Hash of all fields.
|
121
66
|
def info
|
122
|
-
return @entry_data unless @entry_data == nil
|
123
67
|
{
|
124
68
|
:activity => self.activity.info,
|
125
|
-
:id =>
|
69
|
+
:id => self.id,
|
126
70
|
:title => self.title,
|
127
71
|
:content => self.content,
|
128
|
-
:content_type => self.content_type,
|
129
72
|
:link => self.link,
|
130
73
|
:published => self.published,
|
131
74
|
:updated => self.updated
|
data/lib/ostatus/feed.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
require 'nokogiri'
|
2
1
|
require 'open-uri'
|
3
|
-
require '
|
2
|
+
require 'atom'
|
4
3
|
|
5
4
|
require_relative 'entry'
|
6
5
|
require_relative 'author'
|
@@ -8,22 +7,49 @@ require_relative 'author'
|
|
8
7
|
module OStatus
|
9
8
|
|
10
9
|
# This class represents an OStatus Feed object.
|
11
|
-
class Feed
|
10
|
+
class Feed < Atom::Feed
|
11
|
+
include Atom::SimpleExtensions
|
12
|
+
|
13
|
+
namespace Atom::NAMESPACE
|
14
|
+
|
15
|
+
add_extension_namespace :poco, POCO_NS
|
16
|
+
add_extension_namespace :poco, ACTIVITY_NS
|
17
|
+
element :id, :rights, :icon, :logo
|
18
|
+
element :generator, :class => Atom::Generator
|
19
|
+
element :title, :subtitle, :class => Atom::Content
|
20
|
+
element :updated, :class => Time, :content_only => true
|
21
|
+
elements :links, :class => Atom::Link
|
22
|
+
elements :authors, :class => OStatus::Author
|
23
|
+
elements :categories, :class => Atom::Category
|
24
|
+
elements :entries, :class => OStatus::Entry
|
25
|
+
|
26
|
+
attr_reader :url
|
27
|
+
|
12
28
|
def initialize(str, url, access_token, options)
|
13
29
|
@str = str
|
14
30
|
@url = url
|
15
31
|
@access_token = access_token
|
16
32
|
@options = options
|
17
33
|
|
18
|
-
if
|
19
|
-
|
34
|
+
if str
|
35
|
+
super(XML::Reader.string(str))
|
36
|
+
else
|
37
|
+
super(options)
|
20
38
|
end
|
21
39
|
end
|
22
40
|
|
23
41
|
# Creates a new Feed instance given by the atom feed located at 'url'
|
24
42
|
# and optionally using the OAuth::AccessToken given.
|
25
43
|
def Feed.from_url(url, access_token = nil)
|
26
|
-
|
44
|
+
if access_token.nil?
|
45
|
+
# simply open the url
|
46
|
+
str = open(url).read
|
47
|
+
else
|
48
|
+
# open the url through OAuth
|
49
|
+
str = access_token.get(url).body
|
50
|
+
end
|
51
|
+
|
52
|
+
Feed.new(str, url, access_token, nil)
|
27
53
|
end
|
28
54
|
|
29
55
|
# Creates a new Feed instance that contains the information given by
|
@@ -36,52 +62,34 @@ module OStatus
|
|
36
62
|
Feed.new(str, nil, nil, nil)
|
37
63
|
end
|
38
64
|
|
39
|
-
# Returns an array of
|
40
|
-
# that have a rel equal to that given by attribute.
|
41
|
-
# generally as a Hash where the keys are intern strings that give an attribute.
|
65
|
+
# Returns an array of Atom::Link instances for all link tags
|
66
|
+
# that have a rel equal to that given by attribute.
|
42
67
|
#
|
43
68
|
# For example:
|
44
|
-
# link(:hub).first
|
45
|
-
#
|
69
|
+
# link(:hub).first.href -- Gets the first link tag with rel="hub" and
|
70
|
+
# returns the contents of the href attribute.
|
46
71
|
#
|
47
72
|
def link(attribute)
|
48
|
-
|
49
|
-
|
73
|
+
links.find_all { |l| l.rel == attribute.to_s }
|
74
|
+
end
|
50
75
|
|
51
|
-
|
52
|
-
|
53
|
-
|
76
|
+
def links=(given)
|
77
|
+
self.links.clear
|
78
|
+
given.each do |rel,links|
|
79
|
+
links.each do |l|
|
80
|
+
self.links << Atom::Link.new(l.merge({:rel => rel}))
|
81
|
+
end
|
54
82
|
end
|
55
83
|
end
|
56
84
|
|
57
85
|
# Returns an array of URLs for each hub link tag.
|
58
86
|
def hubs
|
59
|
-
|
60
|
-
link(:hub).map do |link|
|
61
|
-
link[:href]
|
62
|
-
end
|
63
|
-
else
|
64
|
-
[]
|
65
|
-
end
|
87
|
+
link(:hub).map { |link| link.href }
|
66
88
|
end
|
67
89
|
|
68
90
|
# Returns the salmon URL from the link tag.
|
69
91
|
def salmon
|
70
|
-
link(:salmon).first
|
71
|
-
end
|
72
|
-
|
73
|
-
# Returns the logo
|
74
|
-
def logo
|
75
|
-
return @options[:logo] unless @options == nil
|
76
|
-
|
77
|
-
pick_first_node(@xml.xpath('/xmlns:feed/xmlns:logo'))
|
78
|
-
end
|
79
|
-
|
80
|
-
# Returns the icon
|
81
|
-
def icon
|
82
|
-
return @options[:icon] unless @options == nil
|
83
|
-
|
84
|
-
pick_first_node(@xml.xpath('/xmlns:feed/xmlns:icon'))
|
92
|
+
link(:salmon).first.href
|
85
93
|
end
|
86
94
|
|
87
95
|
# This method will return a String containing the actual content of
|
@@ -97,88 +105,25 @@ module OStatus
|
|
97
105
|
# open the url through OAuth
|
98
106
|
@access_token.get(@url).body
|
99
107
|
else
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
self.title,
|
104
|
-
|
105
|
-
@url,
|
106
|
-
|
107
|
-
:author_name => self.author.name,
|
108
|
-
:author_email => self.author.email,
|
109
|
-
:author_uri => self.author.uri,
|
110
|
-
|
111
|
-
:hubs => self.hubs
|
112
|
-
)
|
113
|
-
|
114
|
-
@options[:entries].each do |entry|
|
115
|
-
entry_url = entry.url
|
116
|
-
entry_url = @url if entry_url == nil
|
117
|
-
|
118
|
-
feed.add_entry(
|
119
|
-
entry.id,
|
120
|
-
entry.title,
|
121
|
-
entry.updated,
|
122
|
-
|
123
|
-
entry_url,
|
124
|
-
|
125
|
-
:published => entry.published,
|
126
|
-
|
127
|
-
:content => entry.content,
|
128
|
-
|
129
|
-
:author_name => self.author.name,
|
130
|
-
:author_email => self.author.email,
|
131
|
-
:author_uri => self.author.uri
|
132
|
-
)
|
133
|
-
end
|
134
|
-
|
135
|
-
feed.make(:indent => 2)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def pick_first_node(a)
|
140
|
-
if a.empty?
|
141
|
-
nil
|
142
|
-
else
|
143
|
-
a[0].content
|
108
|
+
self.links << Atom::Link.new(:rel => 'self', :href => @url) if @url
|
109
|
+
self.links << Atom::Link.new(:rel => 'edit', :href => @url) if @url
|
110
|
+
self.to_xml
|
144
111
|
end
|
145
112
|
end
|
146
|
-
private :pick_first_node
|
147
|
-
|
148
|
-
def id
|
149
|
-
return @options[:id] unless @options == nil
|
150
|
-
|
151
|
-
pick_first_node(@xml.xpath('/xmlns:feed/xmlns:id'))
|
152
|
-
end
|
153
|
-
|
154
|
-
def title
|
155
|
-
return @options[:title] unless @options == nil
|
156
|
-
|
157
|
-
pick_first_node(@xml.xpath('/xmlns:feed/xmlns:title'))
|
158
|
-
end
|
159
|
-
|
160
|
-
def url
|
161
|
-
return @url
|
162
|
-
end
|
163
113
|
|
164
114
|
# Returns an OStatus::Author that will parse the author information
|
165
115
|
# within the Feed.
|
166
116
|
def author
|
167
|
-
|
168
|
-
|
169
|
-
author_xml = @xml.at_css('author')
|
170
|
-
OStatus::Author.new(author_xml)
|
117
|
+
@options ? @options[:author] : self.authors.first
|
171
118
|
end
|
172
119
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
return @options[:entries] unless @options == nil
|
177
|
-
|
178
|
-
entries_xml = @xml.css('entry')
|
120
|
+
def author= author
|
121
|
+
self.authors.clear << author
|
122
|
+
end
|
179
123
|
|
180
|
-
|
181
|
-
|
124
|
+
def hubs= hubs
|
125
|
+
hubs.each do |hub|
|
126
|
+
links << Atom::Link.new(:rel => 'hub', :href => hub)
|
182
127
|
end
|
183
128
|
end
|
184
129
|
end
|
@@ -1,116 +1,59 @@
|
|
1
1
|
module OStatus
|
2
|
+
POCO_NS = 'http://portablecontacts.net/spec/1.0'
|
2
3
|
|
3
4
|
# Holds information about the extended contact information
|
4
5
|
# in the Feed given in the Portable Contacts specification.
|
5
6
|
class PortableContacts
|
6
|
-
|
7
|
-
# Instantiates a OStatus::PortableContacts object from either
|
8
|
-
# a given root that contains all <poco:*> tags as a
|
9
|
-
# Nokogiri::XML::Element or a Hash containing the properties.
|
10
|
-
def initialize(author_node)
|
11
|
-
if author_node.class == Hash
|
12
|
-
@poco_data = author_node
|
13
|
-
@poco = nil
|
14
|
-
else
|
15
|
-
@poco = author_node
|
16
|
-
@poco_data = nil
|
17
|
-
end
|
18
|
-
end
|
19
7
|
|
20
|
-
|
21
|
-
|
22
|
-
|
8
|
+
# Instantiates a OStatus::PortableContacts object from either
|
9
|
+
# a given root that contains all <poco:*> tags as an ratom Person
|
10
|
+
# or a Hash containing the properties.
|
11
|
+
def initialize(parent)
|
12
|
+
if parent.is_a? Hash
|
13
|
+
@options = parent
|
23
14
|
else
|
24
|
-
|
15
|
+
@parent = parent
|
25
16
|
end
|
26
17
|
end
|
27
|
-
private :pick_first_node
|
28
|
-
|
29
|
-
# Returns the id of the contact, if it exists.
|
30
|
-
def id
|
31
|
-
return @poco_data[:id] unless @poco_data == nil
|
32
|
-
pick_first_node(@poco.xpath('./poco:id'))
|
33
|
-
end
|
34
18
|
|
35
|
-
|
36
|
-
def
|
37
|
-
return @poco_data[:display_name] unless @poco_data == nil
|
38
|
-
pick_first_node(@poco.xpath('./poco:displayName'))
|
39
|
-
end
|
19
|
+
def id; get_prop(:id); end
|
20
|
+
def id= value; set_prop(:id, value); end
|
40
21
|
|
41
|
-
|
42
|
-
def name
|
43
|
-
return @poco_data[:name] unless @poco_data == nil
|
44
|
-
pick_first_node(@poco.xpath('./poco:name'))
|
45
|
-
end
|
22
|
+
def name; get_prop(:name); end
|
23
|
+
def name= value; set_prop(:name, value); end
|
46
24
|
|
47
|
-
|
48
|
-
def nickname
|
49
|
-
return @poco_data[:nickname] unless @poco_data == nil
|
50
|
-
pick_first_node(@poco.xpath('./poco:nickname'))
|
51
|
-
end
|
25
|
+
def nickname; get_prop(:nickname); end
|
26
|
+
def nickname= value; set_prop(:nickname, value); end
|
52
27
|
|
53
|
-
|
54
|
-
def
|
55
|
-
return @poco_data[:published] unless @poco_data == nil
|
56
|
-
pub = pick_first_node(@poco.xpath('./poco:published'))
|
57
|
-
if pub != nil
|
58
|
-
DateTime.parse(pub)
|
59
|
-
end
|
60
|
-
end
|
28
|
+
def gender; get_prop(:gender); end
|
29
|
+
def gender= value; set_prop(:gender, value); end
|
61
30
|
|
62
|
-
|
63
|
-
def
|
64
|
-
return @poco_data[:updated] unless @poco_data == nil
|
65
|
-
upd = pick_first_node(@poco.xpath('./poco:updated'))
|
66
|
-
if upd != nil
|
67
|
-
DateTime.parse(upd)
|
68
|
-
end
|
69
|
-
end
|
31
|
+
def note; get_prop(:note); end
|
32
|
+
def note= value; set_prop(:note, value); end
|
70
33
|
|
71
|
-
|
72
|
-
def
|
73
|
-
return @poco_data[:birthday] unless @poco_data == nil
|
74
|
-
bday = pick_first_node(@poco.xpath('./poco:birthday'))
|
75
|
-
if bday != nil
|
76
|
-
Date.parse(bday)
|
77
|
-
end
|
78
|
-
end
|
34
|
+
def display_name; get_prop(:display_name, 'displayName'); end
|
35
|
+
def display_name= value; set_prop(:display_name, value, 'displayName'); end
|
79
36
|
|
80
|
-
|
81
|
-
|
82
|
-
return @poco_data[:anniversary] unless @poco_data == nil
|
83
|
-
anni = pick_first_node(@poco.xpath('./poco:anniversary'))
|
84
|
-
if anni != nil
|
85
|
-
Date.parse(anni)
|
86
|
-
end
|
37
|
+
def preferred_username
|
38
|
+
get_prop(:preferred_username, 'preferredUsername')
|
87
39
|
end
|
88
40
|
|
89
|
-
|
90
|
-
|
91
|
-
return @poco_data[:gender] unless @poco_data == nil
|
92
|
-
pick_first_node(@poco.xpath('./poco:gender'))
|
41
|
+
def preferred_username= value
|
42
|
+
set_prop(:preferred_username, value, 'preferredUsername')
|
93
43
|
end
|
94
44
|
|
95
|
-
|
96
|
-
def
|
97
|
-
return @poco_data[:note] unless @poco_data == nil
|
98
|
-
pick_first_node(@poco.xpath('./poco:note'))
|
99
|
-
end
|
45
|
+
def updated; get_datetime(:updated); end
|
46
|
+
def published; get_datetime(:published); end
|
100
47
|
|
101
|
-
|
102
|
-
def
|
103
|
-
return @poco_data[:preferred_username] unless @poco_data == nil
|
104
|
-
pick_first_node(@poco.xpath('./poco:preferredUsername'))
|
105
|
-
end
|
48
|
+
def birthday; get_date(:birthday); end
|
49
|
+
def anniversary; get_date(:anniversary); end
|
106
50
|
|
107
51
|
# Returns a boolean that indicates that a bi-directional connection
|
108
52
|
# has been established between the user and the contact, if it is
|
109
53
|
# able to assert this.
|
110
54
|
def connected
|
111
|
-
return @
|
112
|
-
str =
|
113
|
-
return nil if str == nil
|
55
|
+
return @options[:connected] unless @options.nil?
|
56
|
+
str = @parent.poco_connected
|
114
57
|
|
115
58
|
if str == "true"
|
116
59
|
true
|
@@ -120,5 +63,38 @@ module OStatus
|
|
120
63
|
nil
|
121
64
|
end
|
122
65
|
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def get_prop name, xmlName = name
|
70
|
+
@options ? @options[name] : @parent.send("poco_#{xmlName}")
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_prop name, value, xmlName = name
|
74
|
+
if @options
|
75
|
+
@options[name] = value
|
76
|
+
else
|
77
|
+
@parent.send("poco_#{xmlName}=", value)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_datetime x
|
82
|
+
if @options
|
83
|
+
dt = @options[x]
|
84
|
+
DateTime.parse(dt) if dt
|
85
|
+
else
|
86
|
+
@parent.send("poco_#{x}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_date x
|
91
|
+
if @options
|
92
|
+
d = @options[x]
|
93
|
+
Date.parse(d) if d
|
94
|
+
else
|
95
|
+
@parent.send("poco_#{x}")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
123
99
|
end
|
124
100
|
end
|
data/lib/ostatus/version.rb
CHANGED
data/lib/ostatus.rb
CHANGED
data/ostatus.gemspec
CHANGED
@@ -15,8 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.rubyforge_project = "ostatus"
|
16
16
|
|
17
17
|
s.add_dependency "oauth"
|
18
|
-
s.add_dependency "
|
19
|
-
s.add_dependency "tinyatom"
|
18
|
+
s.add_dependency "ratom"
|
20
19
|
s.add_development_dependency "rspec"
|
21
20
|
|
22
21
|
s.files = `git ls-files`.split("\n")
|
data/spec/activity_spec.rb
CHANGED
@@ -23,7 +23,7 @@ describe OStatus::Activity do
|
|
23
23
|
|
24
24
|
describe "#object-type" do
|
25
25
|
it "should give a String containing the content of the activity:object-type tag" do
|
26
|
-
@activity.object_type.should eql(
|
26
|
+
@activity.object_type.should eql(:note)
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should give nil when no activity:object-type was given" do
|
@@ -33,7 +33,7 @@ describe OStatus::Activity do
|
|
33
33
|
|
34
34
|
describe "#verb" do
|
35
35
|
it "should give a String containing the content of the activity:verb tag" do
|
36
|
-
@activity.verb.should eql(
|
36
|
+
@activity.verb.should eql(:post)
|
37
37
|
end
|
38
38
|
|
39
39
|
it "should give nil when no activity:verb was given" do
|
data/spec/author_spec.rb
CHANGED
@@ -16,7 +16,7 @@ describe OStatus::Author do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it "should give an Activity instance that is relevant to the author subtree" do
|
19
|
-
@author.activity.object_type.should eql(
|
19
|
+
@author.activity.object_type.should eql(:person)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require_relative '../lib/ostatus/feed.rb'
|
2
|
+
|
3
|
+
describe 'XML builder' do
|
4
|
+
before(:each) do
|
5
|
+
@feed_url = 'http://example.org/feed'
|
6
|
+
@poco_id = '68b329da9893e34099c7d8ad5cb9c940'
|
7
|
+
@poco = OStatus::PortableContacts.new(:id => @poco_id,
|
8
|
+
:display_name => 'Dean Venture',
|
9
|
+
:preferred_username => 'dean')
|
10
|
+
@author = OStatus::Author.new(:name => 'Dean Venture',
|
11
|
+
:email => 'dean@venture.com',
|
12
|
+
:uri => 'http://geocities.com/~dean',
|
13
|
+
:portable_contacts => @poco)
|
14
|
+
@feed = OStatus::Feed.from_data(@feed_url,
|
15
|
+
:title => "Dean's Updates",
|
16
|
+
:id => @feed_url,
|
17
|
+
:author => @author,
|
18
|
+
:entries => [],
|
19
|
+
:links => {
|
20
|
+
:hub => [{:href => 'http://example.org/hub'}]
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should generate the title' do
|
25
|
+
@feed.atom.should match("<title>Dean's Updates")
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should generate the id' do
|
29
|
+
@feed.atom.should match("<id>#{@feed_url}")
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should generate a self link' do
|
33
|
+
# depending on this attribute order is a really terrible idea, but oh well.
|
34
|
+
@feed.atom.should match("<link rel=\"self\" href=\"#{@feed_url}\"/>")
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should generate the hub link' do
|
38
|
+
# depending on this attribute order is a really terrible idea, but oh well.
|
39
|
+
@feed.atom.should match('<link rel="hub" href="http://example.org/hub"/>')
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'when generating the author' do
|
43
|
+
specify { @feed.atom.should match('<name>Dean Venture') }
|
44
|
+
specify { @feed.atom.should match('<email>dean@venture.com') }
|
45
|
+
specify { @feed.atom.should match('<uri>http://geocities.com/~dean') }
|
46
|
+
specify { @feed.atom.should match("<poco:id>#{@poco_id}") }
|
47
|
+
specify { @feed.atom.should match('<poco:displayName>Dean Venture') }
|
48
|
+
specify { @feed.atom.should match('<poco:preferredUsername>dean') }
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'when generating a feed with entries' do
|
52
|
+
before do
|
53
|
+
@now = Time.now
|
54
|
+
|
55
|
+
@feed.entries << OStatus::Entry.new(
|
56
|
+
:title => 'atom powered robots are running amok lol',
|
57
|
+
:content => 'atom powered robots are running amok lol',
|
58
|
+
:updated => @now,
|
59
|
+
:published => @now,
|
60
|
+
:id => 'http://example.org/feed/1',
|
61
|
+
:link => { :href => 'http://example.org/feed/1' }
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
specify { @feed.atom.should match('<title>atom powered robots') }
|
66
|
+
specify { @feed.atom.should match('<content>atom powered robots') }
|
67
|
+
specify { @feed.atom.should match("<updated>#{@now.iso8601}") }
|
68
|
+
specify { @feed.atom.should match("<published>#{@now.iso8601}") }
|
69
|
+
specify { @feed.atom.should match('<id>http://example.org/feed/1') }
|
70
|
+
specify { @feed.atom.should match('<link href="http://example.org/feed/1"/>') }
|
71
|
+
end
|
72
|
+
end
|
data/spec/entry_spec.rb
CHANGED
@@ -26,12 +26,6 @@ describe OStatus::Entry do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
describe "#content_type" do
|
30
|
-
it "should give a String containing the content of the type attribute on the content tag" do
|
31
|
-
@entry.content_type.should eql("html")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
29
|
describe "#updated" do
|
36
30
|
it "should return a DateTime instance" do
|
37
31
|
@entry.updated.instance_of?(DateTime).should eql(true)
|
@@ -58,12 +52,6 @@ describe OStatus::Entry do
|
|
58
52
|
end
|
59
53
|
end
|
60
54
|
|
61
|
-
describe "#url" do
|
62
|
-
it "should return a String" do
|
63
|
-
@entry.url.class.should eql(String)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
55
|
describe "#info" do
|
68
56
|
it "should give a Hash" do
|
69
57
|
@entry.info.instance_of?(Hash).should eql(true)
|
@@ -81,10 +69,6 @@ describe OStatus::Entry do
|
|
81
69
|
@entry.info[:title].should eql("staples come out of the head tomorrow, oh yeah")
|
82
70
|
end
|
83
71
|
|
84
|
-
it "should contain the content_type" do
|
85
|
-
@entry.info[:content_type].should eql("html")
|
86
|
-
end
|
87
|
-
|
88
72
|
it "should contain a Hash for the link" do
|
89
73
|
@entry.info[:link].class.should eql(Hash)
|
90
74
|
end
|
data/test/example_feed.atom
CHANGED
@@ -22,11 +22,11 @@
|
|
22
22
|
<poco:id>foobar</poco:id>
|
23
23
|
<poco:name>barbaz</poco:name>
|
24
24
|
<poco:nickname>spaz</poco:nickname>
|
25
|
-
<poco:published>2012-02-21T02:15:14+00:00</
|
26
|
-
<poco:updated>2013-02-21T02:15:14+00:00</
|
27
|
-
<poco:birthday>2014-02-21</
|
28
|
-
<poco:anniversary>2015-02-21</
|
29
|
-
<poco:gender>male</
|
25
|
+
<poco:published>2012-02-21T02:15:14+00:00</poco:published>
|
26
|
+
<poco:updated>2013-02-21T02:15:14+00:00</poco:updated>
|
27
|
+
<poco:birthday>2014-02-21</poco:birthday>
|
28
|
+
<poco:anniversary>2015-02-21</poco:anniversary>
|
29
|
+
<poco:gender>male</poco:gender>
|
30
30
|
<poco:note>foo
|
31
31
|
bar</poco:note>
|
32
32
|
<poco:utcOffset>-08:00</poco:utcOffset>
|
@@ -72,7 +72,7 @@ bar</poco:note>
|
|
72
72
|
<entry>
|
73
73
|
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
74
74
|
<activity:object>Foobar</activity:object>
|
75
|
-
<activity:target>Barbaz</activity:
|
75
|
+
<activity:target>Barbaz</activity:target>
|
76
76
|
<id>http://identi.ca/notice/64991641</id>
|
77
77
|
<title>staples come out of the head tomorrow, oh yeah</title>
|
78
78
|
<content type="html">staples come out of the head tomorrow, oh yeah</content>
|
@@ -48,7 +48,7 @@
|
|
48
48
|
<entry>
|
49
49
|
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
50
50
|
<activity:object>Foobar</activity:object>
|
51
|
-
<activity:target>Barbaz</activity:
|
51
|
+
<activity:target>Barbaz</activity:target>
|
52
52
|
<id>http://identi.ca/notice/64991641</id>
|
53
53
|
<title>staples come out of the head tomorrow, oh yeah</title>
|
54
54
|
<content type="html">staples come out of the head tomorrow, oh yeah</content>
|
@@ -22,11 +22,11 @@
|
|
22
22
|
<poco:id>foobar</poco:id>
|
23
23
|
<poco:name>barbaz</poco:name>
|
24
24
|
<poco:nickname>spaz</poco:nickname>
|
25
|
-
<poco:published>2012-02-21T02:15:14+00:00</
|
26
|
-
<poco:updated>2013-02-21T02:15:14+00:00</
|
27
|
-
<poco:birthday>2014-02-21</
|
28
|
-
<poco:anniversary>2015-02-21</
|
29
|
-
<poco:gender>male</
|
25
|
+
<poco:published>2012-02-21T02:15:14+00:00</poco:published>
|
26
|
+
<poco:updated>2013-02-21T02:15:14+00:00</poco:updated>
|
27
|
+
<poco:birthday>2014-02-21</poco:birthday>
|
28
|
+
<poco:anniversary>2015-02-21</poco:anniversary>
|
29
|
+
<poco:gender>male</poco:gender>
|
30
30
|
<poco:note>foo
|
31
31
|
bar</poco:note>
|
32
32
|
<poco:utcOffset>-08:00</poco:utcOffset>
|
@@ -71,7 +71,7 @@ bar</poco:note>
|
|
71
71
|
<entry>
|
72
72
|
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
73
73
|
<activity:object>Foobar</activity:object>
|
74
|
-
<activity:target>Barbaz</activity:
|
74
|
+
<activity:target>Barbaz</activity:target>
|
75
75
|
<id>http://identi.ca/notice/64991641</id>
|
76
76
|
<title>staples come out of the head tomorrow, oh yeah</title>
|
77
77
|
<content type="html">staples come out of the head tomorrow, oh yeah</content>
|
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
|
+
- 7
|
9
|
+
version: 0.0.7
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Hackers of the Severed Hand
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-
|
17
|
+
date: 2011-04-02 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -31,7 +31,7 @@ dependencies:
|
|
31
31
|
type: :runtime
|
32
32
|
version_requirements: *id001
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: ratom
|
35
35
|
prerelease: false
|
36
36
|
requirement: &id002 !ruby/object:Gem::Requirement
|
37
37
|
none: false
|
@@ -43,23 +43,10 @@ dependencies:
|
|
43
43
|
version: "0"
|
44
44
|
type: :runtime
|
45
45
|
version_requirements: *id002
|
46
|
-
- !ruby/object:Gem::Dependency
|
47
|
-
name: tinyatom
|
48
|
-
prerelease: false
|
49
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
segments:
|
55
|
-
- 0
|
56
|
-
version: "0"
|
57
|
-
type: :runtime
|
58
|
-
version_requirements: *id003
|
59
46
|
- !ruby/object:Gem::Dependency
|
60
47
|
name: rspec
|
61
48
|
prerelease: false
|
62
|
-
requirement: &
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
63
50
|
none: false
|
64
51
|
requirements:
|
65
52
|
- - ">="
|
@@ -68,7 +55,7 @@ dependencies:
|
|
68
55
|
- 0
|
69
56
|
version: "0"
|
70
57
|
type: :development
|
71
|
-
version_requirements: *
|
58
|
+
version_requirements: *id003
|
72
59
|
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.
|
73
60
|
email:
|
74
61
|
- hotsh@xomb.org
|
@@ -93,6 +80,7 @@ files:
|
|
93
80
|
- ostatus.gemspec
|
94
81
|
- spec/activity_spec.rb
|
95
82
|
- spec/author_spec.rb
|
83
|
+
- spec/builder_spec.rb
|
96
84
|
- spec/entry_spec.rb
|
97
85
|
- spec/feed_spec.rb
|
98
86
|
- spec/portable_contacts_spec.rb
|
@@ -134,6 +122,7 @@ summary: Implementations of the OStatus data stream objects.
|
|
134
122
|
test_files:
|
135
123
|
- spec/activity_spec.rb
|
136
124
|
- spec/author_spec.rb
|
125
|
+
- spec/builder_spec.rb
|
137
126
|
- spec/entry_spec.rb
|
138
127
|
- spec/feed_spec.rb
|
139
128
|
- spec/portable_contacts_spec.rb
|