remi 0.2.42 → 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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +7 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +13 -26
  5. data/README.md +1 -1
  6. data/features/step_definitions/remi_step.rb +33 -13
  7. data/features/sub_job_example.feature +24 -0
  8. data/features/sub_transform_example.feature +35 -0
  9. data/features/sub_transform_many_to_many.feature +49 -0
  10. data/features/support/env_app.rb +1 -1
  11. data/jobs/all_jobs_shared.rb +19 -16
  12. data/jobs/copy_source_job.rb +11 -9
  13. data/jobs/csv_file_target_job.rb +10 -9
  14. data/jobs/json_job.rb +18 -14
  15. data/jobs/metadata_job.rb +33 -28
  16. data/jobs/parameters_job.rb +14 -11
  17. data/jobs/sample_job.rb +106 -77
  18. data/jobs/sftp_file_target_job.rb +14 -13
  19. data/jobs/sub_job_example_job.rb +86 -0
  20. data/jobs/sub_transform_example_job.rb +43 -0
  21. data/jobs/sub_transform_many_to_many_job.rb +46 -0
  22. data/jobs/transforms/concatenate_job.rb +16 -12
  23. data/jobs/transforms/data_frame_sieve_job.rb +24 -19
  24. data/jobs/transforms/date_diff_job.rb +15 -11
  25. data/jobs/transforms/nvl_job.rb +16 -12
  26. data/jobs/transforms/parse_date_job.rb +17 -14
  27. data/jobs/transforms/partitioner_job.rb +27 -19
  28. data/jobs/transforms/prefix_job.rb +13 -10
  29. data/jobs/transforms/truncate_job.rb +14 -10
  30. data/jobs/transforms/truthy_job.rb +11 -8
  31. data/lib/remi.rb +25 -11
  32. data/lib/remi/data_frame.rb +4 -4
  33. data/lib/remi/data_frame/daru.rb +1 -37
  34. data/lib/remi/data_subject.rb +234 -48
  35. data/lib/remi/data_subjects/csv_file.rb +171 -0
  36. data/lib/remi/data_subjects/data_frame.rb +106 -0
  37. data/lib/remi/data_subjects/file_system.rb +115 -0
  38. data/lib/remi/data_subjects/local_file.rb +109 -0
  39. data/lib/remi/data_subjects/none.rb +31 -0
  40. data/lib/remi/data_subjects/postgres.rb +186 -0
  41. data/lib/remi/data_subjects/s3_file.rb +84 -0
  42. data/lib/remi/data_subjects/salesforce.rb +211 -0
  43. data/lib/remi/data_subjects/sftp_file.rb +196 -0
  44. data/lib/remi/data_subjects/sub_job.rb +50 -0
  45. data/lib/remi/dsl.rb +74 -0
  46. data/lib/remi/encoder.rb +45 -0
  47. data/lib/remi/extractor.rb +21 -0
  48. data/lib/remi/field_symbolizers.rb +1 -0
  49. data/lib/remi/job.rb +279 -113
  50. data/lib/remi/job/parameters.rb +90 -0
  51. data/lib/remi/job/sub_job.rb +35 -0
  52. data/lib/remi/job/transform.rb +165 -0
  53. data/lib/remi/loader.rb +22 -0
  54. data/lib/remi/monkeys/daru.rb +4 -0
  55. data/lib/remi/parser.rb +44 -0
  56. data/lib/remi/testing/business_rules.rb +17 -23
  57. data/lib/remi/testing/data_stub.rb +2 -2
  58. data/lib/remi/version.rb +1 -1
  59. data/remi.gemspec +3 -0
  60. data/spec/data_subject_spec.rb +475 -11
  61. data/spec/data_subjects/csv_file_spec.rb +69 -0
  62. data/spec/data_subjects/data_frame_spec.rb +52 -0
  63. data/spec/{extractor → data_subjects}/file_system_spec.rb +0 -0
  64. data/spec/{extractor → data_subjects}/local_file_spec.rb +0 -0
  65. data/spec/data_subjects/none_spec.rb +41 -0
  66. data/spec/data_subjects/postgres_spec.rb +80 -0
  67. data/spec/{extractor → data_subjects}/s3_file_spec.rb +0 -0
  68. data/spec/data_subjects/salesforce_spec.rb +117 -0
  69. data/spec/{extractor → data_subjects}/sftp_file_spec.rb +16 -0
  70. data/spec/data_subjects/sub_job_spec.rb +33 -0
  71. data/spec/encoder_spec.rb +38 -0
  72. data/spec/extractor_spec.rb +11 -0
  73. data/spec/fixtures/sf_bulk_helper_stubs.rb +443 -0
  74. data/spec/job/transform_spec.rb +257 -0
  75. data/spec/job_spec.rb +507 -0
  76. data/spec/loader_spec.rb +11 -0
  77. data/spec/parser_spec.rb +38 -0
  78. data/spec/sf_bulk_helper_spec.rb +117 -0
  79. data/spec/testing/data_stub_spec.rb +5 -3
  80. metadata +109 -27
  81. data/features/aggregate.feature +0 -42
  82. data/jobs/aggregate_job.rb +0 -31
  83. data/jobs/transforms/transform_jobs.rb +0 -4
  84. data/lib/remi/data_subject/csv_file.rb +0 -162
  85. data/lib/remi/data_subject/data_frame.rb +0 -52
  86. data/lib/remi/data_subject/postgres.rb +0 -134
  87. data/lib/remi/data_subject/salesforce.rb +0 -136
  88. data/lib/remi/data_subject/sftp_file.rb +0 -65
  89. data/lib/remi/extractor/file_system.rb +0 -92
  90. data/lib/remi/extractor/local_file.rb +0 -43
  91. data/lib/remi/extractor/s3_file.rb +0 -57
  92. data/lib/remi/extractor/sftp_file.rb +0 -83
  93. data/spec/data_subject/csv_file_spec.rb +0 -79
  94. data/spec/data_subject/data_frame.rb +0 -27
