adhearsion 2.3.5 → 2.4.0.beta1
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/.travis.yml +0 -1
- data/CHANGELOG.md +14 -0
- data/Gemfile +2 -0
- data/README.markdown +21 -2
- data/adhearsion.gemspec +5 -4
- data/features/cli_plugin.feature +41 -0
- data/features/cli_start.feature +12 -4
- data/features/step_definitions/cli_steps.rb +12 -0
- data/features/support/env.rb +1 -1
- data/features/support/utils.rb +0 -1
- data/lib/adhearsion.rb +4 -1
- data/lib/adhearsion/call.rb +92 -22
- data/lib/adhearsion/call_controller.rb +19 -15
- data/lib/adhearsion/call_controller/dial.rb +157 -25
- data/lib/adhearsion/call_controller/menu_dsl/menu_builder.rb +8 -0
- data/lib/adhearsion/call_controller/output/async_player.rb +1 -1
- data/lib/adhearsion/call_controller/output/formatter.rb +1 -1
- data/lib/adhearsion/call_controller/output/player.rb +1 -1
- data/lib/adhearsion/calls.rb +2 -0
- data/lib/adhearsion/cli_commands.rb +3 -163
- data/lib/adhearsion/cli_commands/ahn_command.rb +141 -0
- data/lib/adhearsion/cli_commands/plugin_command.rb +74 -0
- data/lib/adhearsion/cli_commands/thor_errors.rb +36 -0
- data/lib/adhearsion/console.rb +14 -6
- data/lib/adhearsion/generators/app/templates/spec/call_controllers/simon_game_spec.rb +36 -36
- data/lib/adhearsion/generators/controller/templates/spec/controller_spec.rb +1 -1
- data/lib/adhearsion/generators/plugin/templates/plugin-template.gemspec.tt +0 -1
- data/lib/adhearsion/generators/plugin/templates/spec/plugin-template/controller_methods_spec.rb.tt +1 -1
- data/lib/adhearsion/generators/plugin/templates/spec/spec_helper.rb.tt +0 -1
- data/lib/adhearsion/logging.rb +5 -1
- data/lib/adhearsion/outbound_call.rb +16 -0
- data/lib/adhearsion/punchblock_plugin.rb +0 -2
- data/lib/adhearsion/punchblock_plugin/initializer.rb +7 -12
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/dial_spec.rb +785 -32
- data/spec/adhearsion/call_controller/menu_dsl/menu_builder_spec.rb +10 -0
- data/spec/adhearsion/call_controller/output/async_player_spec.rb +1 -1
- data/spec/adhearsion/call_controller/output/player_spec.rb +1 -1
- data/spec/adhearsion/call_controller/output_spec.rb +3 -3
- data/spec/adhearsion/call_controller/record_spec.rb +1 -1
- data/spec/adhearsion/call_controller_spec.rb +13 -9
- data/spec/adhearsion/call_spec.rb +216 -51
- data/spec/adhearsion/calls_spec.rb +1 -1
- data/spec/adhearsion/console_spec.rb +20 -9
- data/spec/adhearsion/outbound_call_spec.rb +40 -6
- data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +9 -21
- data/spec/adhearsion/punchblock_plugin_spec.rb +1 -1
- data/spec/adhearsion/router_spec.rb +1 -1
- data/spec/spec_helper.rb +11 -15
- data/spec/support/call_controller_test_helpers.rb +2 -2
- data/spec/support/punchblock_mocks.rb +2 -2
- metadata +41 -16
@@ -0,0 +1,141 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
module CLI
|
5
|
+
class AhnCommand < Thor
|
6
|
+
map %w(-h --h -help --help) => :help
|
7
|
+
map %w(-v --v -version --version) => :version
|
8
|
+
map %w(-) => :start
|
9
|
+
|
10
|
+
register ::Adhearsion::CLI::PluginCommand, 'plugin', 'plugin <command>', 'Plugin Tasks.'
|
11
|
+
|
12
|
+
check_unknown_options!
|
13
|
+
|
14
|
+
def self.exit_on_failure?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "create /path/to/directory", "Create a new Adhearsion application under the given path"
|
19
|
+
def create(path)
|
20
|
+
require 'adhearsion/generators/app/app_generator'
|
21
|
+
Generators::AppGenerator.start
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "generate [generator_name] arguments", Generators.help
|
25
|
+
def generate(generator_name = nil, *args)
|
26
|
+
if generator_name
|
27
|
+
Generators.invoke generator_name
|
28
|
+
else
|
29
|
+
help 'generate'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "version", "Shows Adhearsion version"
|
34
|
+
def version
|
35
|
+
say "Adhearsion v#{Adhearsion::VERSION}"
|
36
|
+
exit 0
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "start </path/to/directory>", "Start the Adhearsion server in the foreground with a console"
|
40
|
+
method_option :noconsole, type: :boolean, aliases: %w{--no-console}
|
41
|
+
def start(path = nil)
|
42
|
+
start_app path, options[:noconsole] ? :simple : :console
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "daemon </path/to/directory>", "Start the Adhearsion server in the background"
|
46
|
+
method_option :pidfile, :type => :string, :aliases => %w(--pid-file)
|
47
|
+
def daemon(path = nil)
|
48
|
+
start_app path, :daemon, options[:pidfile]
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "stop </path/to/directory>", "Stop a running Adhearsion server"
|
52
|
+
method_option :pidfile, :type => :string, :aliases => %w(--pid-file)
|
53
|
+
def stop(path = nil)
|
54
|
+
execute_from_app_dir! path
|
55
|
+
|
56
|
+
pid_file = if options[:pidfile]
|
57
|
+
File.exists?(File.expand_path(options[:pidfile])) ?
|
58
|
+
options[:pidfile] :
|
59
|
+
File.join(path, options[:pidfile])
|
60
|
+
else
|
61
|
+
path = Dir.pwd
|
62
|
+
File.join path, Adhearsion::Initializer::DEFAULT_PID_FILE_NAME
|
63
|
+
end
|
64
|
+
pid_file = File.expand_path pid_file
|
65
|
+
|
66
|
+
begin
|
67
|
+
pid = File.read(pid_file).to_i
|
68
|
+
rescue
|
69
|
+
raise PIDReadError, pid_file
|
70
|
+
end
|
71
|
+
|
72
|
+
raise PIDReadError, pid_file if pid.nil?
|
73
|
+
|
74
|
+
say "Stopping Adhearsion server at #{path} with pid #{pid}"
|
75
|
+
waiting_timeout = Time.now + 15
|
76
|
+
begin
|
77
|
+
::Process.kill "TERM", pid
|
78
|
+
sleep 0.25 while process_exists?(pid) && Time.now < waiting_timeout
|
79
|
+
::Process.kill "KILL", pid
|
80
|
+
rescue Errno::ESRCH
|
81
|
+
end
|
82
|
+
|
83
|
+
File.delete pid_file if File.exists? pid_file
|
84
|
+
end
|
85
|
+
|
86
|
+
desc "restart </path/to/directory>", "Restart the Adhearsion server"
|
87
|
+
method_option :pidfile, :type => :string, :aliases => %w(--pid-file)
|
88
|
+
def restart(path = nil)
|
89
|
+
execute_from_app_dir! path
|
90
|
+
begin
|
91
|
+
invoke :stop
|
92
|
+
rescue PIDReadError => e
|
93
|
+
puts e.message
|
94
|
+
end
|
95
|
+
invoke :daemon
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def start_app(path, mode, pid_file = nil)
|
101
|
+
execute_from_app_dir! path
|
102
|
+
say "Starting Adhearsion server at #{Dir.pwd}"
|
103
|
+
Adhearsion::Initializer.start :mode => mode, :pid_file => pid_file
|
104
|
+
end
|
105
|
+
|
106
|
+
def execute_from_app_dir!(path)
|
107
|
+
return if in_app? and running_script_ahn?
|
108
|
+
|
109
|
+
path ||= Dir.pwd if in_app?
|
110
|
+
|
111
|
+
raise PathRequired, ARGV[0] if path.nil? or path.empty?
|
112
|
+
|
113
|
+
Dir.chdir path do
|
114
|
+
raise PathInvalid, path unless ScriptAhnLoader.in_ahn_application?
|
115
|
+
args = ARGV.dup
|
116
|
+
args[1] = '.'
|
117
|
+
ScriptAhnLoader.exec_script_ahn! args
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def running_script_ahn?
|
122
|
+
$0.to_s == "script/ahn"
|
123
|
+
end
|
124
|
+
|
125
|
+
def in_app?
|
126
|
+
ScriptAhnLoader.in_ahn_application? or ScriptAhnLoader.in_ahn_application_subdirectory?
|
127
|
+
end
|
128
|
+
|
129
|
+
def process_exists?(pid = nil)
|
130
|
+
# FIXME: Raise some error here
|
131
|
+
return false if pid.nil?
|
132
|
+
`ps -p #{pid} | sed -e '1d'`.strip.empty?
|
133
|
+
end
|
134
|
+
|
135
|
+
def method_missing(action, *args)
|
136
|
+
help
|
137
|
+
raise UnknownCommand, [action, *args] * " "
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
module CLI
|
5
|
+
class PluginCommand < Thor
|
6
|
+
|
7
|
+
namespace :plugin
|
8
|
+
|
9
|
+
desc "create_github_hook", "Creates ahnhub hook to track github commits"
|
10
|
+
def create_github_hook
|
11
|
+
get_github_vals
|
12
|
+
generate_github_webhook
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "create_rubygem_hook", "Creates ahnhub hook to track rubygem updates"
|
16
|
+
def create_rubygem_hook
|
17
|
+
get_rubygem_vals
|
18
|
+
|
19
|
+
puts `curl -H 'Authorization:#{ENV['RUBYGEM_AUTH']}' \
|
20
|
+
-F 'gem_name=#{ENV['RUBYGEM_NAME']}' \
|
21
|
+
-F 'url=http://www.ahnhub.com/gem' \
|
22
|
+
https://rubygems.org/api/v1/web_hooks/fire`
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "create_ahnhub_hooks", "Creates ahnhub hooks for both a rubygem and github repo"
|
26
|
+
def create_ahnhub_hooks
|
27
|
+
create_github_hooks
|
28
|
+
create_rubygem_hooks
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def get_rubygem_vals
|
34
|
+
ENV['RUBYGEM_NAME'] ||= ask "What's the rubygem name?"
|
35
|
+
ENV['RUBYGEM_AUTH'] ||= ask "What's your authorization key for Rubygems?"
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_github_vals
|
39
|
+
ENV['GITHUB_USERNAME'] ||= ask "What's your github username?"
|
40
|
+
ENV['GITHUB_PASSWORD'] ||= ask "What's your github password?"
|
41
|
+
ENV['GITHUB_REPO'] ||= ask "Please enter the owner and repo (for example, 'adhearsion/new-plugin'): "
|
42
|
+
end
|
43
|
+
|
44
|
+
def github_repo_owner
|
45
|
+
ENV['GITHUB_REPO'].split('/')[0]
|
46
|
+
end
|
47
|
+
|
48
|
+
def github_repo_name
|
49
|
+
ENV['GITHUB_REPO'].split('/')[1]
|
50
|
+
end
|
51
|
+
|
52
|
+
def generate_github_webhook
|
53
|
+
require 'net/http'
|
54
|
+
|
55
|
+
uri = URI("https://api.github.com/repos/#{github_repo_owner}/#{github_repo_name}/hooks")
|
56
|
+
req = Net::HTTP::Post.new(uri.to_s)
|
57
|
+
|
58
|
+
req.basic_auth ENV['GITHUB_USERNAME'], ENV['GITHUB_PASSWORD']
|
59
|
+
req.body = {
|
60
|
+
name: "web",
|
61
|
+
active: true,
|
62
|
+
events: ["push", "pull_request"],
|
63
|
+
config: {url: "http://ahnhub.com/github"}
|
64
|
+
}.to_json
|
65
|
+
|
66
|
+
req["content-type"] = "application/json"
|
67
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
|
68
|
+
response = http.request(req)
|
69
|
+
puts response.body
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
module CLI
|
5
|
+
class UnknownCommand < Thor::Error
|
6
|
+
def initialize(cmd)
|
7
|
+
super "Unknown command: #{cmd}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class PathRequired < Thor::Error
|
12
|
+
def initialize(cmd)
|
13
|
+
super "A valid path is required for #{cmd}, unless run from an Adhearson app directory"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class UnknownGeneratorError < Thor::Error
|
18
|
+
def initialize(gentype)
|
19
|
+
puts "Please specify generator to use (#{Adhearsion::Generators.mappings.keys.join(", ")})"
|
20
|
+
super "Unknown command: #{gentype}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class PathInvalid < Thor::Error
|
25
|
+
def initialize(path)
|
26
|
+
super "Directory #{path} does not belong to an Adhearsion project!"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class PIDReadError < Thor::Error
|
31
|
+
def initialize(path)
|
32
|
+
super "Could not read pid from the file #{path}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/adhearsion/console.rb
CHANGED
@@ -32,13 +32,13 @@ module Adhearsion
|
|
32
32
|
def run
|
33
33
|
set_prompt
|
34
34
|
Pry.config.command_prefix = "%"
|
35
|
-
if
|
36
|
-
logger.error "Cannot start. You are running Adhearsion on Ruby with libedit. You must use readline for the console to work."
|
37
|
-
else
|
35
|
+
if jruby? || cruby_with_readline?
|
38
36
|
logger.info "Launching Adhearsion Console"
|
39
37
|
@pry_thread = Thread.current
|
40
38
|
pry
|
41
39
|
logger.info "Adhearsion Console exiting"
|
40
|
+
else
|
41
|
+
logger.error "Unable to launch Adhearsion Console: This version of Ruby is using libedit. You must use readline for the console to work."
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -65,6 +65,10 @@ module Adhearsion
|
|
65
65
|
Adhearsion.active_calls
|
66
66
|
end
|
67
67
|
|
68
|
+
def originate(*args, &block)
|
69
|
+
Adhearsion::OutboundCall.originate(*args, &block)
|
70
|
+
end
|
71
|
+
|
68
72
|
def take(call = nil)
|
69
73
|
case call
|
70
74
|
when Call
|
@@ -101,16 +105,20 @@ module Adhearsion
|
|
101
105
|
pry
|
102
106
|
end
|
103
107
|
|
104
|
-
def
|
108
|
+
def cruby_with_readline?
|
105
109
|
begin
|
106
110
|
# If NotImplemented then this might be libedit
|
107
111
|
Readline.emacs_editing_mode
|
108
|
-
false
|
109
|
-
rescue NotImplementedError
|
110
112
|
true
|
113
|
+
rescue NotImplementedError
|
114
|
+
false
|
111
115
|
end
|
112
116
|
end
|
113
117
|
|
118
|
+
def jruby?
|
119
|
+
defined? JRUBY_VERSION
|
120
|
+
end
|
121
|
+
|
114
122
|
private
|
115
123
|
|
116
124
|
def set_prompt
|
@@ -9,56 +9,56 @@ describe SimonGame do
|
|
9
9
|
let(:long_response) { OpenStruct.new(:response => "55555") }
|
10
10
|
let(:long_number) { "55555" }
|
11
11
|
|
12
|
-
let(:mock_call) {
|
12
|
+
let(:mock_call) { double 'Call' }
|
13
13
|
subject { SimonGame.new(mock_call) }
|
14
14
|
|
15
|
-
describe "#random_number" do
|
15
|
+
describe "#random_number" do
|
16
16
|
|
17
17
|
before { subject.stub!(:rand).and_return(example_number) }
|
18
18
|
|
19
|
-
it "generates a random number" do
|
20
|
-
subject.random_number.should eq example_number
|
19
|
+
it "generates a random number" do
|
20
|
+
subject.random_number.should eq example_number
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
describe "#update_number" do
|
24
|
+
describe "#update_number" do
|
25
25
|
|
26
26
|
before { subject.number = "123" }
|
27
27
|
before { subject.stub!(:random_number).and_return "4" }
|
28
28
|
|
29
|
-
it "adds a digit to the end of the number" do
|
29
|
+
it "adds a digit to the end of the number" do
|
30
30
|
subject.update_number
|
31
31
|
subject.number.should eq "1234"
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
describe "#collect_attempt" do
|
35
|
+
describe "#collect_attempt" do
|
36
|
+
|
37
|
+
context "when the @number is 1 digit long" do
|
36
38
|
|
37
|
-
context "when the @number is 1 digit long" do
|
38
|
-
|
39
39
|
before { subject.number = "3" }
|
40
40
|
|
41
|
-
it "asks for a 1 digits number" do
|
41
|
+
it "asks for a 1 digits number" do
|
42
42
|
subject.should_receive(:ask).with("3", :limit => 1).and_return(example_response)
|
43
43
|
subject.collect_attempt
|
44
|
-
end
|
44
|
+
end
|
45
45
|
end
|
46
46
|
|
47
|
-
context "when the @number is 5 digits long" do
|
48
|
-
|
47
|
+
context "when the @number is 5 digits long" do
|
48
|
+
|
49
49
|
before { subject.number = long_number }
|
50
50
|
|
51
|
-
it "asks for a 5 digits number" do
|
51
|
+
it "asks for a 5 digits number" do
|
52
52
|
subject.should_receive(:ask).with(long_number, :limit => 5).and_return(long_response)
|
53
53
|
subject.collect_attempt
|
54
|
-
end
|
54
|
+
end
|
55
55
|
end
|
56
56
|
|
57
57
|
context "sets @attempt" do
|
58
58
|
|
59
59
|
before { subject.number = "12345" }
|
60
60
|
|
61
|
-
it "based on the user's response" do
|
61
|
+
it "based on the user's response" do
|
62
62
|
subject.should_receive(:ask).with("12345", :limit => 5).and_return(long_response)
|
63
63
|
subject.collect_attempt
|
64
64
|
subject.attempt.should eq long_number
|
@@ -66,67 +66,67 @@ describe SimonGame do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
describe "#attempt_correct?" do
|
70
|
-
|
69
|
+
describe "#attempt_correct?" do
|
70
|
+
|
71
71
|
before { subject.number = "7" }
|
72
72
|
|
73
|
-
context "with a good attempt" do
|
73
|
+
context "with a good attempt" do
|
74
74
|
|
75
75
|
before { subject.attempt = "7" }
|
76
76
|
|
77
|
-
it "returns true" do
|
77
|
+
it "returns true" do
|
78
78
|
subject.attempt_correct?.should be_true
|
79
79
|
end
|
80
|
-
end
|
80
|
+
end
|
81
81
|
|
82
|
-
context "with a bad attempt" do
|
82
|
+
context "with a bad attempt" do
|
83
83
|
|
84
84
|
before { subject.attempt = "9" }
|
85
85
|
|
86
|
-
it "returns true" do
|
86
|
+
it "returns true" do
|
87
87
|
subject.attempt_correct?.should be_false
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
-
describe "#verify_attempt" do
|
93
|
-
context "when the user is a good guesser" do
|
92
|
+
describe "#verify_attempt" do
|
93
|
+
context "when the user is a good guesser" do
|
94
94
|
|
95
95
|
before { subject.stub!(:attempt_correct?).and_return true }
|
96
96
|
|
97
|
-
it "congradulates them" do
|
97
|
+
it "congradulates them" do
|
98
98
|
subject.should_receive(:speak).with('good')
|
99
99
|
subject.verify_attempt
|
100
100
|
end
|
101
|
-
end
|
101
|
+
end
|
102
102
|
|
103
|
-
context "when the user guesses wrong" do
|
103
|
+
context "when the user guesses wrong" do
|
104
104
|
|
105
105
|
before { subject.number = "12345" }
|
106
106
|
before { subject.attempt = "12346" }
|
107
107
|
|
108
|
-
it "congradulates them" do
|
108
|
+
it "congradulates them" do
|
109
109
|
subject.should_receive(:speak).with('4 times wrong, try again smarty')
|
110
110
|
subject.verify_attempt
|
111
111
|
end
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
115
|
-
describe "#reset" do
|
115
|
+
describe "#reset" do
|
116
116
|
|
117
117
|
before { subject.reset }
|
118
118
|
|
119
|
-
it "sets @number" do
|
119
|
+
it "sets @number" do
|
120
120
|
subject.number.should eq ''
|
121
121
|
end
|
122
122
|
|
123
|
-
it "sets @attempt" do
|
123
|
+
it "sets @attempt" do
|
124
124
|
subject.attempt.should eq ''
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
128
|
-
describe "#run" do
|
129
|
-
it "loops the loop" do
|
128
|
+
describe "#run" do
|
129
|
+
it "loops the loop" do
|
130
130
|
subject.should_receive :answer
|
131
131
|
subject.should_receive :reset
|
132
132
|
|
@@ -134,9 +134,9 @@ describe SimonGame do
|
|
134
134
|
subject.should_receive :collect_attempt
|
135
135
|
subject.should_receive(:verify_attempt).and_throw :rspec_loop_stop
|
136
136
|
|
137
|
-
catch :rspec_loop_stop do
|
137
|
+
catch :rspec_loop_stop do
|
138
138
|
subject.run
|
139
139
|
end
|
140
140
|
end
|
141
141
|
end
|
142
|
-
end
|
142
|
+
end
|