adhearsion 2.0.0.rc4 → 2.0.0.rc5
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.
- 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
|