@@ -1,21 +1,25 @@
1
1
  require_relative 'all_jobs_shared'
2
2
 
3
- class JsonJob
4
- include AllJobsShared
5
-
6
- define_source :source_data, Remi::DataSource::DataFrame,
7
- fields: {
8
- :json_array => { type: :json },
9
- :json_hash => { type: :json }
10
- }
3
+ class JsonJob < Remi::Job
4
+ source :source_data do
5
+ fields(
6
+ {
7
+ :json_array => { type: :json },
8
+ :json_hash => { type: :json }
9
+ }
10
+ )
11
+ end
11
12
 
12
- define_target :target_data, Remi::DataTarget::DataFrame,
13
- fields: {
14
- :second_element => {},
15
- :name_field => {}
16
- }
13
+ target :target_data do
14
+ fields(
15
+ {
16
+ :second_element => {},
17
+ :name_field => {}
18
+ }
19
+ )
20
+ end
17
21
 
18
- define_transform :main do
22
+ transform :main do
19
23
  Remi::SourceToTargetMap.apply(source_data.df, target_data.df, source_metadata: source_data.fields) do
20
24
  map source(:json_array) .target(:second_element)
21
25
  .transform(->(values) { values[1] })
@@ -1,37 +1,42 @@
1
1
  require_relative 'all_jobs_shared'
2
2
  ENV['TZ'] = 'UTC'
3
3
 
4
- class MetadataJob
5
- include AllJobsShared
4
+ class MetadataJob < Remi::Job
5
+ source :source_data do
6
+ fields(
7
+ {
8
+ :activity_id => { from: 'in', in: true, cdc_type: 2 },
9
+ :student_id => { from: 'in', in: true, type: :string, cdc_type: 2 },
10
+ :student_dob => { from: 'in', in: true, type: :date, in_format: '%m/%d/%Y', out_format: '%Y-%m-%d', cdc_type: 2 },
11
+ :activity_type => { from: 'in', in: true, type: :string, valid_values: ['A', 'B', 'C'], cdc_type: 2 },
12
+ :activity_counter => { from: 'in', in: true, type: :integer, cdc_type: 2 },
13
+ :activity_score => { from: 'in', in: true, type: :float, cdc_type: 2 },
14
+ :activity_cost => { from: 'in', in: true, type: :decimal, precision: 8, scale: 2, cdc_type: 2 },
15
+ :activity_date => { from: 'in', in: true, type: :datetime, in_format: '%m/%d/%Y %H:%M:%S', out_format: '%Y-%m-%dT%H:%M:%S', cdc_type: 2 },
16
+ :source_filename => { from: 'in', in: true, type: :string, cdc_type: 1 }
17
+ }
18
+ )
19
+ end
6
20
 
