module StrictStates
The STRICT paradigm:
* Will raise an error if states are spelled wrong when lookups happen through this paradigm. * Typos will be noisy, and many of them will error at app-load, so impossible to miss.
Uses the StrictHash
to accomplish this. See lib/strict_states/strict_hash.rb
The *INCLUDE WITH ARGUMENTS* paradigm:
* future-proof support for any/all state machines * easily integrate with any state machine engine not already supported by this gem
Uses a method (StrictStates.checker
) that returns a module (StrictStates::Checker
) to accomplish this.
Constants
- VERSION
Public Class Methods
checker(**config)
click to toggle source
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
# File lib/strict_states.rb, line 37 def self.checker(**config) validate_config(config) config[:machines] = states_for_machines(config[:klass], config[:machines]) set_strict_state_lookup(config) ::StrictStates::Checker end
Private Class Methods
create_strict_state_lookup(names)
click to toggle source
# File lib/strict_states.rb, line 68 def self.create_strict_state_lookup(names) default_strict_hash = names.each_with_object({}) do |state, memo| memo[state.to_sym] = state end StrictHash[**default_strict_hash] end
engine_name_apis()
click to toggle source
Supported engines:
:pluginaweek - for pluginaweek/state_machine :seuros - for seuros/state_machine :state_machines - for state-machines/state_machines :aasm - for aasm/aasm version < 4.3.0 :aasm_multiple - for aasm/aasm version >= 4.3.0
# File lib/strict_states.rb, line 54 def self.engine_name_apis { pluginaweek: ->(context, machine_name) { context.state_machines[machine_name.to_sym].states.map(&:name) }, seuros: ->(context, machine_name) { context.state_machines[machine_name.to_sym].states.map(&:name) }, state_machines: ->(context, machine_name) { context.state_machines[machine_name.to_sym].states.map(&:name) }, aasm: ->(context, _) { context.aasm.states.map(&:name) }, # aasm gem version < 4.3.0 aasm_multiple: ->(context, machine_name) { context.aasm(machine_name).states.map(&:name) } # aasm gem version >= 4.3.0 } end
get_proc_for_engine(engine)
click to toggle source
# File lib/strict_states.rb, line 118 def self.get_proc_for_engine(engine) if (proc = engine_name_apis[engine]) # Predefined Engine within this gem proc else # Custom state machine name extraction Proc provided by caller engine end end
set_strict_state_lookup(config)
click to toggle source
params:
config - { klass: Car, # any Class object with a state machine machines: { # the machine names, and states defined within each state: ["one", "two", "three"], awesome_level: ["not_awesome", "awesome_11", "bad", "good"], bogus_level: ["new", "pending", "goofy"] } }
Result:
MyModel.strict_state_lookup => { :state => { :one => "one", :two => "two", :three => "three" } :awesome_level => { :not_awesome => "not_awesome", :awesome_11 => "awesome_11", :bad => "bad", :good => "good" }, :bogus_level => { :new => "new", :pending => "pending", :goofy => "goofy" } }
# File lib/strict_states.rb, line 150 def self.set_strict_state_lookup(config) klass = config[:klass] machines = config[:machines] class << klass attr_reader :strict_state_lookup end klass.instance_variable_set(:@strict_state_lookup, StrictStates::StrictHash.new) machines.each do |machine_name, state_array| klass.strict_state_lookup[machine_name.to_sym] = StrictStates.create_strict_state_lookup(state_array).freeze end end
states_for_machines(klass, machines)
click to toggle source
params:
klass - any Class object with a state machine machines - { state: :pluginaweek, awesome_level: :pluginaweek, bogus_level: ->(context, machine_name) { context.state_machines[machine_name.to_sym].states.map(&:name)} }
Example result
{ state: ["one", "two", "three"], awesome_level: ["not_awesome", "awesome_11", "bad", "good"], bogus_level: ["new", "pending", "goofy"] }
# File lib/strict_states.rb, line 107 def self.states_for_machines(klass, machines) machines.inject({}) do |memo, (machine_name, engine)| proc = get_proc_for_engine(engine) memo[machine_name] = strict_states_to_stings( proc.call(klass, machine_name) ) memo end end
strict_states_to_stings(states)
click to toggle source
# File lib/strict_states.rb, line 64 def self.strict_states_to_stings(states) states.map {|state| state.to_s } end
test_machines(machines)
click to toggle source
# File lib/strict_states.rb, line 82 def self.test_machines(machines) machines.values.all? do |engine| engine_name_apis.keys.include?(engine) || engine.respond_to?(:call) end end
validate_config(**config)
click to toggle source
# File lib/strict_states.rb, line 75 def self.validate_config(**config) raise ArgumentError, "config must have a :machines key with Hash value but was #{config[:machines]}" unless config[:machines] && config[:machines].is_a?(Hash) raise ArgumentError, ":machines Hash must have values either from #{engine_name_apis.keys} or as Procs but was #{config[:machines]}" unless test_machines(config[:machines]) raise ArgumentError, "config must have a :klass key with a Class value but was #{config[:klass]}" unless config[:klass] && config[:klass].class == Class true end