statesman 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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