hotdog 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/hotdog/application.rb +40 -38
- data/lib/hotdog/commands/help.rb +23 -2
- data/lib/hotdog/commands/pssh.rb +10 -115
- data/lib/hotdog/commands/search.rb +1 -5
- data/lib/hotdog/commands/ssh.rb +144 -55
- data/lib/hotdog/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1b8937e6ed17779e09622476dae786c9f7e04a4
|
4
|
+
data.tar.gz: 59d5cf9204f2b3cf7a272734ca62253f09b01347
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c763c88d2083cb10007195b7962edd980a47f0145a613081b1a5c9713da91ffed3350daa7bdd1317f7e9d00b928141f69a5005e6fa2026deb127a6e6a1b71173
|
7
|
+
data.tar.gz: 796c96d4b2c954690fc9bbf97339d8ebe4213fc7c3c92fafc5c5a67b90914c208ad4eea7c3a2b21f5c871dc4562c002ff561b443321d949c24038b3491743caf
|
data/lib/hotdog/application.rb
CHANGED
@@ -53,44 +53,47 @@ module Hotdog
|
|
53
53
|
args = @optparse.order(argv)
|
54
54
|
|
55
55
|
begin
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
unless options[:application_key]
|
66
|
-
raise("DATADOG_APPLICATION_KEY is not set")
|
67
|
-
end
|
68
|
-
|
69
|
-
if options[:format] == "ltsv"
|
70
|
-
options[:headers] = true
|
71
|
-
end
|
72
|
-
|
73
|
-
options[:formatter] = get_formatter(options[:format])
|
74
|
-
|
75
|
-
if options[:debug] or options[:verbose]
|
76
|
-
options[:logger].level = Logger::DEBUG
|
77
|
-
else
|
78
|
-
options[:logger].level = Logger::INFO
|
79
|
-
end
|
80
|
-
|
81
|
-
cmd.run(args, @options)
|
56
|
+
command_name = ( args.shift || "help" )
|
57
|
+
begin
|
58
|
+
command = get_command(command_name)
|
59
|
+
rescue NameError
|
60
|
+
STDERR.puts("hotdog: '#{command_name}' is not a hotdog command.")
|
61
|
+
get_command("help").parse_options(@optparse, ["commands"])
|
62
|
+
exit(1)
|
82
63
|
end
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
64
|
+
|
65
|
+
@optparse.banner = "Usage: hotdog #{command_name} [options]"
|
66
|
+
command.define_options(@optparse, @options)
|
67
|
+
|
68
|
+
begin
|
69
|
+
args = command.parse_options(@optparse, args)
|
70
|
+
rescue OptionParser::ParseError => error
|
71
|
+
STDERR.puts("hotdog: #{error.message}")
|
72
|
+
command.parse_options(@optparse, ["--help"])
|
73
|
+
exit(1)
|
74
|
+
end
|
75
|
+
|
76
|
+
unless options[:api_key]
|
77
|
+
raise("DATADOG_API_KEY is not set")
|
92
78
|
end
|
93
|
-
|
79
|
+
|
80
|
+
unless options[:application_key]
|
81
|
+
raise("DATADOG_APPLICATION_KEY is not set")
|
82
|
+
end
|
83
|
+
|
84
|
+
if options[:format] == "ltsv"
|
85
|
+
options[:headers] = true
|
86
|
+
end
|
87
|
+
|
88
|
+
options[:formatter] = get_formatter(options[:format])
|
89
|
+
|
90
|
+
if options[:debug] or options[:verbose]
|
91
|
+
options[:logger].level = Logger::DEBUG
|
92
|
+
else
|
93
|
+
options[:logger].level = Logger::INFO
|
94
|
+
end
|
95
|
+
|
96
|
+
command.run(args, @options)
|
94
97
|
rescue Errno::EPIPE
|
95
98
|
# nop
|
96
99
|
end
|
@@ -174,8 +177,7 @@ module Hotdog
|
|
174
177
|
load library
|
175
178
|
klass = Hotdog::Commands.const_get(const_name(File.basename(library, ".rb")))
|
176
179
|
else
|
177
|
-
|
178
|
-
klass = Hotdog::Commands::Help
|
180
|
+
raise(NameError.new("unknown command: #{name}"))
|
179
181
|
end
|
180
182
|
end
|
181
183
|
klass.new(self)
|
data/lib/hotdog/commands/help.rb
CHANGED
@@ -6,8 +6,29 @@ module Hotdog
|
|
6
6
|
module Commands
|
7
7
|
class Help < BaseCommand
|
8
8
|
def run(args=[], options={})
|
9
|
-
|
10
|
-
|
9
|
+
commands = command_files.map { |file| File.basename(file, ".rb") }.sort.uniq
|
10
|
+
if "commands" == args.first
|
11
|
+
STDOUT.puts("hotdog commands are:")
|
12
|
+
commands.each do |command|
|
13
|
+
STDOUT.puts("- #{command}")
|
14
|
+
end
|
15
|
+
else
|
16
|
+
ruby = File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["ruby_install_name"])
|
17
|
+
if commands.include?(args.first)
|
18
|
+
exit(system(ruby, $0, args.first, "--help") ? 0 : 1)
|
19
|
+
else
|
20
|
+
exit(system(ruby, $0, "--help") ? 0 : 1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def load_path()
|
27
|
+
$LOAD_PATH.map { |path| File.join(path, "hotdog/commands") }.select { |path| File.directory?(path) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def command_files()
|
31
|
+
load_path.flat_map { |path| Dir.glob(File.join(path, "*.rb")) }.select { |file| File.file?(file) }
|
11
32
|
end
|
12
33
|
end
|
13
34
|
end
|
data/lib/hotdog/commands/pssh.rb
CHANGED
@@ -1,130 +1,25 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "json"
|
4
|
+
require "parallel"
|
4
5
|
require "parslet"
|
5
|
-
require "hotdog/commands/search"
|
6
6
|
require "shellwords"
|
7
|
-
require "
|
7
|
+
require "hotdog/commands/ssh"
|
8
8
|
|
9
9
|
module Hotdog
|
10
10
|
module Commands
|
11
|
-
class Pssh <
|
12
|
-
def define_options(optparse, options={})
|
13
|
-
options[:options] = []
|
14
|
-
options[:user] = nil
|
15
|
-
options[:port] = nil
|
16
|
-
options[:identity_file] = nil
|
17
|
-
options[:forward_agent] = false
|
18
|
-
options[:max_parallelism] = nil
|
19
|
-
options[:color] = :auto
|
20
|
-
|
21
|
-
optparse.on("-o SSH_OPTION", "Passes this string to ssh command through shell. This option may be given multiple times") do |option|
|
22
|
-
options[:options] += [option]
|
23
|
-
end
|
24
|
-
optparse.on("-i SSH_IDENTITY_FILE", "SSH identity file path") do |path|
|
25
|
-
options[:identity_file] = path
|
26
|
-
end
|
27
|
-
optparse.on("-A", "Enable agent forwarding", TrueClass) do |b|
|
28
|
-
options[:forward_agent] = b
|
29
|
-
end
|
30
|
-
optparse.on("-p PORT", "Port of the remote host", Integer) do |port|
|
31
|
-
options[:port] = port
|
32
|
-
end
|
33
|
-
optparse.on("-u SSH_USER", "SSH login user name") do |user|
|
34
|
-
options[:user] = user
|
35
|
-
end
|
36
|
-
optparse.on("-P PARALLELISM", "Max parallelism", Integer) do |n|
|
37
|
-
options[:max_parallelism] = n
|
38
|
-
end
|
39
|
-
optparse.on("-v", "--verbose", "Enable verbose ode") do |v|
|
40
|
-
options[:verbose] = v
|
41
|
-
end
|
42
|
-
optparse.on("--color=WHEN", "--colour=WHEN", "Enable colors") do |color|
|
43
|
-
options[:color] = color
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def run(args=[], options={})
|
48
|
-
expression = args.join(" ").strip
|
49
|
-
if expression.empty?
|
50
|
-
# return everything if given expression is empty
|
51
|
-
expression = "*"
|
52
|
-
end
|
53
|
-
|
54
|
-
begin
|
55
|
-
node = parse(expression)
|
56
|
-
rescue Parslet::ParseFailed => error
|
57
|
-
STDERR.puts("syntax error: " + error.cause.ascii_tree)
|
58
|
-
exit(1)
|
59
|
-
end
|
60
|
-
|
61
|
-
result0 = evaluate(node, self)
|
62
|
-
if 0 < result0.length
|
63
|
-
exec_command(result0, options)
|
64
|
-
else
|
65
|
-
STDERR.puts("no match found: #{expression}")
|
66
|
-
exit(1)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
11
|
+
class Pssh < SshAlike
|
70
12
|
private
|
71
|
-
def
|
72
|
-
result, fields = get_hosts(result0)
|
73
|
-
hosts = result.flatten
|
74
|
-
threads = options[:max_parallelism] || hosts.size
|
13
|
+
def run_main(hosts, options={})
|
75
14
|
use_color_p = use_color?
|
76
|
-
stats = Parallel.map(hosts
|
77
|
-
cmdline = build_command_string(host, options)
|
78
|
-
|
79
|
-
IO.popen(cmdline, in: :close, err: [:child, :out]) do |io|
|
80
|
-
io.each_with_index do |s, i|
|
81
|
-
STDOUT.write("\e[0;36m") if use_color_p
|
82
|
-
STDOUT.write("#{name}:#{i}:")
|
83
|
-
STDOUT.write("\e[0m") if use_color_p
|
84
|
-
STDOUT.write(s)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
$?.success? # $? is thread-local variable
|
15
|
+
stats = Parallel.map(hosts, in_threads: parallelism(hosts)) { |host|
|
16
|
+
cmdline = build_command_string(host, @remote_command, options)
|
17
|
+
exec_command(host, cmdline, true, use_color_p)
|
88
18
|
}
|
89
|
-
|
90
|
-
exit(
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def build_command_string(host, options={})
|
95
|
-
# build ssh command
|
96
|
-
base_cmdline = ["ssh"]
|
97
|
-
if options[:forward_agent]
|
98
|
-
base_cmdline << "-A"
|
99
|
-
end
|
100
|
-
if options[:identity_file]
|
101
|
-
base_cmdline << "-i" << options[:identity_file]
|
102
|
-
end
|
103
|
-
if options[:user]
|
104
|
-
base_cmdline << "-l" << options[:user]
|
105
|
-
end
|
106
|
-
base_cmdline << "-o" << "BatchMode=yes"
|
107
|
-
if options[:options]
|
108
|
-
base_cmdline += options[:options].flat_map { |option| ["-o", option] }
|
109
|
-
end
|
110
|
-
if options[:port]
|
111
|
-
base_cmdline << "-p" << options[:port].to_s
|
112
|
-
end
|
113
|
-
cmdline = base_cmdline + [host]
|
114
|
-
if @remote_command
|
115
|
-
cmdline << "--" << @remote_command
|
116
|
-
end
|
117
|
-
Shellwords.join(cmdline)
|
118
|
-
end
|
119
|
-
|
120
|
-
def use_color?
|
121
|
-
case options[:color]
|
122
|
-
when :always
|
123
|
-
true
|
124
|
-
when :never
|
125
|
-
false
|
19
|
+
if stats.all?
|
20
|
+
exit(0)
|
126
21
|
else
|
127
|
-
|
22
|
+
exit(1)
|
128
23
|
end
|
129
24
|
end
|
130
25
|
end
|
@@ -742,11 +742,7 @@ module Hotdog
|
|
742
742
|
def evaluate(environment, options={})
|
743
743
|
values = environment.execute(@query, @values).map { |row| row.first }
|
744
744
|
if values.empty? and @fallback
|
745
|
-
@fallback.evaluate(environment, options)
|
746
|
-
if values.empty?
|
747
|
-
environment.logger.info("no result: #{self.dump.inspect}")
|
748
|
-
end
|
749
|
-
end
|
745
|
+
@fallback.evaluate(environment, options)
|
750
746
|
else
|
751
747
|
values
|
752
748
|
end
|
data/lib/hotdog/commands/ssh.rb
CHANGED
@@ -2,29 +2,21 @@
|
|
2
2
|
|
3
3
|
require "json"
|
4
4
|
require "parslet"
|
5
|
-
require "hotdog/commands/search"
|
6
5
|
require "shellwords"
|
6
|
+
require "hotdog/commands/search"
|
7
7
|
|
8
8
|
module Hotdog
|
9
9
|
module Commands
|
10
|
-
class
|
10
|
+
class SshAlike < Search
|
11
11
|
def define_options(optparse, options={})
|
12
|
-
options[:index] = nil
|
13
12
|
options[:options] = []
|
14
13
|
options[:user] = nil
|
15
14
|
options[:port] = nil
|
16
15
|
options[:identity_file] = nil
|
17
16
|
options[:forward_agent] = false
|
17
|
+
options[:color] = :auto
|
18
|
+
options[:max_parallelism] = nil
|
18
19
|
|
19
|
-
optparse.on("-D BIND_ADDRESS", "Specifies a local \"dynamic\" application-level port forwarding") do |bind_address|
|
20
|
-
options[:dynamic_port_forward] = bind_address
|
21
|
-
end
|
22
|
-
optparse.on("-L BIND_ADDRESS", "Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side") do |bind_address|
|
23
|
-
options[:port_forward] = bind_address
|
24
|
-
end
|
25
|
-
optparse.on("-n", "--index INDEX", "Use this index of host if multiple servers are found", Integer) do |index|
|
26
|
-
options[:index] = index
|
27
|
-
end
|
28
20
|
optparse.on("-o SSH_OPTION", "Passes this string to ssh command through shell. This option may be given multiple times") do |option|
|
29
21
|
options[:options] += [option]
|
30
22
|
end
|
@@ -43,6 +35,15 @@ module Hotdog
|
|
43
35
|
optparse.on("-v", "--verbose", "Enable verbose ode") do |v|
|
44
36
|
options[:verbose] = v
|
45
37
|
end
|
38
|
+
optparse.on("--filter=COMMAND", "Command to filter search result.") do |command|
|
39
|
+
options[:filter_command] = command
|
40
|
+
end
|
41
|
+
optparse.on("-P PARALLELISM", "Max parallelism", Integer) do |n|
|
42
|
+
options[:max_parallelism] = n
|
43
|
+
end
|
44
|
+
optparse.on("--color=WHEN", "--colour=WHEN", "Enable colors") do |color|
|
45
|
+
options[:color] = color
|
46
|
+
end
|
46
47
|
end
|
47
48
|
|
48
49
|
def run(args=[], options={})
|
@@ -60,73 +61,161 @@ module Hotdog
|
|
60
61
|
end
|
61
62
|
|
62
63
|
result0 = evaluate(node, self)
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
result, fields = get_hosts_with_search_tags(result0, node)
|
65
|
+
hosts = filter_hosts(result.flatten)
|
66
|
+
validate_hosts!(hosts)
|
67
|
+
run_main(hosts, options)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def parallelism(hosts)
|
72
|
+
options[:max_parallelism] || hosts.size
|
73
|
+
end
|
74
|
+
|
75
|
+
def filter_hosts(hosts)
|
76
|
+
if options[:filter_command]
|
77
|
+
use_color_p = use_color?
|
78
|
+
filtered_hosts = Parallel.map(hosts, in_threads: parallelism(hosts)) { |host|
|
79
|
+
cmdline = build_command_string(host, options[:filter_command], options)
|
80
|
+
[host, exec_command(host, cmdline, false, use_color_p)]
|
81
|
+
}.select { |host, stat|
|
82
|
+
stat
|
83
|
+
}.map { |host, stat|
|
84
|
+
host
|
85
|
+
}
|
86
|
+
if hosts == filtered_hosts
|
87
|
+
hosts
|
66
88
|
else
|
67
|
-
|
68
|
-
|
69
|
-
else
|
70
|
-
result, fields = get_hosts_with_search_tags(result0, node)
|
71
|
-
|
72
|
-
# add "index" field
|
73
|
-
result = result.each_with_index.map { |host, i| [i] + host }
|
74
|
-
fields = ["index"] + fields
|
75
|
-
|
76
|
-
STDERR.print(format(result, fields: fields))
|
77
|
-
logger.info("found %d host(s)." % result.length)
|
78
|
-
exit(1)
|
79
|
-
end
|
89
|
+
logger.info("filtered host(s): #{(hosts - filtered_hosts).inspect}")
|
90
|
+
filtered_hosts
|
80
91
|
end
|
81
92
|
else
|
82
|
-
|
93
|
+
hosts
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def validate_hosts!(hosts)
|
98
|
+
if hosts.length < 1
|
99
|
+
STDERR.puts("no match found")
|
83
100
|
exit(1)
|
84
101
|
end
|
85
|
-
exit(127)
|
86
102
|
end
|
87
103
|
|
88
|
-
|
89
|
-
|
90
|
-
result, fields = get_hosts(result0)
|
91
|
-
hosts = result.flatten
|
92
|
-
cmdline = build_command_string(hosts.first, options)
|
93
|
-
logger.debug("execute: #{cmdline}")
|
94
|
-
exec(cmdline)
|
104
|
+
def run_main(hosts, options={})
|
105
|
+
raise(NotImplementedError)
|
95
106
|
end
|
96
107
|
|
97
|
-
def
|
98
|
-
|
99
|
-
base_cmdline = ["ssh"]
|
108
|
+
def build_command_options(options={})
|
109
|
+
cmdline = []
|
100
110
|
if options[:forward_agent]
|
101
|
-
|
102
|
-
end
|
103
|
-
if options[:dynamic_port_forward]
|
104
|
-
base_cmdline << "-D" << options[:dynamic_port_forward]
|
105
|
-
end
|
106
|
-
if options[:port_forward]
|
107
|
-
base_cmdline << "-L" << options[:port_forward]
|
111
|
+
cmdline << "-A"
|
108
112
|
end
|
109
113
|
if options[:identity_file]
|
110
|
-
|
114
|
+
cmdline << "-i" << options[:identity_file]
|
111
115
|
end
|
112
116
|
if options[:user]
|
113
|
-
|
117
|
+
cmdline << "-l" << options[:user]
|
114
118
|
end
|
115
119
|
if options[:options]
|
116
|
-
|
120
|
+
cmdline += options[:options].flat_map { |option| ["-o", option] }
|
117
121
|
end
|
118
122
|
if options[:port]
|
119
|
-
|
123
|
+
cmdline << "-p" << options[:port].to_s
|
120
124
|
end
|
121
125
|
if options[:verbose]
|
122
|
-
|
126
|
+
cmdline << "-v"
|
123
127
|
end
|
124
|
-
cmdline
|
125
|
-
|
126
|
-
|
128
|
+
cmdline
|
129
|
+
end
|
130
|
+
|
131
|
+
def build_command_string(host, command=nil, options={})
|
132
|
+
# build ssh command
|
133
|
+
cmdline = ["ssh"] + build_command_options(options) + [host]
|
134
|
+
if command
|
135
|
+
cmdline << "--" << command
|
127
136
|
end
|
128
137
|
Shellwords.join(cmdline)
|
129
138
|
end
|
139
|
+
|
140
|
+
def use_color?
|
141
|
+
case options[:color]
|
142
|
+
when :always
|
143
|
+
true
|
144
|
+
when :never
|
145
|
+
false
|
146
|
+
else
|
147
|
+
STDOUT.tty?
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def exec_command(identifier, cmdline, output=true, colorize=false)
|
152
|
+
logger.debug("execute: #{cmdline}")
|
153
|
+
IO.popen(cmdline, in: :close, err: [:child, :out]) do |io|
|
154
|
+
io.each_with_index do |s, i|
|
155
|
+
if output
|
156
|
+
STDOUT.write("\e[0;36m") if colorize
|
157
|
+
STDOUT.write("#{identifier}:#{i}:")
|
158
|
+
STDOUT.write("\e[0m") if colorize
|
159
|
+
STDOUT.write(s)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
$?.success? # $? is thread-local variable
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
class Ssh < SshAlike
|
168
|
+
def define_options(optparse, options={})
|
169
|
+
super
|
170
|
+
options[:index] = nil
|
171
|
+
optparse.on("-D BIND_ADDRESS", "Specifies a local \"dynamic\" application-level port forwarding") do |bind_address|
|
172
|
+
options[:dynamic_port_forward] = bind_address
|
173
|
+
end
|
174
|
+
optparse.on("-L BIND_ADDRESS", "Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side") do |bind_address|
|
175
|
+
options[:port_forward] = bind_address
|
176
|
+
end
|
177
|
+
optparse.on("-n", "--index INDEX", "Use this index of host if multiple servers are found", Integer) do |index|
|
178
|
+
options[:index] = index
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
def filter_hosts(hosts)
|
184
|
+
hosts = super
|
185
|
+
if options[:index] and options[:index] < hosts.length
|
186
|
+
[hosts[options[:index]]]
|
187
|
+
else
|
188
|
+
hosts
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def validate_hosts!(hosts)
|
193
|
+
super
|
194
|
+
if hosts.length != 1
|
195
|
+
result = hosts.each_with_index.map { |host, i| [i, host] }
|
196
|
+
STDERR.print(format(result, fields: ["index", "host"]))
|
197
|
+
logger.error("found %d hosts." % result.length)
|
198
|
+
exit(1)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def run_main(hosts, options={})
|
203
|
+
cmdline = build_command_string(hosts.first, @remote_command, options)
|
204
|
+
logger.debug("execute: #{cmdline}")
|
205
|
+
exec(cmdline)
|
206
|
+
exit(127)
|
207
|
+
end
|
208
|
+
|
209
|
+
def build_command_options(options={})
|
210
|
+
arguments = super
|
211
|
+
if options[:dynamic_port_forward]
|
212
|
+
arguments << "-D" << options[:dynamic_port_forward]
|
213
|
+
end
|
214
|
+
if options[:port_forward]
|
215
|
+
arguments << "-L" << options[:port_forward]
|
216
|
+
end
|
217
|
+
arguments
|
218
|
+
end
|
130
219
|
end
|
131
220
|
end
|
132
221
|
end
|
data/lib/hotdog/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hotdog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yamashita Yuu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|