shoryuken 6.2.1 → 7.0.0.alpha2
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.
- checksums.yaml +4 -4
 - data/.devcontainer/base.Dockerfile +1 -1
 - data/.github/workflows/push.yml +36 -0
 - data/.github/workflows/specs.yml +40 -30
 - data/.github/workflows/verify-action-pins.yml +16 -0
 - data/.gitignore +2 -1
 - data/.rspec +2 -1
 - data/.rubocop.yml +6 -1
 - data/.ruby-version +1 -0
 - data/Appraisals +8 -27
 - data/CHANGELOG.md +213 -139
 - data/Gemfile +2 -7
 - data/README.md +14 -26
 - data/Rakefile +2 -0
 - data/bin/cli/base.rb +1 -2
 - data/bin/cli/sqs.rb +13 -5
 - data/bin/shoryuken +2 -1
 - data/docker-compose.yml +22 -0
 - data/gemfiles/rails_7_0.gemfile +10 -13
 - data/gemfiles/rails_7_1.gemfile +19 -0
 - data/gemfiles/rails_7_2.gemfile +19 -0
 - data/gemfiles/rails_8_0.gemfile +19 -0
 - data/lib/shoryuken/body_parser.rb +3 -1
 - data/lib/shoryuken/client.rb +2 -0
 - data/lib/shoryuken/default_exception_handler.rb +2 -0
 - data/lib/shoryuken/default_worker_registry.rb +11 -11
 - data/lib/shoryuken/environment_loader.rb +6 -6
 - data/lib/shoryuken/extensions/active_job_adapter.rb +13 -6
 - data/lib/shoryuken/extensions/active_job_concurrent_send_adapter.rb +5 -5
 - data/lib/shoryuken/extensions/active_job_extensions.rb +2 -0
 - data/lib/shoryuken/fetcher.rb +4 -2
 - data/lib/shoryuken/helpers/atomic_boolean.rb +44 -0
 - data/lib/shoryuken/helpers/atomic_counter.rb +104 -0
 - data/lib/shoryuken/helpers/atomic_hash.rb +182 -0
 - data/lib/shoryuken/helpers/hash_utils.rb +56 -0
 - data/lib/shoryuken/helpers/string_utils.rb +65 -0
 - data/lib/shoryuken/inline_message.rb +22 -0
 - data/lib/shoryuken/launcher.rb +2 -0
 - data/lib/shoryuken/logging.rb +19 -5
 - data/lib/shoryuken/manager.rb +15 -5
 - data/lib/shoryuken/message.rb +2 -0
 - data/lib/shoryuken/middleware/chain.rb +2 -0
 - data/lib/shoryuken/middleware/server/active_record.rb +2 -0
 - data/lib/shoryuken/middleware/server/auto_delete.rb +2 -0
 - data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +10 -10
 - data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +5 -3
 - data/lib/shoryuken/middleware/server/timing.rb +2 -0
 - data/lib/shoryuken/options.rb +14 -5
 - data/lib/shoryuken/polling/base_strategy.rb +126 -0
 - data/lib/shoryuken/polling/queue_configuration.rb +103 -0
 - data/lib/shoryuken/polling/strict_priority.rb +2 -0
 - data/lib/shoryuken/polling/weighted_round_robin.rb +2 -0
 - data/lib/shoryuken/processor.rb +5 -2
 - data/lib/shoryuken/queue.rb +6 -4
 - data/lib/shoryuken/runner.rb +9 -12
 - data/lib/shoryuken/util.rb +6 -6
 - data/lib/shoryuken/version.rb +3 -1
 - data/lib/shoryuken/worker/default_executor.rb +2 -0
 - data/lib/shoryuken/worker/inline_executor.rb +9 -2
 - data/lib/shoryuken/worker.rb +2 -0
 - data/lib/shoryuken/worker_registry.rb +2 -0
 - data/lib/shoryuken.rb +11 -34
 - data/renovate.json +16 -0
 - data/shoryuken.gemspec +7 -4
 - data/spec/integration/launcher_spec.rb +3 -4
 - data/spec/shared_examples_for_active_job.rb +13 -8
 - data/spec/shoryuken/body_parser_spec.rb +2 -4
 - data/spec/shoryuken/client_spec.rb +1 -1
 - data/spec/shoryuken/default_exception_handler_spec.rb +9 -10
 - data/spec/shoryuken/default_worker_registry_spec.rb +1 -2
 - data/spec/shoryuken/environment_loader_spec.rb +9 -8
 - data/spec/shoryuken/extensions/active_job_adapter_spec.rb +2 -1
 - data/spec/shoryuken/extensions/active_job_base_spec.rb +2 -1
 - data/spec/shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb +2 -1
 - data/spec/shoryuken/extensions/active_job_wrapper_spec.rb +2 -1
 - data/spec/shoryuken/fetcher_spec.rb +23 -26
 - data/spec/shoryuken/helpers/atomic_boolean_spec.rb +196 -0
 - data/spec/shoryuken/helpers/atomic_counter_spec.rb +177 -0
 - data/spec/shoryuken/helpers/atomic_hash_spec.rb +307 -0
 - data/spec/shoryuken/helpers/hash_utils_spec.rb +145 -0
 - data/spec/shoryuken/helpers/string_utils_spec.rb +124 -0
 - data/spec/shoryuken/helpers_integration_spec.rb +96 -0
 - data/spec/shoryuken/inline_message_spec.rb +196 -0
 - data/spec/shoryuken/launcher_spec.rb +1 -2
 - data/spec/shoryuken/manager_spec.rb +1 -2
 - data/spec/shoryuken/middleware/chain_spec.rb +1 -1
 - data/spec/shoryuken/middleware/server/auto_delete_spec.rb +1 -1
 - data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +1 -1
 - data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +1 -1
 - data/spec/shoryuken/middleware/server/timing_spec.rb +1 -1
 - data/spec/shoryuken/options_spec.rb +4 -4
 - data/spec/shoryuken/polling/base_strategy_spec.rb +280 -0
 - data/spec/shoryuken/polling/queue_configuration_spec.rb +195 -0
 - data/spec/shoryuken/polling/strict_priority_spec.rb +1 -1
 - data/spec/shoryuken/polling/weighted_round_robin_spec.rb +1 -1
 - data/spec/shoryuken/processor_spec.rb +1 -1
 - data/spec/shoryuken/queue_spec.rb +2 -3
 - data/spec/shoryuken/runner_spec.rb +1 -3
 - data/spec/shoryuken/util_spec.rb +1 -1
 - data/spec/shoryuken/worker/default_executor_spec.rb +1 -1
 - data/spec/shoryuken/worker/inline_executor_spec.rb +57 -1
 - data/spec/shoryuken/worker_spec.rb +15 -11
 - data/spec/shoryuken_spec.rb +1 -1
 - data/spec/spec_helper.rb +19 -4
 - metadata +79 -35
 - data/.codeclimate.yml +0 -20
 - data/.github/FUNDING.yml +0 -12
 - data/.github/dependabot.yml +0 -6
 - data/.github/workflows/stale.yml +0 -20
 - data/.reek.yml +0 -5
 - data/gemfiles/aws_sdk_core_2.gemfile +0 -21
 - data/gemfiles/rails_4_2.gemfile +0 -20
 - data/gemfiles/rails_5_2.gemfile +0 -21
 - data/gemfiles/rails_6_0.gemfile +0 -21
 - data/gemfiles/rails_6_1.gemfile +0 -21
 - data/lib/shoryuken/core_ext.rb +0 -69
 - data/lib/shoryuken/polling/base.rb +0 -67
 - data/shoryuken.jpg +0 -0
 - data/spec/shoryuken/core_ext_spec.rb +0 -40
 
