diff --git a/lib/stateful_enum/machine.rb b/lib/stateful_enum/machine.rb index 06067bd..6bc6050 100644 --- a/lib/stateful_enum/machine.rb +++ b/lib/stateful_enum/machine.rb @@ -57,7 +57,7 @@ def initialize(model, column, states, prefix, suffix, name, &block) if to && (condition.nil? || instance_exec(&condition)) #TODO transaction? run_callbacks value_method_name do - original_method = self.class.send(:_enum_methods_module).instance_method "#{prefix}#{to}#{suffix}!" + original_method = self.base_class.send(:_enum_methods_module).instance_method "#{prefix}#{to}#{suffix}!" original_method.bind(self).call end else @@ -85,6 +85,18 @@ def initialize(model, column, states, prefix, suffix, name, &block) define_method "#{value_method_name}_transition" do transitions[send(column).to_sym].try! :first end + + validate on: :update do + state_from, state_to = changes[column] + next if state_from.nil? || state_to.nil? + state_from = state_from.to_sym; state_to = state_to.to_sym + + all_possible_transitions = model.instance_variable_get(:@_defined_stateful_enums).flat_map {|enum| enum.events.map(&:transitions) } + filtered_transitions = all_possible_transitions.map! {|h| h[state_from] }.compact + unless filtered_transitions.any? {|to, _condition| to == state_to } + errors.add column, "cannot transition from #{state_from} to #{state_to}" + end + end end end diff --git a/test/dummy/app/models/special_bug.rb b/test/dummy/app/models/special_bug.rb new file mode 100644 index 0000000..8f1ef7a --- /dev/null +++ b/test/dummy/app/models/special_bug.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class SpecialBug < Bug +end diff --git a/test/dummy/db/migrate/20160307203948_create_bugs.rb b/test/dummy/db/migrate/20160307203948_create_bugs.rb index a2988b8..98cf615 100644 --- a/test/dummy/db/migrate/20160307203948_create_bugs.rb +++ b/test/dummy/db/migrate/20160307203948_create_bugs.rb @@ -8,6 +8,7 @@ def change t.integer :status, default: 0 t.integer :assigned_to_id t.datetime :resolved_at + t.string :type t.timestamps null: false end diff --git a/test/mechanic_machine_test.rb b/test/mechanic_machine_test.rb index 7a3bae8..1affdad 100644 --- a/test/mechanic_machine_test.rb +++ b/test/mechanic_machine_test.rb @@ -10,6 +10,14 @@ def test_transition bug.assign assert_equal 'assigned', bug.status end + + def test_transition_to_sti + special_bug = SpecialBug.new + assert_equal 'unassigned', special_bug.status + special_bug.assigned_to = User.create!(name: 'user 1') + special_bug.assign + assert_equal 'assigned', special_bug.status + end def test_transition! bug = Bug.new @@ -51,6 +59,17 @@ def test_invalid_transition! assert_equal 'resolved', bug.status end + def test_validation_with_direct_attribute_write + bug = Bug.new + bug.status = 'resolved' + assert_equal true, bug.valid? + bug.save! + assert_equal 'resolved', bug.status + + bug.status = 'assigned' + assert_equal false, bug.valid? + end + def test_can_xxxx? bug = Bug.new assert bug.can_resolve? @@ -251,5 +270,20 @@ def test_enum_definition_with_prefix_and_suffix tes.prefix_archive_suffix assert_equal 'archived', tes.status end + + def test_validation_with_direct_attribute_write_with_prefix_and_suffix + ActiveRecord::Migration.create_table(:attribute_write_with_prefix_and_suffix_test) { |t| t.integer :status } + t = Class.new(ActiveRecord::Base) do + self.table_name = 'attribute_write_with_prefix_and_suffix_test' + enum(:status, [:active, :archived], prefix: :prefix, suffix: :suffix) { event(:archive) { transition(active: :archived) } } + end.new status: :active + t.status = 'archived' + assert_equal true, t.valid? + t.save! + assert_equal 'archived', t.status + + t.status = 'active' + assert_equal false, t.valid? + end end end