adhearsion 2.0.0.rc4 → 2.0.0.rc5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -0
- data/adhearsion.gemspec +1 -1
- data/bin/ahn +0 -3
- data/lib/adhearsion.rb +7 -4
- data/lib/adhearsion/call.rb +1 -1
- data/lib/adhearsion/call_controller.rb +1 -0
- data/lib/adhearsion/call_controller/menu_dsl.rb +19 -0
- data/lib/adhearsion/call_controller/menu_dsl/calculated_match.rb +43 -0
- data/lib/adhearsion/call_controller/menu_dsl/calculated_match_collection.rb +45 -0
- data/lib/adhearsion/call_controller/menu_dsl/fixnum_match_calculator.rb +22 -0
- data/lib/adhearsion/call_controller/menu_dsl/match_calculator.rb +40 -0
- data/lib/adhearsion/call_controller/menu_dsl/menu.rb +203 -0
- data/lib/adhearsion/call_controller/menu_dsl/menu_builder.rb +84 -0
- data/lib/adhearsion/call_controller/menu_dsl/range_match_calculator.rb +60 -0
- data/lib/adhearsion/call_controller/menu_dsl/string_match_calculator.rb +25 -0
- data/lib/adhearsion/cli.rb +0 -1
- data/lib/adhearsion/router/route.rb +2 -2
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/input_spec.rb +1 -1
- data/spec/adhearsion/call_controller/menu_dsl/calculated_match_collection_spec.rb +60 -0
- data/spec/adhearsion/call_controller/menu_dsl/calculated_match_spec.rb +61 -0
- data/spec/adhearsion/call_controller/menu_dsl/fixnum_match_calculator_spec.rb +37 -0
- data/spec/adhearsion/call_controller/menu_dsl/match_calculator_spec.rb +17 -0
- data/spec/adhearsion/call_controller/menu_dsl/menu_builder_spec.rb +151 -0
- data/spec/adhearsion/call_controller/menu_dsl/menu_spec.rb +373 -0
- data/spec/adhearsion/call_controller/menu_dsl/range_match_calculator_spec.rb +32 -0
- data/spec/adhearsion/call_controller/menu_dsl/string_match_calculator_spec.rb +40 -0
- metadata +91 -91
- data/lib/adhearsion/menu_dsl.rb +0 -17
- data/lib/adhearsion/menu_dsl/calculated_match.rb +0 -41
- data/lib/adhearsion/menu_dsl/calculated_match_collection.rb +0 -43
- data/lib/adhearsion/menu_dsl/fixnum_match_calculator.rb +0 -20
- data/lib/adhearsion/menu_dsl/match_calculator.rb +0 -38
- data/lib/adhearsion/menu_dsl/menu.rb +0 -201
- data/lib/adhearsion/menu_dsl/menu_builder.rb +0 -82
- data/lib/adhearsion/menu_dsl/range_match_calculator.rb +0 -58
- data/lib/adhearsion/menu_dsl/string_match_calculator.rb +0 -23
- data/spec/adhearsion/menu_dsl/calculated_match_collection_spec.rb +0 -58
- data/spec/adhearsion/menu_dsl/calculated_match_spec.rb +0 -59
- data/spec/adhearsion/menu_dsl/fixnum_match_calculator_spec.rb +0 -35
- data/spec/adhearsion/menu_dsl/match_calculator_spec.rb +0 -15
- data/spec/adhearsion/menu_dsl/menu_builder_spec.rb +0 -149
- data/spec/adhearsion/menu_dsl/menu_spec.rb +0 -371
- data/spec/adhearsion/menu_dsl/range_match_calculator_spec.rb +0 -30
- data/spec/adhearsion/menu_dsl/string_match_calculator_spec.rb +0 -38
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# [develop](https://github.com/adhearsion/adhearsion)
|
2
2
|
|
3
|
+
# [2.0.0.rc5](https://github.com/adhearsion/adhearsion/compare/v2.0.0.rc4...v2.0.0.rc5) - [2012-04-06](https://rubygems.org/gems/adhearsion/versions/2.0.0.rc5)
|
4
|
+
* Bugfix: Fix environment abuse in ahn CLI
|
5
|
+
* Bugfix: Celluloid log messages enter the adhearsion logger
|
6
|
+
|
3
7
|
# [2.0.0.rc4](https://github.com/adhearsion/adhearsion/compare/v2.0.0.rc3...v2.0.0.rc4) - [2012-03-30](https://rubygems.org/gems/adhearsion/versions/2.0.0.rc4)
|
4
8
|
* Feature: `Call#execute_controller` now takes a post-execution callback (proc)
|
5
9
|
* Feature: App generator now includes directory scaffolding for call controller specs and a sample `spec_helper.rb` which loads app config and the `lib/` directory
|
data/adhearsion.gemspec
CHANGED
@@ -40,7 +40,7 @@ Gem::Specification.new do |s|
|
|
40
40
|
s.add_runtime_dependency 'girl_friday'
|
41
41
|
s.add_runtime_dependency 'jruby-openssl' if RUBY_PLATFORM == 'java'
|
42
42
|
s.add_runtime_dependency 'ffi', [">= 1.0.11"]
|
43
|
-
s.add_runtime_dependency 'celluloid', [">= 0.
|
43
|
+
s.add_runtime_dependency 'celluloid', [">= 0.10.0"]
|
44
44
|
s.add_runtime_dependency 'deep_merge'
|
45
45
|
|
46
46
|
# Development dependencies
|
data/bin/ahn
CHANGED
@@ -19,9 +19,6 @@
|
|
19
19
|
# with this library; if not, write to the Free Software Foundation, Inc.,
|
20
20
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
21
21
|
|
22
|
-
$:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
|
23
|
-
|
24
|
-
require 'rubygems'
|
25
22
|
require 'adhearsion/cli'
|
26
23
|
|
27
24
|
Adhearsion::CLI::AhnCommand.start
|
data/lib/adhearsion.rb
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
abort "ERROR: You are running Adhearsion on an unsupported version of Ruby (Ruby #{RUBY_VERSION} #{RUBY_RELEASE_DATE})! Please upgrade to at least Ruby v1.9.2, JRuby 1.6.5 or Rubinius 2.0." if RUBY_VERSION < "1.9.2"
|
4
4
|
|
5
5
|
%w{
|
6
|
-
bundler/setup
|
7
|
-
|
8
6
|
active_support/all
|
9
7
|
uuid
|
10
8
|
future-resource
|
@@ -36,7 +34,6 @@ module Adhearsion
|
|
36
34
|
autoload :Dispatcher
|
37
35
|
autoload :Events
|
38
36
|
autoload :Generators
|
39
|
-
autoload :MenuDSL
|
40
37
|
autoload :Initializer
|
41
38
|
autoload :Logging
|
42
39
|
autoload :OutboundCall
|
@@ -81,7 +78,7 @@ module Adhearsion
|
|
81
78
|
end
|
82
79
|
|
83
80
|
def active_calls
|
84
|
-
if
|
81
|
+
if instance_variable_defined?(:@calls) && @calls.alive?
|
85
82
|
@calls
|
86
83
|
else
|
87
84
|
@calls = Calls.new
|
@@ -95,3 +92,9 @@ module Adhearsion
|
|
95
92
|
end
|
96
93
|
|
97
94
|
Celluloid.exception_handler { |e| Adhearsion::Events.trigger :exception, e }
|
95
|
+
|
96
|
+
module Celluloid
|
97
|
+
def self.logger
|
98
|
+
::Logging.logger['Celluloid']
|
99
|
+
end
|
100
|
+
end
|
data/lib/adhearsion/call.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
class CallController
|
5
|
+
module MenuDSL
|
6
|
+
extend ActiveSupport::Autoload
|
7
|
+
|
8
|
+
autoload :Exceptions
|
9
|
+
autoload :CalculatedMatch
|
10
|
+
autoload :CalculatedMatchCollection
|
11
|
+
autoload :MatchCalculator
|
12
|
+
autoload :FixnumMatchCalculator
|
13
|
+
autoload :RangeMatchCalculator
|
14
|
+
autoload :StringMatchCalculator
|
15
|
+
autoload :MenuBuilder
|
16
|
+
autoload :Menu
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
class CallController
|
5
|
+
module MenuDSL
|
6
|
+
class CalculatedMatch
|
7
|
+
|
8
|
+
def self.failed_match!(pattern, query, match_payload)
|
9
|
+
new :pattern => pattern, :query => query, :match_payload => match_payload
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :match_payload, :potential_matches, :exact_matches, :pattern, :query, :block
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
@pattern, @query, @match_payload, @block = options.values_at :pattern, :query, :match_payload, :block
|
16
|
+
@potential_matches = options[:potential_matches] ? Array(options[:potential_matches]) : []
|
17
|
+
@exact_matches = options[:exact_matches] ? Array(options[:exact_matches]) : []
|
18
|
+
end
|
19
|
+
|
20
|
+
def exact_match?
|
21
|
+
exact_matches.any?
|
22
|
+
end
|
23
|
+
|
24
|
+
def potential_match?
|
25
|
+
potential_matches.any?
|
26
|
+
end
|
27
|
+
|
28
|
+
def failed_match?
|
29
|
+
!(potential_match? || exact_match?)
|
30
|
+
end
|
31
|
+
|
32
|
+
def type_of_match
|
33
|
+
if exact_match?
|
34
|
+
:exact
|
35
|
+
elsif potential_match?
|
36
|
+
:potential
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
class CallController
|
5
|
+
module MenuDSL
|
6
|
+
class CalculatedMatchCollection
|
7
|
+
attr_reader :calculated_matches, :potential_matches, :exact_matches,
|
8
|
+
:actual_potential_matches, :actual_exact_matches
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@calculated_matches = []
|
12
|
+
@potential_matches = []
|
13
|
+
@exact_matches = []
|
14
|
+
@actual_potential_matches = []
|
15
|
+
@actual_exact_matches = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def <<(calculated_match)
|
19
|
+
calculated_matches << calculated_match
|
20
|
+
actual_potential_matches.concat calculated_match.potential_matches
|
21
|
+
actual_exact_matches.concat calculated_match.exact_matches
|
22
|
+
|
23
|
+
potential_matches << calculated_match if calculated_match.potential_match?
|
24
|
+
exact_matches << calculated_match if calculated_match.exact_match?
|
25
|
+
end
|
26
|
+
|
27
|
+
def potential_match_count
|
28
|
+
actual_potential_matches.size
|
29
|
+
end
|
30
|
+
|
31
|
+
def exact_match_count
|
32
|
+
actual_exact_matches.size
|
33
|
+
end
|
34
|
+
|
35
|
+
def potential_match?
|
36
|
+
potential_match_count > 0
|
37
|
+
end
|
38
|
+
|
39
|
+
def exact_match?
|
40
|
+
exact_match_count > 0
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
class CallController
|
5
|
+
module MenuDSL
|
6
|
+
class FixnumMatchCalculator < MatchCalculator
|
7
|
+
|
8
|
+
def match(query)
|
9
|
+
numeric_query = coerce_to_numeric query
|
10
|
+
exact_match, potential_match = nil
|
11
|
+
if pattern == numeric_query
|
12
|
+
exact_match = pattern
|
13
|
+
elsif pattern.to_s.starts_with? query.to_s
|
14
|
+
potential_match = pattern
|
15
|
+
end
|
16
|
+
new_calculated_match :query => query, :exact_matches => exact_match, :potential_matches => potential_match
|
17
|
+
end
|
18
|
+
|
19
|
+
end # class FixnumMatchCalculator
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
class CallController
|
5
|
+
module MenuDSL
|
6
|
+
class MatchCalculator
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def build_with_pattern(pattern, match_payload, &block)
|
11
|
+
class_for_pattern(pattern).new pattern, match_payload, &block
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def class_for_pattern(pattern)
|
17
|
+
MenuDSL.const_get "#{pattern.class.name.camelize}MatchCalculator"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :pattern, :match_payload, :block
|
22
|
+
|
23
|
+
def initialize(pattern, match_payload, &block)
|
24
|
+
@pattern, @match_payload, @block = pattern, match_payload, block
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def new_calculated_match(options)
|
30
|
+
CalculatedMatch.new({:pattern => pattern, :match_payload => match_payload, :block => block}.merge(options))
|
31
|
+
end
|
32
|
+
|
33
|
+
def coerce_to_numeric(victim)
|
34
|
+
victim.kind_of?(Numeric) ? victim : (victim.to_s =~ /^\d+$/ ? victim.to_s.to_i : nil )
|
35
|
+
end
|
36
|
+
|
37
|
+
end # class MatchCalculator
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
class CallController
|
5
|
+
module MenuDSL
|
6
|
+
|
7
|
+
class Menu
|
8
|
+
|
9
|
+
DEFAULT_MAX_NUMBER_OF_TRIES = 1
|
10
|
+
DEFAULT_TIMEOUT = 5
|
11
|
+
|
12
|
+
InvalidStructureError = Class.new Adhearsion::Error
|
13
|
+
|
14
|
+
attr_reader :builder, :timeout, :tries_count, :max_number_of_tries, :terminator, :limit, :interruptible, :status
|
15
|
+
|
16
|
+
def initialize(options = {}, &block)
|
17
|
+
@tries_count = 0 # Counts the number of tries the menu's been executed
|
18
|
+
@timeout = options[:timeout] || DEFAULT_TIMEOUT
|
19
|
+
@max_number_of_tries = options[:tries] || DEFAULT_MAX_NUMBER_OF_TRIES
|
20
|
+
@terminator = options[:terminator].to_s
|
21
|
+
@limit = options[:limit]
|
22
|
+
@interruptible = options.has_key?(:interruptible) ? options[:interruptible] : true
|
23
|
+
@builder = MenuDSL::MenuBuilder.new
|
24
|
+
@terminated = false
|
25
|
+
|
26
|
+
@builder.build(&block) if block
|
27
|
+
|
28
|
+
initialize_digit_buffer
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate(mode = nil)
|
32
|
+
case mode
|
33
|
+
when :basic
|
34
|
+
@terminator.present? || !!@limit || raise(InvalidStructureError, "You must specify at least one of limit or terminator")
|
35
|
+
else
|
36
|
+
@builder.has_matchers? || raise(InvalidStructureError, "You must specify one or more matchers")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def <<(other)
|
41
|
+
if other == terminator
|
42
|
+
@terminated = true
|
43
|
+
else
|
44
|
+
digit_buffer << other
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def digit_buffer
|
49
|
+
@digit_buffer
|
50
|
+
end
|
51
|
+
|
52
|
+
def digit_buffer_string
|
53
|
+
digit_buffer.to_s
|
54
|
+
end
|
55
|
+
alias :result :digit_buffer_string
|
56
|
+
|
57
|
+
def digit_buffer_empty?
|
58
|
+
digit_buffer.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
def continue
|
62
|
+
return get_another_digit_or_timeout! if digit_buffer_empty?
|
63
|
+
|
64
|
+
return menu_terminated! if @terminated
|
65
|
+
return menu_limit_reached! if limit && digit_buffer.size >= limit
|
66
|
+
|
67
|
+
return menu_validator_terminated! if execute_validator_hook
|
68
|
+
|
69
|
+
calculated_matches = builder.calculate_matches_for digit_buffer_string
|
70
|
+
|
71
|
+
if calculated_matches.exact_match_count >= 1
|
72
|
+
first_exact_match = calculated_matches.exact_matches.first
|
73
|
+
if calculated_matches.potential_match_count.zero?
|
74
|
+
menu_result_found! first_exact_match, digit_buffer_string
|
75
|
+
else
|
76
|
+
get_another_digit_or_finish! first_exact_match.match_payload, first_exact_match.query
|
77
|
+
end
|
78
|
+
elsif calculated_matches.potential_match_count >= 1 || !@builder.has_matchers?
|
79
|
+
get_another_digit_or_timeout!
|
80
|
+
else
|
81
|
+
invalid!
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def should_continue?
|
86
|
+
tries_count < max_number_of_tries
|
87
|
+
end
|
88
|
+
|
89
|
+
def restart!
|
90
|
+
@tries_count += 1
|
91
|
+
digit_buffer.clear!
|
92
|
+
end
|
93
|
+
|
94
|
+
def execute_invalid_hook
|
95
|
+
builder.execute_hook_for :invalid, digit_buffer_string
|
96
|
+
end
|
97
|
+
|
98
|
+
def execute_timeout_hook
|
99
|
+
builder.execute_hook_for :timeout, digit_buffer_string
|
100
|
+
end
|
101
|
+
|
102
|
+
def execute_failure_hook
|
103
|
+
builder.execute_hook_for :failure, digit_buffer_string
|
104
|
+
end
|
105
|
+
|
106
|
+
def execute_validator_hook
|
107
|
+
builder.execute_hook_for :validator, digit_buffer_string
|
108
|
+
end
|
109
|
+
|
110
|
+
protected
|
111
|
+
|
112
|
+
# If you're using a more complex class in subclasses, you may want to override this method in addition to the
|
113
|
+
# digit buffer, digit_buffer_empty, and digit_buffer_string methods
|
114
|
+
def initialize_digit_buffer
|
115
|
+
@digit_buffer = ClearableStringBuffer.new
|
116
|
+
end
|
117
|
+
|
118
|
+
def invalid!
|
119
|
+
@status = :invalid
|
120
|
+
MenuResultInvalid.new
|
121
|
+
end
|
122
|
+
|
123
|
+
def menu_result_found!(match_object, new_extension)
|
124
|
+
@status = :matched
|
125
|
+
MenuResultFound.new(match_object, new_extension)
|
126
|
+
end
|
127
|
+
|
128
|
+
def menu_terminated!
|
129
|
+
@status = :terminated
|
130
|
+
MenuTerminated.new
|
131
|
+
end
|
132
|
+
|
133
|
+
def menu_validator_terminated!
|
134
|
+
@status = :validator_terminated
|
135
|
+
MenuValidatorTerminated.new
|
136
|
+
end
|
137
|
+
|
138
|
+
def menu_limit_reached!
|
139
|
+
@status = :limited
|
140
|
+
MenuLimitReached.new
|
141
|
+
end
|
142
|
+
|
143
|
+
def get_another_digit_or_finish!(match_payload, new_extension)
|
144
|
+
@status = :multi_matched
|
145
|
+
MenuGetAnotherDigitOrFinish.new(match_payload, new_extension)
|
146
|
+
end
|
147
|
+
|
148
|
+
def get_another_digit_or_timeout!
|
149
|
+
@status = :potential
|
150
|
+
MenuGetAnotherDigitOrTimeout.new
|
151
|
+
end
|
152
|
+
|
153
|
+
# The superclass from which all message-like exceptions descend. It should never
|
154
|
+
# be instantiated directly.
|
155
|
+
MenuResult = Class.new
|
156
|
+
MenuResultDone = Class.new MenuResult
|
157
|
+
|
158
|
+
class MenuResultFound < MenuResult
|
159
|
+
|
160
|
+
attr_reader :match_object, :new_extension
|
161
|
+
|
162
|
+
def initialize(match_object, new_extension)
|
163
|
+
super()
|
164
|
+
@match_object = match_object
|
165
|
+
@new_extension = new_extension
|
166
|
+
end
|
167
|
+
|
168
|
+
end #class MenuResultFound < MenuResult
|
169
|
+
|
170
|
+
MenuGetAnotherDigit = Module.new
|
171
|
+
|
172
|
+
class MenuGetAnotherDigitOrFinish < MenuResultFound
|
173
|
+
include MenuGetAnotherDigit
|
174
|
+
end
|
175
|
+
|
176
|
+
class MenuGetAnotherDigitOrTimeout < MenuResult
|
177
|
+
include MenuGetAnotherDigit
|
178
|
+
end
|
179
|
+
|
180
|
+
MenuResultInvalid = Class.new MenuResult
|
181
|
+
|
182
|
+
MenuTerminated = Class.new MenuResultDone
|
183
|
+
MenuValidatorTerminated = Class.new MenuResultDone
|
184
|
+
MenuLimitReached = Class.new MenuResultDone
|
185
|
+
|
186
|
+
# For our default purpose, we need the digit_buffer to behave much like a normal String except that it should
|
187
|
+
# handle its own resetting (clearing)
|
188
|
+
class ClearableStringBuffer < String
|
189
|
+
def clear!
|
190
|
+
replace ""
|
191
|
+
end
|
192
|
+
|
193
|
+
def <<(other)
|
194
|
+
super other.to_s
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
end # class Menu
|
200
|
+
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|