adhearsion 2.0.0.beta1 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/.travis.yml +2 -4
  2. data/CHANGELOG.md +34 -4
  3. data/README.markdown +2 -1
  4. data/Rakefile +22 -1
  5. data/adhearsion.gemspec +1 -0
  6. data/bin/ahn +0 -2
  7. data/features/cli_daemon.feature +2 -0
  8. data/features/cli_restart.feature +19 -0
  9. data/features/cli_start.feature +4 -6
  10. data/features/cli_stop.feature +3 -0
  11. data/features/step_definitions/app_generator_steps.rb +2 -0
  12. data/features/step_definitions/cli_steps.rb +2 -0
  13. data/features/support/aruba_helper.rb +2 -0
  14. data/features/support/env.rb +8 -46
  15. data/features/support/utils.rb +2 -0
  16. data/lib/adhearsion.rb +4 -6
  17. data/lib/adhearsion/call.rb +71 -17
  18. data/lib/adhearsion/call_controller.rb +25 -14
  19. data/lib/adhearsion/call_controller/dial.rb +34 -15
  20. data/lib/adhearsion/call_controller/input.rb +186 -144
  21. data/lib/adhearsion/call_controller/output.rb +10 -6
  22. data/lib/adhearsion/call_controller/record.rb +11 -13
  23. data/lib/adhearsion/call_controller/utility.rb +2 -0
  24. data/lib/adhearsion/calls.rb +4 -2
  25. data/lib/adhearsion/cli.rb +4 -0
  26. data/lib/adhearsion/cli_commands.rb +8 -2
  27. data/lib/adhearsion/configuration.rb +7 -3
  28. data/lib/adhearsion/console.rb +17 -17
  29. data/lib/adhearsion/events.rb +10 -4
  30. data/lib/adhearsion/foundation.rb +9 -0
  31. data/lib/adhearsion/foundation/custom_daemonizer.rb +3 -1
  32. data/lib/adhearsion/foundation/exception_handler.rb +2 -0
  33. data/lib/adhearsion/foundation/libc.rb +2 -0
  34. data/lib/adhearsion/foundation/object.rb +3 -0
  35. data/lib/adhearsion/foundation/thread_safety.rb +5 -11
  36. data/lib/adhearsion/generators.rb +2 -0
  37. data/lib/adhearsion/generators/app/app_generator.rb +2 -0
  38. data/lib/adhearsion/generators/app/templates/README.md +9 -0
  39. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +38 -16
  40. data/lib/adhearsion/generators/app/templates/config/environment.rb +2 -0
  41. data/lib/adhearsion/generators/app/templates/lib/simon_game.rb +5 -3
  42. data/lib/adhearsion/generators/controller/controller_generator.rb +2 -0
  43. data/lib/adhearsion/generators/controller/templates/lib/controller.rb +2 -0
  44. data/lib/adhearsion/generators/controller/templates/spec/controller_spec.rb +2 -0
  45. data/lib/adhearsion/generators/generator.rb +3 -1
  46. data/lib/adhearsion/generators/plugin/plugin_generator.rb +2 -0
  47. data/lib/adhearsion/initializer.rb +31 -17
  48. data/lib/adhearsion/linux_proc_name.rb +2 -0
  49. data/lib/adhearsion/logging.rb +5 -3
  50. data/lib/adhearsion/menu_dsl.rb +2 -0
  51. data/lib/adhearsion/menu_dsl/calculated_match.rb +2 -0
  52. data/lib/adhearsion/menu_dsl/calculated_match_collection.rb +2 -0
  53. data/lib/adhearsion/menu_dsl/fixnum_match_calculator.rb +2 -0
  54. data/lib/adhearsion/menu_dsl/match_calculator.rb +2 -0
  55. data/lib/adhearsion/menu_dsl/menu.rb +58 -4
  56. data/lib/adhearsion/menu_dsl/menu_builder.rb +14 -1
  57. data/lib/adhearsion/menu_dsl/range_match_calculator.rb +4 -1
  58. data/lib/adhearsion/menu_dsl/string_match_calculator.rb +2 -0
  59. data/lib/adhearsion/outbound_call.rb +2 -0
  60. data/lib/adhearsion/plugin.rb +9 -7
  61. data/lib/adhearsion/plugin/collection.rb +3 -1
  62. data/lib/adhearsion/plugin/initializer.rb +3 -1
  63. data/lib/adhearsion/process.rb +8 -2
  64. data/lib/adhearsion/punchblock_plugin.rb +3 -1
  65. data/lib/adhearsion/punchblock_plugin/initializer.rb +34 -11
  66. data/lib/adhearsion/router.rb +4 -2
  67. data/lib/adhearsion/router/route.rb +2 -0
  68. data/lib/adhearsion/script_ahn_loader.rb +2 -0
  69. data/lib/adhearsion/tasks.rb +2 -0
  70. data/lib/adhearsion/tasks/configuration.rb +2 -0
  71. data/lib/adhearsion/tasks/debugging.rb +8 -0
  72. data/lib/adhearsion/tasks/environment.rb +2 -0
  73. data/lib/adhearsion/tasks/plugins.rb +2 -0
  74. data/lib/adhearsion/tasks/testing.rb +2 -0
  75. data/lib/adhearsion/version.rb +3 -1
  76. data/pre-commit +2 -0
  77. data/spec/adhearsion/call_controller/dial_spec.rb +114 -25
  78. data/spec/adhearsion/call_controller/input_spec.rb +192 -169
  79. data/spec/adhearsion/call_controller/output_spec.rb +26 -12
  80. data/spec/adhearsion/call_controller/record_spec.rb +29 -77
  81. data/spec/adhearsion/call_controller/utility_spec.rb +69 -0
  82. data/spec/adhearsion/call_controller_spec.rb +90 -15
  83. data/spec/adhearsion/call_spec.rb +92 -24
  84. data/spec/adhearsion/calls_spec.rb +9 -7
  85. data/spec/adhearsion/configuration_spec.rb +58 -56
  86. data/spec/adhearsion/console_spec.rb +4 -2
  87. data/spec/adhearsion/events_spec.rb +9 -7
  88. data/spec/adhearsion/generators_spec.rb +3 -1
  89. data/spec/adhearsion/initializer_spec.rb +16 -14
  90. data/spec/adhearsion/logging_spec.rb +11 -9
  91. data/spec/adhearsion/menu_dsl/calculated_match_collection_spec.rb +6 -4
  92. data/spec/adhearsion/menu_dsl/calculated_match_spec.rb +6 -4
  93. data/spec/adhearsion/menu_dsl/fixnum_match_calculator_spec.rb +3 -1
  94. data/spec/adhearsion/menu_dsl/match_calculator_spec.rb +2 -0
  95. data/spec/adhearsion/menu_dsl/menu_builder_spec.rb +42 -11
  96. data/spec/adhearsion/menu_dsl/menu_spec.rb +197 -36
  97. data/spec/adhearsion/menu_dsl/range_match_calculator_spec.rb +4 -2
  98. data/spec/adhearsion/menu_dsl/string_match_calculator_spec.rb +5 -3
  99. data/spec/adhearsion/outbound_call_spec.rb +7 -5
  100. data/spec/adhearsion/plugin_spec.rb +19 -15
  101. data/spec/adhearsion/process_spec.rb +12 -7
  102. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +35 -15
  103. data/spec/adhearsion/punchblock_plugin_spec.rb +4 -1
  104. data/spec/adhearsion/router/route_spec.rb +8 -6
  105. data/spec/adhearsion/router_spec.rb +12 -10
  106. data/spec/adhearsion_spec.rb +13 -2
  107. data/spec/capture_warnings.rb +33 -0
  108. data/spec/spec_helper.rb +4 -0
  109. data/spec/support/call_controller_test_helpers.rb +2 -4
  110. data/spec/support/initializer_stubs.rb +8 -5
  111. data/spec/support/logging_helpers.rb +2 -0
  112. data/spec/support/punchblock_mocks.rb +2 -0
  113. metadata +84 -71
  114. data/EVENTS +0 -11
  115. data/lib/adhearsion/call_controller/menu.rb +0 -124
  116. data/lib/adhearsion/foundation/all.rb +0 -8
  117. data/spec/adhearsion/call_controller/menu_spec.rb +0 -120
  118. data/spec/adhearsion/menu_dsl_spec.rb +0 -12
