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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/CHANGELOG.md +14 -0
  4. data/Gemfile +2 -0
  5. data/README.markdown +21 -2
  6. data/adhearsion.gemspec +5 -4
  7. data/features/cli_plugin.feature +41 -0
  8. data/features/cli_start.feature +12 -4
  9. data/features/step_definitions/cli_steps.rb +12 -0
  10. data/features/support/env.rb +1 -1
  11. data/features/support/utils.rb +0 -1
  12. data/lib/adhearsion.rb +4 -1
  13. data/lib/adhearsion/call.rb +92 -22
  14. data/lib/adhearsion/call_controller.rb +19 -15
  15. data/lib/adhearsion/call_controller/dial.rb +157 -25
  16. data/lib/adhearsion/call_controller/menu_dsl/menu_builder.rb +8 -0
  17. data/lib/adhearsion/call_controller/output/async_player.rb +1 -1
  18. data/lib/adhearsion/call_controller/output/formatter.rb +1 -1
  19. data/lib/adhearsion/call_controller/output/player.rb +1 -1
  20. data/lib/adhearsion/calls.rb +2 -0
  21. data/lib/adhearsion/cli_commands.rb +3 -163
  22. data/lib/adhearsion/cli_commands/ahn_command.rb +141 -0
  23. data/lib/adhearsion/cli_commands/plugin_command.rb +74 -0
  24. data/lib/adhearsion/cli_commands/thor_errors.rb +36 -0
  25. data/lib/adhearsion/console.rb +14 -6
  26. data/lib/adhearsion/generators/app/templates/spec/call_controllers/simon_game_spec.rb +36 -36
  27. data/lib/adhearsion/generators/controller/templates/spec/controller_spec.rb +1 -1
  28. data/lib/adhearsion/generators/plugin/templates/plugin-template.gemspec.tt +0 -1
  29. data/lib/adhearsion/generators/plugin/templates/spec/plugin-template/controller_methods_spec.rb.tt +1 -1
  30. data/lib/adhearsion/generators/plugin/templates/spec/spec_helper.rb.tt +0 -1
  31. data/lib/adhearsion/logging.rb +5 -1
  32. data/lib/adhearsion/outbound_call.rb +16 -0
  33. data/lib/adhearsion/punchblock_plugin.rb +0 -2
  34. data/lib/adhearsion/punchblock_plugin/initializer.rb +7 -12
  35. data/lib/adhearsion/version.rb +1 -1
  36. data/spec/adhearsion/call_controller/dial_spec.rb +785 -32
  37. data/spec/adhearsion/call_controller/menu_dsl/menu_builder_spec.rb +10 -0
  38. data/spec/adhearsion/call_controller/output/async_player_spec.rb +1 -1
  39. data/spec/adhearsion/call_controller/output/player_spec.rb +1 -1
  40. data/spec/adhearsion/call_controller/output_spec.rb +3 -3
  41. data/spec/adhearsion/call_controller/record_spec.rb +1 -1
  42. data/spec/adhearsion/call_controller_spec.rb +13 -9
  43. data/spec/adhearsion/call_spec.rb +216 -51
  44. data/spec/adhearsion/calls_spec.rb +1 -1
  45. data/spec/adhearsion/console_spec.rb +20 -9
  46. data/spec/adhearsion/outbound_call_spec.rb +40 -6
  47. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +9 -21
  48. data/spec/adhearsion/punchblock_plugin_spec.rb +1 -1
  49. data/spec/adhearsion/router_spec.rb +1 -1
  50. data/spec/spec_helper.rb +11 -15
  51. data/spec/support/call_controller_test_helpers.rb +2 -2
  52. data/spec/support/punchblock_mocks.rb +2 -2
  53. 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
@@ -32,13 +32,13 @@ module Adhearsion
32
32
  def run
33
33
  set_prompt
34
34
  Pry.config.command_prefix = "%"
35
- if libedit?
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 libedit?
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) { 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