xip 0.0.1 → 2.0.0.beta2
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/.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
|