Skip to content

Commit

Permalink
Implement bjob(1).
Browse files Browse the repository at this point in the history
  • Loading branch information
djanowski committed Jun 7, 2013
1 parent b2e4502 commit 6c95c43
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/.gs
/.ruby-version
/test/tmp
20 changes: 20 additions & 0 deletions bin/bjob
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env ruby

require_relative "../vendor/clap"

if File.exist?(".bjobrc")
ARGV.concat(File.read(".bjobrc").split("\n"))
end

exit(1) if ARGV.empty?

ENV["BATCH_INTERACTIVE"] = ENV["PS1"] ? "1" : "0"

args = Clap.run ARGV,
"-r" => -> file { require file },
"-i" => -> { ENV["BATCH_INTERACTIVE"] = "1" },
"-s" => -> { ENV["BATCH_INTERACTIVE"] = "0" }

constant = Module.const_get(args.first)

constant.run
86 changes: 86 additions & 0 deletions test/bin_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
require_relative "prelude"

require "open3"
require "shellwords"
require "fileutils"

scope do
def assert_empty(str)
assert(str.size == 0)
end

setup do
root = File.join(File.expand_path(File.join(File.dirname(__FILE__), "..")))

FileUtils.rm_rf(File.join(root, "test/tmp"))

capture = -> cmd do
out, err, status = Open3.capture3(cmd)

[out.rstrip, err.rstrip, status.exitstatus]
end

sh = -> executable, env do
-> args = [] do
command = %Q[cd #{root} && #{env.map { |k, v| "#{k}=#{v}" }.join(" ")} #{executable} #{Shellwords.join(args)}]

capture.(command)
end
end

[sh.("./bin/bjob", {}), sh, capture, root]
end

test "prints a help message on no arguments" do |bjob, _|
out, err, status = bjob.()

assert_empty err
assert_equal status, 1
end

test "runs a job" do |bjob, _|
out, err, status = bjob.(%w[-r ./test/jobs/ten Ten])

assert_equal "0", File.read("test/tmp/ten/0.txt")
assert_equal "1", File.read("test/tmp/ten/1.txt")

assert_equal status, 0
end

test "is silent on non-terminals by default" do |bjob, _|
out, err, status = bjob.(%w[-r ./test/jobs/ten Ten])

assert_empty out
assert_empty err
end

test "is interactive on terminals by default" do |bjob, sh|
out, err, status = sh.("./bin/bjob", "PS1" => "$").(%w[-r ./test/jobs/ten Ten])

assert_equal out, " 0% ..........\n100%"
assert_empty err
end

test "can be forced to be non interactive with -s" do |bjob, sh|
out, err, status = sh.("./bin/bjob", "PS1" => "$").(%w[-r ./test/jobs/ten Ten -s])

assert_empty out
assert_empty err
end

test "can be forced to be interactive with -i" do |bjob, _|
out, err, status = bjob.(%w[-i -r ./test/jobs/ten Ten])

assert_equal out, " 0% ..........\n100%"
assert_empty err
end

test "picks up .bjobrc" do |bjob, _, capture, root|
out, err, status = capture.(%Q[cd #{File.join(root, "test/roots/bjobrc-ten")} && #{File.join(root, "bin/bjob")} Ten])

assert_equal "0", File.read("test/tmp/ten/0.txt")
assert_equal "1", File.read("test/tmp/ten/1.txt")

assert_empty err
end
end
22 changes: 22 additions & 0 deletions test/jobs/ten.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require_relative "../../lib/bjob"
require "fileutils"

class Ten < BJob::Job
def initialize
@path = File.expand_path("../tmp/ten", File.dirname(__FILE__))
end

def batch(items)
FileUtils.mkdir_p(@path)

yield(items)
end

def process(item)
File.write(File.join(@path, "#{item}.txt"), (item % 2).to_s)
end

def items(filter = nil)
(0..9).to_a
end
end
2 changes: 2 additions & 0 deletions test/roots/bjobrc-ten/.bjobrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-r
../../jobs/ten
46 changes: 46 additions & 0 deletions vendor/clap.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
class Clap
attr :argv
attr :opts

def self.run(args, opts)
new(args, opts).run
end

def initialize(argv, opts)
@argv = argv.dup
@opts = opts
end

def run
args = []

while argv.any?

item = argv.shift
flag = opts[item]

if flag

# Work around lambda semantics in 1.8.7.
arity = [flag.arity, 0].max

# Raise if there are not enough parameters
# available for the flag.
if argv.size < arity
raise ArgumentError
end

# Call the lambda with N items from argv,
# where N is the lambda's arity.
flag.call(*argv.shift(arity))
else

# Collect the items that don't correspond to
# flags.
args << item
end
end

args
end
end

0 comments on commit 6c95c43

Please sign in to comment.