gorgon 0.8.4 → 0.9.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.
- checksums.yaml +8 -8
- data/Gemfile.lock +3 -3
- data/README.md +1 -1
- data/lib/gorgon/listener.rb +15 -4
- data/lib/gorgon/originator.rb +16 -1
- data/lib/gorgon/originator_protocol.rb +38 -5
- data/lib/gorgon/settings/rails_project_files_content.rb +2 -2
- data/lib/gorgon/version.rb +1 -1
- data/spec/listener_spec.rb +17 -21
- data/spec/originator_protocol_spec.rb +26 -9
- data/spec/originator_spec.rb +16 -5
- data/tutorial.md +8 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NTA0MWUwMjQ4ZmIzZmM0ZjAyYWJjOWYxMTA1NWQwZGFiZTQ0MzQ5Yg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MTA4NGNjZjU2OWQxOWQxMmZkYTlkOGNjYjI0MmIxYTI5ODMzZGEzYQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
N2U2ZWZlMWNkNDI2YTEyOTkwNGYwZjQwNGI0NWJmMGEzOGI0ZDg2YTA0MWYw
|
10
|
+
MDgxNGQzMTcwOTZmYmY3OTk4MzY3YTk3NDBmNjEyYjhlMjQwMGEwNDQ0Y2Jk
|
11
|
+
MGJhYTk3M2EyZWE2ZjU5MzZiNTAyNWNhOTdkZTYzYWU0YzViYzk=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
Mzg3NDc4MWZjNzE0MDYwMjZmNWMzMjE1MzNhY2JkYTU2MzBhZTQ0ZmYzNTdm
|
14
|
+
MjdkYTM3YTY5MWVlMjE2ZjRiZjMyYjdmMDNjZDZmMTQ4ODM5Njk3NGU0ZjIy
|
15
|
+
MmYyMDYxOTJkNGFiMzVjZjgxZWRlMDk3NzZhMzQ3M2E5NzEwNjk=
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
gorgon (0.
|
4
|
+
gorgon (0.9.0)
|
5
5
|
amqp (~> 1.1.0)
|
6
6
|
awesome_print
|
7
7
|
colorize (~> 0.5.8)
|
@@ -17,10 +17,10 @@ GEM
|
|
17
17
|
amqp (1.1.8)
|
18
18
|
amq-protocol (>= 1.9.2)
|
19
19
|
eventmachine
|
20
|
-
awesome_print (1.
|
20
|
+
awesome_print (1.6.1)
|
21
21
|
colorize (0.5.8)
|
22
22
|
diff-lcs (1.1.3)
|
23
|
-
eventmachine (1.0.
|
23
|
+
eventmachine (1.0.7)
|
24
24
|
open4 (1.3.4)
|
25
25
|
rake (0.9.2.2)
|
26
26
|
rspec (2.11.0)
|
data/README.md
CHANGED
@@ -21,7 +21,7 @@ Installing listener as a Daemon process (Ubuntu 9.10 or later)
|
|
21
21
|
Gotchas
|
22
22
|
----------------------------------------------------------------
|
23
23
|
|
24
|
-
* if you get `cannot load such file -- qrack/qrack (LoadError)`, just add `gem 'gorgon', '~> 0.8.
|
24
|
+
* if you get `cannot load such file -- qrack/qrack (LoadError)`, just add `gem 'gorgon', '~> 0.8.4' , :group => :remote_test` to your Gemfile, and run tests using `bundle exec gorgon`
|
25
25
|
* If `gorgon install_listener` didn't work for you, you can try [these steps](/daemon_with_upstart_and_rvm.md)
|
26
26
|
|
27
27
|
Also note that the steps in the tutorial are **not** meant to work on every project, they will only give you initial settings. You will probably have to modify the following files:
|
data/lib/gorgon/listener.rb
CHANGED
@@ -28,6 +28,7 @@ class Listener
|
|
28
28
|
log "Listener #{Gorgon::VERSION} initializing"
|
29
29
|
connect
|
30
30
|
initialize_personal_job_queue
|
31
|
+
announce_readiness_to_originators
|
31
32
|
end
|
32
33
|
|
33
34
|
def listen
|
@@ -44,11 +45,17 @@ class Listener
|
|
44
45
|
end
|
45
46
|
|
46
47
|
def initialize_personal_job_queue
|
47
|
-
@job_queue = @bunny.queue("", :
|
48
|
-
exchange = @bunny.exchange(
|
48
|
+
@job_queue = @bunny.queue("job_queue_" + UUIDTools::UUID.timestamp_create.to_s, :auto_delete => true)
|
49
|
+
exchange = @bunny.exchange(job_exchange_name, :type => :fanout)
|
49
50
|
@job_queue.bind(exchange)
|
50
51
|
end
|
51
52
|
|
53
|
+
def announce_readiness_to_originators
|
54
|
+
exchange = @bunny.exchange(originator_exchange_name, :type => :fanout)
|
55
|
+
data = {:listener_queue_name => @job_queue.name}
|
56
|
+
exchange.publish(Yajl::Encoder.encode(data))
|
57
|
+
end
|
58
|
+
|
52
59
|
def poll
|
53
60
|
message = @job_queue.pop
|
54
61
|
return false if message == [nil, nil, nil]
|
@@ -175,8 +182,12 @@ class Listener
|
|
175
182
|
reply_exchange.publish(Yajl::Encoder.encode(message))
|
176
183
|
end
|
177
184
|
|
178
|
-
def
|
179
|
-
OriginatorProtocol.
|
185
|
+
def job_exchange_name
|
186
|
+
OriginatorProtocol.job_exchange_name(configuration.fetch(:cluster_id, nil))
|
187
|
+
end
|
188
|
+
|
189
|
+
def originator_exchange_name
|
190
|
+
OriginatorProtocol.originator_exchange_name(configuration.fetch(:cluster_id, nil))
|
180
191
|
end
|
181
192
|
|
182
193
|
def connection_information
|
data/lib/gorgon/originator.rb
CHANGED
@@ -69,6 +69,10 @@ class Originator
|
|
69
69
|
@protocol.receive_payloads do |payload|
|
70
70
|
handle_reply(payload)
|
71
71
|
end
|
72
|
+
|
73
|
+
@protocol.receive_new_listener_notifications do |payload|
|
74
|
+
handle_new_listener_notification(payload)
|
75
|
+
end
|
72
76
|
end
|
73
77
|
|
74
78
|
callback_handler.after_job_finishes
|
@@ -83,7 +87,7 @@ class Originator
|
|
83
87
|
create_job_state_and_observers
|
84
88
|
|
85
89
|
@logger.log "Publishing Job..."
|
86
|
-
@protocol.
|
90
|
+
@protocol.publish_job_to_all job_definition
|
87
91
|
@logger.log "Job Published"
|
88
92
|
end
|
89
93
|
|
@@ -135,6 +139,17 @@ class Originator
|
|
135
139
|
cleanup_if_job_complete
|
136
140
|
end
|
137
141
|
|
142
|
+
def handle_new_listener_notification(payload)
|
143
|
+
payload = Yajl::Parser.new(:symbolize_keys => true).parse(payload)
|
144
|
+
|
145
|
+
if payload[:listener_queue_name]
|
146
|
+
@protocol.publish_job_to_one(job_definition, payload[:listener_queue_name])
|
147
|
+
else
|
148
|
+
puts "Received unexpected payload on originator queue"
|
149
|
+
ap payload
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
138
153
|
def create_job_state_and_observers
|
139
154
|
@job_state = JobState.new files.count
|
140
155
|
RuntimeRecorder.new @job_state, configuration[:runtime_file]
|
@@ -6,11 +6,20 @@ require 'uuidtools'
|
|
6
6
|
|
7
7
|
class OriginatorProtocol
|
8
8
|
def initialize(logger, cluster_id=nil)
|
9
|
-
@
|
9
|
+
@originator_exchange_name = OriginatorProtocol.originator_exchange_name(cluster_id)
|
10
|
+
@job_exchange_name = OriginatorProtocol.job_exchange_name(cluster_id)
|
10
11
|
@logger = logger
|
11
12
|
end
|
12
13
|
|
13
|
-
def self.
|
14
|
+
def self.originator_exchange_name(cluster_id)
|
15
|
+
if cluster_id
|
16
|
+
"gorgon.originators.#{cluster_id}"
|
17
|
+
else
|
18
|
+
"gorgon.originators"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.job_exchange_name(cluster_id)
|
14
23
|
if cluster_id
|
15
24
|
"gorgon.jobs.#{cluster_id}"
|
16
25
|
else
|
@@ -33,17 +42,29 @@ class OriginatorProtocol
|
|
33
42
|
end
|
34
43
|
end
|
35
44
|
|
36
|
-
def
|
45
|
+
def publish_job_to_all job_definition
|
46
|
+
job_definition = append_protocol_information_to_job_definition(job_definition)
|
47
|
+
@channel.fanout(@job_exchange_name).publish(job_definition.to_json)
|
48
|
+
end
|
49
|
+
|
50
|
+
def publish_job_to_one job_definition, listener_queue_name
|
51
|
+
job_definition = append_protocol_information_to_job_definition(job_definition)
|
52
|
+
@channel.default_exchange.publish(job_definition.to_json, :routing_key => listener_queue_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def append_protocol_information_to_job_definition job_definition
|
56
|
+
job_definition = job_definition.dup
|
57
|
+
|
37
58
|
job_definition.file_queue_name = @file_queue.name
|
38
59
|
job_definition.reply_exchange_name = @reply_exchange.name
|
39
60
|
|
40
|
-
|
61
|
+
return job_definition
|
41
62
|
end
|
42
63
|
|
43
64
|
def send_message_to_listeners type, body={}
|
44
65
|
# TODO: we probably want to use a different exchange for this type of messages
|
45
66
|
message = {:type => type, :reply_exchange_name => @reply_exchange.name, :body => body}
|
46
|
-
@channel.fanout(@
|
67
|
+
@channel.fanout(@job_exchange_name).publish(Yajl::Encoder.encode(message))
|
47
68
|
end
|
48
69
|
|
49
70
|
def receive_payloads
|
@@ -52,6 +73,12 @@ class OriginatorProtocol
|
|
52
73
|
end
|
53
74
|
end
|
54
75
|
|
76
|
+
def receive_new_listener_notifications
|
77
|
+
@originator_queue.subscribe do |payload|
|
78
|
+
yield payload
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
55
82
|
def cancel_job
|
56
83
|
@file_queue.purge if @file_queue
|
57
84
|
@channel.fanout("gorgon.worker_managers").publish(cancel_message) if @channel
|
@@ -69,12 +96,18 @@ class OriginatorProtocol
|
|
69
96
|
@reply_queue = @channel.queue("reply_queue_" + UUIDTools::UUID.timestamp_create.to_s, :auto_delete => true)
|
70
97
|
@reply_exchange = @channel.direct("reply_exchange_" + UUIDTools::UUID.timestamp_create.to_s, :auto_delete => true)
|
71
98
|
@reply_queue.bind(@reply_exchange)
|
99
|
+
|
100
|
+
# Provides a way for new listeners to announce their presence to originators that have already started the job
|
101
|
+
@originator_queue = @channel.queue("originator_queue_" + UUIDTools::UUID.timestamp_create.to_s, :auto_delete => true)
|
102
|
+
@originator_exchange = @channel.fanout(@originator_exchange_name)
|
103
|
+
@originator_queue.bind(@originator_exchange)
|
72
104
|
end
|
73
105
|
|
74
106
|
def cleanup_queues_and_exchange
|
75
107
|
@reply_queue.delete if @reply_queue
|
76
108
|
@file_queue.delete if @file_queue
|
77
109
|
@reply_exchange.delete if @reply_exchange
|
110
|
+
@originator_queue.delete if @originator_queue
|
78
111
|
end
|
79
112
|
|
80
113
|
def cancel_message
|
@@ -8,7 +8,7 @@ module Settings
|
|
8
8
|
@file_server_host = FilesContent.get_file_server_host
|
9
9
|
@sync_exclude = [".git", ".rvmrc","doc","log","tmp"]
|
10
10
|
@originator_log_file = 'log/gorgon-originator.log'
|
11
|
-
@failed_files = '
|
11
|
+
@failed_files = 'gorgon-failed-files.json'
|
12
12
|
create_callbacks
|
13
13
|
end
|
14
14
|
|
@@ -88,7 +88,7 @@ class GorgonCallbacks < Gorgon::DefaultCallbacks
|
|
88
88
|
load './Rakefile'
|
89
89
|
|
90
90
|
begin
|
91
|
-
if Rails.env
|
91
|
+
if Rails.env == 'remote_test'
|
92
92
|
Rake::Task['db:drop'].execute
|
93
93
|
end
|
94
94
|
rescue Exception => ex
|
data/lib/gorgon/version.rb
CHANGED
data/spec/listener_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'gorgon/listener'
|
|
2
2
|
|
3
3
|
describe Listener do
|
4
4
|
let(:connection_information) { double }
|
5
|
-
let(:queue) { double("GorgonBunny Queue", :bind => nil) }
|
5
|
+
let(:queue) { double("GorgonBunny Queue", :bind => nil, :name => "some supposedly unique string") }
|
6
6
|
let(:exchange) { double("GorgonBunny Exchange", :publish => nil) }
|
7
7
|
let(:bunny) { double("GorgonBunny", :start => nil, :queue => queue, :exchange => exchange) }
|
8
8
|
let(:logger) { double("Logger", :info => true, :datetime_format= => "")}
|
@@ -14,23 +14,6 @@ describe Listener do
|
|
14
14
|
Listener.any_instance.stub(:connection_information => connection_information)
|
15
15
|
end
|
16
16
|
|
17
|
-
describe "initialization" do
|
18
|
-
|
19
|
-
before do
|
20
|
-
Listener.any_instance.stub(:connect => nil, :initialize_personal_job_queue => nil)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "connects" do
|
24
|
-
Listener.any_instance.should_receive(:connect)
|
25
|
-
Listener.new
|
26
|
-
end
|
27
|
-
|
28
|
-
it "initializes the personal job queue" do
|
29
|
-
Listener.any_instance.should_receive(:initialize_personal_job_queue)
|
30
|
-
Listener.new
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
17
|
describe "logging to a file" do
|
35
18
|
context "passing a log file path in the configuration" do
|
36
19
|
before do
|
@@ -84,23 +67,36 @@ describe Listener do
|
|
84
67
|
|
85
68
|
describe "#initialize_personal_job_queue" do
|
86
69
|
it "creates the job queue" do
|
87
|
-
|
70
|
+
UUIDTools::UUID.stub(:timestamp_create => "abcd1234")
|
71
|
+
|
72
|
+
bunny.should_receive(:queue).with("job_queue_abcd1234", :auto_delete => true)
|
88
73
|
listener.initialize_personal_job_queue
|
89
74
|
end
|
90
75
|
|
91
|
-
it "
|
76
|
+
it "builds job_exchange_name using cluster_id from configuration" do
|
92
77
|
Listener.any_instance.stub(:configuration).and_return(:cluster_id => 'cluster5')
|
93
78
|
bunny.should_receive(:exchange).with('gorgon.jobs.cluster5', anything).and_return(exchange)
|
94
79
|
listener.initialize_personal_job_queue
|
95
80
|
end
|
96
81
|
|
97
|
-
it "binds the exchange to the queue. Uses gorgon.jobs if there is no
|
82
|
+
it "binds the exchange to the queue. Uses gorgon.jobs if there is no job_exchange_name in configuration" do
|
98
83
|
bunny.should_receive(:exchange).with("gorgon.jobs", :type => :fanout).and_return(exchange)
|
99
84
|
queue.should_receive(:bind).with(exchange)
|
100
85
|
listener.initialize_personal_job_queue
|
101
86
|
end
|
102
87
|
end
|
103
88
|
|
89
|
+
describe "#announce_readiness_to_originators" do
|
90
|
+
it "publishes data to the originator exchange" do
|
91
|
+
originator_exchange = double
|
92
|
+
|
93
|
+
bunny.should_receive(:exchange).with("gorgon.originators", :type => :fanout).and_return(originator_exchange)
|
94
|
+
originator_exchange.should_receive(:publish).with({:listener_queue_name => "some supposedly unique string"}.to_json)
|
95
|
+
|
96
|
+
listener.announce_readiness_to_originators
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
104
100
|
describe "#poll" do
|
105
101
|
|
106
102
|
let(:empty_queue) { [nil, nil, nil] }
|
@@ -65,19 +65,19 @@ describe OriginatorProtocol do
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
describe "#
|
68
|
+
describe "#publish_job_to_all" do
|
69
69
|
before do
|
70
70
|
connect_and_publish_files(@originator_p)
|
71
71
|
end
|
72
72
|
|
73
|
-
it "
|
73
|
+
it "adds queue's names to job_definition and fanout using 'gorgon.jobs' exchange" do
|
74
74
|
channel.should_receive(:fanout).with("gorgon.jobs")
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
expected_job_definition = JobDefinition.new
|
76
|
+
expected_job_definition.file_queue_name = "queue"
|
77
|
+
expected_job_definition.reply_exchange_name = "exchange"
|
78
78
|
|
79
|
-
exchange.should_receive(:publish).with(
|
80
|
-
@originator_p.
|
79
|
+
exchange.should_receive(:publish).with(expected_job_definition.to_json)
|
80
|
+
@originator_p.publish_job_to_all JobDefinition.new
|
81
81
|
end
|
82
82
|
|
83
83
|
it "uses cluster_id in job_queue_name, when it is specified" do
|
@@ -85,7 +85,24 @@ describe OriginatorProtocol do
|
|
85
85
|
|
86
86
|
channel.should_receive(:fanout).with("gorgon.jobs.cluster1")
|
87
87
|
|
88
|
-
originator_p.
|
88
|
+
originator_p.publish_job_to_all JobDefinition.new
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#publish_job_to_one" do
|
93
|
+
before do
|
94
|
+
connect_and_publish_files(@originator_p)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "publishes the job to the specified listener queue" do
|
98
|
+
expected_listener_queue_name = "abcd1234"
|
99
|
+
expected_job_definition = JobDefinition.new
|
100
|
+
expected_job_definition.file_queue_name = "queue"
|
101
|
+
expected_job_definition.reply_exchange_name = "exchange"
|
102
|
+
|
103
|
+
exchange.should_receive(:publish).with(expected_job_definition.to_json, {:routing_key => expected_listener_queue_name})
|
104
|
+
|
105
|
+
@originator_p.publish_job_to_one(JobDefinition.new, expected_listener_queue_name)
|
89
106
|
end
|
90
107
|
end
|
91
108
|
|
@@ -147,7 +164,7 @@ describe OriginatorProtocol do
|
|
147
164
|
|
148
165
|
it "deletes reply_exchange and reply and file queues" do
|
149
166
|
@originator_p.publish_files []
|
150
|
-
queue.should_receive(:delete).
|
167
|
+
queue.should_receive(:delete).exactly(3).times
|
151
168
|
exchange.should_receive(:delete)
|
152
169
|
@originator_p.disconnect
|
153
170
|
end
|
data/spec/originator_spec.rb
CHANGED
@@ -2,16 +2,17 @@ require 'gorgon/originator'
|
|
2
2
|
|
3
3
|
describe Originator do
|
4
4
|
let(:protocol){ double("Originator Protocol", :connect => nil, :publish_files => nil,
|
5
|
-
|
6
|
-
|
5
|
+
:publish_job_to_all => nil, :publish_job_to_one => nil, :receive_payloads => nil, :cancel_job => nil,
|
6
|
+
:disconnect => nil, :receive_new_listener_notifications => nil)}
|
7
7
|
|
8
8
|
let(:configuration){ {:job => {}, :files => ["some/file"], :file_server => {:host => 'host-name'}}}
|
9
9
|
let(:job_state){ double("JobState", :is_job_complete? => false, :file_finished => nil,
|
10
|
-
|
10
|
+
:add_observer => nil)}
|
11
11
|
let(:progress_bar_view){ double("Progress Bar View", :show => nil)}
|
12
12
|
let(:originator_logger){ double("Originator Logger", :log => nil, :log_message => nil)}
|
13
13
|
let(:source_tree_syncer) { double("Source Tree Syncer", :push => nil, :exclude= => nil, :success? => true,
|
14
|
-
|
14
|
+
:sys_command => 'command')}
|
15
|
+
let(:job_definition){ JobDefinition.new }
|
15
16
|
|
16
17
|
before do
|
17
18
|
OriginatorLogger.stub(:new).and_return originator_logger
|
@@ -146,6 +147,16 @@ describe Originator do
|
|
146
147
|
end
|
147
148
|
end
|
148
149
|
|
150
|
+
describe "#handle_new_listener_notification" do
|
151
|
+
it "re-publishes the job definition directly to the queue specified by the notification" do
|
152
|
+
stub_methods
|
153
|
+
@originator.publish
|
154
|
+
|
155
|
+
protocol.should_receive(:publish_job_to_one).with(job_definition, 'abcd1234')
|
156
|
+
@originator.handle_new_listener_notification({:listener_queue_name => 'abcd1234'}.to_json)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
149
160
|
describe "#job_definition" do
|
150
161
|
it "returns a JobDefinition object" do
|
151
162
|
@originator.stub(:configuration).and_return configuration
|
@@ -182,7 +193,7 @@ describe Originator do
|
|
182
193
|
OriginatorProtocol.stub(:new).and_return protocol
|
183
194
|
@originator.stub(:configuration).and_return configuration
|
184
195
|
@originator.stub(:connection_information).and_return 'host'
|
185
|
-
@originator.stub(:job_definition).and_return
|
196
|
+
@originator.stub(:job_definition).and_return job_definition
|
186
197
|
end
|
187
198
|
|
188
199
|
def start_payload
|
data/tutorial.md
CHANGED
@@ -21,7 +21,14 @@ Run `rspec` and make sure all tests pass.
|
|
21
21
|
|
22
22
|
1. Install [RabbitMQ](https://www.rabbitmq.com/download.html).
|
23
23
|
|
24
|
-
|
24
|
+
If you are using Homebrew on OSX, run the following:
|
25
|
+
|
26
|
+
```bash
|
27
|
+
brew install rabbitmq
|
28
|
+
/usr/local/sbin/rabbitmq-server # runs the server
|
29
|
+
```
|
30
|
+
|
31
|
+
2. Add Gorgon to the Gemfile: `gem 'gorgon', '0.8.4'`
|
25
32
|
|
26
33
|
3. `bundle`
|
27
34
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gorgon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Fitzsimmons
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2015-
|
15
|
+
date: 2015-05-04 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: rake
|