better_model 1.0.0 → 1.1.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 +4 -4
- data/README.md +317 -85
- data/lib/better_model/archivable.rb +10 -10
- data/lib/better_model/predicable.rb +21 -44
- data/lib/better_model/state_transition.rb +106 -0
- data/lib/better_model/stateable/configurator.rb +300 -0
- data/lib/better_model/stateable/errors.rb +45 -0
- data/lib/better_model/stateable/guard.rb +87 -0
- data/lib/better_model/stateable/transition.rb +143 -0
- data/lib/better_model/stateable.rb +340 -0
- data/lib/better_model/traceable.rb +446 -0
- data/lib/better_model/validatable/business_rule_validator.rb +47 -0
- data/lib/better_model/validatable/configurator.rb +245 -0
- data/lib/better_model/validatable/order_validator.rb +77 -0
- data/lib/better_model/validatable.rb +270 -0
- data/lib/better_model/version.rb +1 -1
- data/lib/better_model/version_record.rb +63 -0
- data/lib/better_model.rb +15 -2
- data/lib/generators/better_model/stateable/install_generator.rb +37 -0
- data/lib/generators/better_model/stateable/stateable_generator.rb +50 -0
- data/lib/generators/better_model/stateable/templates/README +38 -0
- data/lib/generators/better_model/stateable/templates/install_migration.rb.tt +20 -0
- data/lib/generators/better_model/stateable/templates/migration.rb.tt +9 -0
- data/lib/generators/better_model/traceable/templates/create_table_migration.rb.tt +28 -0
- data/lib/generators/better_model/traceable/traceable_generator.rb +77 -0
- metadata +22 -3
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module BetterModel
|
|
7
|
+
module Generators
|
|
8
|
+
module Stateable
|
|
9
|
+
# Generator per creare la tabella state_transitions
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# rails generate better_model:stateable:install
|
|
13
|
+
# rails generate better_model:stateable:install --table-name=order_transitions
|
|
14
|
+
#
|
|
15
|
+
class InstallGenerator < Rails::Generators::Base
|
|
16
|
+
include ActiveRecord::Generators::Migration
|
|
17
|
+
|
|
18
|
+
source_root File.expand_path("templates", __dir__)
|
|
19
|
+
|
|
20
|
+
class_option :table_name, type: :string, default: "state_transitions",
|
|
21
|
+
desc: "Custom table name for state transitions (default: state_transitions)"
|
|
22
|
+
|
|
23
|
+
desc "Creates the state_transitions table for Stateable history tracking"
|
|
24
|
+
|
|
25
|
+
def create_migration_file
|
|
26
|
+
migration_template "install_migration.rb.tt", "db/migrate/create_#{transitions_table_name}.rb"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def transitions_table_name
|
|
32
|
+
options[:table_name]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module BetterModel
|
|
7
|
+
module Generators
|
|
8
|
+
# Generator per aggiungere Stateable a un modello esistente
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# rails generate better_model:stateable MODEL_NAME [options]
|
|
12
|
+
#
|
|
13
|
+
# Examples:
|
|
14
|
+
# rails generate better_model:stateable Order
|
|
15
|
+
# rails generate better_model:stateable Article --initial-state=draft
|
|
16
|
+
#
|
|
17
|
+
class StateableGenerator < Rails::Generators::NamedBase
|
|
18
|
+
include ActiveRecord::Generators::Migration
|
|
19
|
+
|
|
20
|
+
source_root File.expand_path("templates", __dir__)
|
|
21
|
+
|
|
22
|
+
class_option :initial_state, type: :string, default: nil,
|
|
23
|
+
desc: "Initial state name (default: first state defined)"
|
|
24
|
+
|
|
25
|
+
desc "Adds Stateable support to an existing model by adding a 'state' column"
|
|
26
|
+
|
|
27
|
+
def create_migration_file
|
|
28
|
+
migration_template "migration.rb.tt", "db/migrate/add_stateable_to_#{table_name}.rb"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def show_readme
|
|
32
|
+
readme "README" if behavior == :invoke
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def table_name
|
|
38
|
+
name.tableize
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def model_name
|
|
42
|
+
name.camelize
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def initial_state_value
|
|
46
|
+
options[:initial_state] || "pending"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
===============================================================================
|
|
2
|
+
|
|
3
|
+
Stateable column has been added to your model!
|
|
4
|
+
|
|
5
|
+
Next steps:
|
|
6
|
+
|
|
7
|
+
1. Run the migration:
|
|
8
|
+
$ bin/rails db:migrate
|
|
9
|
+
|
|
10
|
+
2. Add Stateable configuration to your model:
|
|
11
|
+
|
|
12
|
+
class <%= model_name %> < ApplicationRecord
|
|
13
|
+
include BetterModel
|
|
14
|
+
|
|
15
|
+
stateable do
|
|
16
|
+
# Define states
|
|
17
|
+
state :<%= initial_state_value %>, initial: true
|
|
18
|
+
state :confirmed
|
|
19
|
+
state :completed
|
|
20
|
+
|
|
21
|
+
# Define transitions
|
|
22
|
+
transition :confirm, from: :<%= initial_state_value %>, to: :confirmed do
|
|
23
|
+
guard { valid? }
|
|
24
|
+
before { prepare_confirmation }
|
|
25
|
+
after { send_notification }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
transition :complete, from: :confirmed, to: :completed
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
3. Don't forget to create the state_transitions table (once per app):
|
|
33
|
+
$ bin/rails generate better_model:stateable:install
|
|
34
|
+
$ bin/rails db:migrate
|
|
35
|
+
|
|
36
|
+
For more information, see: docs/stateable.md
|
|
37
|
+
|
|
38
|
+
===============================================================================
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class Create<%= transitions_table_name.camelize %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= transitions_table_name %> do |t|
|
|
4
|
+
t.string :transitionable_type, null: false
|
|
5
|
+
t.integer :transitionable_id, null: false
|
|
6
|
+
t.string :event, null: false
|
|
7
|
+
t.string :from_state, null: false
|
|
8
|
+
t.string :to_state, null: false
|
|
9
|
+
t.json :metadata
|
|
10
|
+
|
|
11
|
+
t.datetime :created_at, null: false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
add_index :<%= transitions_table_name %>, [:transitionable_type, :transitionable_id], name: "index_<%= transitions_table_name %>_on_transitionable"
|
|
15
|
+
add_index :<%= transitions_table_name %>, :event
|
|
16
|
+
add_index :<%= transitions_table_name %>, :from_state
|
|
17
|
+
add_index :<%= transitions_table_name %>, :to_state
|
|
18
|
+
add_index :<%= transitions_table_name %>, :created_at
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class AddStateableTo<%= table_name.camelize %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
change_table :<%= table_name %> do |t|
|
|
4
|
+
t.string :state, null: false, default: "<%= initial_state_value %>"
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
add_index :<%= table_name %>, :state
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class Create<%= versions_table_name.camelize %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :<%= versions_table_name %> do |t|
|
|
4
|
+
# Polymorphic association to tracked models
|
|
5
|
+
t.string :item_type, null: false
|
|
6
|
+
t.integer :item_id, null: false
|
|
7
|
+
|
|
8
|
+
# Event type: created, updated, destroyed
|
|
9
|
+
t.string :event, null: false
|
|
10
|
+
|
|
11
|
+
# Changes stored as JSON (before/after values)
|
|
12
|
+
# SQLite uses json, PostgreSQL can use jsonb
|
|
13
|
+
t.json :object_changes
|
|
14
|
+
|
|
15
|
+
# Optional tracking fields
|
|
16
|
+
t.integer :updated_by_id
|
|
17
|
+
t.string :updated_reason
|
|
18
|
+
|
|
19
|
+
t.datetime :created_at, null: false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Indexes for performance
|
|
23
|
+
add_index :<%= versions_table_name %>, [ :item_type, :item_id ], name: "index_<%= versions_table_name %>_on_item"
|
|
24
|
+
add_index :<%= versions_table_name %>, :created_at
|
|
25
|
+
add_index :<%= versions_table_name %>, :updated_by_id
|
|
26
|
+
add_index :<%= versions_table_name %>, :event
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module BetterModel
|
|
6
|
+
module Generators
|
|
7
|
+
class TraceableGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
argument :name, type: :string, default: "model", desc: "Model name (optional, used for default table name)"
|
|
11
|
+
|
|
12
|
+
class_option :create_table, type: :boolean, default: false,
|
|
13
|
+
desc: "Create the versions table (only needed once per table)"
|
|
14
|
+
|
|
15
|
+
class_option :table_name, type: :string, default: nil,
|
|
16
|
+
desc: "Custom table name for versions (default: {model}_versions)"
|
|
17
|
+
|
|
18
|
+
def create_migration_file
|
|
19
|
+
if options[:create_table]
|
|
20
|
+
template "create_table_migration.rb.tt",
|
|
21
|
+
"db/migrate/#{timestamp}_create_#{versions_table_name}.rb"
|
|
22
|
+
|
|
23
|
+
say "Created migration for '#{versions_table_name}' table", :green
|
|
24
|
+
say "Run 'rails db:migrate' to create the table", :green
|
|
25
|
+
else
|
|
26
|
+
say "Traceable will use the '#{versions_table_name}' table", :yellow
|
|
27
|
+
say "If the table doesn't exist yet, run:", :yellow
|
|
28
|
+
say " rails g better_model:traceable #{name} --create-table#{table_name_option_hint}", :green
|
|
29
|
+
say " rails db:migrate", :green
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def show_usage_instructions
|
|
34
|
+
say "\nTo enable Traceable in your model:", :yellow
|
|
35
|
+
say " class #{class_name} < ApplicationRecord", :white
|
|
36
|
+
say " include BetterModel", :white
|
|
37
|
+
say " ", :white
|
|
38
|
+
say " traceable do", :white
|
|
39
|
+
say " track :field1, :field2, :field3", :white
|
|
40
|
+
if custom_table_name?
|
|
41
|
+
say " table_name '#{versions_table_name}'", :white
|
|
42
|
+
else
|
|
43
|
+
say " # table_name '#{versions_table_name}' (default)", :white
|
|
44
|
+
end
|
|
45
|
+
say " end", :white
|
|
46
|
+
say " end", :white
|
|
47
|
+
say "\nSee documentation for more options and usage examples", :yellow
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def timestamp
|
|
53
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def class_name
|
|
57
|
+
name.camelize
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def model_name
|
|
61
|
+
name.underscore
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def versions_table_name
|
|
65
|
+
@versions_table_name ||= options[:table_name] || "#{model_name}_versions"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def custom_table_name?
|
|
69
|
+
options[:table_name].present?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def table_name_option_hint
|
|
73
|
+
custom_table_name? ? " --table-name=#{versions_table_name}" : ""
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: better_model
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- alessiobussolari
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-10-
|
|
11
|
+
date: 2025-10-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -33,7 +33,7 @@ dependencies:
|
|
|
33
33
|
description: BetterModel is a Rails engine gem (Rails 8.1+) that provides powerful
|
|
34
34
|
extensions for ActiveRecord models including declarative status management and more.
|
|
35
35
|
email:
|
|
36
|
-
- alessio@
|
|
36
|
+
- alessio.bussolari@pandev.it
|
|
37
37
|
executables: []
|
|
38
38
|
extensions: []
|
|
39
39
|
extra_rdoc_files: []
|
|
@@ -48,10 +48,29 @@ files:
|
|
|
48
48
|
- lib/better_model/railtie.rb
|
|
49
49
|
- lib/better_model/searchable.rb
|
|
50
50
|
- lib/better_model/sortable.rb
|
|
51
|
+
- lib/better_model/state_transition.rb
|
|
52
|
+
- lib/better_model/stateable.rb
|
|
53
|
+
- lib/better_model/stateable/configurator.rb
|
|
54
|
+
- lib/better_model/stateable/errors.rb
|
|
55
|
+
- lib/better_model/stateable/guard.rb
|
|
56
|
+
- lib/better_model/stateable/transition.rb
|
|
51
57
|
- lib/better_model/statusable.rb
|
|
58
|
+
- lib/better_model/traceable.rb
|
|
59
|
+
- lib/better_model/validatable.rb
|
|
60
|
+
- lib/better_model/validatable/business_rule_validator.rb
|
|
61
|
+
- lib/better_model/validatable/configurator.rb
|
|
62
|
+
- lib/better_model/validatable/order_validator.rb
|
|
52
63
|
- lib/better_model/version.rb
|
|
64
|
+
- lib/better_model/version_record.rb
|
|
53
65
|
- lib/generators/better_model/archivable/archivable_generator.rb
|
|
54
66
|
- lib/generators/better_model/archivable/templates/migration.rb.tt
|
|
67
|
+
- lib/generators/better_model/stateable/install_generator.rb
|
|
68
|
+
- lib/generators/better_model/stateable/stateable_generator.rb
|
|
69
|
+
- lib/generators/better_model/stateable/templates/README
|
|
70
|
+
- lib/generators/better_model/stateable/templates/install_migration.rb.tt
|
|
71
|
+
- lib/generators/better_model/stateable/templates/migration.rb.tt
|
|
72
|
+
- lib/generators/better_model/traceable/templates/create_table_migration.rb.tt
|
|
73
|
+
- lib/generators/better_model/traceable/traceable_generator.rb
|
|
55
74
|
- lib/tasks/better_model_tasks.rake
|
|
56
75
|
homepage: https://github.com/alessiobussolari/better_model
|
|
57
76
|
licenses:
|