ripe 0.2.0 → 0.2.1
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 +4 -4
- data/.travis.yml +0 -5
- data/Guardfile +2 -4
- data/README.md +1 -0
- data/bin/ripe +1 -59
- data/lib/ripe.rb +8 -1
- data/lib/ripe/blocks.rb +13 -0
- data/lib/ripe/blocks/block.rb +142 -0
- data/lib/ripe/blocks/liquid_block.rb +48 -0
- data/lib/ripe/blocks/multi_block.rb +71 -0
- data/lib/ripe/blocks/parallel_block.rb +29 -0
- data/lib/ripe/blocks/serial_block.rb +61 -0
- data/lib/ripe/blocks/working_block.rb +101 -0
- data/lib/ripe/cli.rb +121 -0
- data/lib/ripe/cli/helper.rb +31 -0
- data/lib/ripe/db.rb +7 -0
- data/lib/ripe/db/task.rb +42 -0
- data/lib/ripe/db/task_migration.rb +33 -0
- data/lib/ripe/db/worker.rb +64 -0
- data/lib/ripe/db/worker_migration.rb +41 -0
- data/lib/ripe/dsl.rb +2 -4
- data/lib/ripe/dsl/task_dsl.rb +4 -2
- data/lib/ripe/dsl/workflow_dsl.rb +5 -0
- data/lib/ripe/library.rb +34 -45
- data/lib/ripe/repo.rb +24 -23
- data/lib/ripe/version.rb +1 -1
- data/lib/ripe/worker_controller.rb +72 -144
- data/lib/ripe/worker_controller/preparer.rb +172 -0
- data/lib/ripe/worker_controller/syncer.rb +118 -0
- data/spec/cli_spec.rb +14 -0
- data/spec/library_spec.rb +18 -18
- data/spec/spec_helper.rb +2 -0
- data/spec/testpack.rb +16 -5
- data/spec/testpack/.ripe/meta.db +0 -0
- data/spec/testpack/.ripe/tasks/bar.sh +3 -0
- data/spec/testpack/{ripe → .ripe}/tasks/foo.sh +0 -0
- data/spec/testpack/.ripe/workers/1/1.sh +16 -0
- data/spec/testpack/.ripe/workers/1/2.sh +16 -0
- data/spec/testpack/.ripe/workers/1/job.sh +54 -0
- data/spec/testpack/.ripe/workers/2/3.sh +16 -0
- data/spec/testpack/.ripe/workers/2/4.sh +16 -0
- data/spec/testpack/.ripe/workers/2/job.sh +54 -0
- data/spec/testpack/.ripe/workers/3/5.sh +16 -0
- data/spec/testpack/.ripe/workers/3/6.sh +16 -0
- data/spec/testpack/.ripe/workers/3/job.sh +54 -0
- data/spec/testpack/.ripe/workflows/foobar.rb +23 -0
- data/spec/testpack/{case/Sample1 → Sample1}/bar_output.txt +0 -0
- data/spec/testpack/{case/Sample1 → Sample1}/foo_input.txt +0 -0
- data/spec/testpack/{case/Sample1 → Sample1}/foo_output.txt +0 -0
- data/spec/testpack/{case/Sample2 → Sample2}/bar_output.txt +0 -0
- data/spec/testpack/{case/Sample2 → Sample2}/foo_input.txt +0 -0
- data/spec/testpack/{case/Sample2 → Sample2}/foo_output.txt +0 -0
- data/spec/testpack/{case/Sample3 → Sample3}/bar_output.txt +0 -0
- data/spec/testpack/{case/Sample3 → Sample3}/foo_input.txt +0 -0
- data/spec/testpack/{case/Sample3 → Sample3}/foo_output.txt +0 -0
- data/spec/worker_controller_spec.rb +143 -0
- metadata +66 -40
- data/lib/ripe/block.rb +0 -41
- data/lib/ripe/liquid_block.rb +0 -17
- data/lib/ripe/multi_block.rb +0 -35
- data/lib/ripe/parallel_block.rb +0 -13
- data/lib/ripe/serial_block.rb +0 -37
- data/lib/ripe/task.rb +0 -21
- data/lib/ripe/task_migration.rb +0 -18
- data/lib/ripe/worker.rb +0 -44
- data/lib/ripe/worker_migration.rb +0 -26
- data/lib/ripe/working_block.rb +0 -41
- data/spec/block_spec.rb +0 -7
- data/spec/ripe_spec.rb +0 -7
- data/spec/testpack/ripe/tasks/bar.sh +0 -3
- data/spec/testpack/ripe/workflows/foobar.rb +0 -23
data/lib/ripe/dsl.rb
CHANGED
data/lib/ripe/dsl/task_dsl.rb
CHANGED
|
@@ -33,16 +33,18 @@ module Ripe
|
|
|
33
33
|
# @return [WorkingBlock, nil]
|
|
34
34
|
|
|
35
35
|
def task(handle, &block)
|
|
36
|
-
filename =
|
|
36
|
+
filename = Library.find(:task, handle)
|
|
37
37
|
abort "Could not find task #{handle}." if filename == nil
|
|
38
38
|
|
|
39
39
|
params = TaskDSL.new(handle, &block).params
|
|
40
|
-
WorkingBlock.new(filename, params)
|
|
40
|
+
Blocks::WorkingBlock.new(filename, params)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
##
|
|
44
44
|
# This class provides a DSL for defining a task. It should only be called
|
|
45
45
|
# by #task.
|
|
46
|
+
#
|
|
47
|
+
# @attr_reader params [Hash<Symbol, String>] list of parameters
|
|
46
48
|
|
|
47
49
|
class TaskDSL
|
|
48
50
|
|
|
@@ -37,6 +37,11 @@ module Ripe
|
|
|
37
37
|
##
|
|
38
38
|
# This class provides a DSL for defining a workflow. It should only be
|
|
39
39
|
# called by #workflow.
|
|
40
|
+
#
|
|
41
|
+
# @attr_reader handle [String] the name of the workflow
|
|
42
|
+
# @attr_reader params [Hash<Symbol, String>] list of parameters
|
|
43
|
+
# @attr_reader callback [Proc] the block describing what to do apply to
|
|
44
|
+
# each sample
|
|
40
45
|
|
|
41
46
|
class WorkflowDSL
|
|
42
47
|
|
data/lib/ripe/library.rb
CHANGED
|
@@ -1,59 +1,48 @@
|
|
|
1
1
|
module Ripe
|
|
2
2
|
|
|
3
3
|
##
|
|
4
|
-
# This class represents a library containing all the components
|
|
5
|
-
# to ripe (tasks and workflows) based on what is contained in the
|
|
6
|
-
# environment variable.
|
|
4
|
+
# This singleton class represents a library containing all the components
|
|
5
|
+
# accessible to ripe (tasks and workflows) based on what is contained in the
|
|
6
|
+
# +RIPELIB+ environment variable.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
module Library
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
class << self
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
##
|
|
13
|
+
# Provide a list of search paths
|
|
14
|
+
#
|
|
15
|
+
# @return [List] Return the list of search paths
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@paths = "#{Dir.pwd}/#{Repo::REPOSITORY_PATH}:#{ENV['RIPELIB']}".split(/:/)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
##
|
|
24
|
-
# Search throughout the library for a task component by the name of
|
|
25
|
-
# +handle+. When there is more than one match, give precendence to the
|
|
26
|
-
# component whose path is declared first.
|
|
27
|
-
#
|
|
28
|
-
# @param handle [String] Task to search for
|
|
29
|
-
# @return [String, nil] Return the full path of the component if found,
|
|
30
|
-
# and +nil+ otherwise.
|
|
31
|
-
|
|
32
|
-
def find_task(handle)
|
|
33
|
-
search = @paths.map do |path|
|
|
34
|
-
filename = "#{path}/tasks/#{handle}.sh"
|
|
35
|
-
(File.exists? filename) ? filename : nil
|
|
17
|
+
def paths
|
|
18
|
+
# Prepend the working directory to the list of paths so that the
|
|
19
|
+
# working directory is always looked in first.
|
|
20
|
+
"#{Dir.pwd}/#{Repo::REPOSITORY_PATH}:#{ENV['RIPELIB']}".split(/:/)
|
|
36
21
|
end
|
|
37
22
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
23
|
+
##
|
|
24
|
+
# Search throughout the library for a task or workflow component by the
|
|
25
|
+
# name of +handle+. When there is more than one match, give precendence to
|
|
26
|
+
# component whose path is declared first.
|
|
27
|
+
#
|
|
28
|
+
# @param comp [Symbol] Type of component: either +:workflow+ or
|
|
29
|
+
# +:task+.
|
|
30
|
+
# @param handle [String] Name of component
|
|
31
|
+
# @return [String, nil] Full path of the component if found, and +nil+
|
|
32
|
+
# otherwise.
|
|
33
|
+
|
|
34
|
+
def find(comp, handle)
|
|
35
|
+
ext = { task: 'sh',
|
|
36
|
+
workflow: 'rb' }
|
|
37
|
+
|
|
38
|
+
search = paths.map do |path|
|
|
39
|
+
filename = "#{path}/#{comp}s/#{handle}.#{ext[comp]}"
|
|
40
|
+
(File.exists? filename) ? filename : nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
search.compact.first
|
|
54
44
|
end
|
|
55
45
|
|
|
56
|
-
search.compact.first
|
|
57
46
|
end
|
|
58
47
|
|
|
59
48
|
end
|
data/lib/ripe/repo.rb
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
require 'active_record'
|
|
2
2
|
require 'fileutils'
|
|
3
|
-
require_relative 'block'
|
|
4
|
-
require_relative 'worker'
|
|
5
|
-
require_relative 'worker_controller'
|
|
6
|
-
require_relative 'worker_migration'
|
|
7
|
-
require_relative 'working_block'
|
|
8
|
-
require_relative 'library'
|
|
9
|
-
require_relative 'liquid_block'
|
|
10
|
-
require_relative 'task'
|
|
11
|
-
require_relative 'task_migration'
|
|
12
3
|
|
|
13
4
|
module Ripe
|
|
14
5
|
|
|
@@ -17,11 +8,12 @@ module Ripe
|
|
|
17
8
|
# +git+ repository and is the starting point of the package. It
|
|
18
9
|
# instantiates:
|
|
19
10
|
#
|
|
20
|
-
# * a library containing information as to where to retrieve ripe
|
|
21
|
-
# components such as tasks and workflows;
|
|
22
11
|
# * a database that contains all worker metadata; and
|
|
23
|
-
# * a controller that communicates the database
|
|
24
|
-
# interface.
|
|
12
|
+
# * a controller that communicates with both the database and the compute
|
|
13
|
+
# cluster interface.
|
|
14
|
+
#
|
|
15
|
+
# @attr_reader controller [WorkerController] a controller that communicates
|
|
16
|
+
# with both the database and the computer cluster interface.
|
|
25
17
|
#
|
|
26
18
|
# @see Ripe::WorkerController
|
|
27
19
|
# @see Ripe::Library
|
|
@@ -32,19 +24,27 @@ module Ripe
|
|
|
32
24
|
DATABASE_PATH = "#{REPOSITORY_PATH}/meta.db"
|
|
33
25
|
WORKERS_PATH = "#{REPOSITORY_PATH}/workers"
|
|
34
26
|
|
|
35
|
-
attr_reader :
|
|
27
|
+
attr_reader :controller
|
|
36
28
|
|
|
37
29
|
##
|
|
38
|
-
# Initialize a repository
|
|
30
|
+
# Initialize a repository.
|
|
39
31
|
|
|
40
32
|
def initialize
|
|
41
|
-
@has_repository = File.exists?
|
|
42
|
-
@
|
|
43
|
-
|
|
33
|
+
@has_repository = File.exists? DATABASE_PATH
|
|
34
|
+
@controller = WorkerController.new
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Return whether the ripe repository exists.
|
|
39
|
+
#
|
|
40
|
+
# @return [Boolean] whether the repository exists
|
|
41
|
+
|
|
42
|
+
def has_repository?
|
|
43
|
+
@has_repository
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
##
|
|
47
|
-
# Attach to an existing database
|
|
47
|
+
# Attach to an existing database.
|
|
48
48
|
|
|
49
49
|
def attach
|
|
50
50
|
ActiveRecord::Base.establish_connection({
|
|
@@ -54,14 +54,15 @@ module Ripe
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
##
|
|
57
|
-
# Attach to an existing database, and creates one if
|
|
57
|
+
# Attach to an existing database, and creates one if a database cannot be
|
|
58
|
+
# found.
|
|
58
59
|
|
|
59
60
|
def attach_or_create
|
|
60
61
|
@has_repository ? attach : create
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
##
|
|
64
|
-
# Create a database
|
|
65
|
+
# Create a database.
|
|
65
66
|
|
|
66
67
|
def create
|
|
67
68
|
FileUtils.mkdir_p(REPOSITORY_PATH)
|
|
@@ -71,8 +72,8 @@ module Ripe
|
|
|
71
72
|
attach
|
|
72
73
|
|
|
73
74
|
# Create the tables
|
|
74
|
-
WorkerMigration.up
|
|
75
|
-
TaskMigration.up
|
|
75
|
+
DB::WorkerMigration.up
|
|
76
|
+
DB::TaskMigration.up
|
|
76
77
|
rescue
|
|
77
78
|
destroy
|
|
78
79
|
end
|
data/lib/ripe/version.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
require_relative '
|
|
1
|
+
require_relative 'worker_controller/preparer'
|
|
2
|
+
require_relative 'worker_controller/syncer'
|
|
3
3
|
|
|
4
4
|
module Ripe
|
|
5
5
|
|
|
@@ -10,177 +10,105 @@ module Ripe
|
|
|
10
10
|
|
|
11
11
|
class WorkerController
|
|
12
12
|
|
|
13
|
-
include Singleton
|
|
14
|
-
|
|
15
13
|
##
|
|
16
|
-
#
|
|
14
|
+
# Prepare workers by applying the workflow callback and its parameters to
|
|
17
15
|
# each sample.
|
|
18
16
|
#
|
|
19
17
|
# @see Ripe::DSL::WorkflowDSL#describe
|
|
18
|
+
# @see Ripe::WorkerController::Preparer
|
|
20
19
|
#
|
|
21
|
-
# @param
|
|
22
|
-
# @
|
|
23
|
-
# of sample and a hash of parameters provided by the workflow and by the
|
|
24
|
-
# command line.
|
|
25
|
-
# @param vars [Hash] a list of worker-wide parameters
|
|
26
|
-
|
|
27
|
-
def prepare(samples, callback, vars = {})
|
|
28
|
-
vars = {
|
|
29
|
-
wd: Dir.pwd,
|
|
30
|
-
mode: :patch,
|
|
31
|
-
group_num: 1,
|
|
32
|
-
}.merge(vars)
|
|
33
|
-
|
|
34
|
-
return if ![:patch, :force, :depend].include? vars[:mode].to_sym
|
|
35
|
-
|
|
36
|
-
samples = samples.map do |sample|
|
|
37
|
-
block = callback.call(sample, vars).prune(vars[:mode].to_sym == :force,
|
|
38
|
-
vars[:mode].to_sym == :depend)
|
|
39
|
-
if block != nil
|
|
40
|
-
puts "Preparing sample #{sample}"
|
|
41
|
-
[sample, block]
|
|
42
|
-
else
|
|
43
|
-
puts "Nothing to do for sample #{sample}"
|
|
44
|
-
nil
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
samples = samples.compact
|
|
48
|
-
|
|
49
|
-
samples.each_slice(vars[:group_num].to_i).map do |worker_samples|
|
|
50
|
-
worker = Worker.create(handle: vars[:handle])
|
|
51
|
-
|
|
52
|
-
blocks = worker_samples.map do |sample, block|
|
|
53
|
-
# Preorder traversal of blocks -- assign incremental numbers starting from
|
|
54
|
-
# 1 to each node as it is being traversed, as well as producing the job
|
|
55
|
-
# file for each task.
|
|
56
|
-
post_var_assign = lambda do |subblock|
|
|
57
|
-
if subblock.blocks.length == 0
|
|
58
|
-
# This section is only called when the subblock is actually a working
|
|
59
|
-
# block (a leaf in the block arborescence).
|
|
60
|
-
task = worker.tasks.create({
|
|
61
|
-
sample: sample,
|
|
62
|
-
block: subblock.id,
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
file = File.new(task.sh, 'w')
|
|
66
|
-
file.puts subblock.command
|
|
67
|
-
file.close
|
|
68
|
-
|
|
69
|
-
subblock.vars.merge!(log: task.log)
|
|
70
|
-
else
|
|
71
|
-
subblock.blocks.each(&post_var_assign)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
post_var_assign.call(block)
|
|
76
|
-
block
|
|
77
|
-
end
|
|
20
|
+
# @param (see Preparer#initialize)
|
|
21
|
+
# @return [Array<Worker>] workers prepared in current batch
|
|
78
22
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
stdout: worker.stdout,
|
|
82
|
-
stderr: worker.stderr,
|
|
83
|
-
command: SerialBlock.new(*blocks).command,
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
file = File.new(worker.sh, 'w')
|
|
87
|
-
file.puts LiquidBlock.new("#{PATH}/share/moab.sh", vars).command
|
|
88
|
-
file.close
|
|
89
|
-
|
|
90
|
-
worker.update({
|
|
91
|
-
status: :prepared,
|
|
92
|
-
ppn: vars[:ppn],
|
|
93
|
-
queue: vars[:queue],
|
|
94
|
-
walltime: vars[:walltime],
|
|
95
|
-
})
|
|
96
|
-
worker
|
|
97
|
-
end
|
|
23
|
+
def prepare(workflow, samples, params = {})
|
|
24
|
+
Preparer.new(workflow, samples, params).workers
|
|
98
25
|
end
|
|
99
26
|
|
|
100
27
|
##
|
|
101
|
-
#
|
|
28
|
+
# Apply a block to a list of workers.
|
|
102
29
|
#
|
|
103
|
-
# @param
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
30
|
+
# @param workers [Array<DB::Worker>, DB::Worker] a list of workers or a
|
|
31
|
+
# single worker
|
|
32
|
+
# @return [Array<DB::Worker>] the list of workers given in arguments,
|
|
33
|
+
# with modified states
|
|
34
|
+
|
|
35
|
+
def distribute(workers, &block)
|
|
36
|
+
workers = [workers] if workers.is_a? DB::Worker
|
|
37
|
+
workers.map { |w| block.call(w) }
|
|
108
38
|
end
|
|
109
39
|
|
|
110
40
|
##
|
|
111
|
-
#
|
|
41
|
+
# Run worker job code into bash locally.
|
|
112
42
|
#
|
|
113
|
-
# @param
|
|
43
|
+
# @param (see #distribute)
|
|
44
|
+
# @return (see #distribute)
|
|
114
45
|
|
|
115
|
-
def
|
|
116
|
-
|
|
117
|
-
|
|
46
|
+
def local(workers)
|
|
47
|
+
distribute workers do |worker|
|
|
48
|
+
`bash #{worker.sh}`
|
|
49
|
+
end
|
|
118
50
|
end
|
|
119
51
|
|
|
120
52
|
##
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
53
|
+
# Submit worker jobs to the compute cluster system.
|
|
54
|
+
#
|
|
55
|
+
# @param (see #distribute)
|
|
56
|
+
# @return (see #distribute)
|
|
57
|
+
|
|
58
|
+
def start(workers)
|
|
59
|
+
distribute workers do |worker|
|
|
60
|
+
if worker.status == 'prepared'
|
|
61
|
+
worker.update(status: :queueing,
|
|
62
|
+
moab_id: `qsub '#{worker.sh}'`.strip.split(/\./).first)
|
|
63
|
+
else
|
|
64
|
+
puts "Worker #{worker.id} could not be started: not prepared"
|
|
133
65
|
end
|
|
134
66
|
end
|
|
67
|
+
end
|
|
135
68
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
status: status, # Queued jobs that appear become either idle, blocked or active
|
|
150
|
-
})
|
|
151
|
-
end
|
|
69
|
+
##
|
|
70
|
+
# Cancel worker jobs in the compute cluster system.
|
|
71
|
+
#
|
|
72
|
+
# @param (see #distribute)
|
|
73
|
+
# @return (see #distribute)
|
|
74
|
+
|
|
75
|
+
def cancel(workers)
|
|
76
|
+
distribute workers do |worker|
|
|
77
|
+
if ['queueing', 'idle', 'blocked', 'active'].include? worker.status
|
|
78
|
+
`canceljob #{worker.moab_id}`
|
|
79
|
+
worker.update(status: :cancelled)
|
|
80
|
+
else
|
|
81
|
+
puts "Worker #{worker.id} could not be cancelled: not started"
|
|
152
82
|
end
|
|
153
83
|
end
|
|
84
|
+
end
|
|
154
85
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
stdout = File.new(worker.stdout).readlines.join
|
|
165
|
-
else
|
|
166
|
-
stdout = ""
|
|
167
|
-
end
|
|
168
|
-
worker.update({
|
|
169
|
-
cpu_used: stdout[/Resources:[ \t]*cput=([0-9]{1,2}(\:[0-9]{2})+),/, 1],
|
|
170
|
-
exit_code: stdout[/Exit code:[ \t]*(.*)$/, 1],
|
|
171
|
-
host: stdout[/Nodes:[ \t]*(.*)$/, 1],
|
|
172
|
-
memory_used: stdout[/Resources:.*,mem=([0-9]*[a-zA-Z]*),/, 1],
|
|
173
|
-
time: stdout[/Resources:.*,walltime=([0-9]{1,2}(\:[0-9]{2})+)$/, 1],
|
|
174
|
-
status: :completed,
|
|
175
|
-
})
|
|
176
|
-
end
|
|
177
|
-
end
|
|
86
|
+
##
|
|
87
|
+
# Synchronize the status of jobs with the internal list of workers.
|
|
88
|
+
#
|
|
89
|
+
# @see Ripe::WorkerController::Syncer
|
|
90
|
+
#
|
|
91
|
+
# @return [Array<DB::Worker>] the list of updated workers
|
|
92
|
+
|
|
93
|
+
def sync
|
|
94
|
+
Syncer.new.workers
|
|
178
95
|
end
|
|
179
96
|
|
|
97
|
+
##
|
|
98
|
+
# List the n most recent workers.
|
|
99
|
+
#
|
|
100
|
+
# @param n [Integer] the number of most recent workers
|
|
101
|
+
# @return [Array<DB::Worker>] the list of +n+ most recent workers
|
|
102
|
+
|
|
180
103
|
def list(n = 20)
|
|
181
|
-
Worker.last(n)
|
|
104
|
+
DB::Worker.last(n)
|
|
182
105
|
end
|
|
183
106
|
|
|
107
|
+
##
|
|
108
|
+
# Launch the an interactive text editor from the console.
|
|
109
|
+
#
|
|
110
|
+
# @return [void]
|
|
111
|
+
|
|
184
112
|
def edit(*args)
|
|
185
113
|
system("$EDITOR #{args.join(' ')}")
|
|
186
114
|
end
|