ronin 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/History.txt +13 -0
  2. data/Manifest.txt +19 -17
  3. data/README.txt +9 -9
  4. data/Rakefile +1 -1
  5. data/bin/ronin +2 -2
  6. data/lib/ronin/author.rb +1 -1
  7. data/lib/ronin/cache/extension.rb +4 -12
  8. data/lib/ronin/cache/extension_cache.rb +1 -3
  9. data/lib/ronin/cache/overlay.rb +3 -3
  10. data/lib/ronin/cache/overlay_cache.rb +21 -15
  11. data/lib/ronin/chars/char_set.rb +6 -12
  12. data/lib/ronin/code/reference.rb +142 -0
  13. data/lib/ronin/code/symbol_table.rb +90 -0
  14. data/lib/ronin/context.rb +1 -1
  15. data/lib/ronin/database.rb +12 -3
  16. data/lib/ronin/extensions/string.rb +49 -0
  17. data/lib/ronin/formatting/extensions/text/string.rb +10 -5
  18. data/lib/ronin/object_context.rb +3 -9
  19. data/lib/ronin/pending_context.rb +8 -0
  20. data/lib/ronin/platform.rb +2 -2
  21. data/lib/ronin/product.rb +2 -2
  22. data/lib/ronin/program/command.rb +203 -0
  23. data/lib/ronin/program/commands/add.rb +71 -0
  24. data/lib/ronin/{runner/program → program}/commands/help.rb +14 -16
  25. data/lib/ronin/program/commands/install.rb +64 -0
  26. data/lib/ronin/program/commands/list.rb +79 -0
  27. data/lib/ronin/{runner/program/commands/update.rb → program/commands/remove.rb} +18 -18
  28. data/lib/ronin/{runner/program/commands/remove.rb → program/commands/uninstall.rb} +17 -19
  29. data/lib/ronin/{runner/program/commands/uninstall.rb → program/commands/update.rb} +17 -21
  30. data/lib/ronin/{runner/program → program}/commands.rb +9 -9
  31. data/lib/ronin/{runner/program → program}/exceptions/unknown_command.rb +2 -4
  32. data/lib/ronin/{runner.rb → program/exceptions.rb} +1 -1
  33. data/lib/ronin/{runner/program → program}/options.rb +4 -8
  34. data/lib/ronin/program/program.rb +168 -0
  35. data/lib/ronin/{runner/program/exceptions.rb → program.rb} +2 -1
  36. data/lib/ronin/rpc/client.rb +1 -1
  37. data/lib/ronin/version.rb +1 -1
  38. data/spec/code/reference_spec.rb +72 -0
  39. data/spec/code/symbol_table_spec.rb +35 -0
  40. data/spec/extensions/string_spec.rb +88 -4
  41. data/spec/formatting/text_spec.rb +2 -2
  42. data/spec/helpers.rb +0 -0
  43. metadata +25 -23
  44. data/lib/ronin/runner/program/command.rb +0 -204
  45. data/lib/ronin/runner/program/commands/add.rb +0 -73
  46. data/lib/ronin/runner/program/commands/install.rb +0 -65
  47. data/lib/ronin/runner/program/commands/list.rb +0 -81
  48. data/lib/ronin/runner/program/program.rb +0 -175
  49. data/lib/ronin/runner/program/runner.rb +0 -35
  50. data/lib/ronin/runner/program.rb +0 -26
data/History.txt CHANGED
@@ -1,3 +1,16 @@
1
+ === 0.1.1 / 2008-10-26
2
+
3
+ * Depend on the newly renamed reverse-require (>= 0.1.2) gem.
4
+ * Added Code::SymbolTable for DSLs to make use of.
5
+ * Added String#common_prefix, String#common_postfix and
6
+ String#uncommon_substring, which will be useful when testing for
7
+ successful injections.
8
+ * Added DataMapper column indexes to Author.name, Product.name, Platform.os
9
+ and Platform.version.
10
+ * Renamed String#rand_case to String#random_case.
11
+ * Removed the Runner namespace, renaming the Runner::Program namespace to
12
+ Ronin::Program.
13
+
1
14
  === 0.1.0 / 2008-09-28
2
15
 
3
16
  * Changed how Sessions are setup.
data/Manifest.txt CHANGED
@@ -83,6 +83,8 @@ lib/ronin/network/telnet.rb
83
83
  lib/ronin/network/http.rb
