hootenanny 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +5 -7
- data/Gemfile.lock +46 -28
- data/app/controllers/hootenanny/notifications_controller.rb +31 -0
- data/app/controllers/hootenanny/parameters.rb +16 -0
- data/app/controllers/hootenanny/subscriptions_controller.rb +24 -3
- data/app/models/hootenanny/publish_notification.rb +90 -0
- data/app/models/hootenanny/subscription.rb +72 -23
- data/config/routes.rb +2 -1
- data/db/migrate/20130607182642_add_started_at_and_lease_duration_to_subscriptions.rb +15 -0
- data/db/migrate/20130608225621_add_hmac_secret_to_subscription.rb +5 -0
- data/db/migrate/20130611235218_add_publish_notifications.rb +9 -0
- data/db/migrate/20130612153138_add_timestamps_to_publish_notification.rb +5 -0
- data/db/migrate/20130705200729_add_processed_flag_to_publish_notifications.rb +6 -0
- data/db/migrate/20130711061329_switch_publish_notification_process_state_from_boolean_to_string.rb +11 -0
- data/db/migrate/20130711061558_add_index_to_notifications_state.rb +5 -0
- data/hootenanny.gemspec +6 -2
- data/lib/hootenanny/configuration.rb +43 -0
- data/lib/hootenanny/correspondent.rb +44 -0
- data/lib/hootenanny/errors.rb +42 -1
- data/lib/hootenanny/feed.rb +75 -0
- data/lib/hootenanny/feed/atom_feed.rb +18 -0
- data/lib/hootenanny/feed/atom_feed_item.rb +8 -0
- data/lib/hootenanny/feed/digest_feed.rb +14 -0
- data/lib/hootenanny/feed/digest_feed_item.rb +11 -0
- data/lib/hootenanny/feed/feed_item.rb +30 -0
- data/lib/hootenanny/feed/file.rb +66 -0
- data/lib/hootenanny/feed/json_feed.rb +48 -0
- data/lib/hootenanny/feed/json_feed_item.rb +8 -0
- data/lib/hootenanny/feed/null_feed.rb +27 -0
- data/lib/hootenanny/feed/rss_feed.rb +52 -0
- data/lib/hootenanny/feed/rss_feed_item.rb +8 -0
- data/lib/hootenanny/feed_store.rb +30 -0
- data/lib/hootenanny/feed_store/file_feed_store.rb +55 -0
- data/lib/hootenanny/feed_store/web_feed_store.rb +42 -0
- data/lib/hootenanny/hub.rb +116 -22
- data/lib/hootenanny/publish_notification_expiration_policy.rb +17 -0
- data/lib/hootenanny/request.rb +40 -0
- data/lib/hootenanny/request/publish_notification.rb +94 -0
- data/lib/hootenanny/request/subscription.rb +153 -0
- data/lib/hootenanny/subscription_delivery.rb +47 -0
- data/lib/hootenanny/topic.rb +38 -0
- data/lib/hootenanny/topic_synchronizer.rb +71 -0
- data/lib/hootenanny/uri.rb +53 -0
- data/lib/hootenanny/verification.rb +108 -0
- data/lib/hootenanny/version.rb +1 -1
- data/spec/dummy/config/database.yml +12 -6
- data/spec/dummy/db/migrate/20130607183149_add_started_at_and_lease_duration_to_subscriptions.hootenanny.rb +16 -0
- data/spec/dummy/db/migrate/20130608231253_add_hmac_secret_to_subscription.hootenanny.rb +6 -0
- data/spec/dummy/db/migrate/20130611235546_add_publish_notifications.hootenanny.rb +10 -0
- data/spec/dummy/db/migrate/20130612153353_add_timestamps_to_publish_notification.hootenanny.rb +6 -0
- data/spec/dummy/db/migrate/20130705200832_add_processed_flag_to_publish_notifications.hootenanny.rb +7 -0
- data/spec/dummy/db/migrate/20130711061518_switch_publish_notification_process_state_from_boolean_to_string.hootenanny.rb +12 -0
- data/spec/dummy/db/migrate/20130711061629_add_index_to_notifications_state.hootenanny.rb +6 -0
- data/spec/factories/publish_notification.rb +13 -0
- data/spec/factories/requests/publish_notification.rb +11 -0
- data/spec/factories/requests/subscription.rb +46 -0
- data/spec/factories/subscription.rb +14 -2
- data/spec/factories/verification.rb +15 -0
- data/spec/features/publishers/can_notify_the_hub_of_content_updates_spec.rb +58 -0
- data/spec/features/subscribers/are_protected_from_unwarranted_subscriptions_spec.rb +59 -0
- data/spec/features/subscribers/can_receive_distributions_of_topic_content_spec.rb +175 -0
- data/spec/features/subscribers/can_subscribe_to_a_topic_spec.rb +76 -7
- data/spec/fixtures/feeds/atom/97d79220f68b4bf27.atom +0 -0
- data/spec/fixtures/feeds/atom/sample_feed.atom +51 -0
- data/spec/fixtures/feeds/digest/97d79220f68b4bf27.digest +1 -0
- data/spec/fixtures/feeds/digest/complete_broadcasted_items/5b187098da59f077f/97d79220f68b4bf27.digest +6 -0
- data/spec/fixtures/feeds/digest/incomplete_broadcasted_items/5b187098da59f077f/97d79220f68b4bf27.digest +5 -0
- data/spec/fixtures/feeds/digest/sample_feed.digest +6 -0
- data/spec/fixtures/feeds/json/97d79220f68b4bf27.json +1 -0
- data/spec/fixtures/feeds/json/feed_with_one_item.json +21 -0
- data/spec/fixtures/feeds/json/sample_feed.json +30 -0
- data/spec/fixtures/feeds/rss/5b187098da59f077f/97d79220f68b4bf27.rss +21 -0
- data/spec/fixtures/feeds/rss/97d79220f68b4bf27.rss +0 -0
- data/spec/fixtures/feeds/rss/feed_with_one_item.rss +15 -0
- data/spec/fixtures/feeds/rss/minimal_feed.rss +12 -0
- data/spec/fixtures/feeds/rss/sample_feed.rss +22 -0
- data/spec/fixtures/feeds/rss/sample_feed_2.rss +22 -0
- data/spec/lib/hootenanny/configuration_spec.rb +7 -0
- data/spec/lib/hootenanny/correspondent_spec.rb +94 -0
- data/spec/lib/hootenanny/errors_spec.rb +21 -0
- data/spec/lib/hootenanny/feed/atom_feed_item_spec.rb +9 -0
- data/spec/lib/hootenanny/feed/atom_feed_spec.rb +40 -0
- data/spec/lib/hootenanny/feed/digest_feed_item_spec.rb +9 -0
- data/spec/lib/hootenanny/feed/digest_feed_spec.rb +40 -0
- data/spec/lib/hootenanny/feed/feed_item_spec.rb +49 -0
- data/spec/lib/hootenanny/feed/file_spec.rb +66 -0
- data/spec/lib/hootenanny/feed/json_feed_item_spec.rb +9 -0
- data/spec/lib/hootenanny/feed/json_feed_spec.rb +128 -0
- data/spec/lib/hootenanny/feed/null_feed_spec.rb +9 -0
- data/spec/lib/hootenanny/feed/rss_feed_item_spec.rb +9 -0
- data/spec/lib/hootenanny/feed/rss_feed_spec.rb +143 -0
- data/spec/lib/hootenanny/feed_spec.rb +159 -0
- data/spec/lib/hootenanny/feed_store/file_feed_store_spec.rb +58 -0
- data/spec/lib/hootenanny/feed_store/web_feed_store_spec.rb +47 -0
- data/spec/lib/hootenanny/feed_store_spec.rb +27 -0
- data/spec/lib/hootenanny/hub_spec.rb +73 -0
- data/spec/lib/hootenanny/publish_notification_expiration_policy_spec.rb +35 -0
- data/spec/lib/hootenanny/request/publish_notification_spec.rb +43 -0
- data/spec/lib/hootenanny/request/subscription_spec.rb +89 -0
- data/spec/lib/hootenanny/request_spec.rb +21 -0
- data/spec/lib/hootenanny/subscription_delivery_spec.rb +54 -0
- data/spec/lib/hootenanny/topic_spec.rb +15 -0
- data/spec/lib/hootenanny/topic_synchronizer_spec.rb +98 -0
- data/spec/lib/hootenanny/uri_spec.rb +32 -0
- data/spec/lib/hootenanny/verification_spec.rb +92 -0
- data/spec/models/hootenanny/publish_notification_spec.rb +55 -0
- data/spec/models/hootenanny/subscription_spec.rb +58 -19
- data/spec/support/verification.rb +63 -0
- metadata +231 -14
- data/spec/controllers/hootenanny/hub_spec.rb +0 -15
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -17,10 +17,8 @@ gem "jquery-rails"
|
|
17
17
|
# gem 'debugger'
|
18
18
|
gem 'strong_parameters'
|
19
19
|
|
20
|
-
|
21
|
-
gem '
|
22
|
-
gem '
|
23
|
-
gem '
|
24
|
-
|
25
|
-
gem 'factory_girl_rails'
|
26
|
-
gem 'awesome_print'
|
20
|
+
group :development do
|
21
|
+
gem 'pry'
|
22
|
+
# gem 'pry-plus'
|
23
|
+
gem 'awesome_print'
|
24
|
+
end
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
hootenanny (0.0
|
4
|
+
hootenanny (0.1.0)
|
5
|
+
chronological (~> 1.0.0beta10)
|
6
|
+
faraday (~> 0.8.7)
|
5
7
|
rails (~> 3.2)
|
6
8
|
strong_parameters (~> 0.2.1)
|
7
9
|
|
@@ -35,10 +37,14 @@ GEM
|
|
35
37
|
activesupport (3.2.13)
|
36
38
|
i18n (= 0.6.1)
|
37
39
|
multi_json (~> 1.0)
|
40
|
+
addressable (2.3.4)
|
38
41
|
arel (3.0.2)
|
39
42
|
awesome_print (1.1.0)
|
40
43
|
builder (3.0.4)
|
41
|
-
|
44
|
+
chronological (1.0.0beta10)
|
45
|
+
coderay (1.0.9)
|
46
|
+
crack (0.3.2)
|
47
|
+
database_cleaner (0.9.1)
|
42
48
|
diff-lcs (1.2.4)
|
43
49
|
erubis (2.7.0)
|
44
50
|
factory_girl (4.2.0)
|
@@ -46,11 +52,13 @@ GEM
|
|
46
52
|
factory_girl_rails (4.2.1)
|
47
53
|
factory_girl (~> 4.2.0)
|
48
54
|
railties (>= 3.0.0)
|
49
|
-
|
55
|
+
faraday (0.8.7)
|
56
|
+
multipart-post (~> 1.1)
|
57
|
+
fuubar (1.1.1)
|
50
58
|
rspec (~> 2.0)
|
51
59
|
rspec-instafail (~> 0.2.0)
|
52
|
-
ruby-progressbar (~> 1.0
|
53
|
-
hike (1.2.
|
60
|
+
ruby-progressbar (~> 1.0)
|
61
|
+
hike (1.2.3)
|
54
62
|
i18n (0.6.1)
|
55
63
|
journey (1.0.4)
|
56
64
|
jquery-rails (2.2.1)
|
@@ -60,10 +68,16 @@ GEM
|
|
60
68
|
mail (2.5.4)
|
61
69
|
mime-types (~> 1.16)
|
62
70
|
treetop (~> 1.4.8)
|
71
|
+
method_source (0.8.1)
|
63
72
|
mime-types (1.23)
|
64
|
-
multi_json (1.7.
|
73
|
+
multi_json (1.7.6)
|
74
|
+
multipart-post (1.2.0)
|
65
75
|
pg (0.15.1)
|
66
76
|
polyglot (0.3.3)
|
77
|
+
pry (0.9.12.2)
|
78
|
+
coderay (~> 1.0.5)
|
79
|
+
method_source (~> 0.8)
|
80
|
+
slop (~> 3.4)
|
67
81
|
rack (1.4.5)
|
68
82
|
rack-cache (1.2)
|
69
83
|
rack (>= 0.4)
|
@@ -89,57 +103,61 @@ GEM
|
|
89
103
|
rake (10.0.4)
|
90
104
|
rdoc (3.12.2)
|
91
105
|
json (~> 1.4)
|
92
|
-
rspec (2.
|
93
|
-
rspec-core (
|
94
|
-
rspec-expectations (
|
95
|
-
rspec-mocks (
|
96
|
-
rspec-core (2.
|
97
|
-
rspec-expectations (2.
|
106
|
+
rspec (2.14.0.rc1)
|
107
|
+
rspec-core (= 2.14.0.rc1)
|
108
|
+
rspec-expectations (= 2.14.0.rc1)
|
109
|
+
rspec-mocks (= 2.14.0.rc1)
|
110
|
+
rspec-core (2.14.0.rc1)
|
111
|
+
rspec-expectations (2.14.0.rc1)
|
98
112
|
diff-lcs (>= 1.1.3, < 2.0)
|
99
113
|
rspec-instafail (0.2.4)
|
100
|
-
rspec-mocks (2.
|
101
|
-
rspec-rails (2.
|
114
|
+
rspec-mocks (2.14.0.rc1)
|
115
|
+
rspec-rails (2.14.0.rc1)
|
102
116
|
actionpack (>= 3.0)
|
103
117
|
activesupport (>= 3.0)
|
104
118
|
railties (>= 3.0)
|
105
|
-
rspec-core (
|
106
|
-
rspec-expectations (
|
107
|
-
rspec-mocks (
|
119
|
+
rspec-core (= 2.14.0.rc1)
|
120
|
+
rspec-expectations (= 2.14.0.rc1)
|
121
|
+
rspec-mocks (= 2.14.0.rc1)
|
108
122
|
rspectacular (0.13.0)
|
109
123
|
fuubar (~> 1.0)
|
110
124
|
rspec (~> 2.12)
|
111
|
-
ruby-progressbar (1.
|
112
|
-
|
113
|
-
activesupport (>= 3.0.0)
|
125
|
+
ruby-progressbar (1.1.1)
|
126
|
+
slop (3.4.5)
|
114
127
|
sprockets (2.2.2)
|
115
128
|
hike (~> 1.2)
|
116
129
|
multi_json (~> 1.0)
|
117
130
|
rack (~> 1.0)
|
118
131
|
tilt (~> 1.1, != 1.3.0)
|
119
|
-
sqlite3 (1.3.7)
|
120
132
|
strong_parameters (0.2.1)
|
121
133
|
actionpack (~> 3.0)
|
122
134
|
activemodel (~> 3.0)
|
123
135
|
railties (~> 3.0)
|
124
136
|
thor (0.18.1)
|
125
137
|
tilt (1.4.1)
|
126
|
-
|
138
|
+
timecop (0.6.1)
|
139
|
+
treetop (1.4.14)
|
127
140
|
polyglot
|
128
141
|
polyglot (>= 0.3.1)
|
129
142
|
tzinfo (0.3.37)
|
143
|
+
webmock (1.11.0)
|
144
|
+
addressable (>= 2.2.7)
|
145
|
+
crack (>= 0.3.2)
|
130
146
|
|
131
147
|
PLATFORMS
|
132
148
|
ruby
|
133
149
|
|
134
150
|
DEPENDENCIES
|
135
151
|
awesome_print
|
136
|
-
database_cleaner
|
137
|
-
factory_girl_rails
|
152
|
+
database_cleaner (~> 0.9.1)
|
153
|
+
factory_girl_rails (~> 4.2)
|
138
154
|
hootenanny!
|
139
155
|
jquery-rails
|
140
156
|
pg
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
157
|
+
pry
|
158
|
+
rspec (~> 2.14.0.rc1)
|
159
|
+
rspec-rails (~> 2.14.0.rc1)
|
160
|
+
rspectacular (~> 0.13)
|
145
161
|
strong_parameters
|
162
|
+
timecop (~> 0.6.1)
|
163
|
+
webmock (~> 1.11)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'hootenanny/hub'
|
2
|
+
require 'controllers/hootenanny/parameters'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
class PublishNotificationsController < ActionController::Base
|
6
|
+
include Hootenanny::Parameters
|
7
|
+
|
8
|
+
def create
|
9
|
+
if Hootenanny::Hub.notify_of_publication(publish_notification_request)
|
10
|
+
render :nothing => true, :status => :no_content
|
11
|
+
end
|
12
|
+
rescue Hootenanny::Error => error
|
13
|
+
render json: error,
|
14
|
+
status: :bad_request
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def publish_notification_request
|
20
|
+
@request ||= Hootenanny::Hub.request publish_notification_request_params
|
21
|
+
end
|
22
|
+
|
23
|
+
def publish_notification_request_params
|
24
|
+
params[:type] ||= :publish_notification
|
25
|
+
|
26
|
+
params.permit(:url)
|
27
|
+
|
28
|
+
params
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Hootenanny
|
2
|
+
module Parameters
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
rescue_from(ActionController::ParameterMissing) do |parameter_missing_exception|
|
7
|
+
render json: {
|
8
|
+
error: {
|
9
|
+
message: "Required parameter missing: #{parameter_missing_exception.param}"
|
10
|
+
}
|
11
|
+
},
|
12
|
+
status: :bad_request
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,14 +1,35 @@
|
|
1
1
|
require 'hootenanny/hub'
|
2
|
+
require 'controllers/hootenanny/parameters'
|
2
3
|
|
3
4
|
module Hootenanny
|
4
5
|
class SubscriptionsController < ActionController::Base
|
6
|
+
include Hootenanny::Parameters
|
7
|
+
|
5
8
|
def create
|
6
|
-
if Hootenanny::Hub.subscribe(
|
9
|
+
if Hootenanny::Hub.subscribe(subscription_request)
|
7
10
|
render :nothing => true, :status => :no_content
|
8
11
|
end
|
9
|
-
rescue Hootenanny::
|
10
|
-
render json:
|
12
|
+
rescue Hootenanny::Error => error
|
13
|
+
render json: error,
|
11
14
|
status: :bad_request
|
12
15
|
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def subscription_request
|
20
|
+
@request ||= Hootenanny::Hub.request subscription_request_params
|
21
|
+
end
|
22
|
+
|
23
|
+
def subscription_request_params
|
24
|
+
params[:type] ||= :subscription
|
25
|
+
|
26
|
+
params.require(:callback)
|
27
|
+
params.require(:topic)
|
28
|
+
params.permit(:lease_seconds,
|
29
|
+
:verify_token,
|
30
|
+
:secret)
|
31
|
+
|
32
|
+
params
|
33
|
+
end
|
13
34
|
end
|
14
35
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'hootenanny/uri'
|
2
|
+
|
3
|
+
module Hootenanny
|
4
|
+
class PublishNotification < ActiveRecord::Base
|
5
|
+
self.table_name = 'hootenanny_publish_notifications'
|
6
|
+
|
7
|
+
###
|
8
|
+
# Private: Retrieves a set of notifications for a given set of topics. If
|
9
|
+
# given one topic, only one notification should be returned since there can
|
10
|
+
# only be one notification for a given topic in the queue at any time.
|
11
|
+
#
|
12
|
+
# topic - A String or an Array representing the Topic to look for.
|
13
|
+
#
|
14
|
+
# Examples:
|
15
|
+
#
|
16
|
+
# Hootenanny::PublishNotification.for('http://mytopic')
|
17
|
+
# #=> <ActiveRecord::Relation>
|
18
|
+
#
|
19
|
+
# Hootenanny::PublishNotification.for %w{http://mytopic
|
20
|
+
# http://anothertopic}
|
21
|
+
# #=> <ActiveRecord::Relation>
|
22
|
+
#
|
23
|
+
# Returns an ActiveRecord::Relation
|
24
|
+
#
|
25
|
+
def self.for(topic)
|
26
|
+
where(:topic => topic)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.unprocessed
|
30
|
+
where(:state => 'unprocessed')
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.processed
|
34
|
+
where(:state => 'processed')
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.expired
|
38
|
+
where(:state => 'expired')
|
39
|
+
end
|
40
|
+
|
41
|
+
###
|
42
|
+
# Private: Creates a notification for a given topic. If the notification for
|
43
|
+
# the topic exists, it touches it to reflect the new update time and returns
|
44
|
+
# it. It does _not_ create a new notification.
|
45
|
+
#
|
46
|
+
# topic - A String representing a URI of a topic which has been proposed to
|
47
|
+
# have been updated.
|
48
|
+
#
|
49
|
+
# Examples:
|
50
|
+
#
|
51
|
+
# Hootenanny::PublishNotification.notify('http://example.com')
|
52
|
+
# # => <Hootenanny::PublishNotification topic: 'http://example.com'>
|
53
|
+
#
|
54
|
+
# Returns a Hootenanny::PublishNotification
|
55
|
+
# Raises a Hootenanny::URI::InvalidSchemeError if the topic URI are using
|
56
|
+
# something other than HTTP or HTTPS
|
57
|
+
# Raises a Hootenanny::URI::InvalidError if the URI can't be parsed properly
|
58
|
+
#
|
59
|
+
def self.notify(topic)
|
60
|
+
topic = Hootenanny::URI.parse(topic).to_s
|
61
|
+
|
62
|
+
find_or_initialize(topic).touch
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.each_unprocessed
|
66
|
+
unprocessed.find_each do |notification|
|
67
|
+
yield notification
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def process
|
72
|
+
self.update_attributes(:state => 'processed')
|
73
|
+
end
|
74
|
+
|
75
|
+
def expire
|
76
|
+
self.update_attributes(:state => 'expired')
|
77
|
+
end
|
78
|
+
|
79
|
+
def processed?
|
80
|
+
self.state == 'processed'
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def self.find_or_initialize(topic)
|
86
|
+
where(topic: topic).first ||
|
87
|
+
PublishNotification.create!(topic: topic)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -1,13 +1,26 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'active_record'
|
2
|
+
require 'chronological'
|
3
|
+
require 'hootenanny/uri'
|
3
4
|
|
4
5
|
module Hootenanny
|
5
6
|
class Subscription < ActiveRecord::Base
|
7
|
+
extend Chronological
|
8
|
+
|
6
9
|
self.table_name = 'hootenanny_subscriptions'
|
7
10
|
|
11
|
+
###
|
12
|
+
# Chronological generates time range logic based on :started_at and
|
13
|
+
# :duration_in_seconds
|
14
|
+
#
|
15
|
+
timeframe type: :duration_from_start,
|
16
|
+
duration: :lease_duration
|
17
|
+
|
8
18
|
###
|
9
19
|
# Private: Finds all subscriptions which are subscribed to a given topic
|
10
20
|
#
|
21
|
+
# It takes into account only _active_ subscriptions. If the subscription has
|
22
|
+
# expired, it will not be returned.
|
23
|
+
#
|
11
24
|
# topic - A String represeenting a topic which has been subscribed to
|
12
25
|
#
|
13
26
|
# Example:
|
@@ -19,28 +32,39 @@ class Subscription < ActiveRecord::Base
|
|
19
32
|
# subscriptions which are subscribed to the topic.
|
20
33
|
#
|
21
34
|
def self.to(topic)
|
22
|
-
where(:topic => topic)
|
35
|
+
active.where(:topic => topic.to_s)
|
23
36
|
end
|
24
37
|
|
25
38
|
###
|
26
|
-
# Private:
|
39
|
+
# Private: Subscribes a given subscriber to a given topic URL by creating
|
27
40
|
# a Subscription.
|
28
41
|
#
|
42
|
+
# This operation is idempotent. It will not modify a subscription if one
|
43
|
+
# already exists for the subscriber/topic passed in.
|
44
|
+
#
|
29
45
|
# options - A Hash of options
|
30
46
|
#
|
31
|
-
# :subscriber
|
32
|
-
#
|
33
|
-
# :to
|
34
|
-
#
|
35
|
-
#
|
47
|
+
# :subscriber - A String representing the URI which should be
|
48
|
+
# notified when changes occur on the topic
|
49
|
+
# :to - A String representing the topic URI which is
|
50
|
+
# publishing the items that the subscriber is
|
51
|
+
# interested in.
|
52
|
+
# :digest_secret - A String token of no more than 200 bytes which
|
53
|
+
# will be used during content distribution so that
|
54
|
+
# the subscriber can verify that the request is
|
55
|
+
# from the hub and not a undesired 3rd party
|
56
|
+
# (optional).
|
57
|
+
# :lease_duration - A Number representing the length of time (in
|
58
|
+
# seconds) after the subscription is created until
|
59
|
+
# it should be considered invalid
|
36
60
|
#
|
37
61
|
# Example:
|
38
62
|
#
|
39
63
|
# # If a subscription does not yet exist for the subscriber/topic, it
|
40
64
|
# # creates # a new Subscription
|
41
65
|
#
|
42
|
-
# Subscription.
|
43
|
-
#
|
66
|
+
# Subscription.subscribe(subscriber: 'http://example.com/my_callback',
|
67
|
+
# to: 'http://example.org/my_topic')
|
44
68
|
#
|
45
69
|
# # => <Subscription subscriber: 'http://example.com/my_callback',
|
46
70
|
# topic: 'http://example.org/my_topic'>
|
@@ -48,24 +72,49 @@ class Subscription < ActiveRecord::Base
|
|
48
72
|
#
|
49
73
|
#
|
50
74
|
# # If a subscription already exists for the subscriber/topic, it retrieves
|
51
|
-
# # and returns the current Subscription
|
75
|
+
# # and returns the current Subscription with all other attributes updated
|
76
|
+
# # as if the subscription were new.
|
52
77
|
#
|
53
|
-
# Subscription.
|
54
|
-
#
|
78
|
+
# Subscription.subscribe(subscriber: 'http://example.com/my_callback',
|
79
|
+
# to: 'http://example.org/my_topic',
|
80
|
+
# digest_secret: 'new_secret')
|
55
81
|
#
|
56
|
-
# # => <Subscription subscriber:
|
57
|
-
# topic:
|
82
|
+
# # => <Subscription subscriber: 'http://example.com/my_callback',
|
83
|
+
# topic: 'http://example.org/my_topic',
|
84
|
+
# digest_secret: 'new_secret'>
|
58
85
|
#
|
59
86
|
# Returns a Subscription
|
87
|
+
# Raises a Hootenanny::URI::InvalidSchemeError if the URIs are using something
|
88
|
+
# other than HTTP or HTTPS
|
89
|
+
# Raises a Hootenanny::URI::InvalidError if the URI can't be parsed properly
|
60
90
|
#
|
61
|
-
def self.
|
62
|
-
subscriber
|
63
|
-
topic
|
91
|
+
def self.subscribe(options = {})
|
92
|
+
subscriber = Hootenanny::URI.parse(options.fetch(:subscriber)).to_s
|
93
|
+
topic = Hootenanny::URI.parse(options.fetch(:to)).to_s
|
94
|
+
|
95
|
+
attrs = {}
|
96
|
+
attrs[:started_at] = Time.now.utc
|
97
|
+
attrs[:digest_secret] = options.fetch(:digest_secret, nil)
|
98
|
+
attrs[:lease_duration] = options.fetch(:lease_duration)
|
99
|
+
|
100
|
+
subscription = find_or_initialize(subscriber, topic)
|
101
|
+
|
102
|
+
subscription.update_attributes(attrs)
|
103
|
+
|
104
|
+
subscription
|
105
|
+
end
|
106
|
+
|
107
|
+
def subscriber
|
108
|
+
Hootenanny::URI.parse(read_attribute(:subscriber))
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
64
112
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
113
|
+
def self.find_or_initialize(subscriber, topic)
|
114
|
+
where(subscriber: subscriber,
|
115
|
+
topic: topic).first ||
|
116
|
+
Subscription.new(subscriber: subscriber,
|
117
|
+
topic: topic)
|
69
118
|
end
|
70
119
|
end
|
71
120
|
end
|