rubycut-babushka 0.10.6
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/Gemfile +8 -0
- data/Gemfile.lock +31 -0
- data/README.markdown +246 -0
- data/Rakefile +26 -0
- data/bin/babushka +11 -0
- data/deps/babushka.rb +101 -0
- data/deps/dev.rb +12 -0
- data/deps/fhs.rb +31 -0
- data/deps/git.rb +29 -0
- data/deps/homebrew.rb +30 -0
- data/deps/os_x.rb +33 -0
- data/deps/packages.rb +22 -0
- data/deps/pkg_managers.rb +110 -0
- data/deps/ruby.rb +23 -0
- data/deps/rubygems.rb +24 -0
- data/deps/system.rb +10 -0
- data/deps/templates/app.rb +68 -0
- data/deps/templates/external.rb +12 -0
- data/deps/templates/installer.rb +31 -0
- data/deps/templates/managed.rb +105 -0
- data/deps/templates/ppa.rb +24 -0
- data/deps/templates/src.rb +42 -0
- data/deps/templates/tmbundle.rb +15 -0
- data/lib/babushka.rb +28 -0
- data/lib/babushka/accepts_block_for.rb +72 -0
- data/lib/babushka/accepts_list_for.rb +49 -0
- data/lib/babushka/accepts_value_for.rb +24 -0
- data/lib/babushka/base.rb +78 -0
- data/lib/babushka/bug_reporter.rb +55 -0
- data/lib/babushka/cmdline.rb +133 -0
- data/lib/babushka/cmdline/handler.rb +41 -0
- data/lib/babushka/cmdline/helpers.rb +127 -0
- data/lib/babushka/cmdline/parser.rb +69 -0
- data/lib/babushka/colorizer.rb +59 -0
- data/lib/babushka/core_patches/array.rb +171 -0
- data/lib/babushka/core_patches/blank.rb +22 -0
- data/lib/babushka/core_patches/bytes.rb +52 -0
- data/lib/babushka/core_patches/hash.rb +107 -0
- data/lib/babushka/core_patches/hashish.rb +14 -0
- data/lib/babushka/core_patches/integer.rb +25 -0
- data/lib/babushka/core_patches/io.rb +8 -0
- data/lib/babushka/core_patches/numeric.rb +16 -0
- data/lib/babushka/core_patches/object.rb +27 -0
- data/lib/babushka/core_patches/string.rb +116 -0
- data/lib/babushka/core_patches/symbol.rb +12 -0
- data/lib/babushka/core_patches/try.rb +15 -0
- data/lib/babushka/core_patches/uri.rb +24 -0
- data/lib/babushka/dep.rb +470 -0
- data/lib/babushka/dep_context.rb +18 -0
- data/lib/babushka/dep_definer.rb +115 -0
- data/lib/babushka/dep_pool.rb +49 -0
- data/lib/babushka/dep_runner.rb +85 -0
- data/lib/babushka/dsl.rb +26 -0
- data/lib/babushka/git_repo.rb +185 -0
- data/lib/babushka/helpers/git_helpers.rb +32 -0
- data/lib/babushka/helpers/log_helpers.rb +176 -0
- data/lib/babushka/helpers/path_helpers.rb +34 -0
- data/lib/babushka/helpers/run_helpers.rb +145 -0
- data/lib/babushka/helpers/shell_helpers.rb +229 -0
- data/lib/babushka/helpers/suggest_helpers.rb +16 -0
- data/lib/babushka/helpers/uri_helpers.rb +36 -0
- data/lib/babushka/ip.rb +160 -0
- data/lib/babushka/lambda_chooser.rb +40 -0
- data/lib/babushka/levenshtein.rb +125 -0
- data/lib/babushka/meta_dep.rb +65 -0
- data/lib/babushka/meta_dep_context.rb +15 -0
- data/lib/babushka/parameter.rb +143 -0
- data/lib/babushka/pkg_helper.rb +81 -0
- data/lib/babushka/pkg_helpers/apt_helper.rb +61 -0
- data/lib/babushka/pkg_helpers/base_helper.rb +19 -0
- data/lib/babushka/pkg_helpers/binpkgsrc_helper.rb +48 -0
- data/lib/babushka/pkg_helpers/binports_helper.rb +34 -0
- data/lib/babushka/pkg_helpers/brew_helper.rb +110 -0
- data/lib/babushka/pkg_helpers/gem_helper.rb +120 -0
- data/lib/babushka/pkg_helpers/macports_helper.rb +22 -0
- data/lib/babushka/pkg_helpers/npm_helper.rb +45 -0
- data/lib/babushka/pkg_helpers/pacman_helper.rb +27 -0
- data/lib/babushka/pkg_helpers/pip_helper.rb +45 -0
- data/lib/babushka/pkg_helpers/src_helper.rb +16 -0
- data/lib/babushka/pkg_helpers/yum_helper.rb +25 -0
- data/lib/babushka/popen.rb +40 -0
- data/lib/babushka/prompt.rb +176 -0
- data/lib/babushka/renderable.rb +67 -0
- data/lib/babushka/resource.rb +215 -0
- data/lib/babushka/run_reporter.rb +60 -0
- data/lib/babushka/shell.rb +108 -0
- data/lib/babushka/source.rb +216 -0
- data/lib/babushka/source_pool.rb +146 -0
- data/lib/babushka/system_definitions.rb +97 -0
- data/lib/babushka/system_profile.rb +210 -0
- data/lib/babushka/task.rb +142 -0
- data/lib/babushka/vars.rb +108 -0
- data/lib/babushka/version_of.rb +65 -0
- data/lib/babushka/version_str.rb +57 -0
- data/lib/babushka/xml_string.rb +28 -0
- data/lib/components.rb +82 -0
- data/lib/fancypath/fancypath.rb +200 -0
- data/lib/inkan/inkan.rb +76 -0
- data/spec/acceptance/acceptance.rb +43 -0
- data/spec/acceptance_helper.rb +113 -0
- data/spec/archives/Blah.app.zip +0 -0
- data/spec/archives/archive.tar +0 -0
- data/spec/archives/archive.tar.bz2 +0 -0
- data/spec/archives/archive.tar.gz +0 -0
- data/spec/archives/archive.tbz2 +0 -0
- data/spec/archives/archive.tgz +0 -0
- data/spec/archives/archive.zip +0 -0
- data/spec/archives/content.txt +5 -0
- data/spec/archives/invalid_archive +5 -0
- data/spec/archives/nested archive/content.txt +5 -0
- data/spec/archives/nested_archive.tar +0 -0
- data/spec/archives/really_a_gzip.zip +0 -0
- data/spec/archives/test-0.3.1.tgz +0 -0
- data/spec/archives/tgz_archive +0 -0
- data/spec/archives/zip_without_extension +0 -0
- data/spec/babushka/accepts_for_spec.rb +174 -0
- data/spec/babushka/accepts_for_support.rb +72 -0
- data/spec/babushka/cmdline/console_spec.rb +11 -0
- data/spec/babushka/cmdline/help_spec.rb +61 -0
- data/spec/babushka/cmdline/version_spec.rb +10 -0
- data/spec/babushka/core_patches_spec.rb +171 -0
- data/spec/babushka/dep_context_spec.rb +58 -0
- data/spec/babushka/dep_definer_spec.rb +152 -0
- data/spec/babushka/dep_definer_support.rb +36 -0
- data/spec/babushka/dep_spec.rb +567 -0
- data/spec/babushka/dep_support.rb +29 -0
- data/spec/babushka/deps_spec.rb +113 -0
- data/spec/babushka/gem_helper_spec.rb +90 -0
- data/spec/babushka/git_repo_spec.rb +396 -0
- data/spec/babushka/ip_spec.rb +131 -0
- data/spec/babushka/lambda_chooser_spec.rb +115 -0
- data/spec/babushka/meta_dep_definer_spec.rb +127 -0
- data/spec/babushka/meta_dep_wrapper_spec.rb +32 -0
- data/spec/babushka/parameter_spec.rb +135 -0
- data/spec/babushka/path_helpers_spec.rb +102 -0
- data/spec/babushka/prompt_spec.rb +188 -0
- data/spec/babushka/renderable_spec.rb +100 -0
- data/spec/babushka/resource_spec.rb +141 -0
- data/spec/babushka/run_helpers_spec.rb +26 -0
- data/spec/babushka/shell_helpers_spec.rb +244 -0
- data/spec/babushka/shell_spec.rb +19 -0
- data/spec/babushka/source_pool_spec.rb +320 -0
- data/spec/babushka/source_pool_support.rb +31 -0
- data/spec/babushka/source_spec.rb +382 -0
- data/spec/babushka/source_support.rb +17 -0
- data/spec/babushka/system_profile_spec.rb +61 -0
- data/spec/babushka/task_spec.rb +141 -0
- data/spec/babushka/uri_spec.rb +13 -0
- data/spec/babushka/vars_spec.rb +59 -0
- data/spec/babushka/version_of_spec.rb +110 -0
- data/spec/babushka/version_str_spec.rb +130 -0
- data/spec/babushka/version_str_support.rb +37 -0
- data/spec/babushka/xml_string_spec.rb +98 -0
- data/spec/deps/bad/broken.rb +7 -0
- data/spec/deps/bad/working.rb +3 -0
- data/spec/deps/good/meta.rb +14 -0
- data/spec/deps/good/test.rb +11 -0
- data/spec/deps/outer/deps.rb +19 -0
- data/spec/deps/outer/more deps.rb +11 -0
- data/spec/deps/params/params.rb +10 -0
- data/spec/fancypath/fancypath_spec.rb +272 -0
- data/spec/fancypath_support.rb +10 -0
- data/spec/inkan/inkan_spec.rb +217 -0
- data/spec/renderable/different_example.conf.erb +4 -0
- data/spec/renderable/example.conf.erb +3 -0
- data/spec/renderable/example.sh +6 -0
- data/spec/renderable/with_binding.conf.erb +4 -0
- data/spec/renderable/xml_example.conf.erb +8 -0
- data/spec/repos/remote.git.tgz +0 -0
- data/spec/spec_helper.rb +87 -0
- metadata +238 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'abbrev'
|
|
2
|
+
|
|
3
|
+
module Babushka
|
|
4
|
+
module Cmdline
|
|
5
|
+
|
|
6
|
+
def handle name, description, &blk
|
|
7
|
+
Handler.add name, description, blk
|
|
8
|
+
end
|
|
9
|
+
module_function :handle
|
|
10
|
+
|
|
11
|
+
class Handler
|
|
12
|
+
def self.add name, description, opt_definer
|
|
13
|
+
Handler.new(name, description, opt_definer).tap {|handler|
|
|
14
|
+
(@handlers ||= []).push handler
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.all
|
|
19
|
+
@handlers.reject {|h| h.name == 'global' }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.abbrev
|
|
23
|
+
all.map(&:name).abbrev
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.for name
|
|
27
|
+
@handlers.detect {|h| h.name == name }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
attr_reader :name, :description, :opt_definer, :handler
|
|
31
|
+
|
|
32
|
+
def initialize name, description, opt_definer
|
|
33
|
+
@name, @description, @opt_definer = name, description, (opt_definer || L{})
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def run &handler
|
|
37
|
+
@handler = handler
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
module Babushka
|
|
4
|
+
module Cmdline
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def fail_with message
|
|
8
|
+
log message if message.is_a? String
|
|
9
|
+
exit 1
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module Helpers
|
|
13
|
+
extend LogHelpers
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def print_version opts = {}
|
|
17
|
+
if opts[:full]
|
|
18
|
+
log "Babushka v#{VERSION}, (c) 2011 Ben Hoskings <ben@hoskings.net>"
|
|
19
|
+
else
|
|
20
|
+
log VERSION
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def print_usage
|
|
25
|
+
log "\nThe gist:"
|
|
26
|
+
log " #{Base.program_name} <command> [options]"
|
|
27
|
+
log "\nAlso:"
|
|
28
|
+
log " #{Base.program_name} help <command> # Print command-specific usage info"
|
|
29
|
+
log " #{Base.program_name} <dep name> # A shortcut for 'babushka meet <dep name>'"
|
|
30
|
+
log " #{Base.program_name} babushka # Update babushka itself (what babushka.me/up does)"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def print_handlers
|
|
34
|
+
log "\nCommands:"
|
|
35
|
+
Handler.all.each {|handler|
|
|
36
|
+
log " #{handler.name.ljust(10)} #{handler.description}"
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def print_examples
|
|
41
|
+
log "\nExamples:"
|
|
42
|
+
log " # Inspect the 'system' dep (and all its sub-deps) without touching the system.".colorize('grey')
|
|
43
|
+
log " #{Base.program_name} system --dry-run"
|
|
44
|
+
log "\n"
|
|
45
|
+
log " # Meet the 'fish' dep (i.e. install fish and all its dependencies).".colorize('grey')
|
|
46
|
+
log " #{Base.program_name} fish"
|
|
47
|
+
log "\n"
|
|
48
|
+
log " # Meet the 'user setup' dep, printing lots of debugging (including realtime".colorize('grey')
|
|
49
|
+
log " # shell command output).".colorize('grey')
|
|
50
|
+
log " #{Base.program_name} 'user setup' --debug"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def print_notes
|
|
54
|
+
log "\nCommands can be abbrev'ed, as long as they remain unique."
|
|
55
|
+
log " e.g. '#{Base.program_name} l' is short for '#{Base.program_name} list'."
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def search_results_for q
|
|
59
|
+
YAML.load(search_webservice_for(q).body).sort_by {|i|
|
|
60
|
+
-i[:runs_this_week]
|
|
61
|
+
}.map {|i|
|
|
62
|
+
[
|
|
63
|
+
i[:name],
|
|
64
|
+
i[:source_uri],
|
|
65
|
+
((i[:runs_this_week] && i[:runs_this_week] > 0) ? "#{i[:runs_this_week]} this week" : "#{i[:total_runs]} ever"),
|
|
66
|
+
((i[:runs_this_week] && i[:runs_this_week] > 0) ? "#{(i[:success_rate_this_week] * 100).round}%" : ((i[:total_runs] && i[:total_runs] > 0) ? "#{(i[:total_success_rate] * 100).round}%" : '')),
|
|
67
|
+
(i[:source_uri][github_autosource_regex] ? "#{Base.program_name} #{$1}:#{"'" if i[:name][/\s/]}#{i[:name]}#{"'" if i[:name][/\s/]}" : '✣')
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def print_search_results search_term, results
|
|
73
|
+
log "The webservice knows about #{results.length} dep#{'s' unless results.length == 1} that match#{'es' if results.length == 1} '#{search_term}':"
|
|
74
|
+
log ""
|
|
75
|
+
Logging.log_table(
|
|
76
|
+
['Name', 'Source', 'Runs', ' ✓', 'Command'],
|
|
77
|
+
results
|
|
78
|
+
)
|
|
79
|
+
if (custom_sources = results.select {|r| r[1][github_autosource_regex].nil? }.length) > 0
|
|
80
|
+
log ""
|
|
81
|
+
log "✣ #{custom_sources == 1 ? 'This source has a custom URI' : 'These sources have custom URIs'}, so babushka can't discover #{custom_sources == 1 ? 'it' : 'them'} automatically."
|
|
82
|
+
log " You can run #{custom_sources == 1 ? 'its' : 'their'} deps in the same way, though, once you add #{custom_sources == 1 ? 'it' : 'them'} manually:"
|
|
83
|
+
log " $ #{Base.program_name} sources -a <alias> <uri>"
|
|
84
|
+
log " $ #{Base.program_name} <alias>:<dep>"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def github_autosource_regex
|
|
89
|
+
/^git\:\/\/github\.com\/(.*)\/babushka-deps(\.git)?/
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def search_webservice_for q
|
|
93
|
+
Net::HTTP.start('babushka.me') {|http|
|
|
94
|
+
http.get URI.escape("/deps/search.yaml/#{q}")
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def generate_list_for to_list, filter_str
|
|
99
|
+
context = to_list == :deps ? Base.program_name : ':template =>'
|
|
100
|
+
match_str = filter_str.try(:downcase)
|
|
101
|
+
Base.sources.all_present.each {|source|
|
|
102
|
+
source.load!
|
|
103
|
+
}.map {|source|
|
|
104
|
+
[source, source.send(to_list).items]
|
|
105
|
+
}.map {|(source,items)|
|
|
106
|
+
if match_str.nil? || source.name.downcase[match_str]
|
|
107
|
+
[source, items]
|
|
108
|
+
else
|
|
109
|
+
[source, items.select {|item| item.name.downcase[match_str] }]
|
|
110
|
+
end
|
|
111
|
+
}.select {|(_,items)|
|
|
112
|
+
!items.empty?
|
|
113
|
+
}.sort_by {|(source,_)|
|
|
114
|
+
source.name
|
|
115
|
+
}.each {|(source,items)|
|
|
116
|
+
indent = (items.map {|item| "#{source.name}:#{item.name}".length }.max || 0) + 3
|
|
117
|
+
log ""
|
|
118
|
+
log "# #{source.name} (#{source.type})#{" - #{source.uri}" unless source.implicit?}"
|
|
119
|
+
log "# #{items.length} #{to_list.to_s.chomp(items.length == 1 ? 's' : '')}#{" matching '#{filter_str}'" unless filter_str.nil?}:"
|
|
120
|
+
items.each {|dep|
|
|
121
|
+
log "#{context} #{"'#{source.name}:#{dep.name}'".ljust(indent)}"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
|
|
3
|
+
module Babushka
|
|
4
|
+
module Cmdline
|
|
5
|
+
class Parser
|
|
6
|
+
include LogHelpers
|
|
7
|
+
|
|
8
|
+
attr_reader :verb, :argv, :opts
|
|
9
|
+
|
|
10
|
+
def self.for argv
|
|
11
|
+
if argv.empty? || (%w[-h --help] & argv).any?
|
|
12
|
+
new 'help', argv
|
|
13
|
+
elsif !Handler.abbrev.has_key?(argv.first.sub(/^--/, ''))
|
|
14
|
+
new 'meet', argv, :implicit_verb => true
|
|
15
|
+
else
|
|
16
|
+
new Handler.abbrev[argv.shift.sub(/^--/, '')], argv
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize verb, argv, parse_opts = {}
|
|
21
|
+
@verb, @argv, @opts, @implicit_verb = verb, argv, default_opts, parse_opts[:implicit_verb]
|
|
22
|
+
parse(&Handler.for('global').opt_definer)
|
|
23
|
+
parse(&Handler.for(verb).opt_definer)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def run
|
|
27
|
+
parser.parse! argv
|
|
28
|
+
Handler.for(verb).handler.call self
|
|
29
|
+
rescue OptionParser::ParseError => e
|
|
30
|
+
log_error "The #{e.args.first} option #{error_reason(e)}. #{hint}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def print_usage
|
|
34
|
+
log parser.to_s.sub(/^Usage:.*$/, '')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def parse &blk
|
|
38
|
+
instance_eval(&blk)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def default_opts
|
|
44
|
+
{
|
|
45
|
+
:"[no_]color" => $stdout.tty?
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def hint
|
|
50
|
+
"`babushka#{" #{verb}" unless @implicit_verb} --help` for more info."
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def error_reason e
|
|
54
|
+
{
|
|
55
|
+
OptionParser::MissingArgument => "requires an argument"
|
|
56
|
+
}[e.class] || "isn't valid"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def opt *args, &block
|
|
60
|
+
opt_name = args.collapse(/^--/).first.gsub(/\s.*$/, '').gsub('-', '_').to_sym
|
|
61
|
+
parser.on(*args) {|arg| opts[opt_name] = arg }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def parser
|
|
65
|
+
@parser ||= OptionParser.new
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
class String
|
|
2
|
+
|
|
3
|
+
private
|
|
4
|
+
|
|
5
|
+
class Colorizer
|
|
6
|
+
include Singleton
|
|
7
|
+
|
|
8
|
+
HomeOffset = 29
|
|
9
|
+
LightOffset = 60
|
|
10
|
+
BGOffset = 10
|
|
11
|
+
LightRegex = /^light_/
|
|
12
|
+
ColorRegex = /^(light_)?none|gr[ae]y|red|green|yellow|blue|pink|cyan|white$/
|
|
13
|
+
CtrlRegex = /^bold|underlined?|blink(ing)?|reversed?$/
|
|
14
|
+
ColorOffsets = {
|
|
15
|
+
'none' => 0,
|
|
16
|
+
'gray' => 61, 'grey' => 61,
|
|
17
|
+
'red' => 2,
|
|
18
|
+
'green' => 3,
|
|
19
|
+
'yellow' => 4,
|
|
20
|
+
'blue' => 5,
|
|
21
|
+
'pink' => 6,
|
|
22
|
+
'cyan' => 7,
|
|
23
|
+
'white' => 8
|
|
24
|
+
}
|
|
25
|
+
CtrlOffsets = {
|
|
26
|
+
'bold' => 1,
|
|
27
|
+
'underline' => 4, 'underlined' => 4,
|
|
28
|
+
'blink' => 5, 'blinking' => 5,
|
|
29
|
+
'reverse' => 7, 'reversed' => 7
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def colorize text, description
|
|
33
|
+
return text if Babushka::Base.task.opts[:"[no_]color"] == false
|
|
34
|
+
|
|
35
|
+
terms = " #{description} ".gsub(' light ', ' light_').gsub(' on ', ' on_').strip.split(/\s+/)
|
|
36
|
+
bg = terms.detect {|i| /on_#{ColorRegex}/ =~ i }
|
|
37
|
+
fg = terms.detect {|i| ColorRegex =~ i }
|
|
38
|
+
ctrl = terms.detect {|i| CtrlRegex =~ i }
|
|
39
|
+
|
|
40
|
+
"\e[#{"0;#{fg_for(fg)};#{bg_for(bg) || ctrl_for(ctrl)}"}m#{text}\e[0m"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def fg_for name
|
|
44
|
+
light = name.gsub!(LightRegex, '') unless name.nil?
|
|
45
|
+
(ColorOffsets[name] || 0) + HomeOffset + (light ? LightOffset : 0)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def bg_for name
|
|
49
|
+
# There's a hole in the table on bg=none, so we use BGOffset to the left
|
|
50
|
+
offset = fg_for((name || '').sub(/^on_/, ''))
|
|
51
|
+
offset + BGOffset unless offset == HomeOffset
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def ctrl_for name
|
|
55
|
+
CtrlOffsets[name] || HomeOffset
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
class Array
|
|
2
|
+
# Returns true iff +other+ appears exactly at the start of +self+.
|
|
3
|
+
def starts_with? first, *rest
|
|
4
|
+
other = first.is_a?(Array) ? first : [first].concat(rest)
|
|
5
|
+
self[0, other.length] == other
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Returns true iff +other+ appears exactly at the end of +self+.
|
|
9
|
+
def ends_with? first, *rest
|
|
10
|
+
other = first.is_a?(Array) ? first : [first].concat(rest)
|
|
11
|
+
self[-other.length, other.length] == other
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Like #detect, but return the result of the block instead of the element.
|
|
15
|
+
def pick &block
|
|
16
|
+
value = nil
|
|
17
|
+
detect {|i| value = yield(i) }
|
|
18
|
+
value
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# This is defined separately, and then aliased into place if required, so we
|
|
22
|
+
# can run specs against it no matter which ruby we're running against.
|
|
23
|
+
def local_group_by &block
|
|
24
|
+
inject({}) {|hsh,i|
|
|
25
|
+
(hsh[yield(i)] ||= []).push i
|
|
26
|
+
hsh
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
alias_method :group_by, :local_group_by unless [].respond_to?(:group_by)
|
|
30
|
+
|
|
31
|
+
# Return two arrays, the first being the portion of this array preceding the
|
|
32
|
+
# first element for which the block returs true, and the second being the
|
|
33
|
+
# remainder (or +nil+ if the block didn't return true for any elements).
|
|
34
|
+
def cut &block
|
|
35
|
+
if (cut_at = index {|i| yield i }).nil?
|
|
36
|
+
[self, nil]
|
|
37
|
+
else
|
|
38
|
+
[self[0...cut_at], self[cut_at..-1]]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
# Return two arrays in the same manner as +cut+, but check for element
|
|
42
|
+
# equality against +value+ to find the point at which to cut the array.
|
|
43
|
+
def cut_at value
|
|
44
|
+
cut {|i| i == value }
|
|
45
|
+
end
|
|
46
|
+
# Return a new array containing every element from this array for which
|
|
47
|
+
# the block returns true.
|
|
48
|
+
def extract &block
|
|
49
|
+
dup.extract!(&block)
|
|
50
|
+
end
|
|
51
|
+
# Like +extract+, but remove the extracted values in-place before
|
|
52
|
+
# returning them.
|
|
53
|
+
def extract! &block
|
|
54
|
+
dup.inject [] do |extracted,i|
|
|
55
|
+
extracted << delete(i) if yield i
|
|
56
|
+
extracted
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
# Return a new array containing all the elements from this array that
|
|
60
|
+
# are neither +#nil?+ nor +#blank?+.
|
|
61
|
+
def squash
|
|
62
|
+
dup.squash!
|
|
63
|
+
end
|
|
64
|
+
# Like +squash+, but remove the +#nil?+ and +#blank?+ entries in-place.
|
|
65
|
+
def squash!
|
|
66
|
+
delete_if(&:blank?)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Return a new array containing the elements that match +pattern+, with
|
|
70
|
+
# +pattern+ removed (or replaced via Array#sub, if +replacement+ is
|
|
71
|
+
# supplied).
|
|
72
|
+
#
|
|
73
|
+
# This is useful for selecting items from a list based on some label,
|
|
74
|
+
# removing the label at the same time. A good example is finding the current
|
|
75
|
+
# git branch. Given this repository:
|
|
76
|
+
# $ git branch
|
|
77
|
+
# master
|
|
78
|
+
# * next
|
|
79
|
+
# topic
|
|
80
|
+
# You can use +#collapse+ to retrieve the current branch like this:
|
|
81
|
+
# shell('git branch').split("\n").collapse(/\* /) #=> ["next"]
|
|
82
|
+
def collapse pattern, replacement = ''
|
|
83
|
+
grep(pattern).map {|i| i.sub pattern, replacement }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Return a new array by converting each element in this array to a VersionOf.
|
|
87
|
+
def versions
|
|
88
|
+
map {|i| Babushka::VersionOf::Helpers.VersionOf i }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Return a string describing this array as an English list. The final two
|
|
92
|
+
# elements are separated with 'and', and all the other elements are separated
|
|
93
|
+
# with commas.
|
|
94
|
+
#
|
|
95
|
+
# %w[John Paul Ringo George].to_list #=> "John, Paul, Ringo and George"
|
|
96
|
+
#
|
|
97
|
+
# A custom conjugation can be specified by passing +:conj+; if present, it
|
|
98
|
+
# will be used instead of 'and'.
|
|
99
|
+
#
|
|
100
|
+
# %[rain hail shine].to_list(:conj => 'or') #=> "rain, hail or shine"
|
|
101
|
+
#
|
|
102
|
+
# To add an oxford comma before the conjugation, pass +:oxford => true+.
|
|
103
|
+
#
|
|
104
|
+
# %w[hook line sinker].to_list(:oxford => true) #=> "hook, line, and sinker"
|
|
105
|
+
#
|
|
106
|
+
# If +:suffix+ is set, it will be appended along with the correct linking verb,
|
|
107
|
+
# i.e. 'is' for single-item lists and 'are' otherwise.
|
|
108
|
+
#
|
|
109
|
+
# %w[coffee].to_list(:suffix => 'great') => "coffee is great"
|
|
110
|
+
# %w[Cîroc Żubrówka].to_list(:suffix => 'vodkas') #=> "Cîroc and Żubrówka are vodkas"
|
|
111
|
+
#
|
|
112
|
+
# If +:limit+ is set, only the first +:limit+ items will be included in the
|
|
113
|
+
# output. If any elements were ommitted as a result, the suffix 'et al' will
|
|
114
|
+
# be appended to indicate there are missing elements.
|
|
115
|
+
#
|
|
116
|
+
# %w[latte espresso ristretto].to_list(:suffix => 'coffees', :limit => 2) #=> "latte, espresso et al are coffees"
|
|
117
|
+
#
|
|
118
|
+
# If +:noun+ is set in addition to +:limit+, it will be used to describe the
|
|
119
|
+
# length of the list after 'et al' if any items were ommitted as a result of
|
|
120
|
+
# the +:limit+ setting.
|
|
121
|
+
#
|
|
122
|
+
# %w[latte espresso ristretto].to_list(:limit => 2, :noun => 'coffees') #=> "latte, espresso et al - 3 coffees"
|
|
123
|
+
def to_list(opts = {})
|
|
124
|
+
items = map(&:to_s)
|
|
125
|
+
if opts[:limit].nil? || (length <= opts[:limit])
|
|
126
|
+
[
|
|
127
|
+
items[0..-2].squash.join(', '),
|
|
128
|
+
items.last
|
|
129
|
+
].squash.join("#{',' if opts[:oxford]} #{opts[:conj] || 'and'} ")
|
|
130
|
+
else
|
|
131
|
+
items[0..(opts[:limit] - 1)].squash.join(', ') + ' et al' + (opts[:noun].nil? ? '' : " - #{length} #{opts[:noun]}")
|
|
132
|
+
end +
|
|
133
|
+
(opts[:suffix] ? " #{length > 1 ? 'are' : 'is'} #{opts[:suffix].strip}" : '')
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# If the final element of the array is a +Hash+, it's removed from this array
|
|
137
|
+
# and returned. Otherwise, an empty hash is returned.
|
|
138
|
+
def extract_options!
|
|
139
|
+
last.is_a?(::Hash) ? pop : {}
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# As above, without modifying the receiving object.
|
|
143
|
+
def extract_options
|
|
144
|
+
dup.extract_options!
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Return a new array containing the terms from this array that were
|
|
148
|
+
# determined to be 'similar to' +string+. A string is considered to
|
|
149
|
+
# be similar to another if its Levenshtein distance is less than
|
|
150
|
+
# either the string's length minus one, or one fifth is length plus
|
|
151
|
+
# two, whichever is less.
|
|
152
|
+
#
|
|
153
|
+
# word length 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 …
|
|
154
|
+
# typos allowed 0 0 1 2 3 3 3 3 3 4 4 4 4 4 5 …
|
|
155
|
+
#
|
|
156
|
+
# This means that:
|
|
157
|
+
# - a little over one fifth of strings longer than 4 characters can be misspelt;
|
|
158
|
+
# - strings 3 or 4 characters long can have 1 or 2 misspelt characters respectively;
|
|
159
|
+
# - strings 1 or 2 characters long must be spelt correctly.
|
|
160
|
+
def similar_to string
|
|
161
|
+
map {|term|
|
|
162
|
+
[term, Babushka::Levenshtein.distance(term, string)]
|
|
163
|
+
}.select {|(i, similarity)|
|
|
164
|
+
similarity <= [i.length - 2, (i.length / 5) + 2].min
|
|
165
|
+
}.sort_by {|(_, similarity)|
|
|
166
|
+
similarity
|
|
167
|
+
}.map {|(i, _)|
|
|
168
|
+
i
|
|
169
|
+
}
|
|
170
|
+
end
|
|
171
|
+
end
|