pluginaweek / state_machine

Adds support for creating state machines for attributes on any Ruby class
http://www.pluginaweek.org
MIT License
3.74k stars 510 forks source link

Added state checking at least support - state_from? method #231

Closed hlegius closed 11 years ago

hlegius commented 11 years ago

Hello folks!

I've added a useful resource for "linear based" state machine: an option to check if some object has at least some state.

Motivation:

In one of my needs with state_machine, I'd like to check if my Customer is at least "paid", so, paid or any further states can be valid. Today, I need code something like that:

if @customer.paid? or @customer.verified? or @customer.authorized?

Instead, I wanna replace this painful task with something more encapsulated:

if @customer.state_from?(:paid)

More simple and readable, right? I've added it to state_machine gem with new unit and functional tests, that validates my code, following the project guidelines. I've updated the Readme.md to explain how to use this new feature.

As I explained in README file, this method is useful with "sequential" state machine. With "Vehicle", could be a little strange (I think, so).

Well. This is my contribution to the project. I read the code a lot before update something and I liked what a read. Very nice project.

If you guys have questions or improvements suggestions, please let me know. This is my first Ruby contribute to opensource. I did it before with PHP projects, but in Ruby is my first try =P

Thanks and keep goin'!

the8472 commented 11 years ago

This can be highly counter-intuitive behavior if the state-machine contains cycles or some utility transitions using BlacklistMatcher or AllMatcher, e.g. a "go back to initial state from any point in the process" event.

I have made the experience that what people think of as a "linear" process is in fact not in practice, which can lead to confusion when their assumptions are not met.

hlegius commented 11 years ago

Yes. As I said in PR, this can be strange for non-linear process. What do you consider valid in this case? May I update the feature, enabling to check for cycles and blacklists and in these cases disable this feature... don't know.

This is confuse for state machine with cycles, but I see it too useful when we got one-direction process.

Thanks!

obrie commented 11 years ago

@hlegius Thanks for your effort and congrats on your first Ruby open-source PR! This is an interesting issue and, as @the8472 notes, there are a lot of edge cases with this approach as soon as the state machine definition starts to take on any amount of complexity.

If I were approached with this problem, I would have recommended one of four approaches:

  1. Define a helper method that essentially implements what you've proposed be specific to your particular use case. This allows anyone to make the logic as simple or complex as they need. For example:
    class Customer
      # ...

      def premium?
        paid? || verified? || authorized?
      end
    end
  1. Set an additional attribute on the class when the customer has reached that "minimum" state. Again, this allows for any amount of complex logic around when you deem that customer "premium" (as I've named it in this case). For example:
    class Customer
      state_machine do
        after_transition :to => :paid, :mark_as_premium
      end

      def mark_as_premium
        self.premium = true
        save!
      end
    end
  1. Use an audit trail to see what states have been crossed such as state_machine-audit_trail (https://github.com/wvanbergen/state_machine-audit_trail). This allows you to have the same type of logic around looking at the complete history of transitions and determining a customer's premium status.
  2. Introduce multiple state machines for tracking different states of the customer. For example, I could see an argument for pay_status (paid / unpaid) being different than account_status (unverified / verified / unauthorized / authorized).

I believe that each of these cases will provide the most flexibility and avoids some of the constraints introduced by features that rely on linear machines.

Thoughts?

hlegius commented 11 years ago

@obrie make sense, you're right. Indeed, I liked the project. If you need some help with bugfixes or features in pipeline, I'd like to help!

Thanks!

obrie commented 11 years ago

Thanks @hlegius -- I've been on hiatus for almost a year and am finally coming back to the project (hurray!) .. will certainly keep that in mind :)