dynamic-active-model 0.8.0 → 0.9.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/lib/dynamic-active-model/associations.rb +15 -12
- data/lib/dynamic-active-model/database.rb +12 -4
- data/lib/dynamic-active-model/explorer.rb +17 -7
- data/lib/dynamic-active-model/factory.rb +22 -3
- data/lib/dynamic-active-model/setup.rb +18 -1
- data/lib/dynamic-active-model/version.rb +1 -1
- data/lib/dynamic-active-model.rb +7 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a9f8e2e549471211eb31b49e3e20b8a72e1c47eb2e9c857bcb4a85c11e470558
|
|
4
|
+
data.tar.gz: 70b7edd5ecf3dbe9c164593f1866eb2bd12a9cb89b5c609898052b8ad3f5eee2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 90b12783def828937bef5898a020c6e8639fbf72c83a8649f5227077fce087d1fc619bd25f472b70b83dcf6a551bc97386f57c86b76f4fdc401fbe8c92336c11
|
|
7
|
+
data.tar.gz: c3316c0eb2a01b52e3b2628c913ce1d1d53c6ba7f67a34cbfb879caa820509d5ab676e3a2f78c771ca40a3405f960e65bea4ca9674540fca3192808d32053926
|
|
@@ -45,8 +45,11 @@ module DynamicActiveModel
|
|
|
45
45
|
# @param table_name [String] Name of the table with the foreign key
|
|
46
46
|
# @param foreign_key [String] Name of the foreign key column
|
|
47
47
|
# @param relationship_name [String, nil] Custom name for the relationship
|
|
48
|
+
# @raise [ModelNotFound] If no model exists for the table
|
|
48
49
|
def add_foreign_key(table_name, foreign_key, relationship_name = nil)
|
|
49
|
-
@foreign_keys
|
|
50
|
+
@foreign_keys
|
|
51
|
+
.fetch(table_name.to_s) { raise ModelNotFound, "no model found for table #{table_name}" }
|
|
52
|
+
.add(foreign_key, relationship_name)
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
# Builds all relationships between models based on foreign keys and constraints
|
|
@@ -122,29 +125,29 @@ module DynamicActiveModel
|
|
|
122
125
|
|
|
123
126
|
# Adds a has_many relationship to a model
|
|
124
127
|
# @param relationship_name [String] Name of the relationship
|
|
125
|
-
# @param model [Class] The model
|
|
126
|
-
# @param has_many_model [Class] The model
|
|
128
|
+
# @param model [Class] The referenced (parent) model that gets the association
|
|
129
|
+
# @param has_many_model [Class] The model with the foreign key
|
|
127
130
|
# @param foreign_key [String] The foreign key column name
|
|
128
131
|
def add_has_many(relationship_name, model, has_many_model, foreign_key)
|
|
129
132
|
model.has_many(
|
|
130
133
|
generate_has_many_association_name(relationship_name, model, has_many_model),
|
|
131
134
|
class_name: has_many_model.name,
|
|
132
135
|
foreign_key: foreign_key,
|
|
133
|
-
primary_key:
|
|
136
|
+
primary_key: model.primary_key
|
|
134
137
|
)
|
|
135
138
|
end
|
|
136
139
|
|
|
137
140
|
# Adds a has_one relationship to a model
|
|
138
141
|
# @param relationship_name [String] Name of the relationship
|
|
139
|
-
# @param model [Class] The model
|
|
140
|
-
# @param has_one_model [Class] The model
|
|
142
|
+
# @param model [Class] The referenced (parent) model that gets the association
|
|
143
|
+
# @param has_one_model [Class] The model with the foreign key
|
|
141
144
|
# @param foreign_key [String] The foreign key column name
|
|
142
145
|
def add_has_one(relationship_name, model, has_one_model, foreign_key)
|
|
143
146
|
model.has_one(
|
|
144
147
|
generate_has_one_association_name(relationship_name, model, has_one_model),
|
|
145
148
|
class_name: has_one_model.name,
|
|
146
149
|
foreign_key: foreign_key,
|
|
147
|
-
primary_key:
|
|
150
|
+
primary_key: model.primary_key
|
|
148
151
|
)
|
|
149
152
|
end
|
|
150
153
|
|
|
@@ -161,8 +164,8 @@ module DynamicActiveModel
|
|
|
161
164
|
|
|
162
165
|
# Generates an appropriate name for a has_many association
|
|
163
166
|
# @param relationship_name [String] Original relationship name
|
|
164
|
-
# @param model [Class] The model
|
|
165
|
-
# @param has_many_model [Class] The model
|
|
167
|
+
# @param model [Class] The referenced (parent) model that gets the association
|
|
168
|
+
# @param has_many_model [Class] The model with the foreign key
|
|
166
169
|
# @return [Symbol] The generated association name
|
|
167
170
|
def generate_has_many_association_name(relationship_name, model, has_many_model)
|
|
168
171
|
name =
|
|
@@ -176,8 +179,8 @@ module DynamicActiveModel
|
|
|
176
179
|
|
|
177
180
|
# Generates an appropriate name for a has_one association
|
|
178
181
|
# @param relationship_name [String] Original relationship name
|
|
179
|
-
# @param model [Class] The model
|
|
180
|
-
# @param has_one_model [Class] The model
|
|
182
|
+
# @param model [Class] The referenced (parent) model that gets the association
|
|
183
|
+
# @param has_one_model [Class] The model with the foreign key
|
|
181
184
|
# @return [Symbol] The generated association name
|
|
182
185
|
def generate_has_one_association_name(relationship_name, model, has_one_model)
|
|
183
186
|
name =
|
|
@@ -208,7 +211,7 @@ module DynamicActiveModel
|
|
|
208
211
|
def join_table?(model)
|
|
209
212
|
model.primary_key.nil? &&
|
|
210
213
|
model.columns.size == 2 &&
|
|
211
|
-
model.columns.all? { |column| column.name
|
|
214
|
+
model.columns.all? { |column| column.name.end_with?(ForeignKey.id_suffix) }
|
|
212
215
|
end
|
|
213
216
|
end
|
|
214
217
|
end
|
|
@@ -94,15 +94,16 @@ module DynamicActiveModel
|
|
|
94
94
|
@table_class_names[table_name.to_s] = class_name
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
# Creates ActiveRecord models for all included tables
|
|
98
|
-
# @return [Array] List of
|
|
97
|
+
# Creates ActiveRecord models for all included tables that don't have one yet
|
|
98
|
+
# @return [Array] List of model classes
|
|
99
|
+
# @raise [ClassNameConflict] If two tables map to the same class name
|
|
99
100
|
def create_models!
|
|
100
101
|
@factory.base_class.connection.tables.each do |table_name|
|
|
101
|
-
next
|
|
102
|
-
next unless include_table?(table_name)
|
|
102
|
+
next unless create_model?(table_name)
|
|
103
103
|
|
|
104
104
|
@models << @factory.create(table_name, @table_class_names[table_name])
|
|
105
105
|
end
|
|
106
|
+
@models
|
|
106
107
|
end
|
|
107
108
|
|
|
108
109
|
# @return [Array] List of all skipped tables and patterns
|
|
@@ -174,6 +175,13 @@ module DynamicActiveModel
|
|
|
174
175
|
|
|
175
176
|
private
|
|
176
177
|
|
|
178
|
+
# Checks if a model should be created for a table
|
|
179
|
+
# @param table_name [String] Name of the table
|
|
180
|
+
# @return [Boolean] Whether the table is included and has no model yet
|
|
181
|
+
def create_model?(table_name)
|
|
182
|
+
!skip_table?(table_name) && include_table?(table_name) && get_model(table_name).nil?
|
|
183
|
+
end
|
|
184
|
+
|
|
177
185
|
# Checks if a table should be skipped
|
|
178
186
|
# @param table_name [String] Name of the table
|
|
179
187
|
# @return [Boolean] Whether the table should be skipped
|
|
@@ -28,9 +28,11 @@ module DynamicActiveModel
|
|
|
28
28
|
# @param connection_options [Hash] Database connection options
|
|
29
29
|
# @param skip_tables [Array<String, Regexp>] Tables to exclude from model creation
|
|
30
30
|
# @param relationships [Hash] Custom foreign key relationships to add
|
|
31
|
+
# @param table_class_names [Hash] Custom class names by table name
|
|
31
32
|
# @return [Database] The configured database instance
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
# @raise [ClassNameConflict] If two tables map to the same class name
|
|
34
|
+
def self.explore(base_module, connection_options, skip_tables = [], relationships = {}, table_class_names = {})
|
|
35
|
+
database = create_models!(base_module, connection_options, skip_tables, table_class_names)
|
|
34
36
|
build_relationships!(database, relationships)
|
|
35
37
|
database
|
|
36
38
|
end
|
|
@@ -39,17 +41,25 @@ module DynamicActiveModel
|
|
|
39
41
|
# @param base_module [Module] The namespace for created models
|
|
40
42
|
# @param connection_options [Hash] Database connection options
|
|
41
43
|
# @param skip_tables [Array<String, Regexp>] Tables to exclude from model creation
|
|
44
|
+
# @param table_class_names [Hash] Custom class names by table name
|
|
42
45
|
# @return [Database] The configured database instance
|
|
43
|
-
def self.create_models!(base_module, connection_options, skip_tables)
|
|
46
|
+
def self.create_models!(base_module, connection_options, skip_tables, table_class_names = {})
|
|
44
47
|
database = Database.new(base_module, connection_options)
|
|
45
|
-
skip_tables.each
|
|
46
|
-
|
|
47
|
-
database.skip_table(table_name)
|
|
48
|
-
end
|
|
48
|
+
skip_tables.each { |table| database.skip_table(skip_table_matcher(table)) }
|
|
49
|
+
table_class_names.each { |table_name, class_name| database.table_class_name(table_name, class_name) }
|
|
49
50
|
database.create_models!
|
|
50
51
|
database
|
|
51
52
|
end
|
|
52
53
|
|
|
54
|
+
# Converts a table name containing * wildcards into an anchored Regexp
|
|
55
|
+
# @param table [String, Regexp] Table name, wildcard pattern, or Regexp
|
|
56
|
+
# @return [String, Regexp] The table name or matching Regexp
|
|
57
|
+
def self.skip_table_matcher(table)
|
|
58
|
+
return table unless table.is_a?(String) && table.include?('*')
|
|
59
|
+
|
|
60
|
+
Regexp.new("\\A#{Regexp.escape(table).gsub('\\*', '.*')}\\z")
|
|
61
|
+
end
|
|
62
|
+
|
|
53
63
|
# Sets up relationships between created models
|
|
54
64
|
# @param database [Database] The database instance containing the models
|
|
55
65
|
# @param relationships [Hash] Custom foreign key relationships to add
|
|
@@ -33,10 +33,12 @@ module DynamicActiveModel
|
|
|
33
33
|
# @param table_name [String] Name of the database table
|
|
34
34
|
# @param class_name [String, nil] Optional custom class name
|
|
35
35
|
# @return [Class] The model class
|
|
36
|
+
# @raise [ClassNameConflict] If the class name is already used by a different table
|
|
36
37
|
def create(table_name, class_name = nil)
|
|
37
38
|
class_name ||= generate_class_name(table_name)
|
|
38
|
-
create!(table_name, class_name) unless @base_module.const_defined?(class_name)
|
|
39
|
-
|
|
39
|
+
return create!(table_name, class_name) unless @base_module.const_defined?(class_name, false)
|
|
40
|
+
|
|
41
|
+
existing_model!(table_name, class_name)
|
|
40
42
|
end
|
|
41
43
|
|
|
42
44
|
# Creates a new model class for a table, overwriting if it exists
|
|
@@ -49,7 +51,7 @@ module DynamicActiveModel
|
|
|
49
51
|
include DynamicActiveModel::DangerousAttributesPatch
|
|
50
52
|
end
|
|
51
53
|
@base_module.const_set(class_name, kls)
|
|
52
|
-
@base_module.const_get(class_name)
|
|
54
|
+
@base_module.const_get(class_name, false)
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
# Gets or creates the base class for all models
|
|
@@ -85,5 +87,22 @@ module DynamicActiveModel
|
|
|
85
87
|
|
|
86
88
|
class_name
|
|
87
89
|
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
# Returns the existing model for a class name if it belongs to the table
|
|
94
|
+
# @param table_name [String] Name of the database table
|
|
95
|
+
# @param class_name [String] Name of the existing class
|
|
96
|
+
# @return [Class] The existing model class
|
|
97
|
+
# @raise [ClassNameConflict] If the class belongs to a different table
|
|
98
|
+
def existing_model!(table_name, class_name)
|
|
99
|
+
model = @base_module.const_get(class_name, false)
|
|
100
|
+
return model if model.respond_to?(:table_name) && model.table_name == table_name.to_s
|
|
101
|
+
|
|
102
|
+
raise ClassNameConflict,
|
|
103
|
+
"table #{table_name} maps to class #{model.name}, which is already used by " \
|
|
104
|
+
"#{model.respond_to?(:table_name) ? "table #{model.table_name}" : 'another constant'}; " \
|
|
105
|
+
'set a different class name with table_class_name'
|
|
106
|
+
end
|
|
88
107
|
end
|
|
89
108
|
end
|
|
@@ -47,6 +47,7 @@ module DynamicActiveModel
|
|
|
47
47
|
connection_options: nil,
|
|
48
48
|
skip_tables: [],
|
|
49
49
|
relationships: {},
|
|
50
|
+
table_class_names: {},
|
|
50
51
|
extensions_path: nil,
|
|
51
52
|
extensions_suffix: '.ext.rb'
|
|
52
53
|
}
|
|
@@ -120,6 +121,21 @@ module DynamicActiveModel
|
|
|
120
121
|
redefine_class_method(:dynamic_active_model_config, config)
|
|
121
122
|
end
|
|
122
123
|
|
|
124
|
+
# Sets a custom class name for a table
|
|
125
|
+
# @param table_name [String] Name of the table
|
|
126
|
+
# @param class_name [String] Class name to use for the table's model
|
|
127
|
+
def table_class_name(table_name, class_name)
|
|
128
|
+
config = dynamic_active_model_config
|
|
129
|
+
config[:table_class_names][table_name.to_s] = class_name
|
|
130
|
+
redefine_class_method(:dynamic_active_model_config, config)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Gets the custom class names by table name
|
|
134
|
+
# @return [Hash] The current table class names
|
|
135
|
+
def table_class_names
|
|
136
|
+
dynamic_active_model_config[:table_class_names]
|
|
137
|
+
end
|
|
138
|
+
|
|
123
139
|
# Sets or gets the path for model extensions
|
|
124
140
|
# @param path [String, nil] Path to extension files
|
|
125
141
|
# @return [String, nil] The current extensions path
|
|
@@ -156,7 +172,8 @@ module DynamicActiveModel
|
|
|
156
172
|
self,
|
|
157
173
|
connection_options,
|
|
158
174
|
skip_tables,
|
|
159
|
-
relationships
|
|
175
|
+
relationships,
|
|
176
|
+
table_class_names
|
|
160
177
|
)
|
|
161
178
|
)
|
|
162
179
|
database.update_all_models(extensions_path, extensions_suffix) if extensions_path
|
data/lib/dynamic-active-model.rb
CHANGED
|
@@ -46,6 +46,12 @@ module DynamicActiveModel
|
|
|
46
46
|
# Manages the setup process and configuration
|
|
47
47
|
autoload :Setup, 'dynamic-active-model/setup'
|
|
48
48
|
|
|
49
|
+
# Base class for errors raised by DynamicActiveModel
|
|
50
|
+
class Error < StandardError; end
|
|
51
|
+
|
|
49
52
|
# Raised when a requested model cannot be found
|
|
50
|
-
class ModelNotFound <
|
|
53
|
+
class ModelNotFound < Error; end
|
|
54
|
+
|
|
55
|
+
# Raised when two tables map to the same model class name
|
|
56
|
+
class ClassNameConflict < Error; end
|
|
51
57
|
end
|