pboling/strict_states

View on GitHub
lib/strict_states/checker.rb

Summary

Maintainability
A
0 mins
Test Coverage
module StrictStates
  module Checker
    # Usage:
    #
    #     class MyModel < ActiveRecord::Base
    #       # ...
    #       # <<<===--- AFTER STATE MACHINE DEFINITION ---===>>>
    #       # ...
    #       include StrictStates.checker(
    #                   klass: self,
    #                   machines: {
    #                       state: :pluginaweek,
    #                       awesome_level: :pluginaweek,
    #                       bogus_level: ->(context, machine_name) {
    #                         context.state_machines[machine_name.to_sym].states.map(&:name)
    #                       }
    #                   }
    #               )
    #     end
    #
    def self.included(base)
      base.send(:extend, ClassMethods)
    end

    module ClassMethods
      # Given a state return the same state if they are valid for the given state machine,
      #
      #   MyModel.state_lookup(machine_name: machine_name)
      #   => { :new => "new", :pending => "pending", :goofy => "goofy" }
      #
      def state_lookup(machine_name: :state)
        strict_state_lookup[machine_name.to_sym]
      end

      # Given a state return the same state if they are valid for the given state machine,
      #   otherwise raise an error
      #
      # Example:
      #
      #   MyModel.strict_state(:good, machine_name: :state)
      #   => "good"
      #   MyModel.strict_state(:not_actually_a_thing, machine_name: :drive_status) # Can support multiple state machines per model
      #   => KeyError: key not found: :not_actually_a_thing
      #
      # This is better than creating discrete constants for each potential state string in a state machine,
      #   because this checks, at app boot, to ensure the states are correct.
      #   (e.g. "gift card", vs "gift_card").
      #
      def strict_state(state, machine_name: :state)
        state_lookup(machine_name: machine_name)[state.to_sym] # This will raise an error if the state key is not a valid state
      end

      # Given an array of states return the same array of states if they are valid for the given state machine,
      #   otherwise raise an error
      #
      # Example:
      #
      #   MyModel.strict_state_array(:good, :bad, :on_hold, machine_name: :state)
      #   => ["good", "bad", "on_hold"]
      #   MyModel.strict_state_array(:good, :bad, :steve_martin, machine_name: :drive_status) # Can support multiple state machines per model
      #   => KeyError: key not found: :steve_martin
      #
      # This is better than creating discrete constants for each potential set of states in a state machine,
      #   because this checks, at app boot, to ensure the states are correct.
      # Raw strings in scopes and queries, not created via this method,
      #   will not be bound to the state machine's implementation, so they will fail silently.
      #   e.g. typos like "gift card" vs "gift_card" and no error raised
      #
      def strict_state_array(*names, machine_name: :state)
        names.map {|state| strict_state(state, machine_name: machine_name) }
      end

      # Given the name of a state machine, returns all states defined by the state machine, as an array of strings.
      def strict_all_state_names(machine_name: :state)
        state_lookup(machine_name: machine_name).values # keys would be symbols!
      end
    end
  end
end