nelumba 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +9 -0
  3. data/Gemfile +20 -0
  4. data/README.md +242 -0
  5. data/Rakefile +7 -0
  6. data/assets/lotus_logo_purple.png +0 -0
  7. data/assets/lotus_logo_purple.svg +262 -0
  8. data/lib/nelumba.rb +47 -0
  9. data/lib/nelumba/activity.rb +250 -0
  10. data/lib/nelumba/application.rb +11 -0
  11. data/lib/nelumba/article.rb +11 -0
  12. data/lib/nelumba/atom/account.rb +50 -0
  13. data/lib/nelumba/atom/address.rb +56 -0
  14. data/lib/nelumba/atom/author.rb +176 -0
  15. data/lib/nelumba/atom/category.rb +41 -0
  16. data/lib/nelumba/atom/comment.rb +96 -0
  17. data/lib/nelumba/atom/entry.rb +216 -0
  18. data/lib/nelumba/atom/feed.rb +198 -0
  19. data/lib/nelumba/atom/generator.rb +40 -0
  20. data/lib/nelumba/atom/link.rb +79 -0
  21. data/lib/nelumba/atom/name.rb +57 -0
  22. data/lib/nelumba/atom/organization.rb +62 -0
  23. data/lib/nelumba/atom/person.rb +179 -0
  24. data/lib/nelumba/atom/portable_contacts.rb +117 -0
  25. data/lib/nelumba/atom/source.rb +179 -0
  26. data/lib/nelumba/atom/thread.rb +60 -0
  27. data/lib/nelumba/audio.rb +39 -0
  28. data/lib/nelumba/badge.rb +11 -0
  29. data/lib/nelumba/binary.rb +52 -0
  30. data/lib/nelumba/bookmark.rb +30 -0
  31. data/lib/nelumba/category.rb +49 -0
  32. data/lib/nelumba/collection.rb +34 -0
  33. data/lib/nelumba/comment.rb +47 -0
  34. data/lib/nelumba/crypto.rb +144 -0
  35. data/lib/nelumba/device.rb +11 -0
  36. data/lib/nelumba/discover.rb +362 -0
  37. data/lib/nelumba/event.rb +57 -0
  38. data/lib/nelumba/feed.rb +173 -0
  39. data/lib/nelumba/file.rb +43 -0
  40. data/lib/nelumba/generator.rb +53 -0
  41. data/lib/nelumba/group.rb +11 -0
  42. data/lib/nelumba/identity.rb +63 -0
  43. data/lib/nelumba/image.rb +30 -0
  44. data/lib/nelumba/link.rb +56 -0
  45. data/lib/nelumba/note.rb +34 -0
  46. data/lib/nelumba/notification.rb +229 -0
  47. data/lib/nelumba/object.rb +251 -0
  48. data/lib/nelumba/person.rb +306 -0
  49. data/lib/nelumba/place.rb +34 -0
  50. data/lib/nelumba/product.rb +30 -0
  51. data/lib/nelumba/publisher.rb +44 -0
  52. data/lib/nelumba/question.rb +30 -0
  53. data/lib/nelumba/review.rb +30 -0
  54. data/lib/nelumba/service.rb +11 -0
  55. data/lib/nelumba/subscription.rb +117 -0
  56. data/lib/nelumba/version.rb +3 -0
  57. data/lib/nelumba/video.rb +43 -0
  58. data/nelumba.gemspec +28 -0
  59. data/spec/activity_spec.rb +116 -0
  60. data/spec/application_spec.rb +136 -0
  61. data/spec/article_spec.rb +136 -0
  62. data/spec/atom/comment_spec.rb +455 -0
  63. data/spec/atom/feed_spec.rb +684 -0
  64. data/spec/audio_spec.rb +164 -0
  65. data/spec/badge_spec.rb +136 -0
  66. data/spec/binary_spec.rb +218 -0
  67. data/spec/bookmark.rb +150 -0
  68. data/spec/collection_spec.rb +152 -0
  69. data/spec/comment_spec.rb +128 -0
  70. data/spec/crypto_spec.rb +126 -0
  71. data/spec/device_spec.rb +136 -0
  72. data/spec/event_spec.rb +239 -0
  73. data/spec/feed_spec.rb +252 -0
  74. data/spec/file_spec.rb +190 -0
  75. data/spec/group_spec.rb +136 -0
  76. data/spec/helper.rb +10 -0
  77. data/spec/identity_spec.rb +67 -0
  78. data/spec/image_spec.rb +150 -0
  79. data/spec/link_spec.rb +30 -0
  80. data/spec/note_spec.rb +163 -0
  81. data/spec/notification_spec.rb +89 -0
  82. data/spec/person_spec.rb +244 -0
  83. data/spec/place_spec.rb +162 -0
  84. data/spec/product_spec.rb +150 -0
  85. data/spec/question_spec.rb +156 -0
  86. data/spec/review_spec.rb +149 -0
  87. data/spec/service_spec.rb +136 -0
  88. data/spec/video_spec.rb +164 -0
  89. data/test/example_feed.atom +393 -0
  90. data/test/example_feed_empty_author.atom +336 -0
  91. data/test/example_feed_false_connected.atom +359 -0
  92. data/test/example_feed_link_without_href.atom +134 -0
  93. data/test/example_page.html +4 -0
  94. data/test/mime_type_bug_feed.atom +874 -0
  95. metadata +288 -0