| 
         @@ -0,0 +1,196 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            RSpec.describe Shoryuken::InlineMessage do
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:body) { '{"message": "test"}' }
         
     | 
| 
      
 5 
     | 
    
         
            +
              let(:attributes) { { 'SentTimestamp' => '1234567890' } }
         
     | 
| 
      
 6 
     | 
    
         
            +
              let(:md5_of_body) { 'abc123def456' }
         
     | 
| 
      
 7 
     | 
    
         
            +
              let(:md5_of_message_attributes) { 'def456abc123' }
         
     | 
| 
      
 8 
     | 
    
         
            +
              let(:message_attributes) { { 'CustomAttribute' => { string_value: 'value', data_type: 'String' } } }
         
     | 
| 
      
 9 
     | 
    
         
            +
              let(:message_id) { 'msg-12345' }
         
     | 
| 
      
 10 
     | 
    
         
            +
              let(:receipt_handle) { 'receipt-handle-12345' }
         
     | 
| 
      
 11 
     | 
    
         
            +
              let(:delete) { nil }
         
     | 
| 
      
 12 
     | 
    
         
            +
              let(:queue_name) { 'test-queue' }
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              describe '#new' do
         
     | 
| 
      
 15 
     | 
    
         
            +
                context 'with positional arguments' do
         
     | 
| 
      
 16 
     | 
    
         
            +
                  subject do
         
     | 
| 
      
 17 
     | 
    
         
            +
                    described_class.new(
         
     | 
| 
      
 18 
     | 
    
         
            +
                      body: body,
         
     | 
| 
      
 19 
     | 
    
         
            +
                      attributes: attributes,
         
     | 
| 
      
 20 
     | 
    
         
            +
                      md5_of_body: md5_of_body,
         
     | 
| 
      
 21 
     | 
    
         
            +
                      md5_of_message_attributes: md5_of_message_attributes,
         
     | 
| 
      
 22 
     | 
    
         
            +
                      message_attributes: message_attributes,
         
     | 
| 
      
 23 
     | 
    
         
            +
                      message_id: message_id,
         
     | 
| 
      
 24 
     | 
    
         
            +
                      receipt_handle: receipt_handle,
         
     | 
| 
      
 25 
     | 
    
         
            +
                      delete: delete,
         
     | 
| 
      
 26 
     | 
    
         
            +
                      queue_name: queue_name
         
     | 
| 
      
 27 
     | 
    
         
            +
                    )
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  it 'initializes with all attributes' do
         
     | 
