dynamic-active-model 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0f4acad4070f218fe60381f8dd22b442ca152fa3528fc309d35f72e6a755f238
4
+ data.tar.gz: 4c15e0e3f7046514a16053d757497be8a16617c96c4e6e5f3b473a4b0a642dc2
5
+ SHA512:
6
+ metadata.gz: edf688fbec02059b722d397f47fb4a2872972db6e5855e4a62641d91f57d4f07fc0d58240ca938f9e38c0277a84498e2d7b4ae2857de5c9ee81856b253ca9b00
7
+ data.tar.gz: 766b08e10f4a11c25329a21d646f887a296c9b04553db92c7a3bffcb703c837034cfea3b53ae0780d472b00471aa6400e37d1b19c1514ee1f850eaf14a382801
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DynamicActiveModel module for create ActiveRecord models
4
+ module DynamicActiveModel
5
+ autoload :Database, 'dynamic-active-model/database'
6
+ autoload :Factory, 'dynamic-active-model/factory'
7
+ autoload :ForeignKey, 'dynamic-active-model/foreign_key'
8
+ autoload :Relations, 'dynamic-active-model/relations'
9
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamicActiveModel
4
+ # DynamicActiveModel::Database iterates over the tables of a
5
+ # database and create ActiveRecord models
6
+ class Database
7
+ attr_reader :table_class_names,
8
+ :factory,
9
+ :models
10
+
11
+ def initialize(base_module, connection_options, base_class_name = nil)
12
+ @factory = Factory.new(base_module, connection_options, base_class_name)
13
+ @table_class_names = {}
14
+ @skip_tables = []
15
+ @skip_table_matchers = []
16
+ @models = []
17
+ end
18
+
19
+ def skip_table(table)
20
+ if table.is_a?(Regexp)
21
+ @skip_table_matchers << table
22
+ else
23
+ @skip_tables << table.to_s
24
+ end
25
+ end
26
+
27
+ def table_class_name(table_name, class_name)
28
+ @table_class_names[table_name.to_s] = class_name
29
+ end
30
+
31
+ def create_models!
32
+ @factory.base_class.connection.tables.each do |table_name|
33
+ next if skip_table?(table_name)
34
+
35
+ @models << @factory.create(table_name, @table_class_names[table_name])
36
+ end
37
+ end
38
+
39
+ def skip_tables
40
+ @skip_tables + @skip_table_matchers
41
+ end
42
+
43
+ private
44
+
45
+ def skip_table?(table_name)
46
+ @skip_tables.include?(table_name) ||
47
+ @skip_table_matchers.any? { |r| r.match(table_name) }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamicActiveModel
4
+ # DynamicActiveModel::Factory creates ActiveRecord class for tables
5
+ class Factory
6
+ attr_writer :base_class
7
+
8
+ def initialize(base_module, connection_options, base_class_name = nil)
9
+ @base_module = base_module
10
+ @connection_options = connection_options
11
+ @base_class_name = base_class_name || :DynamicAbstractBase
12
+ end
13
+
14
+ def create(table_name, class_name = nil)
15
+ class_name ||= generate_class_name(table_name)
16
+ kls = Class.new(base_class) do
17
+ self.table_name = table_name
18
+ end
19
+ @base_module.const_set(class_name, kls)
20
+ @base_module.const_get(class_name)
21
+ end
22
+
23
+ # rubocop:disable MethodLength
24
+ def base_class
25
+ @base_class ||=
26
+ begin
27
+ require 'active_record'
28
+
29
+ kls = Class.new(ActiveRecord::Base) do
30
+ self.abstract_class = true
31
+ end
32
+ @base_module.const_set(@base_class_name, kls)
33
+
34
+ kls = @base_module.const_get(@base_class_name)
35
+ kls.establish_connection @connection_options
36
+ kls
37
+ end
38
+ end
39
+ # rubocop:enable MethodLength
40
+
41
+ def generate_class_name(table_name)
42
+ table_name.classify
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamicActiveModel
4
+ # DynamicActiveModel::ForeignKey tracks foreign keys related to the model
5
+ class ForeignKey
6
+ attr_reader :model,
7
+ :keys
8
+
9
+ DEFAULT_ID_SUFFIX = '_id'
10
+
11
+ def self.id_suffix
12
+ @id_suffix || DEFAULT_ID_SUFFIX
13
+ end
14
+
15
+ # rubocop:disable Style/TrivialAccessors
16
+ def self.id_suffix=(val)
17
+ @id_suffix = val
18
+ end
19
+ # rubocop:enable Style/TrivialAccessors
20
+
21
+ def initialize(model)
22
+ @model = model
23
+ @keys = {}
24
+ add(generate_foreign_key(model.table_name), model.table_name.underscore)
25
+ end
26
+
27
+ def add(key, relationship_name)
28
+ @keys[key] = relationship_name
29
+ end
30
+
31
+ def generate_foreign_key(table_name)
32
+ table_name.singularize + self.class.id_suffix
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamicActiveModel
4
+ # DynamicActiveModel::Relations iterates over the models of a
5
+ # database and adds has_many and belongs_to based on foreign keys
6
+ class Relations
7
+ attr_reader :database
8
+
9
+ def initialize(database)
10
+ @database = database
11
+ @foreign_keys = database.models.each_with_object({}) do |model, hsh|
12
+ hsh[model.table_name] = ForeignKey.new(model)
13
+ end
14
+ end
15
+
16
+ def add_foreign_key(table_name, foreign_key, relationship_name)
17
+ @foreign_keys[table_name].add(foreign_key, relationship_name)
18
+ end
19
+
20
+ # iterates over the models and adds relationships
21
+ def build!
22
+ foreign_key_to_models = create_foreign_key_to_model_map
23
+
24
+ @database.models.each do |model|
25
+ model.column_names.each do |column_name|
26
+ next unless foreign_key_to_models[column_name]
27
+
28
+ foreign_key_to_models[column_name].each do |foreign_model, relationship_name|
29
+ next if foreign_model == model
30
+
31
+ add_relationships(relationship_name, model, foreign_model, column_name)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def add_relationships(relationship_name, model, belongs_to_model, foreign_key)
40
+ add_belongs_to(relationship_name, model, belongs_to_model, foreign_key)
41
+ add_has_many(belongs_to_model, model, foreign_key)
42
+ end
43
+
44
+ def add_belongs_to(relationship_name, model, belongs_to_model, foreign_key)
45
+ model.belongs_to(
46
+ relationship_name.singularize.to_sym,
47
+ class_name: belongs_to_model.name,
48
+ foreign_key: foreign_key,
49
+ primary_key: belongs_to_model.primary_key
50
+ )
51
+ end
52
+
53
+ def add_has_many(model, has_many_model, foreign_key)
54
+ model.has_many(
55
+ has_many_model.table_name.underscore.pluralize.to_sym,
56
+ class_name: has_many_model.name,
57
+ foreign_key: foreign_key,
58
+ primary_key: has_many_model.primary_key
59
+ )
60
+ end
61
+
62
+ def create_foreign_key_to_model_map
63
+ @foreign_keys.values.each_with_object({}) do |foreign_key, hsh|
64
+ foreign_key.keys.each do |key, relationship_name|
65
+ hsh[key] ||= []
66
+ hsh[key] << [foreign_key.model, relationship_name]
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dynamic-active-model
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Doug Youch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-07-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Dynamically create ActiveRecord models for tables
28
+ email: dougyouch@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/dynamic-active-model.rb
34
+ - lib/dynamic-active-model/database.rb
35
+ - lib/dynamic-active-model/factory.rb
36
+ - lib/dynamic-active-model/foreign_key.rb
37
+ - lib/dynamic-active-model/relations.rb
38
+ homepage: https://github.com/dougyouch/dynamic-active-model
39
+ licenses: []
40
+ metadata: {}
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubygems_version: 3.0.3
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Dynamic ActiveRecord Models
60
+ test_files: []