dynamic-active-model 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f4acad4070f218fe60381f8dd22b442ca152fa3528fc309d35f72e6a755f238
4
- data.tar.gz: 4c15e0e3f7046514a16053d757497be8a16617c96c4e6e5f3b473a4b0a642dc2
3
+ metadata.gz: 67d4e71c097cafd08ed7e9c3ed2ffd4d8b22b5787fd2f6ab498a709b75de5215
4
+ data.tar.gz: a5cad0b2293d861f93bdf9d6c8fbc983ca9c4af1ab9bb60c22216c074e2b01c2
5
5
  SHA512:
6
- metadata.gz: edf688fbec02059b722d397f47fb4a2872972db6e5855e4a62641d91f57d4f07fc0d58240ca938f9e38c0277a84498e2d7b4ae2857de5c9ee81856b253ca9b00
7
- data.tar.gz: 766b08e10f4a11c25329a21d646f887a296c9b04553db92c7a3bffcb703c837034cfea3b53ae0780d472b00471aa6400e37d1b19c1514ee1f850eaf14a382801
6
+ metadata.gz: 5939469d103fb9c6a6d71ea987e6b0f8fe9c117d1014ca5d752c25307c1d5ff586d64cebae3571cc1eb276e9cfd760e577bae2066c9812223055c1f309b0cb50
7
+ data.tar.gz: 422a33565554689d2055122cc45234e7fd4c7f1f766f018b4fd080df6c86051cc79d950fec706702f489bd225793e84b3dc1d4cc7b66ae2dd2667fe507e0f5f1
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'dynamic-active-model'
5
+ require 'optparse'
6
+ require 'yaml'
7
+
8
+ DEFAULT_CONFIG_FILE = "#{ENV['HOME']}/.dynamic-db-explorer.yml"
9
+
10
+ options = {
11
+ base_module: Object,
12
+ connection_options: {},
13
+ skip_tables: [],
14
+ relationships: {},
15
+ config_file: nil,
16
+ config_section: nil,
17
+ class_files_dir: nil
18
+ }
19
+
20
+ # rubocop:disable Metrics/BlockLength
21
+ OptionParser.new do |opts|
22
+ opts.banner = 'Usage: dynamic-db-explorer'
23
+
24
+ opts.on('-h', '--help', 'Prints this help') do
25
+ puts opts
26
+ exit
27
+ end
28
+
29
+ opts.on('--username USERMNAME', 'DB username') do |v|
30
+ options[:connection_options][:username] = v
31
+ end
32
+
33
+ opts.on('--host HOST', 'DB host') do |v|
34
+ options[:connection_options][:host] = v
35
+ end
36
+
37
+ opts.on('--port PORT', 'DB port') do |v|
38
+ options[:connection_options][:port] = v
39
+ end
40
+
41
+ opts.on('--password PASSWORD', 'DB password') do |v|
42
+ options[:connection_options][:password] = v
43
+ end
44
+
45
+ opts.on('--adapter ADAPTER', 'DB adapter') do |v|
46
+ options[:connection_options][:adapter] = v
47
+ end
48
+
49
+ opts.on('--database DATABASE', 'DB name') do |v|
50
+ options[:connection_options][:database] = v
51
+ end
52
+
53
+ opts.on('--module MODULE_NAME', 'module name') do |v|
54
+ Object.const_set(v, Module.new)
55
+ options[:base_module] = Object.const_get(v)
56
+ end
57
+
58
+ opts.on('-c', '--config FILE', "config file, default #{DEFAULT_CONFIG_FILE}") do |v|
59
+ options[:config_file] = v
60
+ end
61
+
62
+ opts.on('-s', '--section SECTION', 'config file section') do |v|
63
+ options[:config_file] = DEFAULT_CONFIG_FILE if !options[:config_file] && File.exist?(DEFAULT_CONFIG_FILE)
64
+ options[:config_section] = v
65
+ end
66
+
67
+ opts.on('--create-class-files DIR', 'Create ActiveRecord class files') do |v|
68
+ raise("#{v} not found") unless File.exist?(v)
69
+
70
+ options[:class_files_dir] = v
71
+ end
72
+ end.parse!
73
+ # rubocop:enable Metrics/BlockLength
74
+
75
+ if options[:config_file]
76
+ raise('must specify a section') unless options[:config_section]
77
+
78
+ config = YAML.load_file(options[:config_file])[options[:config_section]]
79
+ options[:skip_tables] = config.delete('skip_tables') || []
80
+ options[:relationships] = config.delete('relationships') || {}
81
+ if (module_name = config.delete('module'))
82
+ Object.const_set(module_name, Module.new)
83
+ options[:base_module] = Object.const_get(module_name)
84
+ end
85
+ options[:connection_options] = config.each_with_object({}) { |(name, value), hsh| hsh[name.to_sym] = value }
86
+ end
87
+
88
+ database = DynamicActiveModel::Explorer.explore(
89
+ options[:base_module],
90
+ options[:connection_options],
91
+ options[:skip_tables],
92
+ options[:relationships]
93
+ )
94
+
95
+ if options[:class_files_dir]
96
+ database.models.each do |model|
97
+ DynamicActiveModel::TemplateClassFile.new(model).create_template!(options[:class_files_dir])
98
+ end
99
+ else
100
+ require 'irb'
101
+ IRB.start(__FILE__)
102
+ end
@@ -3,7 +3,9 @@
3
3
  # DynamicActiveModel module for create ActiveRecord models
