ostatus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ostatus.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,9 @@
1
+ OStatus
2
+ =======
3
+
4
+ This gem implements the OStatus protocol data streams and the technologies that are related to it such as ActivityStreams, PortableContacts, and Salmon.
5
+
6
+ What it does
7
+ ------------
8
+
9
+ Right now, it simply parses Atom and gives the ability to parse the XML for each of the objects in the OStatus world.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ # rake spec
4
+ RSpec::Core::RakeTask.new(:spec) do |spec|
5
+ spec.pattern = 'spec/*_spec.rb'
6
+ end
7
+
8
+ # rake doc
9
+ RSpec::Core::RakeTask.new(:doc) do |spec|
10
+ spec.pattern = 'spec/*_spec.rb'
11
+ spec.rspec_opts = ['--format documentation']
12
+ end
13
+
14
+ require 'bundler'
15
+ Bundler::GemHelper.install_tasks
data/lib/ostatus.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'nokogiri'
2
+
3
+ module OStatus
4
+ end
@@ -0,0 +1,63 @@
1
+ module OStatus
2
+
3
+ # This class represents an Activity object for an OStatus entry.
4
+ class Activity
5
+
6
+ # This will create an instance of an Activity class populated
7
+ # with the given data as a Hash or parsable XML given by a
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
18
+ end
19
+
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
+ # Returns the object field or nil if it does not exist.
30
+ def object
31
+ return @activity_data[:object] unless @activity_data == nil
32
+ pick_first_node(@activity.xpath('./activity:object'))
33
+ end
34
+
35
+ # Returns the target field or nil if it does not exist.
36
+ def target
37
+ return @activity_data[:target] unless @activity_data == nil
38
+ pick_first_node(@activity.xpath('./activity:target'))
39
+ end
40
+
41
+ # Returns the verb field or nil if it does not exist.
42
+ def verb
43
+ return @activity_data[:verb] unless @activity_data == nil
44
+ pick_first_node(@activity.xpath('./activity:verb'))
45
+ end
46
+
47
+ # Returns the object-type field or nil if it does not exist.
48
+ def object_type
49
+ return @activity_data[:object_type] unless @activity_data == nil
50
+ pick_first_node(@activity.xpath('./activity:object-type'))
51
+ end
52
+
53
+ # Returns a hash of all relevant fields.
54
+ def info
55
+ {
56
+ :object => self.object,
57
+ :target => self.target,
58
+ :verb => self.verb,
59
+ :object_type => self.object_type
60
+ }
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,62 @@
1
+ require_relative 'activity'
2
+ require_relative 'portable_contacts'
3
+
4
+ module OStatus
5
+
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
21
+
22
+ # Gives an instance of an OStatus::Activity that parses the fields
23
+ # having an activity prefix.
24
+ def activity
25
+ OStatus::Activity.new(@author)
26
+ end
27
+
28
+ def pick_first_node(a)
29
+ if a.empty?
30
+ nil
31
+ else
32
+ a[0].content
33
+ end
34
+ end
35
+ private :pick_first_node
36
+
37
+ # Returns the name of the author, if it exists.
38
+ def name
39
+ return @author_data[:name] unless @author_data == nil
40
+ pick_first_node(@author.css('name'))
41
+ end
42
+
43
+ # Returns the email of the author, if it exists.
44
+ def email
45
+ return @author_data[:email] unless @author_data == nil
46
+ pick_first_node(@author.css('email'))
47
+ end
48
+
49
+ # Returns the uri of the author, if it exists.
50
+ def uri
51
+ return @author_data[:uri] unless @author_data == nil
52
+ pick_first_node(@author.css('uri'))
53
+ end
54
+
55
+ # Returns an instance of a PortableContacts that further describe the
56
+ # author's contact information, if it exists.
57
+ def portable_contacts
58
+ return @author_data[:portable_contacts] unless @author_data == nil
59
+ PortableContacts.new(@author)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,107 @@
1
+ require_relative 'activity'
2
+
3
+ module OStatus
4
+
5
+ # Holds information about an individual entry in the Feed.
6
+ class Entry
7
+
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
+ def activity
24
+ Activity.new(@entry)
25
+ end
26
+
27
+ def pick_first_node(a)
28
+ if a.empty?
29
+ nil
30
+ else
31
+ a[0].content
32
+ end
33
+ end
34
+ private :pick_first_node
35
+
36
+ # Returns the title of the entry.
37
+ def title
38
+ return @entry_data[:title] unless @entry_data == nil
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'))
71
+ end
72
+
73
+ def link
74
+ return @entry_data[:link] unless @entry_data == nil
75
+
76
+ result = {}
77
+
78
+ @entry.css('link').each do |node|
79
+ if node[:rel] != nil
80
+ rel = node[:rel].intern
81
+ if result[rel] == nil
82
+ result[rel] = []
83
+ end
84
+
85
+ result[rel] << node
86
+ end
87
+ end
88
+
89
+ result
90
+ end
91
+
92
+ # Returns a Hash of all fields.
93
+ def info
94
+ return @entry_data unless @entry_data == nil
95
+ {
96
+ :activity => self.activity.info,
97
+ :id => pick_first_node(@entry.css('id')),
98
+ :title => self.title,
99
+ :content => self.content,
100
+ :content_type => self.content_type,
101
+ :link => self.link,
102
+ :published => self.published,
103
+ :updated => self.updated
104
+ }
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,156 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+ require 'tinyatom'
4
+
5
+ require_relative 'entry'
6
+
7
+ module OStatus
8
+
9
+ # This class represents an OStatus Feed object.
10
+ class Feed
11
+ def initialize(url, access_token, author, entries, id, title, links)
12
+ @url = url
13
+ @access_token = access_token
14
+ @author = author
15
+ @entries = entries
16
+ @id = id
17
+ @title = title
18
+ @links = links
19
+
20
+ if id == nil
21
+ @xml = Nokogiri::XML::Document.parse(self.atom)
22
+ end
23
+ end
24
+
25
+ # Creates a new Feed instance given by the atom feed located at 'url'
26
+ # and optionally using the OAuth::AccessToken given.
27
+ def Feed.from_url(url, access_token = nil)
28
+ Feed.new(url, access_token, nil, nil, nil, nil, nil)
29
+ end
30
+
31
+ # Creates a new Feed instance that contains the information given by
32
+ # the various instances of author and entries.
33
+ def Feed.from_data(id, title, url, author, entries, links)
34
+ Feed.new(url, nil, author, entries, id, title, links)
35
+ end
36
+
37
+ # Returns an array of Nokogiri::XML::Element instances for all link tags
38
+ # that have a rel equal to that given by attribute. This can be used
39
+ # generally as a Hash where the keys are intern strings that give an attribute.
40
+ #
41
+ # For example:
42
+ # link(:hub).first[:href] -- Gets the first link tag with rel="hub" and
43
+ # returns the contents of the href attribute.
44
+ #
45
+ def link(attribute)
46
+ return @links[attribute] unless @links == nil
47
+
48
+ # get all links with rel attribute being equal to attribute
49
+ @xml.xpath('/xmlns:feed/xmlns:link').select do |link|
50
+ link[:rel] == attribute.to_s
51
+ end
52
+ end
53
+
54
+ # Returns an array of URLs for each hub link tag.
55
+ def hubs
56
+ link(:hub).map do |link|
57
+ link[:href]
58
+ end
59
+ end
60
+
61
+ # Returns the salmon URL from the link tag.
62
+ def salmon
63
+ link(:salmon).first[:href]
64
+ end
65
+
66
+ # This method will return a String containing the actual content of
67
+ # the atom feed. It will make a network request (through OAuth if
68
+ # an access token was given) to retrieve the document if necessary.
69
+ def atom
70
+ if @id == nil and @access_token == nil
71
+ # simply open the url
72
+ open(@url).read
73
+ elsif @id == nil and @url != nil
74
+ # open the url through OAuth
75
+ @access_token.get(@url).body
76
+ else
77
+ # build the atom file from internal information
78
+ feed = TinyAtom::Feed.new(
79
+ self.id,
80
+ self.title,
81
+ @url,
82
+
83
+ :author_name => self.author.name,
84
+ :author_email => self.author.email,
85
+ :author_uri => self.author.uri,
86
+
87
+ :hubs => self.hubs
88
+ )
89
+
90
+ @entries.each do |entry|
91
+ feed.add_entry(
92
+ entry.id,
93
+ entry.title,
94
+ entry.updated,
95
+
96
+ '',
97
+
98
+ :content => entry.content,
99
+
100
+ :author_name => self.author.name,
101
+ :author_email => self.author.email,
102
+ :author_uri => self.author.uri
103
+ )
104
+ end
105
+
106
+ feed.make(:indent => 2)
107
+ end
108
+ end
109
+
110
+ def pick_first_node(a)
111
+ if a.empty?
112
+ nil
113
+ else
114
+ a[0].content
115
+ end
116
+ end
117
+ private :pick_first_node
118
+
119
+ def id
120
+ return @id if @xml == nil
121
+
122
+ pick_first_node(@xml.xpath('/xmlns:feed/xmlns:id'))
123
+ end
124
+
125
+ def title
126
+ return @title if @xml == nil
127
+
128
+ pick_first_node(@xml.xpath('/xmlns:feed/xmlns:title'))
129
+ end
130
+
131
+ def url
132
+ return @url
133
+ end
134
+
135
+ # Returns an OStatus::Author that will parse the author information
136
+ # within the Feed.
137
+ def author
138
+ return @author if @xml == nil
139
+
140
+ author_xml = @xml.at_css('author')
141
+ OStatus::Author.new(author_xml)
142
+ end
143
+
144
+ # This method gives you an array of OStatus::Entry instances for
145
+ # each entry listed in the feed.
146
+ def entries
147
+ return @entries if @xml == nil
148
+
149
+ entries_xml = @xml.css('entry')
150
+
151
+ entries_xml.map do |entry|
152
+ OStatus::Entry.new(entry)
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,124 @@
1
+ module OStatus
2
+
3
+ # Holds information about the extended contact information
4
+ # in the Feed given in the Portable Contacts specification.
5
+ 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
+
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
+ # 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
+
35
+ # Returns the display_name of the contact, if it exists.
36
+ def display_name
37
+ return @poco_data[:display_name] unless @poco_data == nil
38
+ pick_first_node(@poco.xpath('./poco:displayName'))
39
+ end
40
+
41
+ # Returns the name of the contact, if it exists.
42
+ def name
43
+ return @poco_data[:name] unless @poco_data == nil
44
+ pick_first_node(@poco.xpath('./poco:name'))
45
+ end
46
+
47
+ # Returns the nickname of the contact, if it exists.
48
+ def nickname
49
+ return @poco_data[:nickname] unless @poco_data == nil
50
+ pick_first_node(@poco.xpath('./poco:nickname'))
51
+ end
52
+
53
+ # Returns the published of the contact, if it exists.
54
+ def published
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
61
+
62
+ # Returns the updated of the contact, if it exists.
63
+ def updated
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
70
+
71
+ # Returns the birthday of the contact, if it exists.
72
+ def birthday
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
79
+
80
+ # Returns the anniversary of the contact, if it exists.
81
+ def anniversary
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
87
+ end
88
+
89
+ # Returns the gender of the contact, if it exists.
90
+ def gender
91
+ return @poco_data[:gender] unless @poco_data == nil
92
+ pick_first_node(@poco.xpath('./poco:gender'))
93
+ end
94
+
95
+ # Returns the note of the contact, if it exists.
96
+ def note
97
+ return @poco_data[:note] unless @poco_data == nil
98
+ pick_first_node(@poco.xpath('./poco:note'))
99
+ end
100
+
101
+ # Returns the preferred username of the contact, if it exists.
102
+ def preferred_username
103
+ return @poco_data[:preferred_username] unless @poco_data == nil
104
+ pick_first_node(@poco.xpath('./poco:preferredUsername'))
105
+ end
106
+
107
+ # Returns a boolean that indicates that a bi-directional connection
108
+ # has been established between the user and the contact, if it is
109
+ # able to assert this.
110
+ def connected
111
+ return @poco_data[:connected] unless @poco_data == nil
112
+ str = pick_first_node(@poco.xpath('./poco:connected'))
113
+ return nil if str == nil
114
+
115
+ if str == "true"
116
+ true
117
+ elsif str == "false"
118
+ false
119
+ else
120
+ nil
121
+ end
122
+ end
123
+ end
124
+ end