84
84
  lib/ronin/network/http/exceptions.rb
85
85
  lib/ronin/network/http/exceptions/unknown_request.rb
86
+ lib/ronin/code/reference.rb
87
+ lib/ronin/code/symbol_table.rb
86
88
  lib/ronin/rpc.rb
87
89
  lib/ronin/rpc/exceptions.rb
88
90
  lib/ronin/rpc/exceptions/not_implemented.rb
@@ -135,26 +137,24 @@ lib/ronin/cache/extension.rb
135
137
  lib/ronin/cache/ronin.rb
136
138
  lib/ronin/console.rb
137
139
  lib/ronin/shell.rb
138
- lib/ronin/runner.rb
139
- lib/ronin/runner/program.rb
140
- lib/ronin/runner/program/exceptions.rb
141
- lib/ronin/runner/program/exceptions/unknown_command.rb
142
- lib/ronin/runner/program/command.rb
143
- lib/ronin/runner/program/options.rb
144
- lib/ronin/runner/program/commands.rb
145
- lib/ronin/runner/program/commands/add.rb
146
- lib/ronin/runner/program/commands/install.rb
147
- lib/ronin/runner/program/commands/list.rb
148
- lib/ronin/runner/program/commands/update.rb
149
- lib/ronin/runner/program/commands/remove.rb
150
- lib/ronin/runner/program/commands/uninstall.rb
151
- lib/ronin/runner/program/commands/help.rb
152
- lib/ronin/runner/program/program.rb
153
- lib/ronin/runner/program/runner.rb
140
+ lib/ronin/program.rb
141
+ lib/ronin/program/exceptions.rb
142
+ lib/ronin/program/exceptions/unknown_command.rb
143
+ lib/ronin/program/command.rb
144
+ lib/ronin/program/options.rb
145
+ lib/ronin/program/commands.rb
146
+ lib/ronin/program/commands/add.rb
147
+ lib/ronin/program/commands/install.rb
148
+ lib/ronin/program/commands/list.rb
149
+ lib/ronin/program/commands/update.rb
150
+ lib/ronin/program/commands/remove.rb
151
+ lib/ronin/program/commands/uninstall.rb
152
+ lib/ronin/program/commands/help.rb
153
+ lib/ronin/program/program.rb
154
154
  lib/ronin/ronin.rb
155
155
  lib/ronin/version.rb
156
156
  tasks/spec.rb
157
- spec/helpers/
157
+ spec/helpers.rb
158
158
  spec/spec_helper.rb
159
159
  spec/arch_spec.rb
160
160
  spec/author_spec.rb
@@ -173,6 +173,8 @@ spec/formatting/digest_spec.rb
173
173
  spec/formatting/html_spec.rb
174
174
  spec/formatting/http_spec.rb
175
175
  spec/formatting/text_spec.rb
176
+ spec/code/reference_spec.rb
177
+ spec/code/symbol_table_spec.rb
176
178
  spec/license_spec.rb
177
179
  spec/path_spec.rb
178
180
  spec/platform_spec.rb
data/README.txt CHANGED
@@ -94,16 +94,16 @@ of Ronin.
94
94
  == REQUIREMENTS:
95
95
 
96
96
  * Hpricot
97
- * Mechanize
97
+ * WWW::Mechanize
98
98
  * DataMapper:
99
- * dm-core
100
- * data_objects
101
- * do_sqlite3
102
- * dm-types
103
- * dm-serializer
104
- * dm-aggregates
105
- * reverserequire
106
- * R'epertoire
99
+ * dm-core >= 0.9.3
100
+ * data_objects >= 0.9.3
101
+ * do_sqlite3 >= 0.9.3
102
+ * dm-types >= 0.9.3
103
+ * dm-serializer >= 0.9.3
104
+ * dm-aggregates >= 0.9.3
105
+ * reverse-require >= 0.1.0
106
+ * R'epertoire >= 0.1.2
107
107
 
108
108
  == INSTALL:
109
109
 
data/Rakefile CHANGED
@@ -18,7 +18,7 @@ Hoe.new('ronin', Ronin::VERSION) do |p|
18
18
  ['dm-serializer', '>=0.9.3'],
19
19
  ['dm-aggregates', '>=0.9.3'],
20
20
  ['dm-validations', '>=0.9.3'],
