diff --git a/lib/easy_stalk/configuration.rb b/lib/easy_stalk/configuration.rb index 5f05c5a..b9a9442 100644 --- a/lib/easy_stalk/configuration.rb +++ b/lib/easy_stalk/configuration.rb @@ -28,7 +28,7 @@ def initialize log.progname = Module.nesting.last.name end @default_worker_on_fail = Proc.new { |job_class, job_body, ex| - EasyStalk.logger.error "Worker for #{job_class} on tube[#{job_class.tube_name}] failed #{ex.message}" + EasyStalk.logger.error "Worker for #{job_class} on tube[#{job_class.get_tube_name}] failed #{ex.message}" EasyStalk.logger.error ex.backtrace.join("\n") } diff --git a/lib/easy_stalk/job.rb b/lib/easy_stalk/job.rb index 3cab108..0816119 100644 --- a/lib/easy_stalk/job.rb +++ b/lib/easy_stalk/job.rb @@ -10,70 +10,75 @@ class Job DEFAULT_SERIALIZABLE_CONTEXT_KEYS = [] def self.tube_name(tube=nil) - if tube - @tube_name = tube - else - tube_prefix + (@tube_name || name.split('::').last) - end + @tube_name = tube + end + def self.get_tube_name + get_tube_prefix + (@tube_name || name.split('::').last) end def self.tube_prefix(prefix=nil) - if prefix - @tube_prefix = prefix - else - @tube_prefix || EasyStalk.configuration.default_tube_prefix + define_singleton_method :get_tube_prefix do + prefix + end + end + def self.get_tube_prefix + EasyStalk.configuration.default_tube_prefix + end + + def self.retry_times(attempts=nil) + # max number of times to retry job before burying + define_singleton_method :get_retry_times do + attempts end end + def self.get_retry_times + EasyStalk.configuration.default_retry_times + end def self.priority(pri=nil) # integer < 2**32. 0 is highest - if pri - @priority = pri - else - @priority || EasyStalk.configuration.default_priority + define_method :priority do + pri end end + def priority + EasyStalk.configuration.default_priority + end def self.time_to_run(seconds=nil) # integer seconds to run this job - if seconds - @time_to_run = seconds - else - @time_to_run || EasyStalk.configuration.default_time_to_run + define_method :time_to_run do + seconds end end + def time_to_run + EasyStalk.configuration.default_time_to_run + end def self.delay(seconds=nil) # integer seconds before job is in ready queue - if seconds - @delay = seconds - else - @delay || EasyStalk.configuration.default_delay + define_method :delay do + seconds end end - - def self.retry_times(attempts=nil) - # max number of times to retry job before burying - if attempts - @retry_times = attempts - else - @retry_times || EasyStalk.configuration.default_retry_times - end + def delay + EasyStalk.configuration.default_delay end def self.serializable_context_keys(*keys) - if keys.size > 0 - @serializable_context_keys = keys - else - @serializable_context_keys || DEFAULT_SERIALIZABLE_CONTEXT_KEYS + define_method :serializable_context_keys do + keys end end + def serializable_context_keys + DEFAULT_SERIALIZABLE_CONTEXT_KEYS + end def enqueue(beanstalk_connection, priority: nil, time_to_run: nil, delay: nil, delay_until: nil) - tube = beanstalk_connection.tubes[self.class.tube_name] - pri = priority || self.class.priority - ttr = time_to_run || self.class.time_to_run - delay = delay || self.class.delay + tube = beanstalk_connection.tubes[self.class.get_tube_name] + pri = priority || self.priority + ttr = time_to_run || self.time_to_run + delay = delay || self.delay if delay_until && DateTime === delay_until days = delay_until - DateTime.now @@ -84,7 +89,7 @@ def enqueue(beanstalk_connection, priority: nil, time_to_run: nil, delay: nil, d end def job_data - data = context.to_h.select { |key, value| self.class.serializable_context_keys.include? key } + data = context.to_h.select { |key, value| self.serializable_context_keys.include? key } JSON.dump(data) end diff --git a/lib/easy_stalk/tasks.rb b/lib/easy_stalk/tasks.rb index 78c8a74..d6ac99c 100644 --- a/lib/easy_stalk/tasks.rb +++ b/lib/easy_stalk/tasks.rb @@ -10,7 +10,7 @@ tubes = if args.extras.size > 0 EasyStalk::Job.descendants.select do |job| - tubs.include?(job.tube_name) + tubs.include?(job.get_tube_name) end end diff --git a/lib/easy_stalk/worker.rb b/lib/easy_stalk/worker.rb index 8fd1e65..cc04743 100644 --- a/lib/easy_stalk/worker.rb +++ b/lib/easy_stalk/worker.rb @@ -23,7 +23,7 @@ def work(job_classes = nil, on_fail: nil) @cancelled = false tube_class_hash = Hash[ - job_classes.map { |cls| [cls.tube_name, cls] } + job_classes.map { |cls| [cls.get_tube_name, cls] } ] pool = EasyStalk::Client.create_worker_pool(tube_class_hash.keys) @@ -40,7 +40,8 @@ def work(job_classes = nil, on_fail: nil) rescue => ex # Job issued a failed context or raised an unhandled exception job_class = tube_class_hash[job.tube] - if job.stats.releases <= job_class.retry_times + + if job.stats.releases <= job_class.get_retry_times # Re-enqueue with stepped delay release_with_delay(job) else @@ -54,7 +55,7 @@ def work(job_classes = nil, on_fail: nil) end end - jobs_list = job_classes.map { |job_class| "#{job_class} on tube #{job_class.tube_name}" }.join(", ") + jobs_list = job_classes.map { |job_class| "#{job_class} on tube #{job_class.get_tube_name}" }.join(", ") EasyStalk.logger.info "Worker running #{jobs_list} has been stopped" end diff --git a/spec/lib/easy_stalk/job_spec.rb b/spec/lib/easy_stalk/job_spec.rb index 80f1b28..3dc080f 100644 --- a/spec/lib/easy_stalk/job_spec.rb +++ b/spec/lib/easy_stalk/job_spec.rb @@ -3,34 +3,66 @@ describe EasyStalk::Job do - class EasyStalk::MockJob < EasyStalk::Job + before do + class EasyStalk::MockJob < EasyStalk::Job + end + end + + after do + EasyStalk.send(:remove_const, :MockJob) end - describe EasyStalk::MockJob do + + subject { EasyStalk::MockJob.new } + + context "with a MockJob" do after do EasyStalk.configure end describe 'self << class' do - subject { described_class } + subject { EasyStalk::MockJob } describe '.tube_name' do it 'defaults to class name' do EasyStalk.configure { |config| config.default_tube_prefix = "rating.test." } - expect(subject.tube_name).to eq "rating.test.MockJob" + expect(subject.get_tube_name).to eq "rating.test.MockJob" end it 'can be set manually' do class MockJobWithName < subject tube_name "bar" end EasyStalk.configure { |config| config.default_tube_prefix = "rating.test." } - expect(MockJobWithName.new.class.tube_name).to eq "rating.test.bar" + expect(MockJobWithName.new.class.get_tube_name).to eq "rating.test.bar" + Object.send(:remove_const, :MockJobWithName) end it 'properly uses a prefix' do class MockJobWithNameAndPrefix < subject tube_name "bar" tube_prefix "foo." end - expect(MockJobWithNameAndPrefix.new.class.tube_name).to eq "foo.bar" + expect(MockJobWithNameAndPrefix.new.class.get_tube_name).to eq "foo.bar" + Object.send(:remove_const, :MockJobWithNameAndPrefix) + end + it 'properly uses an inherited prefix' do + class MockJobWithName < subject + tube_prefix "foo." + end + class MockJobWithNameAndPrefix < MockJobWithName + tube_name "bar" + end + expect(MockJobWithNameAndPrefix.new.class.get_tube_name).to eq "foo.bar" + Object.send(:remove_const, :MockJobWithName) + Object.send(:remove_const, :MockJobWithNameAndPrefix) + end + it 'does not inherit tube_name' do + class MockJobWithName < subject + tube_name "bar" + end + class MockChildJobWithoutName < MockJobWithName + end + expect(MockChildJobWithoutName.new.class.get_tube_name).to eq "MockChildJobWithoutName" + Object.send(:remove_const, :MockJobWithName) + Object.send(:remove_const, :MockChildJobWithoutName) end end @@ -39,14 +71,25 @@ class MockJobWithNameAndPrefix < subject class MockJobWithPrefix < subject tube_prefix "bar." end - expect(MockJobWithPrefix.new().class.tube_prefix).to eq "bar." + expect(MockJobWithPrefix.new().class.get_tube_prefix).to eq "bar." + Object.send(:remove_const, :MockJobWithPrefix) + end + it 'uses inheritance if present' do + class MockJobWithPrefix < subject + tube_prefix "bar." + end + class MockChildJobWithPrefix < MockJobWithPrefix + end + expect(MockChildJobWithPrefix.new().class.get_tube_prefix).to eq "bar." + Object.send(:remove_const, :MockJobWithPrefix) + Object.send(:remove_const, :MockChildJobWithPrefix) end it 'uses the env if present' do EasyStalk.configure { |config| config.default_tube_prefix = "foo." } - expect(subject.tube_prefix).to eq "foo." + expect(subject.get_tube_prefix).to eq "foo." end it 'uses blank if no env' do - expect(subject.tube_prefix).to eq "" + expect(subject.get_tube_prefix).to eq "" end end @@ -55,10 +98,21 @@ class MockJobWithPrefix < subject class MockJobWithPri < subject priority 25 end - expect(MockJobWithPri.new().class.priority).to eq 25 + expect(MockJobWithPri.new().priority).to eq 25 + Object.send(:remove_const, :MockJobWithPri) + end + it 'uses inheritance if present' do + class MockJobWithPri < subject + priority 25 + end + class MockChildJobWithPri < MockJobWithPri + end + expect(MockChildJobWithPri.new().priority).to eq 25 + Object.send(:remove_const, :MockJobWithPri) + Object.send(:remove_const, :MockChildJobWithPri) end it 'uses default if not set' do - expect(subject.priority).to eq EasyStalk::Configuration::DEFAULT_PRI + expect(subject.new().priority).to eq EasyStalk::Configuration::DEFAULT_PRI end end @@ -67,10 +121,21 @@ class MockJobWithPri < subject class MockJobWithTtr < subject time_to_run 90 end - expect(MockJobWithTtr.new().class.time_to_run).to eq 90 + expect(MockJobWithTtr.new().time_to_run).to eq 90 + Object.send(:remove_const, :MockJobWithTtr) + end + it 'uses inheritance if present' do + class MockJobWithTtr < subject + time_to_run 90 + end + class MockChildJobWithTtr < MockJobWithTtr + end + expect(MockChildJobWithTtr.new().time_to_run).to eq 90 + Object.send(:remove_const, :MockJobWithTtr) + Object.send(:remove_const, :MockChildJobWithTtr) end it 'uses default if not set' do - expect(subject.time_to_run).to eq EasyStalk::Configuration::DEFAULT_TTR + expect(subject.new().time_to_run).to eq EasyStalk::Configuration::DEFAULT_TTR end end @@ -79,10 +144,21 @@ class MockJobWithTtr < subject class MockJobWithDelay < subject delay 5 end - expect(MockJobWithDelay.new().class.delay).to eq 5 + expect(MockJobWithDelay.new().delay).to eq 5 + Object.send(:remove_const, :MockJobWithDelay) + end + it 'uses inheritance if present' do + class MockJobWithDelay < subject + delay 5 + end + class MockChildJobWithDelay < MockJobWithDelay + end + expect(MockChildJobWithDelay.new().delay).to eq 5 + Object.send(:remove_const, :MockJobWithDelay) + Object.send(:remove_const, :MockChildJobWithDelay) end it 'uses default if not set' do - expect(subject.delay).to eq EasyStalk::Configuration::DEFAULT_DELAY + expect(subject.new().delay).to eq EasyStalk::Configuration::DEFAULT_DELAY end end @@ -91,10 +167,21 @@ class MockJobWithDelay < subject class MockJobWithRetryTimes < subject retry_times 5 end - expect(MockJobWithRetryTimes.new().class.retry_times).to eq 5 + expect(MockJobWithRetryTimes.new().class.get_retry_times).to eq 5 + Object.send(:remove_const, :MockJobWithRetryTimes) + end + it 'uses inheritance if present' do + class MockJobWithRetryTimes < subject + retry_times 5 + end + class MockChildJobWithRetryTimes < MockJobWithRetryTimes + end + expect(MockChildJobWithRetryTimes.new().class.get_retry_times).to eq 5 + Object.send(:remove_const, :MockJobWithRetryTimes) + Object.send(:remove_const, :MockChildJobWithRetryTimes) end it 'uses default if not set' do - expect(subject.retry_times).to eq EasyStalk::Configuration::DEFAULT_RETRY_TIMES + expect(subject.get_retry_times).to eq EasyStalk::Configuration::DEFAULT_RETRY_TIMES end end @@ -103,10 +190,21 @@ class MockJobWithRetryTimes < subject class MockJobWithKeys < subject serializable_context_keys :cat, :dog end - expect(MockJobWithKeys.new().class.serializable_context_keys).to eq [:cat, :dog] + expect(MockJobWithKeys.new().serializable_context_keys).to eq [:cat, :dog] + Object.send(:remove_const, :MockJobWithKeys) + end + it 'uses inheritance if present' do + class MockJobWithKeys < subject + serializable_context_keys :cat, :dog + end + class MockChildJobWithKeys < MockJobWithKeys + end + expect(MockChildJobWithKeys.new().serializable_context_keys).to eq [:cat, :dog] + Object.send(:remove_const, :MockJobWithKeys) + Object.send(:remove_const, :MockChildJobWithKeys) end it 'uses default if not set' do - expect(subject.serializable_context_keys).to eq described_class::DEFAULT_SERIALIZABLE_CONTEXT_KEYS + expect(subject.new().serializable_context_keys).to eq described_class::DEFAULT_SERIALIZABLE_CONTEXT_KEYS end end end @@ -169,6 +267,7 @@ class MockJobWithKeys < described_class } MockJobWithKeys.new(:cat => "mew", "dog" => "wuf", :fish => "blu"). enqueue(conn, priority: pri, time_to_run: ttr, delay: delay) + Object.send(:remove_const, :MockJobWithKeys) end end @@ -195,13 +294,21 @@ class MockJobWithKeys < described_class end context = { :cat => "mew", "dog" => "wuf", :fish => "blu" } expect(MockJobWithKeys.new(context).job_data).to eq "{\"cat\":\"mew\",\"dog\":\"wuf\"}" + Object.send(:remove_const, :MockJobWithKeys) end end describe '.call' do - class ImplementedJob < described_class - def call; end + before do + class ImplementedJob < described_class + def call; end + end end + + after do + Object.send(:remove_const, :ImplementedJob) + end + it { expect { described_class.call }.to raise_error(NotImplementedError) } it { expect { ImplementedJob.call }.to_not raise_error } end diff --git a/spec/lib/easy_stalk/worker_spec.rb b/spec/lib/easy_stalk/worker_spec.rb index 45bd27c..6cad112 100644 --- a/spec/lib/easy_stalk/worker_spec.rb +++ b/spec/lib/easy_stalk/worker_spec.rb @@ -20,9 +20,7 @@ context "with a valid job class" do before do class ValidJob < EasyStalk::Job - def self.tube_name - "job_tube" - end + tube_name "job_tube" def call end @@ -44,7 +42,7 @@ def call expect(Beaneater).to receive(:new).and_return beanstalk expect(beanstalk).to receive(:tubes).and_return(tubes).at_least(1).times expect(tubes).to receive(:watch!).with("job_tube") - sample_job = EasyStalk::MockBeaneater::TubeItem.new("{}", nil, nil, nil, job_instance.class.tube_name) + sample_job = EasyStalk::MockBeaneater::TubeItem.new("{}", nil, nil, nil, job_instance.class.get_tube_name) expect(tubes).to receive(:reserve) { @count ||= 0 if @count < 2 @@ -64,7 +62,7 @@ def call tubes = EasyStalk::MockBeaneater::Tubes.new expect(EzPool).to receive(:new).and_return mocked_client expect(beanstalk).to receive(:tubes).and_return(tubes).at_least(1).times - sample_job = EasyStalk::MockBeaneater::TubeItem.new("{}", nil, nil, nil, job_instance.class.tube_name) + sample_job = EasyStalk::MockBeaneater::TubeItem.new("{}", nil, nil, nil, job_instance.class.get_tube_name) expect(tubes).to receive(:reserve) { @count ||= 0 if @count < 2 @@ -90,7 +88,7 @@ def call tubes = EasyStalk::MockBeaneater::Tubes.new expect(EzPool).to receive(:new).and_return mocked_client expect(beanstalk).to receive(:tubes).and_return(tubes).at_least(1).times - sample_job = EasyStalk::MockBeaneater::TubeItem.new("{}", nil, nil, nil, job_instance.class.tube_name) + sample_job = EasyStalk::MockBeaneater::TubeItem.new("{}", nil, nil, nil, job_instance.class.get_tube_name) expect(tubes).to receive(:reserve) { @count ||= 0 if @count < 2 @@ -129,7 +127,7 @@ def call tubes.watch!(ValidJob) expect(EzPool).to receive(:new).and_return mocked_client expect(beanstalk).to receive(:tubes).and_return(tubes).at_least(1).times - sample_job = EasyStalk::MockBeaneater::TubeItem.new("{}", nil, nil, nil, job_instance.class.tube_name) + sample_job = EasyStalk::MockBeaneater::TubeItem.new("{}", nil, nil, nil, job_instance.class.get_tube_name) expect(tubes).to receive(:reserve) { @count ||= 0 if @count < 2 @@ -172,7 +170,7 @@ def call subject.send :cleanup nil else - job = EasyStalk::MockBeaneater::TubeItem.new("{}", nil, nil, nil, ValidJob.tube_name, @count) + job = EasyStalk::MockBeaneater::TubeItem.new("{}", nil, nil, nil, ValidJob.get_tube_name, @count) expect(job).to receive(:bury) if @count == 3 job end