Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transient attributes quirks #93

Open
boardfish opened this issue Jan 22, 2021 · 1 comment
Open

Transient attributes quirks #93

boardfish opened this issue Jan 22, 2021 · 1 comment

Comments

@boardfish
Copy link

I have a state machine on a class that inherits from ActiveRecord::Base that's something like this:

    state_machine :state do
      state :one, :two do
        validate { |record| record.validate_has_gubbins }
      end
      state :three

      event :one_to_two do
        transition one: :two # , if: ->(m) { m.gubbins.present? }
      end

      event :two_to_three do
        transition two: :three
      end
    end

It's based on an attribute that's not persisted to the database - a transient attribute. I define this with an attr_accessor and set its initial value in an after_initialize hook. The initial value is figured out from the current state of the record.

This is a useful approach because the state machine can be changed without a significant negative impact on existing records - if a new state were introduced between one and two, for example, two would still be stored in the database and would leave all two records in that state, whereas if it's dynamically figured out, they'd just move back to one with all data intact. It's just as useful for validating records at different stages while being a little more flexible where you need it.

There are a couple of quirks with this approach at the moment, though:

  • The validator I've added up there doesn't run. I'm following the advice here to set the validation on both the one and two states. The fact that the validator doesn't run then means I need to use the commented part to protect that transition.
    I did some experimenting myself and the state block does get called, but the validate block does not.
  • initial: doesn't work. I tried setting it to a lambda and a symbol, and I also tried playing with initialize_state_machine(force: true). The fix as mentioned is to set its initial value in an after_initialize hook as you would any other transient attribute, but it'd be better to have it within the state machine declaration.
@boardfish
Copy link
Author

boardfish commented Jan 27, 2021

I've since changed approaches - I'm now using a dynamic state machine as per the docs. Thus far, I think I'd recommend that, but I've got the following observations:

  • The state machine is on the Machine class rather than the object that gets passed in to Machine#new. You'll need to allow the machine to recognize the parent object if you want to use that in conditional transitions. Aim for something like if: ->(machine) { machine.parent. [...] }
  • You'll also need to set the initial state based on the parent object.

Tangentially related, but with transient attributes, use the ActiveRecord Attributes API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant