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 ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ html
6
+ .*.sw?
7
+ tmp
8
+ results.html
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.2@methadone-dev --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in methadone.gemspec
4
+ gemspec
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,7 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ Given /^the directory "([^"]*)" does not exist$/ do |dir|
5
+ dir = File.join(ARUBA_DIR,dir)
6
+ rm_rf dir,:verbose => false, :secure => true
7
+ end
@@ -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,3 @@
1
+ require 'methadone/version'
2
+ require 'methadone/cli_logger'
3
+ require 'methadone/cli_logging'
@@ -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
@@ -0,0 +1,3 @@
1
+ module Methadone
2
+ VERSION = "0.0.1"
3
+ 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,11 @@
1
+ #!/usr/bin/env ruby -w
2
+
3
+ require 'optparse'
4
+
5
+ option_parser = OptionParser.new do |opts|
6
+ executable_name = File.basename(__FILE__)
7
+ opts.banner = "Usage: #{executable_name} [options]"
8
+ end
9
+
10
+ option_parser.parse!
11
+
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'aruba/cucumber'
2
+
3
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
4
+
5
+ Before do
6
+ # Using "announce" causes massive warnings on 1.9.2
7
+ @puts = true
8
+ end
@@ -0,0 +1,7 @@
1
+ require 'minitest/autorun'
2
+
3
+ class TestSomething < MiniTest::Unit::TestCase
4
+ def test_truth
5
+ assert true
6
+ end
7
+ end
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