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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +116 -0
  3. data/.gitignore +12 -0
  4. data/CHANGELOG.md +135 -0
  5. data/Gemfile +4 -1
  6. data/Gemfile.lock +65 -15
  7. data/LICENSE +6 -4
  8. data/README.md +51 -1
  9. data/VERSION +1 -0
  10. data/bin/xip +3 -11
  11. data/lib/xip.rb +1 -3
  12. data/lib/xip/base.rb +189 -0
  13. data/lib/xip/cli.rb +273 -0
  14. data/lib/xip/cli_base.rb +24 -0
  15. data/lib/xip/commands/command.rb +13 -0
  16. data/lib/xip/commands/console.rb +74 -0
  17. data/lib/xip/commands/server.rb +63 -0
  18. data/lib/xip/configuration.rb +56 -0
  19. data/lib/xip/controller/callbacks.rb +63 -0
  20. data/lib/xip/controller/catch_all.rb +84 -0
  21. data/lib/xip/controller/controller.rb +274 -0
  22. data/lib/xip/controller/dev_jumps.rb +40 -0
  23. data/lib/xip/controller/dynamic_delay.rb +61 -0
  24. data/lib/xip/controller/helpers.rb +128 -0
  25. data/lib/xip/controller/interrupt_detect.rb +99 -0
  26. data/lib/xip/controller/messages.rb +283 -0
  27. data/lib/xip/controller/nlp.rb +49 -0
  28. data/lib/xip/controller/replies.rb +281 -0
  29. data/lib/xip/controller/unrecognized_message.rb +61 -0
  30. data/lib/xip/core_ext.rb +5 -0
  31. data/lib/xip/core_ext/numeric.rb +10 -0
  32. data/lib/xip/core_ext/string.rb +18 -0
  33. data/lib/xip/dispatcher.rb +68 -0
  34. data/lib/xip/errors.rb +55 -0
  35. data/lib/xip/flow/base.rb +69 -0
  36. data/lib/xip/flow/specification.rb +56 -0
  37. data/lib/xip/flow/state.rb +82 -0
  38. data/lib/xip/generators/builder.rb +41 -0
  39. data/lib/xip/generators/builder/.gitignore +30 -0
  40. data/lib/xip/generators/builder/Gemfile +19 -0
  41. data/lib/xip/generators/builder/Procfile.dev +2 -0
  42. data/lib/xip/generators/builder/README.md +9 -0
  43. data/lib/xip/generators/builder/Rakefile +2 -0
  44. data/lib/xip/generators/builder/bot/controllers/bot_controller.rb +55 -0
  45. data/lib/xip/generators/builder/bot/controllers/catch_alls_controller.rb +21 -0
  46. data/lib/xip/generators/builder/bot/controllers/concerns/.keep +0 -0
  47. data/lib/xip/generators/builder/bot/controllers/goodbyes_controller.rb +9 -0
  48. data/lib/xip/generators/builder/bot/controllers/hellos_controller.rb +9 -0
  49. data/lib/xip/generators/builder/bot/controllers/interrupts_controller.rb +9 -0
  50. data/lib/xip/generators/builder/bot/controllers/unrecognized_messages_controller.rb +9 -0
  51. data/lib/xip/generators/builder/bot/helpers/bot_helper.rb +2 -0
  52. data/lib/xip/generators/builder/bot/models/bot_record.rb +3 -0
  53. data/lib/xip/generators/builder/bot/models/concerns/.keep +0 -0
  54. data/lib/xip/generators/builder/bot/replies/catch_alls/level1.yml +2 -0
  55. data/lib/xip/generators/builder/bot/replies/goodbyes/say_goodbye.yml +2 -0
  56. data/lib/xip/generators/builder/bot/replies/hellos/say_hello.yml +2 -0
  57. data/lib/xip/generators/builder/config.ru +4 -0
  58. data/lib/xip/generators/builder/config/boot.rb +6 -0
  59. data/lib/xip/generators/builder/config/database.yml +25 -0
  60. data/lib/xip/generators/builder/config/environment.rb +2 -0
  61. data/lib/xip/generators/builder/config/flow_map.rb +25 -0
  62. data/lib/xip/generators/builder/config/initializers/autoload.rb +8 -0
  63. data/lib/xip/generators/builder/config/initializers/inflections.rb +16 -0
  64. data/lib/xip/generators/builder/config/puma.rb +25 -0
  65. data/lib/xip/generators/builder/config/services.yml +35 -0
  66. data/lib/xip/generators/builder/config/sidekiq.yml +3 -0
  67. data/lib/xip/generators/builder/db/seeds.rb +7 -0
  68. data/lib/xip/generators/generate.rb +39 -0
  69. data/lib/xip/generators/generate/flow/controllers/controller.tt +7 -0
  70. data/lib/xip/generators/generate/flow/helpers/helper.tt +3 -0
  71. data/lib/xip/generators/generate/flow/replies/ask_example.tt +9 -0
  72. data/lib/xip/helpers/redis.rb +40 -0
  73. data/lib/xip/jobs.rb +9 -0
  74. data/lib/xip/lock.rb +82 -0
  75. data/lib/xip/logger.rb +9 -3
  76. data/lib/xip/migrations/configurator.rb +73 -0
  77. data/lib/xip/migrations/generators.rb +16 -0
  78. data/lib/xip/migrations/railtie_config.rb +14 -0
  79. data/lib/xip/migrations/tasks.rb +43 -0
  80. data/lib/xip/nlp/client.rb +21 -0
  81. data/lib/xip/nlp/result.rb +56 -0
  82. data/lib/xip/reloader.rb +89 -0
  83. data/lib/xip/reply.rb +36 -0
  84. data/lib/xip/scheduled_reply.rb +18 -0
  85. data/lib/xip/server.rb +63 -0
  86. data/lib/xip/service_message.rb +17 -0
  87. data/lib/xip/service_reply.rb +44 -0
  88. data/lib/xip/services/base_client.rb +24 -0
  89. data/lib/xip/services/base_message_handler.rb +27 -0
  90. data/lib/xip/services/base_reply_handler.rb +72 -0
  91. data/lib/xip/services/jobs/handle_message_job.rb +21 -0
  92. data/lib/xip/session.rb +203 -0
  93. data/lib/xip/version.rb +7 -1
  94. data/logo.svg +17 -0
  95. data/spec/configuration_spec.rb +93 -0
  96. data/spec/controller/callbacks_spec.rb +217 -0
  97. data/spec/controller/catch_all_spec.rb +154 -0
  98. data/spec/controller/controller_spec.rb +889 -0
  99. data/spec/controller/dynamic_delay_spec.rb +70 -0
  100. data/spec/controller/helpers_spec.rb +119 -0
  101. data/spec/controller/interrupt_detect_spec.rb +171 -0
  102. data/spec/controller/messages_spec.rb +744 -0
  103. data/spec/controller/nlp_spec.rb +93 -0
  104. data/spec/controller/replies_spec.rb +694 -0
  105. data/spec/controller/unrecognized_message_spec.rb +168 -0
  106. data/spec/dispatcher_spec.rb +79 -0
  107. data/spec/flow/flow_spec.rb +82 -0
  108. data/spec/flow/state_spec.rb +109 -0
  109. data/spec/helpers/redis_spec.rb +77 -0
  110. data/spec/lock_spec.rb +100 -0
  111. data/spec/nlp/client_spec.rb +23 -0
  112. data/spec/nlp/result_spec.rb +57 -0
  113. data/spec/replies/hello.yml.erb +15 -0
  114. data/spec/replies/messages/say_hola.yml+facebook.erb +6 -0
  115. data/spec/replies/messages/say_hola.yml+twilio.erb +6 -0
  116. data/spec/replies/messages/say_hola.yml.erb +6 -0
  117. data/spec/replies/messages/say_howdy_with_dynamic.yml +79 -0
  118. data/spec/replies/messages/say_msgs_without_breaks.yml +4 -0
  119. data/spec/replies/messages/say_offer.yml +6 -0
  120. data/spec/replies/messages/say_offer_with_dynamic.yml +6 -0
  121. data/spec/replies/messages/say_oi.yml.erb +15 -0
  122. data/spec/replies/messages/say_randomize_speech.yml +10 -0
  123. data/spec/replies/messages/say_randomize_text.yml +10 -0
  124. data/spec/replies/messages/say_yo.yml +6 -0
  125. data/spec/replies/messages/say_yo.yml+twitter +6 -0
  126. data/spec/replies/messages/sub1/sub2/say_nested.yml +10 -0
  127. data/spec/reply_spec.rb +61 -0
  128. data/spec/scheduled_reply_spec.rb +23 -0
  129. data/spec/service_reply_spec.rb +92 -0
  130. data/spec/session_spec.rb +366 -0
  131. data/spec/spec_helper.rb +22 -66
  132. data/spec/support/alternate_helpers/foo_helper.rb +5 -0
  133. data/spec/support/controllers/vaders_controller.rb +24 -0
  134. data/spec/support/helpers/fun/games_helper.rb +7 -0
  135. data/spec/support/helpers/fun/pdf_helper.rb +7 -0
  136. data/spec/support/helpers/standalone_helper.rb +5 -0
  137. data/spec/support/helpers_typo/users_helper.rb +2 -0
  138. data/spec/support/nlp_clients/dialogflow.rb +9 -0
  139. data/spec/support/nlp_clients/luis.rb +9 -0
  140. data/spec/support/nlp_results/luis_result.rb +163 -0
  141. data/spec/support/sample_messages.rb +66 -0
  142. data/spec/support/services.yml +31 -0
  143. data/spec/support/services_with_erb.yml +31 -0
  144. data/spec/version_spec.rb +16 -0
  145. data/xip.gemspec +25 -14
  146. metadata +320 -18
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class GoodbyesController < BotController
4
+
5
+ def say_goodbye
6
+ send_replies
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HellosController < BotController
4
+
5
+ def say_hello
6
+ send_replies
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class InterruptsController < BotController
4
+
5
+ def say_interrupted
6
+ do_nothing
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UnrecognizedMessagesController < BotController
4
+
5
+ def handle_unrecognized_message
6
+ do_nothing
7
+ end
8
+
9
+ end
@@ -0,0 +1,2 @@
1
+ module BotHelper
2
+ end
@@ -0,0 +1,3 @@
1
+ class BotRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -0,0 +1,2 @@
1
+ - reply_type: text
2
+ text: Oops. It looks like something went wrong. Let's try that again
@@ -0,0 +1,2 @@
1
+ - reply_type: text
2
+ text: Goodbye World!
@@ -0,0 +1,2 @@
1
+ - reply_type: text
2
+ text: Hello World!
@@ -0,0 +1,4 @@
1
+ require 'rack/handler/puma'
2
+ require_relative 'config/boot'
3
+
4
+ Rack::Handler::Puma.run(Xip::Server)
@@ -0,0 +1,6 @@
1
+ require 'xip'
2
+ require_relative './environment'
3
+
4
+ Bundler.require(:default, Xip.env)
5
+
6
+ Xip.boot
@@ -0,0 +1,25 @@
1
+ # SQLite version 3.x
2
+ # gem install sqlite3
3
+ #
4
+ # Ensure the SQLite 3 gem is defined in your Gemfile
5
+ # gem 'sqlite3'
6
+ #
7
+ default: &default
8
+ adapter: sqlite3
9
+ pool: <%= ENV.fetch("XIP_MAX_THREADS") { 5 } %>
10
+ timeout: 5000
11
+
12
+ development:
13
+ <<: *default
14
+ database: db/development.sqlite3
15
+
16
+ # Warning: The database defined as "test" will be erased and
17
+ # re-generated from your development database when you run "rake".
18
+ # Do not set this db to the same as development or production.
19
+ test:
20
+ <<: *default
21
+ database: db/test.sqlite3
22
+
23
+ production:
24
+ <<: *default
25
+ database: db/production.sqlite3
@@ -0,0 +1,2 @@
1
+ REDIS_URL = ENV['REDIS_URL'] || 'redis://localhost:6379/0'
2
+ $redis = Redis.new(:url => REDIS_URL)
@@ -0,0 +1,25 @@
1
+ class FlowMap
2
+
3
+ include Xip::Flow
4
+
5
+ flow :hello do
6
+ state :say_hello
7
+ end
8
+
9
+ flow :goodbye do
10
+ state :say_goodbye
11
+ end
12
+
13
+ flow :interrupt do
14
+ state :say_interrupted
15
+ end
16
+
17
+ flow :unrecognized_message do
18
+ state :handle_unrecognized_message
19
+ end
20
+
21
+ flow :catch_all do
22
+ state :level1
23
+ end
24
+
25
+ end
@@ -0,0 +1,8 @@
1
+ # Add additional directories below for hot-reloading during development.
2
+ # You'll want to include any custom directories you create for your code.
3
+ # To stop certain files or directories from autoloading, use autoload_ignore_paths
4
+
5
+ # Xip.config.autoload_paths << File.join(Xip.root, 'bot', 'services')
6
+ # Xip.config.autoload_paths << File.join(Xip.root, 'bot', 'jobs')
7
+
8
+ # Xip.config.autoload_ignore_paths << File.join(Xip.root, 'bot', 'overrides')
@@ -0,0 +1,16 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new inflection rules using the following format. Inflections
4
+ # are locale specific, and you may define rules for as many different
5
+ # locales as you wish. All of these examples are active by default:
6
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
7
+ # inflect.plural /^(ox)$/i, '\1en'
8
+ # inflect.singular /^(ox)en/i, '\1'
9
+ # inflect.irregular 'person', 'people'
10
+ # inflect.uncountable %w( fish sheep )
11
+ # end
12
+
13
+ # These inflection rules are supported but not enabled by default:
14
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
15
+ # inflect.acronym 'RESTful'
16
+ # end
@@ -0,0 +1,25 @@
1
+ threads_count = ENV.fetch("XIP_MAX_THREADS") { 5 }.to_i
2
+ threads threads_count, threads_count
3
+
4
+ # Specifies the `port` that Puma will listen on to receive requests, default is 3000.
5
+ #
6
+ port ENV.fetch("PORT") { 3000 }
7
+
8
+ # Specifies the number of `workers` to boot in clustered mode.
9
+ # Workers are forked webserver processes. If using threads and workers together
10
+ # the concurrency of the application would be max `threads` * `workers`.
11
+ # Workers do not work on JRuby or Windows (both of which do not support
12
+ # processes).
13
+ #
14
+ # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
15
+
16
+ # Use the `preload_app!` method when specifying a `workers` number.
17
+ # This directive tells Puma to first boot the application and load code
18
+ # before forking the application. This takes advantage of Copy On Write
19
+ # process behavior so workers use less memory.
20
+ #
21
+ # preload_app!
22
+
23
+ # Specifies the `environment` that Puma will run in.
24
+ #
25
+ environment ENV.fetch("XIP_ENV") { "development" }
@@ -0,0 +1,35 @@
1
+ default: &default
2
+ # ==========================================
3
+ # ===== Example Facebook Service Setup =====
4
+ # ==========================================
5
+ # facebook:
6
+ # verify_token: XXXFACEBOOK_VERIFY_TOKENXXX
7
+ # page_access_token: XXXFACEBOOK_ACCESS_TOKENXXX
8
+ # setup:
9
+ # greeting: # Greetings are broken up by locale
10
+ # - locale: default
11
+ # text: "Welcome to my Facebook Bot."
12
+ # get_started:
13
+ # payload: new_user
14
+ # persistent_menu:
15
+ # - locale: default
16
+ # composer_input_disabled: false
17
+ # call_to_actions:
18
+ # - type: payload
19
+ # text: Some Button
20
+ # payload: some_button
21
+ #
22
+ # ===========================================
23
+ # ======== Example SMS Service Setup ========
24
+ # ===========================================
25
+ # twilio:
26
+ # account_sid: XXXTWILIO_ACCOUNT_SIDXXX
27
+ # auth_token: XXXTWILIO_AUTH_TOKENXXX
28
+ # from_phone: +14155330000
29
+
30
+ production:
31
+ <<: *default
32
+ development:
33
+ <<: *default
34
+ test:
35
+ <<: *default
@@ -0,0 +1,3 @@
1
+ ---
2
+ :verbose: false
3
+ :concurrency: <%= ENV.fetch('SIDEKIQ_CONCURRENCY', 5).to_i %>
@@ -0,0 +1,7 @@
1
+ # This file should contain all the record creation needed to seed the database with its default values.
2
+ # The data can then be loaded with the `xip db:seed` command (or created alongside the database with db:setup).
3
+ #
4
+ # Examples:
5
+ #
6
+ # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
7
+ # Character.create(name: 'Luke', movie: movies.first)
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'thor/group'
5
+
6
+ module Xip
7
+ module Generators
8
+ class Generate < Thor::Group
9
+ include Thor::Actions
10
+
11
+ argument :generator
12
+ argument :name
13
+
14
+ def self.source_root
15
+ File.dirname(__FILE__) + "/generate/flow"
16
+ end
17
+
18
+ def create_controller
19
+ template('controllers/controller.tt', "bot/controllers/#{name.pluralize}_controller.rb")
20
+ end
21
+
22
+ def create_replies
23
+ # Sample Ask Reply
24
+ template('replies/ask_example.tt', "bot/replies/#{name.pluralize}/ask_example.yml.erb")
25
+ end
26
+
27
+ def create_helper
28
+ template('helpers/helper.tt', "bot/helpers/#{name}_helper.rb")
29
+ end
30
+
31
+ def edit_flow_map
32
+ inject_into_file "config/flow_map.rb", after: "include Xip::Flow\n" do
33
+ "\n\tflow :#{name} do\n\t\tstate :ask_example\n\tend\n"
34
+ end
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ class <%= name.classify.pluralize %>Controller < BotController
2
+
3
+ def ask_example
4
+ send_replies
5
+ end
6
+
7
+ end
@@ -0,0 +1,3 @@
1
+ module <%= name.capitalize.underscore.camelize %>Helper
2
+
3
+ end
@@ -0,0 +1,9 @@
1
+ - reply_type: text
2
+ text: "Welcome to your brand new Xip flow."
3
+ - reply_type: delay
4
+ duration: 2
5
+ - reply_type: text
6
+ text: 'That was pretty easy huh?'
7
+ suggestions:
8
+ - text: "Yes"
9
+ - text: "No"
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Xip
5
+ module Redis
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ private
10
+
11
+ def get_key(key, expiration: Xip.config.session_ttl)
12
+ if expiration > 0
13
+ getex(key, expiration)
14
+ else
15
+ $redis.get(key)
16
+ end
17
+ end
18
+
19
+ def delete_key(key)
20
+ $redis.del(key)
21
+ end
22
+
23
+ def getex(key, expiration=Xip.config.session_ttl)
24
+ $redis.multi do
25
+ $redis.expire(key, expiration)
26
+ $redis.get(key)
27
+ end.last
28
+ end
29
+
30
+ def persist_key(key:, value:, expiration: Xip.config.session_ttl)
31
+ if expiration > 0
32
+ $redis.setex(key, expiration, value)
33
+ else
34
+ $redis.set(key, value)
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ class Jobs
5
+
6
+ include Sidekiq::Worker
7
+
8
+ end
9
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xip
4
+ class Lock
5
+
6
+ include Xip::Redis
7
+
8
+ attr_accessor :session_id, :session_slug, :position, :tid
9
+
10
+ def initialize(session_id:, session_slug: nil, position: nil)
11
+ @session_id = session_id
12
+ @session_slug = session_slug
13
+ @position = position
14
+ @tid = Xip.tid
15
+ end
16
+
17
+ def self.find_lock(session_id:)
18
+ lock = Lock.new(session_id: session_id)
19
+ lock_slug = lock.slug # fetch lock from Redis
20
+
21
+ return if lock_slug.nil?
22
+
23
+ # parse the lock slug
24
+ tid_and_session_slug, position = lock_slug.split(':')
25
+ tid, session_slug = tid_and_session_slug.split('#')
26
+
27
+ # set the values from the slug to the lock object
28
+ lock.session_slug = session_slug
29
+ lock.position = position&.to_i
30
+ lock.tid = tid
31
+ lock
32
+ end
33
+
34
+ def create
35
+ if session_slug.blank?
36
+ raise(
37
+ ArgumentError,
38
+ 'A session_slug must be specified before a lock can be created.'
39
+ )
40
+ end
41
+
42
+ # Expire locks after 30 seconds to prevent zombie locks from blocking
43
+ # other threads to interact with a session.
44
+ persist_key(
45
+ key: lock_key,
46
+ value: generate_lock,
47
+ expiration: Xip.config.lock_autorelease
48
+ )
49
+ end
50
+
51
+ def release
52
+ delete_key(lock_key)
53
+ end
54
+
55
+ def slug
56
+ # We don't want to extend the expiration time that would result if
57
+ # we specified one here.
58
+ get_key(lock_key, expiration: 0)
59
+ end
60
+
61
+ # Returns a hash:
62
+ # { flow: 'flow_name', state: 'state_name' }
63
+ def flow_and_state
64
+ Session.flow_and_state_from_session_slug(slug: session_slug)
65
+ end
66
+
67
+ private
68
+
69
+ def lock_key
70
+ [@session_id, 'lock'].join('-')
71
+ end
72
+
73
+ def generate_lock
74
+ if @position.present?
75
+ @session_slug = [@session_slug, @position].join(':')
76
+ end
77
+
78
+ [@tid, @session_slug].join('#')
79
+ end
80
+
81
+ end
82
+ end
@@ -25,7 +25,9 @@ module Xip
25
25
  end
26
26
 
27
27
  def self.log(topic:, message:)
28
- puts "#{print_topic(topic)} #{message}"
28
+ unless ENV['XIP_ENV'] == 'test'
29
+ puts "TID-#{Xip.tid} #{print_topic(topic)} #{message}"
30
+ end
29
31
  end
30
32
 
31
33
  def self.print_topic(topic)
@@ -36,13 +38,17 @@ module Xip
36
38
  :green
37
39
  when :previous_session, :back_to_session
38
40
  :yellow
41
+ when :interrupt
42
+ :magenta
39
43
  when :facebook, :twilio, :bandwidth
40
44
  :blue
41
45
  when :smooch
42
46
  :magenta
43
- when :alexa
47
+ when :alexa, :voice, :unrecognized_message
44
48
  :light_cyan
45
- when :catch_all
49
+ when :nlp
50
+ :cyan
51
+ when :catch_all, :err
46
52
  :red
47
53
  when :user
48
54
  :white