optparse-plus 3.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/CHANGES.md +66 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +201 -0
- data/README.rdoc +173 -0
- data/Rakefile +94 -0
- data/bin/optparse_plus +130 -0
- data/fix.rb +29 -0
- data/lib/optparse-plus.rb +1 -0
- data/lib/optparse_plus.rb +15 -0
- data/lib/optparse_plus/argv_parser.rb +50 -0
- data/lib/optparse_plus/cli.rb +116 -0
- data/lib/optparse_plus/cli_logger.rb +133 -0
- data/lib/optparse_plus/cli_logging.rb +138 -0
- data/lib/optparse_plus/cucumber.rb +119 -0
- data/lib/optparse_plus/error.rb +32 -0
- data/lib/optparse_plus/execution_strategy/base.rb +34 -0
- data/lib/optparse_plus/execution_strategy/jvm.rb +37 -0
- data/lib/optparse_plus/execution_strategy/mri.rb +16 -0
- data/lib/optparse_plus/execution_strategy/open_3.rb +16 -0
- data/lib/optparse_plus/execution_strategy/open_4.rb +22 -0
- data/lib/optparse_plus/execution_strategy/rbx_open_4.rb +12 -0
- data/lib/optparse_plus/exit_now.rb +40 -0
- data/lib/optparse_plus/main.rb +603 -0
- data/lib/optparse_plus/process_status.rb +45 -0
- data/lib/optparse_plus/sh.rb +223 -0
- data/lib/optparse_plus/test/base_integration_test.rb +31 -0
- data/lib/optparse_plus/test/integration_test_assertions.rb +65 -0
- data/lib/optparse_plus/version.rb +3 -0
- data/optparse_plus.gemspec +28 -0
- data/templates/full/.gitignore.erb +4 -0
- data/templates/full/README.rdoc.erb +24 -0
- data/templates/full/Rakefile.erb +71 -0
- data/templates/full/_license_head.txt.erb +2 -0
- data/templates/full/apache_LICENSE.txt.erb +203 -0
- data/templates/full/bin/executable.erb +45 -0
- data/templates/full/custom_LICENSE.txt.erb +0 -0
- data/templates/full/gplv2_LICENSE.txt.erb +14 -0
- data/templates/full/gplv3_LICENSE.txt.erb +14 -0
- data/templates/full/mit_LICENSE.txt.erb +7 -0
- data/templates/rspec/spec/something_spec.rb.erb +5 -0
- data/templates/test_unit/test/integration/test_cli.rb.erb +11 -0
- data/templates/test_unit/test/unit/test_something.rb.erb +7 -0
- data/test/integration/base_integration_test.rb +60 -0
- data/test/integration/test_bootstrap.rb +150 -0
- data/test/integration/test_cli.rb +21 -0
- data/test/integration/test_license.rb +56 -0
- data/test/integration/test_readme.rb +53 -0
- data/test/integration/test_rspec.rb +28 -0
- data/test/integration/test_version.rb +21 -0
- data/test/unit/base_test.rb +19 -0
- data/test/unit/command_for_tests.sh +7 -0
- data/test/unit/execution_strategy/test_base.rb +24 -0
- data/test/unit/execution_strategy/test_jvm.rb +77 -0
- data/test/unit/execution_strategy/test_mri.rb +32 -0
- data/test/unit/execution_strategy/test_open_3.rb +70 -0
- data/test/unit/execution_strategy/test_open_4.rb +86 -0
- data/test/unit/execution_strategy/test_rbx_open_4.rb +25 -0
- data/test/unit/test/test_integration_test_assertions.rb +211 -0
- data/test/unit/test_cli_logger.rb +219 -0
- data/test/unit/test_cli_logging.rb +243 -0
- data/test/unit/test_exit_now.rb +37 -0
- data/test/unit/test_main.rb +840 -0
- data/test/unit/test_sh.rb +404 -0
- metadata +260 -0
data/Rakefile
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'sdoc'
|
2
|
+
require 'bundler'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
include Rake::DSL
|
7
|
+
|
8
|
+
Bundler::GemHelper.install_tasks
|
9
|
+
|
10
|
+
desc 'run unit tests'
|
11
|
+
Rake::TestTask.new do |t|
|
12
|
+
t.libs << "lib"
|
13
|
+
t.libs << "test/unit"
|
14
|
+
test_file = ENV["TEST"]
|
15
|
+
ENV.delete("TEST")
|
16
|
+
t.test_files = if test_file
|
17
|
+
[test_file]
|
18
|
+
else
|
19
|
+
FileList['test/unit/test_*.rb'] +
|
20
|
+
FileList['test/unit/execution_strategy/test_*.rb'] +
|
21
|
+
FileList['test/unit/test/test_*.rb']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'run integration tests'
|
26
|
+
Rake::TestTask.new("test:integration") do |t|
|
27
|
+
t.libs << "lib"
|
28
|
+
t.libs << "test/integration"
|
29
|
+
test_file = ENV["TEST"]
|
30
|
+
ENV.delete("TEST")
|
31
|
+
t.test_files = if test_file
|
32
|
+
[test_file]
|
33
|
+
else
|
34
|
+
FileList['test/integration/test_*.rb']
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'build rdoc'
|
39
|
+
task :rdoc => [:build_rdoc, :hack_css]
|
40
|
+
RDoc::Task.new(:build_rdoc) do |rd|
|
41
|
+
rd.main = "README.rdoc"
|
42
|
+
rd.options << '-f' << 'sdoc'
|
43
|
+
rd.template = 'direct'
|
44
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
45
|
+
rd.title = 'optparse-plus - Power Up your Command Line Apps'
|
46
|
+
end
|
47
|
+
CLOBBER << 'html'
|
48
|
+
|
49
|
+
FONT_FIX = {
|
50
|
+
"0.82em" => "16px",
|
51
|
+
"0.833em" => "16px",
|
52
|
+
"0.85em" => "16px",
|
53
|
+
"1.15em" => "20px",
|
54
|
+
"1.1em" => "20px",
|
55
|
+
"1.2em" => "20px",
|
56
|
+
"1.4em" => "24px",
|
57
|
+
"1.5em" => "24px",
|
58
|
+
"1.6em" => "32px",
|
59
|
+
"1em" => "16px",
|
60
|
+
"2.1em" => "38px",
|
61
|
+
}
|
62
|
+
|
63
|
+
|
64
|
+
task :hack_css do
|
65
|
+
maincss = File.open('html/css/main.css').readlines
|
66
|
+
File.open('html/css/main.css','w') do |file|
|
67
|
+
file.puts '@import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700);'
|
68
|
+
maincss.each do |line|
|
69
|
+
if line.strip == 'font-family: "Helvetica Neue", Arial, sans-serif;'
|
70
|
+
file.puts 'font-family: Lato, "Helvetica Neue", Arial, sans-serif;'
|
71
|
+
elsif line.strip == 'font-family: monospace;'
|
72
|
+
file.puts 'font-family: Monaco, monospace;'
|
73
|
+
elsif line =~ /^pre\s*$/
|
74
|
+
file.puts "pre {
|
75
|
+
font-family: Monaco, monospace;
|
76
|
+
margin-bottom: 1em;
|
77
|
+
}
|
78
|
+
pre.original"
|
79
|
+
elsif line =~ /^\s*font-size:\s*(.*)\s*;/
|
80
|
+
if FONT_FIX[$1]
|
81
|
+
file.puts "font-size: #{FONT_FIX[$1]};"
|
82
|
+
else
|
83
|
+
file.puts line.chomp
|
84
|
+
end
|
85
|
+
else
|
86
|
+
file.puts line.chomp
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
CLEAN << "coverage"
|
93
|
+
CLOBBER << FileList['**/*.rbc']
|
94
|
+
task :default => [:test, "test:integration"]
|
data/bin/optparse_plus
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'optparse'
|
5
|
+
require 'optparse_plus'
|
6
|
+
require 'optparse_plus/cli'
|
7
|
+
|
8
|
+
include FileUtils
|
9
|
+
include OptparsePlus::Main
|
10
|
+
include OptparsePlus::CLILogging
|
11
|
+
include OptparsePlus::CLI
|
12
|
+
include OptparsePlus::SH
|
13
|
+
|
14
|
+
main do |app_name|
|
15
|
+
check_and_prepare_basedir!(app_name,options[:force])
|
16
|
+
using_readme = options[:readme]
|
17
|
+
|
18
|
+
gemname = File.basename(app_name)
|
19
|
+
module_name = gemname.split(/-/).map(&:capitalize).collect{ |segment| segment.split(/_/).map(&:capitalize).join('') }.join('::')
|
20
|
+
|
21
|
+
debug "Creating project for gem #{gemname}"
|
22
|
+
|
23
|
+
chdir File.dirname(app_name)
|
24
|
+
|
25
|
+
require_file = nil
|
26
|
+
sh! "bundle gem #{gemname} --no-color" do |stdout|
|
27
|
+
require_file = stdout.split(/\n/).select { |file|
|
28
|
+
file =~ /\.rb$/ && file !~ /version.rb$/
|
29
|
+
}.first
|
30
|
+
end
|
31
|
+
rm_rf "#{gemname}/spec" # Don't want the default RSpec droppings
|
32
|
+
rm_rf "#{gemname}/.rspec"
|
33
|
+
|
34
|
+
require_file = gemname if require_file.nil?
|
35
|
+
require_file.gsub!(/^.*lib\//,'')
|
36
|
+
|
37
|
+
chdir gemname
|
38
|
+
|
39
|
+
template_dirs_in(:full).each { |dir| mkdir_p dir }
|
40
|
+
|
41
|
+
rspec = options[:rspec]
|
42
|
+
|
43
|
+
["Rakefile", ".gitignore", ].each do |file|
|
44
|
+
copy_file file, :binding => binding
|
45
|
+
end
|
46
|
+
|
47
|
+
if rspec
|
48
|
+
template_dirs_in(:rspec).each { |dir| mkdir_p dir }
|
49
|
+
copy_file "spec/something_spec.rb", :from => :rspec, :binding => binding
|
50
|
+
else
|
51
|
+
template_dirs_in(:test_unit).each { |dir| mkdir_p dir }
|
52
|
+
copy_file "test/unit/test_something.rb", :from => :test_unit, :binding => binding
|
53
|
+
copy_file "test/integration/test_cli.rb", :from => :test_unit, :binding => binding
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
gemspec = "#{gemname}.gemspec"
|
58
|
+
gem_variable = File.open(gemspec) { |x| x.read }.match(/(\w+)\.executables/)[1]
|
59
|
+
|
60
|
+
license = options[:license]
|
61
|
+
warn "warning: your app has no license" unless license
|
62
|
+
license = nil if license == 'NONE'
|
63
|
+
if license
|
64
|
+
copy_file "#{options[:license]}_LICENSE.txt", :as => "LICENSE.txt"
|
65
|
+
else
|
66
|
+
#Remove the MIT license generated by `bundle gem`
|
67
|
+
debug "Making sure no LICENSE.txt file exists at the root of the repo"
|
68
|
+
FileUtils.rm_f "LICENSE.txt"
|
69
|
+
end
|
70
|
+
|
71
|
+
#Ensure the gemspec file mentions the correct license
|
72
|
+
gemspec_content = File.read(gemspec)
|
73
|
+
if gemspec_content =~ /(^\s*#{gem_variable}\.license\s*=\s*).*/
|
74
|
+
gemspec_content.gsub!(/(^\s*#{gem_variable}\.license\s*=\s*).*/,"\\1#{license.to_s.inspect}")
|
75
|
+
else
|
76
|
+
gemspec_content.gsub!(/(^\s*#{gem_variable}\.name\s*=\s*.*)/,"\\1\n#{" #{gem_variable}.license".ljust(20)} = #{license.to_s.inspect.upcase}")
|
77
|
+
end
|
78
|
+
# RubyGems won't deal with a gemspec in this state and so Bundler won't even
|
79
|
+
# work at all. This is not helpful, so replace the magic keys its looking for
|
80
|
+
# with something else
|
81
|
+
gemspec_content.gsub!(/TODO/,"to-do")
|
82
|
+
gemspec_content.gsub!(/FIXME/,"fix me")
|
83
|
+
gemspec_content.gsub!(/^.*\.homepage.*$/,"")
|
84
|
+
File.open(gemspec,'w') {|f| f.write gemspec_content }
|
85
|
+
|
86
|
+
|
87
|
+
copy_file "README.rdoc", :binding => binding if using_readme
|
88
|
+
|
89
|
+
copy_file "bin/executable", :as => gemname, :executable => true, :binding => binding
|
90
|
+
|
91
|
+
add_to_file gemspec, [
|
92
|
+
" #{gem_variable}.add_development_dependency('rdoc')",
|
93
|
+
" #{gem_variable}.add_dependency('optparse_plus', '~> #{OptparsePlus::VERSION}')",
|
94
|
+
], :before => /^end\s*$/
|
95
|
+
ruby_major,ruby_minor,ruby_patch = RUBY_VERSION.split(/\./).map(&:to_i)
|
96
|
+
|
97
|
+
if ruby_major >= 2 && ruby_minor >= 2
|
98
|
+
add_to_file gemspec, [
|
99
|
+
" #{gem_variable}.add_development_dependency('test-unit')",
|
100
|
+
], :before => /^end\s*$/
|
101
|
+
end
|
102
|
+
|
103
|
+
if rspec
|
104
|
+
add_to_file gemspec, [
|
105
|
+
" #{gem_variable}.add_development_dependency('rspec', '~> 3')",
|
106
|
+
], :before => /^end\s*$/
|
107
|
+
end
|
108
|
+
FileUtils.rm_f "README.md", verbose: true
|
109
|
+
|
110
|
+
sh! %q(git add --all .)
|
111
|
+
end
|
112
|
+
|
113
|
+
options[:readme] = true
|
114
|
+
|
115
|
+
description "Kick the bash habit by bootstrapping your Ruby command-line apps"
|
116
|
+
|
117
|
+
on("--force","Overwrite files if they exist")
|
118
|
+
on("--[no-]readme","[Do not ]produce a README file")
|
119
|
+
on("--rspec", "Generate RSpec unit tests instead of Test::Unit")
|
120
|
+
|
121
|
+
licenses = %w(mit apache gplv2 gplv3 custom NONE)
|
122
|
+
on("-l LICENSE","--license",licenses,"Specify the license for your project",'(' + licenses.join('|') + ')')
|
123
|
+
|
124
|
+
use_log_level_option
|
125
|
+
|
126
|
+
arg :app_name, :required, "Name of your app, which is used for the gem name and executable name"
|
127
|
+
|
128
|
+
version OptparsePlus::VERSION, :compact => true
|
129
|
+
|
130
|
+
go!
|
data/fix.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
|
7
|
+
ARGV.each do |filename|
|
8
|
+
filename = Pathname(filename)
|
9
|
+
|
10
|
+
contents = File.read(filename).split(/\n/)
|
11
|
+
File.open(filename,"w") do |file|
|
12
|
+
contents.each do |line|
|
13
|
+
file.puts line.gsub(/methadone/,"optparse_plus").gsub(/Methadone/,"OptparsePlus")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if filename.split.any? { |_| _ == "methadone" }
|
18
|
+
new_filename = filename.split.map { |_|
|
19
|
+
if _ == "methadone"
|
20
|
+
"optparse_plus"
|
21
|
+
else
|
22
|
+
_
|
23
|
+
end
|
24
|
+
}.join
|
25
|
+
FileUtils.mkdir_p new_filename.dirname
|
26
|
+
FileUtils.mv filename new_filename
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "optparse_plus"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'optparse_plus/version'
|
2
|
+
require 'optparse_plus/cli_logger'
|
3
|
+
require 'optparse_plus/cli_logging'
|
4
|
+
require 'optparse_plus/exit_now'
|
5
|
+
require 'optparse_plus/argv_parser'
|
6
|
+
require 'optparse_plus/main'
|
7
|
+
require 'optparse_plus/error'
|
8
|
+
require 'optparse_plus/execution_strategy/base'
|
9
|
+
require 'optparse_plus/execution_strategy/mri'
|
10
|
+
require 'optparse_plus/execution_strategy/open_3'
|
11
|
+
require 'optparse_plus/execution_strategy/open_4'
|
12
|
+
require 'optparse_plus/execution_strategy/rbx_open_4'
|
13
|
+
require 'optparse_plus/execution_strategy/jvm'
|
14
|
+
require 'optparse_plus/sh'
|
15
|
+
# Note: DO NOT require cli.rb, cucumber.rb, or anything in test/ here
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module OptparsePlus #:nodoc:
|
2
|
+
# Assists with parsing strings in the same way that ARGV might.
|
3
|
+
# This is *not* used to parse the command-line, but used to
|
4
|
+
# parse config files/environment variables so they can be placed into ARGV and properly interpretted by
|
5
|
+
# OptionParser
|
6
|
+
module ARGVParser #:nodoc:
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
# Parses +string+, returning an array that can be placed into ARGV or given to OptionParser
|
11
|
+
def parse_string_for_argv(string) #:nodoc:
|
12
|
+
return [] if string.nil?
|
13
|
+
|
14
|
+
args = [] # return value we are building up
|
15
|
+
current = 0 # pointer to where we are in +string+
|
16
|
+
next_arg = '' # the next arg we are building up to ultimatley put into args
|
17
|
+
inside_quote = nil # quote character we are "inside" of
|
18
|
+
last_char = nil # the last character we saw
|
19
|
+
|
20
|
+
while current < string.length
|
21
|
+
char = string.chars.to_a[current]
|
22
|
+
case char
|
23
|
+
when /["']/
|
24
|
+
if inside_quote.nil? # eat the quote, but remember we are now "inside" one
|
25
|
+
inside_quote = char
|
26
|
+
elsif inside_quote == char # we closed the quote we were "inside"
|
27
|
+
inside_quote = nil
|
28
|
+
else # we got a different quote, so it goes in literally
|
29
|
+
next_arg << char
|
30
|
+
end
|
31
|
+
when /\s/
|
32
|
+
if last_char == "\\" # we have an escaped space, replace the escape char
|
33
|
+
next_arg[-1] = char
|
34
|
+
elsif inside_quote # we are inside a quote so keep the space
|
35
|
+
next_arg << char
|
36
|
+
else # new argument
|
37
|
+
args << next_arg
|
38
|
+
next_arg = ''
|
39
|
+
end
|
40
|
+
else
|
41
|
+
next_arg << char
|
42
|
+
end
|
43
|
+
current += 1
|
44
|
+
last_char = char
|
45
|
+
end
|
46
|
+
args << next_arg unless next_arg == ''
|
47
|
+
args
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module OptparsePlus
|
4
|
+
# <b>OptparsePlus Internal - treat as private</b>
|
5
|
+
#
|
6
|
+
# Stuff to implement optparse_plus's CLI app. These
|
7
|
+
# stuff isn't generally for your use and it's not
|
8
|
+
# included when you require 'optparse_plus'
|
9
|
+
module CLI
|
10
|
+
|
11
|
+
# Checks that the basedir can be used, either by
|
12
|
+
# not existing, or by existing and force is true.
|
13
|
+
# In that case, we clean it out entirely
|
14
|
+
#
|
15
|
+
# +basedir+:: base directory where the user wants to create a new project
|
16
|
+
# +force+:: if true, and +basedir+ exists, delete it before proceeding
|
17
|
+
#
|
18
|
+
# This will exit the app if the dir exists and force is false
|
19
|
+
def check_and_prepare_basedir!(basedir,force)
|
20
|
+
if File.exists? basedir
|
21
|
+
if force
|
22
|
+
rm_rf basedir, :verbose => true, :secure => true
|
23
|
+
else
|
24
|
+
exit_now! 1,"error: #{basedir} exists, use --force to override"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
mkdir_p basedir
|
28
|
+
end
|
29
|
+
|
30
|
+
# Add content to a file
|
31
|
+
#
|
32
|
+
# +file+:: path to the file
|
33
|
+
# +lines+:: Array of String representing the lines to add
|
34
|
+
# +options+:: Hash of options:
|
35
|
+
# <tt>:before</tt>:: A regexp that will appear right after the new content. i.e.
|
36
|
+
# this is where to insert said content.
|
37
|
+
def add_to_file(file,lines,options = {})
|
38
|
+
new_lines = []
|
39
|
+
found_line = false
|
40
|
+
File.open(file).readlines.each do |line|
|
41
|
+
line.chomp!
|
42
|
+
if options[:before] && options[:before] === line
|
43
|
+
found_line = true
|
44
|
+
new_lines += lines
|
45
|
+
end
|
46
|
+
new_lines << line
|
47
|
+
end
|
48
|
+
|
49
|
+
raise "No line matched #{options[:before]}" if options[:before] && !found_line
|
50
|
+
|
51
|
+
new_lines += lines unless options[:before]
|
52
|
+
File.open(file,'w') do |fp|
|
53
|
+
new_lines.each { |line| fp.puts line }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Copies a file, running it through ERB
|
58
|
+
#
|
59
|
+
# +relative_path+:: path to the file, relative to the project root, minus the .erb extension
|
60
|
+
# You should use forward slashes to separate paths; this method
|
61
|
+
# will handle making the ultimate path OS independent.
|
62
|
+
# +options+:: Options to affect how the copy is done:
|
63
|
+
# <tt>:from</tt>:: The name of the profile from which to find the file, "full" by default
|
64
|
+
# <tt>:as</tt>:: The name the file should get if not the one in relative_path
|
65
|
+
# <tt>:executable</tt>:: true if this file should be set executable
|
66
|
+
# <tt>:binding</tt>:: the binding to use for the template
|
67
|
+
def copy_file(relative_path,options = {})
|
68
|
+
options[:from] ||= :full
|
69
|
+
|
70
|
+
relative_path = File.join(relative_path.split(/\//))
|
71
|
+
|
72
|
+
template_path = File.join(template_dir(options[:from]),relative_path + ".erb")
|
73
|
+
template = ERB.new(File.open(template_path).readlines.join(''))
|
74
|
+
|
75
|
+
relative_path_parts = File.split(relative_path)
|
76
|
+
relative_path_parts[-1] = options[:as] if options[:as]
|
77
|
+
|
78
|
+
erb_binding = options[:binding] or binding
|
79
|
+
|
80
|
+
File.open(File.join(relative_path_parts),'w') do |file|
|
81
|
+
file.puts template.result(erb_binding)
|
82
|
+
file.chmod(0755) if options[:executable]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get the location of the templates for profile "from"
|
87
|
+
def template_dir(from)
|
88
|
+
File.join(File.dirname(__FILE__),'..','..','templates',from.to_s)
|
89
|
+
end
|
90
|
+
|
91
|
+
def template_dirs_in(profile)
|
92
|
+
template_dir = template_dir(profile)
|
93
|
+
|
94
|
+
Dir["#{template_dir}/**/*"].select { |x|
|
95
|
+
File.directory? x
|
96
|
+
}.map { |dir|
|
97
|
+
dir.gsub(/^#{template_dir}\//,'')
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def render_license_partial(partial)
|
102
|
+
ERB.new(File.read(template_dir('full/'+partial))).result(binding).strip
|
103
|
+
end
|
104
|
+
|
105
|
+
def gemspec
|
106
|
+
@gemspec || @gemspec=_get_gemspec
|
107
|
+
end
|
108
|
+
private
|
109
|
+
def _get_gemspec
|
110
|
+
files=Dir.glob("*.gemspec")
|
111
|
+
raise "Multiple gemspec files" if files.size>1
|
112
|
+
raise "No gemspec file" if files.size < 1
|
113
|
+
Gem::Specification::load(files.first)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module OptparsePlus
|
4
|
+
# A Logger instance that gives better control of messaging the user
|
5
|
+
# and logging app activity. At it's most basic, you would use <tt>info</tt>
|
6
|
+
# as a replacement for +puts+ and <tt>error</tt> as a replacement
|
7
|
+
# for <tt>STDERR.puts</tt>. Since this is a logger, however, you
|
8
|
+
# can also use #debug, #warn, and #fatal, and you can control
|
9
|
+
# the format and "logging level" as such.
|
10
|
+
#
|
11
|
+
# So, by default:
|
12
|
+
# * debug messages do not appear anywhere
|
13
|
+
# * info messages appear on the standard output
|
14
|
+
# * warn, error, and fatal messagse appear on the standard error
|
15
|
+
# * The default format of messages is simply the message, no logging cruft, however if your output
|
16
|
+
# is redirected to a file, a better timestamped logging format is used
|
17
|
+
#
|
18
|
+
# You can customize this in several ways:
|
19
|
+
#
|
20
|
+
# * You can override the devices used by passing different devices to the constructor
|
21
|
+
# * You can adjust the level of message that goes to the error logger via error_level=
|
22
|
+
# * You can adjust the format for messages to the error logger separately via error_formatter=
|
23
|
+
#
|
24
|
+
# === Example
|
25
|
+
#
|
26
|
+
# logger = CLILogger.new
|
27
|
+
# logger.debug("Starting up") # => only the standard output gets this
|
28
|
+
# logger.warn("careful!") # => only the standard error gets this
|
29
|
+
# logger.error("Something went wrong!") # => only the standard error gets this
|
30
|
+
#
|
31
|
+
# logger = CLILogger.new
|
32
|
+
# logger.error_level = Logger::ERROR
|
33
|
+
# logger.debug("Starting up") # => only the standard output gets this
|
34
|
+
# logger.warn("careful!") # => only the standard OUTPUT gets this
|
35
|
+
# logger.error("Something went wrong!") # => only the standard error gets this
|
36
|
+
#
|
37
|
+
# logger = CLILogger.new('logfile.txt')
|
38
|
+
# logger.debug("Starting up") # => logfile.txt gets this
|
39
|
+
# logger.error("Something went wrong!") # => BOTH logfile.txt AND the standard error get this
|
40
|
+
class CLILogger < Logger
|
41
|
+
BLANK_FORMAT = proc { |severity,datetime,progname,msg|
|
42
|
+
msg + "\n"
|
43
|
+
}
|
44
|
+
|
45
|
+
# Helper to proxy methods to the super class AND to the internal error logger
|
46
|
+
#
|
47
|
+
# +symbol+:: Symbol for name of the method to proxy
|
48
|
+
def self.proxy_method(symbol) #:nodoc:
|
49
|
+
old_name = "old_#{symbol}".to_sym
|
50
|
+
alias_method old_name,symbol
|
51
|
+
define_method symbol do |*args,&block|
|
52
|
+
send(old_name,*args,&block)
|
53
|
+
@stderr_logger.send(symbol,*args,&block)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
proxy_method :'formatter='
|
58
|
+
proxy_method :'datetime_format='
|
59
|
+
|
60
|
+
def add(severity, message = nil, progname = nil, &block) #:nodoc:
|
61
|
+
if @split_logs
|
62
|
+
unless severity >= @stderr_logger.level
|
63
|
+
super(severity,message,progname,&block)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
super(severity,message,progname,&block)
|
67
|
+
end
|
68
|
+
@stderr_logger.add(severity,message,progname,&block)
|
69
|
+
end
|
70
|
+
|
71
|
+
DEFAULT_ERROR_LEVEL = Logger::Severity::WARN
|
72
|
+
|
73
|
+
# A logger that logs error-type messages to a second device; useful
|
74
|
+
# for ensuring that error messages go to standard error. This should be
|
75
|
+
# pretty smart about doing the right thing. If both log devices are
|
76
|
+
# ttys, e.g. one is going to standard error and the other to the standard output,
|
77
|
+
# messages only appear once in the overall output stream. In other words,
|
78
|
+
# an ERROR logged will show up *only* in the standard error. If either
|
79
|
+
# log device is NOT a tty, then all messages go to +log_device+ and only
|
80
|
+
# errors go to +error_device+
|
81
|
+
#
|
82
|
+
# +log_device+:: device where all log messages should go, based on level
|
83
|
+
# +error_device+:: device where all error messages should go. By default, this is Logger::Severity::WARN
|
84
|
+
def initialize(log_device=$stdout,error_device=$stderr)
|
85
|
+
@stderr_logger = Logger.new(error_device)
|
86
|
+
|
87
|
+
super(log_device)
|
88
|
+
|
89
|
+
log_device_tty = tty?(log_device)
|
90
|
+
error_device_tty = tty?(error_device)
|
91
|
+
|
92
|
+
@split_logs = log_device_tty && error_device_tty
|
93
|
+
|
94
|
+
self.level = Logger::Severity::INFO
|
95
|
+
@stderr_logger.level = DEFAULT_ERROR_LEVEL
|
96
|
+
|
97
|
+
self.formatter = BLANK_FORMAT if log_device_tty
|
98
|
+
@stderr_logger.formatter = BLANK_FORMAT if error_device_tty
|
99
|
+
end
|
100
|
+
|
101
|
+
def level=(level)
|
102
|
+
super(level)
|
103
|
+
if (level > DEFAULT_ERROR_LEVEL) && @split_logs
|
104
|
+
@stderr_logger.level = level
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Set the threshold for what messages go to the error device. Note that calling
|
109
|
+
# #level= will *not* affect the error logger *unless* both devices are TTYs.
|
110
|
+
#
|
111
|
+
# +level+:: a constant from Logger::Severity for the level of messages that should go
|
112
|
+
# to the error logger
|
113
|
+
def error_level=(level)
|
114
|
+
@stderr_logger.level = level
|
115
|
+
end
|
116
|
+
|
117
|
+
# Overrides the formatter for the error logger. A future call to #formatter= will
|
118
|
+
# affect both, so the order of the calls matters.
|
119
|
+
#
|
120
|
+
# +formatter+:: Proc that handles the formatting, the same as for #formatter=
|
121
|
+
def error_formatter=(formatter)
|
122
|
+
@stderr_logger.formatter=formatter
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def tty?(device_or_string)
|
128
|
+
return device_or_string.tty? if device_or_string.respond_to? :tty?
|
129
|
+
false
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|