broadcast-ruby 0.2.0 → 0.3.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 +87 -0
- data/Gemfile.lock +3 -2
- data/README.md +272 -17
- data/SDK-COVERAGE.md +422 -0
- data/lib/broadcast/client.rb +36 -136
- data/lib/broadcast/configuration.rb +53 -4
- data/lib/broadcast/connection.rb +278 -0
- data/lib/broadcast/debug_logger.rb +64 -0
- data/lib/broadcast/delivery_method.rb +5 -0
- data/lib/broadcast/errors.rb +27 -1
- data/lib/broadcast/resources/autopilots.rb +100 -0
- data/lib/broadcast/resources/discovery.rb +36 -0
- data/lib/broadcast/resources/migration.rb +75 -0
- data/lib/broadcast/resources/opt_in_forms.rb +12 -0
- data/lib/broadcast/resources/subscribers.rb +24 -0
- data/lib/broadcast/resources/templates.rb +17 -0
- data/lib/broadcast/resources/transactionals.rb +39 -2
- data/lib/broadcast/response.rb +104 -0
- data/lib/broadcast/version.rb +1 -1
- data/lib/broadcast/webhook.rb +34 -0
- data/lib/broadcast.rb +6 -0
- metadata +9 -3
- data/.rubocop.yml +0 -50
|
@@ -24,6 +24,18 @@ module Broadcast
|
|
|
24
24
|
# Nested settings hashes (theme_settings, automation_settings, security_settings,
|
|
25
25
|
# trigger_settings, widget_settings) and arrays (opt_in_form_blocks_attributes,
|
|
26
26
|
# opt_in_post_submission_blocks_attributes) are passed through verbatim.
|
|
27
|
+
#
|
|
28
|
+
# Core:
|
|
29
|
+
# label:, form_type:, widget_type:, enabled:, allowed_embedding_domains:
|
|
30
|
+
#
|
|
31
|
+
# Confirmation / welcome flow:
|
|
32
|
+
# confirmation_email_template_id: Template sent to confirm the address
|
|
33
|
+
# welcome_email_template_id: Template sent after confirmation
|
|
34
|
+
# confirmation_redirect_url: where to send the subscriber post-confirm
|
|
35
|
+
# include_unsubscribe_link_in_confirmation: boolean
|
|
36
|
+
#
|
|
37
|
+
# Custom fields declared in opt_in_form_blocks_attributes support a
|
|
38
|
+
# multi-line variant, which renders as a textarea on the hosted form.
|
|
27
39
|
def create(**attrs)
|
|
28
40
|
post('/api/v1/opt_in_forms', { opt_in_form: attrs })
|
|
29
41
|
end
|
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
module Broadcast
|
|
4
4
|
module Resources
|
|
5
5
|
class Subscribers < Base
|
|
6
|
+
# List subscribers (250 per page, with `pagination` metadata; pass `page:`).
|
|
7
|
+
#
|
|
8
|
+
# Filters, all optional and combinable:
|
|
9
|
+
# is_active: true | false
|
|
10
|
+
# source: exact match on the source string
|
|
11
|
+
# created_after: ISO-8601 timestamp
|
|
12
|
+
# created_before: ISO-8601 timestamp
|
|
13
|
+
# tags: array — AND logic, subscriber must have all of them
|
|
14
|
+
# email: partial (case-insensitive) match, not exact
|
|
15
|
+
# confirmation_status: 'confirmed' | 'unconfirmed'
|
|
16
|
+
# custom_data: hash — JSONB containment, e.g. { plan: 'pro' }
|
|
17
|
+
#
|
|
18
|
+
# An unparseable created_after/created_before is *ignored* by the server
|
|
19
|
+
# rather than rejected; it comes back as a `parameter_ignored` warning on
|
|
20
|
+
# the response, so a bad timestamp silently widens the result set unless
|
|
21
|
+
# you check `result.warnings`.
|
|
6
22
|
def list(**params)
|
|
7
23
|
get('/api/v1/subscribers.json', params)
|
|
8
24
|
end
|
|
@@ -22,6 +38,14 @@ module Broadcast
|
|
|
22
38
|
# When set, the subscriber is created in unconfirmed state
|
|
23
39
|
# and a confirmation email is queued.
|
|
24
40
|
# confirmation_template_id: custom confirmation template (used with double_opt_in: true)
|
|
41
|
+
#
|
|
42
|
+
# Admin tokens only:
|
|
43
|
+
# confirmed_at: backdate the confirmation timestamp on create.
|
|
44
|
+
# Intended for migrating an already-confirmed list off
|
|
45
|
+
# another provider. Ignored (with a warning) on update,
|
|
46
|
+
# and ignored entirely for channel-scoped tokens.
|
|
47
|
+
#
|
|
48
|
+
# Note `unsubscribed_at` is never settable here — use `unsubscribe(email)`.
|
|
25
49
|
def create(**attrs)
|
|
26
50
|
double_opt_in = attrs.delete(:double_opt_in)
|
|
27
51
|
confirmation_template_id = attrs.delete(:confirmation_template_id)
|
|
@@ -11,6 +11,23 @@ module Broadcast
|
|
|
11
11
|
get("/api/v1/templates/#{id}")
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
# Create a template. Attrs are wrapped under `template:` on the wire.
|
|
15
|
+
#
|
|
16
|
+
# Content:
|
|
17
|
+
# label:, subject:, preheader:, body:, html_body:
|
|
18
|
+
#
|
|
19
|
+
# Confirmation templates (double opt-in):
|
|
20
|
+
# template_purpose: marks the template's role, e.g. 'confirmation'
|
|
21
|
+
# confirmation_text: copy shown in the confirmation email
|
|
22
|
+
# default_confirmation: make this the channel's default confirmation template
|
|
23
|
+
# confirmation_page_settings: per-state page copy, keyed by state, each
|
|
24
|
+
# taking { heading:, body: } — e.g.
|
|
25
|
+
# { 'confirmed' => { heading: 'You're in',
|
|
26
|
+
# body: 'Thanks for confirming.' },
|
|
27
|
+
# 'expired' => { heading: 'Link expired', body: '...' } }
|
|
28
|
+
#
|
|
29
|
+
# Anything the server doesn't recognize comes back as an
|
|
30
|
+
# `unrecognized_parameter` warning on the response rather than an error.
|
|
14
31
|
def create(**attrs)
|
|
15
32
|
post('/api/v1/templates', { template: attrs })
|
|
16
33
|
end
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module Broadcast
|
|
4
4
|
module Resources
|
|
5
5
|
class Transactionals < Base
|
|
6
|
+
MAX_IDEMPOTENCY_KEY_LENGTH = 255
|
|
7
|
+
|
|
6
8
|
# Send a transactional email.
|
|
7
9
|
#
|
|
8
10
|
# Required:
|
|
@@ -20,11 +22,29 @@ module Broadcast
|
|
|
20
22
|
# Holds the email until the recipient confirms.
|
|
21
23
|
# confirmation_template_id: custom confirmation template (used with double_opt_in: true)
|
|
22
24
|
# subscriber: { first_name:, last_name: } — populates Subscriber on first send
|
|
25
|
+
# idempotency_key: see below
|
|
26
|
+
#
|
|
27
|
+
# Idempotency
|
|
28
|
+
# -----------
|
|
29
|
+
# Pass `idempotency_key:` to make a retry safe. The server stores the
|
|
30
|
+
# response for 24 hours keyed on (token, key) and replays it rather than
|
|
31
|
+
# sending a second email. Check `result.idempotent_replay?` to tell a
|
|
32
|
+
# replay from a fresh send.
|
|
33
|
+
#
|
|
34
|
+
# client.transactionals.create(to: 'a@b.com', subject: 'Receipt',
|
|
35
|
+
# body: '<p>Thanks</p>',
|
|
36
|
+
# idempotency_key: "receipt-#{order.id}")
|
|
37
|
+
#
|
|
38
|
+
# The key is part of a fingerprint over method + full path + body:
|
|
39
|
+
# - same key, same payload, still running -> Broadcast::ConflictError (409)
|
|
40
|
+
# - same key, *different* payload -> Broadcast::ValidationError (422)
|
|
41
|
+
# That 422 means "this key was already used for something else", not that
|
|
42
|
+
# the email was invalid — don't retry it with the same key.
|
|
23
43
|
# rubocop:disable Metrics/ParameterLists -- mirrors the API's flat param surface
|
|
24
44
|
def create(to:, subject: nil, body: nil, reply_to: nil, preheader: nil,
|
|
25
45
|
template_id: nil, include_unsubscribe_link: nil,
|
|
26
46
|
double_opt_in: nil, confirmation_template_id: nil,
|
|
27
|
-
subscriber: nil, **extra)
|
|
47
|
+
subscriber: nil, idempotency_key: nil, **extra)
|
|
28
48
|
# rubocop:enable Metrics/ParameterLists
|
|
29
49
|
payload = { to: to }
|
|
30
50
|
payload[:subject] = subject unless subject.nil?
|
|
@@ -38,12 +58,29 @@ module Broadcast
|
|
|
38
58
|
payload[:subscriber] = subscriber unless subscriber.nil?
|
|
39
59
|
payload.merge!(extra) unless extra.empty?
|
|
40
60
|
|
|
41
|
-
post
|
|
61
|
+
@client.request(:post, '/api/v1/transactionals.json', payload,
|
|
62
|
+
headers: idempotency_headers(idempotency_key))
|
|
42
63
|
end
|
|
43
64
|
|
|
44
65
|
def get_transactional(id)
|
|
45
66
|
get("/api/v1/transactionals/#{id}.json")
|
|
46
67
|
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def idempotency_headers(key)
|
|
72
|
+
return {} if key.nil?
|
|
73
|
+
|
|
74
|
+
key = key.to_s.strip
|
|
75
|
+
return {} if key.empty?
|
|
76
|
+
|
|
77
|
+
if key.length > MAX_IDEMPOTENCY_KEY_LENGTH
|
|
78
|
+
raise ArgumentError,
|
|
79
|
+
"idempotency_key must be #{MAX_IDEMPOTENCY_KEY_LENGTH} characters or fewer (got #{key.length})"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
{ 'Idempotency-Key' => key }
|
|
83
|
+
end
|
|
47
84
|
end
|
|
48
85
|
end
|
|
49
86
|
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'time'
|
|
4
|
+
|
|
5
|
+
module Broadcast
|
|
6
|
+
# A single entry from the API's `warnings` array (docs: api-response-warnings).
|
|
7
|
+
# The API raises these on successful 2xx responses when it accepted the request
|
|
8
|
+
# but ignored part of it — an unrecognized parameter, a parameter that only
|
|
9
|
+
# applies in another mode, a value the server overrode.
|
|
10
|
+
#
|
|
11
|
+
# `param` is a dot-path to the offending parameter (e.g. "subscriber.foo").
|
|
12
|
+
# The API never includes submitted values, so a warning is safe to log.
|
|
13
|
+
Warning = Struct.new(:code, :param, :message) do
|
|
14
|
+
def to_s
|
|
15
|
+
param ? "[#{code}] #{param}: #{message}" : "[#{code}] #{message}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Parsed X-RateLimit-* response headers. `reset` is the time the current
|
|
20
|
+
# window rolls over, not a duration.
|
|
21
|
+
RateLimit = Struct.new(:limit, :remaining, :reset)
|
|
22
|
+
|
|
23
|
+
# The value returned by every JSON API call.
|
|
24
|
+
#
|
|
25
|
+
# Response subclasses Hash rather than wrapping it, so everything that worked
|
|
26
|
+
# against the raw parsed body still works — `result['id']`, `result.is_a?(Hash)`,
|
|
27
|
+
# `result.dig(...)`, equality against a plain Hash. The response metadata the
|
|
28
|
+
# API sends alongside the body is exposed as extra readers.
|
|
29
|
+
#
|
|
30
|
+
# result = client.subscribers.create(email: 'user@example.com', foo: 'bar')
|
|
31
|
+
# result['id'] # => 42 (unchanged from v0.2)
|
|
32
|
+
# result.warnings # => [#<Warning code="unrecognized_parameter" ...>]
|
|
33
|
+
# result.rate_limit.remaining # => 118
|
|
34
|
+
# result.status # => 201
|
|
35
|
+
#
|
|
36
|
+
# Non-Hash JSON bodies (a bare array) are returned as-is and carry no metadata.
|
|
37
|
+
class Response < Hash
|
|
38
|
+
attr_reader :status, :headers
|
|
39
|
+
|
|
40
|
+
class << self
|
|
41
|
+
# Wraps a parsed JSON body when it is a Hash; passes anything else through.
|
|
42
|
+
def build(parsed, status:, headers: {})
|
|
43
|
+
return parsed unless parsed.is_a?(::Hash)
|
|
44
|
+
|
|
45
|
+
response = new
|
|
46
|
+
response.replace(parsed)
|
|
47
|
+
response.attach_metadata(status: status, headers: headers)
|
|
48
|
+
response
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @api private — set once by .build immediately after construction
|
|
53
|
+
def attach_metadata(status:, headers:)
|
|
54
|
+
@status = status
|
|
55
|
+
@headers = headers
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def warnings
|
|
60
|
+
@warnings ||= Array(self['warnings']).filter_map do |entry|
|
|
61
|
+
next unless entry.is_a?(::Hash)
|
|
62
|
+
|
|
63
|
+
Warning.new(code: entry['code'], param: entry['param'], message: entry['message'])
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def warnings?
|
|
68
|
+
!warnings.empty?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def rate_limit
|
|
72
|
+
return @rate_limit if defined?(@rate_limit)
|
|
73
|
+
|
|
74
|
+
limit = header('x-ratelimit-limit')
|
|
75
|
+
@rate_limit = if limit.nil?
|
|
76
|
+
nil
|
|
77
|
+
else
|
|
78
|
+
RateLimit.new(
|
|
79
|
+
limit: limit.to_i,
|
|
80
|
+
remaining: header('x-ratelimit-remaining')&.to_i,
|
|
81
|
+
reset: parse_time(header('x-ratelimit-reset'))
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# True when the API replayed a stored response for a repeated
|
|
87
|
+
# Idempotency-Key rather than performing the write again.
|
|
88
|
+
def idempotent_replay?
|
|
89
|
+
header('idempotency-replayed') == 'true'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def header(name)
|
|
95
|
+
(@headers || {})[name]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def parse_time(value)
|
|
99
|
+
value && Time.iso8601(value)
|
|
100
|
+
rescue ArgumentError
|
|
101
|
+
nil
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
data/lib/broadcast/version.rb
CHANGED
data/lib/broadcast/webhook.rb
CHANGED
|
@@ -7,6 +7,40 @@ module Broadcast
|
|
|
7
7
|
module Webhook
|
|
8
8
|
TIMESTAMP_TOLERANCE = 300 # 5 minutes
|
|
9
9
|
|
|
10
|
+
# Every event type a webhook endpoint can subscribe to, mirroring
|
|
11
|
+
# WebhookEndpoint::AVAILABLE_EVENT_TYPES server-side. Use these when
|
|
12
|
+
# creating an endpoint — an unknown event type is dropped silently.
|
|
13
|
+
EMAIL_EVENTS = %w[
|
|
14
|
+
email.sent email.delivered email.delivery_delayed email.complained
|
|
15
|
+
email.bounced email.opened email.clicked email.failed
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
SUBSCRIBER_EVENTS = %w[
|
|
19
|
+
subscriber.created subscriber.updated subscriber.deleted
|
|
20
|
+
subscriber.subscribed subscriber.unsubscribed subscriber.bounced
|
|
21
|
+
subscriber.complained
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
BROADCAST_EVENTS = %w[
|
|
25
|
+
broadcast.scheduled broadcast.queueing broadcast.sending broadcast.sent
|
|
26
|
+
broadcast.failed broadcast.partial_failure broadcast.aborted
|
|
27
|
+
broadcast.paused
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
SEQUENCE_EVENTS = %w[
|
|
31
|
+
sequence.subscriber_added sequence.subscriber_completed
|
|
32
|
+
sequence.subscriber_moved sequence.subscriber_removed
|
|
33
|
+
sequence.subscriber_paused sequence.subscriber_resumed
|
|
34
|
+
sequence.subscriber_error
|
|
35
|
+
].freeze
|
|
36
|
+
|
|
37
|
+
# Delivery-machinery events, not content events.
|
|
38
|
+
SYSTEM_EVENTS = %w[message.attempt.exhausted test.webhook].freeze
|
|
39
|
+
|
|
40
|
+
EVENT_TYPES = (
|
|
41
|
+
EMAIL_EVENTS + SUBSCRIBER_EVENTS + BROADCAST_EVENTS + SEQUENCE_EVENTS + SYSTEM_EVENTS
|
|
42
|
+
).freeze
|
|
43
|
+
|
|
10
44
|
module_function
|
|
11
45
|
|
|
12
46
|
def verify(payload, signature_header, timestamp_header, secret:, now: nil)
|
data/lib/broadcast.rb
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'broadcast/version'
|
|
4
4
|
require_relative 'broadcast/errors'
|
|
5
|
+
require_relative 'broadcast/response'
|
|
5
6
|
require_relative 'broadcast/configuration'
|
|
7
|
+
require_relative 'broadcast/debug_logger'
|
|
8
|
+
require_relative 'broadcast/connection'
|
|
6
9
|
require_relative 'broadcast/client'
|
|
7
10
|
require_relative 'broadcast/webhook'
|
|
8
11
|
require_relative 'broadcast/resources/base'
|
|
@@ -15,6 +18,9 @@ require_relative 'broadcast/resources/webhook_endpoints'
|
|
|
15
18
|
require_relative 'broadcast/resources/transactionals'
|
|
16
19
|
require_relative 'broadcast/resources/opt_in_forms'
|
|
17
20
|
require_relative 'broadcast/resources/email_servers'
|
|
21
|
+
require_relative 'broadcast/resources/autopilots'
|
|
22
|
+
require_relative 'broadcast/resources/discovery'
|
|
23
|
+
require_relative 'broadcast/resources/migration'
|
|
18
24
|
|
|
19
25
|
# ActionMailer integration — only loaded when Rails is present
|
|
20
26
|
if defined?(Rails::Railtie)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: broadcast-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon Chiu
|
|
@@ -31,23 +31,28 @@ executables: []
|
|
|
31
31
|
extensions: []
|
|
32
32
|
extra_rdoc_files: []
|
|
33
33
|
files:
|
|
34
|
-
- ".rubocop.yml"
|
|
35
34
|
- CHANGELOG.md
|
|
36
35
|
- Gemfile
|
|
37
36
|
- Gemfile.lock
|
|
38
37
|
- LICENSE.txt
|
|
39
38
|
- README.md
|
|
40
39
|
- Rakefile
|
|
40
|
+
- SDK-COVERAGE.md
|
|
41
41
|
- lib/broadcast-ruby.rb
|
|
42
42
|
- lib/broadcast.rb
|
|
43
43
|
- lib/broadcast/client.rb
|
|
44
44
|
- lib/broadcast/configuration.rb
|
|
45
|
+
- lib/broadcast/connection.rb
|
|
46
|
+
- lib/broadcast/debug_logger.rb
|
|
45
47
|
- lib/broadcast/delivery_method.rb
|
|
46
48
|
- lib/broadcast/errors.rb
|
|
47
49
|
- lib/broadcast/railtie.rb
|
|
50
|
+
- lib/broadcast/resources/autopilots.rb
|
|
48
51
|
- lib/broadcast/resources/base.rb
|
|
49
52
|
- lib/broadcast/resources/broadcasts.rb
|
|
53
|
+
- lib/broadcast/resources/discovery.rb
|
|
50
54
|
- lib/broadcast/resources/email_servers.rb
|
|
55
|
+
- lib/broadcast/resources/migration.rb
|
|
51
56
|
- lib/broadcast/resources/opt_in_forms.rb
|
|
52
57
|
- lib/broadcast/resources/segments.rb
|
|
53
58
|
- lib/broadcast/resources/sequences.rb
|
|
@@ -55,6 +60,7 @@ files:
|
|
|
55
60
|
- lib/broadcast/resources/templates.rb
|
|
56
61
|
- lib/broadcast/resources/transactionals.rb
|
|
57
62
|
- lib/broadcast/resources/webhook_endpoints.rb
|
|
63
|
+
- lib/broadcast/response.rb
|
|
58
64
|
- lib/broadcast/version.rb
|
|
59
65
|
- lib/broadcast/webhook.rb
|
|
60
66
|
homepage: https://github.com/send-broadcast/broadcast-ruby
|
|
@@ -80,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
80
86
|
- !ruby/object:Gem::Version
|
|
81
87
|
version: '0'
|
|
82
88
|
requirements: []
|
|
83
|
-
rubygems_version:
|
|
89
|
+
rubygems_version: 4.0.16
|
|
84
90
|
specification_version: 4
|
|
85
91
|
summary: Ruby client for the Broadcast email platform
|
|
86
92
|
test_files: []
|
data/.rubocop.yml
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
AllCops:
|
|
2
|
-
TargetRubyVersion: 3.2
|
|
3
|
-
NewCops: enable
|
|
4
|
-
SuggestExtensions: false
|
|
5
|
-
|
|
6
|
-
Style/StringLiterals:
|
|
7
|
-
EnforcedStyle: single_quotes
|
|
8
|
-
|
|
9
|
-
Style/StringLiteralsInInterpolation:
|
|
10
|
-
EnforcedStyle: single_quotes
|
|
11
|
-
|
|
12
|
-
Style/Documentation:
|
|
13
|
-
Enabled: false
|
|
14
|
-
|
|
15
|
-
Metrics/MethodLength:
|
|
16
|
-
Max: 25
|
|
17
|
-
Exclude:
|
|
18
|
-
- test/**/*
|
|
19
|
-
|
|
20
|
-
Metrics/AbcSize:
|
|
21
|
-
Max: 35
|
|
22
|
-
Exclude:
|
|
23
|
-
- test/**/*
|
|
24
|
-
|
|
25
|
-
Metrics/ClassLength:
|
|
26
|
-
Max: 200
|
|
27
|
-
Exclude:
|
|
28
|
-
- test/**/*
|
|
29
|
-
|
|
30
|
-
Metrics/CyclomaticComplexity:
|
|
31
|
-
Max: 15
|
|
32
|
-
|
|
33
|
-
Metrics/PerceivedComplexity:
|
|
34
|
-
Max: 15
|
|
35
|
-
|
|
36
|
-
Layout/LineLength:
|
|
37
|
-
Max: 120
|
|
38
|
-
|
|
39
|
-
Naming/FileName:
|
|
40
|
-
Exclude:
|
|
41
|
-
- lib/broadcast-ruby.rb
|
|
42
|
-
|
|
43
|
-
Naming/MethodParameterName:
|
|
44
|
-
AllowedNames:
|
|
45
|
-
- a
|
|
46
|
-
- b
|
|
47
|
-
- id
|
|
48
|
-
- k
|
|
49
|
-
- v
|
|
50
|
-
- to
|