stealth 0.9.1

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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +58 -0
  3. data/.gitignore +12 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +81 -0
  6. data/LICENSE +20 -0
  7. data/README.md +1 -0
  8. data/VERSION +1 -0
  9. data/bin/stealth +5 -0
  10. data/lib/stealth/base.rb +87 -0
  11. data/lib/stealth/cli.rb +82 -0
  12. data/lib/stealth/cli_base.rb +25 -0
  13. data/lib/stealth/commands/command.rb +14 -0
  14. data/lib/stealth/commands/console.rb +75 -0
  15. data/lib/stealth/commands/server.rb +20 -0
  16. data/lib/stealth/configuration.rb +54 -0
  17. data/lib/stealth/controller.rb +190 -0
  18. data/lib/stealth/dispatcher.rb +48 -0
  19. data/lib/stealth/errors.rb +32 -0
  20. data/lib/stealth/flow/base.rb +256 -0
  21. data/lib/stealth/flow/errors.rb +25 -0
  22. data/lib/stealth/flow/event.rb +43 -0
  23. data/lib/stealth/flow/event_collection.rb +41 -0
  24. data/lib/stealth/flow/specification.rb +67 -0
  25. data/lib/stealth/flow/state.rb +48 -0
  26. data/lib/stealth/jobs.rb +10 -0
  27. data/lib/stealth/logger.rb +16 -0
  28. data/lib/stealth/reply.rb +19 -0
  29. data/lib/stealth/server.rb +38 -0
  30. data/lib/stealth/service_message.rb +17 -0
  31. data/lib/stealth/service_reply.rb +30 -0
  32. data/lib/stealth/services/base_client.rb +28 -0
  33. data/lib/stealth/services/base_message_handler.rb +28 -0
  34. data/lib/stealth/services/base_reply_handler.rb +65 -0
  35. data/lib/stealth/services/facebook/client.rb +35 -0
  36. data/lib/stealth/services/facebook/events/message_event.rb +59 -0
  37. data/lib/stealth/services/facebook/events/postback_event.rb +36 -0
  38. data/lib/stealth/services/facebook/message_handler.rb +84 -0
  39. data/lib/stealth/services/facebook/reply_handler.rb +471 -0
  40. data/lib/stealth/services/facebook/setup.rb +25 -0
  41. data/lib/stealth/services/jobs/handle_message_job.rb +22 -0
  42. data/lib/stealth/session.rb +74 -0
  43. data/lib/stealth/version.rb +12 -0
  44. data/lib/stealth.rb +1 -0
  45. data/spec/configuration_spec.rb +52 -0
  46. data/spec/flow/custom_transitions_spec.rb +99 -0
  47. data/spec/flow/flow_spec.rb +91 -0
  48. data/spec/flow/transition_callbacks_spec.rb +228 -0
  49. data/spec/replies/nested_reply_with_erb.yml +16 -0
  50. data/spec/sample_services_yml/services.yml +31 -0
  51. data/spec/sample_services_yml/services_with_erb.yml +31 -0
  52. data/spec/service_reply_spec.rb +34 -0
  53. data/spec/spec_helper.rb +13 -0
  54. data/spec/version_spec.rb +16 -0
  55. data/stealth.gemspec +30 -0
  56. metadata +247 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 332b23102210b0b6af8adc0cb6be1c75691b75c8
