statesman 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d60f1115392a3fb65d3911e82fb0afbda37fa632
4
- data.tar.gz: 09691527947f5a9b5b788191a84beb6503c1d175
3
+ metadata.gz: f687db42dd1b0fc5cd988d7ec0ebcab007987a15
4
+ data.tar.gz: 659f1fd7fe54809344ef91627df309ea7aa5765b
5
5
  SHA512:
6
- metadata.gz: 9a3c0edea7bf34a8eccd9106cb916f85599b7afff8b0e8ad781972ac38535e1a325659be2f71a91332323790309cf0103e3034ccc97d43a2d88b416da882a42d
7
- data.tar.gz: f53ba2aea159a9e372a26d8b803459317370082631b4070369ba5e8f8e87315fdd0a8a7117db331d509a2635753651123eb667285b5628025b377992457f8248
6
+ metadata.gz: f3f81abd3724a20f36f89c518fcaec9058c65571cd49b297d24ab695e1808f1da5b4e6e92ab52177915d95ba54ae13bc014582c7958085b0ba527c82b96a79fa
7
+ data.tar.gz: a437668bf5b79bd9671f0bd0387ac44ceac25f7767c9204ce78a8338fc6d5117d9afb0620d69f569bb20e5025b4962a31290761a5fcebbdad7d82c0ef4f54bed
data/.rubocop.yml CHANGED
@@ -18,3 +18,6 @@ MethodLength:
18
18
  CountComments: false # count full line comments?
19
19
  Max: 15
20
20
 
21
+ # Don't require utf-8 encoding comment
22
+ Encoding:
23
+ Enabled: false
data/.travis.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  rvm:
2
2
  - 2.0.0
3
+ - 1.9.3
3
4
  services: mongodb
4
5
  script:
