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.
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