Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Ruby 3.0 added #1130

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby: [2.5, 2.6, 2.7, jruby, jruby-head, ruby-head]
ruby: [2.5, 2.6, 2.7, 3.0, jruby, jruby-head, ruby-head]
rails_version:
- '5.2.0'
- '5.2.5'
- '6.0.0'
- '6.1.0.rc2'
- '6.1.0'
- 'edge'
include:
#
Expand Down
6 changes: 3 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ platforms :jruby do
elsif ENV['RAILS_VERSION']
gem 'railties', "~> #{ENV['RAILS_VERSION']}"
else
gem 'railties', ['>= 3.0', '< 6.2']
gem 'railties', ['>= 3.0', '< 7.1']
end
end

Expand All @@ -43,8 +43,8 @@ group :test do
gem 'actionmailer', "~> #{ENV['RAILS_VERSION']}"
gem 'activerecord', "~> #{ENV['RAILS_VERSION']}"
else
gem 'actionmailer', ['>= 3.0', '< 6.2']
gem 'activerecord', ['>= 3.0', '< 6.2']
gem 'actionmailer', ['>= 3.0', '< 7.1']
gem 'activerecord', ['>= 3.0', '< 7.1']
end

gem 'rspec', '>= 3'
Expand Down
2 changes: 1 addition & 1 deletion delayed_job.gemspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-

Gem::Specification.new do |spec|
spec.add_dependency 'activesupport', ['>= 3.0', '< 6.2']
spec.add_dependency 'activesupport', ['>= 3.0', '< 7.1']
spec.authors = ['Brandon Keepers', 'Brian Ryckbost', 'Chris Gaffney', 'David Genord II', 'Erik Michaels-Ober', 'Matt Griffin', 'Steve Richert', 'Tobias Lütke']
spec.description = 'Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background. It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks.'
spec.email = ['brian@collectiveidea.com']
Expand Down
2 changes: 1 addition & 1 deletion lib/delayed/performable_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Delayed
class PerformableMailer < PerformableMethod
def perform
mailer = object.send(method_name, *args)
mailer = super
mailer.respond_to?(:deliver_now) ? mailer.deliver_now : mailer.deliver
end
end
Expand Down
39 changes: 32 additions & 7 deletions lib/delayed/performable_method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,43 @@ def display_name
end
end

def perform
object.send(method_name, *args) if object
# required to support named parameters in RUBY 3.0
# Otherwise the following error is thrown
# ArgumentError:
# wrong number of arguments (given 1, expected 0; required keywords:
if RUBY_VERSION >= '3.0'
def perform
return unless object

if args_is_a_hash?
object.send(method_name, **args.first)
else
object.send(method_name, *args)
end
end

def args_is_a_hash?
args.size == 1 && args.first.is_a?(Hash)
end
else
def perform
object.send(method_name, *args) if object
end
end

def method(sym)
object.method(sym)
end

# rubocop:disable MethodMissing
def method_missing(symbol, *args)
object.send(symbol, *args)
end
method_def = []
location = caller_locations(1, 1).first
file = location.path
line = location.lineno
definition = RUBY_VERSION >= '3.0' ? '...' : '*args, &block'
method_def <<
"def method_missing(#{definition})" \
" object.send(#{definition})" \
'end'
module_eval(method_def.join(';'), file, line)
# rubocop:enable MethodMissing

def respond_to?(symbol, include_private = false)
Expand Down
101 changes: 101 additions & 0 deletions spec/performable_method_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'helper'
require 'action_controller/metal/strong_parameters' if ActionPack::VERSION::MAJOR >= 5

describe Delayed::PerformableMethod do
describe 'perform' do
Expand All @@ -22,6 +23,106 @@
end
end

describe 'perform with hash object' do
before do
@method = Delayed::PerformableMethod.new('foo', :count, [{:o => true}])
end

it 'calls the method on the object' do
expect(@method.object).to receive(:count).with(:o => true)
@method.perform
end
end

describe 'perform with many hash objects' do
before do
@method = Delayed::PerformableMethod.new('foo', :count, [{:o => true}, {:o2 => true}])
end

it 'calls the method on the object' do
expect(@method.object).to receive(:count).with({:o => true}, :o2 => true)
@method.perform
end
end

if ActionPack::VERSION::MAJOR >= 5
describe 'perform with params object' do
before do
@params = ActionController::Parameters.new(:person => {
:name => 'Francesco',
:age => 22,
:role => 'admin'
})

@method = Delayed::PerformableMethod.new('foo', :count, [@params])
end

it 'calls the method on the object' do
expect(@method.object).to receive(:count).with(@params)
@method.perform
end
end

describe 'perform with sample object and params object' do
before do
@params = ActionController::Parameters.new(:person => {
:name => 'Francesco',
:age => 22,
:role => 'admin'
})

klass = Class.new do
def test_method(_o1, _o2)
true
end
end

@method = Delayed::PerformableMethod.new(klass.new, :test_method, ['o', @params])
end

it 'calls the method on the object' do
expect(@method.object).to receive(:test_method).with('o', @params)
@method.perform
end

it 'calls the method on the object (real)' do
expect(@method.perform).to be true
end
end
end

describe 'perform with sample object and hash object' do
before do
@method = Delayed::PerformableMethod.new('foo', :count, ['o', {:o => true}])
end

it 'calls the method on the object' do
expect(@method.object).to receive(:count).with('o', :o => true)
@method.perform
end
end

describe 'perform with hash to named parameters' do
before do
klass = Class.new do
def test_method(name:, any:)
true if name && any
end
end

@method = Delayed::PerformableMethod.new(klass.new, :test_method, [{:name => 'name', :any => 'any'}])
end

it 'calls the method on the object' do
expect(@method.object).to receive(:test_method).with(:name => 'name', :any => 'any')
@method.perform
end

it 'calls the method on the object (real)' do
expect(@method.perform).to be true
end
end

it "raises a NoMethodError if target method doesn't exist" do
expect do
Delayed::PerformableMethod.new(Object, :method_that_does_not_exist, [])
Expand Down