batch_processor 0.2.6 → 0.3.0

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1046 -11
  3. data/lib/batch_processor/batch/controller.rb +1 -1
  4. data/lib/batch_processor/batch/core.rb +1 -1
  5. data/lib/batch_processor/batch/job.rb +1 -1
  6. data/lib/batch_processor/batch/job_controller.rb +1 -1
  7. data/lib/batch_processor/batch/predicates.rb +3 -1
  8. data/lib/batch_processor/batch/processor.rb +16 -3
  9. data/lib/batch_processor/batch_base.rb +1 -0
  10. data/lib/batch_processor/batch_details.rb +1 -1
  11. data/lib/batch_processor/batch_job.rb +3 -1
  12. data/lib/batch_processor/collection.rb +1 -0
  13. data/lib/batch_processor/processor/execute.rb +1 -1
  14. data/lib/batch_processor/processor/process.rb +1 -1
  15. data/lib/batch_processor/processor_base.rb +1 -0
  16. data/lib/batch_processor/processors/parallel.rb +1 -0
  17. data/lib/batch_processor/processors/sequential.rb +1 -0
  18. data/lib/batch_processor/rspec/custom_matchers/set_processor_option.rb +1 -1
  19. data/lib/batch_processor/rspec/custom_matchers/use_default_job_class.rb +1 -1
  20. data/lib/batch_processor/rspec/custom_matchers/use_default_processor.rb +1 -1
  21. data/lib/batch_processor/rspec/custom_matchers/use_job_class.rb +1 -1
  22. data/lib/batch_processor/rspec/custom_matchers/use_parallel_processor.rb +1 -1
  23. data/lib/batch_processor/rspec/custom_matchers/use_sequential_processor.rb +1 -1
  24. data/lib/batch_processor/version.rb +1 -1
  25. data/lib/generators/batch_processor/USAGE +9 -0
  26. data/lib/generators/batch_processor/application_batch/USAGE +9 -0
  27. data/lib/generators/batch_processor/application_batch/application_batch_generator.rb +15 -0
  28. data/lib/generators/batch_processor/application_batch/templates/application_batch.rb +3 -0
  29. data/lib/generators/batch_processor/application_job/USAGE +0 -0
  30. data/lib/generators/batch_processor/application_job/application_job_generator.rb +15 -0
  31. data/lib/generators/batch_processor/application_job/templates/application_job.rb +4 -0
  32. data/lib/generators/batch_processor/batch_processor_generator.rb +15 -0
  33. data/lib/generators/batch_processor/install/USAGE +9 -0
  34. data/lib/generators/batch_processor/install/install_generator.rb +12 -0
  35. data/lib/generators/batch_processor/templates/batch.rb.erb +21 -0
  36. data/lib/generators/rspec/application_batch/USAGE +9 -0
  37. data/lib/generators/rspec/application_batch/application_batch_generator.rb +14 -0
  38. data/lib/generators/rspec/application_batch/templates/application_batch_spec.rb +8 -0
  39. data/lib/generators/rspec/application_job/USAGE +9 -0
  40. data/lib/generators/rspec/application_job/application_job_generator.rb +14 -0
  41. data/lib/generators/rspec/application_job/templates/application_job_spec.rb +8 -0
  42. data/lib/generators/rspec/batch_processor/USAGE +8 -0
  43. data/lib/generators/rspec/batch_processor/batch_processor_generator.rb +13 -0
  44. data/lib/generators/rspec/batch_processor/templates/batch_spec.rb.erb +29 -0
  45. metadata +22 -2
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The controller performs updates on and tracks details of a batch.
3
+ # Batches have a status which is driven by the jobs it is processing. Callbacks are fired in response to status changes.
4
4
  module BatchProcessor
5
5
  module Batch
6
6
  module Controller
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # A batch has an ID, details, and is recoverable as an STI.
3
+ # Batches accept a unique identifier and input representing the arguments and options which define it's collection.
4
4
  module BatchProcessor
5
5
  module Batch
6
6
  module Core
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # A batch job is what process each item in the collection.
3
+ # Unless otherwise specified a `Batch` assumes its **Job** class shares a common name.
4
4
  module BatchProcessor
5
5
  module Batch
6
6
  module Job
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The job controller performs updates on and tracks details related to the jobs in a batch.
3
+ # A BatchJob calls into the Batch to report on it's lifecycle from start to finish, including on success and failure.
4
4
  module BatchProcessor
5
5
  module Batch
6
6
  module JobController
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Predicates allow inspection of the status of a batch.
3
+ # The **Status** of a batch is manifested by a collection of predicates which track certain lifecycle events.
4
4
  module BatchProcessor
5
5
  module Batch
6
6
  module Predicates
