employer 0.0.1 → 0.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.
@@ -0,0 +1,6 @@
1
+ require "employer/employees/forking_employee"
2
+ require "support/shared_examples/employee"
3
+
4
+ describe Employer::Employees::ForkingEmployee, unless: RUBY_PLATFORM =~ /java/ do
5
+ it_behaves_like "an employee"
6
+ end
@@ -0,0 +1,6 @@
1
+ require "employer/employees/threading_employee"
2
+ require "support/shared_examples/employee"
3
+
4
+ describe Employer::Employees::ThreadingEmployee do
5
+ it_behaves_like "an employee"
6
+ end
@@ -0,0 +1,89 @@
1
+ require "employer/job"
2
+
3
+ describe Employer::Job do
4
+ let(:job_class) do
5
+ Class.new do
6
+ include Employer::Job
7
+
8
+ attribute :name
9
+ attribute :shape
10
+ end
11
+ end
12
+ let(:job) { job_class.new }
13
+
14
+ before(:each) do
15
+ stub_const("Namespaced::TestJob", job_class)
16
+ end
17
+
18
+ it "adds id attribute" do
19
+ job.id.should be_nil
20
+ job.id = 1
21
+ job.id.should eq(1)
22
+ end
23
+
24
+ describe "#try_again?" do
25
+ it "returns false" do
26
+ job.try_again?.should be_false
27
+ end
28
+ end
29
+
30
+ describe ".attribute" do
31
+ it "adds attribute accessors" do
32
+ job.name.should be_nil
33
+ job.name = "Block"
34
+ job.name.should eq("Block")
35
+
36
+ job.shape.should be_nil
37
+ job.shape = :square
38
+ job.shape.should eq(:square)
39
+ end
40
+
41
+ it "registers attribute names" do
42
+ job_class.attribute_names.should eq([:name, :shape])
43
+ job.attribute_names.should eq([:name, :shape])
44
+ end
45
+ end
46
+
47
+ describe "#serialize" do
48
+ it "builds a Hash of id, class name and attributes" do
49
+ job = Namespaced::TestJob.new
50
+ job.id = 1
51
+ job.name = "Ball"
52
+ job.shape = :circle
53
+
54
+ job.serialize.should eq({id: 1, class: "Namespaced::TestJob", attributes: {name: "Ball", shape: :circle}})
55
+ end
56
+
57
+ it "leaves out id if it is nil" do
58
+ job = Namespaced::TestJob.new
59
+ job.name = "Ball"
60
+ job.shape = :circle
61
+
62
+ job.serialize.should eq({class: "Namespaced::TestJob", attributes: {name: "Ball", shape: :circle}})
63
+ end
64
+
65
+ it "leaves out attributes with a nil value" do
66
+ job = Namespaced::TestJob.new
67
+ job.id = 1
68
+ job.name = "Ball"
69
+
70
+ job.serialize.should eq({id: 1, class: "Namespaced::TestJob", attributes: {name: "Ball"}})
71
+ end
72
+ end
73
+
74
+ describe ".deserialize" do
75
+ it "sets the id and attributes" do
76
+ serialized_job = {id: 1, class: "Namespaced::TestJob", attributes: {name: "Ball", shape: :circle}}
77
+ job = Namespaced::TestJob.deserialize(serialized_job)
78
+ job.should be_instance_of(Namespaced::TestJob)
79
+ job.id.should eq(1)
80
+ job.name.should eq("Ball")
81
+ job.shape.should eq(:circle)
82
+ end
83
+
84
+ it "must have the right class name" do
85
+ serialized_job = {id: 1, class: "TestJob", attributes: {name: "Ball", shape: :circle}}
86
+ expect { Namespaced::TestJob.deserialize(serialized_job) }.to raise_error(Employer::Errors::JobClassMismatch)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,92 @@
1
+ require "employer/pipeline"
2
+
3
+ describe Employer::Pipeline do
4
+ let(:pipeline) { Employer::Pipeline.new }
5
+ let(:backend) { double("Pipeline backend") }
6
+ let(:job) { double("Job") }
7
+
8
+ it "has a pluggable backend" do
9
+ pipeline.backend = backend
10
+ pipeline.backend.should eq(backend)
11
+ end
12
+
13
+ describe "#enqueue" do
14
+ it "serializes and then enqueues jobs using its backend" do
15
+ job_id = 1
16
+ serialized_job = {class: "TestJob"}
17
+
18
+ job.should_receive(:serialize).and_return(serialized_job)
19
+ backend.should_receive(:enqueue).with(serialized_job).and_return(job_id)
20
+
21
+ pipeline.backend = backend
22
+ pipeline.enqueue(job).should eq(job_id)
23
+ end
24
+
25
+ it "fails when no backend is set" do
26
+ expect { pipeline.enqueue(job) }.to raise_error(Employer::Errors::PipelineBackendRequired)
27
+ end
28
+ end
29
+
30
+ describe "#dequeue" do
31
+ it "dequeues job using its backend and properly instantiates it" do
32
+ stub_const("TestJob", Class.new)
33
+ job_id = 1
34
+ job.stub(:id).and_return(job_id)
35
+ serialized_job = {id: job_id, class: "TestJob"}
36
+
37
+ backend.should_receive(:dequeue).and_return(serialized_job)
38
+ TestJob.should_receive(:deserialize).and_return(job)
39
+
40
+ pipeline.backend = backend
41
+ dequeued_job = pipeline.dequeue
42
+ dequeued_job.should eq(job)
43
+ dequeued_job.id.should eq(job_id)
44
+ end
45
+
46
+ it "returns nil when there is no job in the backend's queue" do
47
+ backend.should_receive(:dequeue).and_return(nil)
48
+ pipeline.backend = backend
49
+ pipeline.dequeue.should be_nil
50
+ end
51
+
52
+ it "fails when no backend is set" do
53
+ expect { pipeline.dequeue }.to raise_error(Employer::Errors::PipelineBackendRequired)
54
+ end
55
+ end
56
+
57
+ describe "#complete" do
58
+ it "completes job using its backend" do
59
+ backend.should_receive(:complete).with(job)
60
+ pipeline.backend = backend
61
+ pipeline.complete(job)
62
+ end
63
+
64
+ it "fails when no backend is set" do
65
+ expect { pipeline.complete(double) }.to raise_error(Employer::Errors::PipelineBackendRequired)
66
+ end
67
+ end
68
+
69
+ describe "#reset" do
70
+ it "resets the job using its backend" do
71
+ backend.should_receive(:reset).with(job)
72
+ pipeline.backend = backend
73
+ pipeline.reset(job)
74
+ end
75
+
76
+ it "fails when no backend is set" do
77
+ expect { pipeline.reset(double) }.to raise_error(Employer::Errors::PipelineBackendRequired)
78
+ end
79
+ end
80
+
81
+ describe "#fail" do
82
+ it "fails the job using its backend" do
83
+ backend.should_receive(:fail).with(job)
84
+ pipeline.backend = backend
85
+ pipeline.fail(job)
86
+ end
87
+
88
+ it "fails when no backend is set" do
89
+ expect { pipeline.fail(double) }.to raise_error(Employer::Errors::PipelineBackendRequired)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,105 @@
1
+ require "employer/workshop"
2
+
3
+ describe Employer::Workshop do
4
+ before(:each) do
5
+ stub_const("Employer::Boss", Class.new)
6
+ stub_const("Employer::Pipeline", Class.new)
7
+ stub_const("Employer::Employees::ForkingEmployee", Class.new)
8
+ stub_const("Employer::Employees::ThreadingEmployee", Class.new)
9
+ stub_const("TestPipelineBackend", Class.new)
10
+ end
11
+
12
+ let(:boss) { double("Boss").as_null_object }
13
+ let(:config_code) do
14
+ <<CONFIG
15
+ pipeline_backend TestPipelineBackend.new
16
+ forking_employees 2
17
+ CONFIG
18
+ end
19
+ let(:workshop) do
20
+ Employer::Boss.should_receive(:new).and_return(boss)
21
+ Employer::Workshop.setup(config_code)
22
+ end
23
+
24
+ describe ".setup" do
25
+ it "sets up a workshop" do
26
+ pipeline_backend = double("Pipeline backend")
27
+ TestPipelineBackend.should_receive(:new).and_return(pipeline_backend)
28
+ boss = double("Boss")
29
+ pipeline = double("Pipeline")
30
+ forking_employee1 = double("Forking Employee 1")
31
+ forking_employee2 = double("Forking Employee 2")
32
+ forking_employee3 = double("Forking Employee 3")
33
+ threading_employee1 = double("Threading Employee 1")
34
+ threading_employee2 = double("Threading Employee 2")
35
+
36
+ Employer::Boss.should_receive(:new).and_return(boss)
37
+ Employer::Pipeline.should_receive(:new).and_return(pipeline)
38
+ boss.should_receive(:pipeline=).with(pipeline)
39
+ boss.should_receive(:pipeline).and_return(pipeline)
40
+ pipeline.should_receive(:backend=).with(pipeline_backend)
41
+ Employer::Employees::ForkingEmployee.should_receive(:new).and_return(forking_employee1, forking_employee2, forking_employee3)
42
+ Employer::Employees::ThreadingEmployee.should_receive(:new).and_return(threading_employee1, threading_employee2)
43
+ boss.should_receive(:allocate_employee).with(forking_employee1)
44
+ boss.should_receive(:allocate_employee).with(forking_employee2)
45
+ boss.should_receive(:allocate_employee).with(forking_employee3)
46
+ boss.should_receive(:allocate_employee).with(threading_employee1)
47
+ boss.should_receive(:allocate_employee).with(threading_employee2)
48
+
49
+ config_code = <<CONFIG
50
+ pipeline_backend TestPipelineBackend.new
51
+ forking_employees 3
52
+ threading_employees 2
53
+ CONFIG
54
+
55
+ workshop = Employer::Workshop.setup(config_code)
56
+ workshop.should be_instance_of(Employer::Workshop)
57
+ end
58
+ end
59
+
60
+ describe ".pipeline" do
61
+ it "sets up a pipeline to feed the workshop" do
62
+ boss = double("Boss").as_null_object
63
+ Employer::Boss.should_receive(:new).and_return(boss)
64
+ pipeline = double("Pipeline").as_null_object
65
+ boss.stub(:pipeline).and_return(pipeline)
66
+ Employer::Pipeline.should_receive(:new).and_return(pipeline)
67
+ pipeline_backend = double("Pipeline backend")
68
+ TestPipelineBackend.should_receive(:new).and_return(pipeline_backend)
69
+ File.should_receive(:read).with("config/employee.rb").and_return(config_code)
70
+
71
+ workshop_pipeline = Employer::Workshop.pipeline("config/employee.rb")
72
+ workshop_pipeline.should eq(pipeline)
73
+ end
74
+ end
75
+
76
+ describe "#run" do
77
+ it "should call manage on the boss" do
78
+ boss.should_receive(:manage)
79
+ workshop.run
80
+ end
81
+ end
82
+
83
+ describe "#stop" do
84
+ it "should call stop_managing on the boss" do
85
+ boss.should_receive(:stop_managing)
86
+ workshop.stop
87
+ end
88
+ end
89
+
90
+ describe "#stop_now" do
91
+ it "should call stop_managing and stop_employees on the boss" do
92
+ boss.should_receive(:stop_managing)
93
+ boss.should_receive(:stop_employees)
94
+ workshop.stop_now
95
+ end
96
+ end
97
+
98
+ describe "#pipeline" do
99
+ it "returns the pipeline" do
100
+ pipeline = double("Pipeline").as_null_object
101
+ boss.stub(:pipeline).and_return(pipeline)
102
+ workshop.pipeline.should eq(pipeline)
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,2 @@
1
+ require "pry"
2
+
@@ -0,0 +1,132 @@
1
+ shared_examples "an employee" do
2
+ let(:employee) { described_class.new }
3
+ let(:completing_job_class) do
4
+ Class.new do
5
+ def perform
6
+ sleep 0.2
7
+ end
8
+ end
9
+ end
10
+ let(:completing_job) { completing_job_class.new}
11
+ let(:failing_job_class) do
12
+ Class.new do
13
+ def perform
14
+ sleep 0.2
15
+ raise "Oops"
16
+ end
17
+ end
18
+ end
19
+ let(:failing_job) { failing_job_class.new}
20
+ let(:job) { double("Job") }
21
+
22
+ describe "#work" do
23
+ it "rejects a job while its already working on one" do
24
+ employee.should_receive(:free?).and_return(false)
25
+ expect { employee.work(job) }.to raise_error(Employer::Errors::EmployeeBusy)
26
+ end
27
+ end
28
+
29
+ describe "#work_in_progress?" do
30
+ it "is true while a job is running" do
31
+ employee.work(completing_job)
32
+ employee.work_in_progress?.should be_true
33
+ sleep 0.3
34
+ employee.work_in_progress?.should be_false
35
+ end
36
+ end
37
+
38
+ describe "#work_completed?" do
39
+ it "is true when a job completes" do
40
+ employee.work(completing_job)
41
+ employee.work_completed?.should be_false
42
+ sleep 0.3
43
+ employee.work_completed?.should be_true
44
+ end
45
+
46
+ it "is false when a job fails" do
47
+ employee.work(failing_job)
48
+ employee.work_completed?.should be_false
49
+ sleep 0.3
50
+ employee.work_completed?.should be_false
51
+ end
52
+ end
53
+
54
+ describe "#work_failed?" do
55
+ it "is true when a job fails" do
56
+ employee.work(failing_job)
57
+ employee.work_failed?.should be_false
58
+ sleep 0.3
59
+ employee.work_failed?.should be_true
60
+ end
61
+
62
+ it "is false when a job completes" do
63
+ employee.work(completing_job)
64
+ employee.work_failed?.should be_false
65
+ sleep 0.3
66
+ employee.work_failed?.should be_false
67
+ end
68
+ end
69
+
70
+ describe "#wait_for_completion" do
71
+ it "waits for the job to complete" do
72
+ employee.work(completing_job)
73
+ employee.wait_for_completion
74
+ employee.work_in_progress?.should be_false
75
+ employee.work_completed?.should be_true
76
+ employee.free?.should be_false
77
+ end
78
+ end
79
+
80
+ describe "#stop_working" do
81
+ it "stops job immediately" do
82
+ employee.work(completing_job)
83
+ employee.should_receive(:force_work_stop).and_call_original
84
+ employee.stop_working
85
+ employee.work_in_progress?.should be_false
86
+ employee.free?.should be_false
87
+ end
88
+
89
+ it "just returns if the job just completed" do
90
+ employee.work(completing_job)
91
+ employee.wait_for_completion
92
+ employee.should_receive(:force_work_stop).never
93
+ employee.stop_working
94
+ end
95
+
96
+ it "just returns if the job just failed" do
97
+ employee.work(failing_job)
98
+ employee.wait_for_completion
99
+ employee.should_receive(:force_work_stop).never
100
+ employee.stop_working
101
+ end
102
+ end
103
+
104
+ describe "#free" do
105
+ before(:each) do
106
+ employee.work(job)
107
+ end
108
+
109
+ it "clears job state after its completed a job, allowing for a new job to be worked on" do
110
+ employee.should_receive(:work_completed?).and_return(true)
111
+ employee.free
112
+ employee.free?.should be_true
113
+ employee.job.should be_nil
114
+ end
115
+
116
+ it "clears job state after it failed a job, allowing for a new job to be worked on" do
117
+ employee.should_receive(:work_completed?).and_return(false)
118
+ employee.should_receive(:work_failed?).and_return(true)
119
+ employee.free
120
+ employee.free?.should be_true
121
+ employee.job.should be_nil
122
+ end
123
+
124
+ it "does not clear job state while it is still busy" do
125
+ employee.should_receive(:work_completed?).and_return(false)
126
+ employee.should_receive(:work_failed?).and_return(false)
127
+ employee.free
128
+ employee.free?.should be_false
129
+ employee.job.should eq(job)
130
+ end
131
+ end
132
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: employer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: '0.1'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,25 +9,101 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-25 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2013-02-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.17'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0.17'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: pry
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
14
62
  description:
