adhearsion 2.0.0.alpha1 → 2.0.0.alpha2
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/.gitignore +2 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.md +17 -0
- data/adhearsion.gemspec +4 -3
- data/features/app_generator.feature +3 -1
- data/features/cli.feature +7 -7
- data/features/support/env.rb +46 -0
- data/lib/adhearsion.rb +1 -2
- data/lib/adhearsion/call.rb +59 -19
- data/lib/adhearsion/call_controller.rb +20 -24
- data/lib/adhearsion/call_controller/dial.rb +18 -18
- data/lib/adhearsion/cli_commands.rb +26 -9
- data/lib/adhearsion/configuration.rb +39 -10
- data/lib/adhearsion/console.rb +61 -42
- data/lib/adhearsion/foundation/libc.rb +13 -0
- data/lib/adhearsion/generators/app/app_generator.rb +4 -1
- data/lib/adhearsion/generators/app/templates/{Gemfile → Gemfile.erb} +1 -1
- data/lib/adhearsion/generators/app/templates/Rakefile +3 -22
- data/lib/adhearsion/generators/app/templates/gitignore +7 -0
- data/lib/adhearsion/generators/app/templates/lib/simon_game.rb +1 -0
- data/lib/adhearsion/generators/app/templates/script/ahn +1 -0
- data/lib/adhearsion/initializer.rb +24 -12
- data/lib/adhearsion/linux_proc_name.rb +41 -0
- data/lib/adhearsion/outbound_call.rb +10 -5
- data/lib/adhearsion/plugin.rb +29 -132
- data/lib/adhearsion/process.rb +4 -1
- data/lib/adhearsion/punchblock_plugin.rb +14 -5
- data/lib/adhearsion/punchblock_plugin/initializer.rb +8 -1
- data/lib/adhearsion/router/route.rb +1 -3
- data/lib/adhearsion/tasks.rb +6 -12
- data/lib/adhearsion/tasks/configuration.rb +7 -24
- data/lib/adhearsion/tasks/environment.rb +12 -0
- data/lib/adhearsion/tasks/plugins.rb +9 -14
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/dial_spec.rb +46 -22
- data/spec/adhearsion/call_controller_spec.rb +48 -13
- data/spec/adhearsion/call_spec.rb +144 -23
- data/spec/adhearsion/calls_spec.rb +8 -4
- data/spec/adhearsion/console_spec.rb +24 -0
- data/spec/adhearsion/initializer/logging_spec.rb +0 -3
- data/spec/adhearsion/initializer_spec.rb +52 -37
- data/spec/adhearsion/logging_spec.rb +0 -3
- data/spec/adhearsion/outbound_call_spec.rb +12 -2
- data/spec/adhearsion/plugin_spec.rb +74 -184
- data/spec/adhearsion/process_spec.rb +59 -26
- data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +3 -4
- data/spec/adhearsion/punchblock_plugin_spec.rb +11 -0
- data/spec/adhearsion/router/route_spec.rb +37 -6
- data/spec/adhearsion_spec.rb +31 -8
- data/spec/spec_helper.rb +14 -0
- data/spec/support/call_controller_test_helpers.rb +2 -2
- data/spec/support/logging_helpers.rb +2 -0
- metadata +85 -68
- data/lib/adhearsion/dialplan_controller.rb +0 -9
- data/lib/adhearsion/foundation/synchronized_hash.rb +0 -93
- data/lib/adhearsion/plugin/methods_container.rb +0 -6
- data/spec/adhearsion/dialplan_controller_spec.rb +0 -26
@@ -0,0 +1,41 @@
|
|
1
|
+
module Adhearsion
|
2
|
+
|
3
|
+
# https://gist.github.com/1350729
|
4
|
+
#
|
5
|
+
# Eric Lindvall <eric@5stops.com>
|
6
|
+
#
|
7
|
+
# Update the process name for the process you're running in.
|
8
|
+
#
|
9
|
+
# $0 => updates proc name for ps command
|
10
|
+
# prctl => updates proc name for lsof, top, killall commands (...)
|
11
|
+
#
|
12
|
+
# prctl does not work on OS X
|
13
|
+
#
|
14
|
+
module LinuxProcName
|
15
|
+
# Set process name
|
16
|
+
PR_SET_NAME = 15
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_accessor :error
|
20
|
+
|
21
|
+
def set_proc_name(name)
|
22
|
+
$0 = name # process name in ps command
|
23
|
+
if error
|
24
|
+
logger.warn error
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
return false unless LibC.respond_to?(:prctl)
|
28
|
+
|
29
|
+
# The name can be up to 16 bytes long, and should be null-terminated if
|
30
|
+
# it contains fewer bytes.
|
31
|
+
name = name.slice(0, 16)
|
32
|
+
ptr = FFI::MemoryPointer.from_string(name)
|
33
|
+
LibC.prctl(PR_SET_NAME, ptr.address, 0, 0) # process name in top, lsof, etc
|
34
|
+
ensure
|
35
|
+
ptr.free if ptr
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -2,6 +2,8 @@ module Adhearsion
|
|
2
2
|
class OutboundCall < Call
|
3
3
|
attr_reader :dial_command
|
4
4
|
|
5
|
+
delegate :to, :from, :to => :dial_command, :allow_nil => true
|
6
|
+
|
5
7
|
class << self
|
6
8
|
def originate(to, opts = {})
|
7
9
|
new.tap do |call|
|
@@ -15,10 +17,6 @@ module Adhearsion
|
|
15
17
|
dial_command.call_id if dial_command
|
16
18
|
end
|
17
19
|
|
18
|
-
def variables
|
19
|
-
{}
|
20
|
-
end
|
21
|
-
|
22
20
|
def client
|
23
21
|
PunchblockPlugin::Initializer.client
|
24
22
|
end
|
@@ -34,7 +32,14 @@ module Adhearsion
|
|
34
32
|
|
35
33
|
def dial(to, options = {})
|
36
34
|
options.merge! :to => to
|
37
|
-
|
35
|
+
if options[:timeout]
|
36
|
+
wait_timeout = options[:timeout]
|
37
|
+
options[:timeout] = options[:timeout] * 1000
|
38
|
+
else
|
39
|
+
wait_timeout = 60
|
40
|
+
end
|
41
|
+
|
42
|
+
write_and_await_response(Punchblock::Command::Dial.new(options), wait_timeout).tap do |dial_command|
|
38
43
|
@dial_command = dial_command
|
39
44
|
Adhearsion.active_calls << self
|
40
45
|
end
|
data/lib/adhearsion/plugin.rb
CHANGED
@@ -12,7 +12,6 @@ module Adhearsion
|
|
12
12
|
# * create initializers
|
13
13
|
# * add rake tasks to Adhearsion
|
14
14
|
# * add/modify configuration files
|
15
|
-
# * add dialplan, rpc, console and events methods
|
16
15
|
#
|
17
16
|
# == How to create your Adhearsion Plugin
|
18
17
|
#
|
@@ -25,19 +24,6 @@ module Adhearsion
|
|
25
24
|
# end
|
26
25
|
# end
|
27
26
|
#
|
28
|
-
# == How to add a new dialplan method
|
29
|
-
#
|
30
|
-
# module MyPlugin
|
31
|
-
# class Plugin < Adhearsion::Plugin
|
32
|
-
# dialplan :my_new_dialplan_method do
|
33
|
-
# logger.info "this dialplan method is really awesome #{call.inspect}. It says 'hello world'"
|
34
|
-
# speak "hello world"
|
35
|
-
# end
|
36
|
-
# end
|
37
|
-
# end
|
38
|
-
#
|
39
|
-
# Create a new rpc, console or events methods is as ease just following this approach
|
40
|
-
#
|
41
27
|
# == Execute a specific code while initializing Adhearison
|
42
28
|
#
|
43
29
|
# module MyPlugin
|
@@ -49,7 +35,7 @@ module Adhearsion
|
|
49
35
|
# end
|
50
36
|
#
|
51
37
|
# As Rails::Railtie does, you can define the exact point when you want to load your plugin
|
52
|
-
# during the
|
38
|
+
# during the initialization process.
|
53
39
|
#
|
54
40
|
# module MyPlugin
|
55
41
|
# class Plugin < Adhearsion::Plugin
|
@@ -65,81 +51,11 @@ module Adhearsion
|
|
65
51
|
|
66
52
|
METHODS_OPTIONS = {:load => true, :scope => false}
|
67
53
|
|
68
|
-
SCOPE_NAMES = [:dialplan, :rpc, :events, :console]
|
69
|
-
|
70
54
|
autoload :Configuration
|
71
55
|
autoload :Collection
|
72
56
|
autoload :Initializer
|
73
|
-
autoload :MethodsContainer
|
74
57
|
|
75
58
|
class << self
|
76
|
-
# Metaprogramming to create the class methods that can be used in user defined plugins to
|
77
|
-
# create specific scope methods
|
78
|
-
SCOPE_NAMES.each do |name|
|
79
|
-
|
80
|
-
# This block will create the relevant methods to handle how to add new methods
|
81
|
-
# to Adhearsion scopes via an Adhearsion Plugin.
|
82
|
-
# The scope method should have a name and a lambda block that will be executed in the
|
83
|
-
# call ExecutionEnvironment context.
|
84
|
-
#
|
85
|
-
# class AhnPluginDemo < Adhearsion::Plugin
|
86
|
-
# dialplan :adh_plugin_demo do
|
87
|
-
# speak "hello world"
|
88
|
-
# end
|
89
|
-
# end
|
90
|
-
#
|
91
|
-
# You could also defined a dialplan or other scope method as above, but you cannot access
|
92
|
-
# the ExecutionEnvironment methods from your specific method due to ruby restrictions
|
93
|
-
# when defining methods (the above lambda version should fit any requirement)
|
94
|
-
#
|
95
|
-
# class AhnPluginDemo < Adhearsion::Plugin
|
96
|
-
# dialplan :adh_plugin_demo
|
97
|
-
#
|
98
|
-
# def self.adh_plugin_demo
|
99
|
-
# logger.debug "I can do fun stuff here, but I cannot access methods as speak"
|
100
|
-
# logger.debug "I can make an HTTP request"
|
101
|
-
# logger.debug "I can log to a specific logging system"
|
102
|
-
# logger.debug "I can access database..."
|
103
|
-
# logger.debug "but I cannot access call control methods"
|
104
|
-
# end
|
105
|
-
#
|
106
|
-
# end
|
107
|
-
#
|
108
|
-
define_method name do |method_name, &block|
|
109
|
-
case method_name
|
110
|
-
when Array
|
111
|
-
method_name.each do |method|
|
112
|
-
send name, method
|
113
|
-
end
|
114
|
-
return
|
115
|
-
when Hash
|
116
|
-
args = method_name
|
117
|
-
method_name = method_name[:name]
|
118
|
-
end
|
119
|
-
|
120
|
-
options = args.nil? ? METHODS_OPTIONS : METHODS_OPTIONS.merge(args)
|
121
|
-
options[:load] or return
|
122
|
-
logger.debug "Adding method #{method_name} to scope #{name}"
|
123
|
-
@@methods_container[name].store({:class => self, :method => method_name}, block.nil? ? nil : block)
|
124
|
-
end
|
125
|
-
|
126
|
-
# This method is a helper to retrieve the specific module that holds the user
|
127
|
-
# defined scope methods
|
128
|
-
define_method "#{name.to_s}_module" do
|
129
|
-
Adhearsion::Plugin.methods_scope[name]
|
130
|
-
end
|
131
|
-
|
132
|
-
# Helper to add scope methods to any class/instance
|
133
|
-
define_method "add_#{name.to_s}_methods" do |object|
|
134
|
-
if object.kind_of?(Module)
|
135
|
-
object.send :include, Adhearsion::Plugin.methods_scope[name]
|
136
|
-
else
|
137
|
-
object.extend Adhearsion::Plugin.methods_scope[name]
|
138
|
-
end
|
139
|
-
object
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
59
|
##
|
144
60
|
# Class method that allows any subclass (any Adhearsion plugin) to register rake tasks.
|
145
61
|
#
|
@@ -189,13 +105,6 @@ module Adhearsion
|
|
189
105
|
end
|
190
106
|
end
|
191
107
|
|
192
|
-
def methods_scope
|
193
|
-
@methods_scope ||= Hash.new { |hash, key| hash[key] = Module.new }
|
194
|
-
end
|
195
|
-
|
196
|
-
# Keep methods to be added
|
197
|
-
@@methods_container = Hash.new { |hash, key| hash[key] = MethodsContainer.new }
|
198
|
-
|
199
108
|
def subclasses
|
200
109
|
@subclasses ||= []
|
201
110
|
end
|
@@ -217,7 +126,7 @@ module Adhearsion
|
|
217
126
|
@plugin_name = name
|
218
127
|
end
|
219
128
|
|
220
|
-
def config
|
129
|
+
def config(name = nil, &block)
|
221
130
|
if block_given?
|
222
131
|
if name.nil?
|
223
132
|
name = self.plugin_name
|
@@ -225,51 +134,16 @@ module Adhearsion
|
|
225
134
|
self.plugin_name = name
|
226
135
|
end
|
227
136
|
::Loquacious::Configuration.defaults_for name, &Proc.new
|
137
|
+
::Loquacious.configuration_for plugin_name, &block
|
138
|
+
else
|
139
|
+
::Loquacious.configuration_for plugin_name
|
228
140
|
end
|
229
|
-
|
230
|
-
::Loquacious.configuration_for plugin_name
|
231
141
|
end
|
232
142
|
|
233
143
|
def show_description
|
234
144
|
::Loquacious::Configuration.help_for plugin_name
|
235
145
|
end
|
236
146
|
|
237
|
-
def load_plugins
|
238
|
-
load_methods
|
239
|
-
init_plugins
|
240
|
-
end
|
241
|
-
|
242
|
-
# Load plugins scope methods (scope = dialplan, console, etc)
|
243
|
-
def load_methods
|
244
|
-
unless @@methods_container.empty?
|
245
|
-
|
246
|
-
@@methods_container.each_pair do |scope, methods|
|
247
|
-
|
248
|
-
logger.debug "Loading #{methods.length} #{scope} methods"
|
249
|
-
|
250
|
-
methods.each_pair do |class_method, block|
|
251
|
-
klass, method = class_method[:class], class_method[:method]
|
252
|
-
if block.nil?
|
253
|
-
if klass.respond_to?(method)
|
254
|
-
block = klass.method(method).to_proc
|
255
|
-
elsif klass.instance_methods.include?(method)
|
256
|
-
block = klass.instance_method(method).bind(klass.new)
|
257
|
-
else
|
258
|
-
logger.warn "Unable to load #{scope} method #{method} from plugin class #{klass}"
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
logger.debug "Defining method #{method}"
|
263
|
-
block.nil? and raise NoMethodError.new "Invalid #{scope} method: <#{method}>"
|
264
|
-
self.send("#{scope}_module").send(:define_method, method, &block)
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
# We need to extend Console class with the plugin defined methods
|
269
|
-
Adhearsion::Console.extend(self.console_module) unless self.console_module.instance_methods.empty?
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
147
|
# Recursively initialization of all the loaded plugins
|
274
148
|
def init_plugins *args
|
275
149
|
initializers.tsort.each do |initializer|
|
@@ -277,21 +151,44 @@ module Adhearsion
|
|
277
151
|
end
|
278
152
|
end
|
279
153
|
|
154
|
+
def run_plugins *args
|
155
|
+
runners.tsort.each do |runner|
|
156
|
+
runner.run *args
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
280
160
|
def initializers
|
281
161
|
@initializers ||= Collection.new
|
282
162
|
end
|
283
163
|
|
164
|
+
def runners
|
165
|
+
@runners ||= Collection.new
|
166
|
+
end
|
167
|
+
|
284
168
|
# Class method that will be used by subclasses to initialize the plugin
|
285
169
|
# @param name Symbol plugin initializer name
|
286
170
|
# @param opts Hash
|
287
171
|
# * :before specify the plugin to be loaded before another plugin
|
288
172
|
# * :after specify the plugin to be loaded after another plugin
|
289
|
-
def init(name, opts = {})
|
173
|
+
def init(name = nil, opts = {})
|
174
|
+
name = plugin_name unless name
|
290
175
|
block_given? or raise ArgumentError, "A block must be passed while defining the Plugin initialization process"
|
291
176
|
opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
|
292
177
|
Adhearsion::Plugin.initializers << Initializer.new(name, nil, opts, &Proc.new)
|
293
178
|
end
|
294
179
|
|
180
|
+
# Class method that will be used by subclasses to run the plugin
|
181
|
+
# @param name Symbol plugin initializer name
|
182
|
+
# @param opts Hash
|
183
|
+
# * :before specify the plugin to be loaded before another plugin
|
184
|
+
# * :after specify the plugin to be loaded after another plugin
|
185
|
+
def run(name = nil, opts = {})
|
186
|
+
name = plugin_name unless name
|
187
|
+
block_given? or raise ArgumentError, "A block must be passed while defining the Plugin run process"
|
188
|
+
opts[:after] ||= runners.last.name unless runners.empty? || runners.find { |i| i.name == opts[:before] }
|
189
|
+
Adhearsion::Plugin.runners << Initializer.new(name, nil, opts, &Proc.new)
|
190
|
+
end
|
191
|
+
|
295
192
|
def count
|
296
193
|
subclasses.length
|
297
194
|
end
|
data/lib/adhearsion/process.rb
CHANGED
@@ -57,7 +57,7 @@ module Adhearsion
|
|
57
57
|
|
58
58
|
def log_state_change(transition)
|
59
59
|
event, from, to = transition.event, transition.from_name, transition.to_name
|
60
|
-
logger.info "Transitioning from #{from} to #{to} with #{Adhearsion.active_calls.size} active calls."
|
60
|
+
logger.info "Transitioning from #{from} to #{to} with #{Adhearsion.active_calls.size} active calls due to #{event} event."
|
61
61
|
end
|
62
62
|
|
63
63
|
def request_stop
|
@@ -69,10 +69,13 @@ module Adhearsion
|
|
69
69
|
Adhearsion.active_calls.each do |call|
|
70
70
|
call.hangup
|
71
71
|
end
|
72
|
+
|
72
73
|
# This should shut down any remaining threads. Once those threads have
|
73
74
|
# stopped, important_threads will be empty and the process will exit
|
74
75
|
# normally.
|
75
76
|
Events.trigger_immediately :shutdown
|
77
|
+
|
78
|
+
Console.stop
|
76
79
|
end
|
77
80
|
|
78
81
|
def stop_when_zero_calls
|
@@ -5,7 +5,7 @@ module Adhearsion
|
|
5
5
|
autoload :Initializer
|
6
6
|
|
7
7
|
config :punchblock do
|
8
|
-
platform
|
8
|
+
platform :xmpp , :transform => Proc.new { |v| v.to_sym }, :desc => <<-__
|
9
9
|
Platform punchblock shall use to connect to the Telephony provider. Currently supported values:
|
10
10
|
- :xmpp
|
11
11
|
- :asterisk
|
@@ -13,17 +13,26 @@ module Adhearsion
|
|
13
13
|
username "usera@127.0.0.1", :desc => "Authentication credentials"
|
14
14
|
password "1" , :desc => "Authentication credentials"
|
15
15
|
host nil , :desc => "Host punchblock needs to connect (where rayo or asterisk are located)"
|
16
|
-
port nil , :desc => "Port punchblock needs to connect (by default 5038 for Asterisk, 5222 for Rayo)"
|
16
|
+
port nil , :transform => Proc.new { |v| PunchblockPlugin.validate_number v }, :desc => "Port punchblock needs to connect (by default 5038 for Asterisk, 5222 for Rayo)"
|
17
17
|
root_domain nil , :desc => "The root domain at which to address the server"
|
18
18
|
calls_domain nil , :desc => "The domain at which to address calls"
|
19
19
|
mixers_domain nil , :desc => "The domain at which to address mixers"
|
20
|
-
connection_timeout 60 , :desc => "The amount of time to wait for a connection"
|
21
|
-
reconnect_attempts 1.0/0.0 , :desc => "The number of times to (re)attempt connection to the server"
|
22
|
-
reconnect_timer 5 , :desc => "Delay between connection attempts"
|
20
|
+
connection_timeout 60 , :transform => Proc.new { |v| PunchblockPlugin.validate_number v }, :desc => "The amount of time to wait for a connection"
|
21
|
+
reconnect_attempts 1.0/0.0 , :transform => Proc.new { |v| PunchblockPlugin.validate_number v }, :desc => "The number of times to (re)attempt connection to the server"
|
22
|
+
reconnect_timer 5 , :transform => Proc.new { |v| PunchblockPlugin.validate_number v }, :desc => "Delay between connection attempts"
|
23
23
|
end
|
24
24
|
|
25
25
|
init :punchblock do
|
26
26
|
Initializer.start
|
27
27
|
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
delegate :client, :to => Initializer
|
31
|
+
|
32
|
+
def validate_number(value)
|
33
|
+
return 1.0/0.0 if ["Infinity", 1.0/0.0].include? value
|
34
|
+
value.to_i
|
35
|
+
end
|
36
|
+
end
|
28
37
|
end
|
29
38
|
end
|
@@ -36,7 +36,7 @@ module Adhearsion
|
|
36
36
|
# When a stop is requested, change our status to "Do Not Disturb"
|
37
37
|
# This should prevent the telephony engine from sending us any new calls.
|
38
38
|
Events.register_callback :stop_requested do
|
39
|
-
connection.not_ready!
|
39
|
+
connection.not_ready! if connection.connected?
|
40
40
|
end
|
41
41
|
|
42
42
|
# Make sure we stop everything when we shutdown
|
@@ -70,12 +70,19 @@ module Adhearsion
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def connect
|
73
|
+
return unless Process.state_name == :booting
|
73
74
|
m = Mutex.new
|
74
75
|
blocker = ConditionVariable.new
|
76
|
+
|
75
77
|
Events.punchblock ::Punchblock::Connection::Connected do
|
76
78
|
m.synchronize { blocker.broadcast }
|
77
79
|
end
|
78
80
|
|
81
|
+
Events.shutdown do
|
82
|
+
logger.info "Shutting down while connecting. Breaking the connection block."
|
83
|
+
m.synchronize { blocker.broadcast }
|
84
|
+
end
|
85
|
+
|
79
86
|
Adhearsion::Process.important_threads << Thread.new do
|
80
87
|
catching_standard_errors { connect_to_server }
|
81
88
|
end
|
@@ -22,9 +22,7 @@ module Adhearsion
|
|
22
22
|
def dispatcher
|
23
23
|
@dispatcher ||= lambda do |call|
|
24
24
|
controller = if target.respond_to?(:call)
|
25
|
-
|
26
|
-
controller.dialplan = target
|
27
|
-
end
|
25
|
+
CallController.new call, &target
|
28
26
|
else
|
29
27
|
target.new call
|
30
28
|
end
|
data/lib/adhearsion/tasks.rb
CHANGED
@@ -1,22 +1,16 @@
|
|
1
1
|
require 'adhearsion'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
testing
|
6
|
-
plugins
|
7
|
-
>.each do |file|
|
8
|
-
require "adhearsion/tasks/#{file}"
|
3
|
+
Dir[File.join(File.dirname(__FILE__), "tasks/*.rb")].each do |file|
|
4
|
+
require file
|
9
5
|
end
|
10
6
|
|
11
7
|
Adhearsion::Plugin.load_tasks
|
12
8
|
|
13
9
|
puts "\nAdhearsion configured environment: #{Adhearsion.config.platform.environment}\n" unless ARGV.empty?
|
14
10
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
puts "Adhearsion version: #{Adhearsion::VERSION}"
|
19
|
-
end
|
11
|
+
desc "Dump useful information about this application's Adhearsion environment"
|
12
|
+
task :about do
|
13
|
+
puts "Adhearsion version: #{Adhearsion::VERSION}"
|
20
14
|
end
|
21
15
|
|
22
|
-
task :default =>
|
16
|
+
task :default => :about
|