21
- ['reverserequire', '>=0.1.0'],
21
+ ['reverse-require', '>=0.1.2'],
22
22
  ['repertoire', '>=0.1.2']]
23
23
  end
24
24
 
data/bin/ronin CHANGED
@@ -7,6 +7,6 @@ unless $LOAD_PATH.include?(lib_dir)
7
7
  $LOAD_PATH << lib_dir
8
8
  end
9
9
 
10
- require 'ronin/runner/program'
10
+ require 'ronin/program'
11
11
 
12
- Ronin::Runner.program(ARGV)
12
+ Ronin::Program.run(*ARGV)
data/lib/ronin/author.rb CHANGED
@@ -37,7 +37,7 @@ module Ronin
37
37
  property :id, Serial
38
38
 
39
39
  # Name of author
40
- property :name, String
40
+ property :name, String, :index => true
41
41
 
42
42
  # Author's associated group
43
43
  property :organization, String
@@ -190,9 +190,7 @@ module Ronin
190
190
  # end
191
191
  #
192
192
  def Extension.run_from(path,&block)
193
- Extension.load_from(path) do |ext|
194
- ext.run(&block)
195
- end
193
+ Extension.load_from(path) { |ext| ext.run(&block) }
196
194
  end
197
195
 
198
196
  #
@@ -205,9 +203,7 @@ module Ronin
205
203
  # end
206
204
  #
207
205
  def Extension.load(name,&block)
208
- Extension.new(name) do |ext|
209
- ext.include(name,&block)
210
- end
206
+ Extension.new(name) { |ext| ext.include(name,&block) }
211
207
  end
212
208
 
213
209
  #
@@ -219,9 +215,7 @@ module Ronin
219
215
  # end
220
216
  #
221
217
  def Extension.run(name,&block)
222
- Extension.load(name) do |ext|
223
- ext.run(&block)
224
- end
218
+ Extension.load(name) { |ext| ext.run(&block) }
225
219
  end
226
220
 
227
221
  #
@@ -255,9 +249,7 @@ module Ronin
255
249
  #
256
250
  def include(name,&block)
257
251
  Extension.load_paths(name) do
258
- Extension.each_path_for(name) do |path|
259
- include_path(path)
260
- end
252
+ Extension.each_path_for(name) { |path| include_path(path) }
261
253
  end
262
254
 
263
255
  block.call(self) if block
@@ -38,9 +38,7 @@ module Ronin
38
38
  end
39
39
 
40
40
  catch('EXIT') do
41
- each_extension do |ext|
42
- ext.perform_teardown
43
- end
41
+ each_extension { |ext| ext.perform_teardown }
44
42
  end
45
43
 
46
44
  block.call(self) if block
@@ -146,7 +146,9 @@ module Ronin
146
146
  # <tt>:media</tt>:: The media of the Overlay.
147
147
  #
148
148
  def Overlay.install(options={},&block)
149
- Repertoire.checkout(:media => options[:media], :uri => options[:uri], :into => Config::REPOSITORY_DIR) do |path,media,uri|
149
+ options = options.merge(:into => Config::REPOSITORY_DIR)
150
+
151
+ Repertoire.checkout(options) do |path,media,uri|
150
152
  return Overlay.add(path,media,uri,&block)
151
153
  end
152
154
  end
@@ -395,8 +397,6 @@ module Ronin
395
397
  if File.file?(metadata_path)
396
398
  metadata = REXML::Document.new(open(metadata_path))
397
399
 
398
- #@authors = Author.from_xml(metadata,'/ronin-overlay/contributors/author')
399
-
400
400
  metadata.elements.each('/ronin-overlay') do |repo|
401
401
  @name = repo.elements['name'].get_text.to_s.strip
402
402
  @license = repo.elements['license'].get_text.to_s.strip
@@ -46,14 +46,12 @@ module Ronin
46
46
  @path = File.expand_path(path)
47
47
 
48
48
  if File.file?(@path)
49
- File.open(@path) do |file|
50
- descriptions = YAML.load(file)
51
-
52
- if descriptions.kind_of?(Array)
53
- descriptions.each do |repo|
54
- if repo.kind_of?(Hash)
55
- add(Overlay.new(repo[:path],repo[:media],repo[:uri]))
56
- end
49
+ descriptions = YAML.load(File.read(@path))
50
+
51
+ if descriptions.kind_of?(Array)
52
+ descriptions.each do |overlay|
53
+ if overlay.kind_of?(Hash)
54
+ add(Overlay.new(overlay[:path],overlay[:media],overlay[:uri]))
57
55
  end
