methadone 0.0.1
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/.gitignore +8 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/README.rdoc +136 -0
- data/Rakefile +35 -0
- data/bin/methadone +136 -0
- data/features/bootstrap.feature +89 -0
- data/features/step_definitions/bootstrap_steps.rb +7 -0
- data/features/support/env.rb +12 -0
- data/lib/methadone.rb +3 -0
- data/lib/methadone/cli_logger.rb +65 -0
- data/lib/methadone/cli_logging.rb +52 -0
- data/lib/methadone/cucumber.rb +30 -0
- data/lib/methadone/version.rb +3 -0
- data/methadone.gemspec +25 -0
- data/templates/full/Rakefile.erb +28 -0
- data/templates/full/bin/executable.erb +11 -0
- data/templates/full/features/executable.feature.erb +12 -0
- data/templates/full/features/step_definitions/executable_steps.rb.erb +1 -0
- data/templates/full/features/support/env.rb.erb +8 -0
- data/templates/full/test/tc_something.rb.erb +7 -0
- data/test/base_test.rb +18 -0
- data/test/test_cli_logger.rb +117 -0
- data/test/test_cli_logging.rb +97 -0
- metadata +123 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rvm use 1.9.2@methadone-dev --create
|
data/Gemfile
ADDED
data/README.rdoc
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
= Methadone - kick the bash habit and start your command line apps off right
|
|
2
|
+
|
|
3
|
+
Author:: Dave Copeland (mailto:davetron5000 at g mail dot com)
|
|
4
|
+
Copyright:: Copyright (c) 2011 by Dave Copeland
|
|
5
|
+
License:: Distributes under the Apache License, see LICENSE.txt in the source distro
|
|
6
|
+
|
|
7
|
+
A smattering of tools to make your command-line apps easily awesome; kick the bash habit without sacrificing any of the power.
|
|
8
|
+
|
|
9
|
+
Currently, this is under development and has the following to offer:
|
|
10
|
+
|
|
11
|
+
* Bootstrapping a new CLI app
|
|
12
|
+
* Utility Classes
|
|
13
|
+
* Methadone::CLILogger - a logger subclass that sends error message to standard error and all messages to standard out
|
|
14
|
+
* Methadone::CLILogging - a module that, when included in any class, provides easy access to a shared logger
|
|
15
|
+
* Cucumber Steps
|
|
16
|
+
|
|
17
|
+
== Links
|
|
18
|
+
|
|
19
|
+
* {Source on Github}[http://github.com/davetron5000/methadone]
|
|
20
|
+
* RDoc[http://rdoc.info/github/davetron5000/methadone/master/frames]
|
|
21
|
+
|
|
22
|
+
== Bootstrapping
|
|
23
|
+
|
|
24
|
+
The +methadone+ command-line app will bootstrap a new command-line app, setting up a proper gem structure, unit tests, and cucumber-based tests with aruba:
|
|
25
|
+
|
|
26
|
+
$ methadone --help
|
|
27
|
+
Usage: methadone [options] app_name
|
|
28
|
+
--force Overwrite files if they exist
|
|
29
|
+
$ methadone newgem
|
|
30
|
+
$ cd newgem
|
|
31
|
+
$ rake
|
|
32
|
+
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
|
|
33
|
+
1 scenario (1 passed)
|
|
34
|
+
3 steps (3 passed)
|
|
35
|
+
$ cat features/newgem.feature
|
|
36
|
+
Feature: My bootstrapped app kinda works
|
|
37
|
+
In order to get going on coding my awesome app
|
|
38
|
+
I want to have aruba and cucumber setup
|
|
39
|
+
So I don't have to do it myself
|
|
40
|
+
|
|
41
|
+
Scenario: App just runs
|
|
42
|
+
When I run `newgem --help`
|
|
43
|
+
Then the exit status should be 0
|
|
44
|
+
And the output should contain:
|
|
45
|
+
"""
|
|
46
|
+
Usage: newgem [options]
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
Basically, this sets you up with all the boilerplate that you *should* be using to write a command-line app.
|
|
50
|
+
|
|
51
|
+
== Utility Classes
|
|
52
|
+
|
|
53
|
+
Currently, there are classes the assist in directing output logger-style to the right place; basically ensuring that errors go to +STDERR+ and everything else goes to +STDOUT+. All of this is, of course, configurable
|
|
54
|
+
|
|
55
|
+
=== Examples
|
|
56
|
+
|
|
57
|
+
==== Using STDOUT as a log, respecting STDERR
|
|
58
|
+
|
|
59
|
+
require 'methadone'
|
|
60
|
+
|
|
61
|
+
include Methadone::CLILogging
|
|
62
|
+
|
|
63
|
+
command = "rm -rf /tmp/*"
|
|
64
|
+
debug("About to run #{command}") # => goes only to STDOUT
|
|
65
|
+
if system(command)
|
|
66
|
+
info("Succesfully ran #{command}") # => goes only to STDOUT
|
|
67
|
+
else
|
|
68
|
+
error("There was a problem running #{command}") # => goes to STDOUT AND STDERR
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
==== Using a log file, but respecting STDERR
|
|
72
|
+
|
|
73
|
+
require 'methadone'
|
|
74
|
+
|
|
75
|
+
include Methadone::CLILogging
|
|
76
|
+
|
|
77
|
+
self.logger = CLILogger.new("logfile.txt")
|
|
78
|
+
command = "rm -rf /tmp/*"
|
|
79
|
+
debug("About to run #{command}") # => goes only to logfile.txt
|
|
80
|
+
if system(command)
|
|
81
|
+
info("Succesfully ran #{command}") # => goes only to logfile.txt
|
|
82
|
+
else
|
|
83
|
+
error("There was a problem running #{command}") # => goes to logfile.txt AND STDERR
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
== Cucumber Steps
|
|
87
|
+
|
|
88
|
+
Methadone uses aruba[http://www.github.com/cucumber/aruba] for BDD-style testing with cucumber. This library has some awesome steps, and methadone provides additional, more opinionated, steps.
|
|
89
|
+
|
|
90
|
+
=== Example
|
|
91
|
+
|
|
92
|
+
Here's an example from methadone's own tests:
|
|
93
|
+
|
|
94
|
+
Scenario: Help is properly documented
|
|
95
|
+
When I get help for "methadone"
|
|
96
|
+
Then the exit status should be 0
|
|
97
|
+
And the following options should be documented:
|
|
98
|
+
|--force|
|
|
99
|
+
And the banner should be present
|
|
100
|
+
And the banner should document that this app takes options
|
|
101
|
+
And the banner should document that this app's arguments are:
|
|
102
|
+
|app_name|required|
|
|
103
|
+
|
|
104
|
+
=== Steps Provided
|
|
105
|
+
* Run <tt>command_to_run --help</tt> using aruba
|
|
106
|
+
|
|
107
|
+
When I get help for "command_to_run"
|
|
108
|
+
|
|
109
|
+
* Make sure that each option shows up in the help and has *some* sort of documentation
|
|
110
|
+
|
|
111
|
+
Then the following options should be documented:
|
|
112
|
+
|--force|
|
|
113
|
+
|-x |
|
|
114
|
+
|
|
115
|
+
* Check an individual option for documentation:
|
|
116
|
+
|
|
117
|
+
Then the option "--force" should be documented
|
|
118
|
+
|
|
119
|
+
* Checks that the help has a proper usage banner
|
|
120
|
+
|
|
121
|
+
Then the banner should be present
|
|
122
|
+
|
|
123
|
+
* Checks that the usage banner indicates it takes options via <tt>[options]</tt>
|
|
124
|
+
|
|
125
|
+
Then the banner should document that this app takes options
|
|
126
|
+
|
|
127
|
+
* Checks that the app's usage banner documents that its arguments are <tt>args</tt>
|
|
128
|
+
|
|
129
|
+
Then the banner should document that this app's arguments are "args"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
== What might be
|
|
133
|
+
|
|
134
|
+
* Support for running external commands easily, with full error checking
|
|
135
|
+
* Support for main method-style implementation
|
|
136
|
+
* Easy support for filtering the output of a command, e.g. <tt>File.open("ls|")</tt> Perl-style
|
data/Rakefile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'bundler'
|
|
2
|
+
require 'rake/clean'
|
|
3
|
+
require 'rake/testtask'
|
|
4
|
+
gem 'rdoc'
|
|
5
|
+
require 'rdoc/task'
|
|
6
|
+
require 'cucumber'
|
|
7
|
+
require 'cucumber/rake/task'
|
|
8
|
+
|
|
9
|
+
include Rake::DSL
|
|
10
|
+
|
|
11
|
+
Bundler::GemHelper.install_tasks
|
|
12
|
+
|
|
13
|
+
desc 'run tests'
|
|
14
|
+
Rake::TestTask.new do |t|
|
|
15
|
+
t.libs << "lib"
|
|
16
|
+
t.libs << "test"
|
|
17
|
+
t.test_files = FileList['test/test_*.rb']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
desc 'build rdoc'
|
|
21
|
+
Rake::RDocTask.new do |rd|
|
|
22
|
+
rd.main = "README.rdoc"
|
|
23
|
+
rd.generator = 'hanna'
|
|
24
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
|
25
|
+
rd.title = 'Methadone - Power Up your Command Line Apps'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
CUKE_RESULTS = 'results.html'
|
|
29
|
+
CLEAN << CUKE_RESULTS
|
|
30
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
|
31
|
+
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x"
|
|
32
|
+
t.fork = false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
task :default => [:test, :features]
|
data/bin/methadone
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'erb'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
|
|
7
|
+
include FileUtils
|
|
8
|
+
|
|
9
|
+
def main(basedir,force)
|
|
10
|
+
if Dir.exists? basedir
|
|
11
|
+
if force
|
|
12
|
+
rm_rf basedir, :verbose => true, :secure => true
|
|
13
|
+
else
|
|
14
|
+
STDERR.puts "error: #{basedir} exists, use --force to override"
|
|
15
|
+
return 1
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
mkdir_p basedir
|
|
20
|
+
|
|
21
|
+
gemname = File.basename(basedir)
|
|
22
|
+
chdir File.dirname(basedir)
|
|
23
|
+
|
|
24
|
+
%x[bundle gem #{gemname}]
|
|
25
|
+
|
|
26
|
+
chdir gemname
|
|
27
|
+
|
|
28
|
+
template_dirs_in(:full).each { |dir| mkdir_p dir }
|
|
29
|
+
|
|
30
|
+
copy_file "Rakefile"
|
|
31
|
+
copy_file "test/tc_something.rb"
|
|
32
|
+
copy_file "features/executable.feature", :as => "#{gemname}.feature", :binding => binding
|
|
33
|
+
copy_file "features/support/env.rb"
|
|
34
|
+
copy_file "features/step_definitions/executable_steps.rb", :as => "#{gemname}_steps.rb"
|
|
35
|
+
copy_file "bin/executable", :as => gemname, :executable => true
|
|
36
|
+
add_to_file "#{gemname}.gemspec", [
|
|
37
|
+
" s.add_development_dependency('rdoc')",
|
|
38
|
+
" s.add_development_dependency('grancher')",
|
|
39
|
+
" s.add_development_dependency('aruba')",
|
|
40
|
+
], :before => /^end\s*$/
|
|
41
|
+
|
|
42
|
+
return 0
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Add content to a file
|
|
46
|
+
#
|
|
47
|
+
# +file+:: path to the file
|
|
48
|
+
# +lines+:: Array of String representing the lines to add
|
|
49
|
+
# +options+:: Hash of options:
|
|
50
|
+
# <tt>:before</tt>:: A regexp that will appear right after the new content. i.e.
|
|
51
|
+
# this is where to insert said content.
|
|
52
|
+
def add_to_file(file,lines,options = {})
|
|
53
|
+
new_lines = []
|
|
54
|
+
found_line = false
|
|
55
|
+
File.open(file).readlines.each do |line|
|
|
56
|
+
line.chomp!
|
|
57
|
+
if options[:before] && options[:before] === line
|
|
58
|
+
found_line = true
|
|
59
|
+
new_lines += lines
|
|
60
|
+
end
|
|
61
|
+
new_lines << line
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
raise "No line matched #{options[:before]}" if options[:before] && !found_line
|
|
65
|
+
|
|
66
|
+
new_lines += lines unless options[:before]
|
|
67
|
+
File.open(file,'w') do |fp|
|
|
68
|
+
new_lines.each { |line| fp.puts line }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Copies a file, running it through ERB
|
|
73
|
+
#
|
|
74
|
+
# +relative_path+:: path to the file, relative to the project root, minus the .erb extension
|
|
75
|
+
# You should use forward slashes to separate paths; this method
|
|
76
|
+
# will handle making the ultimate path OS independent.
|
|
77
|
+
# +options+:: Options to affect how the copy is done:
|
|
78
|
+
# <tt>:from</tt>:: The name of the profile from which to find the file, "full" by default
|
|
79
|
+
# <tt>:as</tt>:: The name the file should get if not the one in relative_path
|
|
80
|
+
# <tt>:executable</tt>:: true if this file should be set executable
|
|
81
|
+
# <tt>:binding</tt>:: the binding to use for the template
|
|
82
|
+
def copy_file(relative_path,options = {})
|
|
83
|
+
options[:from] ||= :full
|
|
84
|
+
|
|
85
|
+
relative_path = File.join(relative_path.split(/\//))
|
|
86
|
+
|
|
87
|
+
template_path = File.join(template_dir(options[:from]),relative_path + ".erb")
|
|
88
|
+
template = ERB.new(File.open(template_path).readlines.join(''))
|
|
89
|
+
|
|
90
|
+
relative_path_parts = File.split(relative_path)
|
|
91
|
+
relative_path_parts[-1] = options[:as] if options[:as]
|
|
92
|
+
|
|
93
|
+
erb_binding = options[:binding] or binding
|
|
94
|
+
|
|
95
|
+
File.open(File.join(relative_path_parts),'w') do |file|
|
|
96
|
+
file.puts template.result(erb_binding)
|
|
97
|
+
file.chmod(0755) if options[:executable]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Get the location of the templates for profile "from"
|
|
102
|
+
def template_dir(from)
|
|
103
|
+
File.join(File.dirname(EXE),'..','templates',from.to_s)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def template_dirs_in(profile)
|
|
107
|
+
template_dir = template_dir(:full)
|
|
108
|
+
|
|
109
|
+
Dir["#{template_dir}/**/*"].select { |x|
|
|
110
|
+
File.directory? x
|
|
111
|
+
}.map { |dir|
|
|
112
|
+
dir.gsub(/^#{template_dir}\//,'')
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
options = {}
|
|
117
|
+
option_parser = OptionParser.new do |opts|
|
|
118
|
+
executable = File.basename(__FILE__)
|
|
119
|
+
opts.banner = "Usage: #{executable} [options] app_name"
|
|
120
|
+
|
|
121
|
+
opts.on("--force","Overwrite files if they exist") do
|
|
122
|
+
options[:force] = true
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
option_parser.parse!
|
|
127
|
+
|
|
128
|
+
EXE = File.expand_path(__FILE__)
|
|
129
|
+
|
|
130
|
+
if ARGV.empty?
|
|
131
|
+
STDERR.puts("error: app_dir required")
|
|
132
|
+
exit 2
|
|
133
|
+
else
|
|
134
|
+
exit main(ARGV[0],options[:force])
|
|
135
|
+
end
|
|
136
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Feature: Bootstrap a new command-line app
|
|
2
|
+
As an awesome developer who wants to make a command-line app
|
|
3
|
+
I should be able to use methadone to bootstrap it
|
|
4
|
+
And get all kinds of cool things
|
|
5
|
+
|
|
6
|
+
Background:
|
|
7
|
+
Given the directory "tmp/newgem" does not exist
|
|
8
|
+
|
|
9
|
+
Scenario: Bootstrap a new app from scratch
|
|
10
|
+
When I successfully run `methadone tmp/newgem`
|
|
11
|
+
Then the following directories should exist:
|
|
12
|
+
|tmp/newgem |
|
|
13
|
+
|tmp/newgem/bin |
|
|
14
|
+
|tmp/newgem/lib |
|
|
15
|
+
|tmp/newgem/lib/newgem |
|
|
16
|
+
|tmp/newgem/test |
|
|
17
|
+
|tmp/newgem/features |
|
|
18
|
+
|tmp/newgem/features/support |
|
|
19
|
+
|tmp/newgem/features/step_definitions |
|
|
20
|
+
And the following files should exist:
|
|
21
|
+
|tmp/newgem/newgem.gemspec |
|
|
22
|
+
|tmp/newgem/Rakefile |
|
|
23
|
+
|tmp/newgem/Gemfile |
|
|
24
|
+
|tmp/newgem/bin/newgem |
|
|
25
|
+
|tmp/newgem/features/newgem.feature |
|
|
26
|
+
|tmp/newgem/features/support/env.rb |
|
|
27
|
+
|tmp/newgem/features/step_definitions/newgem_steps.rb |
|
|
28
|
+
|tmp/newgem/test/tc_something.rb |
|
|
29
|
+
And the file "tmp/newgem/newgem.gemspec" should match /add_development_dependency\('grancher'/
|
|
30
|
+
And the file "tmp/newgem/newgem.gemspec" should match /add_development_dependency\('aruba'/
|
|
31
|
+
And the file "tmp/newgem/newgem.gemspec" should match /add_development_dependency\('rdoc'/
|
|
32
|
+
Given I cd to "tmp/newgem"
|
|
33
|
+
When I successfully run `rake -T`
|
|
34
|
+
Then the output should contain:
|
|
35
|
+
"""
|
|
36
|
+
rake build # Build newgem-0.0.1.gem into the pkg directory
|
|
37
|
+
rake clean # Remove any temporary products.
|
|
38
|
+
rake clobber # Remove any generated file.
|
|
39
|
+
rake clobber_rdoc # Remove RDoc HTML files
|
|
40
|
+
rake features # Run Cucumber features
|
|
41
|
+
rake install # Build and install newgem-0.0.1.gem into system gems
|
|
42
|
+
rake rdoc # Build RDoc HTML files
|
|
43
|
+
rake release # Create tag v0.0.1 and build and push newgem-0.0.1.gem to Rubygems
|
|
44
|
+
rake rerdoc # Rebuild RDoc HTML files
|
|
45
|
+
rake test # Run tests
|
|
46
|
+
"""
|
|
47
|
+
When I run `rake`
|
|
48
|
+
Then the exit status should be 0
|
|
49
|
+
And the output should contain:
|
|
50
|
+
"""
|
|
51
|
+
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips
|
|
52
|
+
"""
|
|
53
|
+
And the output should contain:
|
|
54
|
+
"""
|
|
55
|
+
1 scenario (1 passed)
|
|
56
|
+
3 steps (3 passed)
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
Scenario: Won't squash an existing dir
|
|
60
|
+
When I successfully run `methadone tmp/newgem`
|
|
61
|
+
And I run `methadone tmp/newgem`
|
|
62
|
+
Then the exit status should not be 0
|
|
63
|
+
And the stderr should contain:
|
|
64
|
+
"""
|
|
65
|
+
error: tmp/newgem exists, use --force to override
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
Scenario: WILL squash an existing dir if we use --force
|
|
69
|
+
When I successfully run `methadone tmp/newgem`
|
|
70
|
+
And I run `methadone --force tmp/newgem`
|
|
71
|
+
Then the exit status should be 0
|
|
72
|
+
|
|
73
|
+
Scenario: We must supply a dirname
|
|
74
|
+
When I run `methadone`
|
|
75
|
+
Then the exit status should not be 0
|
|
76
|
+
And the stderr should contain:
|
|
77
|
+
"""
|
|
78
|
+
error: app_dir required
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
Scenario: Help is properly documented
|
|
82
|
+
When I get help for "methadone"
|
|
83
|
+
Then the exit status should be 0
|
|
84
|
+
And the following options should be documented:
|
|
85
|
+
|--force|
|
|
86
|
+
And the banner should be present
|
|
87
|
+
And the banner should document that this app takes options
|
|
88
|
+
And the banner should document that this app's arguments are:
|
|
89
|
+
|app_name|required|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'aruba/cucumber'
|
|
2
|
+
require 'methadone/cucumber'
|
|
3
|
+
|
|
4
|
+
ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
|
|
5
|
+
ARUBA_DIR = File.join(%w(tmp aruba))
|
|
6
|
+
Before do
|
|
7
|
+
@dirs = [ARUBA_DIR]
|
|
8
|
+
@puts = true
|
|
9
|
+
@aruba_timeout_seconds = 60
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
|
data/lib/methadone.rb
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
|
|
3
|
+
module Methadone
|
|
4
|
+
# A Logger appropriate for a command-line program in that it logs
|
|
5
|
+
# all messages (based on level) to the standard output and logs "error" type
|
|
6
|
+
# messages additionally to the standard error. By default, this will pretty
|
|
7
|
+
# much do what you want, however it can be customized:
|
|
8
|
+
#
|
|
9
|
+
# * You can override the devices used by passing different devices to the constructor
|
|
10
|
+
# * You can adjust the level of message that goes to the error logger via error_level=
|
|
11
|
+
# * You can adjust the format for messages to the error logger separately via error_formatter=
|
|
12
|
+
#
|
|
13
|
+
# === Example
|
|
14
|
+
#
|
|
15
|
+
# logger = CLILogger.new
|
|
16
|
+
# logger.debug("Starting up") # => only the standard output gets this
|
|
17
|
+
# logger.error("Something went wrong!") # => both standard error AND standard output get this
|
|
18
|
+
class CLILogger < Logger
|
|
19
|
+
|
|
20
|
+
# Helper to proxy methods to the super class AND to the internal error logger
|
|
21
|
+
#
|
|
22
|
+
# +symbol+:: Symbol for name of the method to proxy
|
|
23
|
+
def self.proxy_method(symbol) #:nodoc:
|
|
24
|
+
old_name = "old_#{symbol}".to_sym
|
|
25
|
+
alias_method old_name,symbol
|
|
26
|
+
define_method symbol do |*args,&block|
|
|
27
|
+
send(old_name,*args,&block)
|
|
28
|
+
@stderr_logger.send(symbol,*args,&block)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
proxy_method :'formatter='
|
|
33
|
+
proxy_method :'datetime_format='
|
|
34
|
+
proxy_method :add
|
|
35
|
+
|
|
36
|
+
# A logger that logs error-type messages to a second device; useful
|
|
37
|
+
# for ensuring that error messages go to standard error
|
|
38
|
+
#
|
|
39
|
+
# +log_device+:: device where all log messages should go, based on level
|
|
40
|
+
# +error_device+:: device where all error messages should go. By default, this is Logger::Severity::WARN
|
|
41
|
+
def initialize(log_device=$stdout,error_device=$stderr)
|
|
42
|
+
super(log_device)
|
|
43
|
+
@stderr_logger = Logger.new(error_device)
|
|
44
|
+
@stderr_logger.level = Logger::Severity::WARN
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Set the threshold for what messages go to the error device. Note that calling
|
|
48
|
+
# #level= will *not* affect the error logger
|
|
49
|
+
#
|
|
50
|
+
# +level+:: a constant from Logger::Severity for the level of messages that should go
|
|
51
|
+
# to the error logger
|
|
52
|
+
def error_level=(level)
|
|
53
|
+
@stderr_logger.level = level
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Overrides the formatter for the error logger. A future call to #formatter= will
|
|
57
|
+
# affect both, so the order of the calls matters.
|
|
58
|
+
#
|
|
59
|
+
# +formatter+:: Proc that handles the formatting, the same as for #formatter=
|
|
60
|
+
def error_formatter=(formatter)
|
|
61
|
+
@stderr_logger.formatter=formatter
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Methadone
|
|
2
|
+
# Provides easier access to a shared Methadone::CLILogger instance.
|
|
3
|
+
#
|
|
4
|
+
# Include this module into your class, and #logger provides access to a shared logger.
|
|
5
|
+
# This is handy if you want all of your clases to have access to the same logger, but
|
|
6
|
+
# don't want to (or aren't able to) pass it around to each class.
|
|
7
|
+
#
|
|
8
|
+
# This also provides methods for direct logging without going through the #logger
|
|
9
|
+
#
|
|
10
|
+
# === Example
|
|
11
|
+
#
|
|
12
|
+
# class MyClass
|
|
13
|
+
# include Methadone::CLILogger
|
|
14
|
+
#
|
|
15
|
+
# def doit
|
|
16
|
+
# debug("About to doit!")
|
|
17
|
+
# if results
|
|
18
|
+
# info("We did it!"
|
|
19
|
+
# else
|
|
20
|
+
# error("Something went wrong")
|
|
21
|
+
# end
|
|
22
|
+
# debug("Done doing it")
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
25
|
+
module CLILogging
|
|
26
|
+
# Access the shared logger. All classes that include this module
|
|
27
|
+
# will get the same logger via this method.
|
|
28
|
+
def logger
|
|
29
|
+
@@logger ||= CLILogger.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Change the global logger that includers will use. Useful if you
|
|
33
|
+
# don't want the default configured logger.
|
|
34
|
+
#
|
|
35
|
+
# +new_logger+:: the new logger. May not be nil and should be a a logger of some kind
|
|
36
|
+
def logger=(new_logger)
|
|
37
|
+
raise ArgumentError,"Logger may not be nil" if new_logger.nil?
|
|
38
|
+
@@logger = new_logger
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# pass-through to <tt>logger.debug(progname,&block)</tt>
|
|
42
|
+
def debug(progname = nil, &block); logger.debug(progname,&block); end
|
|
43
|
+
# pass-through to <tt>logger.info(progname,&block)</tt>
|
|
44
|
+
def info(progname = nil, &block); logger.info(progname,&block); end
|
|
45
|
+
# pass-through to <tt>logger.warn(progname,&block)</tt>
|
|
46
|
+
def warn(progname = nil, &block); logger.warn(progname,&block); end
|
|
47
|
+
# pass-through to <tt>logger.error(progname,&block)</tt>
|
|
48
|
+
def error(progname = nil, &block); logger.error(progname,&block); end
|
|
49
|
+
# pass-through to <tt>logger.fatal(progname,&block)</tt>
|
|
50
|
+
def fatal(progname = nil, &block); logger.fatal(progname,&block); end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
When /^I get help for "([^"]*)"$/ do |app_name|
|
|
2
|
+
@app_name = app_name
|
|
3
|
+
When %(I run `#{app_name} --help`)
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
Then /^the following options should be documented:$/ do |options|
|
|
7
|
+
options.raw.each do |option|
|
|
8
|
+
Then %(the option "#{option[0]}" should be documented)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
Then /^the option "([^"]*)" should be documented$/ do |option|
|
|
13
|
+
Then %(the output should match /^\\s*#{option}\\s+\\w\\w\\w+/)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Then /^the banner should be present$/ do
|
|
17
|
+
Then %(the output should match /Usage: #{@app_name}/)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Then /^the banner should document that this app takes options$/ do
|
|
21
|
+
Then %(the output should match /\[options\]/)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Then /^the banner should document that this app's arguments are:$/ do |table|
|
|
25
|
+
expected_arguments = table.raw.map { |row|
|
|
26
|
+
option = row[0]
|
|
27
|
+
option = "[#{option}]" if row[1] == 'optional'
|
|
28
|
+
}.join(' ')
|
|
29
|
+
Then %(the output should contain "#{expected_arguments}")
|
|
30
|
+
end
|
data/methadone.gemspec
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "methadone/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "methadone"
|
|
7
|
+
s.version = Methadone::VERSION
|
|
8
|
+
s.platform = Gem::Platform::RUBY
|
|
9
|
+
s.authors = ["davetron5000"]
|
|
10
|
+
s.email = ["davetron5000 at gmail.com"]
|
|
11
|
+
s.homepage = "http://github.com/davetron5000/methadone"
|
|
12
|
+
s.summary = %q{Kick the bash habit and start your command-line apps off right}
|
|
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
|
+
|
|
15
|
+
s.rubyforge_project = "methadone"
|
|
16
|
+
|
|
17
|
+
s.files = `git ls-files`.split("\n")
|
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
20
|
+
s.require_paths = ["lib"]
|
|
21
|
+
s.add_development_dependency("rspec-expectations")
|
|
22
|
+
s.add_development_dependency("rake")
|
|
23
|
+
s.add_development_dependency("rdoc","~> 3.6.1")
|
|
24
|
+
s.add_development_dependency("aruba")
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'bundler'
|
|
2
|
+
require 'rake/clean'
|
|
3
|
+
require 'rake/testtask'
|
|
4
|
+
require 'cucumber'
|
|
5
|
+
require 'cucumber/rake/task'
|
|
6
|
+
require 'rdoc/task'
|
|
7
|
+
|
|
8
|
+
include Rake::DSL
|
|
9
|
+
|
|
10
|
+
Bundler::GemHelper.install_tasks
|
|
11
|
+
|
|
12
|
+
Rake::TestTask.new do |t|
|
|
13
|
+
t.pattern = 'test/tc_*.rb'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
CUKE_RESULTS = 'results.html'
|
|
17
|
+
CLEAN << CUKE_RESULTS
|
|
18
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
|
19
|
+
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
|
|
20
|
+
t.fork = false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Rake::RDocTask.new do |rd|
|
|
24
|
+
rd.main = "README.rdoc"
|
|
25
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
task :default => [:test,:features]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Feature: My bootstrapped app kinda works
|
|
2
|
+
In order to get going on coding my awesome app
|
|
3
|
+
I want to have aruba and cucumber setup
|
|
4
|
+
So I don't have to do it myself
|
|
5
|
+
|
|
6
|
+
Scenario: App just runs
|
|
7
|
+
When I run `<%= gemname %> --help`
|
|
8
|
+
Then the exit status should be 0
|
|
9
|
+
And the output should contain:
|
|
10
|
+
"""
|
|
11
|
+
Usage: <%= gemname %> [options]
|
|
12
|
+
"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Put your step defintions here
|
data/test/base_test.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'rspec/expectations'
|
|
3
|
+
|
|
4
|
+
class BaseTest < Test::Unit::TestCase
|
|
5
|
+
# Copied from Rails; makes a test method using a string
|
|
6
|
+
def self.test(name, &block)
|
|
7
|
+
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
|
|
8
|
+
defined = instance_method(test_name) rescue false
|
|
9
|
+
raise "#{test_name} is already defined in #{self}" if defined
|
|
10
|
+
if block_given?
|
|
11
|
+
define_method(test_name, &block)
|
|
12
|
+
else
|
|
13
|
+
define_method(test_name) do
|
|
14
|
+
raise "No implementation provided for #{name}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require 'base_test'
|
|
2
|
+
require 'methadone'
|
|
3
|
+
require 'stringio'
|
|
4
|
+
|
|
5
|
+
class TestCLILogger < BaseTest
|
|
6
|
+
include Methadone
|
|
7
|
+
|
|
8
|
+
def setup
|
|
9
|
+
@blank_format = proc { |severity,datetime,progname,msg|
|
|
10
|
+
msg + "\n"
|
|
11
|
+
}
|
|
12
|
+
@real_stderr = $stderr
|
|
13
|
+
@real_stdout = $stdout
|
|
14
|
+
$stderr = StringIO.new
|
|
15
|
+
$stdout = StringIO.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def teardown
|
|
19
|
+
$stderr = @real_stderr
|
|
20
|
+
$stdout = @real_stdout
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test "logger sends everything to stdout, and warns, errors, and fatals to stderr" do
|
|
24
|
+
logger = logger_with_blank_format
|
|
25
|
+
|
|
26
|
+
logger.debug("debug")
|
|
27
|
+
logger.info("info")
|
|
28
|
+
logger.warn("warn")
|
|
29
|
+
logger.error("error")
|
|
30
|
+
logger.fatal("fatal")
|
|
31
|
+
|
|
32
|
+
$stdout.string.should == "debug\ninfo\nwarn\nerror\nfatal\n"
|
|
33
|
+
$stderr.string.should == "warn\nerror\nfatal\n"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
test "we can control what goes to stderr" do
|
|
37
|
+
logger = logger_with_blank_format
|
|
38
|
+
logger.error_level = Logger::Severity::FATAL
|
|
39
|
+
|
|
40
|
+
logger.debug("debug")
|
|
41
|
+
logger.info("info")
|
|
42
|
+
logger.warn("warn")
|
|
43
|
+
logger.error("error")
|
|
44
|
+
logger.fatal("fatal")
|
|
45
|
+
|
|
46
|
+
$stdout.string.should == "debug\ninfo\nwarn\nerror\nfatal\n"
|
|
47
|
+
$stderr.string.should == "fatal\n"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
test "we can log to alternate devices easily" do
|
|
51
|
+
out = StringIO.new
|
|
52
|
+
err = StringIO.new
|
|
53
|
+
|
|
54
|
+
logger = CLILogger.new(out,err)
|
|
55
|
+
logger.formatter = @blank_format
|
|
56
|
+
|
|
57
|
+
logger.debug("debug")
|
|
58
|
+
logger.info("info")
|
|
59
|
+
logger.warn("warn")
|
|
60
|
+
logger.error("error")
|
|
61
|
+
logger.fatal("fatal")
|
|
62
|
+
|
|
63
|
+
out.string.should == "debug\ninfo\nwarn\nerror\nfatal\n"
|
|
64
|
+
err.string.should == "warn\nerror\nfatal\n"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
test "error logger ignores the log level" do
|
|
69
|
+
logger = logger_with_blank_format
|
|
70
|
+
logger.level = Logger::Severity::FATAL
|
|
71
|
+
logger.debug("debug")
|
|
72
|
+
logger.info("info")
|
|
73
|
+
logger.warn("warn")
|
|
74
|
+
logger.error("error")
|
|
75
|
+
logger.fatal("fatal")
|
|
76
|
+
|
|
77
|
+
$stdout.string.should == "fatal\n"
|
|
78
|
+
$stderr.string.should == "warn\nerror\nfatal\n"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
test "both loggers use the same date format" do
|
|
82
|
+
logger = CLILogger.new
|
|
83
|
+
logger.datetime_format = "the time"
|
|
84
|
+
logger.debug("debug")
|
|
85
|
+
logger.error("error")
|
|
86
|
+
$stdout.string.should match /the time.*DEBUG.*debug/
|
|
87
|
+
$stderr.string.should match /the time.*ERROR.*error/
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
test "error logger does not get <<" do
|
|
91
|
+
logger = logger_with_blank_format
|
|
92
|
+
logger << "foo"
|
|
93
|
+
$stdout.string.should == "foo"
|
|
94
|
+
$stderr.string.should == ""
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
test "error logger can have a different format" do
|
|
98
|
+
logger = logger_with_blank_format
|
|
99
|
+
logger.error_formatter = proc { |severity,datetime,progname,msg|
|
|
100
|
+
"ERROR_LOGGER: #{msg}\n"
|
|
101
|
+
}
|
|
102
|
+
logger.debug("debug")
|
|
103
|
+
logger.error("error")
|
|
104
|
+
$stdout.string.should == "debug\nerror\n"
|
|
105
|
+
$stderr.string.should == "ERROR_LOGGER: error\n"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def logger_with_blank_format
|
|
111
|
+
logger = CLILogger.new
|
|
112
|
+
logger.formatter = @blank_format
|
|
113
|
+
logger.level = Logger::Severity::DEBUG
|
|
114
|
+
logger
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'base_test'
|
|
2
|
+
require 'methadone'
|
|
3
|
+
require 'stringio'
|
|
4
|
+
|
|
5
|
+
class TestCLILogging < BaseTest
|
|
6
|
+
include Methadone
|
|
7
|
+
|
|
8
|
+
def setup
|
|
9
|
+
@blank_format = proc do |severity,datetime,progname,msg|
|
|
10
|
+
msg + "\n"
|
|
11
|
+
end
|
|
12
|
+
@real_stderr = $stderr
|
|
13
|
+
@real_stdout = $stdout
|
|
14
|
+
$stderr = StringIO.new
|
|
15
|
+
$stdout = StringIO.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def teardown
|
|
19
|
+
$stderr = @real_stderr
|
|
20
|
+
$stdout = @real_stdout
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class MyClassThatLogsToStdout
|
|
24
|
+
include Methadone::CLILogging
|
|
25
|
+
|
|
26
|
+
def initialize
|
|
27
|
+
logger.formatter = proc do |severity,datetime,progname,msg|
|
|
28
|
+
msg + "\n"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def doit
|
|
33
|
+
debug("debug")
|
|
34
|
+
info("info")
|
|
35
|
+
warn("warn")
|
|
36
|
+
error("error")
|
|
37
|
+
fatal("fatal")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def logger_id; logger.object_id; end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
test "a class can include CLILogging and get terser logging" do
|
|
44
|
+
MyClassThatLogsToStdout.new.doit
|
|
45
|
+
$stdout.string.should == "debug\ninfo\nwarn\nerror\nfatal\n"
|
|
46
|
+
$stderr.string.should == "warn\nerror\nfatal\n"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
test "another class using CLILogging gets the same logger instance" do
|
|
50
|
+
first = MyClassThatLogsToStdout.new
|
|
51
|
+
second = MyOtherClassThatLogsToStdout.new
|
|
52
|
+
first.logger_id.should == second.logger_id
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
test "we can change the global logger" do
|
|
56
|
+
first = MyClassThatLogsToStdout.new
|
|
57
|
+
second = MyOtherClassThatLogsToStdout.new
|
|
58
|
+
logger_id = second.logger_id
|
|
59
|
+
|
|
60
|
+
second.change_logger
|
|
61
|
+
|
|
62
|
+
logger_id.should_not == second.logger_id
|
|
63
|
+
first.logger_id.should == second.logger_id
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
test "we cannot use a nil logger" do
|
|
67
|
+
lambda { MyOtherClassThatLogsToStdout.new.change_to_nil_logger }.should raise_error(ArgumentError)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class MyOtherClassThatLogsToStdout
|
|
71
|
+
include Methadone::CLILogging
|
|
72
|
+
|
|
73
|
+
def initialize
|
|
74
|
+
logger.formatter = proc do |severity,datetime,progname,msg|
|
|
75
|
+
msg + "\n"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def doit
|
|
80
|
+
debug("debug")
|
|
81
|
+
info("info")
|
|
82
|
+
warn("warn")
|
|
83
|
+
error("error")
|
|
84
|
+
fatal("fatal")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def change_logger
|
|
88
|
+
self.logger=Methadone::CLILogger.new
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def change_to_nil_logger
|
|
92
|
+
self.logger = nil
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def logger_id; logger.object_id; end
|
|
96
|
+
end
|
|
97
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: methadone
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- davetron5000
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2011-09-12 00:00:00.000000000Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: rspec-expectations
|
|
16
|
+
requirement: &70311581180940 !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: *70311581180940
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: rake
|
|
27
|
+
requirement: &70311581180520 !ruby/object:Gem::Requirement
|
|
28
|
+
none: false
|
|
29
|
+
requirements:
|
|
30
|
+
- - ! '>='
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: *70311581180520
|
|
36
|
+
- !ruby/object:Gem::Dependency
|
|
37
|
+
name: rdoc
|
|
38
|
+
requirement: &70311581180020 !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ~>
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: 3.6.1
|
|
44
|
+
type: :development
|
|
45
|
+
prerelease: false
|
|
46
|
+
version_requirements: *70311581180020
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: aruba
|
|
49
|
+
requirement: &70311581179600 !ruby/object:Gem::Requirement
|
|
50
|
+
none: false
|
|
51
|
+
requirements:
|
|
52
|
+
- - ! '>='
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
type: :development
|
|
56
|
+
prerelease: false
|
|
57
|
+
version_requirements: *70311581179600
|
|
58
|
+
description: Methadone provides a lot of small but useful features for developing
|
|
59
|
+
a command-line app, including an opinionated bootstrapping process, some helpful
|
|
60
|
+
cucumber steps, and some classes to bridge logging and output into a simple, unified,
|
|
61
|
+
interface
|
|
62
|
+
email:
|
|
63
|
+
- davetron5000 at gmail.com
|
|
64
|
+
executables:
|
|
65
|
+
- methadone
|
|
66
|
+
extensions: []
|
|
67
|
+
extra_rdoc_files: []
|
|
68
|
+
files:
|
|
69
|
+
- .gitignore
|
|
70
|
+
- .rvmrc
|
|
71
|
+
- Gemfile
|
|
72
|
+
- README.rdoc
|
|
73
|
+
- Rakefile
|
|
74
|
+
- bin/methadone
|
|
75
|
+
- features/bootstrap.feature
|
|
76
|
+
- features/step_definitions/bootstrap_steps.rb
|
|
77
|
+
- features/support/env.rb
|
|
78
|
+
- lib/methadone.rb
|
|
79
|
+
- lib/methadone/cli_logger.rb
|
|
80
|
+
- lib/methadone/cli_logging.rb
|
|
81
|
+
- lib/methadone/cucumber.rb
|
|
82
|
+
- lib/methadone/version.rb
|
|
83
|
+
- methadone.gemspec
|
|
84
|
+
- templates/full/Rakefile.erb
|
|
85
|
+
- templates/full/bin/executable.erb
|
|
86
|
+
- templates/full/features/executable.feature.erb
|
|
87
|
+
- templates/full/features/step_definitions/executable_steps.rb.erb
|
|
88
|
+
- templates/full/features/support/env.rb.erb
|
|
89
|
+
- templates/full/test/tc_something.rb.erb
|
|
90
|
+
- test/base_test.rb
|
|
91
|
+
- test/test_cli_logger.rb
|
|
92
|
+
- test/test_cli_logging.rb
|
|
93
|
+
homepage: http://github.com/davetron5000/methadone
|
|
94
|
+
licenses: []
|
|
95
|
+
post_install_message:
|
|
96
|
+
rdoc_options: []
|
|
97
|
+
require_paths:
|
|
98
|
+
- lib
|
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
100
|
+
none: false
|
|
101
|
+
requirements:
|
|
102
|
+
- - ! '>='
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '0'
|
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
|
+
none: false
|
|
107
|
+
requirements:
|
|
108
|
+
- - ! '>='
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
requirements: []
|
|
112
|
+
rubyforge_project: methadone
|
|
113
|
+
rubygems_version: 1.8.10
|
|
114
|
+
signing_key:
|
|
115
|
+
specification_version: 3
|
|
116
|
+
summary: Kick the bash habit and start your command-line apps off right
|
|
117
|
+
test_files:
|
|
118
|
+
- features/bootstrap.feature
|
|
119
|
+
- features/step_definitions/bootstrap_steps.rb
|
|
120
|
+
- features/support/env.rb
|
|
121
|
+
- test/base_test.rb
|
|
122
|
+
- test/test_cli_logger.rb
|
|
123
|
+
- test/test_cli_logging.rb
|