| 
      
 31 
     | 
    
         
            +
                    expect(subject.body).to eq(body)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    expect(subject.attributes).to eq(attributes)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    expect(subject.md5_of_body).to eq(md5_of_body)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    expect(subject.md5_of_message_attributes).to eq(md5_of_message_attributes)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    expect(subject.message_attributes).to eq(message_attributes)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    expect(subject.message_id).to eq(message_id)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    expect(subject.receipt_handle).to eq(receipt_handle)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    expect(subject.delete).to eq(delete)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    expect(subject.queue_name).to eq(queue_name)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                context 'with keyword arguments' do
         
     | 
| 
      
 44 
     | 
    
         
            +
                  subject do
         
     | 
| 
      
 45 
     | 
    
         
            +
                    described_class.new(
         
     | 
| 
      
 46 
     | 
    
         
            +
                      body: body,
         
     | 
| 
      
 47 
     | 
    
         
            +
                      attributes: attributes,
         
     | 
| 
      
 48 
     | 
    
         
            +
                      md5_of_body: md5_of_body,
         
     | 
| 
      
 49 
     | 
    
         
            +
                      md5_of_message_attributes: md5_of_message_attributes,
         
     | 
| 
      
 50 
     | 
    
         
            +
                      message_attributes: message_attributes,
         
     | 
| 
      
 51 
     | 
    
         
            +
                      message_id: message_id,
         
     | 
| 
      
 52 
     | 
    
         
            +
                      receipt_handle: receipt_handle,
         
     | 
| 
      
 53 
     | 
    
         
            +
                      delete: delete,
         
     | 
| 
      
 54 
     | 
    
         
            +
                      queue_name: queue_name
         
     | 
| 
      
 55 
     | 
    
         
            +
                    )
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  it 'initializes with all attributes' do
         
     | 
| 
      
 59 
     | 
    
         
            +
                    expect(subject.body).to eq(body)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    expect(subject.attributes).to eq(attributes)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    expect(subject.md5_of_body).to eq(md5_of_body)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    expect(subject.md5_of_message_attributes).to eq(md5_of_message_attributes)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    expect(subject.message_attributes).to eq(message_attributes)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    expect(subject.message_id).to eq(message_id)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    expect(subject.receipt_handle).to eq(receipt_handle)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    expect(subject.delete).to eq(delete)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    expect(subject.queue_name).to eq(queue_name)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                context 'with nil values' do
         
     | 
| 
      
 72 
     | 
    
         
            +
                  subject do
         
     | 
| 
      
 73 
     | 
    
         
            +
                    described_class.new(
         
     | 
| 
      
 74 
     | 
    
         
            +
                      body: body,
         
     | 
| 
      
 75 
     | 
    
         
            +
                      attributes: nil,
         
     | 
| 
      
 76 
     | 
    
         
            +
                      md5_of_body: nil,
         
     | 
| 
      
 77 
     | 
    
         
            +
                      md5_of_message_attributes: nil,
         
     | 
| 
      
 78 
     | 
    
         
            +
                      message_attributes: message_attributes,
         
     | 
| 
      
 79 
     | 
    
         
            +
                      message_id: nil,
         
     | 
| 
      
 80 
     | 
    
         
            +
                      receipt_handle: nil,
         
     | 
| 
      
 81 
     | 
    
         
            +
                      delete: nil,
         
     | 
| 
      
 82 
     | 
    
         
            +
                      queue_name: queue_name
         
     | 
| 
      
 83 
     | 
    
         
            +
                    )
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                  it 'handles nil values correctly' do
         
     | 
| 
      
 87 
     | 
    
         
            +
                    expect(subject.body).to eq(body)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    expect(subject.attributes).to be_nil
         
     | 
| 
      
 89 
     | 
    
         
            +
                    expect(subject.md5_of_body).to be_nil
         
     | 
| 
      
 90 
     | 
    
         
            +
                    expect(subject.md5_of_message_attributes).to be_nil
         
     | 
| 
      
 91 
     | 
    
         
            +
                    expect(subject.message_attributes).to eq(message_attributes)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    expect(subject.message_id).to be_nil
         
     | 
| 
      
 93 
     | 
    
         
            +
                    expect(subject.receipt_handle).to be_nil
         
     | 
| 
      
 94 
     | 
    
         
            +
                    expect(subject.delete).to be_nil
         
     | 
| 
      
 95 
     | 
    
         
            +
                    expect(subject.queue_name).to eq(queue_name)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                context 'with minimal required attributes' do
         
     | 
| 
      
 100 
     | 
    
         
            +
                  subject { described_class.new(body: body, queue_name: queue_name) }
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                  it 'initializes with only required attributes' do
         
     | 
| 
      
 103 
     | 
    
         
            +
                    expect(subject.body).to eq(body)
         
     | 
