Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion lib/stateful_enum/machine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions test/dummy/app/models/special_bug.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# frozen_string_literal: true

class SpecialBug < Bug
end
1 change: 1 addition & 0 deletions test/dummy/db/migrate/20160307203948_create_bugs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions test/mechanic_machine_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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?
Expand Down Expand Up @@ -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