mobilize-base 1.0.55 → 1.0.75
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +12 -10
- data/lib/mobilize-base/extensions/google_drive/client_login_fetcher.rb +22 -11
- data/lib/mobilize-base/extensions/google_drive/file.rb +5 -1
- data/lib/mobilize-base/handlers/gdrive.rb +9 -0
- data/lib/mobilize-base/handlers/gfile.rb +1 -1
- data/lib/mobilize-base/handlers/gsheet.rb +3 -3
- data/lib/mobilize-base/models/job.rb +27 -3
- data/lib/mobilize-base/models/runner.rb +18 -10
- data/lib/mobilize-base/models/task.rb +33 -16
- data/lib/mobilize-base/version.rb +1 -1
- data/mobilize-base.gemspec +0 -1
- data/test/base_job_rows.yml +4 -4
- data/test/mobilize-base_test.rb +14 -5
- metadata +4 -20
data/README.md
CHANGED
@@ -390,7 +390,7 @@ Mobilize takes the environment from your Rails.env if you're running
|
|
390
390
|
Rails, or assumes "development." You can specify "development", "test",
|
391
391
|
or "production," as per the yml files.
|
392
392
|
|
393
|
-
Otherwise, it takes it from MOBILIZE_ENV parameter,
|
393
|
+
Otherwise, it takes it from MOBILIZE_ENV parameter, as in:
|
394
394
|
|
395
395
|
``` ruby
|
396
396
|
> ENV['MOBILIZE_ENV'] = 'production'
|
@@ -493,22 +493,24 @@ name>))` and enter values under each header:
|
|
493
493
|
* Tasks have this syntax: <handler>.<call> <params>.
|
494
494
|
* handler specifies the file that should receive the task
|
495
495
|
* the call specifies the method within the file. The method should
|
496
|
-
be called `"<
|
496
|
+
be called `"<handler>.<call>_by_task_path"`
|
497
497
|
* the params the method accepts, which are custom to each
|
498
|
-
task. These should be
|
499
|
-
|
498
|
+
task. These should be of the for `<key1>: <value1>, <key2>: <value2>`, where
|
499
|
+
`<key>` is an unquoted string and `<value>` is a quoted string, an
|
500
|
+
integer, an array (delimited by square braces), or a hash (delimited by
|
501
|
+
curly braces).
|
500
502
|
* For mobilize-base, the following tasks are available:
|
501
|
-
* gsheet.read
|
502
|
-
* The
|
503
|
+
* gsheet.read `source: <input_gsheet_full_path>`, which reads the sheet.
|
504
|
+
* The gsheet_full_path should be of the form `<gbook_name>/<gsheet_name>`. The test uses
|
503
505
|
"Requestor_mobilize(test)/base1_task1.in".
|
504
|
-
* gsheet.write
|
505
|
-
which writes the specified task output to the
|
506
|
-
* The
|
506
|
+
* gsheet.write `source: <task_relative_path>`,`target: <target_gsheet_path>`,
|
507
|
+
which writes the specified task output to the target_gsheet.
|
508
|
+
* The task_relative_path should be of the form `<task_column>` or
|
507
509
|
`<job_name/task_column>`. The test uses "base1/task1" for the first test
|
508
510
|
and simply "task1" for the second test. Both of these take the output
|
509
511
|
from the first task.
|
510
512
|
* The test uses "Requestor_mobilize(test)/base1.out" and
|
511
|
-
"Requestor_mobilize(test)/base2.out" for
|
513
|
+
"Requestor_mobilize(test)/base2.out" for target sheets.
|
512
514
|
|
513
515
|
<a name='section_Start_Run_Test'></a>
|
514
516
|
### Run Test
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module GoogleDrive
|
2
2
|
class ClientLoginFetcher
|
3
3
|
def request_raw(method, url, data, extra_header, auth)
|
4
|
+
clf = self
|
4
5
|
#this is patched to handle server errors due to http chaos
|
5
6
|
uri = URI.parse(url)
|
6
7
|
response = nil
|
@@ -14,22 +15,32 @@ module GoogleDrive
|
|
14
15
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
15
16
|
#set 600 to allow for large downloads
|
16
17
|
http.read_timeout = 600
|
17
|
-
response =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
response = begin
|
19
|
+
clf.http_call(http, method, uri, data, extra_header, auth)
|
20
|
+
rescue
|
21
|
+
#timeouts etc.
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
if response.nil?
|
25
|
+
attempts +=1
|
26
|
+
else
|
27
|
+
if response.code.ie{|rcode| rcode.starts_with?("4") or rcode.starts_with?("5")}
|
28
|
+
if response.body.downcase.index("rate limit") or response.body.downcase.index("captcha")
|
29
|
+
if sleep_time
|
30
|
+
sleep_time = sleep_time * attempts
|
31
|
+
else
|
32
|
+
sleep_time = (rand*100).to_i
|
33
|
+
end
|
22
34
|
else
|
23
|
-
sleep_time =
|
35
|
+
sleep_time = 10
|
24
36
|
end
|
25
|
-
|
26
|
-
sleep_time
|
37
|
+
attempts += 1
|
38
|
+
puts "Sleeping for #{sleep_time.to_s} due to #{response.body}"
|
39
|
+
sleep sleep_time
|
27
40
|
end
|
28
|
-
attempts += 1
|
29
|
-
puts "Sleeping for #{sleep_time.to_s} due to #{response.body}"
|
30
|
-
sleep sleep_time
|
31
41
|
end
|
32
42
|
end
|
43
|
+
raise "No response after 5 attempts" if response.nil?
|
33
44
|
raise response.body if response.code.ie{|rcode| rcode.starts_with?("4") or rcode.starts_with?("5")}
|
34
45
|
return response
|
35
46
|
end
|
@@ -54,7 +54,11 @@ module GoogleDrive
|
|
54
54
|
raise "Invalid role #{role}"
|
55
55
|
end
|
56
56
|
else
|
57
|
-
|
57
|
+
begin
|
58
|
+
f.acl.push({:scope_type=>"user",:scope=>email,:role=>role})
|
59
|
+
rescue => exc
|
60
|
+
raise exc unless exc.to_s.index("user already has access")
|
61
|
+
end
|
58
62
|
end
|
59
63
|
return true
|
60
64
|
end
|
@@ -54,6 +54,15 @@ module Mobilize
|
|
54
54
|
return false
|
55
55
|
end
|
56
56
|
|
57
|
+
def Gdrive.unslot_worker_by_path(path)
|
58
|
+
begin
|
59
|
+
Mobilize::Resque.set_worker_args_by_path(path,{'gdrive_slot'=>nil})
|
60
|
+
return true
|
61
|
+
rescue
|
62
|
+
return false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
57
66
|
def Gdrive.root(gdrive_slot=nil)
|
58
67
|
pw = Gdrive.password(gdrive_slot)
|
59
68
|
GoogleDrive.login(gdrive_slot,pw)
|
@@ -27,7 +27,7 @@ module Mobilize
|
|
27
27
|
gdrive_slot = Gdrive.slot_worker_by_path(t.path)
|
28
28
|
return false unless gdrive_slot
|
29
29
|
t = Task.where(:path=>task_path)
|
30
|
-
gfile_path = t.params
|
30
|
+
gfile_path = t.params['file']
|
31
31
|
Gfile.find_by_path(gfile_path,gdrive_slot).read
|
32
32
|
end
|
33
33
|
end
|
@@ -37,7 +37,7 @@ module Mobilize
|
|
37
37
|
gdrive_slot = Gdrive.slot_worker_by_path(task_path)
|
38
38
|
return false unless gdrive_slot
|
39
39
|
t = Task.where(:path=>task_path).first
|
40
|
-
gsheet_path = t.params
|
40
|
+
gsheet_path = t.params['source']
|
41
41
|
Gsheet.find_by_path(gsheet_path,gdrive_slot).to_tsv
|
42
42
|
end
|
43
43
|
|
@@ -46,8 +46,8 @@ module Mobilize
|
|
46
46
|
#return false if there are no emails available
|
47
47
|
return false unless gdrive_slot
|
48
48
|
t = Task.where(:path=>task_path).first
|
49
|
-
source = t.params
|
50
|
-
target_path = t.params
|
49
|
+
source = t.params['source']
|
50
|
+
target_path = t.params['target']
|
51
51
|
source_job_name, source_task_name = if source.index("/")
|
52
52
|
source.split("/")
|
53
53
|
else
|
@@ -5,8 +5,6 @@ module Mobilize
|
|
5
5
|
field :path, type: String
|
6
6
|
field :active, type: Boolean
|
7
7
|
field :trigger, type: String
|
8
|
-
field :status, type: String
|
9
|
-
field :last_completed_at, type: Time
|
10
8
|
|
11
9
|
index({ path: 1})
|
12
10
|
|
@@ -26,6 +24,32 @@ module Mobilize
|
|
26
24
|
return j
|
27
25
|
end
|
28
26
|
|
27
|
+
def status
|
28
|
+
#last task status
|
29
|
+
j = self
|
30
|
+
j.active_task.status
|
31
|
+
end
|
32
|
+
|
33
|
+
def active_task
|
34
|
+
j = self
|
35
|
+
#latest started at or first
|
36
|
+
j.tasks.select{|t| t.started_at}.sort_by{|t| t.started_at}.last || j.tasks.first
|
37
|
+
end
|
38
|
+
|
39
|
+
def completed_at
|
40
|
+
j = self
|
41
|
+
j.tasks.last.completed_at
|
42
|
+
end
|
43
|
+
|
44
|
+
def failed_at
|
45
|
+
j = self
|
46
|
+
j.tasks.last.failed_at
|
47
|
+
end
|
48
|
+
|
49
|
+
def status_at
|
50
|
+
j.active_task.status_at
|
51
|
+
end
|
52
|
+
|
29
53
|
#convenience methods
|
30
54
|
def runner
|
31
55
|
j = self
|
@@ -41,7 +65,7 @@ module Mobilize
|
|
41
65
|
def is_due?
|
42
66
|
j = self
|
43
67
|
return false if j.is_working? or j.active == false or j.trigger.to_s.starts_with?("after")
|
44
|
-
last_run = j.
|
68
|
+
last_run = j.completed_at
|
45
69
|
#check trigger
|
46
70
|
trigger = j.trigger
|
47
71
|
return true if trigger == 'once'
|
@@ -5,7 +5,9 @@ module Mobilize
|
|
5
5
|
field :path, type: String
|
6
6
|
field :active, type: Boolean
|
7
7
|
field :status, type: String
|
8
|
-
field :
|
8
|
+
field :started_at, type: Time
|
9
|
+
field :status_at, type: Time
|
10
|
+
field :completed_at, type: Time
|
9
11
|
|
10
12
|
index({ path: 1})
|
11
13
|
|
@@ -13,9 +15,9 @@ module Mobilize
|
|
13
15
|
%w{name active trigger status task1 task2 task3 task4 task5}
|
14
16
|
end
|
15
17
|
|
16
|
-
def
|
18
|
+
def cached_at
|
17
19
|
r = self
|
18
|
-
Dataset.find_or_create_by_path(r.path).
|
20
|
+
Dataset.find_or_create_by_path(r.path).cached_at
|
19
21
|
end
|
20
22
|
|
21
23
|
def worker
|
@@ -51,12 +53,12 @@ module Mobilize
|
|
51
53
|
j.tasks.first.enqueue!
|
52
54
|
end
|
53
55
|
rescue ScriptError, StandardError => exc
|
54
|
-
|
56
|
+
r.update_status("Failed to enqueue #{j.path} with #{exc.to_s}")
|
55
57
|
j.update_attributes(:active=>false)
|
56
58
|
end
|
57
59
|
end
|
58
60
|
r.update_gsheet(gdrive_slot)
|
59
|
-
r.update_attributes(:
|
61
|
+
r.update_attributes(:completed_at=>Time.now.utc)
|
60
62
|
end
|
61
63
|
|
62
64
|
def dataset
|
@@ -77,7 +79,7 @@ module Mobilize
|
|
77
79
|
r = self
|
78
80
|
jobs_sheet = Gsheet.find_or_create_by_path(r.path,gdrive_slot)
|
79
81
|
jobs_sheet.add_headers(r.headers)
|
80
|
-
jobs_sheet.delete_sheet1
|
82
|
+
begin;jobs_sheet.delete_sheet1;rescue;end #don't care if sheet1 deletion fails
|
81
83
|
return jobs_sheet
|
82
84
|
end
|
83
85
|
|
@@ -128,7 +130,12 @@ module Mobilize
|
|
128
130
|
def update_gsheet(gdrive_slot)
|
129
131
|
r = self
|
130
132
|
jobs_gsheet = r.gsheet(gdrive_slot)
|
131
|
-
|
133
|
+
upd_jobs = r.jobs.select do |j|
|
134
|
+
j.completed_at.nil? ||
|
135
|
+
j.completed_at > j.runner.completed_at ||
|
136
|
+
(j.failed_at and j.failed_at > j.runner.completed_at)
|
137
|
+
end
|
138
|
+
upd_rows = upd_jobs.map{|j| {'name'=>j.name, 'active'=>j.active, 'status'=>j.status}}
|
132
139
|
jobs_gsheet.add_or_update_rows(upd_rows)
|
133
140
|
r.update_status("gsheet updated")
|
134
141
|
return true
|
@@ -152,7 +159,7 @@ module Mobilize
|
|
152
159
|
|
153
160
|
def update_status(msg)
|
154
161
|
r = self
|
155
|
-
r.update_attributes(:status=>msg)
|
162
|
+
r.update_attributes(:status=>msg, :status_at=>Time.now.utc)
|
156
163
|
Mobilize::Resque.set_worker_args_by_path(r.path,{'status'=>msg})
|
157
164
|
return true
|
158
165
|
end
|
@@ -165,12 +172,13 @@ module Mobilize
|
|
165
172
|
def is_due?
|
166
173
|
r = self.reload
|
167
174
|
return false if r.is_working?
|
168
|
-
|
169
|
-
return true if r.
|
175
|
+
prev_due_time = Time.now.utc - Jobtracker.runner_read_freq
|
176
|
+
return true if r.started_at.nil? or r.started_at < prev_due_time
|
170
177
|
end
|
171
178
|
|
172
179
|
def enqueue!
|
173
180
|
r = self
|
181
|
+
r.update_attributes(:started_at=>Time.now.utc)
|
174
182
|
::Resque::Job.create("mobilize",Runner,r.path,{})
|
175
183
|
return true
|
176
184
|
end
|
@@ -7,8 +7,10 @@ module Mobilize
|
|
7
7
|
field :call, type: String
|
8
8
|
field :param_string, type: Array
|
9
9
|
field :status, type: String
|
10
|
-
field :
|
11
|
-
field :
|
10
|
+
field :completed_at, type: Time
|
11
|
+
field :started_at, type: Time
|
12
|
+
field :failed_at, type: Time
|
13
|
+
field :status_at, type: Time
|
12
14
|
|
13
15
|
index({ path: 1})
|
14
16
|
|
@@ -27,13 +29,22 @@ module Mobilize
|
|
27
29
|
Dataset.find_or_create_by_handler_and_path("gridfs","#{t.path}/stderr")
|
28
30
|
end
|
29
31
|
|
32
|
+
def log_dataset
|
33
|
+
t = self
|
34
|
+
Dataset.find_or_create_by_handler_and_path("gridfs","#{t.path}/log")
|
35
|
+
end
|
36
|
+
|
30
37
|
def params
|
31
38
|
t = self
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
39
|
+
#evaluates param_string to ruby hash
|
40
|
+
#using YAML parser
|
41
|
+
#TODO: eliminate ridiculousness
|
42
|
+
begin
|
43
|
+
YAML.load(t.param_string)
|
44
|
+
raise "Must resolve to Hash" unless result.class==Hash
|
45
|
+
rescue
|
46
|
+
sub_param_string = t.param_string.gsub(":\"",": \"").gsub(":'",": '").gsub(":[",": [").gsub(":{",": {").gsub(/(:[0-9])/,'taskparamsgsub\1').gsub('taskparamsgsub:',': ')
|
47
|
+
YAML.load("{#{sub_param_string}}")
|
37
48
|
end
|
38
49
|
end
|
39
50
|
|
@@ -69,12 +80,16 @@ module Mobilize
|
|
69
80
|
t.update_status(%{Starting at #{Time.now.utc}})
|
70
81
|
stdout, stderr = [nil,nil]
|
71
82
|
begin
|
72
|
-
stdout = "Mobilize::#{t.handler.humanize}".constantize.send("#{t.call}_by_task_path",t.path).to_s
|
83
|
+
stdout,log = "Mobilize::#{t.handler.humanize}".constantize.send("#{t.call}_by_task_path",t.path).to_s
|
84
|
+
#write to log if method returns an array w 2 members
|
85
|
+
t.log_dataset.write_cache(log) if log
|
73
86
|
rescue ScriptError, StandardError => exc
|
74
87
|
stderr = [exc.to_s,exc.backtrace.to_s].join("\n")
|
75
|
-
#record the failure in Job so it appears on Runner
|
76
|
-
|
77
|
-
|
88
|
+
#record the failure in Job so it appears on Runner, turn it off
|
89
|
+
#so it doesn't run again
|
90
|
+
j.update_attributes(:active=>false)
|
91
|
+
t.update_attributes(:failed_at=>Time.now.utc)
|
92
|
+
t.update_status("Failed at #{Time.now.utc.to_s}")
|
78
93
|
raise exc
|
79
94
|
end
|
80
95
|
if stdout == false
|
@@ -86,12 +101,13 @@ module Mobilize
|
|
86
101
|
t.stdout_dataset.write_cache(stdout)
|
87
102
|
t.update_attributes(:status=>"Completed at #{Time.now.utc.to_s}")
|
88
103
|
if t.idx == j.tasks.length
|
89
|
-
|
90
|
-
j.update_attributes(:active=>false) if j.trigger.strip == "once"
|
91
|
-
t.update_attributes(:
|
104
|
+
#job has completed
|
105
|
+
j.update_attributes(:active=>false) if j.trigger.strip.downcase == "once"
|
106
|
+
t.update_attributes(:completed_at=>Time.now.utc)
|
107
|
+
t.update_status("Completed at #{Time.now.utc.to_s}")
|
92
108
|
#check for any dependent jobs, if there are, enqueue them
|
93
109
|
r = j.runner
|
94
|
-
dep_jobs = r.jobs.select{|dj| dj.active==true and dj.trigger=="after #{j.name}"}
|
110
|
+
dep_jobs = r.jobs.select{|dj| dj.active==true and dj.trigger.strip.downcase == "after #{j.name}"}
|
95
111
|
#put begin/rescue so all dependencies run
|
96
112
|
dep_jobs.each{|dj| begin;dj.tasks.first.enqueue! unless dj.is_working?;rescue;end}
|
97
113
|
else
|
@@ -103,6 +119,7 @@ module Mobilize
|
|
103
119
|
|
104
120
|
def enqueue!
|
105
121
|
t = self
|
122
|
+
t.update_attributes(:started_at=>Time.now.utc)
|
106
123
|
::Resque::Job.create("mobilize",Task,t.path,{})
|
107
124
|
return true
|
108
125
|
end
|
@@ -124,7 +141,7 @@ module Mobilize
|
|
124
141
|
|
125
142
|
def update_status(msg)
|
126
143
|
t = self
|
127
|
-
t.update_attributes(:status=>msg)
|
144
|
+
t.update_attributes(:status=>msg,:status_at=>Time.now.utc)
|
128
145
|
Mobilize::Resque.set_worker_args_by_path(t.path,{'status'=>msg})
|
129
146
|
return true
|
130
147
|
end
|
data/mobilize-base.gemspec
CHANGED
@@ -29,7 +29,6 @@ Gem::Specification.new do |s|
|
|
29
29
|
s.add_runtime_dependency 'redis',"~>3.0.0"
|
30
30
|
s.add_runtime_dependency 'resque','1.21.0'
|
31
31
|
s.add_runtime_dependency 'google_drive','0.3.2'
|
32
|
-
s.add_runtime_dependency 'bluepill','0.0.60'
|
33
32
|
s.add_runtime_dependency 'popen4','0.1.2'
|
34
33
|
s.add_runtime_dependency 'actionmailer','3.1.1'
|
35
34
|
end
|
data/test/base_job_rows.yml
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
active: true
|
3
3
|
trigger: once
|
4
4
|
status: ""
|
5
|
-
task1: 'gsheet.read "Runner_mobilize(test)/base1_task1.in"'
|
6
|
-
task2: 'gsheet.write "base1/task1", "Runner_mobilize(test)/base1.out"'
|
5
|
+
task1: 'gsheet.read source:"Runner_mobilize(test)/base1_task1.in"'
|
6
|
+
task2: 'gsheet.write source:"base1/task1", target:"Runner_mobilize(test)/base1.out"'
|
7
7
|
|
8
8
|
- name: "base2"
|
9
9
|
active: true
|
10
10
|
trigger: "after base1"
|
11
11
|
status: ""
|
12
|
-
task1: 'gsheet.read "Runner_mobilize(test)/base1.out"'
|
13
|
-
task2: 'gsheet.write "task1", "Runner_mobilize(test)/base2.out"'
|
12
|
+
task1: 'gsheet.read source:"Runner_mobilize(test)/base1.out"'
|
13
|
+
task2: 'gsheet.write source:"task1", target:"Runner_mobilize(test)/base2.out"'
|
data/test/mobilize-base_test.rb
CHANGED
@@ -44,15 +44,24 @@ describe "Mobilize" do
|
|
44
44
|
puts "add row to jobs sheet, wait 120s"
|
45
45
|
test_job_rows = ::YAML.load_file("#{Mobilize::Base.root}/test/base_job_rows.yml")
|
46
46
|
jobs_sheet.add_or_update_rows(test_job_rows)
|
47
|
-
|
48
|
-
puts "job row added, force enqueued runner"
|
49
|
-
r.enqueue!
|
50
47
|
sleep 120
|
51
48
|
|
52
49
|
puts "jobtracker posted test sheet data to test destination, and checksum succeeded?"
|
53
|
-
|
50
|
+
test_target_sheet_1 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1.out",gdrive_slot)
|
51
|
+
test_target_sheet_1 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1.out",gdrive_slot)
|
52
|
+
|
53
|
+
|
54
|
+
assert test_target_sheet_1.to_tsv == test_source_sheet.to_tsv
|
54
55
|
|
55
|
-
|
56
|
+
puts "delete both output sheets, set first job to active=true"
|
57
|
+
test_target_sheet_1.delete
|
58
|
+
|
59
|
+
jobs_sheet.add_or_update_rows([{'name'=>'base1','active'=>true}])
|
60
|
+
sleep 90
|
61
|
+
|
62
|
+
test_target_sheet_2 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1.out",gdrive_slot)
|
63
|
+
puts "jobtracker posted test sheet data to test destination, and checksum succeeded?"
|
64
|
+
assert test_target_sheet_2.to_tsv == test_source_sheet.to_tsv
|
56
65
|
|
57
66
|
end
|
58
67
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mobilize-base
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.75
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -139,22 +139,6 @@ dependencies:
|
|
139
139
|
- - '='
|
140
140
|
- !ruby/object:Gem::Version
|
141
141
|
version: 0.3.2
|
142
|
-
- !ruby/object:Gem::Dependency
|
143
|
-
name: bluepill
|
144
|
-
requirement: !ruby/object:Gem::Requirement
|
145
|
-
none: false
|
146
|
-
requirements:
|
147
|
-
- - '='
|
148
|
-
- !ruby/object:Gem::Version
|
149
|
-
version: 0.0.60
|
150
|
-
type: :runtime
|
151
|
-
prerelease: false
|
152
|
-
version_requirements: !ruby/object:Gem::Requirement
|
153
|
-
none: false
|
154
|
-
requirements:
|
155
|
-
- - '='
|
156
|
-
- !ruby/object:Gem::Version
|
157
|
-
version: 0.0.60
|
158
142
|
- !ruby/object:Gem::Dependency
|
159
143
|
name: popen4
|
160
144
|
requirement: !ruby/object:Gem::Requirement
|
@@ -254,7 +238,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
254
238
|
version: '0'
|
255
239
|
segments:
|
256
240
|
- 0
|
257
|
-
hash:
|
241
|
+
hash: 2930191387184106525
|
258
242
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
259
243
|
none: false
|
260
244
|
requirements:
|
@@ -263,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
263
247
|
version: '0'
|
264
248
|
segments:
|
265
249
|
- 0
|
266
|
-
hash:
|
250
|
+
hash: 2930191387184106525
|
267
251
|
requirements: []
|
268
252
|
rubyforge_project: mobilize-base
|
269
253
|
rubygems_version: 1.8.24
|