ronin 0.2.2 → 0.2.3
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/History.txt +53 -0
- data/Manifest.txt +26 -18
- data/README.txt +12 -19
- data/Rakefile +9 -9
- data/TODO.txt +5 -5
- data/lib/ronin/cacheable.rb +246 -0
- data/lib/ronin/database/database.rb +21 -3
- data/lib/ronin/database/exceptions/invalid_config.rb +1 -1
- data/lib/ronin/environment.rb +5 -2
- data/lib/ronin/extensions.rb +2 -0
- data/lib/ronin/{models.rb → extensions/array.rb} +19 -6
- data/lib/ronin/extensions/ip_addr.rb +127 -0
- data/lib/ronin/{objectify/exceptions/object_context_not_found.rb → extensions/kernel.rb} +14 -3
- data/lib/ronin/extensions/uri/query_params.rb +4 -2
- data/lib/ronin/formatting/extensions/binary/integer.rb +6 -0
- data/lib/ronin/formatting/extensions/binary/string.rb +14 -12
- data/lib/ronin/formatting/extensions/text/string.rb +37 -19
- data/lib/ronin/has_license.rb +4 -2
- data/lib/ronin/model.rb +0 -1
- data/lib/ronin/network/extensions/http/net.rb +30 -3
- data/lib/ronin/network/extensions/telnet/net.rb +0 -18
- data/lib/ronin/network/http/exceptions/unknown_request.rb +1 -1
- data/lib/ronin/platform/exceptions/extension_not_found.rb +1 -1
- data/lib/ronin/platform/exceptions/overlay_cached.rb +1 -1
- data/lib/ronin/platform/exceptions/overlay_not_found.rb +1 -1
- data/lib/ronin/platform/extension.rb +18 -3
- data/lib/ronin/platform/extension_cache.rb +7 -1
- data/lib/ronin/platform/object_cache.rb +13 -12
- data/lib/ronin/platform/overlay.rb +14 -6
- data/lib/ronin/platform/overlay_cache.rb +11 -5
- data/lib/ronin/product.rb +20 -3
- data/lib/ronin/ronin.rb +0 -15
- data/lib/ronin/rpc/exceptions/response_missing.rb +1 -1
- data/lib/ronin/sessions/esmtp.rb +1 -10
- data/lib/ronin/{objectify.rb → sessions/exceptions.rb} +3 -3
- data/lib/ronin/{objectify/exceptions/unknown_object_context.rb → sessions/exceptions/variable_missing.rb} +4 -4
- data/lib/ronin/sessions/http.rb +3 -10
- data/lib/ronin/sessions/imap.rb +1 -10
- data/lib/ronin/sessions/pop3.rb +1 -9
- data/lib/ronin/sessions/session.rb +11 -21
- data/lib/ronin/sessions/smtp.rb +1 -10
- data/lib/ronin/sessions/tcp.rb +10 -13
- data/lib/ronin/sessions/telnet.rb +2 -17
- data/lib/ronin/sessions/udp.rb +6 -11
- data/lib/ronin/static/finders.rb +24 -0
- data/lib/ronin/ui/command_line/command_line.rb +41 -21
- data/lib/ronin/ui/command_line/commands/help.rb +7 -3
- data/lib/ronin/ui/command_line/commands/ls.rb +1 -1
- data/lib/ronin/ui/command_line/commands/rm.rb +1 -1
- data/lib/ronin/ui/command_line/exceptions/unknown_command.rb +1 -1
- data/lib/ronin/ui/diagnostics.rb +12 -5
- data/lib/ronin/ui/hexdump/extensions/file.rb +3 -1
- data/lib/ronin/ui/hexdump/hexdump.rb +1 -1
- data/lib/ronin/ui/verbose.rb +14 -0
- data/lib/ronin/version.rb +1 -1
- data/spec/cacheable_spec.rb +150 -0
- data/spec/classes/cacheable_model.rb +15 -0
- data/spec/classes/licensed_model.rb +12 -0
- data/spec/code/classes/thing.rb +13 -0
- data/spec/code/reference_spec.rb +1 -14
- data/spec/extensions/array_spec.rb +34 -0
- data/spec/extensions/ip_addr_spec.rb +44 -0
- data/spec/extensions/kernel_spec.rb +19 -0
- data/spec/extensions/uri/query_params_spec.rb +8 -0
- data/spec/formatting/binary/string_spec.rb +1 -1
- data/spec/formatting/digest/string_spec.rb +84 -0
- data/spec/formatting/http/string_spec.rb +84 -0
- data/spec/formatting/text/string_spec.rb +51 -0
- data/spec/has_license_spec.rb +29 -0
- data/spec/helpers/cacheable.rb +7 -0
- data/spec/helpers/contexts/ronin_cacheable_model.rb +13 -0
- data/spec/helpers/database.rb +5 -0
- data/spec/platform/helpers/overlays/hello/lib/init.rb +1 -0
- data/spec/platform/helpers/overlays/hello/lib/stuff/another_test.rb +6 -0
- data/spec/platform/helpers/overlays/hello/lib/stuff/test.rb +4 -0
- data/spec/platform/overlay_cache_spec.rb +1 -1
- data/spec/platform/overlay_spec.rb +28 -0
- data/spec/product_spec.rb +7 -0
- data/spec/sessions/classes/test_session.rb +11 -0
- data/spec/sessions/classes/uses_test_session.rb +10 -0
- data/spec/sessions/session_spec.rb +13 -44
- data/spec/spec_helper.rb +0 -5
- data/spec/static/{helpers → classes}/static_class.rb +0 -0
- data/spec/static/helpers/static.rb +0 -1
- data/spec/static/static_spec.rb +1 -0
- metadata +44 -35
- data/lib/ronin/objectify/exceptions.rb +0 -25
- data/lib/ronin/objectify/objectify.rb +0 -240
- data/lib/ronin/target.rb +0 -44
- data/lib/ronin/ui/command_line/param_parser.rb +0 -93
- data/spec/formatting/digest_spec.rb +0 -54
- data/spec/formatting/http_spec.rb +0 -53
- data/spec/formatting/text_spec.rb +0 -40
- data/spec/helpers.rb +0 -0
- data/spec/objectify/objectify_spec.rb +0 -31
- data/spec/target_spec.rb +0 -16
- data/spec/ui/command_line/helpers/example_command.rb +0 -21
- data/spec/ui/command_line/param_parser_spec.rb +0 -49
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
#
|
|
23
23
|
|
|
24
24
|
require 'ronin/database/exceptions/invalid_config'
|
|
25
|
+
require 'ronin/model'
|
|
25
26
|
require 'ronin/arch'
|
|
26
27
|
require 'ronin/os'
|
|
27
28
|
require 'ronin/author'
|
|
@@ -103,6 +104,25 @@ module Ronin
|
|
|
103
104
|
return @@ronin_database_log = DataMapper::Logger.new(stream,level)
|
|
104
105
|
end
|
|
105
106
|
|
|
107
|
+
#
|
|
108
|
+
# Returns +true+ if the Database is setup, returns +false+ otherwise.
|
|
109
|
+
#
|
|
110
|
+
def Database.setup?
|
|
111
|
+
repository = DataMapper.repository(Model::REPOSITORY_NAME)
|
|
112
|
+
|
|
113
|
+
return repository.class.adapters.has_key?(repository.name)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
#
|
|
117
|
+
# Call the given _block_ then run auto-upgrades on the Database.
|
|
118
|
+
#
|
|
119
|
+
def Database.update!(&block)
|
|
120
|
+
block.call if block
|
|
121
|
+
|
|
122
|
+
DataMapper.auto_upgrade!(Model::REPOSITORY_NAME) if Database.setup?
|
|
123
|
+
return nil
|
|
124
|
+
end
|
|
125
|
+
|
|
106
126
|
#
|
|
107
127
|
# Sets up the Database with the given _configuration_. If
|
|
108
128
|
# _configuration is not given, +DEFAULT_CONFIG+ will be used to setup
|
|
@@ -115,9 +135,7 @@ module Ronin
|
|
|
115
135
|
# setup the database repository
|
|
116
136
|
DataMapper.setup(Model::REPOSITORY_NAME, configuration)
|
|
117
137
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
DataMapper.auto_upgrade!(Model::REPOSITORY_NAME)
|
|
138
|
+
Database.update!(&block)
|
|
121
139
|
return nil
|
|
122
140
|
end
|
|
123
141
|
end
|
data/lib/ronin/environment.rb
CHANGED
|
@@ -26,11 +26,14 @@ require 'ronin/extensions'
|
|
|
26
26
|
require 'ronin/formatting'
|
|
27
27
|
require 'ronin/path'
|
|
28
28
|
require 'ronin/network'
|
|
29
|
-
require 'ronin/
|
|
29
|
+
require 'ronin/database'
|
|
30
30
|
require 'ronin/ui'
|
|
31
31
|
require 'ronin/ronin'
|
|
32
32
|
|
|
33
33
|
require 'chars'
|
|
34
34
|
require 'pp'
|
|
35
35
|
|
|
36
|
-
Ronin
|
|
36
|
+
module Ronin
|
|
37
|
+
Config.load
|
|
38
|
+
Database.setup
|
|
39
|
+
end
|
data/lib/ronin/extensions.rb
CHANGED
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
#
|
|
23
23
|
|
|
24
24
|
require 'ronin/extensions/meta'
|
|
25
|
+
require 'ronin/extensions/kernel'
|
|
25
26
|
require 'ronin/extensions/hash'
|
|
26
27
|
require 'ronin/extensions/uri'
|
|
27
28
|
require 'ronin/extensions/string'
|
|
28
29
|
require 'ronin/extensions/file'
|
|
30
|
+
require 'ronin/extensions/ip_addr'
|
|
@@ -21,13 +21,26 @@
|
|
|
21
21
|
#++
|
|
22
22
|
#
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
require 'ronin/model'
|
|
24
|
+
class Array
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
#
|
|
27
|
+
# Returns the power set of the array. Shamelessly borrowed from
|
|
28
|
+
# http://johncarrino.net/blog/2006/08/11/powerset-in-ruby/.
|
|
29
|
+
#
|
|
30
|
+
# [1,2,3].power_set
|
|
31
|
+
# # => [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
|
|
32
|
+
#
|
|
33
|
+
def power_set
|
|
34
|
+
inject([[]]) do |power_set,element|
|
|
35
|
+
sub_set = []
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
power_set.each do |i|
|
|
38
|
+
sub_set << i
|
|
39
|
+
sub_set << i + [element]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
sub_set
|
|
43
|
+
end
|
|
32
44
|
end
|
|
45
|
+
|
|
33
46
|
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#
|
|
2
|
+
#--
|
|
3
|
+
# Ronin - A Ruby platform designed for information security and data
|
|
4
|
+
# exploration tasks.
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2006-2009 Hal Brodigan (postmodern.mod3 at gmail.com)
|
|
7
|
+
#
|
|
8
|
+
# This program is free software; you can redistribute it and/or modify
|
|
9
|
+
# it under the terms of the GNU General Public License as published by
|
|
10
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
11
|
+
# (at your option) any later version.
|
|
12
|
+
#
|
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
# GNU General Public License for more details.
|
|
17
|
+
#
|
|
18
|
+
# You should have received a copy of the GNU General Public License
|
|
19
|
+
# along with this program; if not, write to the Free Software
|
|
20
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
21
|
+
#++
|
|
22
|
+
#
|
|
23
|
+
|
|
24
|
+
require 'ipaddr'
|
|
25
|
+
|
|
26
|
+
class IPAddr
|
|
27
|
+
|
|
28
|
+
include Enumerable
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# Iterates over the CIDR or globbed address range, passing each
|
|
32
|
+
# address in the range to the specified _block_. Supports both
|
|
33
|
+
# IPv4 and IPv6 address ranges.
|
|
34
|
+
#
|
|
35
|
+
# IPAddr.each('10.1.1.1/24') do |ip|
|
|
36
|
+
# ...
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# IPAddr.each('10.1.1-5.*') do |ip|
|
|
40
|
+
# ...
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# IPAddr.each('::ff::02-0a::c3') do |ip|
|
|
44
|
+
# ...
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
def IPAddr.each(cidr_or_glob,&block)
|
|
48
|
+
unless (cidr_or_glob.include?('*') || cidr_or_glob.include?('-'))
|
|
49
|
+
IPAddr.new(cidr_or_glob).each(&block)
|
|
50
|
+
return nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
if cidr_or_glob.include?('::')
|
|
54
|
+
prefix = if cidr_or_glob =~ /^::/
|
|
55
|
+
'::'
|
|
56
|
+
else
|
|
57
|
+
''
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
separator = '::'
|
|
61
|
+
base = 16
|
|
62
|
+
|
|
63
|
+
format = lambda { |address|
|
|
64
|
+
prefix + address.map { |i| '%.2x' % i }.join('::')
|
|
65
|
+
}
|
|
66
|
+
else
|
|
67
|
+
separator = '.'
|
|
68
|
+
base = 10
|
|
69
|
+
|
|
70
|
+
format = lambda { |address| address.join('.') }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
ranges = cidr_or_glob.split(separator).map { |segment|
|
|
74
|
+
if segment == '*'
|
|
75
|
+
(1..254)
|
|
76
|
+
elsif segment.include?('-')
|
|
77
|
+
start, stop = segment.split('-',2).map { |i| i.to_i(base) }
|
|
78
|
+
|
|
79
|
+
(start..stop)
|
|
80
|
+
elsif !(segment.empty?)
|
|
81
|
+
segment.to_i(base)
|
|
82
|
+
end
|
|
83
|
+
}.compact
|
|
84
|
+
|
|
85
|
+
expand_range = lambda { |address,remaining|
|
|
86
|
+
if remaining.empty?
|
|
87
|
+
block.call(format.call(address))
|
|
88
|
+
else
|
|
89
|
+
n = remaining.first
|
|
90
|
+
remaining = remaining[1..-1]
|
|
91
|
+
|
|
92
|
+
if n.kind_of?(Range)
|
|
93
|
+
n.each { |i| expand_range.call(address + [i], remaining) }
|
|
94
|
+
else
|
|
95
|
+
expand_range.call(address + [n], remaining)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
expand_range.call([], ranges)
|
|
101
|
+
return nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
#
|
|
105
|
+
# Iterates over each IP address that is included in the addresses mask,
|
|
106
|
+
# passing each address to the specified _block_.
|
|
107
|
+
#
|
|
108
|
+
# IPAddr.new('10.1.1.1/24').each do |ip|
|
|
109
|
+
# puts ip
|
|
110
|
+
# end
|
|
111
|
+
#
|
|
112
|
+
def each(&block)
|
|
113
|
+
case @family
|
|
114
|
+
when Socket::AF_INET
|
|
115
|
+
family_mask = IN4MASK
|
|
116
|
+
when Socket::AF_INET6
|
|
117
|
+
family_mask = IN6MASK
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
(0..((~@mask_addr) & family_mask)).each do |i|
|
|
121
|
+
block.call(_to_string(@addr | i))
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
return self
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end
|
|
@@ -21,9 +21,20 @@
|
|
|
21
21
|
#++
|
|
22
22
|
#
|
|
23
23
|
|
|
24
|
-
module
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
module Kernel
|
|
25
|
+
#
|
|
26
|
+
# Calls the given _block_ and ignores any raised exceptions.
|
|
27
|
+
# If an exception is raised, +nil+ will be returned.
|
|
28
|
+
#
|
|
29
|
+
# try do
|
|
30
|
+
# Resolv.getaddress('might.not.exist.com')
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
def try(&block)
|
|
34
|
+
begin
|
|
35
|
+
block.call if block
|
|
36
|
+
rescue
|
|
37
|
+
return nil
|
|
27
38
|
end
|
|
28
39
|
end
|
|
29
40
|
end
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
#++
|
|
22
22
|
#
|
|
23
23
|
|
|
24
|
+
require 'cgi'
|
|
25
|
+
|
|
24
26
|
module URI
|
|
25
27
|
module QueryParams
|
|
26
28
|
# Query parameters
|
|
@@ -89,9 +91,9 @@ module URI
|
|
|
89
91
|
"#{name}=active"
|
|
90
92
|
elsif value
|
|
91
93
|
if value.kind_of?(Array)
|
|
92
|
-
"#{name}=#{
|
|
94
|
+
"#{name}=#{CGI.escape(value.join(' '))}"
|
|
93
95
|
else
|
|
94
|
-
"#{name}=#{
|
|
96
|
+
"#{name}=#{CGI.escape(value.to_s)}"
|
|
95
97
|
end
|
|
96
98
|
else
|
|
97
99
|
"#{name}="
|
|
@@ -32,6 +32,12 @@ class Integer
|
|
|
32
32
|
# _endian_ must be either <tt>:little</tt>, <tt>:big</tt> or
|
|
33
33
|
# <tt>:net</tt>.
|
|
34
34
|
#
|
|
35
|
+
# 0xff41.bytes(2)
|
|
36
|
+
# # => [65, 255]
|
|
37
|
+
#
|
|
38
|
+
# 0xff41.bytes(4, :big)
|
|
39
|
+
# # => [0, 0, 255, 65]
|
|
40
|
+
#
|
|
35
41
|
def bytes(address_length,endian=:little)
|
|
36
42
|
endian = endian.to_s
|
|
37
43
|
buffer = []
|
|
@@ -40,19 +40,21 @@ class String
|
|
|
40
40
|
#
|
|
41
41
|
def depack(arch,address_length=arch.address_length)
|
|
42
42
|
integer = 0x0
|
|
43
|
+
byte_index = 0
|
|
43
44
|
|
|
44
|
-
if arch.endian=='little'
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
if arch.endian == 'little'
|
|
46
|
+
mask = lambda { |b| b << (byte_index * 8) }
|
|
47
|
+
elsif arch.endian == 'big'
|
|
48
|
+
mask = lambda { |b|
|
|
49
|
+
b << ((address_length - byte_index - 1) * 8)
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
self.each_byte do |b|
|
|
54
|
+
break if byte_index >= address_length
|
|
55
|
+
|
|
56
|
+
integer |= mask.call(b)
|
|
57
|
+
byte_index += 1
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
return integer
|
|
@@ -26,7 +26,7 @@ require 'chars'
|
|
|
26
26
|
class String
|
|
27
27
|
|
|
28
28
|
#
|
|
29
|
-
# Creates a new String by passing each
|
|
29
|
+
# Creates a new String by passing each byte to the specified _block_
|
|
30
30
|
# using the given _options_.
|
|
31
31
|
#
|
|
32
32
|
# _options_ may include the following keys:
|
|
@@ -34,7 +34,7 @@ class String
|
|
|
34
34
|
# defaults to <tt>Chars.all</tt>.
|
|
35
35
|
# <tt>:excluded</tt>:: The characters not to format.
|
|
36
36
|
#
|
|
37
|
-
def
|
|
37
|
+
def format_bytes(options={},&block)
|
|
38
38
|
included = (options[:included] || Chars.all)
|
|
39
39
|
excluded = (options[:excluded] || [])
|
|
40
40
|
|
|
@@ -42,12 +42,10 @@ class String
|
|
|
42
42
|
formatted = ''
|
|
43
43
|
|
|
44
44
|
self.each_byte do |b|
|
|
45
|
-
c = b.chr
|
|
46
|
-
|
|
47
45
|
if targeted.include_byte?(b)
|
|
48
|
-
formatted << block.call(
|
|
46
|
+
formatted << block.call(b)
|
|
49
47
|
else
|
|
50
|
-
formatted <<
|
|
48
|
+
formatted << b
|
|
51
49
|
end
|
|
52
50
|
end
|
|
53
51
|
|
|
@@ -55,7 +53,7 @@ class String
|
|
|
55
53
|
end
|
|
56
54
|
|
|
57
55
|
#
|
|
58
|
-
# Creates a new String by passing each
|
|
56
|
+
# Creates a new String by passing each character to the specified _block_
|
|
59
57
|
# using the given _options_.
|
|
60
58
|
#
|
|
61
59
|
# _options_ may include the following keys:
|
|
@@ -63,15 +61,9 @@ class String
|
|
|
63
61
|
# defaults to <tt>Chars.all</tt>.
|
|
64
62
|
# <tt>:excluded</tt>:: The characters not to format.
|
|
65
63
|
#
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if i.kind_of?(Integer)
|
|
71
|
-
i.chr
|
|
72
|
-
else
|
|
73
|
-
i.to_s
|
|
74
|
-
end
|
|
64
|
+
def format_chars(options={},&block)
|
|
65
|
+
format_bytes(options) do |b|
|
|
66
|
+
block.call(b.chr)
|
|
75
67
|
end
|
|
76
68
|
end
|
|
77
69
|
|
|
@@ -83,14 +75,14 @@ class String
|
|
|
83
75
|
# <tt>:probability</tt>:: The probability that a character will have it's
|
|
84
76
|
# case changed; defaults to 0.5.
|
|
85
77
|
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
78
|
+
# "get out your checkbook".random_case
|
|
79
|
+
# # => "gEt Out YOur CHEckbook"
|
|
88
80
|
#
|
|
89
81
|
def random_case(options={})
|
|
90
82
|
prob = (options[:probability] || 0.5)
|
|
91
83
|
|
|
92
84
|
format_chars(options) do |c|
|
|
93
|
-
if rand
|
|
85
|
+
if rand <= prob
|
|
94
86
|
c.swapcase
|
|
95
87
|
else
|
|
96
88
|
c
|
|
@@ -98,4 +90,30 @@ class String
|
|
|
98
90
|
end
|
|
99
91
|
end
|
|
100
92
|
|
|
93
|
+
#
|
|
94
|
+
# Pads the string using the specified _padding_ out to the given
|
|
95
|
+
# _max_length_. _max_length_ will default to the strings own length,
|
|
96
|
+
# if not given.
|
|
97
|
+
#
|
|
98
|
+
# "hello".pad('A',50)
|
|
99
|
+
# # => "helloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
|
100
|
+
#
|
|
101
|
+
def pad(padding,max_length=self.length)
|
|
102
|
+
padding = padding.to_s
|
|
103
|
+
|
|
104
|
+
if max_length >= self.length
|
|
105
|
+
max_length -= self.length
|
|
106
|
+
else
|
|
107
|
+
max_length = 0
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
padded = self + (padding * (max_length / padding.length))
|
|
111
|
+
|
|
112
|
+
unless (remaining = max_length % padding.length) == 0
|
|
113
|
+
padded += padding[0...remaining]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
return padded
|
|
117
|
+
end
|
|
118
|
+
|
|
101
119
|
end
|