rastman 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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
+