xip 0.0.1 → 2.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +116 -0
- data/.gitignore +12 -0
- data/CHANGELOG.md +135 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +65 -15
- data/LICENSE +6 -4
- data/README.md +51 -1
- data/VERSION +1 -0
- data/bin/xip +3 -11
- data/lib/xip.rb +1 -3
- data/lib/xip/base.rb +189 -0
- data/lib/xip/cli.rb +273 -0
- data/lib/xip/cli_base.rb +24 -0
- data/lib/xip/commands/command.rb +13 -0
- data/lib/xip/commands/console.rb +74 -0
- data/lib/xip/commands/server.rb +63 -0
- data/lib/xip/configuration.rb +56 -0
- data/lib/xip/controller/callbacks.rb +63 -0
- data/lib/xip/controller/catch_all.rb +84 -0
- data/lib/xip/controller/controller.rb +274 -0
- data/lib/xip/controller/dev_jumps.rb +40 -0
- data/lib/xip/controller/dynamic_delay.rb +61 -0
- data/lib/xip/controller/helpers.rb +128 -0
- data/lib/xip/controller/interrupt_detect.rb +99 -0
- data/lib/xip/controller/messages.rb +283 -0
- data/lib/xip/controller/nlp.rb +49 -0
- data/lib/xip/controller/replies.rb +281 -0
- data/lib/xip/controller/unrecognized_message.rb +61 -0
- data/lib/xip/core_ext.rb +5 -0
- data/lib/xip/core_ext/numeric.rb +10 -0
- data/lib/xip/core_ext/string.rb +18 -0
- data/lib/xip/dispatcher.rb +68 -0
- data/lib/xip/errors.rb +55 -0
- data/lib/xip/flow/base.rb +69 -0
- data/lib/xip/flow/specification.rb +56 -0
- data/lib/xip/flow/state.rb +82 -0
- data/lib/xip/generators/builder.rb +41 -0
- data/lib/xip/generators/builder/.gitignore +30 -0
- data/lib/xip/generators/builder/Gemfile +19 -0
- data/lib/xip/generators/builder/Procfile.dev +2 -0
- data/lib/xip/generators/builder/README.md +9 -0
- data/lib/xip/generators/builder/Rakefile +2 -0
- data/lib/xip/generators/builder/bot/controllers/bot_controller.rb +55 -0
- data/lib/xip/generators/builder/bot/controllers/catch_alls_controller.rb +21 -0
- data/lib/xip/generators/builder/bot/controllers/concerns/.keep +0 -0
- data/lib/xip/generators/builder/bot/controllers/goodbyes_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/controllers/hellos_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/controllers/interrupts_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/controllers/unrecognized_messages_controller.rb +9 -0
- data/lib/xip/generators/builder/bot/helpers/bot_helper.rb +2 -0
- data/lib/xip/generators/builder/bot/models/bot_record.rb +3 -0
- data/lib/xip/generators/builder/bot/models/concerns/.keep +0 -0
- data/lib/xip/generators/builder/bot/replies/catch_alls/level1.yml +2 -0
- data/lib/xip/generators/builder/bot/replies/goodbyes/say_goodbye.yml +2 -0
- data/lib/xip/generators/builder/bot/replies/hellos/say_hello.yml +2 -0
- data/lib/xip/generators/builder/config.ru +4 -0
- data/lib/xip/generators/builder/config/boot.rb +6 -0
- data/lib/xip/generators/builder/config/database.yml +25 -0
- data/lib/xip/generators/builder/config/environment.rb +2 -0
- data/lib/xip/generators/builder/config/flow_map.rb +25 -0
- data/lib/xip/generators/builder/config/initializers/autoload.rb +8 -0
- data/lib/xip/generators/builder/config/initializers/inflections.rb +16 -0
- data/lib/xip/generators/builder/config/puma.rb +25 -0
- data/lib/xip/generators/builder/config/services.yml +35 -0
- data/lib/xip/generators/builder/config/sidekiq.yml +3 -0
- data/lib/xip/generators/builder/db/seeds.rb +7 -0
- data/lib/xip/generators/generate.rb +39 -0
- data/lib/xip/generators/generate/flow/controllers/controller.tt +7 -0
- data/lib/xip/generators/generate/flow/helpers/helper.tt +3 -0
- data/lib/xip/generators/generate/flow/replies/ask_example.tt +9 -0
- data/lib/xip/helpers/redis.rb +40 -0
- data/lib/xip/jobs.rb +9 -0
- data/lib/xip/lock.rb +82 -0
- data/lib/xip/logger.rb +9 -3
- data/lib/xip/migrations/configurator.rb +73 -0
- data/lib/xip/migrations/generators.rb +16 -0
- data/lib/xip/migrations/railtie_config.rb +14 -0
- data/lib/xip/migrations/tasks.rb +43 -0
- data/lib/xip/nlp/client.rb +21 -0
- data/lib/xip/nlp/result.rb +56 -0
- data/lib/xip/reloader.rb +89 -0
- data/lib/xip/reply.rb +36 -0
- data/lib/xip/scheduled_reply.rb +18 -0
- data/lib/xip/server.rb +63 -0
- data/lib/xip/service_message.rb +17 -0
- data/lib/xip/service_reply.rb +44 -0
- data/lib/xip/services/base_client.rb +24 -0
- data/lib/xip/services/base_message_handler.rb +27 -0
- data/lib/xip/services/base_reply_handler.rb +72 -0
- data/lib/xip/services/jobs/handle_message_job.rb +21 -0
- data/lib/xip/session.rb +203 -0
- data/lib/xip/version.rb +7 -1
- data/logo.svg +17 -0
- data/spec/configuration_spec.rb +93 -0
- data/spec/controller/callbacks_spec.rb +217 -0
- data/spec/controller/catch_all_spec.rb +154 -0
- data/spec/controller/controller_spec.rb +889 -0
- data/spec/controller/dynamic_delay_spec.rb +70 -0
- data/spec/controller/helpers_spec.rb +119 -0
- data/spec/controller/interrupt_detect_spec.rb +171 -0
- data/spec/controller/messages_spec.rb +744 -0
- data/spec/controller/nlp_spec.rb +93 -0
- data/spec/controller/replies_spec.rb +694 -0
- data/spec/controller/unrecognized_message_spec.rb +168 -0
- data/spec/dispatcher_spec.rb +79 -0
- data/spec/flow/flow_spec.rb +82 -0
- data/spec/flow/state_spec.rb +109 -0
- data/spec/helpers/redis_spec.rb +77 -0
- data/spec/lock_spec.rb +100 -0
- data/spec/nlp/client_spec.rb +23 -0
- data/spec/nlp/result_spec.rb +57 -0
- data/spec/replies/hello.yml.erb +15 -0
- data/spec/replies/messages/say_hola.yml+facebook.erb +6 -0
- data/spec/replies/messages/say_hola.yml+twilio.erb +6 -0
- data/spec/replies/messages/say_hola.yml.erb +6 -0
- data/spec/replies/messages/say_howdy_with_dynamic.yml +79 -0
- data/spec/replies/messages/say_msgs_without_breaks.yml +4 -0
- data/spec/replies/messages/say_offer.yml +6 -0
- data/spec/replies/messages/say_offer_with_dynamic.yml +6 -0
- data/spec/replies/messages/say_oi.yml.erb +15 -0
- data/spec/replies/messages/say_randomize_speech.yml +10 -0
- data/spec/replies/messages/say_randomize_text.yml +10 -0
- data/spec/replies/messages/say_yo.yml +6 -0
- data/spec/replies/messages/say_yo.yml+twitter +6 -0
- data/spec/replies/messages/sub1/sub2/say_nested.yml +10 -0
- data/spec/reply_spec.rb +61 -0
- data/spec/scheduled_reply_spec.rb +23 -0
- data/spec/service_reply_spec.rb +92 -0
- data/spec/session_spec.rb +366 -0
- data/spec/spec_helper.rb +22 -66
- data/spec/support/alternate_helpers/foo_helper.rb +5 -0
- data/spec/support/controllers/vaders_controller.rb +24 -0
- data/spec/support/helpers/fun/games_helper.rb +7 -0
- data/spec/support/helpers/fun/pdf_helper.rb +7 -0
- data/spec/support/helpers/standalone_helper.rb +5 -0
- data/spec/support/helpers_typo/users_helper.rb +2 -0
- data/spec/support/nlp_clients/dialogflow.rb +9 -0
- data/spec/support/nlp_clients/luis.rb +9 -0
- data/spec/support/nlp_results/luis_result.rb +163 -0
- data/spec/support/sample_messages.rb +66 -0
- data/spec/support/services.yml +31 -0
- data/spec/support/services_with_erb.yml +31 -0
- data/spec/version_spec.rb +16 -0
- data/xip.gemspec +25 -14
- metadata +320 -18
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Xip
|
4
|
+
class ServiceReply
|
5
|
+
|
6
|
+
attr_accessor :recipient_id, :replies, :yaml_reply, :context
|
7
|
+
|
8
|
+
def initialize(recipient_id:, yaml_reply:, context:, preprocessor: :none)
|
9
|
+
@recipient_id = recipient_id
|
10
|
+
@yaml_reply = yaml_reply
|
11
|
+
@context = context
|
12
|
+
|
13
|
+
processed_reply = case preprocessor
|
14
|
+
when :erb
|
15
|
+
preprocess_erb
|
16
|
+
when :none
|
17
|
+
@yaml_reply
|
18
|
+
end
|
19
|
+
|
20
|
+
if yaml_reply.is_a?(Array)
|
21
|
+
@replies = load_replies(@yaml_reply)
|
22
|
+
else
|
23
|
+
@replies = load_replies(YAML.load(processed_reply))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def load_replies(unstructured_replies)
|
30
|
+
unstructured_replies.collect do |reply|
|
31
|
+
Xip::Reply.new(unstructured_reply: reply)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def preprocess_erb
|
36
|
+
begin
|
37
|
+
ERB.new(yaml_reply).result(context)
|
38
|
+
rescue NameError => e
|
39
|
+
raise(Xip::Errors::UndefinedVariable, e.message)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'xip/services/base_reply_handler'
|
4
|
+
require 'xip/services/base_message_handler'
|
5
|
+
|
6
|
+
require 'xip/services/jobs/handle_message_job'
|
7
|
+
|
8
|
+
module Xip
|
9
|
+
module Services
|
10
|
+
class BaseClient
|
11
|
+
|
12
|
+
attr_reader :reply
|
13
|
+
|
14
|
+
def initialize(reply:)
|
15
|
+
@reply = reply
|
16
|
+
end
|
17
|
+
|
18
|
+
def transmit
|
19
|
+
raise(Xip::Errors::ServiceImpaired, "Service implementation does not implement 'transmit'")
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Xip
|
4
|
+
module Services
|
5
|
+
class BaseMessageHandler
|
6
|
+
|
7
|
+
attr_reader :params, :headers
|
8
|
+
|
9
|
+
def initialize(params:, headers:)
|
10
|
+
@params = params
|
11
|
+
@headers = headers
|
12
|
+
end
|
13
|
+
|
14
|
+
# Should respond with a Rack response (https://github.com/sinatra/sinatra#return-values)
|
15
|
+
def coordinate
|
16
|
+
raise(Xip::Errors::ServiceImpaired, "Service request handler does not implement 'process'")
|
17
|
+
end
|
18
|
+
|
19
|
+
# After coordinate responds to the service, an optional async job
|
20
|
+
# may be fired that will continue the work via this method
|
21
|
+
def process
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Xip
|
4
|
+
module Services
|
5
|
+
class BaseReplyHandler
|
6
|
+
|
7
|
+
attr_reader :recipient_id, :reply
|
8
|
+
|
9
|
+
def initialize(recipient_id:, reply:)
|
10
|
+
@recipient_id = recipient_id
|
11
|
+
@reply = reply
|
12
|
+
end
|
13
|
+
|
14
|
+
def text
|
15
|
+
reply_format_not_supported(format: 'text')
|
16
|
+
end
|
17
|
+
|
18
|
+
def image
|
19
|
+
reply_format_not_supported(format: 'image')
|
20
|
+
end
|
21
|
+
|
22
|
+
def audio
|
23
|
+
reply_format_not_supported(format: 'audio')
|
24
|
+
end
|
25
|
+
|
26
|
+
def video
|
27
|
+
reply_format_not_supported(format: 'video')
|
28
|
+
end
|
29
|
+
|
30
|
+
def file
|
31
|
+
reply_format_not_supported(format: 'file')
|
32
|
+
end
|
33
|
+
|
34
|
+
def cards
|
35
|
+
reply_format_not_supported(format: 'cards')
|
36
|
+
end
|
37
|
+
|
38
|
+
def list
|
39
|
+
reply_format_not_supported(format: 'list')
|
40
|
+
end
|
41
|
+
|
42
|
+
def receipt
|
43
|
+
reply_format_not_supported(format: 'receipt')
|
44
|
+
end
|
45
|
+
|
46
|
+
def mark_seen
|
47
|
+
reply_format_not_supported(format: 'mark_seen')
|
48
|
+
end
|
49
|
+
|
50
|
+
def enable_typing_indicator
|
51
|
+
reply_format_not_supported(format: 'enable_typing_indicator')
|
52
|
+
end
|
53
|
+
|
54
|
+
def disable_typing_indicator
|
55
|
+
reply_format_not_supported(format: 'disable_typing_indicator')
|
56
|
+
end
|
57
|
+
|
58
|
+
def delay
|
59
|
+
reply_format_not_supported(format: 'delay')
|
60
|
+
end
|
61
|
+
|
62
|
+
def speech
|
63
|
+
reply_format_not_supported(format: 'speech')
|
64
|
+
end
|
65
|
+
|
66
|
+
def ssml
|
67
|
+
reply_format_not_supported(format: 'ssml')
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Xip
|
4
|
+
module Services
|
5
|
+
|
6
|
+
class HandleMessageJob < Xip::Jobs
|
7
|
+
sidekiq_options queue: :xip_webhooks, retry: false
|
8
|
+
|
9
|
+
def perform(service, params, headers)
|
10
|
+
dispatcher = Xip::Dispatcher.new(
|
11
|
+
service: service,
|
12
|
+
params: params,
|
13
|
+
headers: headers
|
14
|
+
)
|
15
|
+
|
16
|
+
dispatcher.process
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/xip/session.rb
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Xip
|
4
|
+
class Session
|
5
|
+
|
6
|
+
include Xip::Redis
|
7
|
+
|
8
|
+
SLUG_SEPARATOR = '->'
|
9
|
+
|
10
|
+
attr_reader :flow, :state, :id, :type
|
11
|
+
attr_accessor :session
|
12
|
+
|
13
|
+
# Session types:
|
14
|
+
# - :primary
|
15
|
+
# - :previous
|
16
|
+
# - :back_to
|
17
|
+
def initialize(id: nil, type: :primary)
|
18
|
+
@id = id
|
19
|
+
@type = type
|
20
|
+
|
21
|
+
if id.present?
|
22
|
+
unless defined?($redis) && $redis.present?
|
23
|
+
raise(
|
24
|
+
Xip::Errors::RedisNotConfigured,
|
25
|
+
"Please make sure REDIS_URL is configured before using sessions"
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
get_session
|
30
|
+
end
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.flow_and_state_from_session_slug(slug:)
|
36
|
+
{
|
37
|
+
flow: slug&.split(SLUG_SEPARATOR)&.first,
|
38
|
+
state: slug&.split(SLUG_SEPARATOR)&.last
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.slugify(flow:, state:)
|
43
|
+
unless flow.present? && state.present?
|
44
|
+
raise(ArgumentError, 'A flow and state must be specified.')
|
45
|
+
end
|
46
|
+
|
47
|
+
[flow, state].join(SLUG_SEPARATOR)
|
48
|
+
end
|
49
|
+
|
50
|
+
def flow
|
51
|
+
return nil if flow_string.blank?
|
52
|
+
|
53
|
+
@flow ||= FlowMap.new.init(flow: flow_string, state: state_string)
|
54
|
+
end
|
55
|
+
|
56
|
+
def state
|
57
|
+
flow&.current_state
|
58
|
+
end
|
59
|
+
|
60
|
+
def flow_string
|
61
|
+
session&.split(SLUG_SEPARATOR)&.first
|
62
|
+
end
|
63
|
+
|
64
|
+
def state_string
|
65
|
+
session&.split(SLUG_SEPARATOR)&.last
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_session
|
69
|
+
@session ||= get_key(session_key)
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_session(new_flow:, new_state:)
|
73
|
+
@flow = nil # override @flow's memoization
|
74
|
+
existing_session = session # tmp backup for previous_session storage
|
75
|
+
@session = self.class.canonical_session_slug(
|
76
|
+
flow: new_flow,
|
77
|
+
state: new_state
|
78
|
+
)
|
79
|
+
|
80
|
+
Xip::Logger.l(
|
81
|
+
topic: [type, 'session'].join('_'),
|
82
|
+
message: "User #{id}: setting session to #{new_flow}->#{new_state}"
|
83
|
+
)
|
84
|
+
|
85
|
+
if primary_session?
|
86
|
+
store_current_to_previous(existing_session: existing_session)
|
87
|
+
end
|
88
|
+
|
89
|
+
persist_key(key: session_key, value: session)
|
90
|
+
end
|
91
|
+
|
92
|
+
def clear_session
|
93
|
+
$redis.del(session_key)
|
94
|
+
end
|
95
|
+
|
96
|
+
def present?
|
97
|
+
session.present?
|
98
|
+
end
|
99
|
+
|
100
|
+
def blank?
|
101
|
+
!present?
|
102
|
+
end
|
103
|
+
|
104
|
+
def +(steps)
|
105
|
+
return nil if flow.blank?
|
106
|
+
return self if steps.zero?
|
107
|
+
|
108
|
+
new_state = self.state + steps
|
109
|
+
new_session = Xip::Session.new(id: self.id)
|
110
|
+
new_session.session = self.class.canonical_session_slug(
|
111
|
+
flow: self.flow_string,
|
112
|
+
state: new_state
|
113
|
+
)
|
114
|
+
|
115
|
+
new_session
|
116
|
+
end
|
117
|
+
|
118
|
+
def -(steps)
|
119
|
+
return nil if flow.blank?
|
120
|
+
|
121
|
+
if steps < 0
|
122
|
+
return self + steps.abs
|
123
|
+
else
|
124
|
+
return self + (-steps)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def ==(other_session)
|
129
|
+
self.flow_string == other_session.flow_string &&
|
130
|
+
self.state_string == other_session.state_string &&
|
131
|
+
self.type == other_session.type &&
|
132
|
+
self.id == other_session.id
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.is_a_session_string?(string)
|
136
|
+
session_regex = /(.+)(#{SLUG_SEPARATOR})(.+)/
|
137
|
+
!!string.match(session_regex)
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.canonical_session_slug(flow:, state:)
|
141
|
+
[flow, state].join(SLUG_SEPARATOR)
|
142
|
+
end
|
143
|
+
|
144
|
+
def session_key
|
145
|
+
case type
|
146
|
+
when :primary
|
147
|
+
id
|
148
|
+
when :previous
|
149
|
+
previous_session_key
|
150
|
+
when :back_to
|
151
|
+
back_to_key
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def primary_session?
|
156
|
+
type == :primary
|
157
|
+
end
|
158
|
+
|
159
|
+
def previous_session?
|
160
|
+
type == :previous
|
161
|
+
end
|
162
|
+
|
163
|
+
def back_to_session?
|
164
|
+
type == :back_to
|
165
|
+
end
|
166
|
+
|
167
|
+
def to_s
|
168
|
+
[flow_string, state_string].join(SLUG_SEPARATOR)
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
def previous_session_key
|
174
|
+
[id, 'previous'].join('-')
|
175
|
+
end
|
176
|
+
|
177
|
+
def back_to_key
|
178
|
+
[id, 'back_to'].join('-')
|
179
|
+
end
|
180
|
+
|
181
|
+
def store_current_to_previous(existing_session:)
|
182
|
+
# Prevent previous_session from becoming current_session
|
183
|
+
if session == existing_session
|
184
|
+
Xip::Logger.l(
|
185
|
+
topic: "previous_session",
|
186
|
+
message: "User #{id}: skipping setting to #{session}"\
|
187
|
+
' because it is the same as current_session'
|
188
|
+
)
|
189
|
+
else
|
190
|
+
Xip::Logger.l(
|
191
|
+
topic: "previous_session",
|
192
|
+
message: "User #{id}: setting to #{existing_session}"
|
193
|
+
)
|
194
|
+
|
195
|
+
persist_key(
|
196
|
+
key: previous_session_key,
|
197
|
+
value: existing_session
|
198
|
+
)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
end
|
data/lib/xip/version.rb
CHANGED
data/logo.svg
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<svg width="925px" height="220px" viewBox="0 0 925 220" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
3
|
+
<title>Dark bg</title>
|
4
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
5
|
+
<g id="Dark-bg">
|
6
|
+
<rect id="Rectangle" fill="#0D1117" x="0" y="0" width="925" height="220"></rect>
|
7
|
+
<path d="M629.337829,63 L629.33697,98.175 L629.375,98.1953125 L661.083984,63 L700.844727,63 L654.360352,112.472656 L703.999023,162.692383 L662.329102,162.692383 L629.375,126.334961 L629.33697,126.199 L629.337829,162.692383 L598.95697,162.692383 L598.95697,63 L629.337829,63 Z M747.441437,163 L747.441437,63.3076172 L717.060578,63.3076172 L717.060578,163 L747.441437,163 Z M838.098602,163 L838.098602,89.0400391 L881.760711,89.0400391 L881.760711,63.3076172 L764.055633,63.3076172 L764.055633,89.0400391 L807.717743,89.0400391 L807.717743,163 L838.098602,163 Z" id="KIT" fill="#9CA3AF" fill-rule="nonzero"></path>
|
8
|
+
<path d="M265.038178,163 L298.324311,126.642578 L331.278412,163 L372.948334,163 L323.309662,112.780273 L369.794037,63.3076172 L330.033295,63.3076172 L298.324311,98.5029297 L266.781342,63.3076172 L226.522553,63.3076172 L273.338959,112.780273 L223.949311,163 L265.038178,163 Z M416.28714,163 L416.28714,63.3076172 L385.906281,63.3076172 L385.906281,163 L416.28714,163 Z M470.333954,163 L470.333954,139.010742 L525.11911,139.010742 C528.384084,139.010742 531.593719,138.817057 534.748016,138.429688 C539.451792,137.931641 543.436167,136.631185 546.701141,134.52832 C554.72523,129.60319 558.764943,118.148112 558.820282,100.163086 C558.87562,94.0205078 558.183889,88.1546224 556.745086,82.5654297 C554.476206,73.2685547 548.720998,67.5133464 539.479461,65.2998047 C535.052378,64.2483724 530.182586,63.6396484 524.870086,63.4736328 L524.870086,63.4736328 L512.252899,63.3076172 L439.953094,63.3076172 L439.953094,163 L470.333954,163 Z M508.600555,113.27832 L470.333954,113.27832 L470.333954,89.0400391 L507.385507,89.0404626 C512.836071,89.0480857 516.94877,89.1586217 519.723602,89.3720703 C525.423472,89.7594401 528.328745,93.4394531 528.439422,100.412109 C528.439422,104.230469 527.77536,107.301758 526.447235,109.625977 C525.285126,111.728841 522.490529,112.890951 518.063446,113.112305 L518.063446,113.112305 L508.600555,113.27832 Z" id="XIP" fill="#9CA3AF" fill-rule="nonzero"></path>
|
9
|
+
<g id="Attempt-2" transform="translate(34.000000, 28.000000)" fill-rule="nonzero">
|
10
|
+
<path d="M35.5079177,0.152082297 L77.9343246,42.5784892 L77.8572,42.6550823 L77.7222,77.722 L42.585,77.858 L42.6852,77.7570823 L0.3292,35.4010823 L0.2292,35.502 L0.3647,0.3647 L35.4302,0.229082297 L35.5079177,0.152082297 Z" id="Combined-Shape" fill="#60A5FA"></path>
|
11
|
+
<path d="M135.585,0.2293 L170.722,0.3647 L170.858,35.4323 L170.933918,35.5079177 L128.507511,77.9343246 L128.43,77.8573 L93.365,77.722 L93.229,42.585 L93.329,42.6843 L135.685,0.3283 L135.585,0.2293 Z" id="Combined-Shape" fill="#34D399"></path>
|
12
|
+
<path d="M128.502,91.229 L128.401675,91.329 L170.757675,133.685 L170.858,133.585 L170.722,168.722 L135.653675,168.858 L135.579082,168.933918 L93.1526754,126.507511 L93.2286754,126.431 L93.365,91.365 L128.502,91.229 Z" id="Combined-Shape" fill="#60A5FA"></path>
|
13
|
+
<path d="M42.5789892,91.1526754 L42.6545823,91.2286754 L77.7222,91.365 L77.8576,126.502 L77.7575823,126.401675 L35.4015823,168.757675 L35.5019,168.858 L0.3647,168.722 L0.228582297,133.654675 L0.152582297,133.579082 L42.5789892,91.1526754 Z" id="Combined-Shape" fill="#34D399"></path>
|
14
|
+
</g>
|
15
|
+
</g>
|
16
|
+
</g>
|
17
|
+
</svg>
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Xip::Configuration" do
|
6
|
+
|
7
|
+
describe "accessing via method calling" do
|
8
|
+
let(:services_yml) { File.read(File.join(File.dirname(__FILE__), 'support', 'services.yml')) }
|
9
|
+
let(:parsed_config) { YAML.load(ERB.new(services_yml).result)[Xip.env] }
|
10
|
+
let(:config) { Xip.load_services_config!(services_yml) }
|
11
|
+
|
12
|
+
it "should return the root node" do
|
13
|
+
expect(config.facebook).to eq parsed_config['facebook']
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should access deeply nested nodes" do
|
17
|
+
expect(config.facebook.setup.greeting).to eq parsed_config['facebook']['setup']['greeting']
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should handle values that are arrays correctly" do
|
21
|
+
expect(config.facebook.setup.persistent_menu).to be_a(Array)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should retain the configuration at the class level" do
|
25
|
+
expect(Xip.config.facebook.setup.greeting).to eq parsed_config['facebook']['setup']['greeting']
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should handle multiple keys at the root level" do
|
29
|
+
expect(config.twilio_sms.account_sid).to eq parsed_config['twilio_sms']['account_sid']
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return nil if the key is not present at the node" do
|
33
|
+
expect(config.twilio_sms.api_key).to be nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should raise a NoMethodError when accessing multi-levels of missing nodes" do
|
37
|
+
expect { config.slack.api_key }.to raise_error(NoMethodError)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "config files with ERB" do
|
42
|
+
let(:services_yml) { File.read(File.join(File.dirname(__FILE__), 'support', 'services_with_erb.yml')) }
|
43
|
+
let(:config) { Xip.load_services_config!(services_yml) }
|
44
|
+
|
45
|
+
it "should replace available embedded env vars" do
|
46
|
+
ENV['FACEBOOK_VERIFY_TOKEN'] = 'it works'
|
47
|
+
expect(config.facebook.verify_token).to eq 'it works'
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should replace unavailable embedded env vars with nil" do
|
51
|
+
expect(config.facebook.challenge).to be_nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should not reload the configuration file if one already exists" do
|
55
|
+
Xip.load_services_config(services_yml)
|
56
|
+
expect(config.facebook.challenge).to be_nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "configuring with default values" do
|
61
|
+
let(:config) {
|
62
|
+
Xip::Configuration.new(
|
63
|
+
{ 'a' => nil, 'x' => 0, 'y' => false, 'z' => '' }
|
64
|
+
)
|
65
|
+
}
|
66
|
+
|
67
|
+
it 'should replace a nil value' do
|
68
|
+
config.set_default('a', 99)
|
69
|
+
expect(config.a).to eq 99
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should NOT replace a zero value' do
|
73
|
+
config.set_default('x', 99)
|
74
|
+
expect(config.x).to eq 0
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should NOT replace a false value' do
|
78
|
+
config.set_default('y', 99)
|
79
|
+
expect(config.y).to be false
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should NOT replace a blank string value' do
|
83
|
+
config.set_default('z', 99)
|
84
|
+
expect(config.z).to eq ''
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should replace a not-set key' do
|
88
|
+
config.set_default('zz', 99)
|
89
|
+
expect(config.zz).to eq 99
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|