58
56
  end
59
57
  end
@@ -135,11 +133,13 @@ module Ronin
135
133
  # end
136
134
  #
137
135
  def add(repo,&block)
138
- if has_overlay?(repo.name)
139
- raise(OverlayCached,"overlay #{repo.to_s.dump} already present in the cache #{self.to_s.dump}",caller)
136
+ name = repo.name.to_s
137
+
138
+ if has_overlay?(name)
139
+ raise(OverlayCached,"overlay #{name.dump} already present in the cache #{self.to_s.dump}",caller)
140
140
  end
141
141
 
142
- self[repo.name.to_s] = repo
142
+ self << repo
143
143
 
144
144
  block.call(self) if block
145
145
  return self
@@ -157,11 +157,13 @@ module Ronin
157
157
  # end
158
158
  #
159
159
  def remove(repo,&block)
160
- unless has_overlay?(repo.name)
161
- raise(OverlayNotFound,"overlay #{repo.to_s.dump} is not present in the cache #{to_s.dump}",caller)
160
+ name = repo.name.to_s
161
+
162
+ unless has_overlay?(name)
163
+ raise(OverlayNotFound,"overlay #{name.dump} is not present in the cache #{self.to_s.dump}",caller)
162
164
  end
163
165
 
164
- delete_if { |key,value| key==repo.name }
166
+ delete_if { |key,value| key == name }
165
167
 
166
168
  block.call(self) if block
167
169
  return self
@@ -200,7 +202,11 @@ module Ronin
200
202
 
201
203
  File.open(output_path,'w') do |output|
202
204
  descriptions = overlays.map do |repo|
203
- {:media_type => repo.media_type, :path => repo.path, :uri => repo.uri}
205
+ {
206
+ :media_type => repo.media_type,
207
+ :path => repo.path,
208
+ :uri => repo.uri
209
+ }
204
210
  end
205
211
 
206
212
  YAML.dump(descriptions,output)
@@ -26,26 +26,20 @@ module Ronin
26
26
  class CharSet < Array
27
27
 
28
28
  #
29
- # Creates a new CharSet object with the given _characters_.
29
+ # Creates a new CharSet object with the given _chars_.
30
30
  #
31
- def initialize(*characters)
31
+ def initialize(*chars)
32
32
  format_char = lambda { |char|
33
- if char.kind_of?(Integer)
33
+ if (char.kind_of?(Array) || char.kind_of?(Range))
34
+ char.map(&format_char)
35
+ elsif char.kind_of?(Integer)
34
36
  char.chr
35
37
  else
36
38
  char.to_s
37
39
  end
38
40
  }
39
41
 
40
- characters = characters.flatten.map do |char|
41
- if char.kind_of?(Range)
42
- char.to_a.map(&format_char)
43
- else
44
- format_char.call(char)
45
- end
46
- end
47
-
48
- super(characters.flatten.uniq)
42
+ super(chars.map(&format_char).flatten.uniq)
49
43
  end
50
44
 
51
45
  #
