ronin 1.4.1 → 1.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.document +1 -0
  2. data/.gitignore +1 -0
  3. data/ChangeLog.md +38 -1
  4. data/Gemfile +10 -10
  5. data/README.md +1 -1
  6. data/Rakefile +13 -2
  7. data/bin/ronin-net-proxy +25 -0
  8. data/gemspec.yml +21 -2
  9. data/lib/bond/completions/ronin.rb +16 -5
  10. data/lib/ronin/arch.rb +5 -5
  11. data/lib/ronin/auto_load.rb +22 -1
  12. data/lib/ronin/campaign.rb +1 -1
  13. data/lib/ronin/database/database.rb +36 -25
  14. data/lib/ronin/installation.rb +2 -2
  15. data/lib/ronin/model/model.rb +5 -6
  16. data/lib/ronin/model/types/description.rb +0 -3
  17. data/lib/ronin/os.rb +2 -2
  18. data/lib/ronin/password.rb +1 -1
  19. data/lib/ronin/repository.rb +6 -6
  20. data/lib/ronin/script/path.rb +1 -2
  21. data/lib/ronin/spec/database.rb +16 -4
  22. data/lib/ronin/ui/cli/cli.rb +1 -1
  23. data/lib/ronin/ui/cli/command.rb +50 -7
  24. data/lib/ronin/ui/cli/commands/console.rb +15 -6
  25. data/lib/ronin/ui/cli/commands/creds.rb +1 -1
  26. data/lib/ronin/ui/cli/commands/database.rb +41 -29
  27. data/lib/ronin/ui/cli/commands/emails.rb +20 -15
  28. data/lib/ronin/ui/cli/commands/help.rb +18 -5
  29. data/lib/ronin/ui/cli/commands/hosts.rb +34 -27
  30. data/lib/ronin/ui/cli/commands/install.rb +21 -4
  31. data/lib/ronin/ui/cli/commands/ips.rb +34 -23
  32. data/lib/ronin/ui/cli/commands/net/proxy.rb +403 -0
  33. data/lib/ronin/ui/cli/commands/repos.rb +4 -4
  34. data/lib/ronin/ui/cli/commands/uninstall.rb +10 -0
  35. data/lib/ronin/ui/cli/commands/update.rb +11 -1
  36. data/lib/ronin/ui/cli/commands/urls.rb +39 -30
  37. data/lib/ronin/ui/cli/commands/wordlist.rb +11 -1
  38. data/lib/ronin/ui/console.rb +1 -0
  39. data/lib/ronin/ui/console/commands.rb +16 -98
  40. data/lib/ronin/ui/console/shell.rb +184 -0
  41. data/lib/ronin/url.rb +3 -3
  42. data/lib/ronin/url_scheme.rb +3 -3
  43. data/lib/ronin/version.rb +1 -1
  44. data/man/ronin-campaigns.1.md +78 -0
  45. data/man/ronin-console.1.md +72 -0
  46. data/man/ronin-creds.1.md +66 -0
  47. data/man/ronin-database.1.md +82 -0
  48. data/man/ronin-emails.1.md +72 -0
  49. data/man/ronin-exec.1.md +49 -0
  50. data/man/ronin-help.1.md +34 -0
  51. data/man/ronin-hosts.1.md +78 -0
  52. data/man/ronin-install.1.md +79 -0
  53. data/man/ronin-ips.1.md +81 -0
  54. data/man/ronin-net-proxy.1.md +86 -0
  55. data/man/ronin-repos.1.md +77 -0
  56. data/man/ronin-uninstall.1.md +67 -0
  57. data/man/ronin-update.1.md +67 -0
  58. data/man/ronin-urls.1.md +84 -0
  59. data/man/ronin-wordlist.1.md +53 -0
  60. data/man/ronin.1.md +26 -0
  61. data/ronin.gemspec +38 -109
  62. data/spec/installation_spec.rb +2 -1
  63. data/spec/spec_helper.rb +2 -0
  64. data/spec/ui/cli/classes/test_command.rb +7 -0
  65. data/spec/ui/cli/command_spec.rb +235 -7
  66. metadata +217 -96
