methadone 0.0.1

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