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