Permalink
Browse files

Adding a Job object for collecting a definition and turning it into a…

… full munging process.
  • Loading branch information...
1 parent 5126729 commit 232d702ceabb5abf3a40c4cf943e10caa1a046c4 @JEG2 committed Jul 18, 2009
Showing with 229 additions and 31 deletions.
  1. +94 −0 lib/mungr/job.rb
  2. +21 −1 test/helper.rb
  3. +91 −0 test/test_job.rb
  4. +23 −30 test/test_munge.rb
View
@@ -0,0 +1,94 @@
+# encoding: UTF-8
+
+require "mungr/reader"
+require "mungr/munge"
+require "mungr/writer"
+
+module Mungr
+ #
+ # A Job object is used to gather Reader, Munge, and Writer objects as a
+ # munging process is defined. After all elements of the process have been
+ # gathered, this Job is used to build() the correct object tree which can be
+ # used to run() the process.
+ #
+ class Job
+ #
+ # Creates an empty Job. You then add Readers, Munges, and Writers needed
+ # and build() the final process. This generally looks something like the
+ # following:
+ #
+ # job = Job.new
+ # reader = Reader.new do |r|
+ # # ...
+ # end
+ # munge = Munge.new do |m|
+ # # ...
+ # end
+ # writer = Writer.new do |w|
+ # # ...
+ # end
+ # job << reader << munge << writer
+ # job.build.run
+ #
+ def initialize
+ @readers = Array.new
+ @munges = Array.new
+ @writers = Array.new
+ end
+
+ #
+ # Adds a Reader, Munge, or Writer that will become a part of the final
+ # munging process.
+ #
+ def <<(reader_or_munge_or_writer)
+ case reader_or_munge_or_writer
+ when Reader
+ @readers << reader_or_munge_or_writer
+ when Munge
+ @munges << reader_or_munge_or_writer
+ when Writer
+ @writers << reader_or_munge_or_writer
+ else
+ fail "You must add a Reader, Munge, or Writer."
+ end
+ self # for chaining
+ end
+
+ #
+ # Constructs the final object tree for the munging process using all
+ # gathered Readers, Munges, and Writers.
+ #
+ # All Readers will be registered, in the order they are added, on the first
+ # Munge added. All Writers will be registered, in the order they are added,
+ # on the last Munge added. All Munges added are chained such that the first
+ # Munge added will be at the top of the chain and last Munge added will be
+ # at the bottom. The last Munge is returned so you can call run() on it to
+ # start the process.
+ #
+ # Adding a Munge is optional and a simple pass-through Munge will be created
+ # and returned (with all Readers and Writers attached), if none is added.
+ #
+ def build
+ fail "You need at least one Reader to build a Job." if @readers.empty?
+ fail "You need at least one Writer to build a Job." if @writers.empty?
+ if @munges.empty?
+ first_munge = last_munge = Munge.new do |m|
+ m.munge { |_, value| value }
+ end
+ else
+ first_munge = last_munge = @munges.pop
+ while munge_as_reader = @munges.pop
+ first_munge.add_reader(munge_as_reader)
+ first_munge = munge_as_reader
+ end
+ end
+ @readers.each do |reader|
+ first_munge.add_reader(reader)
+ end
+ @writers.each do |writer|
+ last_munge.add_writer(writer)
+ end
+ last_munge
+ end
+ end
+end
View
@@ -7,11 +7,31 @@ class MiniTest::Unit::TestCase
private
#######
- def reader(&init)
+ def reader(*inputs, &init)
+ unless inputs.empty?
+ return reader do |r|
+ r.read { inputs.shift }
+ end
+ end
@reader = Mungr::Reader.new(&init)
end
+ def munger(&init)
+ @munge = Mungr::Munge.new(&init)
+ end
+
+ def munge(&transform)
+ munger do |m|
+ m.munge { |_, *data| transform[*data] }
+ end
+ end
+
def writer(&init)
+ if init.nil?
+ return writer do |w|
+ w.write { }
+ end
+ end
@writer = Mungr::Writer.new(&init)
end
end
View
@@ -0,0 +1,91 @@
+# encoding: UTF-8
+
+require "helper"
+
+require "mungr/job"
+
+class TestJob < MiniTest::Unit::TestCase
+ def setup
+ reset_job
+
+ @small_num_reader = reader(1, 2, 3)
+ @big_num_reader = reader(10, 20, 30)
+ @add_one_munge = munge { |*nums| nums.map { |n| n + 1 } }
+ @double_munge = munge { |*nums| nums.map { |n| n * 2 } }
+ @gather_writer = writer do |w|
+ w.prepare { @gathered = Array.new }
+ w.write { |gathered, *nums| gathered << nums }
+ end
+ @void_writer = writer
+
+ [ @small_num_reader, @big_num_reader,
+ @add_one_munge,
+ @double_munge,
+ @gather_writer, @void_writer ].each do |reader_or_munge_or_writer|
+ @job << reader_or_munge_or_writer
+ end
+ end
+
+ ####################
+ ### Requirements ###
+ ####################
+
+ def test_a_reader_is_required
+ reset_job
+ assert_raises(RuntimeError) do
+ @job.build
+ end
+ end
+
+ def test_a_writer_is_required
+ reset_job
+ @job << @small_num_reader
+ assert_raises(RuntimeError) do
+ @job.build
+ end
+ end
+
+ def test_a_munge_is_optional_with_a_pass_through_default
+ reset_job
+ @job << reader(:pass_through)
+ @job << @void_writer
+ munge = @job.build
+ assert_instance_of(Mungr::Munge, munge)
+ assert_equal(:pass_through, munge.munge)
+ end
+
+ #################
+ ### Interface ###
+ #################
+
+ def test_cannot_add_a_non_reader_non_munge_non_writer_object
+ assert_raises(RuntimeError) do
+ @job << :bad_object
+ end
+ end
+
+ def test_adds_can_be_chained
+ assert_same(@job, @job << @void_writer)
+ end
+
+ #################
+ ### Structure ###
+ #################
+
+ def test_the_last_munge_is_returned
+ assert_same(@double_munge, @job.build)
+ end
+
+ def test_readers_feed_chained_mungers_which_feed_writers
+ @job.build.run
+ assert_equal([[4, 22], [6, 42], [8, 62]], @gathered) # verify final results
+ end
+
+ #######
+ private
+ #######
+
+ def reset_job
+ @job = Mungr::Job.new
+ end
+end
Oops, something went wrong.

0 comments on commit 232d702

Please sign in to comment.