@@ -1,9 +1,14 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  class CallController
3
5
  module Output
4
- def speak(text, options = {})
6
+ PlaybackError = Class.new StandardError # Represents failure to play audio, such as when the sound file cannot be found
7
+
8
+ def say(text, options = {})
5
9
  play_ssml(text, options) || output(:text, text.to_s, options)
6
10
  end
11
+ alias :speak :say
7
12
 
8
13
  #
9
14
  # Plays the specified sound file names. This method will handle Time/DateTime objects (e.g. Time.now),
@@ -50,7 +55,7 @@ module Adhearsion
50
55
  # @see play
51
56
  #
52
57
  def play!(*arguments)
53
- play *arguments or raise Adhearsion::PlaybackError, "One of the passed outputs is invalid"
58
+ play(*arguments) or raise PlaybackError, "One of the passed outputs is invalid"
54
59
  end
55
60
 
56
61
  #
@@ -162,7 +167,7 @@ module Adhearsion
162
167
  result = interruptible_play! output
163
168
  rescue PlaybackError => e
164
169
  # Ignore this exception and play the next output
165
- logger.warn e.message
170
+ logger.warn "Error playing back the prompt: #{e.message}"
166
171
  ensure
167
172
  break if result
168
173
  end
@@ -174,7 +179,7 @@ module Adhearsion
174
179
  result = nil
