statecraft 0.1.0 → 0.1.1

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
  SHA256:
3
- metadata.gz: 1694416ac7a88eff03c4f73c1fab5c0b2d8bd182f7c8d88d72c4eb1da2574ecc
4
- data.tar.gz: 4fa98301ac81c1d5b2cee6007048fe05f2ea6db36400cb2d6844d8f19fef9011
3
+ metadata.gz: b9f0a6cac349045859dd7966a325ae9d0a4b27175b846e8f43bd924b298576b6
4
+ data.tar.gz: a148de67af8ddd2bb976ae5ed03db6a38122ebd795013f9aae6c5803e45f7120
5
5
  SHA512:
6
- metadata.gz: 6cc41535452dc33140e39f342c07488491b595c945538fc71f661e2233d03f2e922a52a3182e6d2022f57c2eed5cc494810126189f280fc532aae973d5eb2f4b
7
- data.tar.gz: bc485f03bcee46d53f027d955def314aa7f2836211811dba751c93b2a29eaa8887a944479d2fb62ab62f540aa0e43aca0813c95f115212488e9495f80a8b8f90
6
+ metadata.gz: a48dbba9f2e06f23bbcffc0fc632f17f6915954737c2f104c132fa8763823bb9f07a0e754dabf4eb95d14b21b97c912885d0d1efa252c7e3ce64cedeeb206a22
7
+ data.tar.gz: 8256c0463e14d6ddf19d2a757647f81bae4bfa73ecc37796f905adc82d397dbdcb730c45861d23b172711e75aff224482de7dbbdc94a07fbbc9c4cca95fd4c37
data/README.md CHANGED
@@ -349,6 +349,26 @@ degrades — the locking clause is dropped, the reload still runs, and
349
349
  statecraft warns once per machine per process that row-locking guarantees
350
350
  require PostgreSQL.
351
351
 
352
+ ## Running the tests
353
+
354
+ Natively, against SQLite:
355
+
356
+ ```sh
357
+ bundle install
358
+ bundle exec rspec
359
+ ```
360
+
361
+ Against PostgreSQL — where the concurrency proof actually runs — point
362
+ `DATABASE_URL` at a database of your own, or use the bundled containers, which
363
+ pin the same PostgreSQL major as CI and take the Ruby and ActiveRecord versions
364
+ as parameters, so any CI matrix cell reproduces locally:
365
+
366
+ ```sh
367
+ docker compose run --rm test # sqlite, default Ruby
368
+ docker compose run --rm test-postgres # PostgreSQL 16
369
+ AR_VERSION=7.2 RUBY_VERSION=3.3 docker compose run --rm test-postgres
370
+ ```
371
+
352
372
  ## License
353
373
 
354
374
  MIT. See [LICENSE.txt](LICENSE.txt).
@@ -93,13 +93,6 @@ module Statecraft
93
93
  def migration_class_name
94
94
  "Create#{class_name}StateMachine"
95
95
  end
96
-
97
- def metadata_column_type
98
- adapter = ActiveRecord::Base.connection.adapter_name
99
- adapter.match?(/postg/i) ? "jsonb" : "json"
100
- rescue ActiveRecord::ActiveRecordError
101
- "jsonb"
102
- end
103
96
  end
104
97
  end
