sensu 0.8.19 → 0.9.0.beta
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/README.org +2 -1
- data/lib/sensu.rb +1 -1
- data/lib/sensu/api.rb +7 -3
- data/lib/sensu/client.rb +19 -18
- data/lib/sensu/config.rb +33 -8
- data/lib/sensu/helpers/redis.rb +7 -1
- data/lib/sensu/helpers/ruby.rb +34 -0
- data/lib/sensu/server.rb +72 -34
- metadata +13 -10
data/README.org
CHANGED
@@ -9,8 +9,9 @@
|
|
9
9
|
* Documentation
|
10
10
|
Find it [[https://github.com/sonian/sensu/wiki][HERE]].
|
11
11
|
* Other Projects
|
12
|
-
- [[https://github.com/sonian/sensu-
|
12
|
+
- [[https://github.com/sonian/sensu-community-plugins][Sensu Community Plugins]]
|
13
13
|
- [[https://github.com/sonian/sensu-dashboard][Sensu Dashboard]]
|
14
|
+
- [[https://github.com/sonian/sensu-plugin][Sensu Plugin & Handler Helper]]
|
14
15
|
* License
|
15
16
|
Sensu is released under the [[https://github.com/sonian/sensu/blob/master/MIT-LICENSE.txt][MIT license]].
|
16
17
|
* Contributing
|
data/lib/sensu.rb
CHANGED
data/lib/sensu/api.rb
CHANGED
@@ -26,11 +26,15 @@ module Sensu
|
|
26
26
|
config = Sensu::Config.new(options)
|
27
27
|
$settings = config.settings
|
28
28
|
$logger = config.open_log
|
29
|
+
if options[:daemonize]
|
30
|
+
Process.daemonize
|
31
|
+
end
|
32
|
+
Process.write_pid(options[:pid_file])
|
29
33
|
$logger.debug('[setup] -- connecting to redis')
|
30
|
-
$redis =
|
34
|
+
$redis = Redis.connect($settings.redis.to_hash.symbolize_keys)
|
31
35
|
$logger.debug('[setup] -- connecting to rabbitmq')
|
32
|
-
|
33
|
-
$amq =
|
36
|
+
rabbitmq = AMQP.connect($settings.rabbitmq.to_hash.symbolize_keys)
|
37
|
+
$amq = AMQP::Channel.new(rabbitmq)
|
34
38
|
end
|
35
39
|
|
36
40
|
def self.stop(signal)
|
data/lib/sensu/client.rb
CHANGED
@@ -3,8 +3,12 @@ require File.join(File.dirname(__FILE__), 'config')
|
|
3
3
|
module Sensu
|
4
4
|
class Client
|
5
5
|
def self.run(options={})
|
6
|
+
client = self.new(options)
|
7
|
+
if options[:daemonize]
|
8
|
+
Process.daemonize
|
9
|
+
end
|
10
|
+
Process.write_pid(options[:pid_file])
|
6
11
|
EM.run do
|
7
|
-
client = self.new(options)
|
8
12
|
client.setup_amqp
|
9
13
|
client.setup_keepalives
|
10
14
|
client.setup_subscriptions
|
@@ -34,30 +38,27 @@ module Sensu
|
|
34
38
|
|
35
39
|
def setup_amqp
|
36
40
|
@logger.debug('[amqp] -- connecting to rabbitmq')
|
37
|
-
|
38
|
-
@amq =
|
41
|
+
rabbitmq = AMQP.connect(@settings.rabbitmq.to_hash.symbolize_keys)
|
42
|
+
@amq = AMQP::Channel.new(rabbitmq)
|
39
43
|
end
|
40
44
|
|
41
45
|
def publish_keepalive
|
46
|
+
@settings.client.timestamp = Time.now.to_i
|
42
47
|
@logger.debug('[keepalive] -- publishing keepalive -- ' + @settings.client.timestamp.to_s)
|
43
|
-
@
|
44
|
-
@keepalive_queue.publish(@settings.client.to_json)
|
48
|
+
@amq.queue('keepalives').publish(@settings.client.to_json)
|
45
49
|
end
|
46
50
|
|
47
51
|
def setup_keepalives
|
48
52
|
@logger.debug('[keepalive] -- setup keepalives')
|
49
|
-
@settings.client.timestamp = Time.now.to_i
|
50
53
|
publish_keepalive
|
51
54
|
EM.add_periodic_timer(30) do
|
52
|
-
@settings.client.timestamp = Time.now.to_i
|
53
55
|
publish_keepalive
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
57
59
|
def publish_result(check)
|
58
60
|
@logger.info('[result] -- publishing check result -- ' + check.status.to_s + ' -- ' + check.name)
|
59
|
-
@
|
60
|
-
@result_queue.publish({
|
61
|
+
@amq.queue('results').publish({
|
61
62
|
:client => @settings.client.name,
|
62
63
|
:check => check.to_hash
|
63
64
|
}.to_json)
|
@@ -115,7 +116,7 @@ module Sensu
|
|
115
116
|
def setup_subscriptions
|
116
117
|
@logger.debug('[subscribe] -- setup subscriptions')
|
117
118
|
@check_queue = @amq.queue(String.unique, :exclusive => true)
|
118
|
-
@settings.client.subscriptions.push('uchiwa')
|
119
|
+
@settings.client.subscriptions.push('uchiwa').uniq!
|
119
120
|
@settings.client.subscriptions.each do |exchange|
|
120
121
|
@logger.debug('[subscribe] -- queue binding to exchange -- ' + exchange)
|
121
122
|
@check_queue.bind(@amq.fanout(exchange))
|
@@ -171,18 +172,18 @@ module Sensu
|
|
171
172
|
def setup_socket
|
172
173
|
@logger.debug('[socket] -- starting up socket server')
|
173
174
|
EM.start_server('127.0.0.1', 3030, ClientSocket) do |socket|
|
175
|
+
socket.settings = @settings
|
174
176
|
socket.logger = @logger
|
175
|
-
socket.
|
176
|
-
socket.result_queue = @amq.queue('results')
|
177
|
+
socket.amq = @amq
|
177
178
|
end
|
178
179
|
end
|
179
180
|
end
|
180
181
|
|
181
182
|
class ClientSocket < EM::Connection
|
182
|
-
attr_accessor :
|
183
|
+
attr_accessor :settings, :logger, :amq
|
183
184
|
|
184
185
|
def receive_data(data)
|
185
|
-
@logger.debug('[socket] -- received data from
|
186
|
+
@logger.debug('[socket] -- new connection -- received data from external source')
|
186
187
|
begin
|
187
188
|
check = Hashie::Mash.new(JSON.parse(data))
|
188
189
|
validates = %w[name status output].all? do |key|
|
@@ -190,21 +191,21 @@ module Sensu
|
|
190
191
|
end
|
191
192
|
if validates
|
192
193
|
@logger.info('[socket] -- publishing check result -- ' + check.name)
|
193
|
-
@
|
194
|
-
:client => @
|
194
|
+
@amq.queue('results').publish({
|
195
|
+
:client => @settings.client.name,
|
195
196
|
:check => check.to_hash
|
196
197
|
}.to_json)
|
197
198
|
else
|
198
199
|
@logger.warn('[socket] -- a check name, exit status, and output are required -- e.g. {name: x, status: 0, output: "y"}')
|
199
200
|
end
|
200
201
|
rescue JSON::ParserError => error
|
201
|
-
@logger.warn('[socket] -- check result must be valid JSON: ' + error)
|
202
|
+
@logger.warn('[socket] -- check result must be valid JSON: ' + error.to_s)
|
202
203
|
end
|
203
204
|
close_connection
|
204
205
|
end
|
205
206
|
|
206
207
|
def unbind
|
207
|
-
@logger.debug('[socket] --
|
208
|
+
@logger.debug('[socket] -- connection closed')
|
208
209
|
end
|
209
210
|
end
|
210
211
|
end
|
data/lib/sensu/config.rb
CHANGED
@@ -22,6 +22,8 @@ module Sensu
|
|
22
22
|
:config_file => '/etc/sensu/config.json',
|
23
23
|
:config_dir => '/etc/sensu/conf.d',
|
24
24
|
:validate => true,
|
25
|
+
:pid_file => '/tmp/' + File.basename($0) + '.pid',
|
26
|
+
:daemonize => false
|
25
27
|
}
|
26
28
|
|
27
29
|
def initialize(options={})
|
@@ -33,7 +35,9 @@ module Sensu
|
|
33
35
|
def open_log
|
34
36
|
@logger = Cabin::Channel.new
|
35
37
|
if File.writable?(@options[:log_file]) || !File.exist?(@options[:log_file]) && File.writable?(File.dirname(@options[:log_file]))
|
36
|
-
|
38
|
+
STDOUT.reopen(@options[:log_file], 'a')
|
39
|
+
STDERR.reopen(STDOUT)
|
40
|
+
ruby_logger = Logger.new(STDOUT)
|
37
41
|
else
|
38
42
|
invalid_config('log file is not writable: ' + @options[:log_file])
|
39
43
|
end
|
@@ -78,6 +82,26 @@ module Sensu
|
|
78
82
|
unless @settings.handlers.include?('default')
|
79
83
|
invalid_config('missing default handler')
|
80
84
|
end
|
85
|
+
@settings.handlers.each do |name, details|
|
86
|
+
unless details.key?('type')
|
87
|
+
invalid_config('missing type for handler ' + name)
|
88
|
+
end
|
89
|
+
case details.type
|
90
|
+
when 'pipe'
|
91
|
+
unless details.key?('command')
|
92
|
+
invalid_config('missing command for pipe handler ' + name)
|
93
|
+
end
|
94
|
+
when 'amqp'
|
95
|
+
unless details.key?('exchange')
|
96
|
+
invalid_config('missing exchange details for amqp handler ' + name)
|
97
|
+
end
|
98
|
+
unless details.exchange.key?('name')
|
99
|
+
invalid_config('missing exchange name for amqp handler ' + name)
|
100
|
+
end
|
101
|
+
else
|
102
|
+
invalid_config('unknown type for handler ' + name)
|
103
|
+
end
|
104
|
+
end
|
81
105
|
when 'api'
|
82
106
|
has_keys(%w[redis api])
|
83
107
|
when 'client'
|
@@ -139,12 +163,7 @@ module Sensu
|
|
139
163
|
puts opts
|
140
164
|
exit
|
141
165
|
end
|
142
|
-
current_process = $0
|
143
|
-
if current_process == 'sensu-server' || current_process == 'rake'
|
144
|
-
opts.on('-w', '--worker', 'Only consume jobs, no check publishing (default: false)') do
|
145
|
-
options[:worker] = true
|
146
|
-
end
|
147
|
-
end
|
166
|
+
current_process = File.basename($0)
|
148
167
|
opts.on('-c', '--config FILE', 'Sensu JSON config FILE (default: /etc/sensu/config.json)') do |file|
|
149
168
|
options[:config_file] = file
|
150
169
|
end
|
@@ -157,9 +176,15 @@ module Sensu
|
|
157
176
|
opts.on('-v', '--verbose', 'Enable verbose logging (default: false)') do
|
158
177
|
options[:verbose] = true
|
159
178
|
end
|
179
|
+
opts.on('-b', '--background', 'Fork into backgaround (daemon mode) (default: false)') do
|
180
|
+
options[:daemonize] = true
|
181
|
+
end
|
182
|
+
opts.on('-p', '--pid_file FILE', 'Sensu PID FILE (default: ' + DEFAULT_OPTIONS[:pid_file] + ')') do |file|
|
183
|
+
options[:pid_file] = file
|
184
|
+
end
|
160
185
|
end
|
161
186
|
optparse.parse!(arguments)
|
162
|
-
options
|
187
|
+
DEFAULT_OPTIONS.merge(options)
|
163
188
|
end
|
164
189
|
end
|
165
190
|
end
|
data/lib/sensu/helpers/redis.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Redis
|
2
|
-
class
|
2
|
+
class Client
|
3
3
|
def connection_completed
|
4
4
|
@connected = true
|
5
5
|
@port, @host = Socket.unpack_sockaddr_in(get_peername)
|
@@ -22,4 +22,10 @@ module Redis
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
def self.connect(options={})
|
27
|
+
host = options[:host] || 'localhost'
|
28
|
+
port = options[:port] || 6379
|
29
|
+
EM.connect(host, port, Redis::Client)
|
30
|
+
end
|
25
31
|
end
|
data/lib/sensu/helpers/ruby.rb
CHANGED
@@ -44,3 +44,37 @@ class String
|
|
44
44
|
rand(36**chars).to_s(36)
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
48
|
+
module Process
|
49
|
+
def self.write_pid(pid_file)
|
50
|
+
begin
|
51
|
+
File.open(pid_file, 'w') do |file|
|
52
|
+
file.write(self.pid.to_s + "\n")
|
53
|
+
end
|
54
|
+
rescue
|
55
|
+
raise 'could not write to pid file: ' + pid_file
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.daemonize
|
60
|
+
srand
|
61
|
+
fork and exit
|
62
|
+
unless session_id = self.setsid
|
63
|
+
raise 'cannot detach from controlling terminal'
|
64
|
+
end
|
65
|
+
trap 'SIGHUP', 'IGNORE'
|
66
|
+
exit if pid = fork
|
67
|
+
Dir.chdir "/"
|
68
|
+
ObjectSpace.each_object(IO) do |io|
|
69
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
70
|
+
begin
|
71
|
+
unless io.closed?
|
72
|
+
io.close
|
73
|
+
end
|
74
|
+
rescue
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
return session_id
|
79
|
+
end
|
80
|
+
end
|
data/lib/sensu/server.rb
CHANGED
@@ -6,20 +6,21 @@ require File.join(File.dirname(__FILE__), 'helpers', 'redis')
|
|
6
6
|
|
7
7
|
module Sensu
|
8
8
|
class Server
|
9
|
-
attr_accessor :redis, :amq, :
|
9
|
+
attr_accessor :redis, :amq, :is_master
|
10
10
|
|
11
11
|
def self.run(options={})
|
12
|
+
server = self.new(options)
|
13
|
+
if options[:daemonize]
|
14
|
+
Process.daemonize
|
15
|
+
end
|
16
|
+
Process.write_pid(options[:pid_file])
|
12
17
|
EM.threadpool_size = 16
|
13
18
|
EM.run do
|
14
|
-
server = self.new(options)
|
15
19
|
server.setup_redis
|
16
20
|
server.setup_amqp
|
17
21
|
server.setup_keepalives
|
18
22
|
server.setup_results
|
19
|
-
|
20
|
-
server.setup_publisher
|
21
|
-
server.setup_keepalive_monitor
|
22
|
-
end
|
23
|
+
server.setup_master_monitor
|
23
24
|
server.setup_queue_monitor
|
24
25
|
|
25
26
|
%w[INT TERM].each do |signal|
|
@@ -34,7 +35,6 @@ module Sensu
|
|
34
35
|
config = Sensu::Config.new(options)
|
35
36
|
@settings = config.settings
|
36
37
|
@logger = config.open_log
|
37
|
-
@is_worker = options[:worker]
|
38
38
|
end
|
39
39
|
|
40
40
|
def stop(signal)
|
@@ -46,13 +46,13 @@ module Sensu
|
|
46
46
|
|
47
47
|
def setup_redis
|
48
48
|
@logger.debug('[redis] -- connecting to redis')
|
49
|
-
@redis =
|
49
|
+
@redis = Redis.connect(@settings.redis.to_hash.symbolize_keys)
|
50
50
|
end
|
51
51
|
|
52
52
|
def setup_amqp
|
53
53
|
@logger.debug('[amqp] -- connecting to rabbitmq')
|
54
|
-
|
55
|
-
@amq =
|
54
|
+
rabbitmq = AMQP.connect(@settings.rabbitmq.to_hash.symbolize_keys)
|
55
|
+
@amq = AMQP::Channel.new(rabbitmq)
|
56
56
|
end
|
57
57
|
|
58
58
|
def setup_keepalives
|
@@ -84,37 +84,35 @@ module Sensu
|
|
84
84
|
handlers.each do |handler|
|
85
85
|
if @settings.handlers.key?(handler)
|
86
86
|
@logger.debug('[event] -- handling event -- ' + [handler, event.client.name, event.check.name].join(' -- '))
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
when "amqp"
|
91
|
-
exchange = details.exchange || 'events'
|
92
|
-
@logger.debug('[event] -- publishing event to amqp exchange -- ' + [exchange, event.client.name, event.check.name].join(' -- '))
|
93
|
-
message = details.send_only_check_output ? event.check.output : event.to_json
|
94
|
-
@amq.direct(exchange).publish(message)
|
95
|
-
else
|
96
|
-
@logger.warn('[event] -- unknown handler type -- ' + details.type)
|
97
|
-
end
|
98
|
-
else
|
87
|
+
details = @settings.handlers[handler]
|
88
|
+
case details.type
|
89
|
+
when "pipe"
|
99
90
|
handle = proc do
|
100
91
|
output = ''
|
101
92
|
Bundler.with_clean_env do
|
102
93
|
begin
|
103
|
-
IO.popen(
|
94
|
+
IO.popen(details.command + ' 2>&1', 'r+') do |io|
|
104
95
|
io.write(event.to_json)
|
105
96
|
io.close_write
|
106
97
|
output = io.read
|
107
98
|
end
|
108
99
|
rescue Errno::EPIPE => error
|
109
|
-
output = handler + ' -- broken pipe: ' + error
|
100
|
+
output = handler + ' -- broken pipe: ' + error.to_s
|
110
101
|
end
|
111
102
|
end
|
112
103
|
output
|
113
104
|
end
|
114
105
|
EM.defer(handle, report)
|
106
|
+
when "amqp"
|
107
|
+
exchange = details.exchange.name
|
108
|
+
exchange_type = details.exchange.key?('type') ? details.exchange.type.to_sym : :direct
|
109
|
+
exchange_options = details.exchange.reject { |key, value| %w[name type].include?(key) }
|
110
|
+
@logger.debug('[event] -- publishing event to amqp exchange -- ' + [exchange, event.client.name, event.check.name].join(' -- '))
|
111
|
+
payload = details.send_only_check_output ? event.check.output : event.to_json
|
112
|
+
@amq.method(exchange_type).call(exchange, exchange_options).publish(payload)
|
115
113
|
end
|
116
114
|
else
|
117
|
-
@logger.warn('[event] -- handler
|
115
|
+
@logger.warn('[event] -- unknown handler -- ' + handler)
|
118
116
|
end
|
119
117
|
end
|
120
118
|
end
|
@@ -210,14 +208,13 @@ module Sensu
|
|
210
208
|
@result_queue = @amq.queue('results')
|
211
209
|
@result_queue.subscribe do |result_json|
|
212
210
|
result = Hashie::Mash.new(JSON.parse(result_json))
|
213
|
-
@logger.info('[result] -- received result -- ' + result.client
|
211
|
+
@logger.info('[result] -- received result -- ' + [result.check.name, result.client, result.check.status, result.check.output].join(' -- '))
|
214
212
|
process_result(result)
|
215
213
|
end
|
216
214
|
end
|
217
215
|
|
218
216
|
def setup_publisher(options={})
|
219
217
|
@logger.debug('[publisher] -- setup publisher')
|
220
|
-
exchanges = Hash.new
|
221
218
|
stagger = options[:test] ? 0 : 7
|
222
219
|
@settings.checks.each_with_index do |(name, details), index|
|
223
220
|
check_request = Hashie::Mash.new({:name => name})
|
@@ -231,12 +228,11 @@ module Sensu
|
|
231
228
|
else
|
232
229
|
exchange = target
|
233
230
|
end
|
234
|
-
exchanges[exchange] ||= @amq.fanout(exchange)
|
235
231
|
interval = options[:test] ? 0.5 : details.interval
|
236
232
|
EM.add_periodic_timer(interval) do
|
237
233
|
check_request.issued = Time.now.to_i
|
238
234
|
@logger.info('[publisher] -- publishing check -- ' + name + ' -- ' + exchange)
|
239
|
-
|
235
|
+
@amq.fanout(exchange).publish(check_request.to_json)
|
240
236
|
end
|
241
237
|
end
|
242
238
|
end
|
@@ -264,17 +260,17 @@ module Sensu
|
|
264
260
|
when time_since_last_keepalive >= 180
|
265
261
|
result.check.status = 2
|
266
262
|
result.check.output = 'No keep-alive sent from host in over 180 seconds'
|
267
|
-
@
|
263
|
+
@amq.queue('results').publish(result.to_json)
|
268
264
|
when time_since_last_keepalive >= 120
|
269
265
|
result.check.status = 1
|
270
266
|
result.check.output = 'No keep-alive sent from host in over 120 seconds'
|
271
|
-
@
|
267
|
+
@amq.queue('results').publish(result.to_json)
|
272
268
|
else
|
273
269
|
@redis.hexists('events:' + client_id, 'keepalive').callback do |exists|
|
274
270
|
if exists
|
275
271
|
result.check.status = 0
|
276
272
|
result.check.output = 'Keep-alive sent from host'
|
277
|
-
@
|
273
|
+
@amq.queue('results').publish(result.to_json)
|
278
274
|
end
|
279
275
|
end
|
280
276
|
end
|
@@ -284,15 +280,57 @@ module Sensu
|
|
284
280
|
end
|
285
281
|
end
|
286
282
|
|
283
|
+
def master_duties
|
284
|
+
setup_publisher
|
285
|
+
setup_keepalive_monitor
|
286
|
+
end
|
287
|
+
|
288
|
+
def request_master_election
|
289
|
+
@is_master ||= false
|
290
|
+
@redis.setnx('lock:master', Time.now.to_i).callback do |created|
|
291
|
+
if created
|
292
|
+
@logger.info('[master] -- i am the master')
|
293
|
+
@is_master = true
|
294
|
+
master_duties
|
295
|
+
else
|
296
|
+
@redis.get('lock:master') do |timestamp|
|
297
|
+
if Time.now.to_i - timestamp.to_i >= 60
|
298
|
+
@redis.getset('lock:master', Time.now.to_i).callback do |previous|
|
299
|
+
if previous == timestamp
|
300
|
+
@logger.info('[master] -- i am now the master')
|
301
|
+
@is_master = true
|
302
|
+
master_duties
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def setup_master_monitor
|
312
|
+
request_master_election
|
313
|
+
EM.add_periodic_timer(20) do
|
314
|
+
if @is_master
|
315
|
+
timestamp = Time.now.to_i
|
316
|
+
@redis.set('lock:master', timestamp).callback do
|
317
|
+
@logger.debug('[master] -- updated master lock timestamp -- ' + timestamp.to_s)
|
318
|
+
end
|
319
|
+
else
|
320
|
+
request_master_election
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
287
325
|
def setup_queue_monitor
|
288
326
|
@logger.debug('[monitor] -- setup queue monitor')
|
289
327
|
EM.add_periodic_timer(5) do
|
290
328
|
unless @keepalive_queue.subscribed?
|
291
|
-
@logger.warn('[monitor] -- reconnecting to rabbitmq')
|
329
|
+
@logger.warn('[monitor] -- reconnecting to rabbitmq -- keepalives')
|
292
330
|
setup_keepalives
|
293
331
|
end
|
294
332
|
unless @result_queue.subscribed?
|
295
|
-
@logger.warn('[monitor] -- reconnecting to rabbitmq')
|
333
|
+
@logger.warn('[monitor] -- reconnecting to rabbitmq -- results')
|
296
334
|
setup_results
|
297
335
|
end
|
298
336
|
end
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sensu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 31098121
|
5
|
+
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
- beta
|
11
|
+
version: 0.9.0.beta
|
11
12
|
platform: ruby
|
12
13
|
authors:
|
13
14
|
- Sean Porter
|
@@ -16,7 +17,7 @@ autorequire:
|
|
16
17
|
bindir: bin
|
17
18
|
cert_chain: []
|
18
19
|
|
19
|
-
date: 2011-12-
|
20
|
+
date: 2011-12-21 00:00:00 -08:00
|
20
21
|
default_executable:
|
21
22
|
dependencies:
|
22
23
|
- !ruby/object:Gem::Dependency
|
@@ -263,12 +264,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
263
264
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
264
265
|
none: false
|
265
266
|
requirements:
|
266
|
-
- - "
|
267
|
+
- - ">"
|
267
268
|
- !ruby/object:Gem::Version
|
268
|
-
hash:
|
269
|
+
hash: 25
|
269
270
|
segments:
|
270
|
-
-
|
271
|
-
|
271
|
+
- 1
|
272
|
+
- 3
|
273
|
+
- 1
|
274
|
+
version: 1.3.1
|
272
275
|
requirements: []
|
273
276
|
|
274
277
|
rubyforge_project:
|