sm_sms_campaign_webhook 0.1.1 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +56 -0
- data/README.md +340 -7
- data/app/controllers/sm_sms_campaign_webhook/application_controller.rb +22 -0
- data/app/controllers/sm_sms_campaign_webhook/webhook_controller.rb +25 -0
- data/app/exceptions/sm_sms_campaign_webhook/error.rb +6 -0
- data/app/exceptions/sm_sms_campaign_webhook/invalid_payload.rb +6 -0
- data/app/exceptions/sm_sms_campaign_webhook/invalid_payload_value.rb +6 -0
- data/app/exceptions/sm_sms_campaign_webhook/missing_config_error.rb +6 -0
- data/app/exceptions/sm_sms_campaign_webhook/payload_dispatch_error.rb +6 -0
- data/app/jobs/sm_sms_campaign_webhook/application_job.rb +7 -0
- data/app/jobs/sm_sms_campaign_webhook/dispatch_payload_job.rb +14 -0
- data/app/jobs/sm_sms_campaign_webhook/process_campaign_engagement_job.rb +14 -0
- data/app/models/sm_sms_campaign_webhook/campaign_engagement/answer.rb +76 -0
- data/app/models/sm_sms_campaign_webhook/campaign_engagement.rb +216 -0
- data/app/operations/sm_sms_campaign_webhook/campaign_engagement_operation.rb +36 -0
- data/app/operations/sm_sms_campaign_webhook/payload_operation.rb +26 -0
- data/app/processors/sm_sms_campaign_webhook/default_processor.rb +11 -0
- data/app/processors/sm_sms_campaign_webhook/processable.rb +21 -0
- data/config/routes.rb +7 -0
- data/lib/generators/sm_sms_campaign_webhook/install/USAGE +12 -0
- data/lib/generators/sm_sms_campaign_webhook/install/install_generator.rb +35 -0
- data/lib/generators/sm_sms_campaign_webhook/install/templates/README +25 -0
- data/lib/generators/sm_sms_campaign_webhook/install/templates/sm_sms_campaign_webhook.rb +9 -0
- data/lib/generators/sm_sms_campaign_webhook/install/templates/sms_payload_processor.rb.erb +13 -0
- data/lib/sm_sms_campaign_webhook/engine.rb +11 -0
- data/lib/sm_sms_campaign_webhook/version.rb +1 -1
- data/lib/sm_sms_campaign_webhook.rb +25 -2
- data/sm_sms_campaign_webhook.gemspec +35 -21
- metadata +87 -22
- data/.gitignore +0 -16
- data/.rspec +0 -3
- data/.travis.yml +0 -18
- data/Gemfile +0 -4
- data/Rakefile +0 -6
- data/bin/console +0 -14
- data/bin/setup +0 -8
@@ -0,0 +1,216 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
module SmSmsCampaignWebhook
|
6
|
+
# Data model for campaign engagement event from SMS campaign.
|
7
|
+
class CampaignEngagement
|
8
|
+
attr_reader :payload
|
9
|
+
|
10
|
+
# @param payload [Hash] Campaign engagement event payload
|
11
|
+
def initialize(payload:)
|
12
|
+
@payload = payload.deep_dup.freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [String] Campaign engagement event UUID
|
16
|
+
# @raise [InvalidPayload] when uuid missing from payload
|
17
|
+
def event_uuid
|
18
|
+
@event_uuid ||= String(
|
19
|
+
payload.fetch("uuid") {
|
20
|
+
raise_invalid_payload_for("uuid")
|
21
|
+
}.freeze
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String] Campaign engagement event type
|
26
|
+
# @raise [InvalidPayload] when type missing from payload
|
27
|
+
def event_type
|
28
|
+
@event_type ||= String(
|
29
|
+
payload.fetch("type") {
|
30
|
+
raise_invalid_payload_for("type")
|
31
|
+
}.freeze
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [DateTime] Campaign engagement event timestamp
|
36
|
+
# @raise [InvalidPayload] when created_at missing from payload
|
37
|
+
# @raise [InvalidPayloadValue] when created_at not datetime
|
38
|
+
def event_created_at
|
39
|
+
@event_created_at ||= begin
|
40
|
+
raw_created_at = payload.fetch("created_at") {
|
41
|
+
raise_invalid_payload_for("created_at")
|
42
|
+
}
|
43
|
+
DateTime.parse(raw_created_at).freeze
|
44
|
+
end
|
45
|
+
rescue ArgumentError
|
46
|
+
raise InvalidPayloadValue,
|
47
|
+
"created_at has invalid datetime value #{payload.inspect}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Integer] ID of the engaged campaign
|
51
|
+
# @raise [InvalidPayload] when campaign id missing from payload
|
52
|
+
# @raise [InvalidPayloadValue] when campaign id not numeric
|
53
|
+
def campaign_id
|
54
|
+
@campaign_id ||= Integer(
|
55
|
+
campaign_hash.fetch("id") {
|
56
|
+
raise_invalid_payload_for("campaign id")
|
57
|
+
}
|
58
|
+
)
|
59
|
+
rescue ArgumentError
|
60
|
+
raise InvalidPayloadValue,
|
61
|
+
"campaign id has invalid integer value #{payload.inspect}"
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [String] Keyword of the engaged campaign
|
65
|
+
# @raise [InvalidPayload] when campaign keyword missing from payload
|
66
|
+
def campaign_keyword
|
67
|
+
@campaign_keyword ||= String(
|
68
|
+
campaign_hash.fetch("keyword") {
|
69
|
+
raise_invalid_payload_for("campaign keyword")
|
70
|
+
}.freeze
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [Integer] ID of the engaging phone
|
75
|
+
# @raise [InvalidPayload] when phone id missing from payload
|
76
|
+
# @raise [InvalidPayloadValue] when phone id not numeric
|
77
|
+
def phone_id
|
78
|
+
@phone_id ||= Integer(
|
79
|
+
phone_hash.fetch("id") {
|
80
|
+
raise_invalid_payload_for("phone id")
|
81
|
+
}
|
82
|
+
)
|
83
|
+
rescue ArgumentError
|
84
|
+
raise InvalidPayloadValue,
|
85
|
+
"phone id has invalid integer value #{payload.inspect}"
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [String] Phone number engaging the campaign
|
89
|
+
# @raise [InvalidPayload] when phone number missing from payload
|
90
|
+
def phone_number
|
91
|
+
@phone_number ||= String(
|
92
|
+
phone_hash.fetch("number") {
|
93
|
+
raise_invalid_payload_for("phone number")
|
94
|
+
}.freeze
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [Integer] ID of campaign engagement state record
|
99
|
+
# @raise [InvalidPayload] when phone_campaign_state id missing from payload
|
100
|
+
# @raise [InvalidPayloadValue] when phone_campaign_state id not numeric
|
101
|
+
def phone_campaign_state_id
|
102
|
+
@phone_campaign_state_id ||= Integer(
|
103
|
+
phone_campaign_state_hash.fetch("id") {
|
104
|
+
raise_invalid_payload_for("phone_campaign_state id")
|
105
|
+
}
|
106
|
+
)
|
107
|
+
rescue ArgumentError
|
108
|
+
raise InvalidPayloadValue,
|
109
|
+
"phone_campaign_state id has invalid integer value #{payload.inspect}"
|
110
|
+
end
|
111
|
+
|
112
|
+
# @return [TrueClass,FalseClass] Has campaign engagement completed?
|
113
|
+
# @raise [InvalidPayload] when phone_campaign_state completed missing from payload
|
114
|
+
# @raise [InvalidPayloadValue] when phone_campaign_state completed is not boolean
|
115
|
+
def phone_campaign_state_completed?
|
116
|
+
# Has the boolean value already been assigned?
|
117
|
+
unless @phone_campaign_state_completed.nil?
|
118
|
+
return @phone_campaign_state_completed
|
119
|
+
end
|
120
|
+
|
121
|
+
# Extract the value and memoize it.
|
122
|
+
@phone_campaign_state_completed = begin
|
123
|
+
completed = phone_campaign_state_hash
|
124
|
+
.fetch("completed") {
|
125
|
+
raise_invalid_payload_for("phone_campaign_state completed")
|
126
|
+
}
|
127
|
+
|
128
|
+
# Is this a boolean value?
|
129
|
+
if [true, false].none?(completed)
|
130
|
+
raise InvalidPayloadValue,
|
131
|
+
"phone_campaign_state completed has invalid boolean value #{payload.inspect}"
|
132
|
+
end
|
133
|
+
|
134
|
+
completed
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @return [DateTime,NilClass] Timestamp of campaign engagement completion if completed
|
139
|
+
# @raise [InvalidPayload] when phone_campaign_state completed_at missing from payload
|
140
|
+
# @raise [InvalidPayloadValue] when phone_campaign_state completed_at not datetime
|
141
|
+
def phone_campaign_state_completed_at
|
142
|
+
@phone_campaign_state_completed_at ||= begin
|
143
|
+
raw_completed_at = phone_campaign_state_hash.fetch("completed_at") {
|
144
|
+
raise_invalid_payload_for("phone_campaign_state completed_at")
|
145
|
+
}
|
146
|
+
DateTime.parse(raw_completed_at).freeze if raw_completed_at
|
147
|
+
end
|
148
|
+
rescue ArgumentError
|
149
|
+
raise InvalidPayloadValue,
|
150
|
+
"phone_campaign_state completed_at has invalid datetime value #{payload.inspect}"
|
151
|
+
end
|
152
|
+
|
153
|
+
# @return [Array<Answer>] Modeled campaign engagement answers
|
154
|
+
# @raise [InvalidPayload] when phone_campaign_state answers missing from payload
|
155
|
+
# @raise [InvalidPayloadValue] when phone_campaign_state answers not hash
|
156
|
+
def phone_campaign_state_answers
|
157
|
+
@phone_campaign_state_answers ||= begin
|
158
|
+
# Extract answers data from payload.
|
159
|
+
data = phone_campaign_state_hash.fetch("answers") {
|
160
|
+
raise_invalid_payload_for("phone_campaign_state answers")
|
161
|
+
}
|
162
|
+
|
163
|
+
# Is this hash data?
|
164
|
+
unless data.is_a?(Hash)
|
165
|
+
raise InvalidPayloadValue,
|
166
|
+
"phone_campaign_state answers has invalid hash value #{payload.inspect}"
|
167
|
+
end
|
168
|
+
|
169
|
+
# Cast answers data.
|
170
|
+
Answer.cast(data: data).freeze
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# @return [TrueClass,FalseClass] Are any campaign engagement answers present?
|
175
|
+
def phone_campaign_state_answers?
|
176
|
+
!phone_campaign_state_answers.empty?
|
177
|
+
end
|
178
|
+
|
179
|
+
# @param field [String] Answer data to find
|
180
|
+
# @return [Answer,NilClass] Modeled answer for field when found
|
181
|
+
def answer_for(field:)
|
182
|
+
phone_campaign_state_answers.detect do |answer|
|
183
|
+
answer.field == field
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
private
|
188
|
+
|
189
|
+
# @param attr [String] Expected attribute missing from payload
|
190
|
+
def raise_invalid_payload_for(attr)
|
191
|
+
raise InvalidPayload, "#{attr} missing from payload #{payload.inspect}"
|
192
|
+
end
|
193
|
+
|
194
|
+
# @return [Hash] Data from campaign engagement payload
|
195
|
+
def payload_data
|
196
|
+
@payload_data ||= payload.fetch("data", {}).freeze
|
197
|
+
end
|
198
|
+
|
199
|
+
# @return [Hash] Campaign hash from payload data
|
200
|
+
def campaign_hash
|
201
|
+
@campaign_hash ||= payload_data.fetch("campaign", {}).freeze
|
202
|
+
end
|
203
|
+
|
204
|
+
# @return [Hash] Phone hash from payload data
|
205
|
+
def phone_hash
|
206
|
+
@phone_hash ||= payload_data.fetch("phone", {}).freeze
|
207
|
+
end
|
208
|
+
|
209
|
+
# @return [Hash] Campaign engagement state hash from payload data
|
210
|
+
def phone_campaign_state_hash
|
211
|
+
@phone_campaign_state_hash ||= payload_data
|
212
|
+
.fetch("phone_campaign_state", {})
|
213
|
+
.freeze
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SmSmsCampaignWebhook
|
4
|
+
# Handles campaign engagement payload data modeling and processing.
|
5
|
+
class CampaignEngagementOperation
|
6
|
+
# Models the campaign engagement payload and hands of processing
|
7
|
+
# of the data to the processor.
|
8
|
+
#
|
9
|
+
# @param payload [Hash] Deserialized SMS campaign engagement payload
|
10
|
+
# @raise [PayloadDispatchError] when not campaign engagement payload
|
11
|
+
# @see .processor
|
12
|
+
def self.process(payload:)
|
13
|
+
logger.debug "#{name} - Processing campaign engagement payload: #{payload.inspect}"
|
14
|
+
|
15
|
+
if payload.fetch("type") != "campaign.engagement"
|
16
|
+
raise PayloadDispatchError,
|
17
|
+
"dispatched payload different from campaign.engagement #{payload.inspect}"
|
18
|
+
end
|
19
|
+
|
20
|
+
campaign_engagement = CampaignEngagement.new(payload: payload)
|
21
|
+
processor.process_campaign_engagement(campaign_engagement)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Processable] Configured SMS campaign payload processor
|
25
|
+
# @see SmSmsCampaignWebhook.processor
|
26
|
+
def self.processor
|
27
|
+
@processor ||= SmSmsCampaignWebhook.processor
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [ActiveSupport::Logger] Abstraction of app logger
|
31
|
+
def self.logger
|
32
|
+
@logger ||= Rails.logger
|
33
|
+
end
|
34
|
+
private_class_method :logger
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SmSmsCampaignWebhook
|
4
|
+
# Handles payload data modeling and data processing.
|
5
|
+
class PayloadOperation
|
6
|
+
# @param payload [Hash] Deserialized payload from SMS campaign service
|
7
|
+
# @see ProcessCampaignEngagementJob#perform
|
8
|
+
def self.dispatch(payload:)
|
9
|
+
logger.debug "#{name} - Dispatching payload: #{payload.inspect}"
|
10
|
+
|
11
|
+
case payload.fetch("type", "unknown")
|
12
|
+
when "campaign.engagement"
|
13
|
+
ProcessCampaignEngagementJob.perform_later(payload)
|
14
|
+
else
|
15
|
+
# NOOP - Unsupported event type.
|
16
|
+
logger.warn "#{name} - Unsupported event type"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [ActiveSupport::Logger] Abstraction of app logger
|
21
|
+
def self.logger
|
22
|
+
@logger ||= Rails.logger
|
23
|
+
end
|
24
|
+
private_class_method :logger
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SmSmsCampaignWebhook
|
4
|
+
# Define behavior that SMS campaign payload processors must implement.
|
5
|
+
module Processable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
class_methods do
|
9
|
+
# Campaign engagement operation sends modeled campaign engagement payload
|
10
|
+
# to this method for applying business logic. Implementors should define
|
11
|
+
# the app behavior to properly handle this data.
|
12
|
+
#
|
13
|
+
# @param campaign_engagement [CampaignEngagement] modeled payload
|
14
|
+
# @raise [NotImplementedError] requiring implementing class to define behavior
|
15
|
+
def process_campaign_engagement(campaign_engagement)
|
16
|
+
raise NotImplementedError,
|
17
|
+
"#{self.class} must implement .process_campaign_engagement receiving campaign_engagement param"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Description:
|
2
|
+
Configures the app to handle inbound requests from the SMS campaign service.
|
3
|
+
|
4
|
+
Example:
|
5
|
+
rails generate sm_sms_campaign_webhook:install
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
app/processors/sms_payload_processor.rb
|
9
|
+
config/initializers/sm_sms_campaign_webhook.rb
|
10
|
+
|
11
|
+
This will mount the engine in:
|
12
|
+
config/routes.rb
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/base"
|
4
|
+
|
5
|
+
module SmSmsCampaignWebhook
|
6
|
+
# Namespace for generators provided by the gem.
|
7
|
+
module Generators
|
8
|
+
# Installs files to prep an app for SMS campaign webhook.
|
9
|
+
class InstallGenerator < Rails::Generators::Base
|
10
|
+
source_root File.expand_path("templates", __dir__)
|
11
|
+
|
12
|
+
# Copy initializer template to config/initializers
|
13
|
+
def copy_initializer
|
14
|
+
template "sm_sms_campaign_webhook.rb", "config/initializers/sm_sms_campaign_webhook.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Copy processor template to app/processors
|
18
|
+
def copy_processor
|
19
|
+
template "sms_payload_processor.rb.erb", "app/processors/sms_payload_processor.rb"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Mount engine to path in config/routes.rb
|
23
|
+
def add_mount_path
|
24
|
+
insert_into_file "config/routes.rb",
|
25
|
+
" mount SmSmsCampaignWebhook::Engine => \"/sms_campaign\"\n",
|
26
|
+
after: "Rails.application.routes.draw do\n"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Dump the README for the app developer
|
30
|
+
def show_readme
|
31
|
+
readme "README" if behavior == :invoke
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
================================================================================
|
2
|
+
|
3
|
+
Some setup you must do manually if you haven't yet:
|
4
|
+
|
5
|
+
1. Set the ENV value SM_SMS_CAMPAIGN_WEBHOOK_AUTH_TOKEN with a Rails secret:
|
6
|
+
|
7
|
+
$ bundle exec rails secret
|
8
|
+
|
9
|
+
2. Prep the app for ActiveJob using Sidekiq. In config/application.rb:
|
10
|
+
|
11
|
+
config.active_job.queue_adapter = :sidekiq
|
12
|
+
|
13
|
+
In Procfile or appropriate app launch config:
|
14
|
+
|
15
|
+
worker: bundle exec sidekiq --config config/sidekiq.yml
|
16
|
+
|
17
|
+
3. Implement SmsPayloadProcessor behavior. Remember, the generated processor
|
18
|
+
provides the skeleton but will raise errors out of the box. Data handling
|
19
|
+
is app specific!
|
20
|
+
|
21
|
+
This will configure the app to be ready to accept SMS campaign service payloads.
|
22
|
+
|
23
|
+
Happy hacking!
|
24
|
+
|
25
|
+
================================================================================
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sm_sms_campaign_webhook"
|
4
|
+
|
5
|
+
SmSmsCampaignWebhook.config do |config|
|
6
|
+
# SMS campaign payload processor implementing SmSmsCampaignWebhook::Processable behavior.
|
7
|
+
# default: SmSmsCampaignWebhook::DefaultProcessor (raises errors for processing)
|
8
|
+
config.processor = SmsPayloadProcessor
|
9
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Processes SMS campaign service payloads.
|
4
|
+
# @see https://github.com/SouthernMade/sm_sms_campaign_webhook/blob/v<%= SmSmsCampaignWebhook::VERSION %>/app/processors/sm_sms_campaign_webhook/processable.rb SmSmsCampaignWebhook::Processable
|
5
|
+
class SmsPayloadProcessor
|
6
|
+
include SmSmsCampaignWebhook::Processable
|
7
|
+
|
8
|
+
# @param campaign_engagement [SmSmsCampaignWebhook::CampaignEngagement]
|
9
|
+
# @see https://github.com/SouthernMade/sm_sms_campaign_webhook/blob/v<%= SmSmsCampaignWebhook::VERSION %>/app/models/sm_sms_campaign_webhook/campaign_engagement.rb SmSmsCampaignWebhook::CampaignEngagement
|
10
|
+
# @todo Implement business logic to process campaign engagement.
|
11
|
+
# def self.process_campaign_engagement(campaign_engagement)
|
12
|
+
# end
|
13
|
+
end
|
@@ -1,9 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "sm_sms_campaign_webhook/engine"
|
3
4
|
require "sm_sms_campaign_webhook/version"
|
4
5
|
|
5
6
|
# Namespace for SMS campaign webhook.
|
6
7
|
module SmSmsCampaignWebhook
|
7
|
-
#
|
8
|
-
|
8
|
+
# @return [SmSmsCampaignWebhook] self for configuration purposes
|
9
|
+
def self.config(&block)
|
10
|
+
yield self if block
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String] SMS campaign webhook auth token
|
14
|
+
# @raise [MissingConfigError] when ENV does not contain SM_SMS_CAMPAIGN_WEBHOOK_AUTH_TOKEN value
|
15
|
+
def self.auth_token
|
16
|
+
@auth_token ||= ENV.fetch("SM_SMS_CAMPAIGN_WEBHOOK_AUTH_TOKEN") {
|
17
|
+
raise MissingConfigError,
|
18
|
+
"ENV does not contain SM_SMS_CAMPAIGN_WEBHOOK_AUTH_TOKEN value"
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Processable] SMS campaign payload processor used by operations
|
23
|
+
def self.processor
|
24
|
+
@processor ||= DefaultProcessor
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param processor [Processable] Custom SMS campaign payload processor
|
28
|
+
# @see Processable
|
29
|
+
def self.processor=(processor)
|
30
|
+
@processor = processor
|
31
|
+
end
|
9
32
|
end
|
@@ -1,34 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
lib = File.expand_path("lib", __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
5
|
require "sm_sms_campaign_webhook/version"
|
4
6
|
|
5
7
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name
|
7
|
-
spec.version
|
8
|
-
spec.
|
9
|
-
spec.
|
10
|
-
|
11
|
-
spec.
|
12
|
-
spec.
|
13
|
-
spec.
|
14
|
-
|
15
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
8
|
+
spec.name = "sm_sms_campaign_webhook"
|
9
|
+
spec.version = SmSmsCampaignWebhook::VERSION
|
10
|
+
spec.license = "MIT"
|
11
|
+
spec.summary = "Middleware providing webhook for Southern Made SMS Campaign Engagement."
|
12
|
+
spec.description = "Middleware providing webhook for Southern Made SMS Campaign Engagement."
|
13
|
+
spec.homepage = "https://github.com/SouthernMade/sm_sms_campaign_webhook"
|
14
|
+
spec.authors = ["Cameron Dykes", "Matt Mueller"]
|
15
|
+
spec.email = ["cameron@southernmade.com", "matt@southernmade.com"]
|
16
16
|
|
17
|
-
spec.metadata
|
18
|
-
|
19
|
-
|
17
|
+
spec.metadata = {
|
18
|
+
"allowed_push_host" => "https://rubygems.org",
|
19
|
+
"changelog_uri" => "#{spec.homepage}/blob/develop/CHANGELOG.md",
|
20
|
+
"documentation_uri" => "https://www.rubydoc.info/gems/sm_sms_campaign_webhook/#{spec.version}",
|
21
|
+
"homepage_uri" => spec.homepage,
|
22
|
+
"source_code_uri" => spec.homepage
|
23
|
+
}
|
20
24
|
|
21
25
|
# Specify which files should be added to the gem when it is released.
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
spec.files = Dir[
|
27
|
+
"{app,config,lib}/**/*",
|
28
|
+
"CHANGELOG.md",
|
29
|
+
"LICENSE.txt",
|
30
|
+
"README.md",
|
31
|
+
"sm_sms_campaign_webhook.gemspec"
|
32
|
+
]
|
28
33
|
spec.require_paths = ["lib"]
|
29
34
|
|
35
|
+
# Required version of Ruby guided by Rails.
|
36
|
+
spec.required_ruby_version = ">= 2.7.5"
|
37
|
+
|
38
|
+
# Runtime dependencies.
|
39
|
+
spec.add_dependency "rails", [">= 6.0", "< 7.1"]
|
40
|
+
|
41
|
+
# Development + test dependencies.
|
30
42
|
spec.add_development_dependency "bundler", "~> 2.0"
|
31
|
-
spec.add_development_dependency "rake", "~>
|
32
|
-
spec.add_development_dependency "rspec", "~>
|
43
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
44
|
+
spec.add_development_dependency "rspec-rails", "~> 5.0"
|
45
|
+
spec.add_development_dependency "simplecov", "~> 0.20"
|
46
|
+
spec.add_development_dependency "standard", "~> 1.0"
|
33
47
|
spec.add_development_dependency "yard", "~> 0.9"
|
34
48
|
end
|