methadone 1.0.0.rc5 → 1.0.0.rc6
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/lib/methadone/cli.rb +2 -0
- data/lib/methadone/cli_logging.rb +3 -0
- data/lib/methadone/cucumber.rb +1 -1
- data/lib/methadone/error.rb +8 -1
- data/lib/methadone/execution_strategy/jvm.rb +2 -0
- data/lib/methadone/execution_strategy/mri.rb +2 -0
- data/lib/methadone/execution_strategy/open_3.rb +2 -0
- data/lib/methadone/execution_strategy/open_4.rb +2 -0
- data/lib/methadone/execution_strategy/rbx_open_4.rb +2 -0
- data/lib/methadone/exit_now.rb +18 -3
- data/lib/methadone/main.rb +34 -6
- data/lib/methadone/process_status.rb +45 -0
- data/lib/methadone/sh.rb +52 -29
- data/lib/methadone/version.rb +1 -1
- data/methadone.gemspec +10 -0
- data/test/test_main.rb +20 -0
- data/test/test_sh.rb +104 -3
- metadata +23 -47
- data/tutorial/.vimrc +0 -6
- data/tutorial/1_intro.md +0 -52
- data/tutorial/2_bootstrap.md +0 -174
- data/tutorial/3_ui.md +0 -336
- data/tutorial/4_happy_path.md +0 -405
- data/tutorial/5_more_features.md +0 -693
- data/tutorial/6_refactor.md +0 -220
- data/tutorial/7_logging_debugging.md +0 -274
- data/tutorial/8_conclusion.md +0 -11
- data/tutorial/code/.rvmrc +0 -1
- data/tutorial/code/fullstop/.gitignore +0 -5
- data/tutorial/code/fullstop/Gemfile +0 -4
- data/tutorial/code/fullstop/LICENSE.txt +0 -202
- data/tutorial/code/fullstop/README.rdoc +0 -23
- data/tutorial/code/fullstop/Rakefile +0 -31
- data/tutorial/code/fullstop/bin/fullstop +0 -43
- data/tutorial/code/fullstop/features/fullstop.feature +0 -40
- data/tutorial/code/fullstop/features/step_definitions/fullstop_steps.rb +0 -64
- data/tutorial/code/fullstop/features/support/env.rb +0 -22
- data/tutorial/code/fullstop/fullstop.gemspec +0 -28
- data/tutorial/code/fullstop/lib/fullstop.rb +0 -2
- data/tutorial/code/fullstop/lib/fullstop/repo.rb +0 -38
- data/tutorial/code/fullstop/lib/fullstop/version.rb +0 -3
- data/tutorial/code/fullstop/test/tc_something.rb +0 -7
- data/tutorial/en.utf-8.add +0 -18
- data/tutorial/toc.md +0 -27
data/methadone.gemspec
CHANGED
@@ -11,6 +11,16 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.homepage = "http://github.com/davetron5000/methadone"
|
12
12
|
s.summary = %q{Kick the bash habit and start your command-line apps off right}
|
13
13
|
s.description = %q{Methadone provides a lot of small but useful features for developing a command-line app, including an opinionated bootstrapping process, some helpful cucumber steps, and some classes to bridge logging and output into a simple, unified, interface}
|
14
|
+
s.post_install_message = "
|
15
|
+
|
16
|
+
!!!!!!!!!!!!!!!!!!!!!!
|
17
|
+
|
18
|
+
If you are on Ruby 1.8 or REE, you MUST
|
19
|
+
|
20
|
+
gem install open4
|
21
|
+
|
22
|
+
!!!!!!!!!!!!!!!!!!!!!!
|
23
|
+
"
|
14
24
|
|
15
25
|
s.rubyforge_project = "methadone"
|
16
26
|
|
data/test/test_main.rb
CHANGED
@@ -209,6 +209,26 @@ class TestMain < BaseTest
|
|
209
209
|
}
|
210
210
|
end
|
211
211
|
|
212
|
+
test_that "when we help_now! we exit and show help" do
|
213
|
+
Given {
|
214
|
+
@message = any_sentence
|
215
|
+
main do
|
216
|
+
help_now!(@message)
|
217
|
+
end
|
218
|
+
|
219
|
+
opts.on("--switch") { options[:switch] = true }
|
220
|
+
opts.on("--flag FLAG") { |value| options[:flag] = value }
|
221
|
+
|
222
|
+
set_argv []
|
223
|
+
}
|
224
|
+
|
225
|
+
Then {
|
226
|
+
assert_exits(64) { When run_go! }
|
227
|
+
assert $stdout.string.include?(opts.to_s),"Expected #{$stdout.string} to contain #{opts.to_s}"
|
228
|
+
assert_logged_at_error @message
|
229
|
+
}
|
230
|
+
end
|
231
|
+
|
212
232
|
test_that "opts allows us to more expediently set up OptionParser" do
|
213
233
|
Given {
|
214
234
|
@switch = nil
|
data/test/test_sh.rb
CHANGED
@@ -108,6 +108,30 @@ class TestSH < Clean::Test::TestCase
|
|
108
108
|
assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
|
109
109
|
}
|
110
110
|
end
|
111
|
+
|
112
|
+
test_that "#{method}, when the command succeeds and given a block of three arguments, calls the block with the stdout, stderr, and exit code" do
|
113
|
+
Given {
|
114
|
+
use_capturing_logger
|
115
|
+
@command = test_command
|
116
|
+
@block_called = false
|
117
|
+
@stdout_received = nil
|
118
|
+
@stderr_received = nil
|
119
|
+
@exitstatus_received = nil
|
120
|
+
}
|
121
|
+
When {
|
122
|
+
@exit_code = self.send(method,@command) do |stdout,stderr,exitstatus|
|
123
|
+
@stdout_received = stdout
|
124
|
+
@stderr_received = stderr
|
125
|
+
@exitstatus_received = exitstatus
|
126
|
+
end
|
127
|
+
}
|
128
|
+
Then {
|
129
|
+
@stdout_received.should == test_command_stdout
|
130
|
+
@stderr_received.length.should == 0
|
131
|
+
@exitstatus_received.should == 0
|
132
|
+
assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
|
133
|
+
}
|
134
|
+
end
|
111
135
|
end
|
112
136
|
|
113
137
|
test_that "sh, when the command fails and given a block, doesn't call the block" do
|
@@ -115,7 +139,7 @@ class TestSH < Clean::Test::TestCase
|
|
115
139
|
use_capturing_logger
|
116
140
|
@command = test_command("foo")
|
117
141
|
@block_called = false
|
118
|
-
}
|
142
|
+
}
|
119
143
|
When {
|
120
144
|
@exit_code = sh @command do
|
121
145
|
@block_called = true
|
@@ -127,6 +151,50 @@ class TestSH < Clean::Test::TestCase
|
|
127
151
|
}
|
128
152
|
end
|
129
153
|
|
154
|
+
test_that "sh, when the command fails with an unexpected status, and given a block, doesn't call the block" do
|
155
|
+
Given {
|
156
|
+
use_capturing_logger
|
157
|
+
@command = test_command("foo")
|
158
|
+
@block_called = false
|
159
|
+
}
|
160
|
+
When {
|
161
|
+
@exit_code = sh @command, :expected => [2] do
|
162
|
+
@block_called = true
|
163
|
+
end
|
164
|
+
}
|
165
|
+
Then {
|
166
|
+
@exit_code.should == 1
|
167
|
+
assert_logger_output_for_failure(@logger,@command,test_command_stdout,test_command_stderr)
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
[1,[1],[1,2]].each do |expected|
|
172
|
+
[:sh,:sh!].each do |method|
|
173
|
+
test_that "#{method}, when the command fails with an expected error code (using syntax #{expected}/#{expected.class}), treats it as success" do
|
174
|
+
Given {
|
175
|
+
use_capturing_logger
|
176
|
+
@command = test_command("foo")
|
177
|
+
@block_called = false
|
178
|
+
@exitstatus_received = nil
|
179
|
+
}
|
180
|
+
When {
|
181
|
+
@exit_code = self.send(method,@command,:expected => expected) do |_,_,exitstatus|
|
182
|
+
@block_called = true
|
183
|
+
@exitstatus_received = exitstatus
|
184
|
+
end
|
185
|
+
}
|
186
|
+
Then {
|
187
|
+
@exit_code.should == 1
|
188
|
+
@block_called.should == true
|
189
|
+
@exitstatus_received.should == 1
|
190
|
+
@logger.debugs[0].should == "Executing '#{test_command}foo'"
|
191
|
+
@logger.debugs[1].should == "Output of '#{test_command}foo': #{test_command_stdout}"
|
192
|
+
@logger.warns[0].should == "Error output of '#{test_command}foo': #{test_command_stderr}"
|
193
|
+
}
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
130
198
|
test_that "sh runs a command that will fail and logs about it" do
|
131
199
|
Given {
|
132
200
|
use_capturing_logger
|
@@ -219,7 +287,11 @@ class TestSH < Clean::Test::TestCase
|
|
219
287
|
|
220
288
|
def run_command(command)
|
221
289
|
@command = command
|
222
|
-
|
290
|
+
if @exitcode.kind_of? Fixnum
|
291
|
+
[any_string,any_string,OpenStruct.new(:exitstatus => @exitcode)]
|
292
|
+
else
|
293
|
+
[any_string,any_string,@exitcode]
|
294
|
+
end
|
223
295
|
end
|
224
296
|
|
225
297
|
def exception_meaning_command_not_found
|
@@ -251,7 +323,35 @@ class TestSH < Clean::Test::TestCase
|
|
251
323
|
}
|
252
324
|
Then {
|
253
325
|
@app.strategy.command.should == @command
|
254
|
-
@results.should == @
|
326
|
+
@results.should == @exit_code
|
327
|
+
}
|
328
|
+
end
|
329
|
+
|
330
|
+
test_that "when the execution strategy returns a non-int, but truthy value, it gets coerced into a 0" do
|
331
|
+
Given {
|
332
|
+
@app = MyExecutionStrategyApp.new(true)
|
333
|
+
@command = "ls"
|
334
|
+
}
|
335
|
+
When {
|
336
|
+
@results = @app.sh(@command)
|
337
|
+
}
|
338
|
+
Then {
|
339
|
+
@app.strategy.command.should == @command
|
340
|
+
@results.should == 0
|
341
|
+
}
|
342
|
+
end
|
343
|
+
|
344
|
+
test_that "when the execution strategy returns a non-int, but falsey value, it gets coerced into a 1" do
|
345
|
+
Given {
|
346
|
+
@app = MyExecutionStrategyApp.new(false)
|
347
|
+
@command = "ls"
|
348
|
+
}
|
349
|
+
When {
|
350
|
+
@results = @app.sh(@command)
|
351
|
+
}
|
352
|
+
Then {
|
353
|
+
@app.strategy.command.should == @command
|
354
|
+
@results.should == 1
|
255
355
|
}
|
256
356
|
end
|
257
357
|
|
@@ -276,6 +376,7 @@ private
|
|
276
376
|
change_logger(@logger)
|
277
377
|
end
|
278
378
|
|
379
|
+
# Runs the test command which exits with the length of ARGV/args
|
279
380
|
def test_command(args='')
|
280
381
|
File.join(File.dirname(__FILE__),'command_for_tests.rb') + ' ' + args
|
281
382
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: methadone
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc6
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-03-29 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
|
-
requirement: &
|
16
|
+
requirement: &70347550918840 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70347550918840
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec-expectations
|
27
|
-
requirement: &
|
27
|
+
requirement: &70347550918300 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '2.6'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70347550918300
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake
|
38
|
-
requirement: &
|
38
|
+
requirement: &70347550917880 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70347550917880
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rdoc
|
49
|
-
requirement: &
|
49
|
+
requirement: &70347550917340 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '3.9'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70347550917340
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: cucumber
|
60
|
-
requirement: &
|
60
|
+
requirement: &70347550916840 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.1.1
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70347550916840
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: aruba
|
71
|
-
requirement: &
|
71
|
+
requirement: &70347550916460 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70347550916460
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: simplecov
|
82
|
-
requirement: &
|
82
|
+
requirement: &70347550915860 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: '0.5'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70347550915860
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: clean_test
|
93
|
-
requirement: &
|
93
|
+
requirement: &70347550915300 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ~>
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: '0.10'
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *70347550915300
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: mocha
|
104
|
-
requirement: &
|
104
|
+
requirement: &70347550914780 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
@@ -109,7 +109,7 @@ dependencies:
|
|
109
109
|
version: '0'
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *70347550914780
|
113
113
|
description: Methadone provides a lot of small but useful features for developing
|
114
114
|
a command-line app, including an opinionated bootstrapping process, some helpful
|
115
115
|
cucumber steps, and some classes to bridge logging and output into a simple, unified,
|
@@ -152,6 +152,7 @@ files:
|
|
152
152
|
- lib/methadone/execution_strategy/rbx_open_4.rb
|
153
153
|
- lib/methadone/exit_now.rb
|
154
154
|
- lib/methadone/main.rb
|
155
|
+
- lib/methadone/process_status.rb
|
155
156
|
- lib/methadone/sh.rb
|
156
157
|
- lib/methadone/version.rb
|
157
158
|
- methadone.gemspec
|
@@ -179,35 +180,10 @@ files:
|
|
179
180
|
- test/test_exit_now.rb
|
180
181
|
- test/test_main.rb
|
181
182
|
- test/test_sh.rb
|
182
|
-
- tutorial/.vimrc
|
183
|
-
- tutorial/1_intro.md
|
184
|
-
- tutorial/2_bootstrap.md
|
185
|
-
- tutorial/3_ui.md
|
186
|
-
- tutorial/4_happy_path.md
|
187
|
-
- tutorial/5_more_features.md
|
188
|
-
- tutorial/6_refactor.md
|
189
|
-
- tutorial/7_logging_debugging.md
|
190
|
-
- tutorial/8_conclusion.md
|
191
|
-
- tutorial/code/.rvmrc
|
192
|
-
- tutorial/code/fullstop/.gitignore
|
193
|
-
- tutorial/code/fullstop/Gemfile
|
194
|
-
- tutorial/code/fullstop/LICENSE.txt
|
195
|
-
- tutorial/code/fullstop/README.rdoc
|
196
|
-
- tutorial/code/fullstop/Rakefile
|
197
|
-
- tutorial/code/fullstop/bin/fullstop
|
198
|
-
- tutorial/code/fullstop/features/fullstop.feature
|
199
|
-
- tutorial/code/fullstop/features/step_definitions/fullstop_steps.rb
|
200
|
-
- tutorial/code/fullstop/features/support/env.rb
|
201
|
-
- tutorial/code/fullstop/fullstop.gemspec
|
202
|
-
- tutorial/code/fullstop/lib/fullstop.rb
|
203
|
-
- tutorial/code/fullstop/lib/fullstop/repo.rb
|
204
|
-
- tutorial/code/fullstop/lib/fullstop/version.rb
|
205
|
-
- tutorial/code/fullstop/test/tc_something.rb
|
206
|
-
- tutorial/en.utf-8.add
|
207
|
-
- tutorial/toc.md
|
208
183
|
homepage: http://github.com/davetron5000/methadone
|
209
184
|
licenses: []
|
210
|
-
post_install_message:
|
185
|
+
post_install_message: ! "\n\n!!!!!!!!!!!!!!!!!!!!!!\n\nIf you are on Ruby 1.8 or REE,
|
186
|
+
you MUST\n\ngem install open4\n\n!!!!!!!!!!!!!!!!!!!!!!\n "
|
211
187
|
rdoc_options: []
|
212
188
|
require_paths:
|
213
189
|
- lib
|
data/tutorial/.vimrc
DELETED
data/tutorial/1_intro.md
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
# Awesome Ruby Command Line Apps with Methadone
|
2
|
-
|
3
|
-
Kick the bash habit, and make all your command-line apps with Ruby.
|
4
|
-
|
5
|
-
In [Build Awesome Command-Line Applications in Ruby][clibook], I lay out how to make an awesome command-line application using
|
6
|
-
Ruby. The book focuses on tools like `OptionParser` to create the app. As I wrote and researched, it became clear that there
|
7
|
-
was a gap between `OptionParser`, which is very powerful, yet verbose, and other command line tools like [trollop][trollop],
|
8
|
-
[main][main], and [thor][thor], which have simple APIs, but aren't very powerful.
|
9
|
-
|
10
|
-
[clibook]: http://www.awesomecommandlineapps.com
|
11
|
-
[main]: http://github.com/ahoward/main
|
12
|
-
[trollop]: http://trollop.rubyforge.org
|
13
|
-
[thor]: http://www.github.com/wycats/thor
|
14
|
-
|
15
|
-
I created Methadone to bridge that gap. Methadone provides all the power of `OptionParser`, but has a simple, clean API.
|
16
|
-
Methadone also includes additional tools and classes to make your command-line apps even better.
|
17
|
-
|
18
|
-
This tutorial will show you how to make a simple command-line app using Methadone that will be easy-to-use, easy-to-maintain, and
|
19
|
-
fully tested.
|
20
|
-
|
21
|
-
## What you'll need
|
22
|
-
|
23
|
-
You'll need an installation of Ruby and the ability to install Ruby gems. I would recommend that you use rvm and a gemset to
|
24
|
-
work through these, but they aren't required. There's a decent [walkthrough][setup] on my book's website of setting this up the
|
25
|
-
way I work. Although Methadone works on most versions of Ruby, I would recommend you use Ruby
|
26
|
-
1.9.3, if you can. If not, try to use an MRI Ruby as those versions (1.8.7, REE, 1.9.2, or 1.9.3) have the highest compatibility
|
27
|
-
with other gems.
|
28
|
-
|
29
|
-
[setup]: http://www.awesomecommandlineapps.com/setup.html
|
30
|
-
|
31
|
-
## How this is organized
|
32
|
-
|
33
|
-
This is a tutorial for making a simple command-line app. Unlike some tutorials and books, we will be working through this using
|
34
|
-
a "test-first" approach. One thing that Methadone tries to enable is using [TDD][tdd] for creating and writing your command-line
|
35
|
-
app. As such, we'll write tests as much as possible to drive our work.
|
36
|
-
|
37
|
-
[tdd]: http://en.wikipedia.org/wiki/Test-driven_development
|
38
|
-
|
39
|
-
## The tutorial app
|
40
|
-
|
41
|
-
The app we'll build is going to manage "dot files". These are the files that live in your home directory and configure your
|
42
|
-
shell, editor, and various other programs. For example, `~/.bashrc` is the file to configure `bash`. Many developers keep these
|
43
|
-
files on [Github][github] so that they can maintain the same files across multiple computers.
|
44
|
-
|
45
|
-
[github]: http://www.github.com
|
46
|
-
|
47
|
-
To set this up on a new computer, you have to checkout the repo, and symlink all the files to your home directory. To update the
|
48
|
-
files you have to update the repo and then check if any new files were added. This is the sort of tedious manual process that is
|
49
|
-
ripe for automation via a command-line app.
|
50
|
-
|
51
|
-
We'll develop a simplified version to demonstrate how to use Methadone.
|
52
|
-
|
data/tutorial/2_bootstrap.md
DELETED
@@ -1,174 +0,0 @@
|
|
1
|
-
# Bootstrapping our app
|
2
|
-
|
3
|
-
One thing that's great about writing a webapp with Ruby on Rails is that, with one command, you have a skeleton app, including
|
4
|
-
a fully functional test framework set up. You can start writing tests immediately. There's no common equivalent for a
|
5
|
-
command-line app, which is what Methadone aims to provide.
|
6
|
-
|
7
|
-
Methadone will also bootstrap other aspects of your app, such a `Rakefile`, a gemspec, a shell of an executable, a license, and a
|
8
|
-
README. First, let's install Methadone via RubyGems (note that if your aren't using rvm, you may need to use `sudo` to install
|
9
|
-
gems):
|
10
|
-
|
11
|
-
```sh
|
12
|
-
$ gem install methadone
|
13
|
-
Fetching: methadone-1.0.0.gem (100%)
|
14
|
-
Successfully installed methadone-1.0.0
|
15
|
-
1 gem installed
|
16
|
-
Installing ri documentation for methadone-1.0.0...
|
17
|
-
Installing RDoc documentation for methadone-1.0.0...
|
18
|
-
```
|
19
|
-
|
20
|
-
Methadone comes bundled with a command-line app that will do the bootstrapping:
|
21
|
-
|
22
|
-
```sh
|
23
|
-
$ methadone --help
|
24
|
-
Usage: methadone [options] app_name
|
25
|
-
|
26
|
-
Kick the bash habit by bootstrapping your Ruby command-line apps
|
27
|
-
|
28
|
-
v1.0.0
|
29
|
-
|
30
|
-
Options:
|
31
|
-
--force Overwrite files if they exist
|
32
|
-
--[no-]readme [Do not ]produce a README file
|
33
|
-
-l, --license LICENSE Specify the license for your project (mit|apache|custom|NONE)
|
34
|
-
--log-level LEVEL Set the logging level (debug|info|warn|error|fatal)
|
35
|
-
(Default: info)
|
36
|
-
--version Show help/version info
|
37
|
-
|
38
|
-
Default values can be placed in the METHODONE_OPTS environment variable
|
39
|
-
```
|
40
|
-
|
41
|
-
The app that we'll be building in this tutorial is be called `fullstop`, which is derived from the [British name][fullstop] for a period, which is the character used as a prefix to our dotfiles, and, is the reason they are called "dot" files in the first place. Based on the command-line syntax for `methadone`, we can create our app right now with one simple command. We'll use the Apache license as well as a README.
|
42
|
-
|
43
|
-
[fullstop]: http://en.wikipedia.org/wiki/Full_stop
|
44
|
-
|
45
|
-
```sh
|
46
|
-
$ methadone --readme --license apache fullstop
|
47
|
-
$ ls fullstop
|
48
|
-
Gemfile README.rdoc bin/
|
49
|
-
fullstop.gemspec test/ LICENSE.txt
|
50
|
-
Rakefile features/ lib/
|
51
|
-
```
|
52
|
-
|
53
|
-
As you can see, we've got a generic gemified project. Before we can start developing, we'll need to install a few gems using Bundler first:
|
54
|
-
|
55
|
-
```sh
|
56
|
-
$ cd fullstop
|
57
|
-
$ bundle install
|
58
|
-
Fetching source index for http://rubygems.org/
|
59
|
-
Installing rake (0.9.2.2)
|
60
|
-
Installing ffi (1.0.11) with native extensions
|
61
|
-
Installing childprocess (0.3.1)
|
62
|
-
Installing builder (3.0.0)
|
63
|
-
Installing diff-lcs (1.1.3)
|
64
|
-
Installing json (1.6.5) with native extensions
|
65
|
-
Installing gherkin (2.7.6) with native extensions
|
66
|
-
Installing term-ansicolor (1.0.7)
|
67
|
-
Installing cucumber (1.1.4)
|
68
|
-
Installing rspec-core (2.8.0)
|
69
|
-
Installing rspec-expectations (2.8.0)
|
70
|
-
Installing rspec-mocks (2.8.0)
|
71
|
-
Installing rspec (2.8.0)
|
72
|
-
Installing aruba (0.4.11)
|
73
|
-
Using bundler (1.0.21)
|
74
|
-
Installing methadone (0.5.1)
|
75
|
-
Using fullstop (0.0.1) from source at /Users/davec/Projects/methadone/tutorial/code/fullstop
|
76
|
-
Installing rdoc (3.12)
|
77
|
-
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
|
78
|
-
```
|
79
|
-
|
80
|
-
Your versions might not match up, but this should be more or less what you see. The first thing you'll notice is that this seems
|
81
|
-
like a *lot* of gems! Most of them are brought in by our acceptance testing framework, [aruba][aruba], which is a library on top
|
82
|
-
of [cucumber][cucumber] tailor-made for testing command-line apps. Methadone also assumes you'll be unit
|
83
|
-
testing with `Test::Unit`, which is a fine default. In fact, both unit and acceptance tests are set up for you and available
|
84
|
-
via `rake` tasks. Let's see them in action.
|
85
|
-
|
86
|
-
[aruba]: http://www.github.com/cucumber/aruba
|
87
|
-
[cucumber]: http://cukes.info
|
88
|
-
|
89
|
-
```sh
|
90
|
-
$ rake
|
91
|
-
Run options:
|
92
|
-
|
93
|
-
# Running tests:
|
94
|
-
|
95
|
-
.
|
96
|
-
|
97
|
-
Finished tests in 0.000623s, 1605.1364 tests/s, 1605.1364 assertions/s.
|
98
|
-
|
99
|
-
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
|
100
|
-
......
|
101
|
-
|
102
|
-
1 scenario (1 passed)
|
103
|
-
6 steps (6 passed)
|
104
|
-
0m0.136s
|
105
|
-
```
|
106
|
-
|
107
|
-
As you can see, we ran one unit test and one cucumber scenario. These were provided by Methadone as placeholders for your tests.
|
108
|
-
Just like what Ruby on Rails does when you create an app, Methadone has reduced the friction between your need to write software
|
109
|
-
and your ability to do so.
|
110
|
-
|
111
|
-
Methadone also generated a very basic scaffold of the command-line app itself. It's in `bin` and is called `fullstop` (the
|
112
|
-
argument we gave to `methadone`). Let's run it now:
|
113
|
-
|
114
|
-
```sh
|
115
|
-
$ bin/fullstop
|
116
|
-
/Users/davec/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- fullstop (LoadError)
|
117
|
-
from /Users/davec/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
|
118
|
-
from bin/fullstop:5:in `<main>'
|
119
|
-
```
|
120
|
-
|
121
|
-
Oops! What happened?
|
122
|
-
|
123
|
-
Methadone is encouraging you to develop your app with best practices, and one such practice is to not have your executables mess with the load path. In many Ruby command-line applications, you'll see code like this at the top of the file:
|
124
|
-
|
125
|
-
```ruby
|
126
|
-
$: << File.join(File.dirname(__FILE__),'..','lib')
|
127
|
-
```
|
128
|
-
|
129
|
-
This puts the `lib` directory, that is relative to the `bin` directory (where our executable lives), into Ruby's load path. This will allow *us* to run the app easily, but for your users, it's not necessary if you distribute your app with RubyGems (which you should do) and it's generally not a good idea to modify the load path. In order to run the app directly during development, we'll need to use `bundle exec`, like so (note that we won't be running our app a lot in development, but scripting it using Aruba so we can test its behavior in an automated way):
|
130
|
-
|
131
|
-
```sh
|
132
|
-
$ bundle exec bin/fullstop --help
|
133
|
-
Usage: fullstop [options]
|
134
|
-
|
135
|
-
v0.0.1
|
136
|
-
|
137
|
-
Options:
|
138
|
-
--version Show help/version info
|
139
|
-
--log-level LEVEL Set the logging level (debug|info|warn|error|fatal)
|
140
|
-
(Default: info)
|
141
|
-
```
|
142
|
-
|
143
|
-
|
144
|
-
Not too bad! We've got the makings of a reasonable help system, versioning support, a usage statement and a working executable.
|
145
|
-
Just remember to run the app with `bundle exec` while you're developing. Remember, your users won't have to worry about as long
|
146
|
-
as they installed it with RubyGems.
|
147
|
-
|
148
|
-
Before we move on, let's look at the cucumber scenario that Methadone generated for us. We're going to work "outside in" on our
|
149
|
-
app, so this will be a sneak peek at what we'll be doing next.
|
150
|
-
|
151
|
-
```sh
|
152
|
-
$ cat features/fullstop.feature
|
153
|
-
```
|
154
|
-
```cucumber
|
155
|
-
Feature: My bootstrapped app kinda works
|
156
|
-
In order to get going on coding my awesome app
|
157
|
-
I want to have aruba and cucumber setup
|
158
|
-
So I don't have to do it myself
|
159
|
-
|
160
|
-
Scenario: App just runs
|
161
|
-
When I get help for "fullstop"
|
162
|
-
Then the exit status should be 0
|
163
|
-
And the banner should be present
|
164
|
-
And the banner should document that this app takes options
|
165
|
-
And the following options should be documented:
|
166
|
-
|--version|
|
167
|
-
And the banner should document that this app takes no arguments
|
168
|
-
```
|
169
|
-
|
170
|
-
We probably won't keep this exactly scenario around, but it's a good demonstration of Aruba and Cucumber, and will help to get us
|
171
|
-
going. Since this scenario passes, that means that we already have the cucumber steps defined somewhere. As we'll see, the
|
172
|
-
combination of Aruba and Methadone results in a lot of pre-defined steps that make acceptance testing a snap.
|
173
|
-
|
174
|
-
In the next section, we'll expand this scenario to create the user interface we'll need to get our app going.
|