15
63
  email:
16
64
  - mark@without-brains.net
17
- executables: []
65
+ executables:
66
+ - employer
18
67
  extensions: []
19
68
  extra_rdoc_files: []
20
69
  files:
21
70
  - .gitignore
71
+ - .pryrc
72
+ - .rspec
22
73
  - Gemfile
23
74
  - LICENSE.txt
24
75
  - README.md
25
76
  - Rakefile
77
+ - bin/employer
26
78
  - employer.gemspec
27
79
  - lib/employer.rb
80
+ - lib/employer/boss.rb
81
+ - lib/employer/cli.rb
82
+ - lib/employer/employees.rb
83
+ - lib/employer/employees/abstract_employee.rb
84
+ - lib/employer/employees/forking_employee.rb
85
+ - lib/employer/employees/threading_employee.rb
86
+ - lib/employer/errors.rb
87
+ - lib/employer/errors/employee_busy.rb
88
+ - lib/employer/errors/error.rb
89
+ - lib/employer/errors/job_class_mismatch.rb
90
+ - lib/employer/errors/no_employee_free.rb
91
+ - lib/employer/errors/pipeline_backend_required.rb
92
+ - lib/employer/job.rb
93
+ - lib/employer/pipeline.rb
28
94
  - lib/employer/version.rb