5
6
  - bundle exec rubocop
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ ## v0.2.0, 16 December 2013
2
+ *Additions*
3
+ - Adds Ruby 1.9.3 support (patch by [@jakehow](https://github.com/jakehow))
4
+ - All Mongo dependent tests are tagged so they can be excluded from test runs
5
+
6
+ *Changes*
7
+ - Specs now crash immediately if Mongo is not running
8
+
9
+ ## v0.1.0, 5 November 2013
10
+
11
+ *Additions*
12
+ - Adds Mongoid adapter and generators (patch by [@dluxemburg](https://github.com/dluxemburg))
13
+
14
+ *Changes*
15
+ - Replaces `config#transition_class` with `Statesman::Adapters::ActiveRecordTransition` mixin. (inspired by [@cjbell88](https://github.com/cjbell88))
16
+ - Renames the active record transition generator from `statesman:transition` to `statesman:active_record_transition`.
17
+ - Moves to using `require_relative` internally where possible to avoid stomping on application load paths.
18
+
19
+ ## v0.0.1, 28 October 2013.
20
+ - Initial release
data/README.md CHANGED
@@ -1,14 +1,20 @@
1
1
  ![Statesman](http://f.cl.ly/items/410n2A0S3l1W0i3i0o2K/statesman.png)
2
2
 
3
- A statesmanlike state machine library for Ruby 2.0.
3
+ A statesmanlike state machine library for Ruby 1.9.3 and 2.0.
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/statesman.png)](http://badge.fury.io/rb/statesman)
6
6
  [![Build Status](https://travis-ci.org/gocardless/statesman.png?branch=master)](https://travis-ci.org/gocardless/statesman)
7
7
  [![Code Climate](https://codeclimate.com/github/gocardless/statesman.png)](https://codeclimate.com/github/gocardless/statesman)
8
8
 
9
- Statesman is a little different from other state machine libraries which tack state behaviour directly onto a model. A statesman state machine is defined as a separate class which is instantiated with the model to which it should apply. State transitions are also modelled as a class which can optionally be persisted to the database for a full audit history, including JSON metadata which can be set during a transition.
9
+ Statesman is a little different from other state machine libraries which tack
10
+ state behaviour directly onto a model. A statesman state machine is defined as a
11
+ separate class which is instantiated with the model to which it should
12
+ apply. State transitions are also modelled as a class which can optionally be
13
+ persisted to the database for a full audit history, including JSON metadata
14
+ which can be set during a transition.
10
15
 
11
- This data model allows for interesting things like using a different state machine depending on the value of a model attribute.
16
+ This data model allows for interesting things like using a different state
17
+ machine depending on the value of a model attribute.
12
18
 
13
19
  ## TL;DR Usage
14
20
 
@@ -24,7 +30,7 @@ class OrderStateMachine
24
30
  state :failed
25
31
  state :refunded
26
32
 
27
- transition from: :created, to: [:checking_out, :cancelled]
33
+ transition from: :pending, to: [:checking_out, :cancelled]
28
34
  transition from: :checking_out, to: [:purchased, :cancelled]
29
35
  transition from: :purchased, to: [:shipped, :failed]
30
36
  transition from: :shipped, to: :refunded
@@ -61,7 +67,7 @@ class OrderTransition < ActiveRecord::Base
61
67
  end
62
68
 
63
69
  Order.first.state_machine.current_state
64
- # => "created"
70
+ # => "pending"
65
71
 
66
72
  Order.first.state_machine.can_transition_to?(:cancelled)
67
73
  # => true/false
@@ -78,7 +84,7 @@ Order.first.state_machine.transition_to!(:cancelled)
78
84
  By default Statesman stores transition history in memory only. It can be
79
85
  persisted by configuring Statesman to use a different adapter. For example,
80
86
  ActiveRecord within Rails:
81
-
87
+
82
88
  `config/initializers/statesman.rb`:
83
89
 
84
90
  ```ruby
@@ -123,7 +129,9 @@ Statesman.configure do
123
129
  storage_adapter(Statesman::Adapters::Mongoid)
124
130
  end
125
131
  ```
126
- Statesman defaults to storing transitions in memory. If you're using rails, you can instead configure it to persist transitions to the database by using the ActiveRecord or Mongoid adapter.
132
+ Statesman defaults to storing transitions in memory. If you're using rails, you
133
+ can instead configure it to persist transitions to the database by using the
134
+ ActiveRecord or Mongoid adapter.
127
135
 
128
136
 
129
137
  ## Class methods
@@ -139,7 +147,8 @@ Define a new state and optionally mark as the initial state.
139
147
  ```ruby
140
148
  Machine.transition(from: :some_state, to: :another_state)
141
149
  ```
142
- Define a transition rule. Both method parameters are required, `to` can also be an array of states (`.transition(from: :some_state, to: [:another_state, :some_other_state])`).
150
+ Define a transition rule. Both method parameters are required, `to` can also be
151
+ an array of states (`.transition(from: :some_state, to: [:another_state, :some_other_state])`).
143
152
 
144
153
  #### `Machine.guard_transition`
145
154
  ```ruby
@@ -147,15 +156,19 @@ Machine.guard_transition(from: :some_state, to: another_state) do |object|
147
156
  object.some_boolean?
148
157
  end
149
158
  ```
150
- Define a guard. `to` and `from` parameters are optional, a nil parameter means guard all transitions. The passed block should evaluate to a boolean and must be idempotent as it could be called many times.
159
+ Define a guard. `to` and `from` parameters are optional, a nil parameter means
160
+ guard all transitions. The passed block should evaluate to a boolean and must
161
+ be idempotent as it could be called many times.
151
162
 
152
163
  #### `Machine.before_transition`
153
164
  ```ruby
154
- Machine.before_transition(from: :some_state, to: another_state) do |object|
165
+ Machine.before_transition(from: :some_state, to: another_state) do |object|
155
166
  object.side_effect
156
167
  end
157
168
  ```
158
- Define a callback to run before a transition. `to` and `from` parameters are optional, a nil parameter means run before all transitions. This callback can have side-effects as it will only be run once immediately before the transition.
169
+ Define a callback to run before a transition. `to` and `from` parameters are
170
+ optional, a nil parameter means run before all transitions. This callback can
171
+ have side-effects as it will only be run once immediately before the transition.
159
172
 
160
173
  #### `Machine.after_transition`
161
174
  ```ruby
@@ -163,13 +176,19 @@ Machine.after_transition(from: :some_state, to: another_state) do |object, trans
163
176
  object.side_effect
164
177
  end
165
178
  ```
166
- Define a callback to run after a successful transition. `to` and `from` parameters are optional, a nil parameter means run after all transitions. The model object and transition object are passed as arguments to the callback. This callback can have side-effects as it will only be run once immediately after the transition.
179
+ Define a callback to run after a successful transition. `to` and `from`
180
+ parameters are optional, a nil parameter means run after all transitions. The
181
+ model object and transition object are passed as arguments to the callback.
182
+ This callback can have side-effects as it will only be run once immediately
183
+ after the transition.
167
184
 
168
185
  #### `Machine.new`
169
186
  ```ruby
170
187
  my_machine = Machine.new(my_model, transition_class: MyTransitionModel)
171
188
  ```
172
- Initialize a new state machine instance. `my_model` is required. If using the ActiveRecord adapter `my_model` should have a `has_many` association with `MyTransitionModel`.
189
+ Initialize a new state machine instance. `my_model` is required. If using the
190
+ ActiveRecord adapter `my_model` should have a `has_many` association with
191
+ `MyTransitionModel`.
173
192
 
174
193
  ## Instance methods
175
194
 
@@ -183,13 +202,16 @@ Returns a sorted array of all transition objects.
183
202
  Returns the most recent transition object.
184
203
 
185
204
  #### `Machine#can_transition_to?(:state)`
186
- Returns true if the current state can transition to the passed state and all applicable guards pass.
205
+ Returns true if the current state can transition to the passed state and all
206
+ applicable guards pass.
187
207
 
188
208
  #### `Machine#transition_to!(:state)`
189
- Transition to the passed state, returning `true` on success. Raises `Statesman::GuardFailedError` or `Statesman::TransitionFailedError` on failure.
209
+ Transition to the passed state, returning `true` on success. Raises
210
+ `Statesman::GuardFailedError` or `Statesman::TransitionFailedError` on failure.
190
211
 
191
212
  #### `Machine#transition_to(:state)`
192
- Transition to the passed state, returning `true` on success. Swallows all exceptions and returns false on failure.
213
+ Transition to the passed state, returning `true` on success. Swallows all
214
+ exceptions and returns false on failure.
193
215
 
194
216
  ---
195
217
 
@@ -6,21 +6,23 @@ module Statesman
6
6
  attr_reader :to
7
7
  attr_reader :callback
8
8
 
9
- def initialize(from: nil, to: nil, callback: nil)
10
- unless callback.respond_to?(:call)
9
+ def initialize(options = { from: nil, to: nil, callback: nil })
10
+ unless options[:callback].respond_to?(:call)
11
11
  raise InvalidCallbackError, "No callback passed"
12
12
  end
13
13
 
14
- @from = from
15
- @to = to
16
- @callback = callback
14
+ @from = options[:from]
15
+ @to = options[:to]
16
+ @callback = options[:callback]
17
17
  end
18
18
 
19
19
  def call(*args)
20
20
  callback.call(*args)
21
21
  end
22
22
 
23
- def applies_to?(from: nil, to: nil)
23
+ def applies_to?(options = { from: nil, to: nil })
24
+ from = options[:from]
25
+ to = options[:to]
24
26
  # rubocop:disable RedundantSelf
25
27
  (self.from.nil? && self.to.nil?) ||
26
28
  (from.nil? && to == self.to) ||
@@ -19,9 +19,9 @@ module Statesman
19
19
  @states ||= []
20
20
  end
21
21
 
22
- def state(name, initial: false)
22
+ def state(name, options = { initial: false })
23
23
  name = name.to_s
24
- if initial
24
+ if options[:initial]
25
25
  validate_initial_state(name)
26
26
  @initial_state = name
27
27
  end
@@ -44,9 +44,9 @@ module Statesman
44
44
  @guards ||= []
45
45
  end
46
46
 
47
- def transition(from: nil, to: nil)
48
- from = to_s_or_nil(from)
49
- to = Array(to).map { |item| to_s_or_nil(item) }
47
+ def transition(options = { from: nil, to: nil })
48
+ from = to_s_or_nil(options[:from])
49
+ to = Array(options[:to]).map { |item| to_s_or_nil(item) }
50
50
 
51
51
  successors[from] ||= []
52
52
 
@@ -55,33 +55,33 @@ module Statesman
55
55
  successors[from] += to
56
56
  end
57
57
 
58
- def before_transition(from: nil, to: nil, &block)
59
- from = to_s_or_nil(from)
60
- to = to_s_or_nil(to)
58
+ def before_transition(options = { from: nil, to: nil }, &block)
59
+ from = to_s_or_nil(options[:from])
60
+ to = to_s_or_nil(options[:to])
61
61
 
62
62
  validate_callback_condition(from: from, to: to)
63
63
  before_callbacks << Callback.new(from: from, to: to, callback: block)
64
64
  end
65
65
 
66
- def after_transition(from: nil, to: nil, &block)
67
- from = to_s_or_nil(from)
68
- to = to_s_or_nil(to)
66
+ def after_transition(options = { from: nil, to: nil }, &block)
67
+ from = to_s_or_nil(options[:from])
68
+ to = to_s_or_nil(options[:to])
69
69
 
70
70
  validate_callback_condition(from: from, to: to)
71
71
  after_callbacks << Callback.new(from: from, to: to, callback: block)
72
72
  end
73
73
 
74
- def guard_transition(from: nil, to: nil, &block)
75
- from = to_s_or_nil(from)
76
- to = to_s_or_nil(to)
74
+ def guard_transition(options = { from: nil, to: nil }, &block)
75
+ from = to_s_or_nil(options[:from])
76
+ to = to_s_or_nil(options[:to])
77
77
 
78
78
  validate_callback_condition(from: from, to: to)
79
79
  guards << Guard.new(from: from, to: to, callback: block)
80
80
  end
81
81
 
82
- def validate_callback_condition(from: nil, to: nil)
83
- from = to_s_or_nil(from)
84
- to = to_s_or_nil(to)
82
+ def validate_callback_condition(options = { from: nil, to: nil })
83
+ from = to_s_or_nil(options[:from])
84
+ to = to_s_or_nil(options[:to])
85
85
 
86
86
  [from, to].compact.each { |state| validate_state(state) }
87
87
  return if from.nil? && to.nil?
@@ -139,10 +139,12 @@ module Statesman
139
139
  end
140
140
 
141
141
  def initialize(object,
142
- transition_class: Statesman::Adapters::MemoryTransition)
142
+ options = {
143
+ transition_class: Statesman::Adapters::MemoryTransition
144
+ })
143
145
  @object = object
144
- @storage_adapter = Statesman.storage_adapter.new(transition_class,
145
- object)
146
+ @storage_adapter = Statesman.storage_adapter.new(
147
+ options[:transition_class], object)
146
148
  end
147
149
 
148
150
  def current_state
@@ -191,31 +193,31 @@ module Statesman
191
193
 
192
194
  private
193
195
 
194
- def guards_for(from: nil, to: nil)
195
- select_callbacks_for(self.class.guards, from: from, to: to)
196
+ def guards_for(options = { from: nil, to: nil })
197
+ select_callbacks_for(self.class.guards, options)
196
198
  end
197
199
 
198
- def before_callbacks_for(from: nil, to: nil)
199
- select_callbacks_for(self.class.before_callbacks, from: from, to: to)
200
+ def before_callbacks_for(options = { from: nil, to: nil })
201
+ select_callbacks_for(self.class.before_callbacks, options)
200
202
  end
201
203
 
202
- def after_callbacks_for(from: nil, to: nil)
203
- select_callbacks_for(self.class.after_callbacks, from: from, to: to)
204
+ def after_callbacks_for(options = { from: nil, to: nil })
205
+ select_callbacks_for(self.class.after_callbacks, options)
204
206
  end
205
207
 
206
- def select_callbacks_for(callbacks, from: nil, to: nil)
207
- from = to_s_or_nil(from)
208
- to = to_s_or_nil(to)
208
+ def select_callbacks_for(callbacks, options = { from: nil, to: nil })
209
+ from = to_s_or_nil(options[:from])
210
+ to = to_s_or_nil(options[:to])
209
211
  callbacks.select { |callback| callback.applies_to?(from: from, to: to) }
210
212
  end
211
213
 
212
- def validate_transition(from: nil, to: nil, metadata: nil)
213
- from = to_s_or_nil(from)
214
- to = to_s_or_nil(to)
214
+ def validate_transition(options = { from: nil, to: nil, metadata: nil })
215
+ from = to_s_or_nil(options[:from])
216
+ to = to_s_or_nil(options[:to])
215
217
 
216
218
  # Call all guards, they raise exceptions if they fail
217
219
  guards_for(from: from, to: to).each do |guard|
218
- guard.call(@object, last_transition, metadata)
220
+ guard.call(@object, last_transition, options[:metadata])
219
221
  end
220
222
 
221
223
  successors = self.class.successors[from] || []
@@ -1,3 +1,3 @@
1
1
  module Statesman
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -2,13 +2,29 @@ require "statesman"
2
2
  require "sqlite3"
3
3
  require "active_record"
4
4
  require "support/active_record"
5
+ require "mongoid"
5
6
 
6
7
  RSpec.configure do |config|
7
8
  config.expect_with :rspec do |c|
8
9
  c.syntax = :expect
9
10
  end
10
11
 
11
- config.order = 'random'
12
+ config.order = "random"
13
+
14
+ # Try a mongo connection at the start of the suite and raise if it fails
15
+ begin
16
+ Mongoid.configure do |mongo_config|
17
+ mongo_config.connect_to("statesman_test")
18
+ mongo_config.sessions["default"]["options"]["max_retries"] = 2
19
+ end
20
+ # Attempting a mongo operation will trigger 2 retries then throw an
21
+ # exception if mongo is not running.
22
+ Mongoid.purge! unless config.exclusion_filter[:mongo]
23
+ rescue Moped::Errors::ConnectionFailure => error
24
+ puts "The spec suite requires MongoDB to be installed and running locally"
25
+ puts "Mongo dependent specs can be filtered with rspec --tag '~mongo'"
26
+ raise(error)
27
+ end
12
28
 
13
29
  config.before(:each) do
14
30
  # Connect to & cleanup test database
@@ -4,7 +4,7 @@ require "statesman/exceptions"
4
4
  require "support/mongoid"
5
5
  require "mongoid"
6
6
 
7
- describe Statesman::Adapters::Mongoid do
7
+ describe Statesman::Adapters::Mongoid, mongo: true do
8
8
 
9
9
  after do
10
10
  Mongoid.purge!
@@ -331,7 +331,7 @@ describe Statesman::Machine do
331
331
 
332
332
  context "with a guard" do
333
333
  let(:result) { true }
334
- let(:guard_cb) { -> (*args) { result } }
334
+ let(:guard_cb) { ->(*args) { result } }
335
335
  before { machine.guard_transition(from: :x, to: :y, &guard_cb) }
336
336
 
337
337
  context "and an object to act on" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statesman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Marr
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-05 00:00:00.000000000 Z
12
+ date: 2013-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -147,6 +147,7 @@ files:
147
147
  - .gitignore
148
148
  - .rubocop.yml
149
149
  - .travis.yml
150
+ - CHANGELOG.md
150
151
  - Gemfile
151
152
  - Guardfile
152
153
  - LICENSE.txt