mobilize-base 1.0.55 → 1.0.75
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.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
|