piotrmurach / finite_machine

A minimal finite state machine with a straightforward syntax.
https://piotrmurach.github.io/finite_machine/
MIT License
808 stars 38 forks source link

Ruby 2.6.0 support (ruby-2.6.0-preview3) #53

Closed bmalets closed 5 years ago

bmalets commented 5 years ago

Does this gem support ruby 2.6.0 version?

Found that model_object.finite_machine.current with ruby "ruby-2.6.0-preview3" returns an array of Proc instead if current state.

Different behaviors:

2.5.0 :005 > Order.first.finite_machine
  Order Load (0.9ms)  SELECT  "orders".* FROM "orders" ORDER BY "orders"."number" DESC LIMIT $1  [["LIMIT", 1]]
 => <#FiniteMachine::StateMachine:0x3ff971d6b294 @states=[:none, :cart, :pending, :prepared, :received, :delivered, :sent, :any, :canceled], @events=[:init, :create, :prepare, :receive, :dispatch, :deliver, :cancel], @transitions=[{:none=>:cart}, {:cart=>:pending}, {:pending=>:prepared}, {:prepared=>:received}, {:delivered=>:received}, {:prepared=>:sent}, {:sent=>:delivered}, {:any=>:canceled}]> 
2.5.0 :006 > Order.first.finite_machine.current
  Order Load (0.7ms)  SELECT  "orders".* FROM "orders" ORDER BY "orders"."number" DESC LIMIT $1  [["LIMIT", 1]]
 => :cart 
2.6.0-preview3 :009 > Order.first.finite_machine
  Order Load (0.8ms)  SELECT  "orders".* FROM "orders" ORDER BY "orders"."number" DESC LIMIT $1  [["LIMIT", 1]]
 => #<Class:0x00007fd1f921aef8> 
2.6.0-preview3 :010 > Order.first.finite_machine.current
  Order Load (0.7ms)  SELECT  "orders".* FROM "orders" ORDER BY "orders"."number" DESC LIMIT $1  [["LIMIT", 1]]
 => [#<Proc:0x00007fd1f92416c0>, #<Proc:0x00007fd1f9240748>, #<Proc:0x00007fd1f924bc38>, #<Proc:0x00007fd1f924ad60>, #<Proc:0x00007fd1f924a428>, #<Proc:0x00007fd1f9249000>, #<Proc:0x00007fd1f9248768>] 

Code example (model and concern with finite machine):

module OrderFiniteMachine
  extend ActiveSupport::Concern

  included do
    before_validation :set_initial_state, on: :create
    after_initialize :restore_state
    after_find :restore_state
  end

  def finite_machine
    context = self

    @state_engine ||= FiniteMachine.define do
      target context

      initial :cart

      events do
        event :create, cart: :pending
        event :prepare, pending: :prepared
        event :receive, prepared: :received
        event :dispatch, prepared: :sent
        event :deliver, sent: :delivered
        event :receive, delivered: :received
        event :cancel, any: :canceled
      end
    end
  end

  private

  def set_initial_state
    self.state = finite_machine.current
  end
end

# == Schema Information
#
# Table name: orders
#
#  id                :integer          not null, primary key
#  state             :string
#  created_at        :datetime         not null
#  updated_at        :datetime         not null
#
class Order < ApplicationRecord
  include OrderFiniteMachine
end
piotrmurach commented 5 years ago

Hi Bohdan!

Thanks for using finite_machine!

I don't believe this is anything to do with finite_machine not working on latest Ruby. I've installed Ruby 2.6.0-preview3 and 2.6.0-rc1 and both pass the test suite. Also, please notice that finite_machine is also tested against Ruby master branch on travis.

I'm pretty sure that you're not using the latest version v0.12.0. Why is that? For starters, there is no more target assignment, the context is directly provided as an argument to define. But please bear in mind that define provides an anonymous class now! If you look at your second example you will see that you're receiving exactly that:

=> #<Class:0x00007fd1f921aef8> 

Please see define for more information. In your case you probably need to use new to create an instance of FiniteMachine instead:

def finite_machine
  @state_engine ||= FiniteMachine.new(self) do
  ...

Also please bear in mind that events scope doesn't exist any more which should generate an error for you as well! You specify events directly inside the block:

  initial :cart

  event :create, cart: :pending
  event :prepare, pending: :prepared
  ...

Once you update your code I'm certain it will work on the latest Ruby!

The latest finite_machine had a lot of internals rewritten to make it much simpler, remove ambiguity and unnecessary calls and increase performance.