rastman 0.1.4

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 (6) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README +31 -0
  3. data/TODO +4 -0
  4. data/bin/rastman_mkcalls.rb +181 -0
  5. data/lib/rastman.rb +352 -0
  6. metadata +58 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Mathieu Lajugie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,31 @@
1
+ # = Rastman is a Ruby interface for the Asterisk Manager API
2
+ #
3
+ # When creating a new Rastman::Manager instance, it connects to an Asterisk server
4
+ # on the manager port (<tt>5038</tt> by default), sends a login request
5
+ # and listens to events published by the server. The instance can also
6
+ # be used to send Manager API requests to the server.
7
+ #
8
+ # == Example
9
+ #
10
+ # require "rubygems"
11
+ # require "rastman"
12
+ #
13
+ # rastman = Rastman::Manager.new("myuser", "secret")
14
+ #
15
+ # rastman.add_event_hook(:event) { |evt| puts "Event received: [#{evt.inspect}]"
16
+ #
17
+ # rastman.connect
18
+ #
19
+ # rastman.events(:eventmask => "user,call")
20
+ #
21
+ # rastman.ping
22
+ #
23
+ # rastman.originate :channel => "SIP/myphone@mydomain.com",
24
+ # :context => "default", :exten => "voicemail",
25
+ # :priority => 1
26
+ #
27
+ # rastman.logoff
28
+ #
29
+ #
30
+ # Created by Mathieul on 2007-02-08.
31
+ # Copyright (c) 2007. All rights reserved.
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ [TODO]
2
+ * [X] Rastman library
3
+ * [X] testing suite using RSpec
4
+ * [X] tool to generate calls
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env ruby
2
+ # $Id$
3
+
4
+ require "optparse"
5
+ require "ostruct"
6
+ require "yaml"
7
+ require "rubygems"
8
+ require "rastman"
9
+
10
+ CMD = File.basename(__FILE__)
11
+
12
+ class CmdLine
13
+
14
+ def self.parse(args, opts)
15
+ # set default options
16
+ opts[:login] ||= "username"
17
+ opts[:pwd] ||= "secret"
18
+ opts[:host] ||= "localhost"
19
+ opts[:recipient] ||= "1234567890@example.com"
20
+ opts[:context] ||= "default"
21
+ opts[:exten] ||= "987"
22
+ opts[:rounds] ||= 1
23
+ opts[:rounds_delay] ||= 20
24
+ opts[:calls] ||= 1
25
+ opts[:calls_delay] ||= 1
26
+ opts[:view_events] = false if opts[:view_events].nil?
27
+ opts[:rc] = false
28
+
29
+ o = OptionParser.new do |o|
30
+ o.banner = "Usage: #{CMD} [options]"
31
+ o.separator ""
32
+ o.separator "Options:"
33
+
34
+ o.on("-l", "--login <login>",
35
+ String,
36
+ "Login to use from .../etc/manager.conf (default: #{opts[:login]})") do |login|
37
+ opts[:login] = login
38
+ end
39
+
40
+ o.on("-p", "--pwd <password>",
41
+ String,
42
+ "Password as declared in .../etc/manager.conf (default: #{opts[:pwd]})") do |pwd|
43
+ opts[:pwd] = pwd
44
+ end
45
+
46
+ o.on("-h", "--host <asterisk host name>",
47
+ String,
48
+ "Host name running the Asterisk server (default: #{opts[:host]})") do |host|
49
+ opts[:host] = host
50
+ end
51
+
52
+ o.on("-r", "--recipient <URI>",
53
+ String,
54
+ "Recipient URI (default: #{opts[:recipient]})") do |recipient|
55
+ opts[:recipient] = recipient
56
+ end
57
+
58
+ o.on("-c", "--context <context>",
59
+ String,
60
+ "Dialplan Context to connect the call to (default: #{opts[:context]})") do |context|
61
+ opts[:context] = context
62
+ end
63
+
64
+ o.on("-e", "--exten <extension>",
65
+ String,
66
+ "Dialplan Extension to connect the call to (default: #{opts[:exten]})") do |exten|
67
+ opts[:exten] = exten
68
+ end
69
+
70
+ o.on("-n", "--num-rounds <number>",
71
+ Integer,
72
+ "Number of rounds of call generation to make (default: #{opts[:rounds]})") do |rounds|
73
+ opts[:rounds] = rounds
74
+ end
75
+
76
+ o.on("-d", "--rounds-delay <delay>",
77
+ Float,
78
+ "Number of seconds between two rounds (default: #{opts[:rounds_delay]})") do |delay|
79
+ opts[:rounds_delay] = delay
80
+ end
81
+
82
+ o.on("-a", "--num-calls <number>",
83
+ Integer,
84
+ "Number of rounds of call generation to make (default: #{opts[:calls]})") do |calls|
85
+ opts[:calls] = calls
86
+ end
87
+
88
+ o.on("-t", "--calls-delay <delay>",
89
+ Float,
90
+ "Number of seconds between two calls (default: #{opts[:calls_delay]})") do |delay|
91
+ opts[:calls_delay] = delay
92
+ end
93
+
94
+ o.on("-v", "--view-events",
95
+ "View events generated by Asterisk (default: #{opts[:view_events]})") do |view_events|
96
+ opts[:view_events] = view_events
97
+ end
98
+
99
+ o.on("-m", "--[no-]make-rc",
100
+ "Save options in $HOME/.rastman_mkcallsrc instead of generating calls") do |rc|
101
+ opts[:rc] = rc
102
+ end
103
+
104
+ o.on_tail("-?", "--help", "Show this message") do
105
+ puts o
106
+ exit
107
+ end
108
+
109
+ o.parse!(args)
110
+ end
111
+ opts
112
+ end
113
+
114
+ end
115
+
116
+ # retrieve default parameters if resource file exists
117
+ rc_options, rc_file = {}, File.join(ENV["HOME"], ".rastman_mkcallsrc")
118
+ rc_options = YAML::load(File.read(rc_file)) if File.exists?(rc_file)
119
+
120
+ # parse command line
121
+ opts = CmdLine.parse(ARGV, rc_options)
122
+
123
+ # save resource file if asked for
124
+ if opts[:rc] == true
125
+ opts.delete(:rc)
126
+ File.open(rc_file, "w") { |f| f.write(YAML::dump(opts)) }
127
+ puts "#{CMD}: options saved in #{rc_file}."
128
+ exit
129
+ end
130
+
131
+ cmdr = Rastman::Manager.new(opts[:login], opts[:pwd], :host => opts[:host],
132
+ :connect => true, :eventmask => "off")
133
+
134
+ lsnr = nil
135
+ if opts[:view_events] == true
136
+ require 'pp'
137
+ lsnr = Rastman::Manager.new(opts[:login], opts[:pwd], :host => opts[:host])
138
+ lsnr.add_event_hook(:event) { |evt| pp evt; puts }
139
+ lsnr.connect
140
+ end
141
+
142
+ Signal.trap("INT") do
143
+ puts "#{CMD}: Logging off. Please wait for Asterisk to complete the logoff request."
144
+ cmdr.logoff
145
+ lsnr.logoff unless lsnr.nil?
146
+ Signal.trap("INT", "DEFAULT")
147
+ end
148
+
149
+ to = (opts[:recipient].include?("/") ? "": "SIP/") + opts[:recipient]
150
+
151
+ puts "Starting #{opts[:rounds]} rounds of #{opts[:calls]} calls."
152
+ puts "Call delay is #{opts[:calls_delay]} secs."
153
+ puts "Round delay is #{opts[:rounds_delay]} secs."
154
+ puts
155
+
156
+ # round loop
157
+ (1..opts[:rounds]).each do |round_number|
158
+ puts "Round ##{round_number}: #{opts[:calls]} calls"
159
+ caller_id_prefix = "#{Time.now.strftime "1%M%S"}"
160
+ # calls loop
161
+ (1..opts[:calls]).each do |call_number|
162
+ puts " Call ##{call_number}"
163
+ cmdr.originate(:channel => to,
164
+ :context => opts[:context],
165
+ :exten => opts[:exten],
166
+ :priority => 1,
167
+ :callerid => %|#{caller_id_prefix}#{"%05d" % call_number}|)
168
+ sleep(opts[:calls_delay])
169
+ end
170
+ sleep(opts[:rounds_delay]) unless round_number == opts[:rounds]
171
+ end
172
+
173
+ if lsnr.nil?
174
+ cmdr.logoff
175
+ else
176
+ sleep 0.1 while cmdr.connected?
177
+ end
178
+
179
+ #
180
+ # Created by Mathieul on 2007-02-08.
181
+ # Copyright (c) 2007. All rights reserved.
data/lib/rastman.rb ADDED
@@ -0,0 +1,352 @@
1
+ # $Id$
2
+ # RASTMAN - Ruby Asterisk Manager api
3
+ #
4
+
5
+ require 'monitor'
6
+ require 'socket'
7
+ require 'timeout'
8
+ require 'logger'
9
+
10
+ $rnlog = Logger.new(STDOUT)
11
+ $rnlog.level = Logger::ERROR
12
+
13
+ # The Rastman module contains the Rastman::Manager class that is used to
14
+ # connect to an Asterisk server to send requests and listen to events.
15
+ module Rastman
16
+
17
+ # Generic Rastman error
18
+ class RastmanError < StandardError; end
19
+ # Rastman Login error
20
+ class LoginError < RastmanError; end
21
+ # Rastman Connection error
22
+ class NotConnectedError < RastmanError; end
23
+ # Rastman Deconnection error
24
+ class DisconnectedError < RastmanError; end
25
+
26
+ LINE_SEPARATOR = "\r\n"
27
+
28
+ class << self
29
+
30
+ # Change the log level (default: Logger::ERROR).
31
+ # Can be set to any of the following:
32
+ # * Logger::DEBUG
33
+ # * Logger::INFO
34
+ # * Logger::WARN
35
+ # * Logger::ERROR
36
+ # * Logger::FATAL
37
+ # * Logger::ANY
38
+ def log_level=(level)
39
+ $rnlog.level = level
40
+ end
41
+
42
+ # Return the current log level.
43
+ def log_level
44
+ $rnlog.level
45
+ end
46
+
47
+ # Change it for a new one
48
+ def set_logger(new_logger)
49
+ $rnlog = new_logger
50
+ $rnlog.info('Rastman#set_logger: done')
51
+ end
52
+
53
+ end
54
+
55
+ module Parser
56
+
57
+ def parse_line(line) # :nodoc:
58
+ raise LocalJumpError, "no block given" unless block_given?
59
+ @buffer ||= ""
60
+ @buffer += line
61
+ # if the line doesn't end with CRLF, we store it for the next call
62
+ if /(^[\w\s\/-]*):[\s]*(.*)\r\n$/m =~ @buffer
63
+ yield($1.downcase.to_sym, $2)
64
+ @buffer = ""
65
+ else
66
+ if @buffer[-2..-1] == "\r\n"
67
+ yield(:unknown, "UNKNOWN")
68
+ @buffer = ""
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ # A connection with an Asterisk server is established through a
76
+ # Rastman::Manager object. This object can then be used to
77
+ # send Asterisk Manager requests and listens to Asterisk events.
78
+ #
79
+ # Note that events generated from the result of a request won't be sent
80
+ # immediately back by Asterisk on the same connection.
81
+ # So you should probably use one object to send the requests,
82
+ # and one object to listen to the events.
83
+ class Manager
84
+ include Parser
85
+
86
+ attr_reader :host, :login
87
+
88
+ COMMANDS = [:login, :logoff, :events, :originate, :originate!, :redirect,
89
+ :redirect!, :hangup, :hangup!, :ping, :ping!, :setvar, :setvar!,
90
+ :command, :command!]
91
+ WAIT_FOR_ANSWER = 0.1
92
+
93
+ # Returns a Rastman::Manager object. The login and pwd parameters
94
+ # are the username and secret attributes of the user configured
95
+ # in /path/to/asterisk/configuration/manager.conf.
96
+ #
97
+ # The following options are also accepted:
98
+ # * <tt>:port</tt>: Manager port to connect to (<i>default: 5038</i>),
99
+ # * <tt>:host</tt>: Host name where Asterisk is running (<i>default: localhost</i>),
100
+ # * <tt>:reconnect_timeout</tt>: Timeout between two reconnect attempts (<i>default: 1</i>),
101
+ # * <tt>:connect</tt>: Flag to request a connection right away (<i>default: false</i>).
102
+ # * <tt>:eventmask</tt>: Manager event mask (i.e.: "user,call") (<i>default: on</i>).
103
+ def initialize(login, pwd, opts = {})
104
+ @login, @pwd = login, pwd
105
+ @port = opts[:port] || 5038
106
+ @host = opts[:host] || 'localhost'
107
+ @version = opts[:version]
108
+ @eventmask = opts[:eventmask] || 'on'
109
+ @reconnect_timeout = opts[:reconnect_timeout] || 1
110
+ @connected, @hooks = false, {}
111
+ @conn_lock = nil
112
+ @conn_lock.extend(MonitorMixin)
113
+ add_event_hook(:reconnect) { event_reconnect }
114
+ connect if opts[:connect]
115
+ end
116
+
117
+ # Connects to the server, attempt to login, and start listening to
118
+ # events if successfull.
119
+ #
120
+ # If +should_reconnect+ is true, then the object will attempt to reconnect
121
+ # until successful (the reconnection timeout is configured in new).
122
+ def connect(should_reconnect = true)
123
+ @conn = TCPSocket.new(@host, @port)
124
+ login
125
+ @should_reconnect = should_reconnect
126
+ @listener = Listener.new(@conn, @hooks, :version => @version)
127
+ @listener.start
128
+ end
129
+
130
+ # Returns true if the object is currently connected to the server.
131
+ def connected?
132
+ $rnlog.debug("Rastman::Manager#connected?: @connected = #{@connected}")
133
+ @connected == true
134
+ end
135
+
136
+ # Disconnects from the server (without sending the _LogOff_ request).
137
+ def disconnect
138
+ raise NotConnectedError, "No active connection" if @conn.closed?
139
+ @should_reconnect = false
140
+ @conn.close
141
+ $rnlog.debug("Rastman::Manager#disconnect: closed connection")
142
+ end
143
+
144
+ # Adds (or replaces) a block to be executed when the specified event occurs:
145
+ # * <tt>:event</tt>: an event was received
146
+ # * <tt>:action</tt>: a response to an action was received
147
+ # * <tt>:reconnect</tt>: the object got reconnected to the server
148
+ # * <tt>:disconnect</tt>: the object got disconnected from the server
149
+ def add_event_hook(event, &block)
150
+ check_supported_event(event)
151
+ @hooks[event] = block
152
+ end
153
+
154
+ # Delete the block that was to be executed for the specified event.
155
+ def del_event_hook(event)
156
+ check_supported_event(event)
157
+ @hooks.delete(event)
158
+ end
159
+
160
+ def join
161
+ @listener.join
162
+ end
163
+
164
+ private
165
+ def check_supported_event(event)
166
+ unless [:event, :action, :disconnect, :reconnect].include?(event)
167
+ raise ArgumentError, "Unsupported event #{event}"
168
+ end
169
+ end
170
+
171
+ def event_reconnect #:nodoc:
172
+ @connected = false
173
+ $rnlog.debug("Rastman::Manager#event_reconnect: should_reconnect(#{@should_reconnect})")
174
+ @hooks[:disconnect].call({:event => :disconnect}) unless @hooks[:disconnect].nil?
175
+ if @should_reconnect == true
176
+ while @connected == false
177
+ sleep @reconnect_timeout
178
+ connect rescue nil
179
+ end
180
+ end
181
+ end
182
+
183
+ def login # :nodoc:
184
+ send_action({ :action => 'login', :username => @login, :secret => @pwd, :events => @eventmask })
185
+ wait_for_login_acknowledgement
186
+ end
187
+
188
+ def wait_for_login_acknowledgement # :nodoc:
189
+ Timeout.timeout(10) do
190
+ event, done = {}, false
191
+ until done == true do
192
+ @conn.each(LINE_SEPARATOR) do |line|
193
+ if line == LINE_SEPARATOR
194
+ done = true
195
+ break
196
+ else
197
+ parse_line(line) { |key, value| event[key] = value }
198
+ end
199
+ done = true if @conn.closed?
200
+ end
201
+ if event[:response] == "Success"
202
+ @connected = true
203
+ else
204
+ raise LoginError, event[:message]
205
+ end
206
+ $rnlog.debug("Rastman::Manager#wait_for_login_acknowledgement: @connected(#{@connected}) event(#{event.inspect})")
207
+ end
208
+ end
209
+ rescue Timeout::Error
210
+ raise NotConnectedError, "No answer received for login request"
211
+ end
212
+
213
+ # send_action actually send the action requested to Asterisk.
214
+ # If expects_answer_before is NOT set (default), it sends the
215
+ # request and returns nil right away.
216
+ # If expects_answer_before is set, it sends the request, waits
217
+ # for expects_answer_before seconds or until the response is received
218
+ # (whichever comes first) and returns true if the response if received,
219
+ # false if not.
220
+ def send_action(action, expects_answer_before = nil) # :nodoc:
221
+ $rnlog.debug("Rastman::Manager#send_action: SEND '#{action[:action]}' (#{action.inspect})")
222
+ unless expects_answer_before.nil?
223
+ action_id = action[:actionid] ||= "#{action[:action]}-#{action.hash}"
224
+ answer = []
225
+ @listener.request_answer_for(action_id, answer)
226
+ end
227
+ @conn_lock.synchronize do
228
+ action.each do |key, value|
229
+ $rnlog.debug("Rastman::Manager#send_action: write (#{key.to_s}: #{value}\\r\\n)")
230
+ @conn.write("#{key.to_s}: #{value}\r\n")
231
+ end
232
+ $rnlog.debug("Rastman::Manager#send_action: write (\\r\\n)")
233
+ @conn.write("\r\n")
234
+ end
235
+ result = nil
236
+ unless expects_answer_before.nil?
237
+ waited = 0
238
+ until answer.length > 0
239
+ sleep WAIT_FOR_ANSWER
240
+ waited += WAIT_FOR_ANSWER
241
+ if waited >= expects_answer_before
242
+ answer << {}
243
+ end
244
+ end
245
+ result = answer.first[:response] == "Success" ? true : false rescue false
246
+ end
247
+ return result
248
+ rescue Exception => ex
249
+ $rnlog.info("Rastman::Manager#send_action: exception caught (connection probably closed already): #{ex}")
250
+ end
251
+
252
+ def method_missing(meth, *args, &block) # :nodoc:
253
+ unless COMMANDS.include?(meth)
254
+ raise NoMethodError.new(
255
+ "undefined method `#{meth}' for " +
256
+ "#{self.inspect}:#{self.class.name}"
257
+ )
258
+ end
259
+ command = meth.to_s.downcase
260
+ if command[-1] == ?!
261
+ case args.size
262
+ when 1
263
+ expects_answer_before = 10
264
+ action = args.first
265
+ when 2
266
+ expects_answer_before, action = args
267
+ else
268
+ raise ArgumentError, "#{meth} wrong number of arguments (#{args.size} for 1 or 2)"
269
+ end
270
+ command.chop!
271
+ else
272
+ action = args.size == 0 ? {} : args[0]
273
+ expects_answer_before = nil
274
+ end
275
+ action.merge!({ :action => command })
276
+ @should_reconnect = false if command == "logoff"
277
+ send_action(action, expects_answer_before)
278
+ end
279
+
280
+ end
281
+
282
+ class Listener # :nodoc: all
283
+ include Parser
284
+
285
+ def initialize(conn, hooks, opts = {})
286
+ @conn, @hooks = conn, hooks
287
+ @action_ids = {}
288
+ @mode_1_0 = opts[:version] == "1.0"
289
+ end
290
+
291
+ def start
292
+ @th = Thread.new do
293
+ Thread.current.abort_on_exception = true
294
+ begin
295
+ event = {}
296
+ @conn.each(LINE_SEPARATOR) do |line|
297
+ $rnlog.debug("Rastman::Listener#start: read (#{line.strip})")
298
+ if line == LINE_SEPARATOR
299
+ if event[:event].nil?
300
+ @hooks[:action].call(event.clone) unless @hooks[:action].nil?
301
+ unless event[:actionid].nil?
302
+ container = @action_ids.delete(event[:actionid])
303
+ container << event.clone unless container.nil?
304
+ end
305
+ else
306
+ reformat_userevent(event) if @mode_1_0
307
+ @hooks[:event].call(event.clone) unless @hooks[:event].nil?
308
+ end
309
+ event.clear
310
+ else
311
+ parse_line(line) { |key, value| event[key] = value }
312
+ end
313
+ end
314
+ rescue IOError => e
315
+ $rnlog.warn("Rastman::Listener#start: IOError (#{e}) occured => we close the connection")
316
+ @conn.close rescue nil
317
+ end
318
+ $rnlog.info("Rastman::Listener#start: calling reconnect hook before stopping")
319
+ @hooks[:reconnect].call({:event => :reconnect}) unless @hooks[:reconnect].nil?
320
+ end
321
+ end
322
+
323
+ def join
324
+ @th.join
325
+ end
326
+
327
+ def request_answer_for(id, container)
328
+ @action_ids[id] = container
329
+ end
330
+
331
+ private
332
+ def reformat_userevent(evt)
333
+ name = evt[:event].split("UserEvent")[1]
334
+ unless name.nil?
335
+ evt[:userevent] = name
336
+ evt[:event] = "UserEvent"
337
+ vars = evt.delete(:variables)
338
+ vars.split("|").each do |pair|
339
+ k, v = pair.split(/: */)
340
+ evt[k.downcase.to_sym] = v || ""
341
+ end unless vars.nil?
342
+ evt.delete(:channel)
343
+ end
344
+ end
345
+
346
+ end
347
+
348
+ end
349
+
350
+ #
351
+ # Created by Mathieul on 2007-02-08.
352
+ # Copyright (c) 2007. All rights reserved.
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: rastman
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.4
7
+ date: 2007-06-28 00:00:00 -07:00
8
+ summary: Asterisk Manager API interface for Ruby
9
+ require_paths:
10
+ - lib
11
+ email: mathieul@zlaj.org
12
+ homepage: http://rastman.rubyforge.org/
13
+ rubyforge_project:
14
+ description: Asterisk Manager API interface for Ruby
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.8.4
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Mathieu Lajugie
31
+ files:
32
+ - MIT-LICENSE
33
+ - README
34
+ - TODO
35
+ - bin/rastman_mkcalls.rb
36
+ - lib/rastman.rb
37
+ test_files: []
38
+
39
+ rdoc_options:
40
+ - --quiet
41
+ - --title
42
+ - Rastman Documentation
43
+ - --opname
44
+ - index.html
45
+ - --line-numbers
46
+ - --main
47
+ - README
48
+ - --inline-source
49
+ extra_rdoc_files:
50
+ - README
51
+ executables:
52
+ - rastman_mkcalls.rb
53
+ extensions: []
54
+
55
+ requirements: []
56
+
57
+ dependencies: []
58
+