stealth 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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