| 
      
 104 
     | 
    
         
            +
                    expect(subject.queue_name).to eq(queue_name)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    expect(subject.attributes).to be_nil
         
     | 
| 
      
 106 
     | 
    
         
            +
                    expect(subject.md5_of_body).to be_nil
         
     | 
| 
      
 107 
     | 
    
         
            +
                    expect(subject.md5_of_message_attributes).to be_nil
         
     | 
| 
      
 108 
     | 
    
         
            +
                    expect(subject.message_attributes).to be_nil
         
     | 
| 
      
 109 
     | 
    
         
            +
                    expect(subject.message_id).to be_nil
         
     | 
| 
      
 110 
     | 
    
         
            +
                    expect(subject.receipt_handle).to be_nil
         
     | 
| 
      
 111 
     | 
    
         
            +
                    expect(subject.delete).to be_nil
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
              end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
              describe 'attribute accessors' do
         
     | 
| 
      
 117 
     | 
    
         
            +
                subject do
         
     | 
| 
      
 118 
     | 
    
         
            +
                  described_class.new(
         
     | 
| 
      
 119 
     | 
    
         
            +
                    body: body,
         
     | 
| 
      
 120 
     | 
    
         
            +
                    attributes: attributes,
         
     | 
| 
      
 121 
     | 
    
         
            +
                    md5_of_body: md5_of_body,
         
     | 
| 
      
 122 
     | 
    
         
            +
                    md5_of_message_attributes: md5_of_message_attributes,
         
     | 
| 
      
 123 
     | 
    
         
            +
                    message_attributes: message_attributes,
         
     | 
| 
      
 124 
     | 
    
         
            +
                    message_id: message_id,
         
     | 
| 
      
 125 
     | 
    
         
            +
                    receipt_handle: receipt_handle,
         
     | 
| 
      
 126 
     | 
    
         
            +
                    delete: delete,
         
     | 
| 
      
 127 
     | 
    
         
            +
                    queue_name: queue_name
         
     | 
| 
      
 128 
     | 
    
         
            +
                  )
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                it 'provides read access to all attributes' do
         
     | 
| 
      
 132 
     | 
    
         
            +
                  expect(subject.body).to eq(body)
         
     | 
| 
      
 133 
     | 
    
         
            +
                  expect(subject.attributes).to eq(attributes)
         
     | 
| 
      
 134 
     | 
    
         
            +
                  expect(subject.md5_of_body).to eq(md5_of_body)
         
     | 
| 
      
 135 
     | 
    
         
            +
                  expect(subject.md5_of_message_attributes).to eq(md5_of_message_attributes)
         
     | 
| 
      
 136 
     | 
    
         
            +
                  expect(subject.message_attributes).to eq(message_attributes)
         
     | 
| 
      
 137 
     | 
    
         
            +
                  expect(subject.message_id).to eq(message_id)
         
     | 
| 
      
 138 
     | 
    
         
            +
                  expect(subject.receipt_handle).to eq(receipt_handle)
         
     | 
| 
      
 139 
     | 
    
         
            +
                  expect(subject.delete).to eq(delete)
         
     | 
| 
      
 140 
     | 
    
         
            +
                  expect(subject.queue_name).to eq(queue_name)
         
     | 
| 
      
 141 
     | 
    
         
            +
                end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                it 'provides write access to all attributes' do
         
     | 
| 
      
 144 
     | 
    
         
            +
                  new_body = '{"updated": "message"}'
         
     | 
| 
      
 145 
     | 
    
         
            +
                  new_queue_name = 'updated-queue'
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                  subject.body = new_body
         
     | 
| 
      
 148 
     | 
    
         
            +
                  subject.queue_name = new_queue_name
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                  expect(subject.body).to eq(new_body)
         
     | 
| 
      
 151 
     | 
    
         
            +
                  expect(subject.queue_name).to eq(new_queue_name)
         
     | 
| 
      
 152 
     | 
    
         
            +
                end
         
     | 
| 
      
 153 
     | 
    
         
            +
              end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
              describe 'struct behavior' do
         
     | 
| 
      
 156 
     | 
    
         
            +
                subject { described_class.new(body: body, queue_name: queue_name) }
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                it 'behaves like a struct' do
         
     | 
| 
      
 159 
     | 
    
         
            +
                  expect(subject).to be_a(Struct)
         
     | 
| 
      
 160 
     | 
    
         
            +
                  expect(subject.class.superclass).to eq(Struct)
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                it 'supports array-like access' do
         
     | 
| 
      
 164 
     | 
    
         
            +
                  expect(subject[0]).to eq(body)  # body is first attribute
         
     | 
| 
      
 165 
     | 
    
         
            +
                  expect(subject[-1]).to eq(queue_name)  # queue_name is last attribute
         
     | 
| 
      
 166 
     | 
    
         
            +
                end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                it 'supports enumeration' do
         
     | 
| 
      
 169 
     | 
    
         
            +
                  values = subject.to_a
         
     | 
| 
      
 170 
     | 
    
         
            +
                  expect(values.first).to eq(body)
         
     | 