@@ -0,0 +1,11 @@
1
+ module Nelumba
2
+ class Service
3
+ include Nelumba::Object
4
+
5
+ def to_json_hash
6
+ {
7
+ :objectType => "service"
8
+ }.merge(super)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,117 @@
1
+ module Nelumba
2
+ class Subscription
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'base64'
6
+ require 'hmac-sha1'
7
+
8
+ # The url that should be used to handle subscription handshakes.
9
+ attr_reader :callback_url
10
+
11
+ # The url of the feed one wishes to subscribe to.
12
+ attr_reader :topic_url
13
+
14
+ # The hub this subscription is made with.
15
+ attr_reader :hub
16
+
17
+ # Creates a representation of a subscription.
18
+ #
19
+ # options:
20
+ # :callback_url => The url that should be used to handle subscription
21
+ # handshakes.
22
+ # :topic_url => The url of the feed one wishes to subscribe to.
23
+ # :secret => A secret that will be passed to the callback to better
24
+ # verify that communication is not replayed. Default:
25
+ # A secure random hex.
26
+ # :hubs => A list of hubs to negotiate the subscription with.
27
+ # Default: attempts to discover the hubs when it
28
+ # subscribes for the first time.
29
+ # :hub => The hub we have a subscription with already.
30
+ def initialize(options = {})
31
+ @tokens = []
32
+
33
+ secret = options[:secret] || SecureRandom.hex(32)
34
+ @secret = secret.to_s
35
+
36
+ @callback_url = options[:callback_url]
37
+ @topic_url = options[:topic_url]
38
+ @tokens << options[:token] if options[:token]
39
+
40
+ @hubs = options[:hubs] || []
41
+
42
+ @hub = options[:hub]
43
+ end
44
+
45
+ # Actively searches for hubs by talking to publisher directly
46
+ def discover_hubs_for_topic
47
+ @hubs = Nelumba.feed_from_url(self.topic_url).hubs
48
+ end
49
+
50
+ # Subscribe to the topic through the given hub.
51
+ def subscribe
52
+ return unless self.hub.nil?
53
+
54
+ # Discover hubs if none exist
55
+ @hubs = discover_hubs_for_topic(self.topic_url) if self.hubs.empty?
56
+ @hub = self.hubs.first
57
+ change_subscription(:subscribe, token)
58
+
59
+ # TODO: Check response, if failed, try a different hub
60
+ end
61
+
62
+ # Unsubscribe to the topic.
63
+ def unsubscribe
64
+ return if self.hub.nil?
65
+
66
+ change_subscription(:unsubscribe)
67
+ end
68
+
69
+ # Change our subscription to this topic at a hub.
70
+ # mode: Either :subscribe or :unsubscribe
71
+ # hub_url: The url of the hub to negotiate with
72
+ # token: A token to verify the response from the hub.
73
+ def change_subscription(mode)
74
+ token ||= SecureRandom.hex(32)
75
+ @tokens << token.to_s
76
+
77
+ # TODO: Set up HTTPS foo
78
+ res = Net::HTTP.post_form(URI.parse(self.hub),
79
+ {
80
+ 'hub.mode' => mode.to_s,
81
+ 'hub.callback' => @callback_url,
82
+ 'hub.verify' => 'async',
83
+ 'hub.verify_token' => token,
84
+ 'hub.lease_seconds' => '',
85
+ 'hub.secret' => @secret,
86
+ 'hub.topic' => @topic_url
87
+ })
88
+ end
89
+
90
+ # Verify that a subscription response is valid.
91
+ def verify_subscription(token)
92
+ # Is there a token?
93
+ result = @tokens.include?(token)
94
+
95
+ # Ensure we cannot reuse the token
96
+ @tokens.delete(token)
97
+
98
+ result
99
+ end
100
+
101
+ # Determines if the given body matches the signature.
102
+ def verify_content(body, signature)
103
+ hmac = HMAC::SHA1.hexdigest(@secret, body)
104
+ check = "sha1=" + hmac
105
+ check == signature
106
+ end
107
+
108
+ # Gives the content of a challenge response given the challenge
109
+ # body.
110
+ def challenge_response(challenge_code)
111
+ {
112
+ :body => challenge_code,
113
+ :status => 200
114
+ }
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,3 @@
1
+ module Nelumba
2
+ VERSION = "0.0.13"
3
+ end
@@ -0,0 +1,43 @@
1
+ module Nelumba
2
+ class Video
3
+ include Nelumba::Object
4
+
5
+ # A fragment of HTML markup that, when embedded within another HTML page,
6
+ # provides an interactive user-interface for viewing or listening to the
7
+ # video stream.
8
+ attr_reader :embed_code
9
+
10
+ # A MediaLink to the video content itself.
11
+ attr_reader :stream
12
+
13
+ # Creates a new Video activity object.
14
+ def initialize(options = {}, &blk)
15
+ init(options, &blk)
16
+ end
17
+
18
+ def init(options = {}, &blk)
19
+ super(options, &blk)
20
+
21
+ @embed_code = options[:embed_code]
22
+ @stream = options[:stream]
23
+ end
24
+
25
+ # Returns a hash of all relevant fields.
26
+ def to_hash
27
+ {
28
+ :embed_code => @embed_code,
29
+ :stream => @stream
30
+ }.merge(super)
31
+ end
32
+
33
+ # Returns a hash of all relevant fields with JSON activity streams
34
+ # conventions.
35
+ def to_json_hash
36
+ {
37
+ :objectType => "video",
38
+ :embedCode => @embed_code,
39
+ :stream => @stream
40
+ }.merge(super)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "nelumba/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "nelumba"
7
+ s.version = Nelumba::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Hackers of the Severed Hand']
10
+ s.email = ['hotsh@xomb.org']
11
+ s.homepage = "http://github.com/hotsh/nelumba"
12
+ s.summary = %q{Generalized federated system backend for social networks with ActivityStreams/OStatus/pump.io.}
13
+ s.description = %q{This gem allows easier implementation and utilization of distributed, federated social networks.}
14
+
15
+ s.add_dependency "ratom"
16
+ s.add_dependency "ruby-hmac"
17
+ s.add_dependency "rsa"
18
+ s.add_dependency "time-lord"
19
+ s.add_dependency "nokogiri"
20
+
21
+ s.add_development_dependency "rspec", "~> 2.10.0"
22
+ s.add_development_dependency "rake", "~> 0.9.2"
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,116 @@
1
+ require_relative 'helper'
2
+ require_relative '../lib/nelumba/activity.rb'
3
+
4
+ describe Nelumba::Activity do
5
+ describe "#initialize" do
6
+ it "should store an object" do
7
+ Nelumba::Activity.new(:object => "object").object.must_equal "object"
8
+ end
9
+
10
+ it "should store an type" do
11
+ Nelumba::Activity.new(:type => :audio).type.must_equal :audio
12
+ end
13
+
14
+ it "should store a verb" do
15
+ Nelumba::Activity.new(:verb => :follow).verb.must_equal :follow
16
+ end
17
+
18
+ it "should store a target" do
19
+ Nelumba::Activity.new(:target => "target").target.must_equal "target"
20
+ end
21
+
22
+ it "should store an actor" do
23
+ actor = mock('author')
24
+ Nelumba::Activity.new(:actor => actor).actor.must_equal actor
25
+ end
26
+
27
+ it "should store the published date" do
28
+ time = mock('date')
29
+ Nelumba::Activity.new(:published => time).published.must_equal time
30
+ end
31
+
32
+ it "should store the updated date" do
33
+ time = mock('date')
34
+ Nelumba::Activity.new(:updated => time).updated.must_equal time
35
+ end
36
+
37
+ it "should store a source feed" do
38
+ feed = mock('feed')
39
+ Nelumba::Activity.new(:source => feed).source.must_equal feed
40
+ end
41
+
42
+ it "should store a url" do
43
+ Nelumba::Activity.new(:url => "url").url.must_equal "url"
44
+ end
45
+
46
+ it "should store an id" do
47
+ Nelumba::Activity.new(:uid => "id").uid.must_equal "id"
48
+ end
49
+
50
+ it "should store an array of threads" do
51
+ thread = mock('entry')
52
+ Nelumba::Activity.new(:in_reply_to => [thread]).in_reply_to.must_equal [thread]
53
+ end
54
+
55
+ it "should store an array of threads when only given one entry" do
56
+ thread = mock('entry')
57
+ Nelumba::Activity.new(:in_reply_to => thread).in_reply_to.must_equal [thread]
58
+ end
59
+
60
+ it "should store an empty array of threads by default" do
61
+ Nelumba::Activity.new.in_reply_to.must_equal []
62
+ end
63
+
64
+ it "should store an array of replies" do
65
+ thread = mock('entry')
66
+ Nelumba::Activity.new(:replies => [thread]).replies.must_equal [thread]
67
+ end
68
+
69
+ it "should store an empty array of replies by default" do
70
+ Nelumba::Activity.new.replies.must_equal []
71
+ end
72
+
73
+ it "should store an array of shares" do
74
+ thread = mock('entry')
75
+ Nelumba::Activity.new(:shares => [thread]).shares.must_equal [thread]
76
+ end
77
+
78
+ it "should store an empty array of shares by default" do
79
+ Nelumba::Activity.new.shares.must_equal []
80
+ end
81
+
82
+ it "should store an array of mentions" do
83
+ thread = mock('entry')
84
+ Nelumba::Activity.new(:mentions => [thread]).mentions.must_equal [thread]
85
+ end
86
+
87
+ it "should store an empty array of mentions by default" do
88
+ Nelumba::Activity.new.mentions.must_equal []
89
+ end
90
+
91
+ it "should store an array of likes" do
92
+ thread = mock('entry')
93
+ Nelumba::Activity.new(:likes => [thread]).likes.must_equal [thread]
94
+ end
95
+
96
+ it "should store an empty array of likes by default" do
97
+ Nelumba::Activity.new.likes.must_equal []
98
+ end
99
+
100
+ it "should store an empty hash of interactions by default" do
101
+ Nelumba::Activity.new.interactions.must_equal({})
102
+ end
103
+ end
104
+
105
+ describe "#interaction_count" do
106
+ it "should pull values out of interactions" do
107
+ Nelumba::Activity.new(:interactions => {:share => {:count => 3}})
108
+ .interaction_count(:share).must_equal 3
109
+ end
110
+
111
+ it "should defautl a value of 0 when verb isn't found" do
112
+ Nelumba::Activity.new(:interactions => {:share => {:count => 3}})
113
+ .interaction_count(:like).must_equal 0
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,136 @@
1
+ require_relative 'helper'
2
+ require_relative '../lib/nelumba/application.rb'
3
+
4
+ describe Nelumba::Application do
5
+ describe "#initialize" do
6
+ it "should store an author" do
7
+ author = mock('author')
8
+ Nelumba::Application.new(:author => author).author.must_equal author
9
+ end
10
+
11
+ it "should store content" do
12
+ Nelumba::Application.new(:content => "txt").content.must_equal "txt"
13
+ end
14
+
15
+ it "should store the published date" do
16
+ time = mock('date')
17
+ Nelumba::Application.new(:published => time).published.must_equal time
18
+ end
19
+
20
+ it "should store the updated date" do
21
+ time = mock('date')
22
+ Nelumba::Application.new(:updated => time).updated.must_equal time
23
+ end
24
+
25
+ it "should store a display name" do
26
+ Nelumba::Application.new(:display_name => "url")
27
+ .display_name.must_equal "url"
28
+ end
29
+
30
+ it "should store a summary" do
31
+ Nelumba::Application.new(:summary => "url").summary.must_equal "url"
32
+ end
33
+
34
+ it "should store a url" do
35
+ Nelumba::Application.new(:url => "url").url.must_equal "url"
36
+ end
37
+
38
+ it "should store an id" do
39
+ Nelumba::Application.new(:uid => "id").uid.must_equal "id"
40
+ end
41
+ end
42
+
43
+ describe "#to_hash" do
44
+ it "should contain the content" do
45
+ Nelumba::Application.new(:content => "Hello")
46
+ .to_hash[:content].must_equal "Hello"
47
+ end
48
+
49
+ it "should contain the author" do
50
+ author = mock('Nelumba::Person')
51
+ Nelumba::Application.new(:author => author).to_hash[:author].must_equal author
52
+ end
53
+
54
+ it "should contain the uid" do
55
+ Nelumba::Application.new(:uid => "Hello").to_hash[:uid].must_equal "Hello"
56
+ end
57
+
58
+ it "should contain the url" do
59
+ Nelumba::Application.new(:url => "Hello").to_hash[:url].must_equal "Hello"
60
+ end
61
+
62
+ it "should contain the summary" do
63
+ Nelumba::Application.new(:summary=> "Hello")
64
+ .to_hash[:summary].must_equal "Hello"
65
+ end
66
+
67
+ it "should contain the display name" do
68
+ Nelumba::Application.new(:display_name => "Hello")
69
+ .to_hash[:display_name].must_equal "Hello"
70
+ end
71
+
72
+ it "should contain the published date" do
73
+ date = mock('Time')
74
+ Nelumba::Application.new(:published => date).to_hash[:published].must_equal date
75
+ end
76
+
77
+ it "should contain the updated date" do
78
+ date = mock('Time')
79
+ Nelumba::Application.new(:updated => date).to_hash[:updated].must_equal date
80
+ end
81
+ end
82
+
83
+ describe "#to_json" do
84
+ before do
85
+ author = Nelumba::Person.new :display_name => "wilkie"
86
+ @note = Nelumba::Application.new :content => "Hello",
87
+ :author => author,
88
+ :uid => "id",
89
+ :url => "url",
90
+ :title => "title",
91
+ :summary => "foo",
92
+ :display_name => "meh",
93
+ :published => Time.now,
94
+ :updated => Time.now
95
+
96
+ @json = @note.to_json
97
+ @data = JSON.parse(@json)
98
+ end
99
+
100
+ it "should contain the embedded json for the author" do
101
+ @data["author"].must_equal JSON.parse(@note.author.to_json)
102
+ end
103
+
104
+ it "should contain a 'application' objectType" do
105
+ @data["objectType"].must_equal "application"
106
+ end
107
+
108
+ it "should contain the id" do
109
+ @data["id"].must_equal @note.uid
110
+ end
111
+
112
+ it "should contain the content" do
113
+ @data["content"].must_equal @note.content
114
+ end
115
+
116
+ it "should contain the url" do
117
+ @data["url"].must_equal @note.url
118
+ end
119
+
120
+ it "should contain the summary" do
121
+ @data["summary"].must_equal @note.summary
122
+ end
123
+
124
+ it "should contain the display name" do
125
+ @data["displayName"].must_equal @note.display_name
126
+ end
127
+
128
+ it "should contain the published date as rfc3339" do
129
+ @data["published"].must_equal @note.published.to_date.rfc3339 + 'Z'
130
+ end
131
+
132
+ it "should contain the updated date as rfc3339" do
133
+ @data["updated"].must_equal @note.updated.to_date.rfc3339 + 'Z'
134
+ end
135
+ end
136
+ end