7
- define_source :source_data, Remi::DataSource::DataFrame,
8
- fields: {
9
- :activity_id => { from: 'in', in: true, cdc_type: 2 },
10
- :student_id => { from: 'in', in: true, type: :string, cdc_type: 2 },
11
- :student_dob => { from: 'in', in: true, type: :date, in_format: '%m/%d/%Y', out_format: '%Y-%m-%d', cdc_type: 2 },
12
- :activity_type => { from: 'in', in: true, type: :string, valid_values: ['A', 'B', 'C'], cdc_type: 2 },
13
- :activity_counter => { from: 'in', in: true, type: :integer, cdc_type: 2 },
14
- :activity_score => { from: 'in', in: true, type: :float, cdc_type: 2 },
15
- :activity_cost => { from: 'in', in: true, type: :decimal, precision: 8, scale: 2, cdc_type: 2 },
16
- :activity_date => { from: 'in', in: true, type: :datetime, in_format: '%m/%d/%Y %H:%M:%S', out_format: '%Y-%m-%dT%H:%M:%S', cdc_type: 2 },
17
- :source_filename => { from: 'in', in: true, type: :string, cdc_type: 1 }
18
- }
21
+ target :target_data do
22
+ encoder Remi::Encoder::CsvFile.new path: "#{Remi::Settings.work_dir}/target_data.csv"
19
23
 
20
- define_target :target_data, Remi::DataTarget::CsvFile,
21
- path: "#{Remi::Settings.work_dir}/target_data.csv",
22
- fields: {
23
- :activity_id => { from: 'out', out: true },
24
- :student_id => { from: 'out', out: true, type: :string },
25
- :student_dob => { from: 'out', out: true, type: :date, in_format: '%m/%d/%Y', out_format: '%Y-%m-%d' },
26
- :activity_type => { from: 'out', out: true, type: :string, valid_values: ['A', 'B', 'C'] },
27
- :activity_counter => { from: 'out', out: true, type: :integer },
28
- :activity_score => { from: 'out', out: true, type: :float },
29
- :activity_cost => { from: 'out', out: true, type: :decimal, precision: 8, scale: 2 },
30
- :activity_date => { from: 'out', out: true, type: :datetime, in_format: '%m/%d/%Y %H:%M:%S', out_format: '%Y-%m-%dT%H:%M:%S' },
31
- :source_filename => { from: 'out', out: true, type: :string, cdc_type: 1 }
32
- }
24
+ fields(
25
+ {
26
+ :activity_id => { from: 'out', out: true },
27
+ :student_id => { from: 'out', out: true, type: :string },
28
+ :student_dob => { from: 'out', out: true, type: :date, in_format: '%m/%d/%Y', out_format: '%Y-%m-%d' },
29
+ :activity_type => { from: 'out', out: true, type: :string, valid_values: ['A', 'B', 'C'] },
30
+ :activity_counter => { from: 'out', out: true, type: :integer },
31
+ :activity_score => { from: 'out', out: true, type: :float },
32
+ :activity_cost => { from: 'out', out: true, type: :decimal, precision: 8, scale: 2 },
33
+ :activity_date => { from: 'out', out: true, type: :datetime, in_format: '%m/%d/%Y %H:%M:%S', out_format: '%Y-%m-%dT%H:%M:%S' },
34
+ :source_filename => { from: 'out', out: true, type: :string, cdc_type: 1 }
35
+ }
36
+ )
37
+ end
33
38
 
34
- define_transform :main do
39
+ transform :main do
35
40
  source_data.enforce_types
36
41
 
37
42
  Remi::SourceToTargetMap.apply(source_data.df, target_data.df, source_metadata: source_data.fields, target_metadata: target_data.fields) do
@@ -1,22 +1,25 @@
1
1
  require_relative 'all_jobs_shared'
2
2
 
3
- class ParametersJob
4
- include AllJobsShared
3
+ class ParametersJob < Remi::Job
4
+ param(:myparam) {}
5
+ param(:test_parameter) { "my test parameter value" }
5
6
 
6
- define_param :test_parameter, "my test parameter value"
7
+ source :source_data do
8
+ fields(
9
+ {
10
+ :parameter_name => {}
11
+ }
12
+ )
13
+ end
7
14
 
8
- define_target :source_data, Remi::DataSource::DataFrame,
9
- fields: {
10
- :parameter_name => {}
11
- }
12
- define_target :target_data, Remi::DataTarget::DataFrame
15
+ target :target_data
13
16
 
14
- define_transform :main do
17
+ transform :main do
15
18
  Remi::SourceToTargetMap.apply(source_data.df, target_data.df) do
16
19
  map target(:myparam)
17
- .transform(Remi::Transform::Constant.new(params[:myparam]))
20
+ .transform(Remi::Transform::Constant.new(job.params[:myparam]))
18
21
  map source(:parameter_name) .target(:parameter_name)
19
- .transform(->(v) { params[v.to_sym] })
22
+ .transform(->(v) { job.params[v.to_sym] })
20
23
  end
21
24
  end
22
25
 
@@ -1,88 +1,118 @@
1
1
  # This is an example Remi job that was auto-generated by Remi.
2
2
  require_relative 'all_jobs_shared'
3
- require 'remi/data_subject/salesforce'
4
-
5
- class SampleJob
6
- include AllJobsShared
7
-
8
- define_source :existing_contacts, Remi::DataSource::Salesforce,
9
- object: :Contact,
10
- credentials: params[:salesforce_credentials],
11
- api: :bulk,
12
- fields: {
13
- :Id => {},
14
- :External_ID__c => {},
15
- :IsActive => { type: :boolean },
16
- :CreatedDate => { type: :date, in_format: '%Y-%m-%d %H:%M:%S' }
17
- },
18
- query: <<-EOQ
19
- SELECT
20
- Id,
21
- External_ID__c
22
- FROM
23
- Contact
24
- EOQ
25
-
26
-
27
- define_source :sample_file, Remi::DataSource::CsvFile,
28
- extractor: Remi::Extractor::SftpFile.new(
3
+ require 'remi/data_subjects/salesforce'
4
+
5
+ class SampleJob < AllJobsShared
6
+
7
+ param :program_name_lookup do
8
+ RegexSieve.new(
9
+ {
10
+ /^BIO$/ => "Biology",
11
+ /^Fake Biology$/ => nil,
12
+ /(?:B|Microb)iology/ => "Biology",
13
+ /^CHEM$/ => "Chemistry",
14
+ /Chemistry/ => "Chemistry",
15
+ /Physics/ => "Physics"
16
+ }
17
+ )
18
+ end
19
+
20
+ source :existing_contacts do
21
+ extractor Remi::Extractor::Salesforce.new(
22
+ object: :Contact,
23
+ credentials: params[:salesforce_credentials],
24
+ api: :bulk,
25
+ query: <<-EOQ
26
+ SELECT
27
+ Id,
28
+ External_ID__c,
29
+ IsActive,
30
+ CreatedDate
31
+ FROM
32
+ Contact
33
+ EOQ
34
+ )
35
+ parser Remi::Parser::Salesforce.new
36
+
37
+ field_symbolizer :salesforce
38
+ fields(
39
+ {
40
+ :Id => {},
41
+ :External_ID__c => {},
42
+ :IsActive => { type: :boolean },
43
+ :CreatedDate => { type: :date, in_format: '%Y-%m-%d %H:%M:%S' }
44
+ }
45
+ )
46
+ end
47
+
48
+ source :sample_file do
49
+ extractor Remi::Extractor::SftpFile.new(
29
50
  credentials: params[:sftp],
30
51
  remote_path: '/',
31
52
  pattern: /^SampleFile_(\d+)\.txt/,
32
53
  most_recent_only: true
33
- ),
34
- csv_options: {
35
- headers: true,
36
- col_sep: ",",
37
- encoding: "ISO-8859-1:UTF-8"
38
- },
39
- fields: {
40
- :student_id => {},
41
- :school_id => {},
42
- :school_name => {},
43
- :program => {},
44
- :last_name => {},
45
- :first_name => {},
46
- :current_email => {},
47
- :mailing_address_line_1 => {},
48
- :mailing_address_line_2 => {},
49
- :mailing_city => {},
50
- :mailing_state => {},
51
- :mailing_postal_code => {},
52
- :birthdate => { type: :date, in_format: '%m/%d/%Y'},
53
- :applied_date => { type: :date, in_format: '%m/%d/%Y'}
54
- }
55
-
56
- define_target :all_contacts, Remi::DataTarget::DataFrame
57
-
58
- define_target :contact_updates, Remi::DataTarget::Salesforce,
59
- credentials: params[:salesforce_credentials],
60
- object: :Contact,
61
- operation: :update,
62
- api: :bulk
63
-
64
- define_target :contact_creates, Remi::DataTarget::Salesforce,
65
- credentials: params[:salesforce_credentials],
66
- object: :Contact,
67
- operation: :create,
68
- api: :bulk
69
-
70
- define_param :program_name_lookup, RegexSieve.new({
71
- /^BIO$/ => "Biology",
72
- /^Fake Biology$/ => nil,
73
- /(?:B|Microb)iology/ => "Biology",
74
- /^CHEM$/ => "Chemistry",
75
- /Chemistry/ => "Chemistry",
76
- /Physics/ => "Physics"
77
- })
78
-
79
- define_transform :map_common_fields, sources: [:sample_file, :existing_contacts], targets: :all_contacts do
54
+ )
55
+
56
+ parser Remi::Parser::CsvFile.new(
57
+ csv_options: {
58
+ headers: true,
59
+ col_sep: ",",
60
+ encoding: "ISO-8859-1:UTF-8"
61
+ }
62
+ )
63
+
64
+ fields(
65
+ {
66
+ :student_id => {},
67
+ :school_id => {},
68
+ :school_name => {},
69
+ :program => {},
70
+ :last_name => {},
71
+ :first_name => {},
72
+ :current_email => {},
73
+ :mailing_address_line_1 => {},
74
+ :mailing_address_line_2 => {},
75
+ :mailing_city => {},
76
+ :mailing_state => {},
77
+ :mailing_postal_code => {},
78
+ :birthdate => { type: :date, in_format: '%m/%d/%Y'},
79
+ :applied_date => { type: :date, in_format: '%m/%d/%Y'}
80
+ }
81
+ )
82
+ end
83
+
84
+
85
+ target :all_contacts
80
86
 
