dynamic-active-model 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: {}