4
+ data.tar.gz: b9b7c50fb2079e58f4de2f5d22ca3303f1a3fc1f
5
+ SHA512:
6
+ metadata.gz: 918dcf4f5bd781979b3f805625e75c829071523a15c1264e48e7b96aecc221cc3fb6ed3dfc182b079ecdb21b7ef667f8ebbae1fe8ee92ca32c128ba5b49e3abc
7
+ data.tar.gz: 8d2fdb480e79890dd88bc8b77f25459dd9bf1a65b53b2099c579c5b506b55d21abd6da30ee74092c6d9241ab458848430b3474c918395896d9a154ab8b8cf005
@@ -0,0 +1,58 @@
1
+ # Ruby CircleCI 2.0 configuration file
2
+ #
3
+ # Check https://circleci.com/docs/2.0/language-ruby/ for more details
4
+ #
5
+ version: 2
6
+ jobs:
7
+ build:
8
+ docker:
9
+ # specify the version you desire here
10
+ - image: circleci/ruby:2.4.1-node-browsers
11
+ environment:
12
+ STEALTH_ENV: test
13
+
14
+ # Specify service dependencies here if necessary
15
+ # CircleCI maintains a library of pre-built images
16
+ # documented at https://circleci.com/docs/2.0/circleci-images/
17
+ # - image: circleci/postgres:9.4
18
+
19
+ working_directory: ~/repo
20
+
21
+ steps:
22
+ - checkout
23
+
24
+ # Download and cache dependencies
25
+ - restore_cache:
26
+ keys:
27
+ - v1-dependencies-{{ checksum "Gemfile.lock" }}
28
+ # fallback to using the latest cache if no exact match is found
29
+ - v1-dependencies-
30
+
31
+ - run:
32
+ name: install dependencies
33
+ command: |
34
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
35
+
36
+ - save_cache:
37
+ paths:
38
+ - ./vendor/bundle
39
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}
40
+
41
+ # run tests!
42
+ - run:
43
+ name: run tests
44
+ command: |
45
+ mkdir /tmp/test-results
46
+ TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
47
+
48
+ bundle exec rspec --format progress \
49
+ --format RspecJunitFormatter \
50
+ --out /tmp/test-results/rspec.xml \
51
+ --format progress
52
+
53
+ # collect reports
54
+ - store_test_results:
55
+ path: /tmp/test-results
56
+ - store_artifacts:
57
+ path: /tmp/test-results
58
+ destination: test-results
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ # please add general patterns to your global ignore list
2
+ # see https://github.com/github/gitignore#readme
3
+ .DS_STORE
4
+ *.swp
5
+ *.rbc
6
+ *.sass-cache
7
+ /pkg
8
+ /doc/api
9
+ /coverage
10
+ .yardoc
11
+ doc/
12
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ platforms :mri do
5
+ gem 'oj', '~> 3.3'
6
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,81 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ stealth (0.9.0)
5
+ activesupport (~> 5.1)
6
+ faraday (~> 0.13)
7
+ multi_json (~> 1.12)
8
+ puma (~> 3.10.0)
9
+ sidekiq (~> 5.0)
10
+ sinatra (~> 2.0.0)
11
+ thor (~> 0.20)
12
+
13
+ GEM
14
+ remote: https://rubygems.org/
15
+ specs:
16
+ activesupport (5.1.3)
17
+ concurrent-ruby (~> 1.0, >= 1.0.2)
18
+ i18n (~> 0.7)
19
+ minitest (~> 5.1)
20
+ tzinfo (~> 1.1)
21
+ concurrent-ruby (1.0.5)
22
+ connection_pool (2.2.1)
23
+ diff-lcs (1.3)
24
+ faraday (0.13.1)
25
+ multipart-post (>= 1.2, < 3)
26
+ i18n (0.8.6)
27
+ minitest (5.10.3)
28
+ multi_json (1.12.2)
29
+ multipart-post (2.0.0)
30
+ mustermann (1.0.1)
31
+ oj (3.3.6)
32
+ puma (3.10.0)
33
+ rack (2.0.3)
34
+ rack-protection (2.0.0)
35
+ rack
36
+ rack-test (0.7.0)
37
+ rack (>= 1.0, < 3)
38
+ redis (3.3.3)
39
+ rspec (3.6.0)
40
+ rspec-core (~> 3.6.0)
41
+ rspec-expectations (~> 3.6.0)
42
+ rspec-mocks (~> 3.6.0)
43
+ rspec-core (3.6.0)
44
+ rspec-support (~> 3.6.0)
45
+ rspec-expectations (3.6.0)
46
+ diff-lcs (>= 1.2.0, < 2.0)
47
+ rspec-support (~> 3.6.0)
48
+ rspec-mocks (3.6.0)
49
+ diff-lcs (>= 1.2.0, < 2.0)
50
+ rspec-support (~> 3.6.0)
51
+ rspec-support (3.6.0)
52
+ rspec_junit_formatter (0.3.0)
53
+ rspec-core (>= 2, < 4, != 2.12.0)
54
+ sidekiq (5.0.4)
55
+ concurrent-ruby (~> 1.0)
56
+ connection_pool (~> 2.2, >= 2.2.0)
57
+ rack-protection (>= 1.5.0)
58
+ redis (~> 3.3, >= 3.3.3)
59
+ sinatra (2.0.0)
60
+ mustermann (~> 1.0)
61
+ rack (~> 2.0)
62
+ rack-protection (= 2.0.0)
63
+ tilt (~> 2.0)
64
+ thor (0.20.0)
65
+ thread_safe (0.3.6)
66
+ tilt (2.0.8)
67
+ tzinfo (1.2.3)
68
+ thread_safe (~> 0.1)
69
+
70
+ PLATFORMS
71
+ ruby
72
+
73
+ DEPENDENCIES
74
+ oj (~> 3.3)
75
+ rack-test (~> 0.7.0)
76
+ rspec (~> 3.6.0)
77
+ rspec_junit_formatter (~> 0.3.0)
78
+ stealth!
79
+
80
+ BUNDLED WITH
81
+ 1.15.3
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017 Mauricio Gomes, Black Ops Bureau
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # Stealth
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.9.1
data/bin/stealth ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler'
4
+ require 'stealth/cli'
5
+ Stealth::Cli.start
@@ -0,0 +1,87 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ # base requirements
5
+ require 'yaml'
6
+ require 'sidekiq'
7
+ require 'active_support/all'
8
+
9
+ # core
10
+ require 'stealth/version'
11
+ require 'stealth/errors'
12
+ require 'stealth/logger'
13
+ require 'stealth/configuration'
14
+ require 'stealth/jobs'
15
+ require 'stealth/dispatcher'
16
+ require 'stealth/server'
17
+ require 'stealth/reply'
18
+ require 'stealth/service_reply'
19
+ require 'stealth/service_message'
20
+ require 'stealth/session'
21
+ require 'stealth/controller'
22
+ require 'stealth/flow/base'
23
+ require 'stealth/services/base_client'
24
+
25
+ module Stealth
26
+
27
+ def self.env
28
+ @env ||= ActiveSupport::StringInquirer.new(ENV['STEALTH_ENV'] || 'development')
29
+ end
30
+
31
+ def self.root
32
+ @root ||= File.expand_path(Pathname.new(Dir.pwd))
33
+ end
34
+
35
+ def self.boot
36
+ load_environment
37
+ end
38
+
39
+ def self.config
40
+ @configuration
41
+ end
42
+
43
+ # Loads the services.yml configuration unless one has already been loaded
44
+ def self.load_services_config(services_yaml)
45
+ @semaphore ||= Mutex.new
46
+
47
+ @configuration ||= begin
48
+ @semaphore.synchronize do
49
+ services_config = YAML.load(ERB.new(services_yaml).result)
50
+
51
+ unless services_config.has_key?(env)
52
+ raise Stealth::Errors::ConfigurationError, "Could not find services.yml configuration for #{env} environment."
53
+ end
54
+
55
+ Stealth::Configuration.new(services_config[env])
56
+ end
57
+ end
58
+ end
59
+
60
+ # Same as `load_services_config` but forces the loading even if one has
61
+ # already been loaded
62
+ def self.load_services_config!(services_yaml)
63
+ @configuration = nil
64
+ load_services_config(services_yaml)
65
+ end
66
+
67
+ def self.load_environment
68
+ require File.join(Stealth.root, 'config', 'boot')
69
+ require_directory("config/initializers")
70
+ require_directory("bot")
71
+ end
72
+
73
+ private
74
+
75
+ def self.require_directory(directory)
76
+ for_each_file_in(directory) { |file| require_relative(file) }
77
+ end
78
+
79
+ def self.for_each_file_in(directory, &blk)
80
+ directory = directory.to_s.gsub(%r{(\/|\\)}, File::SEPARATOR)
81
+ directory = Pathname.new(Dir.pwd).join(directory).to_s
82
+ directory = File.join(directory, '**', '*.rb') unless directory =~ /(\*\*)/
83
+
84
+ Dir.glob(directory).sort.each(&blk)
85
+ end
86
+
87
+ end
@@ -0,0 +1,82 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'thor'
5
+ require 'stealth/cli_base'
6
+ require 'stealth/commands/console'
7
+
8
+ module Stealth
9
+ class Cli < Thor
10
+ extend CliBase
11
+
12
+ desc 'version', 'Prints stealth version'
13
+ long_desc <<-EOS
14
+ `stealth version` prints the version of the bundled stealth gem.
15
+ EOS
16
+ def version
17
+ require 'stealth/version'
18
+ puts "#{ Stealth::VERSION }"
19
+ end
20
+ map %w{--version -v} => :version
21
+
22
+
23
+ desc 'server', 'Starts a stealth server'
24
+ long_desc <<-EOS
25
+ `stealth server` starts a server for the current stealth project.
26
+
27
+ $ > stealth server
28
+
29
+ $ > stealth server -p 4500
30
+ EOS
31
+ method_option :port, aliases: '-p', desc: 'The port to run the server on'
32
+ method_option :server, desc: 'Choose a specific Rack::Handler (webrick, thin, etc)'
33
+ method_option :rackup, desc: 'A rackup configuration file path to load (config.ru)'
34
+ method_option :host, desc: 'The host address to bind to'
35
+ method_option :debug, desc: 'Turn on debug output'
36
+ method_option :warn, desc: 'Turn on warnings'
37
+ method_option :daemonize, desc: 'If true, the server will daemonize itself (fork, detach, etc)'
38
+ method_option :pid, desc: 'Path to write a pid file after daemonize'
39
+ method_option :environment, desc: 'Path to environment configuration (config/environment.rb)'
40
+ method_option :code_reloading, desc: 'Code reloading', type: :boolean, default: true
41
+ method_option :help, desc: 'Displays the usage message'
42
+ def server
43
+ if options[:help]
44
+ invoke :help, ['server']
45
+ else
46
+ require 'stealth/commands/server'
47
+ Stealth::Commands::Server.new(options).start
48
+ end
49
+ end
50
+
51
+
52
+ desc 'console', 'Starts a stealth console'
53
+ long_desc <<-EOS
54
+ `stealth console` starts the interactive stealth console.
55
+
56
+ $ > stealth console --engine=pry
57
+ EOS
58
+ method_option :environment, desc: 'Path to environment configuration (config/environment.rb)'
59
+ method_option :engine, desc: "Choose a specific console engine: (#{Stealth::Commands::Console::ENGINES.keys.join('/')})"
60
+ method_option :help, desc: 'Displays the usage method'
61
+ def console
62
+ if options[:help]
63
+ invoke :help, ['console']
64
+ else
65
+ Stealth::Commands::Console.new(options).start
66
+ end
67
+ end
68
+
69
+
70
+ desc 'setup', 'Runs setup tasks for a specified service'
71
+ long_desc <<-EOS
72
+ `stealth setup <service>` runs setup tasks for the specified service.
73
+
74
+ $ > stealth setup facebook
75
+ EOS
76
+ def setup(service)
77
+ Stealth.load_environment
78
+ service_setup_klass = "Stealth::Services::#{service.classify}::Setup".constantize
79
+ service_setup_klass.trigger
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Stealth
5
+ module CliBase
6
+ def define_commands(&blk)
7
+ class_eval(&blk) if block_given?
8
+ end
9
+
10
+ def banner(command, nspace = true, subcommand = false)
11
+ super(command, nspace, namespace != 'stealth:cli')
12
+ end
13
+
14
+ def handle_argument_error(command, error, args, arity)
15
+ name = [(namespace == 'stealth:cli' ? nil : namespace), command.name].compact.join(" ")
16
+
17
+ msg = "ERROR: \"#{basename} #{name}\" was called with "
18
+ msg << "no arguments" if args.empty?
19
+ msg << "arguments " << args.inspect unless args.empty?
20
+ msg << "\nUsage: #{banner(command).inspect}"
21
+
22
+ raise Thor::InvocationError, msg
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'stealth'
5
+
6
+ module Stealth
7
+ module Commands
8
+ class Command
9
+ def initialize(options)
10
+
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,75 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'stealth/commands/command'
5
+
6
+ module Stealth
7
+ module Commands
8
+ # REPL that supports different engines.
9
+ #
10
+ # It is run with:
11
+ #
12
+ # `bundle exec stealth console`
13
+ class Console < Command
14
+ module CodeReloading
15
+ def reload!
16
+ puts 'Reloading...'
17
+ Kernel.exec "#{$PROGRAM_NAME} console"
18
+ end
19
+ end
20
+
21
+ # Supported engines
22
+ ENGINES = {
23
+ 'pry' => 'Pry',
24
+ 'ripl' => 'Ripl',
25
+ 'irb' => 'IRB'
26
+ }.freeze
27
+
28
+ DEFAULT_ENGINE = ['irb'].freeze
29
+
30
+ attr_reader :options
31
+
32
+ def initialize(options)
33
+ super(options)
34
+
35
+ @options = options
36
+ end
37
+
38
+ def start
39
+ prepare
40
+ engine.start
41
+ end
42
+
43
+ def engine
44
+ load_engine options.fetch(:engine) { engine_lookup }
45
+ end
46
+
47
+ private
48
+
49
+ def prepare
50
+ # Clear out ARGV so Pry/IRB don't attempt to parse the rest
51
+ ARGV.shift until ARGV.empty?
52
+
53
+ # Add convenience methods to the main:Object binding
54
+ TOPLEVEL_BINDING.eval('self').__send__(:include, CodeReloading)
55
+
56
+ Stealth.load_environment
57
+ end
58
+
59
+ def engine_lookup
60
+ (ENGINES.find { |_, klass| Object.const_defined?(klass) } || DEFAULT_ENGINE).first
61
+ end
62
+
63
+ def load_engine(engine)
64
+ require engine
65
+ rescue LoadError
66
+ ensure
67
+ return Object.const_get(
68
+ ENGINES.fetch(engine) do
69
+ raise ArgumentError.new("Unknown console engine: `#{engine}'")
70
+ end
71
+ )
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,20 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'rack/handler/puma'
5
+ require 'stealth/commands/command'
6
+
7
+ module Stealth
8
+ module Commands
9
+ class Server < Command
10
+ def initialize(options)
11
+ super(options)
12
+ Stealth.load_environment
13
+ end
14
+
15
+ def start
16
+ Rack::Handler::Puma.run(Stealth::Server)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,54 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'thread'
5
+
6
+ module Stealth
7
+ class Configuration < Hash
8
+
9
+ def initialize(hash)
10
+ hash.each do |k, v|
11
+ self[k] = store(v)
12
+ end
13
+
14
+ self
15
+ end
16
+
17
+ def method_missing(method, *args)
18
+ key = create_config_attribute(method)
19
+
20
+ if setter?(method)
21
+ self[key] = args.first
22
+ else
23
+ super(method, args) && return unless key?(key)
24
+ self[key]
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def store(value)
31
+ if value.is_a?(Hash)
32
+ Stealth::Configuration.new(value)
33
+ else
34
+ value
35
+ end
36
+ end
37
+
38
+ def setter?(method)
39
+ method.slice(-1, 1) == "="
40
+ end
41
+
42
+ def create_config_attribute(method)
43
+ key = basic_config_attribute_from_method(method)
44
+
45
+ key?(key.to_s) ? key.to_s : key
46
+ end
47
+
48
+ def basic_config_attribute_from_method(method)
49
+ setter?(method) ? method.to_s.chop : method
50
+ end
51
+
52
+ end
53
+
54
+ end