Class: Tapioca::Dsl::Compilers::AASM

Inherits:
Tapioca::Dsl::Compiler show all
Extended by:
T::Sig
Defined in:
lib/tapioca/dsl/compilers/aasm.rb

Overview

‘Tapioca::Dsl::Compilers::AASM` generate types for AASM state machines. This gem dynamically defines constants and methods at runtime. For example, given a class:

class MyClass
  include AASM

  aasm do
    state :sleeping, initial: true
    state :running, :cleaning

    event :run do
      transitions from: :sleeping, to: :running
    end
  end
end

This will result in the following constants being defined:

STATE_SLEEPING, STATE_RUNNING, STATE_CLEANING

and the following methods being defined:

sleeping?, running?, cleaning?
run, run!, run_without_validation!, may_run?

Constant Summary collapse

EVENT_CALLBACKS =

Taken directly from the AASM::Core::Event class, here: github.com/aasm/aasm/blob/0e03746/lib/aasm/core/event.rb#L21-L29

T.let(
  ["after", "after_commit", "after_transaction", "before", "before_transaction", "ensure", "error",
   "before_success", "success",].freeze,
  T::Array[String],
)
ConstantType =
type_member { { fixed: T.all(::AASM::ClassMethods, Class) } }

Constants included from Runtime::Reflection

Runtime::Reflection::ANCESTORS_METHOD, Runtime::Reflection::CLASS_METHOD, Runtime::Reflection::CONSTANTS_METHOD, Runtime::Reflection::EQUAL_METHOD, Runtime::Reflection::METHOD_METHOD, Runtime::Reflection::NAME_METHOD, Runtime::Reflection::OBJECT_ID_METHOD, Runtime::Reflection::PRIVATE_INSTANCE_METHODS_METHOD, Runtime::Reflection::PROTECTED_INSTANCE_METHODS_METHOD, Runtime::Reflection::PUBLIC_INSTANCE_METHODS_METHOD, Runtime::Reflection::REQUIRED_FROM_LABELS, Runtime::Reflection::SINGLETON_CLASS_METHOD, Runtime::Reflection::SUPERCLASS_METHOD, Runtime::Reflection::UNDEFINED_CONSTANT

Constants included from SorbetHelper

SorbetHelper::FEATURE_REQUIREMENTS, SorbetHelper::SORBET_BIN, SorbetHelper::SORBET_EXE_PATH_ENV_VAR, SorbetHelper::SORBET_GEM_SPEC, SorbetHelper::SORBET_PAYLOAD_URL

Instance Attribute Summary

Attributes inherited from Tapioca::Dsl::Compiler

#constant, #root

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Tapioca::Dsl::Compiler

#add_error, #compiler_enabled?, handles?, #initialize, processable_constants

Methods included from T::Generic::TypeStoragePatch

#[], #type_member, #type_template

Methods included from Runtime::Reflection

#ancestors_of, #are_equal?, #attached_class_of, #class_of, #constant_defined?, #constantize, #constants_of, #descendants_of, #file_candidates_for, #inherited_ancestors_of, #method_of, #name_of, #name_of_type, #object_id_of, #private_instance_methods_of, #protected_instance_methods_of, #public_instance_methods_of, #qualified_name_of, #resolve_loc, #signature_of, #singleton_class_of, #superclass_of

Methods included from RBIHelper

#as_nilable_type, #create_block_param, #create_kw_opt_param, #create_kw_param, #create_kw_rest_param, #create_opt_param, #create_param, #create_rest_param, #create_typed_param, #sanitize_signature_types, serialize_type_variable, #valid_method_name?, #valid_parameter_name?

Methods included from SorbetHelper

#sorbet, #sorbet_path, #sorbet_supports?

Constructor Details

This class inherits a constructor from Tapioca::Dsl::Compiler

Class Method Details

.gather_constantsObject



122
123
124
# File 'lib/tapioca/dsl/compilers/aasm.rb', line 122

def gather_constants
  T.cast(ObjectSpace.each_object(::AASM::ClassMethods), T::Enumerable[Module])
end

Instance Method Details

#decorateObject



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/tapioca/dsl/compilers/aasm.rb', line 55

def decorate
  aasm = constant.aasm
  return if !aasm || aasm.states.empty?

  root.create_path(constant) do |model|
    # Create all of the constants and methods for each state
    aasm.states.each do |state|
      model.create_constant("STATE_#{state.name.upcase}", value: "T.let(T.unsafe(nil), Symbol)")
      model.create_method("#{state.name}?", return_type: "T::Boolean")
    end

    # Create all of the methods for each event
    parameters = [create_rest_param("opts", type: "T.untyped")]
    aasm.events.each do |event|
      model.create_method(event.name.to_s, parameters: parameters)
      model.create_method("#{event.name}!", parameters: parameters)
      model.create_method("#{event.name}_without_validation!", parameters: parameters)
      model.create_method("may_#{event.name}?", return_type: "T::Boolean")
    end

    # Create the overall state machine method, which will return an
    # instance of the PrivateAASMMachine class.
    model.create_method(
      "aasm",
      parameters: [
        create_rest_param("args", type: "T.untyped"),
        create_block_param("block", type: "T.nilable(T.proc.bind(PrivateAASMMachine).void)"),
      ],
      return_type: "PrivateAASMMachine",
      class_method: true,
    )

    # Create a private machine class that we can pass around for the
    # purpose of binding various procs passed to methods without having
    # to explicitly bind self in each one.
    model.create_class("PrivateAASMMachine", superclass_name: "AASM::Base") do |machine|
      machine.create_method(
        "event",
        parameters: [
          create_param("name", type: "T.untyped"),
          create_opt_param("options", default: "nil", type: "T.untyped"),
          create_block_param("block", type: "T.proc.bind(PrivateAASMEvent).void"),
        ],
      )

      # Create a private event class that we can pass around for the
      # purpose of binding all of the callbacks without having to
      # explicitly bind self in each one.
      machine.create_class("PrivateAASMEvent", superclass_name: "AASM::Core::Event") do |event|
        EVENT_CALLBACKS.each do |method|
          event.create_method(
            method,
            parameters: [
              create_opt_param("symbol", type: "T.nilable(Symbol)", default: "nil"),
              create_block_param("block", type: "T.nilable(T.proc.bind(#{name_of(constant)}).void)"),
            ],
          )
        end
      end
    end
  end
end