| 
      
 171 
     | 
    
         
            +
                  expect(values.last).to eq(queue_name)
         
     | 
| 
      
 172 
     | 
    
         
            +
                  expect(values.length).to eq(9)  # 9 attributes total
         
     | 
| 
      
 173 
     | 
    
         
            +
                end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                it 'supports hash conversion' do
         
     | 
| 
      
 176 
     | 
    
         
            +
                  hash = subject.to_h
         
     | 
| 
      
 177 
     | 
    
         
            +
                  expect(hash[:body]).to eq(body)
         
     | 
| 
      
 178 
     | 
    
         
            +
                  expect(hash[:queue_name]).to eq(queue_name)
         
     | 
| 
      
 179 
     | 
    
         
            +
                  expect(hash.keys).to contain_exactly(
         
     | 
| 
      
 180 
     | 
    
         
            +
                    :body, :attributes, :md5_of_body, :md5_of_message_attributes,
         
     | 
| 
      
 181 
     | 
    
         
            +
                    :message_attributes, :message_id, :receipt_handle, :delete, :queue_name
         
     | 
| 
      
 182 
     | 
    
         
            +
                  )
         
     | 
| 
      
 183 
     | 
    
         
            +
                end
         
     | 
| 
      
 184 
     | 
    
         
            +
              end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
              describe 'equality' do
         
     | 
| 
      
 187 
     | 
    
         
            +
                let(:message1) { described_class.new(body: body, queue_name: queue_name) }
         
     | 
| 
      
 188 
     | 
    
         
            +
                let(:message2) { described_class.new(body: body, queue_name: queue_name) }
         
     | 
| 
      
 189 
     | 
    
         
            +
                let(:message3) { described_class.new(body: 'different', queue_name: queue_name) }
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                it 'compares messages by attribute values' do
         
     | 
| 
      
 192 
     | 
    
         
            +
                  expect(message1).to eq(message2)
         
     | 
| 
      
 193 
     | 
    
         
            +
                  expect(message1).not_to eq(message3)
         
     | 
| 
      
 194 
     | 
    
         
            +
                end
         
     | 
| 
      
 195 
     | 
    
         
            +
              end
         
     | 
| 
      
 196 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,11 +1,11 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            RSpec.describe Shoryuken::Options do
         
     | 
| 
       4 
4 
     | 
    
         
             
              subject { Shoryuken.shoryuken_options }
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
              describe '.on_stop' do
         
     | 
| 
       7 
7 
     | 
    
         
             
                specify do
         
     | 
| 
       8 
     | 
    
         
            -
                  on_stop =  
     | 
| 
      
 8 
     | 
    
         
            +
                  on_stop = proc {}
         
     | 
| 
       9 
9 
     | 
    
         
             
                  Shoryuken.on_stop(&on_stop)
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
                  expect(Shoryuken.stop_callback).to eq(on_stop)
         
     | 
| 
         @@ -14,7 +14,7 @@ RSpec.describe Shoryuken::Options do 
     | 
|
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
              describe '.on_start' do
         
     | 
| 
       16 
16 
     | 
    
         
             
                specify do
         
     | 
| 
       17 
     | 
    
         
            -
                  on_start =  
     | 
| 
      
 17 
     | 
    
         
            +
                  on_start = proc {}
         
     | 
| 
       18 
18 
     | 
    
         
             
                  Shoryuken.on_start(&on_start)
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
                  expect(Shoryuken.start_callback).to eq(on_start)
         
     | 
| 
         @@ -81,7 +81,7 @@ RSpec.describe Shoryuken::Options do 
     | 
|
| 
       81 
81 
     | 
    
         | 
| 
       82 
82 
     | 
    
         
             
                  expect(Shoryuken.sqs_client_receive_message_opts).to eq(
         
     | 
| 
       83 
83 
     | 
    
         
             
                    'default' => { test: 1 },
         
     | 
| 
       84 
     | 
    
         
            -
                    'group1' 
     | 
| 
      
 84 
     | 
    
         
            +
                    'group1' => { test: 2 }
         
     | 
| 
       85 
85 
     | 
    
         
             
                  )
         
     | 
| 
       86 
86 
     | 
    
         
             
                end
         
     | 
| 
       87 
87 
     | 
    
         
             
              end
         
     | 
| 
         @@ -0,0 +1,280 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            RSpec.describe Shoryuken::Polling::BaseStrategy do
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Create a concrete implementation for testing
         
     | 
| 
      
 5 
     | 
    
         
            +
              let(:test_strategy_class) do
         
     | 
| 
      
 6 
     | 
    
         
            +
                Class.new(described_class) do
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(queues, delay = nil)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @queues = queues
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @delay = delay
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def next_queue
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @queues.first
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def messages_found(queue, messages_found)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    # Test implementation - store the call
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @last_messages_found = { queue: queue, count: messages_found }
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def active_queues
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @queues
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  attr_reader :last_messages_found
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              let(:strategy) { test_strategy_class.new(['queue1', 'queue2']) }
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              describe 'abstract interface' do
         
     | 
