dynamic-active-model 0.6.1 → 0.6.4
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/lib/dynamic-active-model/associations.rb +105 -9
- data/lib/dynamic-active-model/dangerous_attributes_patch.rb +22 -2
- data/lib/dynamic-active-model/database.rb +76 -6
- data/lib/dynamic-active-model/explorer.rb +36 -1
- data/lib/dynamic-active-model/factory.rb +36 -3
- data/lib/dynamic-active-model/foreign_key.rb +32 -5
- data/lib/dynamic-active-model/setup.rb +54 -2
- data/lib/dynamic-active-model/template_class_file.rb +115 -3
- data/lib/dynamic-active-model.rb +35 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14a04d1dd0784f4300e46dd358b5ad392cac0f1244b6a7520465df06ae2bb84a
|
4
|
+
data.tar.gz: ad0cf1bda6db17ee7354d64c24dc42cd9330f8404b1b98c10a15f87decde094d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8000cf349d155abaf9feec697a440e3a9d1db4112cb2257005900b712774a6d05b5c41f5f68f8ca2a05443385e202b62ea5ffa021c92a47e6038ad7b65213107
|
7
|
+
data.tar.gz: 7adf72224a086374d1b54034d6f3ada3fb05537acc3d031dfdaf701658dd5cc05121f232f4be80075070db54750f48ad714d18e0b2fd9f0ff2d8aab7bae77bd2
|
@@ -1,26 +1,61 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DynamicActiveModel
|
4
|
-
#
|
5
|
-
#
|
4
|
+
# The Associations class is responsible for automatically detecting and setting up
|
5
|
+
# ActiveRecord relationships between models based on database schema analysis.
|
6
|
+
# It supports the following relationship types:
|
7
|
+
# - belongs_to
|
8
|
+
# - has_many
|
9
|
+
# - has_one (automatically detected from unique constraints)
|
10
|
+
# - has_and_belongs_to_many (automatically detected from join tables)
|
11
|
+
#
|
12
|
+
# @example Basic Usage
|
13
|
+
# db = DynamicActiveModel::Database.new(DB, database_config)
|
14
|
+
# db.create_models!
|
15
|
+
# associations = DynamicActiveModel::Associations.new(db)
|
16
|
+
# associations.build!
|
17
|
+
#
|
18
|
+
# @example Custom Foreign Key
|
19
|
+
# associations.add_foreign_key('users', 'manager_id', 'manager')
|
6
20
|
class Associations
|
7
|
-
|
8
|
-
|
9
|
-
|
21
|
+
# @return [Database] The database instance containing the models
|
22
|
+
attr_reader :database
|
23
|
+
|
24
|
+
# @return [Hash] Mapping of table names to their indexes
|
25
|
+
attr_reader :table_indexes
|
26
|
+
|
27
|
+
# @return [Array] List of detected join tables
|
28
|
+
attr_reader :join_tables
|
29
|
+
|
30
|
+
# Initializes a new Associations instance
|
31
|
+
# @param database [Database] The database instance containing the models
|
10
32
|
def initialize(database)
|
11
33
|
@database = database
|
12
34
|
@table_indexes = {}
|
13
|
-
@
|
14
|
-
|
35
|
+
@join_tables = []
|
36
|
+
@foreign_keys = {}
|
37
|
+
database.models.each do |model|
|
38
|
+
@foreign_keys[model.table_name] = ForeignKey.new(model)
|
15
39
|
@table_indexes[model.table_name] = model.connection.indexes(model.table_name)
|
40
|
+
@join_tables << model if join_table?(model)
|
16
41
|
end
|
17
42
|
end
|
18
43
|
|
44
|
+
# Adds a custom foreign key relationship
|
45
|
+
# @param table_name [String] Name of the table with the foreign key
|
46
|
+
# @param foreign_key [String] Name of the foreign key column
|
47
|
+
# @param relationship_name [String, nil] Custom name for the relationship
|
19
48
|
def add_foreign_key(table_name, foreign_key, relationship_name = nil)
|
20
49
|
@foreign_keys[table_name].add(foreign_key, relationship_name)
|
21
50
|
end
|
22
51
|
|
23
|
-
#
|
52
|
+
# Builds all relationships between models based on foreign keys and constraints
|
53
|
+
# This method:
|
54
|
+
# 1. Maps foreign keys to their corresponding models
|
55
|
+
# 2. Adds belongs_to relationships
|
56
|
+
# 3. Adds has_many or has_one relationships based on unique constraints
|
57
|
+
# 4. Sets up has_and_belongs_to_many relationships for join tables
|
58
|
+
# @return [void]
|
24
59
|
def build!
|
25
60
|
foreign_key_to_models = create_foreign_key_to_model_map
|
26
61
|
|
@@ -35,10 +70,31 @@ module DynamicActiveModel
|
|
35
70
|
end
|
36
71
|
end
|
37
72
|
end
|
73
|
+
|
74
|
+
@join_tables.each do |join_table_model|
|
75
|
+
models = join_table_model.column_names.map { |column_name| foreign_key_to_models[column_name.downcase]&.first&.first }.compact
|
76
|
+
if models.size == 2
|
77
|
+
add_has_and_belongs_to_many(join_table_model, models)
|
78
|
+
end
|
79
|
+
end
|
38
80
|
end
|
39
81
|
|
40
82
|
private
|
41
83
|
|
84
|
+
# Adds has_and_belongs_to_many relationships between two models
|
85
|
+
# @param join_table_model [Class] The join table model
|
86
|
+
# @param models [Array<Class>] The two models to be related
|
87
|
+
def add_has_and_belongs_to_many(join_table_model, models)
|
88
|
+
model1, model2 = *models
|
89
|
+
model1.has_and_belongs_to_many model2.table_name.pluralize.to_sym, join_table: join_table_model.table_name, class_name: model2.name
|
90
|
+
model2.has_and_belongs_to_many model1.table_name.pluralize.to_sym, join_table: join_table_model.table_name, class_name: model1.name
|
91
|
+
end
|
92
|
+
|
93
|
+
# Adds appropriate relationships between two models
|
94
|
+
# @param relationship_name [String] Name of the relationship
|
95
|
+
# @param model [Class] The model with the foreign key
|
96
|
+
# @param belongs_to_model [Class] The model being referenced
|
97
|
+
# @param foreign_key [String] The foreign key column name
|
42
98
|
def add_relationships(relationship_name, model, belongs_to_model, foreign_key)
|
43
99
|
add_belongs_to(relationship_name, model, belongs_to_model, foreign_key)
|
44
100
|
if unique_index?(model, foreign_key)
|
@@ -48,6 +104,11 @@ module DynamicActiveModel
|
|
48
104
|
end
|
49
105
|
end
|
50
106
|
|
107
|
+
# Adds a belongs_to relationship to a model
|
108
|
+
# @param relationship_name [String] Name of the relationship
|
109
|
+
# @param model [Class] The model with the foreign key
|
110
|
+
# @param belongs_to_model [Class] The model being referenced
|
111
|
+
# @param foreign_key [String] The foreign key column name
|
51
112
|
def add_belongs_to(relationship_name, model, belongs_to_model, foreign_key)
|
52
113
|
model.belongs_to(
|
53
114
|
relationship_name.singularize.to_sym,
|
@@ -57,6 +118,11 @@ module DynamicActiveModel
|
|
57
118
|
)
|
58
119
|
end
|
59
120
|
|
121
|
+
# Adds a has_many relationship to a model
|
122
|
+
# @param relationship_name [String] Name of the relationship
|
123
|
+
# @param model [Class] The model with the foreign key
|
124
|
+
# @param has_many_model [Class] The model being referenced
|
125
|
+
# @param foreign_key [String] The foreign key column name
|
60
126
|
def add_has_many(relationship_name, model, has_many_model, foreign_key)
|
61
127
|
model.has_many(
|
62
128
|
generate_has_many_association_name(relationship_name, model, has_many_model),
|
@@ -66,6 +132,11 @@ module DynamicActiveModel
|
|
66
132
|
)
|
67
133
|
end
|
68
134
|
|
135
|
+
# Adds a has_one relationship to a model
|
136
|
+
# @param relationship_name [String] Name of the relationship
|
137
|
+
# @param model [Class] The model with the foreign key
|
138
|
+
# @param has_one_model [Class] The model being referenced
|
139
|
+
# @param foreign_key [String] The foreign key column name
|
69
140
|
def add_has_one(relationship_name, model, has_one_model, foreign_key)
|
70
141
|
model.has_one(
|
71
142
|
generate_has_one_association_name(relationship_name, model, has_one_model),
|
@@ -75,6 +146,8 @@ module DynamicActiveModel
|
|
75
146
|
)
|
76
147
|
end
|
77
148
|
|
149
|
+
# Creates a mapping of foreign key column names to their corresponding models
|
150
|
+
# @return [Hash] Mapping of foreign key names to model and relationship name pairs
|
78
151
|
def create_foreign_key_to_model_map
|
79
152
|
@foreign_keys.values.each_with_object({}) do |foreign_key, hsh|
|
80
153
|
foreign_key.keys.each do |key, relationship_name|
|
@@ -84,16 +157,26 @@ module DynamicActiveModel
|
|
84
157
|
end
|
85
158
|
end
|
86
159
|
|
160
|
+
# Generates an appropriate name for a has_many association
|
161
|
+
# @param relationship_name [String] Original relationship name
|
162
|
+
# @param model [Class] The model with the foreign key
|
163
|
+
# @param has_many_model [Class] The model being referenced
|
164
|
+
# @return [Symbol] The generated association name
|
87
165
|
def generate_has_many_association_name(relationship_name, model, has_many_model)
|
88
166
|
name =
|
89
167
|
if relationship_name == model.table_name.underscore
|
90
168
|
has_many_model.table_name
|
91
169
|
else
|
92
|
-
relationship_name
|
170
|
+
"#{relationship_name}_#{has_many_model.table_name}"
|
93
171
|
end
|
94
172
|
name.underscore.pluralize.to_sym
|
95
173
|
end
|
96
174
|
|
175
|
+
# Generates an appropriate name for a has_one association
|
176
|
+
# @param relationship_name [String] Original relationship name
|
177
|
+
# @param model [Class] The model with the foreign key
|
178
|
+
# @param has_one_model [Class] The model being referenced
|
179
|
+
# @return [Symbol] The generated association name
|
97
180
|
def generate_has_one_association_name(relationship_name, model, has_one_model)
|
98
181
|
name =
|
99
182
|
if relationship_name == model.table_name.underscore
|
@@ -104,6 +187,10 @@ module DynamicActiveModel
|
|
104
187
|
name.underscore.singularize.to_sym
|
105
188
|
end
|
106
189
|
|
190
|
+
# Checks if a foreign key column has a unique index
|
191
|
+
# @param model [Class] The model to check
|
192
|
+
# @param foreign_key [String] The foreign key column name
|
193
|
+
# @return [Boolean] Whether the foreign key has a unique index
|
107
194
|
def unique_index?(model, foreign_key)
|
108
195
|
indexes = table_indexes[model.table_name]
|
109
196
|
indexes.any? do |index|
|
@@ -112,5 +199,14 @@ module DynamicActiveModel
|
|
112
199
|
index.columns.first == foreign_key
|
113
200
|
end
|
114
201
|
end
|
202
|
+
|
203
|
+
# Detects if a model represents a join table for has_and_belongs_to_many
|
204
|
+
# @param model [Class] The model to check
|
205
|
+
# @return [Boolean] Whether the model is a join table
|
206
|
+
def join_table?(model)
|
207
|
+
model.primary_key.nil? &&
|
208
|
+
model.columns.size == 2 &&
|
209
|
+
model.columns.all? { |column| column.name =~ /#{ForeignKey.id_suffix}$/ }
|
210
|
+
end
|
115
211
|
end
|
116
212
|
end
|
@@ -1,9 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DynamicActiveModel
|
4
|
-
#
|
5
|
-
#
|
4
|
+
# The DangerousAttributesPatch module is a safety feature that prevents conflicts
|
5
|
+
# between database column names and Ruby reserved words or ActiveRecord methods.
|
6
|
+
# It automatically detects and ignores columns that could cause conflicts,
|
7
|
+
# particularly focusing on boolean columns that might conflict with Ruby's
|
8
|
+
# question mark methods.
|
9
|
+
#
|
10
|
+
# @example Basic Usage
|
11
|
+
# class User < ActiveRecord::Base
|
12
|
+
# include DynamicActiveModel::DangerousAttributesPatch
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @example With Boolean Column
|
16
|
+
# # If a table has a boolean column named 'class',
|
17
|
+
# # it will be automatically ignored to prevent conflicts
|
18
|
+
# # with Ruby's Object#class method
|
6
19
|
module DangerousAttributesPatch
|
20
|
+
# Extends the including class with dangerous attribute protection
|
21
|
+
# This method:
|
22
|
+
# 1. Checks if the class has any attributes
|
23
|
+
# 2. Identifies columns that could cause conflicts
|
24
|
+
# 3. Adds those columns to the ignored_columns list
|
25
|
+
#
|
26
|
+
# @param base [Class] The ActiveRecord model class
|
7
27
|
def self.included(base)
|
8
28
|
return unless base.attribute_names
|
9
29
|
|
@@ -1,19 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DynamicActiveModel
|
4
|
-
#
|
5
|
-
#
|
4
|
+
# The Database class is responsible for connecting to a database and creating
|
5
|
+
# ActiveRecord models from its tables. It provides functionality for:
|
6
|
+
# - Table filtering (blacklist/whitelist)
|
7
|
+
# - Model creation and management
|
8
|
+
# - Model updates and extensions
|
9
|
+
#
|
10
|
+
# @example Basic Usage
|
11
|
+
# db = DynamicActiveModel::Database.new(DB, database_config)
|
12
|
+
# db.create_models!
|
13
|
+
#
|
14
|
+
# @example Table Filtering
|
15
|
+
# db.skip_table 'temporary_data'
|
16
|
+
# db.include_table 'users'
|
17
|
+
# db.create_models!
|
18
|
+
#
|
19
|
+
# @example Model Updates
|
20
|
+
# db.update_model(:users) do
|
21
|
+
# def full_name
|
22
|
+
# "#{first_name} #{last_name}"
|
23
|
+
# end
|
24
|
+
# end
|
6
25
|
class Database
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
26
|
+
# @return [Hash] Mapping of table names to custom class names
|
27
|
+
attr_reader :table_class_names
|
28
|
+
|
29
|
+
# @return [Factory] Factory instance used for model creation
|
30
|
+
attr_reader :factory
|
31
|
+
|
32
|
+
# @return [Array] List of created model classes
|
33
|
+
attr_reader :models
|
34
|
+
|
35
|
+
# Helper class for updating model definitions
|
11
36
|
ModelUpdater = Struct.new(:model) do
|
37
|
+
# Updates a model's definition with the provided block
|
38
|
+
# @param block [Proc] Code to evaluate in the model's context
|
12
39
|
def update_model(&block)
|
13
40
|
model.class_eval(&block)
|
14
41
|
end
|
15
42
|
end
|
16
43
|
|
44
|
+
# Initializes a new Database instance
|
45
|
+
# @param base_module [Module] The namespace for created models
|
46
|
+
# @param connection_options [Hash] Database connection options
|
47
|
+
# @param base_class_name [String, nil] Optional base class name for models
|
17
48
|
def initialize(base_module, connection_options, base_class_name = nil)
|
18
49
|
@factory = Factory.new(base_module, connection_options, base_class_name)
|
19
50
|
@table_class_names = {}
|
@@ -24,6 +55,8 @@ module DynamicActiveModel
|
|
24
55
|
@models = []
|
25
56
|
end
|
26
57
|
|
58
|
+
# Adds a table to the blacklist
|
59
|
+
# @param table [String, Regexp] Table name or pattern to skip
|
27
60
|
def skip_table(table)
|
28
61
|
if table.is_a?(Regexp)
|
29
62
|
@skip_table_matchers << table
|
@@ -32,10 +65,14 @@ module DynamicActiveModel
|
|
32
65
|
end
|
33
66
|
end
|
34
67
|
|
68
|
+
# Adds multiple tables to the blacklist
|
69
|
+
# @param tables [Array<String, Regexp>] Table names or patterns to skip
|
35
70
|
def skip_tables(tables)
|
36
71
|
tables.each { |table| skip_table(table) }
|
37
72
|
end
|
38
73
|
|
74
|
+
# Adds a table to the whitelist
|
75
|
+
# @param table [String, Regexp] Table name or pattern to include
|
39
76
|
def include_table(table)
|
40
77
|
if table.is_a?(Regexp)
|
41
78
|
@include_table_matchers << table
|
@@ -44,14 +81,21 @@ module DynamicActiveModel
|
|
44
81
|
end
|
45
82
|
end
|
46
83
|
|
84
|
+
# Adds multiple tables to the whitelist
|
85
|
+
# @param tables [Array<String, Regexp>] Table names or patterns to include
|
47
86
|
def include_tables(tables)
|
48
87
|
tables.each { |table| include_table(table) }
|
49
88
|
end
|
50
89
|
|
90
|
+
# Sets a custom class name for a table
|
91
|
+
# @param table_name [String] Name of the table
|
92
|
+
# @param class_name [String] Custom class name to use
|
51
93
|
def table_class_name(table_name, class_name)
|
52
94
|
@table_class_names[table_name.to_s] = class_name
|
53
95
|
end
|
54
96
|
|
97
|
+
# Creates ActiveRecord models for all included tables
|
98
|
+
# @return [Array] List of created model classes
|
55
99
|
def create_models!
|
56
100
|
@factory.base_class.connection.tables.each do |table_name|
|
57
101
|
next if skip_table?(table_name)
|
@@ -61,14 +105,18 @@ module DynamicActiveModel
|
|
61
105
|
end
|
62
106
|
end
|
63
107
|
|
108
|
+
# @return [Array] List of all skipped tables and patterns
|
64
109
|
def skipped_tables
|
65
110
|
@skip_tables + @skip_table_matchers
|
66
111
|
end
|
67
112
|
|
113
|
+
# @return [Array] List of all included tables and patterns
|
68
114
|
def included_tables
|
69
115
|
@include_tables + @include_table_matchers
|
70
116
|
end
|
71
117
|
|
118
|
+
# Disables Single Table Inheritance (STI) for all models
|
119
|
+
# @return [void]
|
72
120
|
def disable_standard_table_inheritance!
|
73
121
|
models.each do |model|
|
74
122
|
model.inheritance_column = :_type_disabled if model.attribute_names.include?('type')
|
@@ -76,11 +124,18 @@ module DynamicActiveModel
|
|
76
124
|
end
|
77
125
|
alias disable_sti! disable_standard_table_inheritance!
|
78
126
|
|
127
|
+
# Finds a model by table name
|
128
|
+
# @param table_name [String] Name of the table
|
129
|
+
# @return [Class, nil] The model class or nil if not found
|
79
130
|
def get_model(table_name)
|
80
131
|
table_name = table_name.to_s
|
81
132
|
models.detect { |model| model.table_name == table_name }
|
82
133
|
end
|
83
134
|
|
135
|
+
# Finds a model by table name, raising an error if not found
|
136
|
+
# @param table_name [String] Name of the table
|
137
|
+
# @return [Class] The model class
|
138
|
+
# @raise [ModelNotFound] If no model is found for the table
|
84
139
|
def get_model!(table_name)
|
85
140
|
model = get_model(table_name)
|
86
141
|
return model if model
|
@@ -88,6 +143,11 @@ module DynamicActiveModel
|
|
88
143
|
raise ::DynamicActiveModel::ModelNotFound, "no model found for table #{table_name}"
|
89
144
|
end
|
90
145
|
|
146
|
+
# Updates a model's definition
|
147
|
+
# @param table_name [String] Name of the table
|
148
|
+
# @param file [String, nil] Path to a file containing model updates
|
149
|
+
# @param block [Proc] Code to evaluate in the model's context
|
150
|
+
# @return [Class] The updated model class
|
91
151
|
def update_model(table_name, file = nil, &block)
|
92
152
|
model = get_model!(table_name)
|
93
153
|
ModelUpdater.new(model).instance_eval(File.read(file)) if file
|
@@ -95,6 +155,10 @@ module DynamicActiveModel
|
|
95
155
|
model
|
96
156
|
end
|
97
157
|
|
158
|
+
# Updates all models using extension files from a directory
|
159
|
+
# @param base_dir [String] Directory containing extension files
|
160
|
+
# @param ext [String] Extension for model update files
|
161
|
+
# @return [void]
|
98
162
|
def update_all_models(base_dir, ext = '.ext.rb')
|
99
163
|
Dir.glob("#{base_dir}/*#{ext}") do |file|
|
100
164
|
next unless File.file?(file)
|
@@ -106,11 +170,17 @@ module DynamicActiveModel
|
|
106
170
|
|
107
171
|
private
|
108
172
|
|
173
|
+
# Checks if a table should be skipped
|
174
|
+
# @param table_name [String] Name of the table
|
175
|
+
# @return [Boolean] Whether the table should be skipped
|
109
176
|
def skip_table?(table_name)
|
110
177
|
@skip_tables.include?(table_name.to_s) ||
|
111
178
|
@skip_table_matchers.any? { |r| r.match(table_name) }
|
112
179
|
end
|
113
180
|
|
181
|
+
# Checks if a table should be included
|
182
|
+
# @param table_name [String] Name of the table
|
183
|
+
# @return [Boolean] Whether the table should be included
|
114
184
|
def include_table?(table_name)
|
115
185
|
(@include_tables.empty? && @include_table_matchers.empty?) ||
|
116
186
|
@include_tables.include?(table_name) ||
|
@@ -1,14 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DynamicActiveModel
|
4
|
-
#
|
4
|
+
# The Explorer module provides a high-level interface for automatically discovering
|
5
|
+
# and setting up ActiveRecord models and their relationships from a database schema.
|
6
|
+
# It combines the functionality of Database and Associations classes into a simple
|
7
|
+
# one-call interface.
|
8
|
+
#
|
9
|
+
# @example Basic Usage
|
10
|
+
# module DB; end
|
11
|
+
# DynamicActiveModel::Explorer.explore(DB, database_config)
|
12
|
+
#
|
13
|
+
# @example With Table Filtering
|
14
|
+
# skip_tables = ['temporary_data', 'audit_logs']
|
15
|
+
# DynamicActiveModel::Explorer.explore(DB, database_config, skip_tables)
|
16
|
+
#
|
17
|
+
# @example With Custom Relationships
|
18
|
+
# relationships = {
|
19
|
+
# 'users' => {
|
20
|
+
# 'manager_id' => 'manager',
|
21
|
+
# 'department_id' => 'department'
|
22
|
+
# }
|
23
|
+
# }
|
24
|
+
# DynamicActiveModel::Explorer.explore(DB, database_config, [], relationships)
|
5
25
|
module Explorer
|
26
|
+
# Creates models and sets up relationships in a single call
|
27
|
+
# @param base_module [Module] The namespace for created models
|
28
|
+
# @param connection_options [Hash] Database connection options
|
29
|
+
# @param skip_tables [Array<String, Regexp>] Tables to exclude from model creation
|
30
|
+
# @param relationships [Hash] Custom foreign key relationships to add
|
31
|
+
# @return [Database] The configured database instance
|
6
32
|
def self.explore(base_module, connection_options, skip_tables = [], relationships = {})
|
7
33
|
database = create_models!(base_module, connection_options, skip_tables)
|
8
34
|
build_relationships!(database, relationships)
|
9
35
|
database
|
10
36
|
end
|
11
37
|
|
38
|
+
# Creates ActiveRecord models from database tables
|
39
|
+
# @param base_module [Module] The namespace for created models
|
40
|
+
# @param connection_options [Hash] Database connection options
|
41
|
+
# @param skip_tables [Array<String, Regexp>] Tables to exclude from model creation
|
42
|
+
# @return [Database] The configured database instance
|
12
43
|
def self.create_models!(base_module, connection_options, skip_tables)
|
13
44
|
database = Database.new(base_module, connection_options)
|
14
45
|
skip_tables.each do |table_name|
|
@@ -19,6 +50,10 @@ module DynamicActiveModel
|
|
19
50
|
database
|
20
51
|
end
|
21
52
|
|
53
|
+
# Sets up relationships between created models
|
54
|
+
# @param database [Database] The database instance containing the models
|
55
|
+
# @param relationships [Hash] Custom foreign key relationships to add
|
56
|
+
# @return [void]
|
22
57
|
def self.build_relationships!(database, relationships)
|
23
58
|
relations = Associations.new(database)
|
24
59
|
relationships.each do |table_name, foreign_keys|
|
@@ -1,22 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DynamicActiveModel
|
4
|
-
#
|
4
|
+
# The Factory class is responsible for creating ActiveRecord model classes
|
5
|
+
# from database tables. It handles:
|
6
|
+
# - Base class creation and connection setup
|
7
|
+
# - Model class generation with proper naming
|
8
|
+
# - Table name assignment
|
9
|
+
# - Safety patches for dangerous attributes
|
10
|
+
#
|
11
|
+
# @example Basic Usage
|
12
|
+
# factory = DynamicActiveModel::Factory.new(DB, database_config)
|
13
|
+
# model = factory.create('users')
|
14
|
+
#
|
15
|
+
# @example With Custom Class Name
|
16
|
+
# factory = DynamicActiveModel::Factory.new(DB, database_config)
|
17
|
+
# model = factory.create('users', 'CustomUser')
|
5
18
|
class Factory
|
19
|
+
# @return [Class] The base class for all generated models
|
6
20
|
attr_writer :base_class
|
7
21
|
|
22
|
+
# Initializes a new Factory instance
|
23
|
+
# @param base_module [Module] The namespace for created models
|
24
|
+
# @param connection_options [Hash] Database connection options
|
25
|
+
# @param base_class_name [Symbol, nil] Optional name for the base class
|
8
26
|
def initialize(base_module, connection_options, base_class_name = nil)
|
9
27
|
@base_module = base_module
|
10
28
|
@connection_options = connection_options
|
11
29
|
@base_class_name = base_class_name || :DynamicAbstractBase
|
12
30
|
end
|
13
31
|
|
32
|
+
# Creates a new model class for a table if it doesn't exist
|
33
|
+
# @param table_name [String] Name of the database table
|
34
|
+
# @param class_name [String, nil] Optional custom class name
|
35
|
+
# @return [Class] The model class
|
14
36
|
def create(table_name, class_name = nil)
|
15
37
|
class_name ||= generate_class_name(table_name)
|
16
38
|
create!(table_name, class_name) unless @base_module.const_defined?(class_name)
|
17
39
|
@base_module.const_get(class_name)
|
18
40
|
end
|
19
41
|
|
42
|
+
# Creates a new model class for a table, overwriting if it exists
|
43
|
+
# @param table_name [String] Name of the database table
|
44
|
+
# @param class_name [String] Name for the model class
|
45
|
+
# @return [Class] The model class
|
20
46
|
def create!(table_name, class_name)
|
21
47
|
kls = Class.new(base_class) do
|
22
48
|
self.table_name = table_name
|
@@ -26,7 +52,12 @@ module DynamicActiveModel
|
|
26
52
|
@base_module.const_get(class_name)
|
27
53
|
end
|
28
54
|
|
29
|
-
#
|
55
|
+
# Gets or creates the base class for all models
|
56
|
+
# This method:
|
57
|
+
# 1. Creates an abstract ActiveRecord::Base subclass if needed
|
58
|
+
# 2. Establishes the database connection
|
59
|
+
# 3. Returns the configured base class
|
60
|
+
# @return [Class] The base class for all models
|
30
61
|
def base_class
|
31
62
|
@base_class ||=
|
32
63
|
begin
|
@@ -44,8 +75,10 @@ module DynamicActiveModel
|
|
44
75
|
end
|
45
76
|
end
|
46
77
|
end
|
47
|
-
# rubocop:enable Metrics/MethodLength
|
48
78
|
|
79
|
+
# Generates a valid Ruby class name from a table name
|
80
|
+
# @param table_name [String] Name of the database table
|
81
|
+
# @return [String] A valid Ruby class name
|
49
82
|
def generate_class_name(table_name)
|
50
83
|
class_name = table_name.classify
|
51
84
|
return "N#{class_name}" if class_name =~ /\A\d/
|
@@ -1,33 +1,60 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DynamicActiveModel
|
4
|
-
#
|
4
|
+
# The ForeignKey class manages foreign key relationships for a model.
|
5
|
+
# It provides functionality for:
|
6
|
+
# - Tracking foreign key columns
|
7
|
+
# - Generating standard foreign key names
|
8
|
+
# - Managing custom relationship names
|
9
|
+
# - Configuring the foreign key suffix
|
10
|
+
#
|
11
|
+
# @example Basic Usage
|
12
|
+
# model = DB::User
|
13
|
+
# fk = DynamicActiveModel::ForeignKey.new(model)
|
14
|
+
# fk.add('manager_id', 'manager')
|
15
|
+
#
|
16
|
+
# @example Custom Suffix
|
17
|
+
# DynamicActiveModel::ForeignKey.id_suffix = '_ref'
|
5
18
|
class ForeignKey
|
6
|
-
|
7
|
-
|
19
|
+
# @return [Class] The model this foreign key belongs to
|
20
|
+
attr_reader :model
|
21
|
+
|
22
|
+
# @return [Hash] Mapping of foreign key columns to relationship names
|
23
|
+
attr_reader :keys
|
8
24
|
|
25
|
+
# Default suffix used for foreign key columns
|
9
26
|
DEFAULT_ID_SUFFIX = '_id'
|
10
27
|
|
28
|
+
# Gets the current foreign key suffix
|
29
|
+
# @return [String] The suffix used for foreign key columns
|
11
30
|
def self.id_suffix
|
12
31
|
@id_suffix || DEFAULT_ID_SUFFIX
|
13
32
|
end
|
14
33
|
|
15
|
-
#
|
34
|
+
# Sets a custom foreign key suffix
|
35
|
+
# @param val [String] The new suffix to use
|
16
36
|
def self.id_suffix=(val)
|
17
37
|
@id_suffix = val
|
18
38
|
end
|
19
|
-
# rubocop:enable Style/TrivialAccessors
|
20
39
|
|
40
|
+
# Initializes a new ForeignKey instance
|
41
|
+
# @param model [Class] The model to track foreign keys for
|
21
42
|
def initialize(model)
|
22
43
|
@model = model
|
23
44
|
@keys = {}
|
24
45
|
add(generate_foreign_key(model.table_name))
|
25
46
|
end
|
26
47
|
|
48
|
+
# Adds a foreign key to track
|
49
|
+
# @param key [String] The foreign key column name
|
50
|
+
# @param relationship_name [String, nil] Optional custom name for the relationship
|
27
51
|
def add(key, relationship_name = nil)
|
28
52
|
@keys[key] = relationship_name || model.table_name.underscore
|
29
53
|
end
|
30
54
|
|
55
|
+
# Generates a standard foreign key name from a table name
|
56
|
+
# @param table_name [String] The name of the referenced table
|
57
|
+
# @return [String] The generated foreign key column name
|
31
58
|
def generate_foreign_key(table_name)
|
32
59
|
table_name.underscore.singularize + self.class.id_suffix
|
33
60
|
end
|
@@ -3,19 +3,45 @@
|
|
3
3
|
require 'inheritance-helper'
|
4
4
|
|
5
5
|
module DynamicActiveModel
|
6
|
-
#
|
6
|
+
# The Setup module provides configuration and initialization methods for
|
7
|
+
# DynamicActiveModel. It allows you to:
|
8
|
+
# - Configure database connections
|
9
|
+
# - Specify tables to skip
|
10
|
+
# - Define custom relationships
|
11
|
+
# - Set up model extensions
|
12
|
+
#
|
13
|
+
# @example Basic Usage
|
14
|
+
# module DB
|
15
|
+
# include DynamicActiveModel::Setup
|
16
|
+
# connection_options database_config
|
17
|
+
# skip_tables ['temporary_data']
|
18
|
+
# create_models!
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @example With Custom Relationships
|
22
|
+
# module DB
|
23
|
+
# include DynamicActiveModel::Setup
|
24
|
+
# foreign_key 'users', 'manager_id', 'manager'
|
25
|
+
# create_models!
|
26
|
+
# end
|
7
27
|
module Setup
|
28
|
+
# Extends the including module with configuration methods
|
29
|
+
# @param base [Module] The module including this module
|
8
30
|
def self.included(base)
|
9
31
|
base.extend InheritanceHelper::Methods
|
10
32
|
base.extend ClassMethods
|
11
33
|
end
|
12
34
|
|
13
|
-
# ClassMethods various class methods for configuring a module
|
35
|
+
# ClassMethods provides various class methods for configuring a module
|
14
36
|
module ClassMethods
|
37
|
+
# Gets the database instance
|
38
|
+
# @return [Database, nil] The configured database instance
|
15
39
|
def database
|
16
40
|
nil
|
17
41
|
end
|
18
42
|
|
43
|
+
# Gets the current configuration
|
44
|
+
# @return [Hash] The configuration hash with default values
|
19
45
|
def dynamic_active_model_config
|
20
46
|
{
|
21
47
|
connection_options: nil,
|
@@ -26,6 +52,9 @@ module DynamicActiveModel
|
|
26
52
|
}
|
27
53
|
end
|
28
54
|
|
55
|
+
# Sets or gets the database connection options
|
56
|
+
# @param options [Hash, String, nil] Database configuration or named configuration
|
57
|
+
# @return [Hash] The current connection options
|
29
58
|
def connection_options(options = nil)
|
30
59
|
if options.is_a?(String)
|
31
60
|
name = options
|
@@ -47,6 +76,9 @@ module DynamicActiveModel
|
|
47
76
|
dynamic_active_model_config[:connection_options]
|
48
77
|
end
|
49
78
|
|
79
|
+
# Sets or gets the list of tables to skip
|
80
|
+
# @param tables [Array<String>, nil] Tables to skip
|
81
|
+
# @return [Array<String>] The current list of skipped tables
|
50
82
|
def skip_tables(tables = nil)
|
51
83
|
if tables
|
52
84
|
config = dynamic_active_model_config
|
@@ -56,12 +88,17 @@ module DynamicActiveModel
|
|
56
88
|
dynamic_active_model_config[:skip_tables]
|
57
89
|
end
|
58
90
|
|
91
|
+
# Adds a single table to the skip list
|
92
|
+
# @param table [String] Table to skip
|
59
93
|
def skip_table(table)
|
60
94
|
config = dynamic_active_model_config
|
61
95
|
config[:skip_tables] << table
|
62
96
|
redefine_class_method(:dynamic_active_model_config, config)
|
63
97
|
end
|
64
98
|
|
99
|
+
# Sets or gets the custom relationships
|
100
|
+
# @param all_relationships [Hash, nil] All custom relationships
|
101
|
+
# @return [Hash] The current relationships
|
65
102
|
def relationships(all_relationships = nil)
|
66
103
|
if all_relationships
|
67
104
|
config = dynamic_active_model_config
|
@@ -71,6 +108,10 @@ module DynamicActiveModel
|
|
71
108
|
dynamic_active_model_config[:relationships]
|
72
109
|
end
|
73
110
|
|
111
|
+
# Adds a custom foreign key relationship
|
112
|
+
# @param table_name [String] Name of the table
|
113
|
+
# @param foreign_key [String] Name of the foreign key column
|
114
|
+
# @param relationship_name [String] Name for the relationship
|
74
115
|
def foreign_key(table_name, foreign_key, relationship_name)
|
75
116
|
config = dynamic_active_model_config
|
76
117
|
current_relationships = config[:relationships]
|
@@ -79,6 +120,9 @@ module DynamicActiveModel
|
|
79
120
|
redefine_class_method(:dynamic_active_model_config, config)
|
80
121
|
end
|
81
122
|
|
123
|
+
# Sets or gets the path for model extensions
|
124
|
+
# @param path [String, nil] Path to extension files
|
125
|
+
# @return [String, nil] The current extensions path
|
82
126
|
def extensions_path(path = nil)
|
83
127
|
if path
|
84
128
|
config = dynamic_active_model_config
|
@@ -88,6 +132,9 @@ module DynamicActiveModel
|
|
88
132
|
dynamic_active_model_config[:extensions_path]
|
89
133
|
end
|
90
134
|
|
135
|
+
# Sets or gets the suffix for extension files
|
136
|
+
# @param suffix [String, nil] File extension suffix
|
137
|
+
# @return [String] The current extensions suffix
|
91
138
|
def extensions_suffix(suffix = nil)
|
92
139
|
if suffix
|
93
140
|
config = dynamic_active_model_config
|
@@ -97,6 +144,11 @@ module DynamicActiveModel
|
|
97
144
|
dynamic_active_model_config[:extensions_suffix]
|
98
145
|
end
|
99
146
|
|
147
|
+
# Creates all models and applies extensions
|
148
|
+
# This method:
|
149
|
+
# 1. Creates models using Explorer
|
150
|
+
# 2. Applies any model extensions if configured
|
151
|
+
# @return [Database] The configured database instance
|
100
152
|
def create_models!
|
101
153
|
redefine_class_method(
|
102
154
|
:database,
|
@@ -1,17 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DynamicActiveModel
|
4
|
-
#
|
4
|
+
# The TemplateClassFile class generates Ruby source files for ActiveRecord models.
|
5
|
+
# It creates properly formatted class definitions that include:
|
6
|
+
# - Table name configuration
|
7
|
+
# - Has many relationships
|
8
|
+
# - Belongs to relationships
|
9
|
+
# - Has one relationships
|
10
|
+
# - Has and belongs to many relationships
|
11
|
+
# - Custom association options
|
12
|
+
#
|
13
|
+
# @example Basic Usage
|
14
|
+
# model = DB::User
|
15
|
+
# template = DynamicActiveModel::TemplateClassFile.new(model)
|
16
|
+
# template.create_template!('app/models')
|
17
|
+
#
|
18
|
+
# @example Generate Source String
|
19
|
+
# model = DB::User
|
20
|
+
# template = DynamicActiveModel::TemplateClassFile.new(model)
|
21
|
+
# source = template.to_s
|
5
22
|
class TemplateClassFile
|
23
|
+
# Initializes a new TemplateClassFile instance
|
24
|
+
# @param model [Class] The ActiveRecord model to generate a template for
|
6
25
|
def initialize(model)
|
7
26
|
@model = model
|
8
27
|
end
|
9
28
|
|
29
|
+
# Creates a Ruby source file for the model
|
30
|
+
# @param dir [String] Directory to create the file in
|
31
|
+
# @return [void]
|
10
32
|
def create_template!(dir)
|
11
33
|
file = dir + '/' + @model.name.underscore + '.rb'
|
12
34
|
File.open(file, 'wb') { |f| f.write(to_s) }
|
13
35
|
end
|
14
36
|
|
37
|
+
# Generates the Ruby source code for the model
|
38
|
+
# @return [String] The complete model class definition
|
15
39
|
def to_s
|
16
40
|
str = "class #{@model.name} < ActiveRecord::Base\n".dup
|
17
41
|
str << " self.table_name = #{@model.table_name.to_sym.inspect}\n" unless @model.name.underscore.pluralize == @model.table_name
|
@@ -21,27 +45,76 @@ module DynamicActiveModel
|
|
21
45
|
all_belongs_to_relationships.each do |assoc|
|
22
46
|
append_association!(str, assoc)
|
23
47
|
end
|
48
|
+
all_has_one_relationships.each do |assoc|
|
49
|
+
append_association!(str, assoc)
|
50
|
+
end
|
51
|
+
all_has_and_belongs_to_many_relationships.each do |assoc|
|
52
|
+
append_association!(str, assoc)
|
53
|
+
end
|
24
54
|
str << "end\n"
|
25
55
|
str
|
26
56
|
end
|
27
57
|
|
28
58
|
private
|
29
59
|
|
60
|
+
# Gets all has_many relationships for the model
|
61
|
+
# @return [Array<ActiveRecord::Reflection::HasManyReflection>]
|
30
62
|
def all_has_many_relationships
|
31
63
|
@model.reflect_on_all_associations.select do |assoc|
|
32
64
|
assoc.is_a?(ActiveRecord::Reflection::HasManyReflection)
|
33
65
|
end
|
34
66
|
end
|
35
67
|
|
68
|
+
# Gets all belongs_to relationships for the model
|
69
|
+
# @return [Array<ActiveRecord::Reflection::BelongsToReflection>]
|
36
70
|
def all_belongs_to_relationships
|
37
71
|
@model.reflect_on_all_associations.select do |assoc|
|
38
72
|
assoc.is_a?(ActiveRecord::Reflection::BelongsToReflection)
|
39
73
|
end
|
40
74
|
end
|
41
75
|
|
76
|
+
# Gets all has_one relationships for the model
|
77
|
+
# @return [Array<ActiveRecord::Reflection::HasOneReflection>]
|
78
|
+
def all_has_one_relationships
|
79
|
+
@model.reflect_on_all_associations.select do |assoc|
|
80
|
+
assoc.is_a?(ActiveRecord::Reflection::HasOneReflection)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Gets all has_and_belongs_to_many relationships for the model
|
85
|
+
# @return [Array<ActiveRecord::Reflection::HasAndBelongsToManyReflection>]
|
86
|
+
def all_has_and_belongs_to_many_relationships
|
87
|
+
@model.reflect_on_all_associations.select do |assoc|
|
88
|
+
assoc.is_a?(ActiveRecord::Reflection::HasAndBelongsToManyReflection)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Appends an association definition to the source string
|
93
|
+
# @param str [String] The source string being built
|
94
|
+
# @param assoc [ActiveRecord::Reflection::AssociationReflection] The association to add
|
42
95
|
def append_association!(str, assoc)
|
43
|
-
assoc_type = assoc
|
44
|
-
|
96
|
+
assoc_type = case assoc
|
97
|
+
when ActiveRecord::Reflection::HasManyReflection
|
98
|
+
'has_many'
|
99
|
+
when ActiveRecord::Reflection::BelongsToReflection
|
100
|
+
'belongs_to'
|
101
|
+
when ActiveRecord::Reflection::HasOneReflection
|
102
|
+
'has_one'
|
103
|
+
when ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
104
|
+
'has_and_belongs_to_many'
|
105
|
+
end
|
106
|
+
|
107
|
+
association_options = case assoc_type
|
108
|
+
when 'has_many'
|
109
|
+
has_many_association_options(assoc)
|
110
|
+
when 'belongs_to'
|
111
|
+
belongs_to_association_options(assoc)
|
112
|
+
when 'has_one'
|
113
|
+
has_one_association_options(assoc)
|
114
|
+
when 'has_and_belongs_to_many'
|
115
|
+
has_and_belongs_to_many_association_options(assoc)
|
116
|
+
end
|
117
|
+
|
45
118
|
str << " #{assoc_type} #{assoc.name.inspect}"
|
46
119
|
unless association_options.empty?
|
47
120
|
association_options.each do |name, value|
|
@@ -51,6 +124,9 @@ module DynamicActiveModel
|
|
51
124
|
str << "\n"
|
52
125
|
end
|
53
126
|
|
127
|
+
# Gets the options for a has_many association
|
128
|
+
# @param assoc [ActiveRecord::Reflection::HasManyReflection] The association
|
129
|
+
# @return [Hash] The association options
|
54
130
|
def has_many_association_options(assoc)
|
55
131
|
options = {}
|
56
132
|
options[:class_name] = assoc.options[:class_name] unless assoc.options[:class_name].underscore.pluralize == assoc.name.to_s
|
@@ -59,6 +135,9 @@ module DynamicActiveModel
|
|
59
135
|
options
|
60
136
|
end
|
61
137
|
|
138
|
+
# Gets the options for a belongs_to association
|
139
|
+
# @param assoc [ActiveRecord::Reflection::BelongsToReflection] The association
|
140
|
+
# @return [Hash] The association options
|
62
141
|
def belongs_to_association_options(assoc)
|
63
142
|
options = {}
|
64
143
|
options[:class_name] = assoc.options[:class_name] unless assoc.options[:class_name] == assoc.name.to_s.classify
|
@@ -67,10 +146,43 @@ module DynamicActiveModel
|
|
67
146
|
options
|
68
147
|
end
|
69
148
|
|
149
|
+
# Gets the options for a has_one association
|
150
|
+
# @param assoc [ActiveRecord::Reflection::HasOneReflection] The association
|
151
|
+
# @return [Hash] The association options
|
152
|
+
def has_one_association_options(assoc)
|
153
|
+
options = {}
|
154
|
+
options[:class_name] = assoc.options[:class_name] unless assoc.options[:class_name].underscore.singularize == assoc.name.to_s
|
155
|
+
options[:foreign_key] = assoc.options[:foreign_key] unless assoc.options[:foreign_key] == default_foreign_key_name
|
156
|
+
options[:primary_key] = assoc.options[:primary_key] unless assoc.options[:primary_key] == 'id'
|
157
|
+
options
|
158
|
+
end
|
159
|
+
|
160
|
+
# Gets the options for a has_and_belongs_to_many association
|
161
|
+
# @param assoc [ActiveRecord::Reflection::HasAndBelongsToManyReflection] The association
|
162
|
+
# @return [Hash] The association options
|
163
|
+
def has_and_belongs_to_many_association_options(assoc)
|
164
|
+
options = {}
|
165
|
+
options[:join_table] = assoc.options[:join_table] if assoc.options[:join_table]
|
166
|
+
options[:class_name] = assoc.options[:class_name] unless assoc.options[:class_name].underscore.singularize == assoc.name.to_s
|
167
|
+
options
|
168
|
+
end
|
169
|
+
|
170
|
+
# Gets the default foreign key name for the model
|
171
|
+
# @return [String] The default foreign key name
|
70
172
|
def default_foreign_key_name
|
71
173
|
@model.table_name.underscore.singularize + '_id'
|
72
174
|
end
|
73
175
|
|
176
|
+
# Generates the default foreign key for the associated model
|
177
|
+
# @param assoc [ActiveRecord::Reflection::HasAndBelongsToManyReflection] The association
|
178
|
+
# @return [String] The generated foreign key name
|
179
|
+
def generate_association_foreign_key(assoc)
|
180
|
+
assoc.options[:class_name].underscore.singularize + '_id'
|
181
|
+
end
|
182
|
+
|
183
|
+
# Gets a constant by its fully qualified name
|
184
|
+
# @param class_name [String] The fully qualified class name
|
185
|
+
# @return [Class] The resolved class
|
74
186
|
def const_get(class_name)
|
75
187
|
class_name.split('::').inject(Object) { |mod, name| mod.const_get(name) }
|
76
188
|
end
|
data/lib/dynamic-active-model.rb
CHANGED
@@ -1,15 +1,49 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# DynamicActiveModel
|
3
|
+
# DynamicActiveModel is a Ruby gem that provides automatic database discovery,
|
4
|
+
# model creation, and relationship mapping for Rails applications.
|
5
|
+
#
|
6
|
+
# It allows developers to dynamically create ActiveRecord models from existing
|
7
|
+
# database schemas without manually writing model classes. The gem automatically
|
8
|
+
# detects and sets up relationships including has_many, belongs_to, has_one,
|
9
|
+
# and has_and_belongs_to_many based on database constraints.
|
10
|
+
#
|
11
|
+
# @example Basic Usage
|
12
|
+
# module DB; end
|
13
|
+
# DynamicActiveModel::Explorer.explore(DB, database_config)
|
14
|
+
#
|
15
|
+
# @example Model Extension
|
16
|
+
# db = DynamicActiveModel::Database.new(DB, database_config)
|
17
|
+
# db.update_model(:users) do
|
18
|
+
# def full_name
|
19
|
+
# "#{first_name} #{last_name}"
|
20
|
+
# end
|
21
|
+
# end
|
4
22
|
module DynamicActiveModel
|
23
|
+
# Database class handles database connection and model creation
|
5
24
|
autoload :Database, 'dynamic-active-model/database'
|
25
|
+
|
26
|
+
# Safety feature that prevents conflicts with Ruby reserved words
|
6
27
|
autoload :DangerousAttributesPatch, 'dynamic-active-model/dangerous_attributes_patch'
|
28
|
+
|
29
|
+
# High-level interface for model discovery and relationship mapping
|
7
30
|
autoload :Explorer, 'dynamic-active-model/explorer'
|
31
|
+
|
32
|
+
# Manages the creation of model classes
|
8
33
|
autoload :Factory, 'dynamic-active-model/factory'
|
34
|
+
|
35
|
+
# Handles foreign key relationships and constraints
|
9
36
|
autoload :ForeignKey, 'dynamic-active-model/foreign_key'
|
37
|
+
|
38
|
+
# Manages automatic discovery and setup of model relationships
|
10
39
|
autoload :Associations, 'dynamic-active-model/associations'
|
40
|
+
|
41
|
+
# Handles generation of model class files
|
11
42
|
autoload :TemplateClassFile, 'dynamic-active-model/template_class_file'
|
43
|
+
|
44
|
+
# Manages the setup process and configuration
|
12
45
|
autoload :Setup, 'dynamic-active-model/setup'
|
13
46
|
|
47
|
+
# Raised when a requested model cannot be found
|
14
48
|
class ModelNotFound < StandardError; end
|
15
49
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamic-active-model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Doug Youch
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-05-
|
10
|
+
date: 2025-05-10 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activerecord
|