@@ -0,0 +1,67 @@
1
+ # ronin-update 1 "April 2012" Ronin "User Manuals"
2
+
3
+ ## SYNOPSIS
4
+
5
+ `ronin update` [*options*] [*REPO*]
6
+
7
+ ## DESCRIPTION
8
+
9
+ Updates Ronin Repositories.
10
+
11
+ ## ARGUMENTS
12
+
13
+ *REPO*
14
+ The name of the Repository to update.
15
+
16
+ ## OPTIONS
17
+
18
+ `-v`, `--[no-]verbose`
19
+ Enable verbose output.
20
+
21
+ `-q`, `--[no-]quiet`
22
+ Disable verbose output.
23
+
24
+ `--[no-]silent`
25
+ Silence all output.
26
+
27
+ `--[no-]color`
28
+ Enables color output.
29
+
30
+ ## EXAMPLES
31
+
32
+ `ronin update repo`
33
+ Updates the repository with with the name `repo`.
34
+
35
+ `ronin update repo@github.com`
36
+ Updates the repository with the name `repo` and from `github.com`.
37
+
38
+ ## FILES
39
+
40
+ *~/.ronin/*
41
+ Ronin configuration directory.
42
+
43
+ *~/.ronin/repos*
44
+ Installation directory for Ronin Repositories.
45
+
46
+ *~/.ronin/database.log*
47
+ Database log.
48
+
49
+ *~/.ronin/database.sqlite3*
50
+ The default sqlite3 Database file.
51
+
52
+ *~/.ronin/database.yml*
53
+ Optional Database configuration.
54
+
55
+ ## ENVIRONMENT
56
+
57
+ HOME
58
+ Specifies the home directory of the user. Ronin will search for the `.ronin`
59
+ configuration directory within the home directory.
60
+
61
+ ## AUTHOR
62
+
63
+ Postmodern <postmodern.mod3@gmail.com>
64
+
65
+ ## SEE ALSO
66
+
67
+ ronin-install(1) ronin-repos(1) ronin-uninstall(1)
@@ -0,0 +1,84 @@
1
+ # ronin-urls 1 "April 2012" Ronin "User Manuals"
2
+
3
+ ## SYNOPSIS
4
+
5
+ `ronin urls` [*options*]
6
+
7
+ ## DESCRIPTION
8
+
9
+ Manages URLs.
10
+
11
+ ## OPTIONS
12
+
13
+ `-v`, `--[no-]verbose`
14
+ Enable verbose output.
15
+
16
+ `-q`, `--[no-]quiet`
17
+ Disable verbose output.
18
+
19
+ `--[no-]silent`
20
+ Silence all output.
21
+
22
+ `--[no-]color`
23
+ Enables color output.
24
+
25
+ `-D`, `--database` *URI*
26
+ The database to URI (`mysql://user:password@host/ronin`).
27
+
28
+ `--[no-]csv`
29
+ CSV output.
30
+
31
+ `--[no-]xml`
32
+ XML output.
33
+
34
+ `--[no-]yaml`
35
+ YAML output.
36
+
37
+ `--[no-]json`
38
+ JSON output.
39
+
40
+ `-i`, `--import` *FILE*
41
+ Imports HostNames from the FILE.
42
+
43
+ `--[no-]http`
44
+ Searches for `http://` URLs.
45
+
46
+ `--[no-]https`
47
+ Searches for `https://` URLs.
48
+
49
+ `-H`, `--hosts` *HOST* [...]
50
+ Searches for URLs with the given HOST name(s).
51
+
52
+ `-p`, `--with-ports` *PORT* [...]
53
+ Searches for URLs associated with the PORT(s).
54
+
55
+ `-d`, `--directory` *DIRECTORY*
56
+ Searches for URLs sharing the DIRECTORY.
57
+
58
+ `-q`, `--with-query-param` *NAME* [...]
59
+ Searches for URLs containing the query-param NAME.
60
+
61
+ `-Q`, `--with-query-value` *VALUE* [...]
62
+ Searches for URLs containing the query-param VALUE.
63
+
64
+ `-l`, `--[no-]list`
65
+ Lists the HostNames.
66
+
67
+ ## FILES
68
+
69
+ *~/.ronin/*
70
+ Ronin configuration directory.
71
+
72
+ *~/.ronin/database.log*
73
+ Database log.
74
+
75
+ *~/.ronin/database.sqlite3*
76
+ The default sqlite3 Database file.
77
+
78
+ *~/.ronin/database.yml*
79
+ Optional Database configuration.
80
+
81
+ ## AUTHOR
82
+
83
+ Postmodern <postmodern.mod3@gmail.com>
84
+
@@ -0,0 +1,53 @@
1
+ # ronin-wordlist 1 "April 2012" Ronin "User Manuals"
2
+
3
+ ## SYNOPSIS
4
+
5
+ `ronin wordlist` [*options*] [*TEMPLATE*]
6
+
7
+ ## DESCRIPTION
8
+
9
+ Builds and/or mutates Wordlists.
10
+
11
+ ## ARGUMENTS
12
+
13
+ *TEMPLATE*
14
+ Options word template to generate the wordlist with (`alpha:7 numeric:1-3`).
15
+
16
+ ## OPTIONS
17
+
18
+ `-v`, `--[no-]verbose`
19
+ Enable verbose output.
20
+
21
+ `-q`, `--[no-]quiet`
22
+ Disable verbose output.
23
+
24
+ `--[no-]silent`
25
+ Silence all output.
26
+
27
+ `--[no-]color`
28
+ Enables color output.
29
+
30
+ `-i`, `--input` *FILE*
31
+ The input text FILE to parse.
32
+
33
+ `-o`, `--output` *PATH*
34
+ The output PATH to write the wordlist to.
35
+
36
+ `-m`, `--mutations` *STRING*:*SUBSTITUTE*
37
+ Mutations to apply to the words. If STRING is found within a word, it will be
38
+ replaced with SUBSTITUTE.
39
+
40
+ ## EXAMPLES
41
+
42
+ `ronin wordlist alpha:7 numeric:1-3`
43
+ Enumerates through every possible word, with each word beginning with seven
44
+ alphabetic characters and ending in 1-3 numeric characters.
45
+
46
+ `ronin wordlist --input text.txt -m e:3 -m a:@ -m o:0`
47
+ Builds a wordlist from a text file, applying basic "leet-speak" mutation rules
48
+ to each word.
49
+
50
+ ## AUTHOR
51
+
52
+ Postmodern <postmodern.mod3@gmail.com>
53
+
@@ -0,0 +1,26 @@
1
+ # ronin 1 "April 2012" Ronin "User Manuals"
2
+
3
+ ## SYNOPSIS
4
+
5
+ `ronin` [*COMMAND*] [*options*]
6
+
7
+ ## DESCRIPTION
8
+
9
+ Runs a Ronin COMMAND or the starts Ronin Console.
10
+
11
+ ## ARGUMENTS
12
+
13
+ *COMMAND*
14
+ The Ronin command to execute.
15
+
16
+ ## OPTIONS
17
+
18
+ Additional options for the COMMAND or the Ronin Console.
19
+
20
+ ## AUTHOR
21
+
22
+ Postmodern <postmodern.mod3@gmail.com>
23
+
24
+ ## SEE ALSO
25
+
26
+ ronin-console(1)
@@ -2,130 +2,59 @@
2
2
 
3
3
  require 'yaml'
4
4
 
5
- Gem::Specification.new do |gemspec|
6
- root = File.dirname(__FILE__)
7
- lib_dir = File.join(root,'lib')
8
- files = if File.directory?('.git')
9
- `git ls-files`.split($/)
10
- elsif File.directory?('.hg')
11
- `hg manifest`.split($/)
12
- elsif File.directory?('.svn')
13
- `svn ls -R`.split($/).select { |path| File.file?(path) }
14
- else
15
- Dir['{**/}{.*,*}'].select { |path| File.file?(path) }
16
- end
5
+ Gem::Specification.new do |gem|
6
+ gemspec = YAML.load_file('gemspec.yml')
17
7
 
18
- filter_files = lambda { |paths|
19
- case paths
20
- when Array
21
- (files & paths)
22
- when String
23
- (files & Dir[paths])
24
- end
25
- }
26
-
27
- version = {
28
- :file => 'ronin/version',
29
- :constant => 'Ronin::VERSION'
30
- }
31
-
32
- defaults = {
33
- 'name' => File.basename(root),
34
- 'files' => files,
35
- 'executables' => filter_files['bin/*'].map { |path| File.basename(path) },
36
- 'test_files' => filter_files['{test/{**/}*_test.rb,spec/{**/}*_spec.rb}'],
37
- 'extra_doc_files' => filter_files['*.{txt,rdoc,md,markdown,tt,textile}'],
38
- }
8
+ gem.name = gemspec.fetch('name')
9
+ gem.version = gemspec.fetch('version') do
10
+ lib_dir = File.join(File.dirname(__FILE__),'lib')
11
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
39
12
 
40
- metadata = defaults.merge(YAML.load_file('gemspec.yml'))
13
+ require 'ronin/version'
14
+ Ronin::VERSION
15
+ end
41
16
 
42
- gemspec.name = metadata.fetch('name',defaults[:name])
43
- gemspec.version = if metadata['version']
44
- metadata['version']
45
- else
46
- $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
17
+ gem.summary = gemspec['summary']
18
+ gem.description = gemspec['description']
19
+ gem.licenses = Array(gemspec['license'])
20
+ gem.authors = Array(gemspec['authors'])
21
+ gem.email = gemspec['email']
22
+ gem.homepage = gemspec['homepage']
47
23
 
48
- require version[:file]
49
- eval(version[:constant])
50
- end
24
+ glob = lambda { |patterns| gem.files & Dir[*patterns] }
51
25
 
52
- gemspec.summary = metadata.fetch('summary',metadata['description'])
53
- gemspec.description = metadata.fetch('description',metadata['summary'])
26
+ gem.files = `git ls-files`.split($/)
27
+ gem.files = glob[gemspec['files']] if gemspec['files']
54
28
 
55
- case metadata['license']
56
- when Array
57
- gemspec.licenses = metadata['license']
58
- when String
59
- gemspec.license = metadata['license']
29
+ gem.executables = gemspec.fetch('executables') do
30
+ glob['bin/*'].map { |path| File.basename(path) }
60
31
  end
32
+ gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
61
33
 
62
- case metadata['authors']
63
- when Array
64
- gemspec.authors = metadata['authors']
65
- when String
66
- gemspec.author = metadata['authors']
67
- end
68
-
69
- gemspec.email = metadata['email']
70
- gemspec.homepage = metadata['homepage']
34
+ gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
35
+ gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb']
36
+ gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
71
37
 
72
- case metadata['require_paths']
73
- when Array
74
- gemspec.require_paths = metadata['require_paths']
75
- when String
76
- gemspec.require_path = metadata['require_paths']
77
- end
38
+ gem.require_paths = Array(gemspec.fetch('require_paths') {
39
+ %w[ext lib].select { |dir| File.directory?(dir) }
40
+ })
78
41
 
79
- gemspec.files = filter_files[metadata['files']]
42
+ gem.requirements = gemspec['requirements']
43
+ gem.required_ruby_version = gemspec['required_ruby_version']
44
+ gem.required_rubygems_version = gemspec['required_rubygems_version']
45
+ gem.post_install_message = gemspec['post_install_message']
80
46
 
81
- gemspec.executables = metadata['executables']
82
- gemspec.extensions = metadata['extensions']
83
-
84
- if Gem::VERSION < '1.7.'
85
- gemspec.default_executable = gemspec.executables.first
86
- end
87
-
88
- gemspec.test_files = filter_files[metadata['test_files']]
89
-
90
- unless gemspec.files.include?('.document')
91
- gemspec.extra_rdoc_files = metadata['extra_doc_files']
92
- end
93
-
94
- gemspec.post_install_message = metadata['post_install_message']
95
- gemspec.requirements = metadata['requirements']
96
-
97
- if gemspec.respond_to?(:required_ruby_version=)
98
- gemspec.required_ruby_version = metadata['required_ruby_version']
99
- end
100
-
101
- if gemspec.respond_to?(:required_rubygems_version=)
102
- gemspec.required_rubygems_version = metadata['required_rubygems_version']
103
- end
104
-
105
- parse_versions = lambda { |versions|
106
- case versions
107
- when Array
108
- versions.map { |v| v.to_s }
109
- when String
110
- versions.split(/,\s*/)
111
- end
112
- }
113
-
114
- if metadata['dependencies']
115
- metadata['dependencies'].each do |name,versions|
116
- gemspec.add_dependency(name,parse_versions[versions])
117
- end
118
- end
47
+ split = lambda { |string| string.split(/,\s*/) }
119
48
 
120
- if metadata['runtime_dependencies']
121
- metadata['runtime_dependencies'].each do |name,versions|
122
- gemspec.add_runtime_dependency(name,parse_versions[versions])
49
+ if gemspec['dependencies']
50
+ gemspec['dependencies'].each do |name,versions|
51
+ gem.add_dependency(name,split[versions])
123
52
  end
124
53
  end
125
54
 
126
- if metadata['development_dependencies']
127
- metadata['development_dependencies'].each do |name,versions|
128
- gemspec.add_development_dependency(name,parse_versions[versions])
55
+ if gemspec['development_dependencies']
56
+ gemspec['development_dependencies'].each do |name,versions|
57
+ gem.add_development_dependency(name,split[versions])
129
58
  end
130
59
  end
131
60
  end
@@ -23,6 +23,7 @@ describe Installation do
23
23
  hosts.rb
24
24
  install.rb
25
25
  ips.rb
26
+ net/proxy.rb
26
27
  repos.rb
27
28
  update.rb
28
29
  urls.rb
@@ -32,7 +33,7 @@ describe Installation do
32
33
  }
33
34
 
34
35
  describe "each_file" do
35
- let(:pattern) { File.join(directory,'*.rb') }
36
+ let(:pattern) { File.join(directory,'**','*.rb') }
36
37
  let(:expected) { files.map { |name| File.join(directory,name) } }
37
38
 
38
39
  it "should enumerate over the files which match a glob pattern" do
@@ -6,9 +6,11 @@ require 'model/models/described_model'
6
6
  require 'model/models/licensed_model'
7
7
  require 'model/models/named_model'
8
8
  require 'model/models/titled_model'
9
+ require 'classes/my_script'
9
10
  require 'helpers/repositories'
10
11
 
11
12
  require 'ronin/spec/database'
13
+ require 'ronin/spec/ui/output'
12
14
 
13
15
  include Ronin
14
16
 
@@ -4,6 +4,13 @@ class TestCommand < Ronin::UI::CLI::Command
4
4
 
5
5
  summary 'Tests the default task'
6
6
 
7
+ usage '[options] PATH FILE [..]'
8
+
9
+ examples [
10
+ 'test_command --foo PATH',
11
+ 'test_command --foo PATH FILE ...'
12
+ ]
13
+
7
14
  option :foo
8
15
 
9
16
  argument :path
@@ -6,8 +6,241 @@ require 'ui/cli/classes/test_command'
6
6
  describe UI::CLI::Command do
7
7
  subject { TestCommand }
8
8
 
9
- it "should have a command name" do
10
- subject.command_name.should == 'test_command'
9
+ describe "command_name" do
10
+ it "should be derived from the Class name" do
11
+ subject.command_name.should == 'test_command'
12
+ end
13
+ end
14
+
15
+ describe "usage" do
16
+ context "without an argument" do
17
+ it "should return the set usage" do
18
+ subject.usage.should == '[options] PATH FILE [..]'
19
+ end
20
+ end
21
+
22
+ context "with an argument" do
23
+ let(:expected) { 'FOO' }
24
+
25
+ subject { Class.new(described_class) }
26
+ before { subject.usage expected }
27
+
28
+ it "should set the usage" do
29
+ subject.usage.should == expected
30
+ end
31
+ end
32
+
33
+ context "default" do
34
+ subject { Class.new(described_class).usage }
35
+
36
+ it { should == '[options]' }
37
+ end
38
+
39
+ context "inherited" do
40
+ let(:superclass) { TestCommand }
41
+
42
+ subject { Class.new(superclass).usage }
43
+
44
+ it "should default to the usage of the superclass" do
45
+ subject.should == superclass.usage
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "summary" do
51
+ context "without an argument" do
52
+ it "should return the set summary" do
53
+ subject.summary.should == 'Tests the default task'
54
+ end
55
+ end
56
+
57
+ context "with an argument" do
58
+ let(:expected) { 'Performs foo' }
59
+
60
+ subject { Class.new(described_class) }
61
+ before { subject.summary expected }
62
+
63
+ it "should set the usage" do
64
+ subject.summary.should == expected
65
+ end
66
+ end
67
+
68
+ context "default" do
69
+ subject { Class.new(described_class).summary }
70
+
71
+ it { should == nil }
72
+ end
73
+
74
+ context "inherited" do
75
+ let(:superclass) { TestCommand }
76
+
77
+ subject { Class.new(superclass).summary }
78
+
79
+ it "should default to the summary of the superclass" do
80
+ subject.should == superclass.summary
81
+ end
82
+ end
83
+ end
84
+
85
+ describe "examples" do
86
+ context "without an argument" do
87
+ it "should return the set summary" do
88
+ # subject.example.should == 'Tests the default task'
89
+ end
90
+ end
91
+
92
+ context "with an argument" do
93
+ let(:expected) do
94
+ ['test_command --foo PATH', 'test_command --foo PATH FILE ...']
95
+ end
96
+
97
+ subject { Class.new(described_class) }
98
+ before { subject.examples expected }
99
+
100
+ it "should set the usage" do
101
+ subject.examples.should == expected
102
+ end
103
+ end
104
+
105
+ context "default" do
106
+ subject { Class.new(described_class).examples }
107
+
108
+ it { should == [] }
109
+ end
110
+
111
+ context "inherited" do
112
+ let(:superclass) { TestCommand }
113
+
114
+ subject { Class.new(superclass).examples }
115
+
116
+ it "should default to the examples of the superclass" do
117
+ subject.should == superclass.examples
118
+ end
119
+ end
120
+ end
121
+
122
+ describe "options" do
123
+ subject { Class.new(described_class).options }
124
+
125
+ context "inherited" do
126
+ it "should be {} by default" do
127
+ subject.should == {}
128
+ end
129
+ end
130
+
131
+ context described_class do
132
+ subject { described_class.options }
133
+
134
+ it "should have a :verbose option" do
135
+ subject.should have_key(:verbose)
136
+ end
137
+
138
+ it "should have a :quiet option" do
139
+ subject.should have_key(:quiet)
140
+ end
141
+
142
+ it "should have a :silent option" do
143
+ subject.should have_key(:silent)
144
+ end
145
+
146
+ it "should have a :color option" do
147
+ subject.should have_key(:color)
148
+ end
149
+
150
+ describe "color option" do
151
+ subject { described_class.new.color }
152
+
153
+ context "when $stdout is a TTY" do
154
+ it { should be_true }
155
+ end
156
+
157
+ context "when $stdout is not a TTY" do
158
+ before do
159
+ @old_stdout = $stdout
160
+ $stdout = StringIO.new
161
+ end
162
+
163
+ it { should be_false }
164
+
165
+ after do
166
+ $stdout = @old_stdout
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ describe "option" do
174
+ let(:name) { :foo }
175
+
176
+ it "should define an option" do
177
+ subject.options[name].should be_kind_of(Hash)
178
+ end
179
+
180
+ it "should define a parameter" do
181
+ subject.should have_param(name)
182
+ end
183
+ end
184
+
185
+ describe "each_option" do
186
+ let(:expected) { [:verbose, :quiet, :silent, :color, :foo] }
187
+
188
+ it "should iterate over each option" do
189
+ names = []
190
+
191
+ subject.each_option do |name,options|
192
+ names << name
193
+ end
194
+
195
+ names.should =~ expected
196
+ end
197
+ end
198
+
199
+ describe "options?" do
200
+ it "should test if there are any defined options" do
201
+ subject.options?.should be_true
202
+ end
203
+ end
204
+
205
+ describe "arguments" do
206
+ context "inherited" do
207
+ subject { Class.new(described_class).arguments }
208
+
209
+ it { should == [] }
210
+ end
211
+ end
212
+
213
+ describe "argument" do
214
+ let(:name) { :foo }
215
+
216
+ subject { Class.new(described_class) }
217
+ before { subject.argument name }
218
+
219
+ it "should add to arguments" do
220
+ subject.arguments.should include(name)
221
+ end
222
+
223
+ it "should define a parameter" do
224
+ subject.should have_param(name)
225
+ end
226
+ end
227
+
228
+ describe "each_argument" do
229
+ let(:expected) { [:path, :files] }
230
+
231
+ it "should iterate over each option" do
232
+ names = []
233
+
234
+ subject.each_argument { |name| names << name }
235
+
236
+ names.should =~ expected
237
+ end
238
+ end
239
+
240
+ describe "arguments?" do
241
+ it "should test if there are any defined arguments" do
242
+ subject.arguments?.should be_true
243
+ end
11
244
  end
12
245
 
13
246
  describe "#run" do
@@ -51,9 +284,4 @@ describe UI::CLI::Command do
51
284
  subject.files.should == files
52
285
  end
53
286
  end
54
-
55
- it "should have zero indentation by default" do
56
- command = subject.new
57
- command.instance_variable_get('@indent').should == 0
58
- end
59
287
  end