@@ -10,6 +10,8 @@ module BatchProcessor
10
10
  date_predicate :started, :enqueued, :aborted, :cleared, :finished
11
11
 
12
12
  job_count_predicate :enqueued, :pending, :running, :failed, :canceled, :unfinished, :finished
13
+
14
+ delegate :valid?, to: :collection, prefix: true
13
15
  end
14
16
 
15
17
  def processing?
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # When processed, the batch performs a job for each item in its collection.
3
+ # Unless otherwise specified a `Batch` uses the **default** `Parallel` Processor.
4
4
  module BatchProcessor
5
5
  module Batch
6
6
  module Processor
7
7
  extend ActiveSupport::Concern
8
8
 
9
- # This is left mutable for extension and customization
9
+ # The default processors can be redefined and new custom ones can be added as well.
10
10
  # rubocop:disable Style/MutableConstant
11
11
  PROCESSOR_CLASS_BY_STRATEGY = {
12
12
  default: BatchProcessor::Processors::Parallel,
@@ -31,6 +31,10 @@ module BatchProcessor
31
31
  new(*arguments).process
32
32
  end
33
33
 
34
+ def process!(*arguments)
35
+ new(*arguments).process!
36
+ end
37
+
34
38
  def processor_class
35
39
  return @processor_class if defined?(@processor_class)
36
40
 
@@ -45,13 +49,22 @@ module BatchProcessor
45
49
 
46
50
  private
47
51
 
52
+ # Certain processors have configurable options; this configuration is specified in the Batch's definition.
48
53
  def processor_option(option, value = nil)
49
54
  _processor_options[option.to_sym] = value
50
55
  end
51
56
  end
52
57
 
53
- def process
58
+ def process!
54
59
  processor_class.execute(batch: self, **_processor_options)
60
+ self
61
+ end
62
+
63
+ def process
64
+ process!
65
+ rescue StandardError => exception
66
+ error :process_error, exception: exception
67
+ self
55
68
  end
56
69
  end
57
70
  end
@@ -7,6 +7,7 @@ require_relative "batch/predicates"
7
7
  require_relative "batch/controller"
8
8
  require_relative "batch/job_controller"
9
9
 
10
+ # A **Batch** defines, controls, and monitors the processing of a collection of items with an `ActiveJob`.
10
11
  module BatchProcessor
11
12
  class BatchBase < Spicerack::InputObject
12
13
  class BatchCollection < BatchProcessor::Collection; end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The details of a batch represent the state of the work to process.
3
+ # The **Details** of a batch are the times of critical lifecycle events and the summary counts of processed jobs.
4
4
  module BatchProcessor
5
5
  class BatchDetails < Spicerack::RedisModel
6
6
  attr_reader :batch_id
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # A batch can only be processed by a batchable job.
3
+ # Only a **BatchJob** can be used to perform work, but it can be run outside of a batch as well.
4
+ # Therefore, the recommendation is to make `ApplicationJob` inherit from `BatchJob`.
4
5
  module BatchProcessor
6
+ # BatchProcessor depends on ActiveJob for handling the processing of individual items in a collection.
5
7
  class BatchJob < ActiveJob::Base
6
8
  attr_accessor :batch_id, :tracked_batch_running, :tracked_batch_failure
7
9
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # A `Collection` takes input to validate and build a (possibly ordered) list of items to process with the Batch's job.
3
4
  module BatchProcessor
4
5
  class Collection < Spicerack::InputModel
5
6
  def items
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # When executed, the processor performs a job for each item in the batch collection.
3
+ # When `.process` is called on a Batch, `.execute` is called on the `Processor` specified in the Batch's definition.
4
4
  module BatchProcessor
5
5
  module Processor
6
6
  module Execute
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Processing a batch performs a job for each item in the batch collection.
3
+ # Processing a Batch performs a job for each item in its collection if **and only if** it has a valid collection.
4
4
  module BatchProcessor
5
5
  module Processor
6
6
  module Process
@@ -3,6 +3,7 @@
3
3
  require_relative "processor/process"
4
4
  require_relative "processor/execute"
5
5
 
6
+ # A **Processor** is a service object which determines how to perform a Batch's jobs to properly process its collection.
6
7
  module BatchProcessor
7
8
  class ProcessorBase < Spicerack::InputObject
8
9
  argument :batch, allow_nil: false
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # The Parallel Processor enqueues jobs to be performed later.
3
4
  module BatchProcessor
4
5
  module Processors
5
6
  class Parallel < BatchProcessor::ProcessorBase
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # The Sequential Processor uses `.perform_now` to procedurally process each job within the current thread.
3
4
  module BatchProcessor
4
5
  module Processors
5
6
  class Sequential < BatchProcessor::ProcessorBase
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # RSpec matcher that tests usages of batches which do not specify a processor
3
+ # RSpec matcher that tests usages of batches specifying processor options.
4
4
  #
5
5
  # class ExampleBatch < ApplicationBatch
6
6
  # processor_option :sorted, true
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # RSpec matcher to DRY out the similarities between the other batch processor matchers.
3
+ # RSpec matcher that tests usages of batches which do not explicitly specify a job.
4
4
  #
5
5
  # class ExampleBatch < ApplicationBatch
6
6
  # end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # RSpec matcher that tests usages of batches which do not specify a processor
3
+ # RSpec matcher that tests usages of batches which do not explicitly specify a processor.
4
4
  #
5
5
  # class ExampleBatch < ApplicationBatch
6
6
  # end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # RSpec matcher to DRY out the similarities between the other batch processor matchers.
3
+ # RSpec matcher that tests usages of batches which explicitly specify a job.
4
4
  #
5
5
  # class ExampleBatch < ApplicationBatch
6
6
  # def self.job_class
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # RSpec matcher that tests usages of `.with_parallel_processor`
3
+ # RSpec matcher that tests usages of `.with_parallel_processor`.
4
4
  #
5
5
  # class ExampleBatch < ApplicationBatch
6
6
  # with_parallel_processor
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # RSpec matcher that tests usages of `.with_parallel_processor`
3
+ # RSpec matcher that tests usages of `.with_sequential_processor`.
4
4
  #
5
5
  # class ExampleBatch < ApplicationBatch
6
6
  # with_sequential_processor
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BatchProcessor
4
- VERSION = "0.2.6"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Stub a Batch with test.
3
+
4
+ Example:
5
+ `rails generate batch_processor foo`
6
+
7
+ Generates:
8
+ Batch: app/batches/foo_batch.rb
9
+ Test: spec/batches/foo_batch_spec.rb
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Create the ApplicationBatch and test.
3
+
4
+ Example:
5
+ `rails generate batch_processor:application_batch`
6
+
7
+ Generates:
8
+ Flow: app/batches/application_batch.rb
9
+ Test: spec/batches/application_batch_spec.rb
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BatchProcessor
4
+ module Generators
5
+ class ApplicationBatchGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ hook_for :test_framework
9
+
10
+ def create_application_batch
11
+ template "application_batch.rb", File.join("app/batches/application_batch.rb")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationBatch < BatchProcessor::BatchBase; end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BatchProcessor
4
+ module Generators
5
+ class ApplicationJobGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ hook_for :test_framework
9
+
10
+ def create_application_job
11
+ template "application_job.rb", File.join("app/jobs/application_job.rb")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationJob < BatchProcessor::BatchJob
4
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BatchProcessor
4
+ module Generators
5
+ class BatchProcessorGenerator < Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ hook_for :test_framework
9
+
10
+ def create_application_flow
11
+ template "batch.rb.erb", File.join("app/batches/", class_path, "#{file_name}_batch.rb")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Creates ApplicationBatch with tests.
3
+
4
+ Example:
5
+ `rails generate batch_processor:install`
6
+
7
+ Generates:
8
+ Batch: app/batches/application_batch.rb
9
+ Test: spec/batches/application_batch_spec.rb
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BatchProcessor
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ def run_other_generators
7
+ generate "batch_processor:application_batch"
8
+ generate "batch_processor:application_job"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %>Batch < ApplicationBatch
4
+ # with_parallel_processor
5
+ # with_sequential_processor
6
+ # processor_option :continue_after_exception, true
7
+ # processor_option :sorted, true
8
+
9
+ # process_with_job OtherJob
10
+
11
+ # allow_empty
12
+
13
+ class Collection < BatchCollection
14
+ # argument :arg, allow_nil: false
15
+ # option :opt, default: 3
16
+
17
+ def items
18
+ # TODO
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Create ApplicationBatch test.
3
+
4
+ Example:
5
+ `rails generate rspec:application_batch`
6
+
7
+ Generates:
8
+ Test: spec/batches/application_batch_spec.rb
9
+
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rspec
4
+ module Generators
5
+ class ApplicationBatchGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ def create_spec_file
9
+ template "application_batch_spec.rb", File.join("spec/batches/application_batch_spec.rb")
10
+ end
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe ApplicationBatch, type: :batch do
6
+ it { is_expected.to inherit_from BatchProcessor::BatchBase }
7
+ end
8
+
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Create ApplicationJob test.
3
+
4
+ Example:
5
+ `rails generate rspec:application_job`
6
+
7
+ Generates:
8
+ Test: spec/jobs/application_job_spec.rb
9
+
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rspec
4
+ module Generators
5
+ class ApplicationJobGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ def create_spec_file
9
+ template "application_job_spec.rb", File.join("spec/jobs/application_job_spec.rb")
10
+ end
11
+ end
12
+ end
13
+ end
14
+