4
4
  module DynamicActiveModel
5
5
  autoload :Database, 'dynamic-active-model/database'
6
+ autoload :Explorer, 'dynamic-active-model/explorer'
6
7
  autoload :Factory, 'dynamic-active-model/factory'
7
8
  autoload :ForeignKey, 'dynamic-active-model/foreign_key'
8
9
  autoload :Relations, 'dynamic-active-model/relations'
10
+ autoload :TemplateClassFile, 'dynamic-active-model/template_class_file'
9
11
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamicActiveModel
4
+ # DynamicActiveModel::EWplorer creates models and relationships
5
+ module Explorer
6
+ def self.explore(base_module, connection_options, skip_tables = [], relationships = {})
7
+ database = create_models!(base_module, connection_options, skip_tables)
8
+ build_relationships!(database, relationships)
9
+ database
10
+ end
11
+
12
+ def self.create_models!(base_module, connection_options, skip_tables)
13
+ database = Database.new(base_module, connection_options)
14
+ skip_tables.each do |table_name|
15
+ table_name = Regexp.new("^#{table_name}") if table_name.include?('*')
16
+ database.skip_table(table_name)
17
+ end
18
+ database.create_models!
19
+ database
20
+ end
21
+
22
+ def self.build_relationships!(database, relationships)
23
+ relations = Relations.new(database)
24
+ relationships.each do |table_name, foreign_keys|
25
+ foreign_keys.each do |foreign_key, relationship_name|
26
+ relations.add_foreign_key(table_name, foreign_key, relationship_name)
27
+ end
28
+ end
29
+ relations.build!
30
+ end
31
+ end
32
+ end
@@ -29,7 +29,7 @@ module DynamicActiveModel
29
29
  end
30
30
 
31
31
  def generate_foreign_key(table_name)
32
- table_name.singularize + self.class.id_suffix
32
+ table_name.underscore.singularize + self.class.id_suffix
33
33
  end
34
34
  end
35
35
  end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamicActiveModel
