nelumba 0.0.13

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.
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