hootenanny 0.0.1 → 0.1.0
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.
- 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
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'hootenanny/feed'
|
3
|
+
require 'hootenanny/feed/json_feed_item'
|
4
|
+
|
5
|
+
module Hootenanny
|
6
|
+
class Feed
|
7
|
+
class JSONFeed < ::Hootenanny::Feed
|
8
|
+
|
9
|
+
def items
|
10
|
+
@items ||= content.fetch('items', []).map { |i| Hootenanny::Feed::JSONFeedItem.new i }
|
11
|
+
end
|
12
|
+
|
13
|
+
def content_type
|
14
|
+
'application/json'
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
@to_s ||= -> do
|
19
|
+
content['items'] = items.map(&:content)
|
20
|
+
|
21
|
+
JSON.dump(content)
|
22
|
+
end.call
|
23
|
+
end
|
24
|
+
|
25
|
+
def content
|
26
|
+
@content
|
27
|
+
end
|
28
|
+
|
29
|
+
def content=(content)
|
30
|
+
@content = coerce_content(content)
|
31
|
+
rescue JSON::ParserError => e
|
32
|
+
raise Hootenanny::Feed::ParseError.wrap(e)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def coerce_content(content)
|
38
|
+
if content.is_a? String
|
39
|
+
JSON.parse(content)
|
40
|
+
elsif content.is_a? Hash
|
41
|
+
content.inject({}) { |hash, pair| hash[pair[0].to_s] = pair[1]; hash }
|
42
|
+
else
|
43
|
+
raise JSON::ParserError.new("Do not know how to parse '#{content}'")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Hootenanny
|
2
|
+
class Feed
|
3
|
+
class NullFeed < ::Hootenanny::Feed
|
4
|
+
|
5
|
+
def items
|
6
|
+
[]
|
7
|
+
end
|
8
|
+
|
9
|
+
def content_type
|
10
|
+
''
|
11
|
+
end
|
12
|
+
|
13
|
+
def -(other)
|
14
|
+
other
|
15
|
+
end
|
16
|
+
|
17
|
+
def +(other)
|
18
|
+
other
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def content=(content)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rss'
|
2
|
+
require 'hootenanny/feed'
|
3
|
+
require 'hootenanny/feed/rss_feed_item'
|
4
|
+
|
5
|
+
module Hootenanny
|
6
|
+
class Feed
|
7
|
+
class RSSFeed < ::Hootenanny::Feed
|
8
|
+
|
9
|
+
def items
|
10
|
+
@items ||= content.items.map { |i| Hootenanny::Feed::RSSFeedItem.new i }
|
11
|
+
end
|
12
|
+
|
13
|
+
def content_type
|
14
|
+
'application/rss+xml'
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
@to_s ||= -> do
|
19
|
+
item_string = items.map { |item| item.content.to_s }.join('')
|
20
|
+
|
21
|
+
feed_string = content.to_s
|
22
|
+
feed_string.gsub!(%r{<item>.*</item>}m, item_string)
|
23
|
+
|
24
|
+
feed_string
|
25
|
+
end.call
|
26
|
+
end
|
27
|
+
|
28
|
+
def content
|
29
|
+
@content
|
30
|
+
end
|
31
|
+
|
32
|
+
def content=(content)
|
33
|
+
@content = coerce_content(content)
|
34
|
+
rescue RSS::Error => e
|
35
|
+
raise Hootenanny::Feed::ParseError.wrap(e)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def coerce_content(content)
|
41
|
+
if content.is_a? String
|
42
|
+
RSS::Parser.parse(content) ||
|
43
|
+
RSS::Rss.new('2.0')
|
44
|
+
elsif content.is_a? RSS::Rss
|
45
|
+
content
|
46
|
+
else
|
47
|
+
raise RSS::Error.new("Do not know how to parse '#{content}'")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
require 'hootenanny/feed_store/file_feed_store'
|
3
|
+
require 'hootenanny/feed_store/web_feed_store'
|
4
|
+
|
5
|
+
module Hootenanny
|
6
|
+
class FeedStore
|
7
|
+
|
8
|
+
def self.fetch(url, options = {})
|
9
|
+
storage_type = options.fetch(:location, :web)
|
10
|
+
feed_store = infer(type: storage_type)
|
11
|
+
|
12
|
+
feed_store.fetch(url)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.infer(options = {})
|
16
|
+
storage_type = options.fetch(:type)
|
17
|
+
storage_class = from_type(storage_type)
|
18
|
+
|
19
|
+
storage_class.new(options)
|
20
|
+
rescue KeyError => e
|
21
|
+
raise Hootenanny::FeedStore::InferenceError.wrap(e)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.from_type(type)
|
25
|
+
"Hootenanny::FeedStore::#{type.to_s.camelize}FeedStore".constantize
|
26
|
+
rescue NameError
|
27
|
+
raise Hootenanny::FeedStore::UnknownStorageTypeError
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'hootenanny/uri'
|
3
|
+
require 'hootenanny/feed'
|
4
|
+
require 'hootenanny/feed/file'
|
5
|
+
|
6
|
+
module Hootenanny
|
7
|
+
class FeedStore
|
8
|
+
class FileFeedStore
|
9
|
+
def initialize(options = {})
|
10
|
+
self.feed_file = options.fetch(:location)
|
11
|
+
end
|
12
|
+
|
13
|
+
def fetch(options = {})
|
14
|
+
self.url = options.fetch(:url)
|
15
|
+
feed_file = self.feed_file << options[:path] << url_file_glob
|
16
|
+
|
17
|
+
Hootenanny::Feed.infer(feed_file.read,
|
18
|
+
type: feed_file.type)
|
19
|
+
end
|
20
|
+
|
21
|
+
def store(options = {})
|
22
|
+
self.url = options.fetch(:url)
|
23
|
+
feed = options.fetch(:feed)
|
24
|
+
feed_type = feed.type
|
25
|
+
|
26
|
+
feed_file = self.feed_file << options[:path] << "#{url}.#{feed_type}"
|
27
|
+
|
28
|
+
feed_file.write(feed)
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
attr_reader :feed_file,
|
34
|
+
:url
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def url_file_glob
|
39
|
+
if @url.nil? || @url.to_s == ''
|
40
|
+
'*.*'
|
41
|
+
else
|
42
|
+
"#{@url}.*"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def url=(other_url)
|
47
|
+
@url = Hootenanny::URI.parse(other_url.to_s).to_digest
|
48
|
+
end
|
49
|
+
|
50
|
+
def feed_file=(location)
|
51
|
+
@feed_file = Hootenanny::Feed::File.new(location)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'hootenanny/feed'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
class FeedStore
|
6
|
+
class WebFeedStore
|
7
|
+
CONTENT_TYPE_MAPPINGS = {
|
8
|
+
'application/rss+xml' => 'RSS',
|
9
|
+
'application/atom+xml' => 'Atom',
|
10
|
+
'application/json' => 'JSON',
|
11
|
+
}
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
end
|
15
|
+
|
16
|
+
def fetch(options = {})
|
17
|
+
self.url = options.fetch(:url)
|
18
|
+
|
19
|
+
Hootenanny::Feed.infer(content,
|
20
|
+
type: feed_serialization_type)
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
attr_accessor :url
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def content
|
30
|
+
response.body
|
31
|
+
end
|
32
|
+
|
33
|
+
def feed_serialization_type
|
34
|
+
CONTENT_TYPE_MAPPINGS.fetch(response.env[:response_headers][:content_type])
|
35
|
+
end
|
36
|
+
|
37
|
+
def response
|
38
|
+
@response ||= Faraday.get(url.to_s)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/hootenanny/hub.rb
CHANGED
@@ -1,41 +1,135 @@
|
|
1
|
-
require 'hootenanny/
|
1
|
+
require 'hootenanny/request'
|
2
|
+
require 'hootenanny/correspondent'
|
3
|
+
require 'hootenanny/publish_notification'
|
4
|
+
require 'hootenanny/publish_notification_expiration_policy'
|
2
5
|
|
6
|
+
###
|
7
|
+
# Public: The class that provides the interface for all of the PubSubHubbub
|
8
|
+
# server functionaliity.
|
9
|
+
#
|
10
|
+
# ## Subscribing to a Feed ##
|
11
|
+
#
|
12
|
+
# In order to subscribe to a topic URI, the subscription request must be passed
|
13
|
+
# in. This object contains all of the information necessary to actually apply
|
14
|
+
# the subscription between the subscriber URI and the topic URI.
|
15
|
+
#
|
16
|
+
# ```ruby
|
17
|
+
# request = Hootenanny::Hub.request(request_attribute_hash)
|
18
|
+
#
|
19
|
+
# Hootenanny::Hub.subscribe(request)
|
20
|
+
# ```
|
21
|
+
#
|
22
|
+
# ## Generating a Request ##
|
23
|
+
#
|
24
|
+
# Before an action may be taken, the proper request must be generated from all
|
25
|
+
# of the disparate information. Once generated, it can be passed to other
|
26
|
+
# methods on the class to perform that action.
|
27
|
+
#
|
28
|
+
# `:type` and `:action` are required options for all requests, other requests
|
29
|
+
# may have additional required options, depending on the `:type` passed in. For
|
30
|
+
# example, a subscription request requires a `:callback` and an `:action`.
|
31
|
+
#
|
32
|
+
# ```ruby
|
33
|
+
# Hootenanny::Hub.request(type: :subscription,
|
34
|
+
# action: :create,
|
35
|
+
# callback: 'http://example.com/callback',
|
36
|
+
# topic: 'http://example.org/topic')
|
37
|
+
# ```
|
38
|
+
#
|
39
|
+
# ## Notifying of a Content Update
|
40
|
+
#
|
41
|
+
# In order to remove the need for the hub to have to poll each of its
|
42
|
+
# publishers, each publisher can create a notification on the hub so that the
|
43
|
+
# hub can handle disseminating that information to its various subscribers.
|
44
|
+
#
|
45
|
+
# ```ruby
|
46
|
+
# Hootenanny::Hub.notify_of_publication(url: 'http://mytopic')
|
47
|
+
# ```
|
48
|
+
#
|
3
49
|
module Hootenanny
|
4
50
|
class Hub
|
5
51
|
|
6
52
|
###
|
7
|
-
# Public: Subscribes
|
8
|
-
#
|
9
|
-
# URL.
|
53
|
+
# Public: Subscribes a subscriber to a topic based on the information defined
|
54
|
+
# in a subscription request.
|
10
55
|
#
|
11
56
|
# This method is idempotent. If a subscription already exists, it will be
|
12
57
|
# reused and sent back without notifying the user of that fact.
|
13
58
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# to this callback URI.
|
18
|
-
# options - A Hash of options
|
59
|
+
# If the subscription does not already exist, it will be verified against the
|
60
|
+
# callback URL. If all is ok, a subscription will be created, if not, an
|
61
|
+
# error with details will be returned.
|
19
62
|
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# activities for a given topic/item/resource.
|
63
|
+
# request - Any object which responds to #apply. Typically this should be
|
64
|
+
# a Hootenanny::Request::Subscription.
|
23
65
|
#
|
24
|
-
#
|
25
|
-
#
|
66
|
+
# Returns an indeterminate object (based on the request passed in) but is
|
67
|
+
# typically a Hootenanny::Subscription.
|
68
|
+
# Raises Hootenanny::SubscriptionAssignmentError with a message describing the
|
69
|
+
# problem
|
70
|
+
#
|
71
|
+
def self.subscribe(request)
|
72
|
+
request.apply
|
73
|
+
end
|
74
|
+
|
75
|
+
###
|
76
|
+
# Public: Notifies the hub that topic has had its content updated since the
|
77
|
+
# last time the content was pulled.
|
26
78
|
#
|
27
|
-
#
|
79
|
+
# This method is idempotent. If a notification already exists, it will be
|
80
|
+
# reused and sent back without notifying the user of that fact.
|
28
81
|
#
|
29
|
-
#
|
30
|
-
#
|
82
|
+
# If all is ok, a notification will be created, if not, an error with details
|
83
|
+
# will be returned.
|
84
|
+
#
|
85
|
+
# request - Any object which responds to #apply. Typically this should be
|
86
|
+
# a Hootenanny::Request::PublishNotification.
|
87
|
+
#
|
88
|
+
# Returns an indeterminate object (based on the request passed in) but is
|
89
|
+
# typically a Hootenanny::PublishNotification.
|
90
|
+
# Raises Hootenanny::PublishNotificationError with a message describing the
|
31
91
|
# problem
|
32
92
|
#
|
33
|
-
def self.
|
34
|
-
|
35
|
-
|
93
|
+
def self.notify_of_publication(request)
|
94
|
+
request.apply
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.broadcast
|
98
|
+
Hootenanny::PublishNotification.each_unprocessed do |notification|
|
99
|
+
if Hootenanny::Correspondent.broadcast(notification.topic)
|
100
|
+
notification.process
|
101
|
+
else
|
102
|
+
Hootenanny::PublishNotificationExpirationPolicy.apply notification
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
36
106
|
|
37
|
-
|
38
|
-
|
107
|
+
###
|
108
|
+
# Public: Generates a request of varying types which can be used to perform
|
109
|
+
# actions such as subscribing to a topic.
|
110
|
+
#
|
111
|
+
# This method has zero side-effects and is only utilized to prepare a request.
|
112
|
+
#
|
113
|
+
# options - A Hash of options which can vary based on the type of request that
|
114
|
+
# is being... requested.
|
115
|
+
#
|
116
|
+
# :type - The type of request (eg: :subscription)
|
117
|
+
# :action - The action to be performed when the request is
|
118
|
+
# applied. This can vary based on the type of request
|
119
|
+
# being created.
|
120
|
+
# :callback - Used for :subscription requests. Is a URI which
|
121
|
+
# should be notified when the topic that it is
|
122
|
+
# interested in, is updated.
|
123
|
+
# :topic - Used for :subscription requests. Is a URI which
|
124
|
+
# represents all of the items which the subscriber is
|
125
|
+
# interested in.
|
126
|
+
#
|
127
|
+
# Returns an indeterminate object (based on the type of request that is
|
128
|
+
# generated, but all of them will inherit from Hootenanny::Request
|
129
|
+
# Raises a Hootenanny::Request::BuildError if any problems are encountered
|
130
|
+
#
|
131
|
+
def self.request(options = {})
|
132
|
+
Hootenanny::Request.build(options)
|
39
133
|
end
|
40
134
|
end
|
41
135
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'hootenanny/configuration'
|
2
|
+
|
3
|
+
module Hootenanny
|
4
|
+
class PublishNotificationExpirationPolicy
|
5
|
+
def self.apply(notification)
|
6
|
+
if notification.updated_at < expiration_threshold
|
7
|
+
notification.expire
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def self.expiration_threshold
|
14
|
+
Hootenanny.config.default_publish_notification_expiration_threshold
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|