hey-you 0.1.6
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 +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.rubocop.yml +0 -0
- data/.travis.yml +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +67 -0
- data/README.md +373 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/hey_you.gemspec +33 -0
- data/lib/hey-you.rb +8 -0
- data/lib/hey_you/builder/_base.rb +40 -0
- data/lib/hey_you/builder/email.rb +17 -0
- data/lib/hey_you/builder/push.rb +15 -0
- data/lib/hey_you/builder.rb +51 -0
- data/lib/hey_you/channels/_base.rb +42 -0
- data/lib/hey_you/channels/email.rb +87 -0
- data/lib/hey_you/channels/push.rb +38 -0
- data/lib/hey_you/config/conigurable.rb +24 -0
- data/lib/hey_you/config/email.rb +31 -0
- data/lib/hey_you/config/push.rb +29 -0
- data/lib/hey_you/config.rb +111 -0
- data/lib/hey_you/receiver.rb +89 -0
- data/lib/hey_you/sender.rb +81 -0
- data/lib/hey_you/version.rb +3 -0
- metadata +170 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require_relative '_base'
|
|
2
|
+
|
|
3
|
+
module HeyYou
|
|
4
|
+
class Builder
|
|
5
|
+
class Email < Base
|
|
6
|
+
attr_reader :subject, :body, :layout, :mailer_class, :mailer_method, :delivery_method
|
|
7
|
+
|
|
8
|
+
def build
|
|
9
|
+
@mailer_class = ch_data.fetch('mailer_class', nil)
|
|
10
|
+
@mailer_method = ch_data.fetch('mailer_method', nil)
|
|
11
|
+
@delivery_method = ch_data.fetch('delivery_method', nil)
|
|
12
|
+
@body = interpolate(ch_data.fetch('body'), options)
|
|
13
|
+
@subject = interpolate(ch_data.fetch('subject'), options)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require_relative '_base'
|
|
2
|
+
|
|
3
|
+
module HeyYou
|
|
4
|
+
class Builder
|
|
5
|
+
class Push < Base
|
|
6
|
+
attr_reader :body, :title, :data
|
|
7
|
+
|
|
8
|
+
def build
|
|
9
|
+
@title = interpolate(ch_data.fetch('title'), options)
|
|
10
|
+
@body = interpolate(ch_data.fetch('body'), options)
|
|
11
|
+
@data = options[:data] || {}
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'i18n'
|
|
2
|
+
require_relative 'builder/email'
|
|
3
|
+
require_relative 'builder/push'
|
|
4
|
+
|
|
5
|
+
module HeyYou
|
|
6
|
+
class Builder
|
|
7
|
+
attr_reader :data, :options
|
|
8
|
+
|
|
9
|
+
# Load data from collection yaml via key and interpolate variables.
|
|
10
|
+
# Define methods for each registered channel. After initialize you can use
|
|
11
|
+
# `instance.<ch_name>`. It will be return instance of HeyYou::Builder::<YOUR_CHANNEL_NAME>
|
|
12
|
+
#
|
|
13
|
+
def initialize(key, **options)
|
|
14
|
+
@data = fetch_from_collection_by_key(key, options[:locale])
|
|
15
|
+
@options = options
|
|
16
|
+
config.registered_channels.each do |ch|
|
|
17
|
+
ch_builder =
|
|
18
|
+
HeyYou::Builder.const_get("#{ch.downcase.capitalize}").new(data, key, options)
|
|
19
|
+
instance_variable_set("@#{ch}".to_sym, ch_builder)
|
|
20
|
+
|
|
21
|
+
define_ch_method(ch)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def define_ch_method(ch)
|
|
28
|
+
method_proc = -> { instance_variable_get("@#{ch}".to_sym) }
|
|
29
|
+
self.class.send(:define_method, ch, method_proc)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def fetch_from_collection_by_key(key, locale)
|
|
33
|
+
keys = []
|
|
34
|
+
if config.localization
|
|
35
|
+
locale = locale || I18n.locale
|
|
36
|
+
raise UnknownLocale, 'You should pass locale.' unless locale
|
|
37
|
+
keys << locale
|
|
38
|
+
end
|
|
39
|
+
keys = keys + key.to_s.split(config.splitter)
|
|
40
|
+
keys.reduce(config.collection) do |memo, nested_key|
|
|
41
|
+
memo[nested_key.to_s] if memo
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def config
|
|
46
|
+
Config.config
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class UnknownLocale < StandardError; end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module HeyYou
|
|
2
|
+
module Channels
|
|
3
|
+
class Base
|
|
4
|
+
class << self
|
|
5
|
+
def send!
|
|
6
|
+
raise NotImplementedError, 'You should define #send! method in your channel.'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def credentials_present?
|
|
12
|
+
required_credentials.all? do |cred|
|
|
13
|
+
if config.send(self.name.split('::').last.downcase).send(cred).nil?
|
|
14
|
+
config.logger&.info(
|
|
15
|
+
"[WARN] required credential was not set. " +
|
|
16
|
+
"Set `config.#{self.name.downcase}.#{cred}` to send notification with #{self.name.downcase}"
|
|
17
|
+
)
|
|
18
|
+
return false
|
|
19
|
+
end
|
|
20
|
+
true
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def required_credentials
|
|
25
|
+
[]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def config
|
|
29
|
+
Config.config
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ch_name
|
|
33
|
+
self.class.name.split('::').last.downcase
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def log(msg)
|
|
37
|
+
config.log("[#{ch_name.upcase}] #{msg}")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require_relative '_base'
|
|
2
|
+
require 'mail'
|
|
3
|
+
|
|
4
|
+
module HeyYou
|
|
5
|
+
module Channels
|
|
6
|
+
class Email < Base
|
|
7
|
+
class << self
|
|
8
|
+
def send!(builder, to:, **options)
|
|
9
|
+
raise CredentialsNotExists unless credentials_present?
|
|
10
|
+
|
|
11
|
+
method = config.email.default_mailing ? :send_via_mail : :send_via_custom_class
|
|
12
|
+
public_send(method, builder, to, options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Send email via custom class instance.
|
|
16
|
+
def send_via_custom_class(builder, to, **options)
|
|
17
|
+
mailer = mailer_class_from_builder(builder, options)
|
|
18
|
+
|
|
19
|
+
mailer_method = options[:mailer_method] ||
|
|
20
|
+
builder.email.mailer_method ||
|
|
21
|
+
config.email.default_mailer_method
|
|
22
|
+
|
|
23
|
+
delivery_method = options[:delivery_method] ||
|
|
24
|
+
builder.email.delivery_method ||
|
|
25
|
+
config.email.default_delivery_method
|
|
26
|
+
|
|
27
|
+
log("Build mail via #{mailer}##{mailer_method}. Delivery with #{delivery_method}")
|
|
28
|
+
mailer_msg = mailer.public_send(mailer_method, data: builder.email, to: to)
|
|
29
|
+
mailer_msg.public_send(delivery_method)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Send email with standard mail (gem 'mail')
|
|
33
|
+
def send_via_mail(builder, to, **_)
|
|
34
|
+
context = self
|
|
35
|
+
mail = Mail.new do
|
|
36
|
+
from HeyYou::Config.instance.email.from
|
|
37
|
+
to to
|
|
38
|
+
subject builder.email.subject
|
|
39
|
+
body context.get_body(builder.email.body)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
mail.delivery_method config.email.mail_delivery_method
|
|
43
|
+
log("Send mail #{mail}")
|
|
44
|
+
mail.deliver
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def get_body(body_text)
|
|
48
|
+
# TODO: Load layout from config here.
|
|
49
|
+
body_text
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def required_credentials
|
|
53
|
+
%i[from]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def mailer_class_from_builder(builder, **options)
|
|
59
|
+
mailer_class = options[:mailer_class] ||
|
|
60
|
+
builder.email.mailer_class ||
|
|
61
|
+
config.email.default_mailer_class
|
|
62
|
+
unless mailer_class
|
|
63
|
+
raise(
|
|
64
|
+
MailerClassNotDefined,
|
|
65
|
+
'You must set mailer_class in notifications collection or pass :mailer_class option'
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
begin
|
|
70
|
+
mailer = Object.const_get(mailer_class.to_s)
|
|
71
|
+
rescue NameError
|
|
72
|
+
raise ActionMailerClassNotDefined, "Mailer #{mailer_class} not initialized"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
mailer
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class CredentialsNotExists < StandardError;
|
|
80
|
+
end
|
|
81
|
+
class ActionMailerClassNotDefined < StandardError;
|
|
82
|
+
end
|
|
83
|
+
class MailerClassNotDefined < StandardError;
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require_relative '_base'
|
|
2
|
+
|
|
3
|
+
module HeyYou
|
|
4
|
+
module Channels
|
|
5
|
+
class Push < Base
|
|
6
|
+
class << self
|
|
7
|
+
def send!(builder, to:, **options)
|
|
8
|
+
options = build_options(builder)
|
|
9
|
+
config.logger&.info("[PUSH] Send #{options} body for #{ids(to)}")
|
|
10
|
+
config.push.fcm_client.send(ids(to), options)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def build_options(builder)
|
|
16
|
+
{
|
|
17
|
+
data: builder.push.data,
|
|
18
|
+
notification: {
|
|
19
|
+
title: builder.push.title,
|
|
20
|
+
body: builder.push.body
|
|
21
|
+
},
|
|
22
|
+
priority: builder.options[:priority] || config.push.priority,
|
|
23
|
+
time_to_live: config.push.ttl
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def ids(to)
|
|
28
|
+
return to if to.is_a?(Array)
|
|
29
|
+
[to]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def required_credentials
|
|
33
|
+
%i[fcm_token]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'singleton'
|
|
2
|
+
|
|
3
|
+
module HeyYou
|
|
4
|
+
class Config
|
|
5
|
+
module Configurable
|
|
6
|
+
|
|
7
|
+
def self.extended klass
|
|
8
|
+
klass.class_eval do
|
|
9
|
+
include Singleton
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def configure(&block)
|
|
14
|
+
# TODO: log warn "Already configured" instead nil
|
|
15
|
+
@configured ? nil : instance_eval(&block)
|
|
16
|
+
@configured = true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def config
|
|
20
|
+
@config ||= self.instance
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require_relative 'conigurable'
|
|
2
|
+
require 'mail'
|
|
3
|
+
|
|
4
|
+
module HeyYou
|
|
5
|
+
class Config
|
|
6
|
+
class Email
|
|
7
|
+
extend Configurable
|
|
8
|
+
|
|
9
|
+
MAIL_DELIVERY_METHOD = :sendmail
|
|
10
|
+
DEFAULT_ACTION_MAILER_METHOD = :send!
|
|
11
|
+
DEFAULT_DELIVERY_METHOD = :deliver_now
|
|
12
|
+
|
|
13
|
+
attr_accessor(
|
|
14
|
+
:from,
|
|
15
|
+
:mail_delivery_method,
|
|
16
|
+
:default_mailing,
|
|
17
|
+
:default_delivery_method,
|
|
18
|
+
:default_mailer_class,
|
|
19
|
+
:default_mailer_method
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def initialize
|
|
23
|
+
@mail_delivery_method ||= MAIL_DELIVERY_METHOD
|
|
24
|
+
@default_mailing ||= !default_mailer_class.nil?
|
|
25
|
+
@async ||= true
|
|
26
|
+
@default_mailer_method ||= DEFAULT_ACTION_MAILER_METHOD
|
|
27
|
+
@default_delivery_method ||= DEFAULT_DELIVERY_METHOD
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require_relative 'conigurable'
|
|
2
|
+
require 'fcm'
|
|
3
|
+
|
|
4
|
+
module HeyYou
|
|
5
|
+
class Config
|
|
6
|
+
class Push
|
|
7
|
+
extend Configurable
|
|
8
|
+
|
|
9
|
+
DEFAULT_PRIORITY = 'high'
|
|
10
|
+
DEFAULT_TTL = 60
|
|
11
|
+
DEFAULT_FCM_TIMEOUT = 30
|
|
12
|
+
|
|
13
|
+
attr_accessor :fcm_token, :priority, :ttl, :fcm_timeout
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
@priority = DEFAULT_PRIORITY
|
|
17
|
+
@ttl = DEFAULT_TTL
|
|
18
|
+
@fcm_timeout = DEFAULT_FCM_TIMEOUT
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def fcm_client
|
|
22
|
+
raise FcmTokenNotExists, 'Can\'t create fcm client: fcm_token not exists' unless fcm_token
|
|
23
|
+
@fcm_client ||= FCM.new(fcm_token, timeout: fcm_timeout)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class FcmTokenNotExists < StandardError; end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require_relative 'config/conigurable'
|
|
3
|
+
require_relative 'config/push'
|
|
4
|
+
require_relative 'config/email'
|
|
5
|
+
|
|
6
|
+
#
|
|
7
|
+
# @config REQUIRED collection_file [String] - File path for general notifications file
|
|
8
|
+
# @config OPTIONAL env_collection_file [String] - File path for environment notifications file
|
|
9
|
+
# @config OPTIONAL splitter [String] - Chars for split notifications keys in builder.
|
|
10
|
+
# For example:
|
|
11
|
+
# if splitter eq `.` you can pass to notification builder 'key_1.nested_key'
|
|
12
|
+
# if splitter eq `/` you can pass to notification builder 'key_1/nested_key'
|
|
13
|
+
# @config OPTIONAL registered_channels - Channels available for service. If your application
|
|
14
|
+
# planning use only push and email just set it as [:push, :email]. Default all channels
|
|
15
|
+
# will available.
|
|
16
|
+
module HeyYou
|
|
17
|
+
class Config
|
|
18
|
+
extend Configurable
|
|
19
|
+
|
|
20
|
+
DEFAULT_REGISTERED_CHANNELS = %i[email push]
|
|
21
|
+
DEFAULT_SPLITTER = '.'
|
|
22
|
+
DEFAULT_GLOBAL_LOG_TAG = 'HeyYou'
|
|
23
|
+
DEFAULT_LOCALIZATION_FLAG = false
|
|
24
|
+
|
|
25
|
+
class CollectionFileNotDefined < StandardError; end
|
|
26
|
+
|
|
27
|
+
attr_reader :collection, :env_collection, :configured, :registered_receivers
|
|
28
|
+
attr_accessor(
|
|
29
|
+
:collection_files, :env_collection_file, :splitter,
|
|
30
|
+
:registered_channels, :localization, :logger, :log_tag
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def initialize
|
|
34
|
+
@registered_channels ||= DEFAULT_REGISTERED_CHANNELS
|
|
35
|
+
@splitter ||= DEFAULT_SPLITTER
|
|
36
|
+
@registered_receivers = []
|
|
37
|
+
@log_tag ||= DEFAULT_GLOBAL_LOG_TAG
|
|
38
|
+
@localization ||= DEFAULT_LOCALIZATION_FLAG
|
|
39
|
+
define_ch_config_methods
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def collection_file
|
|
43
|
+
@collection_files || raise(
|
|
44
|
+
CollectionFileNotDefined,
|
|
45
|
+
'You must define HeyYou::Config.collection_files'
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def collection
|
|
50
|
+
@collection ||= load_collection
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def env_collection
|
|
54
|
+
@env_collection ||= load_env_collection
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def registrate_receiver(receiver_class)
|
|
58
|
+
log("#{receiver_class} registrated as receiver")
|
|
59
|
+
@registered_receivers << receiver_class
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Registrate new custom channel.
|
|
63
|
+
# For successful registration, in application must be exists:
|
|
64
|
+
# 1. HeyYou::Channels::<YOUR_CHANNEL_NAME> < HeyYou::Channels::Base
|
|
65
|
+
# 2. HeyYou::Builder::<YOUR_CHANNEL_NAME> < HeyYou::Builder::Base
|
|
66
|
+
# 3. HeyYou::Config::<YOUR_CHANNEL_NAME> extended HeyYou::Config::Configurable
|
|
67
|
+
#
|
|
68
|
+
def registrate_channel(ch)
|
|
69
|
+
registered_channels << ch.to_sym
|
|
70
|
+
define_ch_config_method(ch)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def log(msg)
|
|
74
|
+
logger&.info("[#{log_tag}] #{msg} ")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def define_ch_config_methods
|
|
80
|
+
registered_channels.each do |ch|
|
|
81
|
+
define_ch_config_method(ch)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Define method for fetch config of channel.
|
|
86
|
+
#
|
|
87
|
+
# For example, if ch == 'push' will define method #push for class instance.
|
|
88
|
+
# New method will return instance of channel config instance
|
|
89
|
+
def define_ch_config_method(ch)
|
|
90
|
+
method_proc = -> { self.class.const_get(ch.capitalize).config }
|
|
91
|
+
self.class.send(:define_method, ch, method_proc)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Load yaml from collection_file and merge it with yaml from env_collection_file
|
|
95
|
+
def load_collection
|
|
96
|
+
@collection_files = [collection_files] if collection_files.is_a?(String)
|
|
97
|
+
notification_collection = {}
|
|
98
|
+
collection_files.each do |file|
|
|
99
|
+
notification_collection.merge!(YAML.load_file(file))
|
|
100
|
+
end
|
|
101
|
+
notification_collection.merge!(env_collection)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def load_env_collection
|
|
105
|
+
if env_collection_file
|
|
106
|
+
return YAML.load_file(env_collection_file) rescue { }
|
|
107
|
+
end
|
|
108
|
+
{}
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require_relative 'sender'
|
|
2
|
+
|
|
3
|
+
module HeyYou
|
|
4
|
+
module Receiver
|
|
5
|
+
attr_reader :receiver_channels, :receiver_data
|
|
6
|
+
|
|
7
|
+
def self.extended klass
|
|
8
|
+
klass.class_eval do
|
|
9
|
+
# Method will be injected for instances methods of class.
|
|
10
|
+
#
|
|
11
|
+
# This can use something like: user.send_notification('key', options).
|
|
12
|
+
def send_notification(notification_key, **options)
|
|
13
|
+
Sender.send_to(self, notification_key, options)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Registrate class as receiver.
|
|
19
|
+
# In parameters pass hash where keys - channels names, and values -
|
|
20
|
+
# procs with values for receive.
|
|
21
|
+
#
|
|
22
|
+
# For instances of classes will be created methods `#{channel_name}_ch_receive_info`
|
|
23
|
+
# after registrate your class as receiver.
|
|
24
|
+
#
|
|
25
|
+
# Example:
|
|
26
|
+
#
|
|
27
|
+
# class User
|
|
28
|
+
# extend HeyYou::Receiver
|
|
29
|
+
#
|
|
30
|
+
# receive(
|
|
31
|
+
# push: -> { push_tokens.value },
|
|
32
|
+
# email: -> { priority_email }
|
|
33
|
+
# )
|
|
34
|
+
#
|
|
35
|
+
# ## Or you can use receive with options:
|
|
36
|
+
# receive(
|
|
37
|
+
# email: { subject: -> { priority_email }, options: { mailer_class: UserMailer, mailer_method: :notify } }
|
|
38
|
+
# )
|
|
39
|
+
#
|
|
40
|
+
# ...
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# user = User.new( push_tokens: PushToken.new(value: "456"), priority_email: 'example@mail.com')
|
|
44
|
+
# user.push_ch_receive_info # => 456
|
|
45
|
+
#
|
|
46
|
+
def receive(receiver_data)
|
|
47
|
+
check_channels(receiver_data.keys)
|
|
48
|
+
|
|
49
|
+
@receiver_data = receiver_data
|
|
50
|
+
@receiver_channels = receiver_data.keys
|
|
51
|
+
hey_you_config.registrate_receiver(self)
|
|
52
|
+
|
|
53
|
+
define_receive_info_methods
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def check_channels(channels)
|
|
59
|
+
channels.all? do |ch|
|
|
60
|
+
next if hey_you_config.registered_channels.include?(ch.to_sym)
|
|
61
|
+
raise(
|
|
62
|
+
NotRegisteredChannel,
|
|
63
|
+
"Channel #{ch} not registered. Registered channels: #{hey_you_config.registered_channels}"
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
@received_channels = channels
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def define_receive_info_methods
|
|
70
|
+
receiver_channels.each do |ch|
|
|
71
|
+
if receiver_data[ch].is_a?(Hash)
|
|
72
|
+
me = self
|
|
73
|
+
self.send(:define_method, "#{ch}_ch_receive_info", receiver_data[ch].fetch(:subject))
|
|
74
|
+
self.send(:define_method, "#{ch}_ch_receive_options", -> { me.receiver_data[ch].fetch(:options, {}) })
|
|
75
|
+
else
|
|
76
|
+
self.send(:define_method, "#{ch}_ch_receive_info", receiver_data[ch])
|
|
77
|
+
self.send(:define_method, "#{ch}_ch_receive_options", -> { {} })
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def hey_you_config
|
|
83
|
+
Config.config
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class NotRegisteredChannel < StandardError;
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require_relative 'builder'
|
|
2
|
+
|
|
3
|
+
require_relative 'channels/push'
|
|
4
|
+
require_relative 'channels/email'
|
|
5
|
+
|
|
6
|
+
module HeyYou
|
|
7
|
+
class Sender
|
|
8
|
+
class << self
|
|
9
|
+
# Send notifications for receiver
|
|
10
|
+
#
|
|
11
|
+
# @input receiver [Instance of receiver class] - registrated receiver
|
|
12
|
+
# @input notification_key [String] - key for notification builder
|
|
13
|
+
# @input options [Hash]
|
|
14
|
+
# @option only [String/Array[String]] - whitelist for using channels
|
|
15
|
+
#
|
|
16
|
+
def send_to(receiver, notification_key, **options)
|
|
17
|
+
unless receiver_valid?(receiver)
|
|
18
|
+
raise NotRegisteredReceiver, "Class '#{receiver.class}' not registered as receiver"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
result = send!(notification_key, receiver, **options)
|
|
22
|
+
config.log("Sender result: #{result}")
|
|
23
|
+
result
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def send!(notification_key, receiver, **options)
|
|
27
|
+
to_hash = {}
|
|
28
|
+
receiver.class.receiver_channels.each do |ch|
|
|
29
|
+
to_hash[ch] = {
|
|
30
|
+
# Fetch receiver's info for sending: phone_number, email, etc
|
|
31
|
+
subject: receiver.public_send("#{ch}_ch_receive_info"),
|
|
32
|
+
# Fetch receiver's options like :mailer_class
|
|
33
|
+
options: receiver.public_send("#{ch}_ch_receive_options") || {}
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
builder = Builder.new(notification_key, options)
|
|
38
|
+
response = {}
|
|
39
|
+
config.registered_channels.each do |ch|
|
|
40
|
+
if channel_allowed?(ch, to_hash, builder, options)
|
|
41
|
+
config.log(
|
|
42
|
+
"Send #{ch} to #{to_hash[ch][:subject]} with data: #{builder.send(ch).data}" \
|
|
43
|
+
" and options: #{to_hash[ch][:options]}"
|
|
44
|
+
)
|
|
45
|
+
response[ch] = Channels.const_get(ch.to_s.capitalize).send!(
|
|
46
|
+
builder, to: to_hash[ch][:subject], **to_hash[ch][:options]
|
|
47
|
+
)
|
|
48
|
+
else
|
|
49
|
+
config.log("Channel #{ch} not allowed.")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
response
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def channel_allowed?(ch, to, builder, **options)
|
|
58
|
+
unless to[ch].is_a?(Hash) ? to[ch.to_sym][:subject] || to[ch.to_s][:subject] : to[ch.to_sym] || to[ch.to_s]
|
|
59
|
+
return false
|
|
60
|
+
end
|
|
61
|
+
channel_allowed_by_only?(ch, options[:only]) && !builder.send(ch).nil?
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def receiver_valid?(receiver)
|
|
65
|
+
config.registered_receivers.include?(receiver.class)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def channel_allowed_by_only?(ch, only)
|
|
69
|
+
return true unless only
|
|
70
|
+
return only.map(&:to_sym).include?(ch.to_sym) if only.is_a?(Array)
|
|
71
|
+
only.to_sym == ch.to_sym
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def config
|
|
75
|
+
Config.config
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class NotRegisteredReceiver < StandardError; end
|
|
80
|
+
end
|
|
81
|
+
end
|