pebblescape 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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +23 -0
- data/Rakefile +2 -0
- data/bin/pebbles +14 -0
- data/lib/pebbles.rb +23 -0
- data/lib/pebbles/api.rb +122 -0
- data/lib/pebbles/api/apps.rb +71 -0
- data/lib/pebbles/api/config_vars.rb +33 -0
- data/lib/pebbles/api/errors.rb +27 -0
- data/lib/pebbles/api/login.rb +14 -0
- data/lib/pebbles/api/releases.rb +33 -0
- data/lib/pebbles/api/user.rb +14 -0
- data/lib/pebbles/auth.rb +302 -0
- data/lib/pebbles/cli.rb +35 -0
- data/lib/pebbles/command.rb +256 -0
- data/lib/pebbles/command/apps.rb +225 -0
- data/lib/pebbles/command/auth.rb +85 -0
- data/lib/pebbles/command/base.rb +231 -0
- data/lib/pebbles/command/config.rb +147 -0
- data/lib/pebbles/command/help.rb +124 -0
- data/lib/pebbles/git.rb +69 -0
- data/lib/pebbles/helpers.rb +284 -0
- data/lib/pebbles/version.rb +3 -0
- data/pebbles.gemspec +27 -0
- metadata +142 -0
@@ -0,0 +1,147 @@
|
|
1
|
+
require "pebbles/command/base"
|
2
|
+
|
3
|
+
# manage app config vars
|
4
|
+
#
|
5
|
+
class Pebbles::Command::Config < Pebbles::Command::Base
|
6
|
+
|
7
|
+
# config
|
8
|
+
#
|
9
|
+
# display the config vars for an app
|
10
|
+
#
|
11
|
+
# -s, --shell # output config vars in shell format
|
12
|
+
#
|
13
|
+
#Examples:
|
14
|
+
#
|
15
|
+
# $ pebbles config
|
16
|
+
# A: one
|
17
|
+
# B: two
|
18
|
+
#
|
19
|
+
# $ pebbles config --shell
|
20
|
+
# A=one
|
21
|
+
# B=two
|
22
|
+
#
|
23
|
+
def index
|
24
|
+
validate_arguments!
|
25
|
+
|
26
|
+
vars = if options[:shell]
|
27
|
+
api.get_config_vars(app).body
|
28
|
+
else
|
29
|
+
api.request(
|
30
|
+
:expects => 200,
|
31
|
+
:method => :get,
|
32
|
+
:path => "/apps/#{app}/config_vars",
|
33
|
+
:query => { "symbolic" => true }
|
34
|
+
).body
|
35
|
+
end
|
36
|
+
|
37
|
+
if vars.empty?
|
38
|
+
display("#{app} has no config vars.")
|
39
|
+
else
|
40
|
+
vars.each {|key, value| vars[key] = value.to_s}
|
41
|
+
if options[:shell]
|
42
|
+
vars.keys.sort.each do |key|
|
43
|
+
display(%{#{key}=#{vars[key]}})
|
44
|
+
end
|
45
|
+
else
|
46
|
+
styled_header("#{app} Config Vars")
|
47
|
+
styled_hash(vars)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# config:set KEY1=VALUE1 [KEY2=VALUE2 ...]
|
53
|
+
#
|
54
|
+
# set one or more config vars
|
55
|
+
#
|
56
|
+
#Example:
|
57
|
+
#
|
58
|
+
# $ pebbles config:set A=one
|
59
|
+
# Setting config vars and restarting example... done, v123
|
60
|
+
# A: one
|
61
|
+
#
|
62
|
+
# $ pebbles config:set A=one B=two
|
63
|
+
# Setting config vars and restarting example... done, v123
|
64
|
+
# A: one
|
65
|
+
# B: two
|
66
|
+
#
|
67
|
+
def set
|
68
|
+
unless args.size > 0 and args.all? { |a| a.include?('=') }
|
69
|
+
error("Usage: pebbles config:set KEY1=VALUE1 [KEY2=VALUE2 ...]\nMust specify KEY and VALUE to set.")
|
70
|
+
end
|
71
|
+
|
72
|
+
vars = args.inject({}) do |vars, arg|
|
73
|
+
key, value = arg.split('=', 2)
|
74
|
+
vars[key] = value
|
75
|
+
vars
|
76
|
+
end
|
77
|
+
|
78
|
+
action("Setting config vars and restarting #{app}") do
|
79
|
+
api.put_config_vars(app, vars)
|
80
|
+
|
81
|
+
@status = begin
|
82
|
+
if release = api.get_release(app, 'current').body
|
83
|
+
release['name']
|
84
|
+
end
|
85
|
+
rescue Pebbles::API::Errors::RequestFailed => e
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
vars.each {|key, value| vars[key] = value.to_s}
|
90
|
+
styled_hash(vars)
|
91
|
+
end
|
92
|
+
|
93
|
+
alias_command "config:add", "config:set"
|
94
|
+
|
95
|
+
# config:get KEY
|
96
|
+
#
|
97
|
+
# display a config value for an app
|
98
|
+
#
|
99
|
+
#Examples:
|
100
|
+
#
|
101
|
+
# $ pebbles config:get A
|
102
|
+
# one
|
103
|
+
#
|
104
|
+
def get
|
105
|
+
unless key = shift_argument
|
106
|
+
error("Usage: pebbles config:get KEY\nMust specify KEY.")
|
107
|
+
end
|
108
|
+
validate_arguments!
|
109
|
+
|
110
|
+
vars = api.get_config_vars(app).body
|
111
|
+
key, value = vars.detect {|k,v| k == key}
|
112
|
+
display(value.to_s)
|
113
|
+
end
|
114
|
+
|
115
|
+
# config:unset KEY1 [KEY2 ...]
|
116
|
+
#
|
117
|
+
# unset one or more config vars
|
118
|
+
#
|
119
|
+
# $ pebbles config:unset A
|
120
|
+
# Unsetting A and restarting example... done, v123
|
121
|
+
#
|
122
|
+
# $ pebbles config:unset A B
|
123
|
+
# Unsetting A and restarting example... done, v123
|
124
|
+
# Unsetting B and restarting example... done, v124
|
125
|
+
#
|
126
|
+
def unset
|
127
|
+
if args.empty?
|
128
|
+
error("Usage: pebbles config:unset KEY1 [KEY2 ...]\nMust specify KEY to unset.")
|
129
|
+
end
|
130
|
+
|
131
|
+
args.each do |key|
|
132
|
+
action("Unsetting #{key} and restarting #{app}") do
|
133
|
+
api.delete_config_var(app, key)
|
134
|
+
|
135
|
+
@status = begin
|
136
|
+
if release = api.get_release(app, 'current').body
|
137
|
+
release['name']
|
138
|
+
end
|
139
|
+
rescue Pebbles::API::Errors::RequestFailed => e
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
alias_command "config:remove", "config:unset"
|
146
|
+
|
147
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require "pebbles/command/base"
|
2
|
+
|
3
|
+
# list commands and display help
|
4
|
+
#
|
5
|
+
class Pebbles::Command::Help < Pebbles::Command::Base
|
6
|
+
|
7
|
+
PRIMARY_NAMESPACES = %w( auth apps ps run addons config releases domains logs sharing )
|
8
|
+
|
9
|
+
def index
|
10
|
+
if command = args.shift
|
11
|
+
help_for_command(command)
|
12
|
+
else
|
13
|
+
help_for_root
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_command "-h", "help"
|
18
|
+
alias_command "--help", "help"
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def commands_for_namespace(name)
|
23
|
+
Pebbles::Command.commands.values.select do |command|
|
24
|
+
command[:namespace] == name && command[:command] != name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def namespaces
|
29
|
+
namespaces = Pebbles::Command.namespaces
|
30
|
+
namespaces.delete("app")
|
31
|
+
namespaces
|
32
|
+
end
|
33
|
+
|
34
|
+
def commands
|
35
|
+
Pebbles::Command.commands
|
36
|
+
end
|
37
|
+
|
38
|
+
def skip_namespace?(ns)
|
39
|
+
return true if ns[:description] =~ /DEPRECATED:/
|
40
|
+
return true if ns[:description] =~ /HIDDEN:/
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
def skip_command?(command)
|
45
|
+
return true if command[:help] =~ /DEPRECATED:/
|
46
|
+
return true if command[:help] =~ /^ HIDDEN:/
|
47
|
+
false
|
48
|
+
end
|
49
|
+
|
50
|
+
def primary_namespaces
|
51
|
+
PRIMARY_NAMESPACES.map { |name| namespaces[name] }.compact
|
52
|
+
end
|
53
|
+
|
54
|
+
def additional_namespaces
|
55
|
+
(namespaces.values - primary_namespaces)
|
56
|
+
end
|
57
|
+
|
58
|
+
def summary_for_namespaces(namespaces)
|
59
|
+
size = longest(namespaces.map { |n| n[:name] })
|
60
|
+
namespaces.sort_by {|namespace| namespace[:name]}.each do |namespace|
|
61
|
+
next if skip_namespace?(namespace)
|
62
|
+
name = namespace[:name]
|
63
|
+
namespace[:description]
|
64
|
+
puts " %-#{size}s # %s" % [ name, namespace[:description] ]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def help_for_root
|
69
|
+
puts "Usage: pebbles COMMAND [--app APP] [command-specific-options]"
|
70
|
+
puts
|
71
|
+
puts "Primary help topics, type \"pebbles help TOPIC\" for more details:"
|
72
|
+
puts
|
73
|
+
summary_for_namespaces(primary_namespaces)
|
74
|
+
puts
|
75
|
+
puts "Additional topics:"
|
76
|
+
puts
|
77
|
+
summary_for_namespaces(additional_namespaces)
|
78
|
+
puts
|
79
|
+
end
|
80
|
+
|
81
|
+
def help_for_namespace(name)
|
82
|
+
namespace_commands = commands_for_namespace(name)
|
83
|
+
|
84
|
+
unless namespace_commands.empty?
|
85
|
+
size = longest(namespace_commands.map { |c| c[:banner] })
|
86
|
+
namespace_commands.sort_by { |c| c[:banner].to_s }.each do |command|
|
87
|
+
next if skip_command?(command)
|
88
|
+
command[:summary]
|
89
|
+
puts " %-#{size}s # %s" % [ command[:banner], command[:summary] ]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def help_for_command(name)
|
95
|
+
if command_alias = Pebbles::Command.command_aliases[name]
|
96
|
+
display("Alias: #{name} redirects to #{command_alias}")
|
97
|
+
name = command_alias
|
98
|
+
end
|
99
|
+
if command = commands[name]
|
100
|
+
puts "Usage: pebbles #{command[:banner]}"
|
101
|
+
|
102
|
+
if command[:help].strip.length > 0
|
103
|
+
help = command[:help].split("\n").reject do |line|
|
104
|
+
line =~ /HIDDEN/
|
105
|
+
end
|
106
|
+
puts help[1..-1].join("\n")
|
107
|
+
end
|
108
|
+
puts
|
109
|
+
end
|
110
|
+
|
111
|
+
namespace_commands = commands_for_namespace(name).reject do |command|
|
112
|
+
command[:help] =~ /DEPRECATED/
|
113
|
+
end
|
114
|
+
|
115
|
+
if !namespace_commands.empty?
|
116
|
+
puts "Additional commands, type \"pebbles help COMMAND\" for more details:"
|
117
|
+
puts
|
118
|
+
help_for_namespace(name)
|
119
|
+
puts
|
120
|
+
elsif command.nil?
|
121
|
+
error "#{name} is not a pebbles command. See `pebbles help`."
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/pebbles/git.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require "pebbles/helpers"
|
2
|
+
|
3
|
+
module Pebbles::Git
|
4
|
+
extend Pebbles::Helpers
|
5
|
+
|
6
|
+
def self.check_git_version
|
7
|
+
return unless running_on_windows? || running_on_a_mac?
|
8
|
+
if git_is_insecure(git_version)
|
9
|
+
warn_about_insecure_git
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.git_is_insecure(version)
|
14
|
+
v = Version.parse(version)
|
15
|
+
if v < Version.parse('1.8.5.6')
|
16
|
+
return true
|
17
|
+
end
|
18
|
+
if v >= Version.parse('1.9') && v < Version.parse('1.9.5')
|
19
|
+
return true
|
20
|
+
end
|
21
|
+
if v >= Version.parse('2.0') && v < Version.parse('2.0.5')
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
if v >= Version.parse('2.1') && v < Version.parse('2.1.4')
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
if v >= Version.parse('2.2') && v < Version.parse('2.2.1')
|
28
|
+
return true
|
29
|
+
end
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.warn_about_insecure_git
|
34
|
+
warn "Your version of git is #{git_version}. Which has serious security vulnerabilities."
|
35
|
+
warn "More information here: https://blog.heroku.com/archives/2014/12/23/update_your_git_clients_on_windows_and_os_x"
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def self.git_version
|
41
|
+
version = /git version ([\d\.]+)/.match(`git --version`)
|
42
|
+
error("Git appears to be installed incorrectly\nEnsure that `git --version` outputs the version correctly.") unless version
|
43
|
+
version[1]
|
44
|
+
rescue Errno::ENOENT
|
45
|
+
error("Git must be installed to use pebbles.\nSee instructions here: http://git-scm.com")
|
46
|
+
end
|
47
|
+
|
48
|
+
class Version
|
49
|
+
include Comparable
|
50
|
+
|
51
|
+
attr_accessor :major, :minor, :patch, :special
|
52
|
+
|
53
|
+
def initialize(major, minor=0, patch=0, special=0)
|
54
|
+
@major, @minor, @patch, @special = major, minor, patch, special
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.parse(s)
|
58
|
+
digits = s.split('.').map { |i| i.to_i }
|
59
|
+
Version.new(*digits)
|
60
|
+
end
|
61
|
+
|
62
|
+
def <=>(other)
|
63
|
+
return major <=> other.major unless (major <=> other.major) == 0
|
64
|
+
return minor <=> other.minor unless (minor <=> other.minor) == 0
|
65
|
+
return patch <=> other.patch unless (patch <=> other.patch) == 0
|
66
|
+
return special <=> other.special
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,284 @@
|
|
1
|
+
module Pebbles
|
2
|
+
module Helpers
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def home_directory
|
6
|
+
return Dir.home if defined? Dir.home # Ruby 1.9+
|
7
|
+
running_on_windows? ? ENV['USERPROFILE'].gsub("\\","/") : ENV['HOME']
|
8
|
+
end
|
9
|
+
|
10
|
+
def running_on_windows?
|
11
|
+
RUBY_PLATFORM =~ /mswin32|mingw32/
|
12
|
+
end
|
13
|
+
|
14
|
+
def running_on_a_mac?
|
15
|
+
RUBY_PLATFORM =~ /-darwin\d/
|
16
|
+
end
|
17
|
+
|
18
|
+
def launchy(message, url)
|
19
|
+
action(message) do
|
20
|
+
require("launchy")
|
21
|
+
launchy = Launchy.open(url)
|
22
|
+
if launchy.respond_to?(:join)
|
23
|
+
launchy.join
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def output_with_bang(message="", new_line=true)
|
29
|
+
return if message.to_s.strip == ""
|
30
|
+
display(format_with_bang(message), new_line)
|
31
|
+
end
|
32
|
+
|
33
|
+
def format_with_bang(message)
|
34
|
+
return '' if message.to_s.strip == ""
|
35
|
+
" ! " + message.split("\n").join("\n ! ")
|
36
|
+
end
|
37
|
+
|
38
|
+
def display(msg="", new_line=true)
|
39
|
+
if new_line
|
40
|
+
puts(msg)
|
41
|
+
else
|
42
|
+
print(msg)
|
43
|
+
end
|
44
|
+
$stdout.flush
|
45
|
+
end
|
46
|
+
|
47
|
+
def debug(*args)
|
48
|
+
$stderr.puts(*args) if debugging?
|
49
|
+
end
|
50
|
+
|
51
|
+
def debugging?
|
52
|
+
ENV['PEBBLES_DEBUG']
|
53
|
+
end
|
54
|
+
|
55
|
+
def ask
|
56
|
+
$stdin.gets.to_s.strip
|
57
|
+
end
|
58
|
+
|
59
|
+
def error(message, report=false)
|
60
|
+
if Pebbles::Helpers.error_with_failure
|
61
|
+
display("failed")
|
62
|
+
Pebbles::Helpers.error_with_failure = false
|
63
|
+
end
|
64
|
+
$stderr.puts(format_with_bang(message))
|
65
|
+
exit(1)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.error_with_failure
|
69
|
+
@@error_with_failure ||= false
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.error_with_failure=(new_error_with_failure)
|
73
|
+
@@error_with_failure = new_error_with_failure
|
74
|
+
end
|
75
|
+
|
76
|
+
def hputs(string='')
|
77
|
+
Kernel.puts(string)
|
78
|
+
end
|
79
|
+
|
80
|
+
def longest(items)
|
81
|
+
items.map { |i| i.to_s.length }.sort.last
|
82
|
+
end
|
83
|
+
|
84
|
+
def has_git?
|
85
|
+
%x{ git --version }
|
86
|
+
$?.success?
|
87
|
+
end
|
88
|
+
|
89
|
+
def git(args)
|
90
|
+
return "" unless has_git?
|
91
|
+
flattened_args = [args].flatten.compact.join(" ")
|
92
|
+
%x{ git #{flattened_args} 2>&1 }.strip
|
93
|
+
end
|
94
|
+
|
95
|
+
def has_git_remote?(remote)
|
96
|
+
git('remote').split("\n").include?(remote) && $?.success?
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_git_remote(remote, url)
|
100
|
+
return if has_git_remote? remote
|
101
|
+
git "remote add #{remote} #{url}"
|
102
|
+
display "Git remote #{remote} added" if $?.success?
|
103
|
+
end
|
104
|
+
|
105
|
+
def update_git_remote(remote, url)
|
106
|
+
return unless has_git_remote? remote
|
107
|
+
git "remote set-url #{remote} #{url}"
|
108
|
+
display "Git remote #{remote} updated" if $?.success?
|
109
|
+
end
|
110
|
+
|
111
|
+
@@kb = 1024
|
112
|
+
@@mb = 1024 * @@kb
|
113
|
+
@@gb = 1024 * @@mb
|
114
|
+
def format_bytes(amount)
|
115
|
+
amount = amount.to_i
|
116
|
+
return '(empty)' if amount == 0
|
117
|
+
return amount if amount < @@kb
|
118
|
+
return "#{(amount / @@kb).round}k" if amount < @@mb
|
119
|
+
return "#{(amount / @@mb).round}M" if amount < @@gb
|
120
|
+
return "#{(amount / @@gb).round}G"
|
121
|
+
end
|
122
|
+
|
123
|
+
def json_decode(json)
|
124
|
+
MultiJson.load(json)
|
125
|
+
rescue MultiJson::ParseError
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
|
129
|
+
def with_tty(&block)
|
130
|
+
return unless $stdin.isatty
|
131
|
+
begin
|
132
|
+
yield
|
133
|
+
rescue
|
134
|
+
# fails on windows
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def action(message, options={})
|
139
|
+
message = "#{message} in organization #{org}" if options[:org]
|
140
|
+
display("#{message}... ", false)
|
141
|
+
Pebbles::Helpers.error_with_failure = true
|
142
|
+
ret = yield
|
143
|
+
Pebbles::Helpers.error_with_failure = false
|
144
|
+
display((options[:success] || "done"), false)
|
145
|
+
if @status
|
146
|
+
display(", #{@status}", false)
|
147
|
+
@status = nil
|
148
|
+
end
|
149
|
+
display
|
150
|
+
ret
|
151
|
+
end
|
152
|
+
|
153
|
+
def confirm_command(app_to_confirm = app, message=nil)
|
154
|
+
if confirmed_app = Pebbles::Command.current_options[:confirm]
|
155
|
+
unless confirmed_app == app_to_confirm
|
156
|
+
raise(Pebbles::Command::CommandFailed, "Confirmed app #{confirmed_app} did not match the selected app #{app_to_confirm}.")
|
157
|
+
end
|
158
|
+
return true
|
159
|
+
else
|
160
|
+
display
|
161
|
+
message ||= "WARNING: Destructive Action\nThis command will affect the app: #{app_to_confirm}"
|
162
|
+
message << "\nTo proceed, type \"#{app_to_confirm}\" or re-run this command with --confirm #{app_to_confirm}"
|
163
|
+
output_with_bang(message)
|
164
|
+
display
|
165
|
+
display "> ", false
|
166
|
+
if ask.downcase != app_to_confirm
|
167
|
+
error("Confirmation did not match #{app_to_confirm}. Aborted.")
|
168
|
+
else
|
169
|
+
true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def format_error(error, message='Pebblescape client internal error.')
|
175
|
+
formatted_error = []
|
176
|
+
formatted_error << " ! #{message}"
|
177
|
+
formatted_error << ''
|
178
|
+
formatted_error << " Error: #{error.message} (#{error.class})"
|
179
|
+
command = ARGV.map do |arg|
|
180
|
+
if arg.include?(' ')
|
181
|
+
arg = %{"#{arg}"}
|
182
|
+
else
|
183
|
+
arg
|
184
|
+
end
|
185
|
+
end.join(' ')
|
186
|
+
formatted_error << " Command: pebbles #{command}"
|
187
|
+
require 'pebbles/auth'
|
188
|
+
unless Pebbles::Auth.host == Pebbles::Auth.default_host
|
189
|
+
formatted_error << " Host: #{Pebbles::Auth.host}"
|
190
|
+
end
|
191
|
+
if http_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
|
192
|
+
formatted_error << " HTTP Proxy: #{http_proxy}"
|
193
|
+
end
|
194
|
+
if https_proxy = ENV['https_proxy'] || ENV['HTTPS_PROXY']
|
195
|
+
formatted_error << " HTTPS Proxy: #{https_proxy}"
|
196
|
+
end
|
197
|
+
formatted_error << " Version: #{Pebbles.user_agent}"
|
198
|
+
formatted_error << "\n"
|
199
|
+
formatted_error.join("\n")
|
200
|
+
end
|
201
|
+
|
202
|
+
def styled_header(header)
|
203
|
+
display("=== #{header}")
|
204
|
+
end
|
205
|
+
|
206
|
+
# produces a printf formatter line for an array of items
|
207
|
+
# if an individual line item is an array, it will create columns
|
208
|
+
# that are lined-up
|
209
|
+
#
|
210
|
+
# line_formatter(["foo", "barbaz"]) # => "%-6s"
|
211
|
+
# line_formatter(["foo", "barbaz"], ["bar", "qux"]) # => "%-3s %-6s"
|
212
|
+
#
|
213
|
+
def line_formatter(array)
|
214
|
+
if array.any? {|item| item.is_a?(Array)}
|
215
|
+
cols = []
|
216
|
+
array.each do |item|
|
217
|
+
if item.is_a?(Array)
|
218
|
+
item.each_with_index { |val,idx| cols[idx] = [cols[idx]||0, (val || '').length].max }
|
219
|
+
end
|
220
|
+
end
|
221
|
+
cols.map { |col| "%-#{col}s" }.join(" ")
|
222
|
+
else
|
223
|
+
"%s"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def styled_array(array, options={})
|
228
|
+
fmt = line_formatter(array)
|
229
|
+
array = array.sort unless options[:sort] == false
|
230
|
+
array.each do |element|
|
231
|
+
display((fmt % element).rstrip)
|
232
|
+
end
|
233
|
+
display
|
234
|
+
end
|
235
|
+
|
236
|
+
def styled_hash(hash, keys=nil)
|
237
|
+
max_key_length = hash.keys.map {|key| key.to_s.length}.max + 2
|
238
|
+
keys ||= hash.keys.sort {|x,y| x.to_s <=> y.to_s}
|
239
|
+
keys.each do |key|
|
240
|
+
case value = hash[key]
|
241
|
+
when Array
|
242
|
+
if value.empty?
|
243
|
+
next
|
244
|
+
else
|
245
|
+
elements = value.sort {|x,y| x.to_s <=> y.to_s}
|
246
|
+
display("#{key}: ".ljust(max_key_length), false)
|
247
|
+
display(elements[0])
|
248
|
+
elements[1..-1].each do |element|
|
249
|
+
display("#{' ' * max_key_length}#{element}")
|
250
|
+
end
|
251
|
+
if elements.length > 1
|
252
|
+
display
|
253
|
+
end
|
254
|
+
end
|
255
|
+
when nil
|
256
|
+
next
|
257
|
+
else
|
258
|
+
display("#{key}: ".ljust(max_key_length), false)
|
259
|
+
display(value)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def flatten_hash(hash, key)
|
265
|
+
hash[key].each do |k, v|
|
266
|
+
hash["#{key}_#{k}"] = v
|
267
|
+
end
|
268
|
+
|
269
|
+
hash.delete(key)
|
270
|
+
end
|
271
|
+
|
272
|
+
def styled_error(error, message='Pebblescape client internal error.')
|
273
|
+
if Pebbles::Helpers.error_with_failure
|
274
|
+
display("failed")
|
275
|
+
Pebbles::Helpers.error_with_failure = false
|
276
|
+
end
|
277
|
+
$stderr.puts(format_error(error, message))
|
278
|
+
end
|
279
|
+
|
280
|
+
def has_http_git_entry_in_netrc
|
281
|
+
Auth.netrc && Auth.netrc[Auth.http_git_host]
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|