employer 0.0.1 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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