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

Generator for ActiveJob::Base descendants #298

Merged
merged 1 commit into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
71 changes: 71 additions & 0 deletions lib/tapioca/compilers/dsl/active_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# typed: strict
# frozen_string_literal: true

require "parlour"

begin
require "active_job"
rescue LoadError
return
end

module Tapioca
module Compilers
module Dsl
# `Tapioca::Compilers::Dsl::ActiveJob` generates RBI files for subclasses of
# [`ActiveJob::Base`](https://api.rubyonrails.org/classes/ActiveJob/Base.html).
#
# For example, with the following `ActiveJob` subclass:
#
# ~~~rb
# class NotifyUserJob < ActiveJob::Base
# def perform(user)
# # ...
# end
# end
# ~~~
#
# this generator will produce the RBI file `notify_user_job.rbi` with the following content:
#
# ~~~rbi
# # notify_user_job.rbi
# # typed: true
# class NotifyUserJob
# sig { params(user: T.untyped).returns(NotifyUserJob) }
# def self.perform_later(user); end
#
# sig { params(user: T.untyped).returns(NotifyUserJob) }
# def self.perform_now(user); end
# end
# ~~~
class ActiveJob < Base
extend T::Sig

sig { override.params(root: Parlour::RbiGenerator::Namespace, constant: T.class_of(::ActiveJob::Base)).void }
def decorate(root, constant)
root.path(constant) do |job|
next unless constant.instance_methods(false).include?(:perform)

method = constant.instance_method(:perform)
parameters = compile_method_parameters_to_parlour(method)

%w[perform_later perform_now].each do |name|
kddnewton marked this conversation as resolved.
Show resolved Hide resolved
create_method(
job,
name,
parameters: parameters,
return_type: constant.name,
class_method: true
)
end
end
end

sig { override.returns(T::Enumerable[Module]) }
def gather_constants
::ActiveJob::Base.descendants
end
end
end
end
end
28 changes: 28 additions & 0 deletions manual/generator_activejob.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## ActiveJob

`Tapioca::Compilers::Dsl::ActiveJob` generates RBI files for subclasses of
[`ActiveJob::Base`](https://api.rubyonrails.org/classes/ActiveJob/Base.html).

For example, with the following `ActiveJob` subclass:

~~~rb
class NotifyUserJob < ActiveJob::Base
def perform(user)
# ...
end
end
~~~

this generator will produce the RBI file `notify_user_job.rbi` with the following content:

~~~rbi
# notify_user_job.rbi
# typed: true
class NotifyUserJob
sig { params(user: T.untyped).returns(NotifyUserJob) }
def self.perform_later(user); end

sig { params(user: T.untyped).returns(NotifyUserJob) }
def self.perform_now(user); end
end
~~~
1 change: 1 addition & 0 deletions manual/generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ In the following section you will find all available DSL generators:
<!-- START_GENERATOR_LIST -->
* [ActionControllerHelpers](generator_actioncontrollerhelpers.md)
* [ActionMailer](generator_actionmailer.md)
* [ActiveJob](generator_activejob.md)
* [ActiveRecordAssociations](generator_activerecordassociations.md)
* [ActiveRecordColumns](generator_activerecordcolumns.md)
* [ActiveRecordEnum](generator_activerecordenum.md)
Expand Down
101 changes: 101 additions & 0 deletions spec/tapioca/compilers/dsl/active_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# typed: strict
# frozen_string_literal: true

require "spec_helper"

class Tapioca::Compilers::Dsl::ActiveJobSpec < DslSpec
describe("#initialize") do
it("gathers no constants if there are no ActiveJob subclasses") do
assert_empty(gathered_constants)
end

it("gathers only ActiveJob subclasses") do
add_ruby_file("content.rb", <<~RUBY)
class NotifyJob < ActiveJob::Base
end

class User
end
RUBY

assert_equal(["NotifyJob"], gathered_constants)
end

it("gathers subclasses of ActiveJob subclasses") do
add_ruby_file("content.rb", <<~RUBY)
class NotifyJob < ActiveJob::Base
end

class SecondaryNotifyJob < NotifyJob
end
RUBY

assert_equal(["NotifyJob", "SecondaryNotifyJob"], gathered_constants)
end
end

describe("#decorate") do
it("generates empty RBI file if there is no perform method") do
add_ruby_file("job.rb", <<~RUBY)
class NotifyJob < ActiveJob::Base
end
RUBY

expected = <<~RBI
# typed: strong
class NotifyJob
end
RBI

assert_equal(expected, rbi_for(:NotifyJob))
end

it("generates correct RBI file for subclass with methods") do
add_ruby_file("job.rb", <<~RUBY)
class NotifyJob < ActiveJob::Base
def perform(user_id)
# ...
end
end
RUBY

expected = <<~RBI
# typed: strong
class NotifyJob
sig { params(user_id: T.untyped).returns(NotifyJob) }
def self.perform_later(user_id); end

sig { params(user_id: T.untyped).returns(NotifyJob) }
def self.perform_now(user_id); end
end
RBI

assert_equal(expected, rbi_for(:NotifyJob))
end

it("generates correct RBI file for subclass with method signatures") do
add_ruby_file("job.rb", <<~RUBY)
class NotifyJob < ActiveJob::Base
extend T::Sig
sig { params(user_id: Integer).void }
def perform(user_id)
# ...
end
end
RUBY

expected = <<~RBI
# typed: strong
class NotifyJob
sig { params(user_id: Integer).returns(NotifyJob) }
def self.perform_later(user_id); end

sig { params(user_id: Integer).returns(NotifyJob) }
def self.perform_now(user_id); end
end
RBI

assert_equal(expected, rbi_for(:NotifyJob))
end
end
end