resque-stages 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.rubocop.yml +103 -0
- data/.rubocop_todo.yml +34 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +172 -0
- data/LICENSE.txt +21 -0
- data/README.md +250 -0
- data/Rakefile +8 -0
- data/bin/console +16 -0
- data/bin/setup +8 -0
- data/lib/resque-stages.rb +11 -0
- data/lib/resque/plugins/stages.rb +110 -0
- data/lib/resque/plugins/stages/cleaner.rb +36 -0
- data/lib/resque/plugins/stages/redis_access.rb +16 -0
- data/lib/resque/plugins/stages/staged_group.rb +181 -0
- data/lib/resque/plugins/stages/staged_group_list.rb +79 -0
- data/lib/resque/plugins/stages/staged_group_stage.rb +275 -0
- data/lib/resque/plugins/stages/staged_job.rb +271 -0
- data/lib/resque/plugins/stages/version.rb +9 -0
- data/lib/resque/server/public/stages.css +56 -0
- data/lib/resque/server/views/_group_stages_list_pagination.erb +67 -0
- data/lib/resque/server/views/_group_stages_list_table.erb +25 -0
- data/lib/resque/server/views/_stage_job_list_pagination.erb +72 -0
- data/lib/resque/server/views/_stage_job_list_table.erb +46 -0
- data/lib/resque/server/views/_staged_group_list_pagination.erb +67 -0
- data/lib/resque/server/views/_staged_group_list_table.erb +44 -0
- data/lib/resque/server/views/group_stages_list.erb +58 -0
- data/lib/resque/server/views/groups.erb +40 -0
- data/lib/resque/server/views/job_details.erb +91 -0
- data/lib/resque/server/views/stage.erb +64 -0
- data/lib/resque/stages_server.rb +240 -0
- data/read_me/groups_list.png +0 -0
- data/read_me/job.png +0 -0
- data/read_me/stage.png +0 -0
- data/read_me/stages.png +0 -0
- data/resque-stages.gemspec +49 -0
- data/spec/rails_helper.rb +40 -0
- data/spec/resque/plugins/stages/cleaner_spec.rb +82 -0
- data/spec/resque/plugins/stages/staged_group_list_spec.rb +96 -0
- data/spec/resque/plugins/stages/staged_group_spec.rb +226 -0
- data/spec/resque/plugins/stages/staged_group_stage_spec.rb +293 -0
- data/spec/resque/plugins/stages/staged_job_spec.rb +324 -0
- data/spec/resque/plugins/stages_spec.rb +369 -0
- data/spec/resque/server/public/stages.css_spec.rb +18 -0
- data/spec/resque/server/views/group_stages_list.erb_spec.rb +67 -0
- data/spec/resque/server/views/groups.erb_spec.rb +81 -0
- data/spec/resque/server/views/job_details.erb_spec.rb +100 -0
- data/spec/resque/server/views/stage.erb_spec.rb +68 -0
- data/spec/spec_helper.rb +104 -0
- data/spec/support/01_utils/fake_logger.rb +7 -0
- data/spec/support/config/redis-auth.yml +12 -0
- data/spec/support/fake_logger.rb +7 -0
- data/spec/support/jobs/basic_job.rb +17 -0
- data/spec/support/jobs/compressed_job.rb +18 -0
- data/spec/support/jobs/retry_job.rb +21 -0
- data/spec/support/purge_all.rb +15 -0
- metadata +297 -0
Binary file
|
data/read_me/job.png
ADDED
Binary file
|
data/read_me/stage.png
ADDED
Binary file
|
data/read_me/stages.png
ADDED
Binary file
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/resque/plugins/stages/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "resque-stages"
|
7
|
+
spec.version = Resque::Plugins::Stages::VERSION
|
8
|
+
spec.authors = ["RealNobody"]
|
9
|
+
spec.email = ["RealNobody1@cox.net"]
|
10
|
+
|
11
|
+
spec.summary = "A Resque gem for executing batches of jobs in stages to ensure that some jobs complete execution before other jobs."
|
12
|
+
spec.description = "A Resque gem for executing batches of jobs in stages. All jobs in a stage must complete before any job in the next" \
|
13
|
+
" stage is started allowing you to be sure that jobs are not executed out of sequence."
|
14
|
+
spec.homepage = "https://github.com/RealNobody/resque-stages"
|
15
|
+
spec.license = "MIT"
|
16
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
17
|
+
|
18
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
19
|
+
|
20
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
21
|
+
spec.metadata["source_code_uri"] = "https://github.com/RealNobody/resque-stages"
|
22
|
+
spec.metadata["changelog_uri"] = "https://github.com/RealNobody/resque-stages"
|
23
|
+
|
24
|
+
# Specify which files should be added to the gem when it is released.
|
25
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
26
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
27
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
28
|
+
end
|
29
|
+
|
30
|
+
spec.test_files = Dir["spec/**/*"]
|
31
|
+
|
32
|
+
spec.bindir = "exe"
|
33
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
34
|
+
spec.require_paths = ["lib"]
|
35
|
+
|
36
|
+
spec.add_dependency "redis"
|
37
|
+
spec.add_dependency "redis-namespace"
|
38
|
+
spec.add_dependency "resque"
|
39
|
+
|
40
|
+
spec.add_development_dependency "activesupport"
|
41
|
+
spec.add_development_dependency "cornucopia"
|
42
|
+
spec.add_development_dependency "faker"
|
43
|
+
spec.add_development_dependency "gem-release"
|
44
|
+
spec.add_development_dependency "resque-compressible"
|
45
|
+
spec.add_development_dependency "resque-retry"
|
46
|
+
spec.add_development_dependency "rspec-rails", "> 3.9.1"
|
47
|
+
spec.add_development_dependency "rubocop"
|
48
|
+
spec.add_development_dependency "simplecov"
|
49
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "simplecov"
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
require "active_support/testing/time_helpers"
|
7
|
+
require "spec_helper"
|
8
|
+
require "cornucopia/rspec_hooks"
|
9
|
+
require "resque-stages"
|
10
|
+
require "resque-compressible"
|
11
|
+
require "resque-retry"
|
12
|
+
require "faker"
|
13
|
+
require "rack/test"
|
14
|
+
require "resque/server"
|
15
|
+
require "resque/stages_server"
|
16
|
+
require "resque-scheduler"
|
17
|
+
|
18
|
+
Dir[File.expand_path("spec/support/**/*.rb"), File.dirname(__FILE__)].sort.each do |f|
|
19
|
+
require f unless File.directory?(f)
|
20
|
+
end
|
21
|
+
|
22
|
+
FileUtils.mkdir_p(File.expand_path("../log", File.dirname(__FILE__)))
|
23
|
+
|
24
|
+
redis_logger = Logger.new(File.expand_path("../log/redis.log", File.dirname(__FILE__)))
|
25
|
+
redis_logger.level = Logger::DEBUG
|
26
|
+
redis_logger.formatter = Logger::Formatter.new
|
27
|
+
|
28
|
+
redis_options = YAML.load_file(File.expand_path("support/config/redis-auth.yml", File.dirname(__FILE__)))
|
29
|
+
Redis.current = Redis.new(redis_options.merge(logger: redis_logger))
|
30
|
+
|
31
|
+
Resque.redis = Redis.new(redis_options)
|
32
|
+
Resque.inline = true
|
33
|
+
|
34
|
+
RSpec.configure do |config|
|
35
|
+
config.include ActiveSupport::Testing::TimeHelpers
|
36
|
+
end
|
37
|
+
|
38
|
+
# Cornucopia::Util::Configuration.context_seed = 1
|
39
|
+
# Cornucopia::Util::Configuration.seed = 1
|
40
|
+
# Cornucopia::Util::Configuration.order_seed = 1
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails_helper"
|
4
|
+
|
5
|
+
RSpec.describe Resque::Plugins::Stages::Cleaner do
|
6
|
+
let(:group) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid) }
|
7
|
+
let(:load_group) { Resque::Plugins::Stages::StagedGroup.new(group.group_id) }
|
8
|
+
let(:stage) { group.current_stage }
|
9
|
+
let(:load_stage) { Resque::Plugins::Stages::StagedGroupStage.new(stage.group_stage_id) }
|
10
|
+
let!(:job) { stage.enqueue BasicJob }
|
11
|
+
let(:load_job) { Resque::Plugins::Stages::StagedJob.new(job.job_id) }
|
12
|
+
|
13
|
+
describe "purge_all" do
|
14
|
+
it "deletes all values" do
|
15
|
+
Resque::Plugins::Stages::Cleaner.purge_all
|
16
|
+
|
17
|
+
expect(job).to be_blank
|
18
|
+
expect(stage).to be_blank
|
19
|
+
expect(group).to be_blank
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "cleanup_jobs" do
|
24
|
+
it "does not recreate the group if the group info is deleted" do
|
25
|
+
job.redis.keys("StagedGroup::*::Info").each { |key| job.redis.del(key) }
|
26
|
+
|
27
|
+
Resque::Plugins::Stages::Cleaner.cleanup_jobs
|
28
|
+
expect(load_group.stages.values).to be_include stage
|
29
|
+
end
|
30
|
+
|
31
|
+
it "does not recreate the group if the group list is deleted" do
|
32
|
+
job.redis.keys("StagedGroup::*").each do |key|
|
33
|
+
next if key.include?("::Info")
|
34
|
+
|
35
|
+
job.redis.del(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
Resque::Plugins::Stages::Cleaner.cleanup_jobs
|
39
|
+
expect(load_group.stages.values).to be_include stage
|
40
|
+
end
|
41
|
+
|
42
|
+
it "recreates the group if the group is deleted" do
|
43
|
+
job.redis.keys("StagedGroup::*").each { |key| job.redis.del(key) }
|
44
|
+
|
45
|
+
Resque::Plugins::Stages::Cleaner.cleanup_jobs
|
46
|
+
expect(load_group.stages.values).to be_include stage
|
47
|
+
end
|
48
|
+
|
49
|
+
it "creates a new group if the group is deleted and cannot be found" do
|
50
|
+
job.redis.keys("StagedGroup::*").each { |key| job.redis.del(key) }
|
51
|
+
job.redis.keys("StagedGroupStage::*::staged_group").each { |key| job.redis.del(key) }
|
52
|
+
|
53
|
+
Resque::Plugins::Stages::Cleaner.cleanup_jobs
|
54
|
+
expect(load_group.stages.values).not_to be_include stage
|
55
|
+
expect(load_stage.staged_group.stages.values).to be_include stage
|
56
|
+
expect(load_stage.jobs).to be_include(job)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "does not create a new stage if it can be found" do
|
60
|
+
job.redis.keys("StagedGroup::*").each { |key| job.redis.del(key) }
|
61
|
+
job.redis.keys("StagedGroupStage::*").each do |key|
|
62
|
+
next if key.include?("::staged_group")
|
63
|
+
|
64
|
+
job.redis.del(key)
|
65
|
+
end
|
66
|
+
|
67
|
+
Resque::Plugins::Stages::Cleaner.cleanup_jobs
|
68
|
+
expect(load_group.stages.values).to be_include stage
|
69
|
+
expect(load_stage.jobs).to be_include(job)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "create a new stage if it can be found" do
|
73
|
+
job.redis.keys("StagedGroup::*").each { |key| job.redis.del(key) }
|
74
|
+
job.redis.keys("StagedGroupStage::*").each { |key| job.redis.del(key) }
|
75
|
+
|
76
|
+
Resque::Plugins::Stages::Cleaner.cleanup_jobs
|
77
|
+
expect(load_stage.jobs).not_to be_include(job)
|
78
|
+
expect(load_job.staged_group_stage.jobs).to be_include(job)
|
79
|
+
expect(load_job.staged_group_stage.staged_group.stages.values).to be_include(load_job.staged_group_stage)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails_helper"
|
4
|
+
|
5
|
+
RSpec.describe Resque::Plugins::Stages::StagedGroupList do
|
6
|
+
let(:groups) do
|
7
|
+
[travel_to(3.hours.ago) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid, description: "s Description 1") },
|
8
|
+
travel_to(0.hours.ago) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid, description: "a Description 2") },
|
9
|
+
travel_to(2.hours.ago) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid, description: "not A Description 3") },
|
10
|
+
travel_to(1.hours.ago) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid) }]
|
11
|
+
end
|
12
|
+
let(:group_list) { Resque::Plugins::Stages::StagedGroupList.new }
|
13
|
+
|
14
|
+
describe "groups" do
|
15
|
+
it "lists all groups" do
|
16
|
+
groups
|
17
|
+
expect(group_list.groups.map(&:group_id).sort).to eq groups.map(&:group_id).sort
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "num_groups" do
|
22
|
+
it "counts the groups" do
|
23
|
+
groups
|
24
|
+
expect(group_list.num_groups).to eq 4
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "paginated_groups" do
|
29
|
+
it "sorts groups by description" do
|
30
|
+
groups
|
31
|
+
expect(group_list.paginated_groups("description", "asc", 2, 2)).to eq [groups[2], groups[0]]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "sorts groups by created_at" do
|
35
|
+
groups
|
36
|
+
expect(group_list.paginated_groups("created_at", "asc", 2, 2)).to eq [groups[3], groups[1]]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "sorts groups by num_stages" do
|
40
|
+
groups.each.with_index do |group, index|
|
41
|
+
index.times { |count| group.stage(count) }
|
42
|
+
end
|
43
|
+
|
44
|
+
expect(group_list.paginated_groups("num_stages", "asc", 2, 2)).to eq [groups[2], groups[3]]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "delete_all" do
|
49
|
+
it "deletes all groups" do
|
50
|
+
groups
|
51
|
+
group_list.delete_all
|
52
|
+
|
53
|
+
expect(group_list.groups).to be_empty
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "add_group" do
|
58
|
+
it "adds a group to the list when the group is created" do
|
59
|
+
expect(group_list.groups).to be_empty
|
60
|
+
|
61
|
+
group = Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid, description: "Description 1")
|
62
|
+
|
63
|
+
expect(group_list.groups).to eq [group]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "remove_group" do
|
68
|
+
it "removes a group from the list when the group is deleted" do
|
69
|
+
expect(group_list.groups).to be_empty
|
70
|
+
|
71
|
+
group = Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid, description: "Description 1")
|
72
|
+
|
73
|
+
expect(group_list.groups).to eq [group]
|
74
|
+
|
75
|
+
group.delete
|
76
|
+
|
77
|
+
expect(group_list.groups).to be_empty
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#order_param" do
|
82
|
+
it "returns asc for any column other than the current one" do
|
83
|
+
expect(group_list.order_param("sort_option",
|
84
|
+
"current_sort",
|
85
|
+
%w[asc desc].sample)).to eq "asc"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "returns desc for the current column if it is asc" do
|
89
|
+
expect(group_list.order_param("sort_option", "sort_option", "asc")).to eq "desc"
|
90
|
+
end
|
91
|
+
|
92
|
+
it "returns asc for the current column if it is desc" do
|
93
|
+
expect(group_list.order_param("sort_option", "sort_option", "desc")).to eq "asc"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails_helper"
|
4
|
+
|
5
|
+
RSpec.describe Resque::Plugins::Stages::StagedGroup do
|
6
|
+
let(:group) { Resque::Plugins::Stages::StagedGroup.new SecureRandom.uuid }
|
7
|
+
let(:load_group) { Resque::Plugins::Stages::StagedGroup.new group.group_id }
|
8
|
+
|
9
|
+
describe "within_a_grouping" do
|
10
|
+
it "yields a grouping" do
|
11
|
+
Resque::Plugins::Stages::StagedGroup.within_a_grouping do |grouping|
|
12
|
+
expect(grouping).to be_a Resque::Plugins::Stages::StagedGroup
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "initiates the grouping" do
|
17
|
+
test_grouping = nil
|
18
|
+
|
19
|
+
Resque::Plugins::Stages::StagedGroup.within_a_grouping do |grouping|
|
20
|
+
test_grouping = grouping
|
21
|
+
allow(grouping).to receive(:initiate)
|
22
|
+
end
|
23
|
+
|
24
|
+
expect(test_grouping).to have_received(:initiate)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "description" do
|
29
|
+
it "defaults the description to the group_id" do
|
30
|
+
group.description = nil
|
31
|
+
|
32
|
+
expect(load_group.description).to eq group.group_id.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
it "sets the description to anything" do
|
36
|
+
group.description = "This is a description"
|
37
|
+
|
38
|
+
expect(load_group.description).to eq "This is a description"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "created_at" do
|
43
|
+
it "returns the created_at time" do
|
44
|
+
time = Time.now
|
45
|
+
|
46
|
+
travel_to(time) do
|
47
|
+
time = Time.now
|
48
|
+
group
|
49
|
+
end
|
50
|
+
|
51
|
+
travel_to(time + 1.day) do
|
52
|
+
expect(load_group.created_at).to eq time
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "with stages" do
|
58
|
+
let(:stages) do
|
59
|
+
stage = group.stage(8)
|
60
|
+
Resque::Plugins::Stages::StagedJob.create_job stage, BasicJob
|
61
|
+
stage.redis.hset(stage.send(:staged_group_key), "status", :running.to_s)
|
62
|
+
|
63
|
+
stage = group.stage(1)
|
64
|
+
Resque::Plugins::Stages::StagedJob.create_job stage, BasicJob
|
65
|
+
stage.redis.hset(stage.send(:staged_group_key), "status", :complete.to_s)
|
66
|
+
|
67
|
+
stage = group.stage(2)
|
68
|
+
Resque::Plugins::Stages::StagedJob.create_job stage, BasicJob
|
69
|
+
stage.redis.hset(stage.send(:staged_group_key), "status", :pending.to_s)
|
70
|
+
|
71
|
+
stage = group.stage(0)
|
72
|
+
Resque::Plugins::Stages::StagedJob.create_job stage, BasicJob
|
73
|
+
stage.redis.hset(stage.send(:staged_group_key), "status", :complete.to_s)
|
74
|
+
|
75
|
+
group.stages
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "initiate" do
|
79
|
+
it "initiates the first non-completed stage" do
|
80
|
+
allow(group).to receive(:stages).and_return stages
|
81
|
+
|
82
|
+
stages.each_value { |stage| allow(stage).to receive(:initiate) }
|
83
|
+
|
84
|
+
group.initiate
|
85
|
+
|
86
|
+
expect(stages[2]).to have_received(:initiate)
|
87
|
+
stages.each do |key, stage|
|
88
|
+
next if key == 2
|
89
|
+
|
90
|
+
expect(stage).not_to have_received(:initiate)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "current_stage" do
|
96
|
+
it "returns the first non-complete stage" do
|
97
|
+
stages
|
98
|
+
|
99
|
+
expect(load_group.current_stage).to eq stages[2]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "returns the first non-complete stage" do
|
103
|
+
stage = stages[2]
|
104
|
+
stage.redis.hset(stage.send(:staged_group_key), "status", :complete.to_s)
|
105
|
+
|
106
|
+
expect(load_group.current_stage).to eq stages[8]
|
107
|
+
end
|
108
|
+
|
109
|
+
it "builds a new stage if all current stages are complete" do
|
110
|
+
stages[2].status = :complete
|
111
|
+
stages[8].status = :complete
|
112
|
+
|
113
|
+
stage = load_group.current_stage
|
114
|
+
expect(stage.number).to eq 1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "stage_completed" do
|
119
|
+
it "initiates the next stage when a stage is compelted" do
|
120
|
+
allow(group).to receive(:stages).and_return stages
|
121
|
+
|
122
|
+
stages.each_value do |stage|
|
123
|
+
allow(stage).to receive(:initiate)
|
124
|
+
allow(stage).to receive(:staged_group).and_return group
|
125
|
+
end
|
126
|
+
|
127
|
+
stages[8].status = :complete
|
128
|
+
|
129
|
+
expect(stages[2]).to have_received(:initiate)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "deletes the group if the last stage is completed" do
|
133
|
+
allow(group).to receive(:stages).and_return stages
|
134
|
+
allow(group).to receive(:delete)
|
135
|
+
|
136
|
+
stages.each_value do |stage|
|
137
|
+
allow(stage).to receive(:initiate)
|
138
|
+
allow(stage).to receive(:staged_group).and_return group
|
139
|
+
end
|
140
|
+
|
141
|
+
stages[8].status = :complete
|
142
|
+
stages[2].status = :complete
|
143
|
+
|
144
|
+
expect(group).to have_received(:delete)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "last_stage" do
|
149
|
+
it "returns the largest stage" do
|
150
|
+
stages
|
151
|
+
|
152
|
+
expect(load_group.last_stage).to eq stages[8]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "stages" do
|
157
|
+
it "returns a hash of all stages keyed by the stage number" do
|
158
|
+
stages
|
159
|
+
|
160
|
+
expect(load_group.stages).to eq stages
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "paged_stages" do
|
165
|
+
it "a page of data" do
|
166
|
+
stages
|
167
|
+
|
168
|
+
expect(load_group.paged_stages(1, 2)).to eq [stages[8], stages[1]]
|
169
|
+
end
|
170
|
+
|
171
|
+
it "a mid page of data" do
|
172
|
+
stages
|
173
|
+
|
174
|
+
expect(load_group.paged_stages(2, 2)).to eq [stages[2], stages[0]]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "num_stages" do
|
179
|
+
it "a page of data" do
|
180
|
+
stages
|
181
|
+
|
182
|
+
expect(load_group.num_stages).to eq 4
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "stage(value)" do
|
187
|
+
it "returns the stage with that number if it exists" do
|
188
|
+
stages
|
189
|
+
|
190
|
+
stage = load_group.stage(2)
|
191
|
+
expect(stage).to eq stages[2]
|
192
|
+
expect(stage.number).to eq 2
|
193
|
+
end
|
194
|
+
|
195
|
+
it "returns a new stage with that number if it does not exist" do
|
196
|
+
stages
|
197
|
+
|
198
|
+
stage = load_group.stage(6)
|
199
|
+
expect(stages).not_to be_include stage
|
200
|
+
expect(stage.number).to eq 6
|
201
|
+
|
202
|
+
expect(load_group.stages.length).to eq stages.length + 1
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "delete" do
|
207
|
+
it "deletes all stages" do
|
208
|
+
allow(group).to receive(:stages).and_return stages
|
209
|
+
|
210
|
+
stages.each { |_key, stage| allow(stage).to receive(:delete) }
|
211
|
+
|
212
|
+
group.delete
|
213
|
+
|
214
|
+
stages.each_value do |stage|
|
215
|
+
expect(stage).to have_received(:delete)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
it "removes a stage when it is deleted" do
|
220
|
+
stages[2].delete
|
221
|
+
|
222
|
+
expect(load_group.stages[2]).to be_nil
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|