87
+ target :contact_updates do
88
+ encoder Remi::Encoder::Salesforce.new
89
+ loader Remi::Loader::Salesforce.new(
90
+ credentials: params[:salesforce_credentials],
91
+ object: :Contact,
92
+ operation: :update,
93
+ api: :bulk
94
+ )
95
+ field_symbolizer :salesforce
96
+ end
97
+
98
+ target :contact_creates do
99
+ encoder Remi::Encoder::Salesforce.new
100
+ loader Remi::Loader::Salesforce.new(
101
+ credentials: params[:salesforce_credentials],
102
+ object: :Contact,
103
+ operation: :create,
104
+ api: :bulk
105
+ )
106
+ field_symbolizer :salesforce
107
+ end
108
+
109
+
110
+ transform :map_common_fields do
81
111
  # Exclude all source records with an invalid program name
82
112
  all_contacts.df = sample_file.df.dup
83
113
  Remi::SourceToTargetMap.apply(all_contacts.df) do
84
114
  map source(:program) .target(:Major__c)
85
- .transform(Remi::Transform::Lookup.new(params[:program_name_lookup]))
115
+ .transform(Remi::Transform::Lookup.new(job.params[:program_name_lookup]))
86
116
  end
87
117
  all_contacts.df = all_contacts.df.where(all_contacts.df[:Major__c].not_eq(nil))
88
118
 
@@ -102,8 +132,7 @@ class SampleJob
102
132
  end
103
133
 
104
134
 
105
- define_transform :map_creates, sources: :all_contacts, targets: :contact_creates do
106
-
135
+ transform :map_creates do
107
136
  work_contact_creates = all_contacts.df.where(all_contacts.df[:Id].eq(nil))
108
137
 
109
138
  Remi::SourceToTargetMap.apply(work_contact_creates) do
@@ -166,7 +195,7 @@ class SampleJob
166
195
  ]
167
196
  end
168
197
 