| 
      
 32 
     | 
    
         
            +
                it 'includes Util module' do
         
     | 
| 
      
 33 
     | 
    
         
            +
                  expect(described_class.included_modules).to include(Shoryuken::Util)
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                describe '#next_queue' do
         
     | 
| 
      
 37 
     | 
    
         
            +
                  it 'raises NotImplementedError in base class' do
         
     | 
| 
      
 38 
     | 
    
         
            +
                    base_strategy = described_class.new
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    expect { base_strategy.next_queue }.to raise_error(NotImplementedError)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  it 'can be implemented by subclasses' do
         
     | 
| 
      
 44 
     | 
    
         
            +
                    expect(strategy.next_queue).to eq('queue1')
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                describe '#messages_found' do
         
     | 
| 
      
 49 
     | 
    
         
            +
                  it 'raises NotImplementedError in base class' do
         
     | 
| 
      
 50 
     | 
    
         
            +
                    base_strategy = described_class.new
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    expect { base_strategy.messages_found('queue', 5) }.to raise_error(NotImplementedError)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  it 'can be implemented by subclasses' do
         
     | 
| 
      
 56 
     | 
    
         
            +
                    strategy.messages_found('test_queue', 3)
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    expect(strategy.last_messages_found).to eq({ queue: 'test_queue', count: 3 })
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  it 'accepts zero messages found' do
         
     | 
| 
      
 62 
     | 
    
         
            +
                    strategy.messages_found('empty_queue', 0)
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    expect(strategy.last_messages_found).to eq({ queue: 'empty_queue', count: 0 })
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                describe '#message_processed' do
         
     | 
| 
      
 69 
     | 
    
         
            +
                  it 'has default empty implementation' do
         
     | 
| 
      
 70 
     | 
    
         
            +
                    base_strategy = described_class.new
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    expect { base_strategy.message_processed('queue') }.not_to raise_error
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  it 'can be overridden by subclasses' do
         
     | 
| 
      
 76 
     | 
    
         
            +
                    # Default implementation should do nothing
         
     | 
| 
      
 77 
     | 
    
         
            +
                    expect { strategy.message_processed('queue') }.not_to raise_error
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                describe '#active_queues' do
         
     | 
| 
      
 82 
     | 
    
         
            +
                  it 'raises NotImplementedError in base class' do
         
     | 
| 
      
 83 
     | 
    
         
            +
                    base_strategy = described_class.new
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    expect { base_strategy.active_queues }.to raise_error(NotImplementedError)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  it 'can be implemented by subclasses' do
         
     | 
| 
      
 89 
     | 
    
         
            +
                    expect(strategy.active_queues).to eq(['queue1', 'queue2'])
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
              end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
              describe '#==' do
         
     | 
| 
      
 95 
     | 
    
         
            +
                context 'when comparing with Array' do
         
     | 
| 
      
 96 
     | 
    
         
            +
                  it 'compares against @queues instance variable' do
         
     | 
| 
      
 97 
     | 
    
         
            +
                    expect(strategy).to eq(['queue1', 'queue2'])
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                  it 'returns false for different arrays' do
         
     | 
| 
      
 101 
     | 
    
         
            +
                    expect(strategy).not_to eq(['queue3', 'queue4'])
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                  it 'returns false for arrays with different order' do
         
     | 
| 
      
 105 
     | 
    
         
            +
                    expect(strategy).not_to eq(['queue2', 'queue1'])
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                context 'when comparing with another strategy' do
         
     | 
| 
      
 110 
     | 
    
         
            +
                  it 'compares active_queues when other responds to active_queues' do
         
     | 
| 
      
 111 
     | 
    
         
            +
                    other_strategy = test_strategy_class.new(['queue1', 'queue2'])
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                    expect(strategy).to eq(other_strategy)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  it 'returns false when active_queues differ' do
         
     | 
| 
      
 117 
     | 
    
         
            +
                    other_strategy = test_strategy_class.new(['queue3', 'queue4'])
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                    expect(strategy).not_to eq(other_strategy)
         
     | 
| 
      
 120 
     | 
    
         
            +
                  end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                  it 'handles strategies with different active_queues order' do
         
     | 
| 
      
 123 
     | 
    
         
            +
                    other_strategy = test_strategy_class.new(['queue2', 'queue1'])
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    expect(strategy).not_to eq(other_strategy)
         
     | 
| 
      
 126 
     | 
    
         
            +
                  end
         
     | 
| 
      
 127 
     | 
    
         
            +
                end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                context 'when comparing with objects that do not respond to active_queues' do
         
     | 
| 
      
 130 
     | 
    
         
            +
                  it 'returns false for strings' do
         
     | 
| 
      
 131 
     | 
    
         
            +
                    expect(strategy).not_to eq('some_string')
         
     | 
| 
      
 132 
     | 
    
         
            +
                  end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                  it 'returns false for numbers' do
         
     | 
| 
      
 135 
     | 
    
         
            +
                    expect(strategy).not_to eq(123)
         
     | 
| 
      
 136 
     | 
    
         
            +
                  end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                  it 'returns false for hashes' do
         
     | 
| 
      
 139 
     | 
    
         
            +
                    expect(strategy).not_to eq({ queues: ['queue1', 'queue2'] })
         
     | 
| 
      
 140 
     | 
    
         
            +
                  end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                  it 'returns false for objects without active_queues method' do
         
     | 
| 
      
 143 
     | 
    
         
            +
                    plain_object = Object.new
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                    expect(strategy).not_to eq(plain_object)
         
     | 
| 
      
 146 
     | 
    
         
            +
                  end
         
     | 
| 
      
 147 
     | 
    
         
            +
                end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                context 'when @queues is not set' do
         
     | 
| 
      
 150 
     | 
    
         
            +
                  let(:strategy_without_queues) { test_strategy_class.new(nil) }
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                  it 'handles nil @queues when comparing with nil' do
         
     | 
| 
      
 153 
     | 
    
         
            +
                    # nil is not an Array, so this goes to the else branch
         
     | 
| 
      
 154 
     | 
    
         
            +
                    # nil doesn't respond to active_queues, so it returns false
         
     | 
| 
      
 155 
     | 
    
         
            +
                    expect(strategy_without_queues == nil).to be false
         
     | 
| 
      
 156 
     | 
    
         
            +
                  end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                  it 'handles nil @queues when comparing with empty array' do
         
     | 
| 
      
 159 
     | 
    
         
            +
                    expect(strategy_without_queues == []).to be false
         
     | 
| 
      
 160 
     | 
    
         
            +
                  end
         
     | 
| 
      
 161 
     | 
    
         
            +
                end
         
     | 
| 
      
 162 
     | 
    
         
            +
              end
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
              describe '#delay' do
         
     | 
| 
      
 165 
     | 
    
         
            +
                context 'when delay is set on strategy' do
         
     | 
| 
      
 166 
     | 
    
         
            +
                  let(:strategy_with_delay) { test_strategy_class.new(['queue1'], 5.0) }
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                  it 'returns the strategy-specific delay' do
         
     | 
| 
      
 169 
     | 
    
         
            +
                    expect(strategy_with_delay.delay).to eq(5.0)
         
     | 
| 
      
 170 
     | 
    
         
            +
                  end
         
     | 
| 
      
 171 
     | 
    
         
            +
                end
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                context 'when delay is not set on strategy' do
         
     | 
| 
      
 174 
     | 
    
         
            +
                  before do
         
     | 
| 
      
 175 
     | 
    
         
            +
                    allow(Shoryuken.options).to receive(:[]).with(:delay).and_return(3.5)
         
     | 
| 
      
 176 
     | 
    
         
            +
                  end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                  it 'returns the global Shoryuken delay converted to float' do
         
     | 
| 
      
 179 
     | 
    
         
            +
                    expect(strategy.delay).to eq(3.5)
         
     | 
| 
      
 180 
     | 
    
         
            +
                  end
         
     | 
| 
      
 181 
     | 
    
         
            +
                end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                context 'when global delay is a string' do
         
     | 
| 
      
 184 
     | 
    
         
            +
                  before do
         
     | 
| 
      
 185 
     | 
    
         
            +
                    allow(Shoryuken.options).to receive(:[]).with(:delay).and_return('2.5')
         
     | 
| 
      
 186 
     | 
    
         
            +
                  end
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                  it 'converts string delay to float' do
         
     | 
| 
      
 189 
     | 
    
         
            +
                    expect(strategy.delay).to eq(2.5)
         
     | 
| 
      
 190 
     | 
    
         
            +
                  end
         
     | 
| 
      
 191 
     | 
    
         
            +
                end
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                context 'when global delay is an integer' do
         
     | 
| 
      
 194 
     | 
    
         
            +
                  before do
         
     | 
| 
      
 195 
     | 
    
         
            +
                    allow(Shoryuken.options).to receive(:[]).with(:delay).and_return(4)
         
     | 
| 
      
 196 
     | 
    
         
            +
                  end
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                  it 'converts integer delay to float' do
         
     | 
| 
      
 199 
     | 
    
         
            +
                    expect(strategy.delay).to eq(4.0)
         
     | 
| 
      
 200 
     | 
    
         
            +
                  end
         
     | 
| 
      
 201 
     | 
    
         
            +
                end
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
                context 'when delay is explicitly set to nil' do
         
     | 
| 
      
 204 
     | 
    
         
            +
                  let(:strategy_with_nil_delay) { test_strategy_class.new(['queue1'], nil) }
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                  before do
         
     | 
| 
      
 207 
     | 
    
         
            +
                    allow(Shoryuken.options).to receive(:[]).with(:delay).and_return(1.5)
         
     | 
| 
      
 208 
     | 
    
         
            +
                  end
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                  it 'falls back to global delay' do
         
     | 
| 
      
 211 
     | 
    
         
            +
                    expect(strategy_with_nil_delay.delay).to eq(1.5)
         
     | 