175
180
  result = :time if [Date, Time, DateTime].include? output.class
176
181
  result = :numeric if output.kind_of?(Numeric) || output =~ /^\d+$/
177
- result = :audio if !result && (/\//.match(output.to_s) || URI::regexp(%w(http https)).match(output.to_s))
182
+ result = :audio if !result && (/^\//.match(output.to_s) || URI::regexp.match(output.to_s))
178
183
  result ||= :text
179
184
  end
180
185
 
@@ -247,13 +252,12 @@ module Adhearsion
247
252
  :value => grammar_accept(digits).to_s
248
253
  }
249
254
  input_stopper_component.register_event_handler ::Punchblock::Event::Complete do |event|
250
- logger.warn "#stream_file Handling input complete event."
251
255
  output_component.stop! unless output_component.complete?
252
256
  end
253
257
  write_and_await_response input_stopper_component
254
258
  begin
255
259
  execute_component_and_await_completion output_component
256
- rescue StandardError => e
260
+ rescue StandardError
257
261
  raise Adhearsion::PlaybackError, "Output failed for argument #{argument.inspect}"
258
262
  end
259
263
  input_stopper_component.stop! if input_stopper_component.executing?
@@ -1,13 +1,16 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  class CallController
3
5
  module Record
6
+ RecordError = Class.new StandardError # Represents failure to record such as when a file cannot be written.
7
+
4
8
  #
5
9
  # Start a recording
6
10
  #
7
11
  # @param [Hash] options
8
12
  # @param [Block] &block to process result of the record method
9
13
  # @option options [Boolean, Optional] :async Execute asynchronously. Defaults to false
10
- # @option options [Proc, Optional] :on_complete Block to be executed on completion when this method is invoked asynchronously
11
14
  # @option options [Boolean, Optional] :start_beep Indicates whether subsequent record will be preceded with a beep. Default is true.
12
15
  # @option options [Boolean, Optional] :start_paused Whether subsequent record will start in PAUSE mode. Default is false.
13
16
  # @option options [String, Optional] :max_duration Indicates the maximum duration (milliseconds) for a recording.
@@ -18,23 +21,18 @@ module Adhearsion
18
21
  #
19
22
  # @return recording object
20
23
 
21
- def record(options = {}, &block)
22
- async = options.delete(:async) ? true : false
23
- on_complete = options.delete :on_complete
24
+ def record(options = {})
25
+ async = options.delete :async
24
26
 
25
27
  component = ::Punchblock::Component::Record.new options
28
+ component.register_event_handler ::Punchblock::Event::Complete do |event|
29
+ catching_standard_errors { yield event if block_given? }
30
+ end
26
31
 
27
32
  if async
28
- execute_component(component).tap do |result|
29
- # FIXME: Setting the callback after we begin execution constitutes a race condition. We need to mock the component creation here, since the tests assume we're using whatever is returned by component exection, and that's what we pass the complete event to
30
- result.register_event_handler Punchblock::Event::Complete do |event|
31
- catching_standard_errors { on_complete.call event }
32
- end
33
- end
33
+ write_and_await_response component
34
34
  else
35
- execute_component_and_await_completion(component).tap do |result|
36
- yield result.complete_event if block_given?
37
- end
35
+ execute_component_and_await_completion component
38
36
  end
39
37
  end
40
38
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  class CallController
3
5
  module Utility
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'thread'
2
4
 
3
5
  module Adhearsion
@@ -44,14 +46,14 @@ module Adhearsion
44
46
 
45
47
  def with_tag(tag)
46
48
  atomically do
47
- calls.inject([]) do |calls_with_tag,(key,call)|
49
+ calls.inject([]) do |calls_with_tag,(_,call)|
48
50
  call.tagged_with?(tag) ? calls_with_tag << call : calls_with_tag
49
51
  end
50
52
  end
51
53
  end
52
54
 
53
55
  def each(&block)
54
- atomically { calls.values.each &block }
56
+ atomically { calls.values.each(&block) }
55
57
  end
56
58
 
57
59
  def each_pair
@@ -1,7 +1,11 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'adhearsion/script_ahn_loader'
2
4
 
3
5
  # If we are inside an Adhearsion application this method performs an exec and thus
4
6
  # the rest of this script is not run.
5
7
  Adhearsion::ScriptAhnLoader.exec_script_ahn!
6
8
 
9
+ require 'bundler/setup'
10
+ require 'adhearsion'
7
11
  require 'adhearsion/cli_commands'
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'fileutils'
2
4
  require 'adhearsion/script_ahn_loader'
3
5
  require 'thor'
@@ -95,14 +97,18 @@ module Adhearsion
95
97
  rescue Errno::ESRCH
96
98
  end
97
99
 
98
- File.delete pid_file
100
+ File.delete pid_file if File.exists? pid_file
99
101
  end
100
102
 
101
103
  desc "restart </path/to/directory>", "Restart the Adhearsion server"
102
104
  method_option :pidfile, :type => :string, :aliases => %w(--pid-file)
103
105
  def restart(path = nil)
104
106
  execute_from_app_dir! path
105
- invoke :stop
107
+ begin
108
+ invoke :stop
109
+ rescue PIDReadError => e
110
+ puts e.message
111
+ end
106
112
  invoke :daemon
107
113
  end
108
114
 
@@ -1,7 +1,10 @@
1
- module Adhearsion
1
+ # encoding: utf-8
2
2
 
3
+ module Adhearsion
3
4
  class Configuration
4
5
 
6
+ ConfigurationError = Class.new StandardError # Error raised while trying to configure a non existent plugin
7
+
5
8
  ##
6
9
  # Initialize the configuration object
7
10
  #
@@ -72,9 +75,10 @@ module Adhearsion
72
75
  end
73
76
 
74
77
  def add_environment(env)
78
+ return if self.class.method_defined? env.to_sym
75
79
  self.class.send(:define_method, env.to_sym) do |*args, &block|
76
80
  unless block.nil? || env != self.platform.environment.to_sym
77
- self.instance_eval &block
81
+ self.instance_eval(&block)
78
82
  end
79
83
  self
80
84
  end
@@ -96,7 +100,7 @@ module Adhearsion
96
100
  # Adhearsion.config.foo => returns the configuration object associated to the foo plugin
97
101
  def method_missing(method_name, *args, &block)
98
102
  config = Loquacious::Configuration.for method_name, &block
99
- raise Adhearsion::ConfigurationError.new "Invalid plugin #{method_name}" if config.nil?
103
+ raise Adhearsion::Configuration::ConfigurationError.new "Invalid plugin #{method_name}" if config.nil?
100
104
  config
101
105
  end
102
106
 
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'pry'
2
4
 
3
5
  module Adhearsion
@@ -33,18 +35,18 @@ module Adhearsion
33
35
  if libedit?
34
36
  logger.error "Cannot start. You are running Adhearsion on Ruby with libedit. You must use readline for the console to work."
35
37
  else
36
- logger.info "Starting up..."
38
+ logger.info "Launching Adhearsion Console"
37
39
  @pry_thread = Thread.current
38
40
  pry
39
- logger.info "Console exiting"
41
+ logger.info "Adhearsion Console exiting"
40
42
  end
41
43
  end
42
44
 
43
45
  def stop
44
- return unless @pry_thread
46
+ return unless instance_variable_defined?(:@pry_thread)
45
47
  @pry_thread.kill
46
48
  @pry_thread = nil
47
- logger.info "Shutting down"
49
+ logger.info "Adhearsion Console shutting down"
48
50
  end
49
51
 
50
52
  def log_level(level = nil)
@@ -76,15 +78,17 @@ module Adhearsion
76
78
  when nil
77
79
  case calls.size
78
80
  when 0
79
- logger.warn "No calls exist for control"
81
+ logger.warn "No calls active to take"
80
82
  when 1
81
83
  interact_with_call calls.values.first
82
84
  else
83
85
  puts "Please choose a call:"
86
+ puts "# (inbound/outbound) details"
84
87
  current_calls = calls.values
85
- current_calls.each_with_index do |call, index|
86
- puts "#{index}. (#{call.is_a?(OutboundCall) ? 'o' : 'i' }) #{call.id} from #{call.from} to #{call.to}"
88
+ current_calls.each_with_index do |active_call, index|
89
+ puts "#{index}: (#{active_call.is_a?(OutboundCall) ? 'o' : 'i' }) #{active_call.id} from #{active_call.from} to #{active_call.to}"
87
90
  end
91
+ print "#> "
88
92
  index = input.gets.chomp.to_i
89
93
  call = current_calls[index]
90
94
  interact_with_call call
@@ -112,11 +116,11 @@ module Adhearsion
112
116
  def set_prompt
113
117
  Pry.prompt = [
114
118
  proc do |*args|
115
- obj, nest_level, pry_instance = args
119
+ _, nest_level, _ = args
116
120
  "AHN#{' ' * nest_level}> "
117
121
  end,
118
122
  proc do |*args|
119
- obj, nest_level, pry_instance = args
123
+ _, nest_level, _ = args
120
124
  "AHN#{' ' * nest_level}? "
121
125
  end
122
126
  ]
@@ -130,20 +134,16 @@ module Adhearsion
130
134
  call.pause_controllers
131
135
  CallController.exec InteractiveController.new(call)
132
136
  ensure
133
- logger.debug "Resuming call's controllers"
137
+ logger.debug "Restoring control of call to controllers"
134
138
  call.resume_controllers
135
139
  end
136
140
  end
137
141
 
138
142
  class InteractiveController < CallController
139
143
  def run
140
- logger.debug "Starting interactive controller."
141
- begin
142
- pry
143
- rescue => e
144
- logger.error e
145
- end
146
- logger.debug "Interactive controller finished."
144
+ logger.debug "Starting interactive controller"
145
+ pry
146
+ logger.debug "Interactive controller finished"
147
147
  end
148
148
 
149
149
  def hangup(*args)
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  class Events
3
5
 
@@ -26,7 +28,7 @@ module Adhearsion
26
28
  refresh!
27
29
 
28
30
  def queue
29
- @queue || reinitialize_queue!
31
+ queue? ? @queue : reinitialize_queue!
30
32
  end
31
33
 
32
34
  def trigger(type, object = nil)
@@ -37,8 +39,12 @@ module Adhearsion
37
39
  queue.push_immediately Message.new(type, object)
38
40
  end
39
41
 
42
+ def queue?
43
+ instance_variable_defined? :@queue
44
+ end
45
+
40
46
  def reinitialize_queue!
41
- GirlFriday.shutdown! if @queue
47
+ GirlFriday.shutdown! if queue?
42
48
  # TODO: Extract number of threads to use from Adhearsion.config
43
49
  @queue = GirlFriday::WorkQueue.new 'main_queue', :error_handler => ErrorHandler do |message|
44
50
  begin
@@ -54,7 +60,7 @@ module Adhearsion
54
60
  end
55
61
 
56
62
  def draw(&block)
57
- instance_exec &block
63
+ instance_exec(&block)
58
64
  end
59
65
 
60
66
  def method_missing(method_name, *args, &block)
@@ -62,7 +68,7 @@ module Adhearsion
62
68
  end
63
69
 
64
70
  def respond_to?(method_name)
65
- return true if @handlers && @handlers.has_key?(method_name)
71
+ return true if instance_variable_defined?(:@handlers) && @handlers.has_key?(method_name)
66
72
  super
67
73
  end
68
74
 
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ require 'English'
4
+ require 'tmpdir'
5
+ require 'tempfile'
6
+
7
+ Dir.glob File.join(File.dirname(__FILE__), 'foundation', "*rb") do |file|
8
+ require file
9
+ end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  # This is largely based on the Daemonize library by Travis Whitton and
2
4
  # Judson Lester. http://grub.ath.cx/daemonize. I cleaned it up a bit to
3
5
  # meet Adhearsion's quality standards.
@@ -28,7 +30,7 @@ module Adhearsion
28
30
  # Prevent the possibility of acquiring a controlling terminal
29
31
  if oldmode.zero?
30
32
  trap 'SIGHUP', 'IGNORE'
31
- exit if pid = safefork
33
+ exit if safefork
32
34
  end
33
35
 
34
36
  Dir.chdir "/" # Release old working directory
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  class Object
2
4
  def catching_standard_errors(&block)
3
5
  begin
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'adhearsion/linux_proc_name'
2
4
  require 'ffi'
3
5
 
@@ -1,6 +1,9 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'adhearsion/logging'
2
4
 
3
5
  class Object
6
+ undef :pb_logger
4
7
  def pb_logger
5
8
  logger
6
9
  end
@@ -1,15 +1,17 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'thread'
2
4
 
3
5
  class Object
4
6
  def synchronize(&block)
5
7
  @mutex ||= Mutex.new
6
- @mutex.synchronize &block
8
+ @mutex.synchronize(&block)
7
9
  end
8
10
  end
9
11
 
10
- class ThreadSafeArray
12
+ class ThreadSafeArray < BasicObject
11
13
  def initialize
12
- @mutex = Mutex.new
14
+ @mutex = ::Mutex.new
13
15
  @array = []
14
16
  end
15
17
 
@@ -18,12 +20,4 @@ class ThreadSafeArray
18
20
  @array.send method, *args, &block
19
21
  end
20
22
  end
21
-
22
- def inspect
23
- @mutex.synchronize { @array.inspect }
24
- end
25
-
26
- def to_s
27
- @mutex.synchronize { @array.to_s }
28
- end
29
23
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  module Generators
3
5
  extend ActiveSupport::Autoload
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Adhearsion
2
4
  module Generators
3
5
  class AppGenerator < Generator
@@ -25,4 +25,13 @@ In `config/adhearsion.rb` you'll need to set the VoIP platform you're using, alo
25
25
 
26
26
  Start your new app with "ahn start /path/to/your/app". You'll get a lovely console and should be presented with the SimonGame
27
27
 
28
+ ### Running your app on heroku
29
+
30
+ In order to run an adhearsion application on Heroku, you must create the application on the 'cedar' stack (`heroku apps:create --stack cedar`) and re-scale your processes like so:
31
+
32
+ ```
33
+ heroku ps:scale web=0
34
+ heroku ps:scale ahn=1
35
+ ```
36
+
28
37
  Check out [the Adhearsion website](http://adhearsion.com) for more details of where to go from here.