169
- define_transform :map_updates, sources: :all_contacts, targets: :contact_updates do
198
+ transform :map_updates do
170
199
  contact_updates.df = all_contacts.df[
171
200
  :Id,
172
201
  :Major__c
@@ -1,18 +1,19 @@
1
1
  require_relative 'all_jobs_shared'
2
2
 
3
- class SftpFileTargetJob
4
- include AllJobsShared
5
-
6
-
7
- define_target :some_file, Remi::DataTarget::SftpFile,
8
- credentials: {
9
- host: 'example.com',
10
- username: 'user',
11
- password: 'secret'
12
- },
13
- local_path: "#{Remi::Settings.work_dir}/some_file.csv",
14
- remote_path: "some_file_#{DateTime.current.strftime('%Y%m%d')}.csv"
3
+ class SftpFileTargetJob < Remi::Job
4
+ target :some_file do
5
+ encoder Remi::Encoder::CsvFile.new
6
+ loader Remi::Loader::SftpFile.new(
7
+ credentials: {
8
+ host: 'example.com',
9
+ username: 'user',
10
+ password: 'secret'
11
+ },
12
+ local_path: "#{Remi::Settings.work_dir}/some_file.csv",
13
+ remote_path: "some_file_#{DateTime.current.strftime('%Y%m%d')}.csv"
14
+ )
15
+ end
15
16
 
16
- define_transform :main do
17
+ transform :main do
17
18
  end
18
19
  end
@@ -0,0 +1,86 @@
1
+ require_relative 'all_jobs_shared'
2
+
3
+ class BeersJob < Remi::Job
4
+ source :beers do
5
+ extractor Remi::Extractor::DataFrame.new(
6
+ data: [
7
+ [ 'Baerlic', 'IPA' ],
8
+ [ 'Ex Novo', 'Red' ]
9
+ ]
10
+ )
11
+ parser Remi::Parser::DataFrame.new
12
+ fields(
13
+ {
14
+ brewer: {},
15
+ style: {}
16
+ }
17
+ )
18
+ end
19
+
20
+ transform :main do
21
+ # In the real world, add lots of complex stuff here, possibly grabbing
22
+ # from multiple sources.
23
+ beers.df
24
+ end
25
+ end
26
+
27
+ class ZombifyJob < Remi::Job
28
+ source :beers
29
+ target :zombie_beers
30
+
31
+ transform :main do
32
+ Remi::SourceToTargetMap.apply(beers.df, zombie_beers.df) do
33
+ map source(:brewer) .target(:brewer)
34
+ .transform(Remi::Transform::Prefix.new('Zombie '))
35
+ map source(:style) .target(:style)
36
+ .transform(Remi::Transform::Prefix.new('Zombie '))
37
+ end
38
+ end
39
+ end
40
+
41
+ class SubJobExampleJob < Remi::Job
42
+ sub_job(:beers_job) { BeersJob.new }
43
+ sub_job(:zombify_job) { ZombifyJob.new }
44
+
45
+ # This originates from a source in the sub job
46
+ source :beer_fridge do
47
+ extractor Remi::Extractor::SubJob.new(
48
+ sub_job: beers_job,
49
+ data_subject: :beers
50
+ )
51
+ fields beers_job.fields :beers
52
+ end
53
+
54
+ # This target is used as a source in the sub job
55
+ target :beers_to_zombify do
56
+ loader Remi::Loader::SubJob.new(
57
+ sub_job: zombify_job,
58
+ data_subject: :beers
59
+ )
60
+ end
61
+
62
+ # This source is obtained from the target of the sub job
63
+ source :zombie_fridge do
64
+ extractor Remi::Extractor::SubJob.new(
65
+ sub_job: zombify_job,
66
+ data_subject: :zombie_beers
67
+ )
68
+ fields zombify_job.fields :zombie_beers
69
+ end
70
+
71
+ # These are the ultimate targets of this job
72
+ target :just_beers
73
+ target :zombified_beers
74
+
75
+ transform :zombification do
76
+ # Sub jobs must be executed before their sources are available
77
+ beers_job.execute
78
+ just_beers.df = beer_fridge.df
79
+
80
+ # Sub job targets must be loaded before they are available to subjobs
81
+ beers_to_zombify.df = just_beers.df
82
+ beers_to_zombify.load
83
+ zombify_job.execute
84
+ zombified_beers.df = zombie_fridge.df
85
+ end
86
+ end