@@ -0,0 +1,142 @@
1
+ #
2
+ #--
3
+ # Ronin - A Ruby platform designed for information security and data
4
+ # exploration tasks.
5
+ #
6
+ # Copyright (c) 2006-2008 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
+ module Ronin
25
+ module Code
26
+ class Reference
27
+
28
+ # Object that is being referenced
29
+ attr_accessor :value
30
+
31
+ #
32
+ # Creates a new Reference object with the specified _value_ that will
33
+ # be referenced.
34
+ #
35
+ def initialize(value=nil)
36
+ @value = value
37
+ end
38
+
39
+ #
40
+ # Returns the class of the referenced object.
41
+ #
42
+ def class
43
+ @value.class
44
+ end
45
+
46
+ #
47
+ # Returns +true+ if the referenced object is a kind of _base_ class,
48
+ # returns +false+ otherwise.
49
+ #
50
+ def is_a?(base)
51
+ @value.is_a?(base) || super(base)
52
+ end
53
+
54
+ #
55
+ # Returns +true+ if the referenced object is a kind of _base_ class,
56
+ # returns +false+ otherwise.
57
+ #
58
+ def kind_of?(base)
59
+ @value.kind_of?(base) || super(base)
60
+ end
61
+
62
+ #
63
+ # Returns +true+ if the referenced object is an instance of the
64
+ # specified _base_ class, returns +false+ otherwise.
65
+ #
66
+ def instance_of?(base)
67
+ @value.instance_of?(base) || super(base)
68
+ end
69
+
70
+ #
71
+ # Returns +true+ if the referenced object responds to the specified
72
+ # method _name_, returns +false+ otherwise.
73
+ #
74
+ def respond_to?(name)
75
+ @value.respond_to?(name) || super(name)
76
+ end
77
+
78
+ #
79
+ # Extends the referenced object with the specified _base_ module.
80
+ #
81
+ def extend(base)
82
+ @value.extend(base) if @value
83
+ return self
84
+ end
85
+
86
+ #
87
+ # Evaluates the given _code_ within the referenced object. If a
88
+ # _block_ is given, it will be evaluated within the referenced
89
+ # object.
90
+ #
91
+ def eval(code,&block)
92
+ @value.eval(code,&block)
93
+ end
94
+
95
+ #
96
+ # Evaluates the given _block_ within the referenced object.
97
+ #
98
+ def instance_eval(&block)
99
+ @value.instance_eval(&block)
100
+ end
101
+
102
+ #
103
+ # Returns +true+ if the referenced object equals the specified
104
+ # _value_, returns +false+ otherwise.
105
+ #
106
+ def eql?(value)
107
+ @value.eql?(value) || super(value)
108
+ end
109
+
110
+ alias == eql?
111
+ alias === eql?
112
+
113
+ #
114
+ # Returns the String form of the referenced object.
115
+ #
116
+ def to_s
117
+ @value.to_s
118
+ end
119
+
120
+ #
121
+ # Inspects the referenced object.
122
+ #
123
+ def inspect
124
+ @value.inspect
125
+ end
126
+
127
+ protected
128
+
129
+ #
130
+ # Relays method calls to the referenced object.
131
+ #
132
+ def method_missing(name,*arguments,&block)
133
+ if @value.class.public_method_defined?(name)
134
+ return @value.send(name,*arguments,&block)
135
+ end
136
+
137
+ raise(NoMethodError,name.id2name)
138
+ end
139
+
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,90 @@
1
+ #
2
+ #--
3
+ # Ronin - A Ruby platform designed for information security and data
4
+ # exploration tasks.
5
+ #
6
+ # Copyright (c) 2006-2008 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 'ronin/code/reference'
25
+
26
+ module Ronin
27
+ module Code
28
+ class SymbolTable
29
+
30
+ #
31
+ # Creates a new SymbolTable object with a given Hash of symbol names
32
+ # and their initial values.
33
+ #
34
+ def initialize(symbols={})
35
+ @table = Hash.new { |hash,key| hash[key] = Reference.new }
36
+
37
+ self.symbols = symbols
38
+ end
39
+
40
+ #
41
+ # Returns +true+ if the table has the symbol with the specified
42
+ # _name_, returns +false+ otherwise.
43
+ #
44
+ def has_symbol?(name)
45
+ @table.has_key?(name.to_s)
46
+ end
47
+
48
+ #
49
+ # Returns the symbol with the specified _name_.
50
+ #
51
+ def symbol(name)
52
+ @table[name.to_s]
53
+ end
54
+
55
+ #
56
+ # Sets the symbol values en-mass using the specified _hash_ of
57
+ # symbol names and their values.
58
+ #
59
+ def symbols=(hash)
60
+ hash.each do |name,value|
61
+ self[name] = value
62
+ end
63
+ end
64
+
65
+ #
66
+ # Returns the value of the symbol with the specified _name_.
67
+ #
68
+ def [](name)
69
+ @table[name.to_s].value
70
+ end
71
+
72
+ #
73
+ # Sets the _value_ of the symbol with the specified _name_.
74
+ #
75
+ def []=(name,value)
76
+ @table[name.to_s].value = value
77
+ end
78
+
79
+ #
80
+ # Inspects the symbol table.
81
+ #
82
+ def inspect
83
+ '{' + @table.map { |name,symbol|
84
+ "#{name.inspect}=>#{symbol.inspect}"
85
+ }.join(', ') + '}'
86
+ end
87
+
88
+ end
89
+ end
90
+ end
data/lib/ronin/context.rb CHANGED
@@ -216,7 +216,7 @@ module Ronin
216
216
  new_objs = []
