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