4
+ # DynamicActiveModel::TemplateClassFile creates ActiveRecord file for model
5
+ class TemplateClassFile
6
+ def initialize(model)
7
+ @model = model
8
+ end
9
+
10
+ def create_template!(dir)
11
+ file = dir + '/' + @model.name.underscore + '.rb'
12
+ File.open(file, 'wb') { |f| f.write(to_s) }
13
+ end
14
+
15
+ def to_s
16
+ str = "class #{@model.name} < ActiveRecord::Base\n".dup
17
+ str << " self.table_name = #{@model.table_name.to_sym.inspect}\n" unless @model.name.underscore.pluralize == @model.table_name
18
+ all_has_many_relationships.each do |assoc|
19
+ append_association!(str, assoc)
20
+ end
21
+ all_belongs_to_relationships.each do |assoc|
22
+ append_association!(str, assoc)
23
+ end
24
+ str << "end\n"
25
+ str
26
+ end
27
+
28
+ private
29
+
30
+ def all_has_many_relationships
31
+ @model.reflect_on_all_associations.select do |assoc|
32
+ assoc.is_a?(ActiveRecord::Reflection::HasManyReflection)
33
+ end
34
+ end
35
+
36
+ def all_belongs_to_relationships
37
+ @model.reflect_on_all_associations.select do |assoc|
38
+ assoc.is_a?(ActiveRecord::Reflection::BelongsToReflection)
39
+ end
40
+ end
41
+
42
+ def append_association!(str, assoc)
43
+ assoc_type = assoc.is_a?(ActiveRecord::Reflection::HasManyReflection) ? 'has_many' : 'belongs_to'
44
+ association_options = assoc_type == 'has_many' ? has_many_association_options(assoc) : belongs_to_association_options(assoc)
45
+ str << " #{assoc_type} #{assoc.name.inspect}"
46
+ unless association_options.empty?
47
+ association_options.each do |name, value|
48
+ str << ", #{name}: '#{value}'"
49
+ end
50
+ end
51
+ str << "\n"
52
+ end
53
+
54
+ def has_many_association_options(assoc)
55
+ options = {}
56
+ options[:class_name] = assoc.options[:class_name] unless assoc.options[:class_name].underscore.pluralize == assoc.name.to_s
57
+ options[:foreign_key] = assoc.options[:foreign_key] unless assoc.options[:foreign_key] == default_foreign_key_name
58
+ options[:primary_key] = assoc.options[:primary_key] unless assoc.options[:primary_key] == 'id'
59
+ options
60
+ end
61
+
62
+ def belongs_to_association_options(assoc)
63
+ options = {}
64
+ options[:class_name] = assoc.options[:class_name] unless assoc.options[:class_name] == assoc.name.to_s.classify
65
+ options[:foreign_key] = assoc.options[:foreign_key] unless assoc.options[:foreign_key] == (assoc.options[:class_name].underscore + '_id')
66
+ options[:primary_key] = assoc.options[:primary_key] unless assoc.options[:primary_key] == 'id'
67
+ options
68
+ end
69
+
70
+ def default_foreign_key_name
71
+ @model.table_name.underscore.singularize + '_id'
72
+ end
73
+
74
+ def const_get(class_name)
75
+ class_name.split('::').inject(Object) { |mod, name| mod.const_get(name) }
76
+ end
77
+ end
78
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamic-active-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Doug Youch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-12 00:00:00.000000000 Z
11
+ date: 2019-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -26,15 +26,19 @@ dependencies:
26
26
  version: '0'
27
27
  description: Dynamically create ActiveRecord models for tables
28
28
  email: dougyouch@gmail.com
29
- executables: []
29
+ executables:
30
+ - dynamic-db-explorer
30
31
  extensions: []
31
32
  extra_rdoc_files: []
32
33
  files:
34
+ - bin/dynamic-db-explorer
33
35
  - lib/dynamic-active-model.rb
34
36
  - lib/dynamic-active-model/database.rb
37
+ - lib/dynamic-active-model/explorer.rb
35
38
  - lib/dynamic-active-model/factory.rb
36
39
  - lib/dynamic-active-model/foreign_key.rb
37
40
  - lib/dynamic-active-model/relations.rb
41
+ - lib/dynamic-active-model/template_class_file.rb
38
42
  homepage: https://github.com/dougyouch/dynamic-active-model
39
43
  licenses: []
40
44
  metadata: {}