statesman 1.2.1 → 1.2.2

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: dc520c4907fb1573b7682bd1db4affe787b1d036
4
- data.tar.gz: 796e3a0482fc5814b9ae9da4ec870dc97d43b851
3
+ metadata.gz: a4cc83b59dfad9ed139c2215211c93550f06d4ea
4
+ data.tar.gz: 170879ce893bcd74ab1742dd609d4a9abb163ffd
5
5
  SHA512:
6
- metadata.gz: 3ae6ba2af2f37b4df09d025d2bdfce5849aba5ba3e23a3d7c341061db4ad8002547177302a155b29c2770bc58d5b0988c1df2854e186a36d0236f9d5f0ca96e9
7
- data.tar.gz: 57d5ab47d228ae72de01cff8e9b03db86bdb755bb70b8231a5f7639d7d45320fc647587061c76053353c4d56004a3dbefa8a52045e4bbd6e6175bc58fbc69cc3
6
+ metadata.gz: a1c4175b5bc89dbe750b758f37cef8e08469515eb94f470f7872b7dcba2c7dec4650e4ed28670e7fc7d2f64647abf8dbc35e6f2ea0b980041fe38586a8eebfd9
7
+ data.tar.gz: fb34e4fd474a1ca62585e7837bc16525b872b9b69f99b65ccb10e69f06b359c2cadc14ef04f75ff90ee459d779e9a15a8769fb7c6b06a2704b84cc6c15869173
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## v1.2.2 24 March 2015
2
+
3
+ - Add support for namespaced transition models (patch by [@DanielWright](https://github.com/DanielWright))
4
+
1
5
  ## v1.2.1 24 March 2015
2
6
 
3
7
  - Add support for Postgres 9.4's `jsonb` column type (patch by [@isaacseymour](https://github.com/isaacseymour))
data/README.md CHANGED
@@ -134,11 +134,12 @@ And add an association from the parent model:
134
134
 
135
135
  ```ruby
136
136
  class Order < ActiveRecord::Base
137
- has_many :order_transitions
137
+ has_many :transitions, class_name: "OrderTransition"
138
138
 
139
139
  # Initialize the state machine
140
140
  def state_machine
141
- @state_machine ||= OrderStateMachine.new(self, transition_class: OrderTransition)
141
+ @state_machine ||= OrderStateMachine.new(self, transition_class: OrderTransition,
142
+ association_name: :transitions)
142
143
  end
143
144
 
144
145
  # Optionally delegate some methods
@@ -298,6 +299,29 @@ class Order < ActiveRecord::Base
298
299
  end
299
300
  ```
300
301
 
302
+ If the transition class-name differs from the association name, you will also
303
+ need to define a corresponding `transition_name` class method:
304
+
305
+ ```ruby
306
+ class Order < ActiveRecord::Base
307
+ has_many :transitions, class_name: "OrderTransition"
308
+
309
+ private
310
+
311
+ def self.transition_name
312
+ :transitions
313
+ end
314
+
315
+ def self.transition_class
316
+ OrderTransition
317
+ end
318
+
319
+ def self.initial_state
320
+ OrderStateMachine.initial_state
321
+ end
322
+ end
323
+ ```
324
+
301
325
  #### `Model.in_state(:state_1, :state_2, etc)`
302
326
  Returns all models currently in any of the supplied states.
303
327
 
@@ -8,7 +8,7 @@ module Statesman
8
8
 
9
9
  JSON_COLUMN_TYPES = %w(json jsonb).freeze
10
10
 
11
- def initialize(transition_class, parent_model, observer)
11
+ def initialize(transition_class, parent_model, observer, options = {})
12
12
  serialized = serialized?(transition_class)
13
13
  column_type = transition_class.columns_hash['metadata'].sql_type
14
14
  if !serialized && !JSON_COLUMN_TYPES.include?(column_type)
@@ -22,6 +22,8 @@ module Statesman
22
22
  @transition_class = transition_class
23
23
  @parent_model = parent_model
24
24
  @observer = observer
25
+ @association_name =
26
+ options[:association_name] || @transition_class.table_name
25
27
  end
26
28
 
27
29
  def create(from, to, metadata = {})
@@ -76,7 +78,7 @@ module Statesman
76
78
  end
77
79
 
78
80
  def transitions_for_parent
79
- @parent_model.send(@transition_class.table_name)
81
+ @parent_model.send(@association_name)
80
82
  end
81
83
 
82
84
  def unset_old_most_recent
@@ -66,23 +66,31 @@ module Statesman
66
66
  transition_class.table_name.to_sym
67
67
  end
68
68
 
69
+ def transition_reflection
70
+ reflect_on_association(transition_name)
71
+ end
72
+
69
73
  def model_foreign_key
70
- reflect_on_association(transition_name).foreign_key
74
+ transition_reflection.foreign_key
75
+ end
76
+
77
+ def model_table
78
+ transition_reflection.table_name
71
79
  end
72
80
 
73
81
  def transition1_join
74
- "LEFT OUTER JOIN #{transition_name} transition1
82
+ "LEFT OUTER JOIN #{model_table} transition1
75
83
  ON transition1.#{model_foreign_key} = #{table_name}.id"
76
84
  end
77
85
 
78
86
  def transition2_join
79
- "LEFT OUTER JOIN #{transition_name} transition2
87
+ "LEFT OUTER JOIN #{model_table} transition2
80
88
  ON transition2.#{model_foreign_key} = #{table_name}.id
81
89
  AND transition2.sort_key > transition1.sort_key"
82
90
  end
83
91
 
84
92
  def most_recent_transition_join
85
- "LEFT OUTER JOIN #{transition_name} AS last_transition
93
+ "LEFT OUTER JOIN #{model_table} AS last_transition
86
94
  ON #{table_name}.id = last_transition.#{model_foreign_key}
87
95
  AND last_transition.most_recent = #{db_true}"
88
96
  end
@@ -9,7 +9,7 @@ module Statesman
9
9
 
10
10
  # We only accept mode as a parameter to maintain a consistent interface
11
11
  # with other adapters which require it.
12
- def initialize(transition_class, parent_model, observer)
12
+ def initialize(transition_class, parent_model, observer, _ = {})
13
13
  @history = []
14
14
  @transition_class = transition_class
15
15
  @parent_model = parent_model
@@ -6,7 +6,7 @@ module Statesman
6
6
  attr_reader :transition_class
7
7
  attr_reader :parent_model
8
8
 
9
- def initialize(transition_class, parent_model, observer)
9
+ def initialize(transition_class, parent_model, observer, _ = {})
10
10
  @transition_class = transition_class
11
11
  @parent_model = parent_model
12
12
  @observer = observer
@@ -183,7 +183,7 @@ module Statesman
183
183
  @object = object
184
184
  @transition_class = options[:transition_class]
185
185
  @storage_adapter = adapter_class(@transition_class).new(
186
- @transition_class, object, self)
186
+ @transition_class, object, self, options)
187
187
  send(:after_initialize) if respond_to? :after_initialize
188
188
  end
189
189
 
@@ -1,3 +1,3 @@
1
1
  module Statesman
2
- VERSION = "1.2.1"
2
+ VERSION = "1.2.2"
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -39,7 +39,12 @@ RSpec.configure do |config|
39
39
  end
40
40
 
41
41
  config.before(:each, active_record: true) do
42
- tables = %w(my_active_record_models my_active_record_model_transitions)
42
+ tables = %w(
43
+ my_active_record_models
44
+ my_active_record_model_transitions
45
+ my_namespace_my_active_record_models
46
+ my_namespace_my_active_record_model_transitions
47
+ )
43
48
  tables.each do |table_name|
44
49
  sql = "DROP TABLE IF EXISTS #{table_name};"
45
50
  ActiveRecord::Base.connection.execute(sql)
@@ -204,4 +204,25 @@ describe Statesman::Adapters::ActiveRecord, active_record: true do
204
204
  end
205
205
  end
206
206
  end
207
+
208
+ context "with a namespaced model" do
209
+ before do
210
+ silence_stream(STDOUT) do
211
+ CreateNamespacedARModelMigration.migrate(:up)
212
+ CreateNamespacedARModelTransitionMigration.migrate(:up)
213
+ end
214
+ end
215
+
216
+ before do
217
+ MyNamespace::MyActiveRecordModelTransition.serialize(:metadata, JSON)
218
+ end
219
+ let(:observer) { double(Statesman::Machine, execute: nil) }
220
+ let(:model) do
221
+ MyNamespace::MyActiveRecordModel.create(current_state: :pending)
222
+ end
223
+
224
+ it_behaves_like "an adapter",
225
+ described_class, MyNamespace::MyActiveRecordModelTransition,
226
+ association_name: :my_active_record_model_transitions
227
+ end
207
228
  end
@@ -11,9 +11,16 @@ require "spec_helper"
11
11
  # history: Returns the full transition history
12
12
  # last: Returns the latest transition history item
13
13
  #
14
- shared_examples_for "an adapter" do |adapter_class, transition_class|
14
+ # rubocop:disable Metrics/LineLength
15
+ # NOTE This line cannot reasonably be shortened.
16
+ shared_examples_for "an adapter" do |adapter_class, transition_class, options = {}|
17
+ # rubocop:enable Metrics/LineLength
18
+
15
19
  let(:observer) { double(Statesman::Machine, execute: nil) }
16
- let(:adapter) { adapter_class.new(transition_class, model, observer) }
20
+ let(:adapter) do
21
+ adapter_class.new(transition_class,
22
+ model, observer, options)
23
+ end
17
24
 
18
25
  describe "#initialize" do
19
26
  subject { adapter }
@@ -316,21 +316,23 @@ describe Statesman::Machine do
316
316
  context "transition class" do
317
317
  it "sets a default" do
318
318
  expect(Statesman.storage_adapter).to receive(:new).once.
319
- with(Statesman::Adapters::MemoryTransition, my_model, anything)
319
+ with(Statesman::Adapters::MemoryTransition,
320
+ my_model, anything, anything)
320
321
  machine.new(my_model)
321
322
  end
322
323
 
323
324
  it "sets the passed class" do
324
325
  my_transition_class = Class.new
325
326
  expect(Statesman.storage_adapter).to receive(:new).once.
326
- with(my_transition_class, my_model, anything)
327
+ with(my_transition_class, my_model, anything, anything)
327
328
  machine.new(my_model, transition_class: my_transition_class)
328
329
  end
329
330
 
330
331
  it "falls back to Memory without transaction_class" do
331
332
  allow(Statesman).to receive(:storage_adapter).and_return(Class.new)
332
333
  expect(Statesman::Adapters::Memory).to receive(:new).once.
333
- with(Statesman::Adapters::MemoryTransition, my_model, anything)
334
+ with(Statesman::Adapters::MemoryTransition,
335
+ my_model, anything, anything)
334
336
  machine.new(my_model)
335
337
  end
336
338
  end
@@ -37,7 +37,7 @@ class CreateMyActiveRecordModelMigration < ActiveRecord::Migration
37
37
  def change
38
38
  create_table :my_active_record_models do |t|
39
39
  t.string :current_state
40
- t.timestamps(null: false)
40
+ t.timestamps null: false
41
41
  end
42
42
  end
43
43
  end
@@ -59,7 +59,7 @@ class CreateMyActiveRecordModelTransitionMigration < ActiveRecord::Migration
59
59
  t.text :metadata, default: '{}'
60
60
  end
61
61
 
62
- t.timestamps(null: false)
62
+ t.timestamps null: false
63
63
  end
64
64
 
65
65
  add_index :my_active_record_model_transitions,
@@ -82,3 +82,65 @@ class DropMostRecentColumn < ActiveRecord::Migration
82
82
  remove_column :my_active_record_model_transitions, :most_recent
83
83
  end
84
84
  end
85
+
86
+ module MyNamespace
87
+ class MyActiveRecordModel < ActiveRecord::Base
88
+ has_many :my_active_record_model_transitions,
89
+ class_name: "MyNamespace::MyActiveRecordModelTransition"
90
+
91
+ def self.table_name_prefix
92
+ "my_namespace_"
93
+ end
94
+
95
+ def state_machine
96
+ @state_machine ||= MyStateMachine.new(
97
+ self, transition_class: MyNameSpace::MyActiveRecordModelTransition,
98
+ association_name: :my_active_record_model_transitions)
99
+ end
100
+
101
+ def metadata
102
+ super || {}
103
+ end
104
+ end
105
+
106
+ class MyActiveRecordModelTransition < ActiveRecord::Base
107
+ belongs_to :my_active_record_model,
108
+ class_name: "MyNamespace::MyActiveRecordModel"
109
+ serialize :metadata, JSON
110
+
111
+ def self.table_name_prefix
112
+ "my_namespace_"
113
+ end
114
+ end
115
+ end
116
+
117
+ class CreateNamespacedARModelMigration < ActiveRecord::Migration
118
+ def change
119
+ create_table :my_namespace_my_active_record_models do |t|
120
+ t.string :current_state
121
+ t.timestamps null: false
122
+ end
123
+ end
124
+ end
125
+
126
+ class CreateNamespacedARModelTransitionMigration < ActiveRecord::Migration
127
+ def change
128
+ create_table :my_namespace_my_active_record_model_transitions do |t|
129
+ t.string :to_state
130
+ t.integer :my_active_record_model_id
131
+ t.integer :sort_key
132
+
133
+ # MySQL doesn't allow default values on text fields
134
+ if ActiveRecord::Base.connection.adapter_name == 'Mysql2'
135
+ t.text :metadata
136
+ else
137
+ t.text :metadata, default: '{}'
138
+ end
139
+
140
+ t.timestamps null: false
141
+ end
142
+
143
+ add_index :my_namespace_my_active_record_model_transitions, :sort_key,
144
+ unique: true, name: 'my_namespaced_key'
145
+ end
146
+ end
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: 1.2.1
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Marr