methadone 1.0.0.rc5 → 1.0.0.rc6
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|