105
98
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
4
+ def change
5
+ add_column :<%= table_name %>, :state, :string, null: false, default: "pending"
6
+ add_index :<%= table_name %>, :state
7
+ add_column :<%= table_name %>, :state_changed_at, :datetime
8
+
9
+ # The existing table may carry legacy values, so no CHECK constraint is
10
+ # generated here. To add one later without blocking writes:
11
+ # ALTER TABLE ... ADD CONSTRAINT ... CHECK (state IN (...)) NOT VALID;
12
+ # -- clean the data --
13
+ # ALTER TABLE ... VALIDATE CONSTRAINT ...;
14
+
15
+ create_table :<%= log_table_name.pluralize %> do |t|
16
+ t.references :<%= file_name %>, null: false,
17
+ foreign_key: { to_table: :<%= table_name %>, on_delete: :cascade },
18
+ index: false
19
+ t.string :from_state, null: false
20
+ t.string :to_state, null: false
21
+ t.string :event
22
+ t.column :metadata, metadata_column_type, null: false, default: {}
23
+ t.datetime :created_at, null: false
24
+ t.index %i[<%= foreign_key_column %> id]
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # jsonb where the database has it, json elsewhere: the adapter is known
31
+ # only when the migration actually runs, never at generation time.
32
+ def metadata_column_type
33
+ connection.adapter_name.match?(/postg/i) ? :jsonb : :json
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Shared home for private guard helpers used by several machines. Keep it
4
+ # free of DSL declarations (states, transitions, events): machine definitions
5
+ # are NOT inherited — each machine declares its own graph.
6
+ class ApplicationMachine
7
+ include Statecraft::Machine
8
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
4
+ def change
5
+ create_table :<%= table_name %> do |t|
6
+ t.string :state, null: false, default: "pending", index: true
7
+ t.datetime :state_changed_at
8
+ t.timestamps null: false
9
+ end
10
+
11
+ # add new states here when the machine grows
12
+ add_check_constraint :<%= table_name %>, "state IN ('pending')",
13
+ name: "<%= table_name %>_state_check"
14
+
15
+ create_table :<%= log_table_name.pluralize %> do |t|
16
+ t.references :<%= file_name %>, null: false,
17
+ foreign_key: { to_table: :<%= table_name %>, on_delete: :cascade },
18
+ index: false
19
+ t.string :from_state, null: false
20
+ t.string :to_state, null: false
21
+ t.string :event
22
+ t.column :metadata, metadata_column_type, null: false, default: {}
23
+ t.datetime :created_at, null: false
24
+ t.index %i[<%= foreign_key_column %> id]
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # jsonb where the database has it, json elsewhere: the adapter is known
31
+ # only when the migration actually runs, never at generation time.
32
+ def metadata_column_type
33
+ connection.adapter_name.match?(/postg/i) ? :jsonb : :json
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Append-only transition log for <%= class_name %>. This is a read-model:
4
+ # scopes, reading methods and serializers are yours; the transition pipeline
5
+ # writes rows through the insert path and never calls validations or
6
+ # callbacks declared here.
7
+ class <%= class_name %>Transition < <%= parent_class_name %>
8
+ def readonly? = persisted?
9
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %>Flow < ApplicationMachine
4
+ state :pending, initial: true
5
+ # state :paid
6
+ #
7
+ # event :pay, from: :pending, to: :paid, guard: :payable?
8
+ #
9
+ # Guards and callbacks resolve to instance methods of this class:
10
+ #
11
+ # private
12
+ #
13
+ # def payable?(record, metadata)
14
+ # metadata["amount"].to_i.positive?
15
+ # end
16
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= class_name %> < ApplicationRecord
4
+ state_machine <%= class_name %>Flow, changed_at: true, helpers: true, scopes: true
5
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Statecraft
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statecraft
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Pugachev
@@ -61,6 +61,12 @@ files:
61
61
  - LICENSE.txt
62
62
  - README.md
63
63
  - lib/generators/statecraft/machine/machine_generator.rb
64
+ - lib/generators/statecraft/machine/templates/add_migration.rb.tt
65
+ - lib/generators/statecraft/machine/templates/application_machine.rb.tt
66
+ - lib/generators/statecraft/machine/templates/create_migration.rb.tt
67
+ - lib/generators/statecraft/machine/templates/log_model.rb.tt
68
+ - lib/generators/statecraft/machine/templates/machine.rb.tt
69
+ - lib/generators/statecraft/machine/templates/model.rb.tt
64
70
  - lib/statecraft.rb
65
71
  - lib/statecraft/errors.rb
66
72
  - lib/statecraft/instrumentation.rb
@@ -71,9 +77,14 @@ files:
71
77
  - lib/statecraft/pipeline.rb
72
78
  - lib/statecraft/version.rb
73
79
  - lib/statecraft/warnings.rb
80
+ homepage: https://supostat.github.io/statecraft/
74
81
  licenses:
75
82
  - MIT
76
83
  metadata:
84
+ homepage_uri: https://supostat.github.io/statecraft/
85
+ source_code_uri: https://github.com/supostat/statecraft
86
+ changelog_uri: https://github.com/supostat/statecraft/releases
87
+ bug_tracker_uri: https://github.com/supostat/statecraft/issues
77
88
  rubygems_mfa_required: 'true'
78
89
  rdoc_options: []
79
90
  require_paths: