stateful_enum icon indicating copy to clipboard operation
stateful_enum copied to clipboard

Support STI and inheritance(with test)

Open unasuke opened this issue 7 years ago • 2 comments

dups #29 .But I wrote test.

Original pull request description is below.

I used the gem on a STI class and got an error which would be solved with using on self.class => self.class.base_class.

It would be nice if you could do a patch release with this fix.

Thank you in advance!!


If use stateful_enum on STI model, raise that error.

/home/unasuke/src/github.com/unasuke/stateful_enum/test/mechanic_machine_test.rb:18:in `test_transition_to_sti'
     15:     special_bug = SpecialBug.new
     16:     assert_equal 'unassigned', special_bug.status
     17:     special_bug.assigned_to = User.create!(name: 'user 1')
  => 18:     special_bug.assign
     19:     assert_equal 'assigned', special_bug.status
     20:   end
     21:
/home/unasuke/src/github.com/unasuke/stateful_enum/lib/stateful_enum/machine.rb:56:in `block (2 levels) in initialize'
/home/unasuke/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.2.0/lib/active_support/callbacks.rb:98:in `run_callbacks'
/home/unasuke/src/github.com/unasuke/stateful_enum/lib/stateful_enum/machine.rb:57:in `block (3 levels) in initialize'
/home/unasuke/src/github.com/unasuke/stateful_enum/lib/stateful_enum/machine.rb:57:in `instance_method'
Error: test_transition_to_sti(StatefulEnumTest): NameError: undefined method `assigned!' for module `#<Module:0x0000564443546130>'
=========================================================================================================

unasuke avatar May 11 '18 08:05 unasuke

@raskhadafi @unasuke Nice, but what's gonna happen if we created multiple STI child classes, and then declared enums on each of these? e.g.

class Bug < ActiveRecord::Base
  self.abstract_class = true
end

class NormalBug < Bug
  enum status: {unassigned: 0, assigned: 1, resolved: 2, closed: 3} do
    ...
  end
end

class CriticalBug < Bug
  enum status: {unassigned: 0, assigned: 1, resolved: 2, closed: 3, released: 4} do
    ...
  end
end

So, maybe we can fallback to base_class only if the current class has no enum declared?

Moreover, there can be enums with the same name on BOTH parent and children classes, and I'm not sure what's the desired behavior in such case. You're proposing to fallback on the parent class' method, but I guess that's not how AR built-in enum works (I guess. I mean, I just guess. Haven't actually tried).

amatsuda avatar May 11 '18 09:05 amatsuda

I confirmed the current behavior of stateful_enum and it seems good for me. (STI doesn't work when base_class is an abstract class)

How do you think @amatsuda ? Is it test cases lack some edge-case?

and... @raskhadafi, do you think about this?

class Bug < ActiveRecord::Base
  enum status: {unassigned: 0, assigned: 1, resolved: 2, closed: 3} do
    event :assign do
      transition :unassigned => :assigned
    end
  end
end

class NormalBug < Bug
  enum status: {unassigned: 0, assigned: 1, resolved: 2, closed: 3} do
    event :assign do
      transition :closed => :assigned
    end
    event :resolve do
      transition [:unassigned, :assigned] => :resolved
    end
  end
end

class CriticalBug < Bug
  enum status: {unassigned: 0, assigned: 1, resolved: 2, closed: 3, released: 4} do
    event :assign do
      transition :closed => :assigned
    end
    event :release do
      transition :resolved => :released
    end
  end
end

class BugTest < Minitest::Test
  def test_bug_cannnot_resolve
    bug = Bug.create!(status: :unassigned)

    assert_raises(NoMethodError) do
      bug.resolve
    end
  end

  def test_normal_bug_can_resolve
    normal_bug = NormalBug.create!(status: :unassigned)
    normal_bug.resolve
    assert_equal 'resolved', normal_bug.status
  end

  def test_unassigned_critical_big_cannot_assign
    critical_bug = CriticalBug.create!(status: :unassigned)
    critical_bug.assign
    assert_equal 'unassigned', critical_bug.status
  end
end
sample code(full)
# frozen_string_literal: true

begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  #gem "rails", github: "rails/rails"
  gem "rails", '5.2.0'
  gem "sqlite3"
  gem 'stateful_enum', git: 'https://github.com/unasuke/stateful_enum.git', ref: '6f6e18b1d'
end

require "active_record"
require "minitest/autorun"
require "logger"

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :bugs, force: true do |t|
    t.integer :status
    t.string :type
  end
end

class Bug < ActiveRecord::Base
  enum status: {unassigned: 0, assigned: 1, resolved: 2, closed: 3} do
    event :assign do
      transition :unassigned => :assigned
    end
  end
end

class NormalBug < Bug
  enum status: {unassigned: 0, assigned: 1, resolved: 2, closed: 3} do
    event :assign do
      transition :closed => :assigned
    end
    event :resolve do
      transition [:unassigned, :assigned] => :resolved
    end
  end
end

class CriticalBug < Bug
  enum status: {unassigned: 0, assigned: 1, resolved: 2, closed: 3, released: 4} do
    event :assign do
      transition :closed => :assigned
    end
    event :release do
      transition :resolved => :released
    end
  end
end

class BugTest < Minitest::Test
  def test_bug_cannnot_resolve
    bug = Bug.create!(status: :unassigned)

    assert_raises(NoMethodError) do
      bug.resolve
    end
  end

  def test_normal_bug_can_resolve
    normal_bug = NormalBug.create!(status: :unassigned)
    normal_bug.resolve
    assert_equal 'resolved', normal_bug.status
  end

  def test_unassigned_critical_big_cannot_assign
    critical_bug = CriticalBug.create!(status: :unassigned)
    critical_bug.assign
    assert_equal 'unassigned', critical_bug.status
  end
end

unasuke avatar Jun 18 '18 09:06 unasuke