class Tapioca::Compilers::Dsl::AASM

`Tapioca::Compilers::Dsl::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?

Constants

EVENT_CALLBACKS

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

Public Instance Methods

decorate(root, constant) click to toggle source
# File lib/tapioca/compilers/dsl/aasm.rb, line 53
def decorate(root, constant)
  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_block_param("block", type: "T.proc.bind(#{constant.name}).void"),
            ]
          )
        end
      end
    end
  end
end
gather_constants() click to toggle source
# File lib/tapioca/compilers/dsl/aasm.rb, line 116
def gather_constants
  T.cast(ObjectSpace.each_object(::AASM::ClassMethods), T::Enumerable[Module])
end