217
217
 
218
218
  Context.load_blocks(path) do |pending|
219
- pending.blocks.each do |name,context_block|
219
+ pending.each_block do |name,context_block|
220
220
  if Context.is_context?(name)
221
221
  new_obj = Context.contexts[name].new
222
222
  new_obj.instance_eval(&context_block)
@@ -80,6 +80,13 @@ module Ronin
80
80
  @@ronin_database_config = configuration
81
81
  end
82
82
 
83
+ #
84
+ # Returns the current Database log.
85
+ #
86
+ def Database.log
87
+ @@ronin_database_log ||= nil
88
+ end
89
+
83
90
  #
84
91
  # Setup the Database log with the given _options_.
85
92
  #
@@ -94,8 +101,7 @@ module Ronin
94
101
  stream = (options[:stream] || File.new(path,'w+'))
95
102
  level = (options[:level] || DEFAULT_LOG_LEVEL)
96
103
 
97
- DataMapper::Logger.new(stream,level)
98
- return nil
104
+ return @@ronin_database_log = DataMapper::Logger.new(stream,level)
99
105
  end
100
106
 
101
107
  #
@@ -104,7 +110,10 @@ module Ronin
104
110
  # the Database.
105
111
  #
106
112
  def Database.setup(configuration=Database.config,&block)
107
- Database.setup_log
113
+ # setup the database log
114
+ Database.setup_log unless Database.log
115
+
116
+ # setup the database repository
108
117
  DataMapper.setup(Model::REPOSITORY_NAME, configuration)
109
118
 
110
119
  block.call if block
@@ -34,4 +34,53 @@ class String
34
34
  downcase.gsub(/(::|[\s\-])/,'_')
35
35
  end
36
36
 
37
+ #
38
+ # Returns the common prefix of the string and the specified _other_
39
+ # string. If no common prefix can be found an empty string will be
40
+ # returned.
41
+ #
42
+ def common_prefix(other)
43
+ min_length = [length, other.length].min
44
+
45
+ min_length.times do |i|
46
+ if self[i] != other[i]
47
+ return self[0...i]
48
+ end
49
+ end
50
+
51
+ return self[0...min_length]
52
+ end
53
+
54
+ #
55
+ # Returns the common postfix of the string and the specified _other_
56
+ # string. If no common postfix can be found an empty string will be
57
+ # returned.
58
+ #
59
+ def common_postfix(other)
60
+ min_length = [length, other.length].min
61
+
62
+ (min_length - 1).times do |i|
63
+ index = (length - i -1)
64
+ other_index = (other.length - i -1)
65
+
66
+ if self[index] != other[other_index]
67
+ return self[(index + 1)..-1]
68
+ end
69
+ end
70
+
71
+ return ''
72
+ end
73
+
74
+ #
75
+ # Returns the uncommon substring within the specified _other_ string,
76
+ # which does not occur within the string. If no uncommon substring can be
77
+ # found, an empty string will be returned.
78
+ #
79
+ def uncommon_substring(other)
80
+ prefix = common_prefix(other)
81
+ postfix = self[prefix.length..-1].common_postfix(other[prefix.length..-1])
82
+
83
+ return self[prefix.length...(length - postfix.length)]
84
+ end
85
+
37
86
  end
@@ -39,14 +39,19 @@ class String
39
39
  excluded = (options[:excluded] || [])
40
40
 
41
41
  formatted = included - excluded
42
+ formatted = ''
43
+
44
+ self.each_byte do |b|
45
+ c = b.chr
42
46
 
43
- return self.scan(/./m).map { |c|
44
47
  if formatted.include?(c)
45
- block.call(c)
48
+ formatted_chars << block.call(c)
46
49
  else
47
- c
50
+ formatted_chars << c
48
51
  end
49
- }.join
52
+ end
53
+
54
+ return formatted
50
55
  end
51
56
 
52
57
  #
@@ -81,7 +86,7 @@ class String
81
86
  # "get out your checkbook".rand_case
82
87
  # # => "gEt Out YOur CHEckbook"
83
88
  #
84
- def rand_case(options={})
89
+ def random_case(options={})
85
90
  prob = (options[:probability] || 0.5)
86
91
 
87
92
  format_chars(options) do |c|