29
- homepage: ''
30
- licenses: []
95
+ - lib/employer/workshop.rb
96
+ - spec/employer/boss_spec.rb
97
+ - spec/employer/employees/forking_employee_spec.rb
98
+ - spec/employer/employees/threading_employee_spec.rb
99
+ - spec/employer/job_spec.rb
100
+ - spec/employer/pipeline_spec.rb
101
+ - spec/employer/workshop_spec.rb
102
+ - spec/spec_helper.rb
103
+ - spec/support/shared_examples/employee.rb
104
+ homepage: https://github.com/mkremer/employer
105
+ licenses:
106
+ - MIT
31
107
  post_install_message:
32
108
  rdoc_options: []
33
109
  require_paths:
@@ -50,4 +126,12 @@ rubygems_version: 1.8.23
50
126
  signing_key:
51
127
  specification_version: 3
52
128
  summary: Job processing made easy
53
- test_files: []
129
+ test_files:
130
+ - spec/employer/boss_spec.rb
131
+ - spec/employer/employees/forking_employee_spec.rb
132
+ - spec/employer/employees/threading_employee_spec.rb
133
+ - spec/employer/job_spec.rb
134
+ - spec/employer/pipeline_spec.rb
135
+ - spec/employer/workshop_spec.rb
136
+ - spec/spec_helper.rb
137
+ - spec/support/shared_examples/employee.rb