instrumental_tools 0.1.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.
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/README.md +27 -0
- data/Rakefile +1 -0
- data/bin/gitstrumental +50 -0
- data/bin/instrument_server +267 -0
- data/bin/instrumental +54 -0
- data/instrumental_tools.gemspec +20 -0
- data/lib/instrumental_tools/version.rb +5 -0
- metadata +99 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Instrumental Tools
|
2
|
+
|
3
|
+
Collection of tools for use with the Instrumental (http://www.instrumentalapp.com/) app
|
4
|
+
|
5
|
+
## instrument_server
|
6
|
+
|
7
|
+
Use to collect various monitoring statistics of a server. Execute with:
|
8
|
+
|
9
|
+
```sh
|
10
|
+
instrument_server [INSTRUMENTAL_API_KEY]
|
11
|
+
```
|
12
|
+
|
13
|
+
Linux note: Install iostat (part of the sysstat package) in order to collect disk I/O metrics.
|
14
|
+
|
15
|
+
## instrumental
|
16
|
+
|
17
|
+
Output text graphs of the different metrics in your project.
|
18
|
+
|
19
|
+
See all options with: `instrumental --help`
|
20
|
+
|
21
|
+
## gitstrumental
|
22
|
+
|
23
|
+
Collect statistics on commit counts in a given git repo. Execute in the repo directory with:
|
24
|
+
|
25
|
+
```sh
|
26
|
+
gitstrumental [INSTRUMENTAL_API_KEY]
|
27
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/bin/gitstrumental
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'instrumental_agent'
|
4
|
+
require 'instrumental_agent'
|
5
|
+
|
6
|
+
token = ENV["INSTRUMENTAL_TOKEN"] || ARGV[0]
|
7
|
+
|
8
|
+
if !token
|
9
|
+
puts 'Usage: gitstrumental API_TOKEN'
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
|
13
|
+
lines = `git log --format="REC %at %h '%cn'" --numstat`.chomp.split(/\n+/).reject(&:empty?)
|
14
|
+
branch = File.basename(`git symbolic-ref HEAD`.chomp).gsub(/[^a-z0-9]/i, "_")
|
15
|
+
if fetch_url = `git remote show origin`.chomp.split(/\n+/).grep(/Fetch URL:/).first
|
16
|
+
repo = File.basename(fetch_url.split("/").last, ".git")
|
17
|
+
else
|
18
|
+
repo = File.basename(Dir.pwd, ".git")
|
19
|
+
end
|
20
|
+
prolog = [repo, branch].join(".")
|
21
|
+
I = Instrumental::Agent.new(token)
|
22
|
+
curstat = {}
|
23
|
+
lines.each do |line|
|
24
|
+
if line =~ /^REC/
|
25
|
+
if !curstat.empty?
|
26
|
+
name, ts, changes = curstat[:name], curstat[:timestamp], curstat[:changes]
|
27
|
+
I.increment("git.#{prolog}.commits.#{name}", 1, ts)
|
28
|
+
|
29
|
+
adds, deletes = changes.reduce do |prev, pair|
|
30
|
+
[prev[0] + pair[0], prev[1] + pair[1]]
|
31
|
+
end
|
32
|
+
I.increment("git.#{prolog}.commits.#{name}.add", adds, ts)
|
33
|
+
I.increment("git.#{prolog}.commits.#{name}.delete", deletes, ts)
|
34
|
+
I.increment("git.#{prolog}.num_commits", 1, ts)
|
35
|
+
end
|
36
|
+
rec, ts, hash, *name_parts = line.split(" ")
|
37
|
+
name = name_parts.join(" ").gsub(/^'([^']+)'$/, "\\1").gsub(/[^a-z0-9]/i, "_")
|
38
|
+
curstat = {
|
39
|
+
:name => name,
|
40
|
+
:timestamp => ts.to_i,
|
41
|
+
:hash => hash,
|
42
|
+
:changes => []
|
43
|
+
}
|
44
|
+
else
|
45
|
+
adds, deletes, file = line.split(/\s+/)
|
46
|
+
curstat[:changes] << [
|
47
|
+
adds.to_i, deletes.to_i
|
48
|
+
]
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
gem 'instrumental_agent'
|
5
|
+
require 'instrumental_agent'
|
6
|
+
|
7
|
+
|
8
|
+
class SystemInspector
|
9
|
+
TYPES = [:gauges, :incrementors]
|
10
|
+
attr_accessor *TYPES
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@gauges = {}
|
14
|
+
@incrementors = {}
|
15
|
+
@platform =
|
16
|
+
case RUBY_PLATFORM
|
17
|
+
when /linux/
|
18
|
+
Linux
|
19
|
+
when /darwin/
|
20
|
+
OSX
|
21
|
+
else
|
22
|
+
raise "unsupported OS"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_all
|
27
|
+
load @platform.load_cpu
|
28
|
+
load @platform.load_memory
|
29
|
+
load @platform.load_disks
|
30
|
+
load @platform.load_filesystem
|
31
|
+
end
|
32
|
+
|
33
|
+
def load(stats)
|
34
|
+
@gauges.merge!(stats[:gauges] || {})
|
35
|
+
end
|
36
|
+
|
37
|
+
module OSX
|
38
|
+
def self.load_cpu
|
39
|
+
{ :gauges => top }
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.top
|
43
|
+
lines = []
|
44
|
+
processes = date = load = cpu = nil
|
45
|
+
IO.popen('top -l 1 -n 0') do |top|
|
46
|
+
processes = top.gets.split(': ')[1]
|
47
|
+
date = top.gets
|
48
|
+
load = top.gets.split(': ')[1]
|
49
|
+
cpu = top.gets.split(': ')[1]
|
50
|
+
end
|
51
|
+
|
52
|
+
user, system, idle = cpu.split(", ").map { |v| v.to_f }
|
53
|
+
load1, load5, load15 = load.split(", ").map { |v| v.to_f }
|
54
|
+
total, running, stuck, sleeping, threads = processes.split(", ").map { |v| v.to_i }
|
55
|
+
|
56
|
+
{
|
57
|
+
'cpu.user' => user,
|
58
|
+
'cpu.system' => system,
|
59
|
+
'cpu.idle' => idle,
|
60
|
+
'load.1min' => load1,
|
61
|
+
'load.5min' => load5,
|
62
|
+
'load.15min' => load15,
|
63
|
+
'processes.total' => total,
|
64
|
+
'processes.running' => running,
|
65
|
+
'processes.stuck' => stuck,
|
66
|
+
'processes.sleeping' => sleeping,
|
67
|
+
'threads' => threads,
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.load_memory
|
72
|
+
# TODO: swap
|
73
|
+
{ :gauges => vm_stat }
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.vm_stat
|
77
|
+
header, *rows = `vm_stat`.split("\n")
|
78
|
+
page_size = header.match(/page size of (\d+) bytes/)[1].to_i
|
79
|
+
sections = ["free", "active", "inactive", "wired", "speculative", "wired down"]
|
80
|
+
output = {}
|
81
|
+
total = 0.0
|
82
|
+
rows.each do |row|
|
83
|
+
if match = row.match(/Pages (.*):\s+(\d+)\./)
|
84
|
+
section, value = match[1, 2]
|
85
|
+
if sections.include?(section)
|
86
|
+
value = value.to_f * page_size / 1024 / 1024
|
87
|
+
output["memory.#{section.gsub(' ', '_')}_mb"] = value
|
88
|
+
total += value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
output["memory.free_percent"] = output["memory.free_mb"] / total * 100 # TODO: verify
|
93
|
+
output
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.load_disks
|
97
|
+
{ :gauges => df }
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.df
|
101
|
+
output = {}
|
102
|
+
`df -k`.split("\n").grep(%r{^/dev/}).each do |line|
|
103
|
+
device, total, used, available, capacity, mount = line.split(/\s+/)
|
104
|
+
names = [File.basename(device)]
|
105
|
+
names << 'root' if mount == '/'
|
106
|
+
names.each do |name|
|
107
|
+
output["disk.#{name}.total_mb"] = total.to_f / 1024
|
108
|
+
output["disk.#{name}.used_mb"] = used.to_f / 1024
|
109
|
+
output["disk.#{name}.available_mb"] = available.to_f / 1024
|
110
|
+
output["disk.#{name}.available_percent"] = available.to_f / total.to_f * 100
|
111
|
+
end
|
112
|
+
end
|
113
|
+
output
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.netstat(interface = 'en1')
|
117
|
+
# mostly functional network io stats
|
118
|
+
headers, *lines = `netstat -ibI #{interface}`.split("\n").map { |l| l.split(/\s+/) } # FIXME: vulnerability?
|
119
|
+
headers = headers.map { |h| h.downcase }
|
120
|
+
lines.each do |line|
|
121
|
+
if !line[3].include?(':')
|
122
|
+
return Hash[headers.zip(line)]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.load_filesystem
|
128
|
+
{}
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
module Linux
|
134
|
+
def self.load_cpu
|
135
|
+
output = { :gauges => {} }
|
136
|
+
output[:gauges].merge!(cpu)
|
137
|
+
output[:gauges].merge!(loadavg)
|
138
|
+
output
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.cpu
|
142
|
+
cpu, user, nice, system, idle, iowait = `cat /proc/stat | grep cpu[^0-9]`.chomp.split(/\s+/)
|
143
|
+
total = user.to_i + system.to_i + idle.to_i + iowait.to_i
|
144
|
+
{
|
145
|
+
'cpu.user' => (user.to_f / total) * 100,
|
146
|
+
'cpu.system' => (system.to_f / total) * 100,
|
147
|
+
'cpu.idle' => (idle.to_f / total) * 100,
|
148
|
+
'cpu.iowait' => (iowait.to_f / total) * 100,
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.loadavg
|
153
|
+
min_1, min_5, min_15 = `cat /proc/loadavg`.split(/\s+/)
|
154
|
+
{
|
155
|
+
'load.1min' => min_1.to_f,
|
156
|
+
'load.5min' => min_5.to_f,
|
157
|
+
'load.15min' => min_15.to_f,
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.load_memory
|
162
|
+
output = { :gauges => {} }
|
163
|
+
output[:gauges].merge!(memory)
|
164
|
+
output[:gauges].merge!(swap)
|
165
|
+
output
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.memory
|
169
|
+
_, total, used, free, shared, buffers, cached = `free -k -o | grep Mem`.chomp.split(/\s+/)
|
170
|
+
{
|
171
|
+
'memory.used_mb' => used.to_f / 1024,
|
172
|
+
'memory.free_mb' => free.to_f / 1024,
|
173
|
+
'memory.buffers_mb' => buffers.to_f / 1024,
|
174
|
+
'memory.cached_mb' => cached.to_f / 1024,
|
175
|
+
'memory.free_percent' => (free.to_f / total.to_f) * 100,
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.swap
|
180
|
+
_, total, used, free = `free -k -o | grep Swap`.chomp.split(/\s+/)
|
181
|
+
{
|
182
|
+
'swap.used_mb' => used.to_f / 1024,
|
183
|
+
'swap.free_mb' => free.to_f / 1024,
|
184
|
+
'swap.free_percent' => (free.to_f / total.to_f) * 100,
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.load_disks
|
189
|
+
{ :gauges => disks.merge(iostat) }
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.disks
|
193
|
+
output = {}
|
194
|
+
`df -Pk`.split("\n").grep(%r{^/dev/}).each do |line|
|
195
|
+
device, total, used, available, capacity, mount = line.split(/\s+/)
|
196
|
+
names = [File.basename(device)]
|
197
|
+
names << 'root' if mount == '/'
|
198
|
+
names.each do |name|
|
199
|
+
output["disk.#{name}.total_mb"] = total.to_f / 1024
|
200
|
+
output["disk.#{name}.used_mb"] = used.to_f / 1024
|
201
|
+
output["disk.#{name}.available_mb"] = available.to_f / 1024
|
202
|
+
output["disk.#{name}.available_percent"] = available.to_f / total.to_f * 100
|
203
|
+
end
|
204
|
+
end
|
205
|
+
output
|
206
|
+
end
|
207
|
+
|
208
|
+
def self.iostat
|
209
|
+
output = {}
|
210
|
+
if system('iostat > /dev/null 2>&1')
|
211
|
+
lines = `iostat -dx`.split("\n")
|
212
|
+
header = lines.grep(/^Device/i).first
|
213
|
+
dev_lines = lines[(lines.index(header) + 1)..-1]
|
214
|
+
util_index = header.split.index('%util')
|
215
|
+
dev_lines.each do |dev_line|
|
216
|
+
values = dev_line.split
|
217
|
+
output["disk.#{values[0]}.percent_utilization"] = values[util_index].to_f
|
218
|
+
end
|
219
|
+
end
|
220
|
+
output
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.load_filesystem
|
224
|
+
{ :gauges => filesystem }
|
225
|
+
end
|
226
|
+
|
227
|
+
def self.filesystem
|
228
|
+
allocated, unused, max = `sysctl fs.file-nr`.split[-3..-1].map(&:to_i)
|
229
|
+
open_files = allocated - unused
|
230
|
+
{ 'filesystem.open_files' => open_files,
|
231
|
+
'filesystem.max_open_files' => max,
|
232
|
+
'filesystem.open_files_pct_max' => (open_files.to_f / max.to_f) * 100,
|
233
|
+
}
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
token = ENV["INSTRUMENTAL_TOKEN"]
|
240
|
+
if !token
|
241
|
+
token, collector = *ARGV
|
242
|
+
else
|
243
|
+
collector = *ARGV
|
244
|
+
end
|
245
|
+
unless token
|
246
|
+
puts "Usage: #{$0} <token> [collector]"
|
247
|
+
exit 1
|
248
|
+
end
|
249
|
+
I = Instrumental::Agent.new(token, :collector => collector)
|
250
|
+
|
251
|
+
host = `hostname`.chomp
|
252
|
+
|
253
|
+
puts "Collecting stats under the hostname: #{host}"
|
254
|
+
|
255
|
+
unless system('iostat > /dev/null 2>&1')
|
256
|
+
puts 'Install iostat (sysstat package) to collect disk I/O metrics.'
|
257
|
+
end
|
258
|
+
|
259
|
+
loop do
|
260
|
+
inspector = SystemInspector.new
|
261
|
+
inspector.load_all
|
262
|
+
inspector.gauges.each do |stat, value|
|
263
|
+
I.gauge("#{host}.#{stat}", value)
|
264
|
+
end
|
265
|
+
# I.increment("#{host}.#{stat}", delta)
|
266
|
+
sleep 10
|
267
|
+
end
|
data/bin/instrumental
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'cgi'
|
3
|
+
require 'net/http'
|
4
|
+
require 'optparse'
|
5
|
+
require 'rubygems'
|
6
|
+
gem 'json'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
|
10
|
+
metrics = "*"
|
11
|
+
token = ENV["INSTRUMENTAL_TOKEN"]
|
12
|
+
allowed_durations = [1800, 3600, 10800, 21600, 43200, 86400, 259200, 604800, 1209600]
|
13
|
+
duration = nil
|
14
|
+
|
15
|
+
opts = OptionParser.new do |opts|
|
16
|
+
opts.banner = "Usage: instrumental [options]"
|
17
|
+
|
18
|
+
opts.on("-m", "--metrics METRICS", "Comma separated list of metrics you're interested in. Wildcard (*) allowed") do |m|
|
19
|
+
metrics = m
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("-t", "--token TOKEN", "Your API token. Also can be set as an environment variable, INSTRUMENTAL_TOKEN") do |t|
|
23
|
+
token = t
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on("-d", "--duration DURATION", "Duration ago in seconds to view. You can specify #{allowed_durations.join(" ")}") do |d|
|
27
|
+
duration = allowed_durations.index(d.to_i) && d.to_i
|
28
|
+
end
|
29
|
+
end.parse!
|
30
|
+
|
31
|
+
search_line = CGI.escape(metrics)
|
32
|
+
duration &&= "&duration=#{duration}"
|
33
|
+
http = Net::HTTP.new("instrumentalapp.com", 80)
|
34
|
+
request = Net::HTTP::Get.new("/metrics/#{search_line}?token=#{token}&format=json#{duration}")
|
35
|
+
response = http.request(request)
|
36
|
+
raise "Server error: #{response.message} #{response.code}" if response.code.to_i != 200
|
37
|
+
response = JSON.parse(response.body)
|
38
|
+
bars = ["\342\226\201", "\342\226\202", "\342\226\203", "\342\226\204", "\342\226\205", "\342\226\206", "\342\226\207"]
|
39
|
+
max_key_size = response.collect { |k, v| k.size }.max
|
40
|
+
scale = bars.size - 1
|
41
|
+
response.each do |metric, data|
|
42
|
+
data = data.collect { |h| h["value"].to_f }
|
43
|
+
min = data.min
|
44
|
+
range = data.max - min
|
45
|
+
graph = data.collect do |v|
|
46
|
+
if (idx = (((v - min) / range) * scale)).nan?
|
47
|
+
idx = 0
|
48
|
+
elsif idx.infinite?
|
49
|
+
idx = scale
|
50
|
+
end
|
51
|
+
bars[idx.round]
|
52
|
+
end
|
53
|
+
puts "%#{max_key_size}s | %s" % [metric, graph.join]
|
54
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
$: << "./lib"
|
2
|
+
require 'instrumental_tools/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "instrumental_tools"
|
6
|
+
s.version = Instrumental::Tools::VERSION
|
7
|
+
s.authors = ["Elijah Miller", "Christopher Zelenak", "Kristopher Chambers", "Matthew Hassfurder"]
|
8
|
+
s.email = ["support@instrumentalapp.com"]
|
9
|
+
s.homepage = "http://github.com/fastestforward/instrumental_tools"
|
10
|
+
s.summary = %q{Command line tools for Instrumental}
|
11
|
+
s.description = %q{Tools for displaying information from and reporting to Instrumental (instrumentalapp.com)}
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.add_runtime_dependency(%q<json>, [">=0"])
|
18
|
+
s.add_runtime_dependency(%q<instrumental_agent>, [">=0.5"])
|
19
|
+
s.add_development_dependency(%q<rake>, [">=0"])
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: instrumental_tools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Elijah Miller
|
9
|
+
- Christopher Zelenak
|
10
|
+
- Kristopher Chambers
|
11
|
+
- Matthew Hassfurder
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
date: 2011-12-12 00:00:00.000000000 Z
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
18
|
+
name: json
|
19
|
+
requirement: &70344909872300 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ! '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '0'
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: *70344909872300
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: instrumental_agent
|
30
|
+
requirement: &70344909871780 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ! '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0.5'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: *70344909871780
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: rake
|
41
|
+
requirement: &70344909871300 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: *70344909871300
|
50
|
+
description: Tools for displaying information from and reporting to Instrumental (instrumentalapp.com)
|
51
|
+
email:
|
52
|
+
- support@instrumentalapp.com
|
53
|
+
executables:
|
54
|
+
- gitstrumental
|
55
|
+
- instrument_server
|
56
|
+
- instrumental
|
57
|
+
extensions: []
|
58
|
+
extra_rdoc_files: []
|
59
|
+
files:
|
60
|
+
- .gitignore
|
61
|
+
- Gemfile
|
62
|
+
- README.md
|
63
|
+
- Rakefile
|
64
|
+
- bin/gitstrumental
|
65
|
+
- bin/instrument_server
|
66
|
+
- bin/instrumental
|
67
|
+
- instrumental_tools.gemspec
|
68
|
+
- lib/instrumental_tools/version.rb
|
69
|
+
homepage: http://github.com/fastestforward/instrumental_tools
|
70
|
+
licenses: []
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ! '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
hash: 266192678575170194
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
hash: 266192678575170194
|
93
|
+
requirements: []
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 1.8.11
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: Command line tools for Instrumental
|
99
|
+
test_files: []
|