| 
      
 212 
     | 
    
         
            +
                  end
         
     | 
| 
      
 213 
     | 
    
         
            +
                end
         
     | 
| 
      
 214 
     | 
    
         
            +
              end
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
              describe 'inheritance patterns' do
         
     | 
| 
      
 217 
     | 
    
         
            +
                it 'allows subclasses to call super for implemented methods' do
         
     | 
| 
      
 218 
     | 
    
         
            +
                  subclass = Class.new(described_class) do
         
     | 
| 
      
 219 
     | 
    
         
            +
                    def next_queue
         
     | 
| 
      
 220 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 221 
     | 
    
         
            +
                        super
         
     | 
| 
      
 222 
     | 
    
         
            +
                      rescue NotImplementedError
         
     | 
| 
      
 223 
     | 
    
         
            +
                        'fallback'
         
     | 
| 
      
 224 
     | 
    
         
            +
                      end
         
     | 
| 
      
 225 
     | 
    
         
            +
                    end
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                    def active_queues
         
     | 
| 
      
 228 
     | 
    
         
            +
                      []
         
     | 
| 
      
 229 
     | 
    
         
            +
                    end
         
     | 
| 
      
 230 
     | 
    
         
            +
             
     | 
| 
      
 231 
     | 
    
         
            +
                    def messages_found(queue, count)
         
     | 
| 
      
 232 
     | 
    
         
            +
                      # Implementation required
         
     | 
| 
      
 233 
     | 
    
         
            +
                    end
         
     | 
| 
      
 234 
     | 
    
         
            +
                  end
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
                  instance = subclass.new
         
     | 
| 
      
 237 
     | 
    
         
            +
                  expect(instance.next_queue).to eq('fallback')
         
     | 
| 
      
 238 
     | 
    
         
            +
                end
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
                it 'supports method chaining in subclasses' do
         
     | 
| 
      
 241 
     | 
    
         
            +
                  chainable_strategy = Class.new(described_class) do
         
     | 
| 
      
 242 
     | 
    
         
            +
                    def initialize(queues)
         
     | 
| 
      
 243 
     | 
    
         
            +
                      @queues = queues
         
     | 
| 
      
 244 
     | 
    
         
            +
                      @call_chain = []
         
     | 
| 
      
 245 
     | 
    
         
            +
                    end
         
     | 
| 
      
 246 
     | 
    
         
            +
             
     | 
| 
      
 247 
     | 
    
         
            +
                    def next_queue
         
     | 
| 
      
 248 
     | 
    
         
            +
                      @call_chain << :next_queue
         
     | 
| 
      
 249 
     | 
    
         
            +
                      @queues.first
         
     | 
| 
      
 250 
     | 
    
         
            +
                    end
         
     | 
| 
      
 251 
     | 
    
         
            +
             
     | 
| 
      
 252 
     | 
    
         
            +
                    def messages_found(queue, count)
         
     | 
| 
      
 253 
     | 
    
         
            +
                      @call_chain << :messages_found
         
     | 
| 
      
 254 
     | 
    
         
            +
                      self
         
     | 
| 
      
 255 
     | 
    
         
            +
                    end
         
     | 
| 
      
 256 
     | 
    
         
            +
             
     | 
| 
      
 257 
     | 
    
         
            +
                    def active_queues
         
     | 
| 
      
 258 
     | 
    
         
            +
                      @call_chain << :active_queues
         
     | 
| 
      
 259 
     | 
    
         
            +
                      @queues
         
     | 
| 
      
 260 
     | 
    
         
            +
                    end
         
     | 
| 
      
 261 
     | 
    
         
            +
             
     | 
| 
      
 262 
     | 
    
         
            +
                    attr_reader :call_chain
         
     | 
| 
      
 263 
     | 
    
         
            +
                  end
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
                  strategy = chainable_strategy.new(['test'])
         
     | 
| 
      
 266 
     | 
    
         
            +
                  result = strategy.messages_found('queue', 1)
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
                  expect(result).to be(strategy)
         
     | 
| 
      
 269 
     | 
    
         
            +
                  expect(strategy.call_chain).to eq([:messages_found])
         
     | 
| 
      
 270 
     | 
    
         
            +
                end
         
     | 
| 
      
 271 
     | 
    
         
            +
              end
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
              describe 'utility method access' do
         
     | 
| 
      
 274 
     | 
    
         
            +
                it 'provides access to utility methods through Util module' do
         
     | 
| 
      
 275 
     | 
    
         
            +
                  # Verify that utility methods are available
         
     | 
| 
      
 276 
     | 
    
         
            +
                  expect(strategy).to respond_to(:unparse_queues)
         
     | 
| 
      
 277 
     | 
    
         
            +
                  expect(strategy).to respond_to(:logger)
         
     | 
| 
      
 278 
     | 
    
         
            +
                end
         
     | 
| 
      
 279 
     | 
    
         
            +
              end
         
     | 
| 
      
 280 
     | 
    
         
            +
            end
         
     |