active_record_mysql_repl 0.1.0 → 0.1.2
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/.rspec +3 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/exe/army +5 -0
- data/json_schemas/army_schema.json +34 -0
- data/json_schemas/association_schema.json +80 -0
- data/json_schemas/databases_schema.json +61 -0
- data/lib/active_record_mysql_repl/cli/erd.rb +11 -0
- data/lib/active_record_mysql_repl/cli/main.rb +60 -0
- data/lib/active_record_mysql_repl/cli/options.rb +13 -0
- data/lib/active_record_mysql_repl/cli/zsh_completion.rb +37 -0
- data/lib/active_record_mysql_repl/cli.rb +10 -0
- data/lib/active_record_mysql_repl/config.rb +38 -0
- data/lib/active_record_mysql_repl/database/association/analysis.rb +17 -0
- data/lib/active_record_mysql_repl/database/association/definition.rb +0 -0
- data/lib/active_record_mysql_repl/database/association.rb +83 -0
- data/lib/active_record_mysql_repl/database/configs.rb +53 -0
- data/lib/active_record_mysql_repl/database/connection.rb +42 -0
- data/lib/active_record_mysql_repl/database/loader.rb +63 -0
- data/lib/active_record_mysql_repl/database.rb +10 -0
- data/lib/active_record_mysql_repl/extensions/active_record.rb +32 -0
- data/lib/active_record_mysql_repl/extensions/global.rb +29 -0
- data/lib/active_record_mysql_repl/extensions/hash.rb +20 -0
- data/lib/active_record_mysql_repl/extensions/object.rb +138 -0
- data/lib/active_record_mysql_repl/extensions/tabler.rb +32 -0
- data/lib/active_record_mysql_repl/extensions.rb +18 -0
- data/lib/active_record_mysql_repl/ssh_tunnel.rb +21 -0
- data/lib/active_record_mysql_repl/version.rb +5 -0
- data/lib/active_record_mysql_repl.rb +12 -0
- data/sample_config/.army.sample.yml +6 -0
- data/sample_config/.pryrc.sample +9 -0
- data/sample_config/associations.sample.yml +13 -0
- data/sample_config/databases.sample.yml +9 -0
- data/sample_config/sample_db.sql +153 -0
- metadata +75 -36
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordMysqlRepl
|
4
|
+
module Database
|
5
|
+
class Associations
|
6
|
+
class AnalyzedTable
|
7
|
+
attr_accessor :name, :has_many, :has_one, :belongs_to
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
@has_many = []
|
11
|
+
@has_one = []
|
12
|
+
@belongs_to = []
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.load(path)
|
17
|
+
new(YAML.load_file(path, aliases: true))
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(associations)
|
21
|
+
@associations = associations.map { |key, association| [key, Association.new(association)] }.to_h
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
@associations[key]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Analyze the relationship between tables based on the information of *_id columns
|
29
|
+
# For example, if users.company_id exists, users belongs_to companies and companies has_many users
|
30
|
+
def self.analyze(tables, association_settings)
|
31
|
+
analyzed_tables = tables.map { |table| [table, AnalyzedTable.new(table)] }.to_h
|
32
|
+
|
33
|
+
analyzed_tables.each do |table_name, table|
|
34
|
+
association_setting = association_settings[table_name]
|
35
|
+
columns = ActiveRecord::Base.connection.columns(table_name)
|
36
|
+
columns.each do |column|
|
37
|
+
next if association_setting.present? && association_settings.ignore_columns(table_name).include?(column.name)
|
38
|
+
|
39
|
+
associatable = column.name.gsub(/_id$/, '') if column.name.end_with?('_id')
|
40
|
+
next if associatable.blank? || associatable == 'class' # reserved word
|
41
|
+
|
42
|
+
if analyzed_tables.keys.include?(associatable.pluralize)
|
43
|
+
table.belongs_to << associatable.singularize if associatable
|
44
|
+
analyzed_tables[associatable.pluralize].has_many << table_name.pluralize
|
45
|
+
else
|
46
|
+
associatable_table_name = associatable.split('_').last
|
47
|
+
if analyzed_tables.keys.include?(associatable_table_name.pluralize)
|
48
|
+
table.belongs_to << { name: associatable, class_name: associatable_table_name.classify, foreign_key: :id }
|
49
|
+
else
|
50
|
+
table.belongs_to << { name: associatable, class_name: table_name.singularize.classify, foreign_key: :id }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
next if association_setting.blank?
|
56
|
+
|
57
|
+
# merge yaml settings
|
58
|
+
[:has_many, :belongs_to].each do |type|
|
59
|
+
ass = association_setting.fetch(type.to_s, []).map(&:symbolize_keys)
|
60
|
+
table.send(type).concat(ass) unless ass.blank?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
analyzed_tables.values
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Association
|
69
|
+
def initialize(association)
|
70
|
+
@association = association
|
71
|
+
end
|
72
|
+
|
73
|
+
def [](table)
|
74
|
+
table = (@association.keys - ['ignore_columns']) & [table]
|
75
|
+
@association[table.first] if table.present?
|
76
|
+
end
|
77
|
+
|
78
|
+
def ignore_columns(table)
|
79
|
+
@association.fetch('ignore_columns', {}).fetch(table, [])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordMysqlRepl
|
4
|
+
module Database
|
5
|
+
class Configs
|
6
|
+
attr_reader :configs
|
7
|
+
|
8
|
+
def self.load(path)
|
9
|
+
new(YAML.load_file(path))
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
configs[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def keys
|
17
|
+
configs.keys
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def initialize(configs)
|
23
|
+
@configs = configs.map { |key, config| [key, Config.new(config)] }.to_h
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
class Config
|
30
|
+
attr_reader *%i[
|
31
|
+
bastion
|
32
|
+
ssh_user
|
33
|
+
remote_host
|
34
|
+
database
|
35
|
+
port
|
36
|
+
user
|
37
|
+
password
|
38
|
+
prompt_color
|
39
|
+
]
|
40
|
+
|
41
|
+
def initialize(config)
|
42
|
+
@bastion = config["bastion"]
|
43
|
+
@ssh_user = config["ssh_user"]
|
44
|
+
@remote_host = config["remote_host"]
|
45
|
+
@database = config["database"]
|
46
|
+
@port = config["port"]
|
47
|
+
@user = config["user"]
|
48
|
+
@password = config["password"]
|
49
|
+
@prompt_color = config["prompt_color"]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordMysqlRepl
|
4
|
+
module Database
|
5
|
+
module Connection
|
6
|
+
MAX_RETRY = 3
|
7
|
+
|
8
|
+
def self.connect(db_config, port)
|
9
|
+
conn = {
|
10
|
+
adapter: 'mysql2',
|
11
|
+
host: '127.0.0.1',
|
12
|
+
port: port,
|
13
|
+
username: db_config.user,
|
14
|
+
password: db_config.password,
|
15
|
+
database: db_config.database,
|
16
|
+
}
|
17
|
+
|
18
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
19
|
+
conn = ActiveRecord::Base.establish_connection(conn)
|
20
|
+
|
21
|
+
puts "Ensureing connection to #{db_config.database} on port 127.0.0.1:#{port}".gray
|
22
|
+
|
23
|
+
tables = nil
|
24
|
+
(1..MAX_RETRY-1).to_a.each do |time|
|
25
|
+
begin
|
26
|
+
tables = ActiveRecord::Base.connection.tables
|
27
|
+
rescue => e
|
28
|
+
puts "#{e}, Retry #{time}/#{MAX_RETRY}".red
|
29
|
+
sleep time*time
|
30
|
+
next
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if tables.blank?
|
35
|
+
raise "Retred #{MAX_RETRY} times, but failed to connect to database."
|
36
|
+
end
|
37
|
+
|
38
|
+
yield if block_given?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'active_record'
|
5
|
+
require 'active_support/all'
|
6
|
+
|
7
|
+
module ActiveRecordMysqlRepl
|
8
|
+
module Database
|
9
|
+
module Loader
|
10
|
+
def self.load_tables(db_config_key, army_config, port)
|
11
|
+
puts "Loading tables".gray
|
12
|
+
|
13
|
+
tables = ActiveRecord::Base.connection.tables
|
14
|
+
association_settings = Associations.load(army_config.associations)
|
15
|
+
|
16
|
+
analyzed_tables = Associations.analyze(tables, association_settings[db_config_key])
|
17
|
+
skipped = []
|
18
|
+
analyzed_tables.each do |analyzed_table|
|
19
|
+
# defer model definition for tables with `has_many: :xxx, through: xxx` associations
|
20
|
+
if analyzed_table.has_many.any? { |hm| hm.is_a?(Hash) && hm.key?(:through) }
|
21
|
+
skipped << analyzed_table
|
22
|
+
next
|
23
|
+
end
|
24
|
+
define_model(analyzed_table)
|
25
|
+
end
|
26
|
+
|
27
|
+
skipped.each do |analyzed_table|
|
28
|
+
define_model(analyzed_table)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.define_model(analyzed_table)
|
33
|
+
klass = Class.new(ActiveRecord::Base)
|
34
|
+
|
35
|
+
analyzed_table.has_many.each do |has_many|
|
36
|
+
if has_many.is_a?(String)
|
37
|
+
klass.has_many has_many.to_sym
|
38
|
+
elsif has_many.is_a?(Hash)
|
39
|
+
klass.has_many has_many[:name].to_sym, **has_many.except(:name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
analyzed_table.has_one.each do |has_one|
|
44
|
+
klass.has_one has_one[:name].to_sym, class_name: has_one[:class_name], foreign_key: has_one[:foreign_key]
|
45
|
+
end
|
46
|
+
|
47
|
+
analyzed_table.belongs_to.each do |belongs_to|
|
48
|
+
if belongs_to.is_a?(String)
|
49
|
+
klass.belongs_to belongs_to.to_sym
|
50
|
+
elsif belongs_to.is_a?(Hash)
|
51
|
+
klass.belongs_to belongs_to[:name].to_sym, class_name: belongs_to[:class_name].to_sym
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# allow type column
|
56
|
+
klass.inheritance_column = :_type_disabled
|
57
|
+
klass.table_name = analyzed_table.name
|
58
|
+
|
59
|
+
Object.const_set(analyzed_table.name.classify, klass)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiverecordMysqlRepl
|
4
|
+
module Extensions
|
5
|
+
module ActiveRecord
|
6
|
+
module Base
|
7
|
+
def as(name = nil)
|
8
|
+
self.reflect_on_all_associations(name).map(&:name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def describe
|
12
|
+
[
|
13
|
+
exec_sql("DESCRIBE #{self.table_name}").tab(:h),
|
14
|
+
exec_sql("SHOW INDEX FROM #{self.table_name}").tab(:h)
|
15
|
+
].join("\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
alias desc describe
|
19
|
+
alias d describe
|
20
|
+
|
21
|
+
def show_ddl
|
22
|
+
exec_sql("SHOW CREATE TABLE #{self.table_name}")[0]['Create Table']
|
23
|
+
end
|
24
|
+
|
25
|
+
alias ddl show_ddl
|
26
|
+
end
|
27
|
+
|
28
|
+
::ActiveRecord::Base.extend(Base)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiverecordMysqlRepl
|
4
|
+
module Extensions
|
5
|
+
module Global
|
6
|
+
def tables
|
7
|
+
::ActiveRecord::Base.connection.tables.map(&:singularize).map(&:classify)
|
8
|
+
end
|
9
|
+
|
10
|
+
def models
|
11
|
+
::ActiveRecord::Base.subclasses
|
12
|
+
end
|
13
|
+
|
14
|
+
def transaction
|
15
|
+
::ActiveRecord::Base.transaction { yield }
|
16
|
+
end
|
17
|
+
|
18
|
+
def exec_sql(sql)
|
19
|
+
res = ::ActiveRecord::Base.connection.select_all(sql)
|
20
|
+
res.rows.map do |row|
|
21
|
+
res.columns.zip(row).to_h
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Kernel.include(Global)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiverecordMysqlRepl
|
4
|
+
module Extensions
|
5
|
+
module Hash
|
6
|
+
def method_missing(name, *args)
|
7
|
+
if (self.keys & [name, name.to_s]).present?
|
8
|
+
self[name] || self[name.to_s]
|
9
|
+
elsif name.to_s.end_with?('=') && self.keys.include?(name.to_s[0..-2])
|
10
|
+
name = name[0..-2]
|
11
|
+
self[name] = args.first if self.key?(name)
|
12
|
+
self[name.to_sym] = args.first if self.key?(name.to_sym)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
::Hash.include(Hash)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'terminal-table'
|
4
|
+
require 'csv'
|
5
|
+
require 'clipboard'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
module ActiverecordMysqlRepl
|
9
|
+
module Extensions
|
10
|
+
module Object
|
11
|
+
def tabulate(orientation = nil)
|
12
|
+
arr = if self.is_a?(Array)
|
13
|
+
self
|
14
|
+
elsif self.is_a?(::ActiveRecord::Relation)
|
15
|
+
self.to_a
|
16
|
+
else
|
17
|
+
[self]
|
18
|
+
end
|
19
|
+
|
20
|
+
arr = arr.map do |e|
|
21
|
+
if e.is_a?(::ActiveRecord::Base)
|
22
|
+
values = e.attributes.transform_values do |v|
|
23
|
+
next JSON.pretty_generate(v) if v.is_a?(Enumerable) && v.size > 0
|
24
|
+
next 'NULL' if v.nil?
|
25
|
+
next '""' if v == ''
|
26
|
+
v.to_s
|
27
|
+
end
|
28
|
+
next values
|
29
|
+
end
|
30
|
+
e
|
31
|
+
end
|
32
|
+
|
33
|
+
raise "#{arr} is not an array of hash".red unless arr.all? { |e| e.is_a?(Hash) }
|
34
|
+
raise "#{arr} contains Hashes with different keys".red unless arr.map(&:keys).uniq.size == 1
|
35
|
+
|
36
|
+
orientation = case orientation
|
37
|
+
when :vertical, :v
|
38
|
+
:vertical
|
39
|
+
when :horizontal, :h
|
40
|
+
:horizontal
|
41
|
+
else
|
42
|
+
if arr.first.keys.size > 5
|
43
|
+
:vertical
|
44
|
+
else
|
45
|
+
:horizontal
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
t = Terminal::Table.new
|
50
|
+
|
51
|
+
case orientation
|
52
|
+
when :horizontal
|
53
|
+
t.headings = arr.first.keys
|
54
|
+
t.rows = arr.map(&:values)
|
55
|
+
|
56
|
+
when :vertical
|
57
|
+
t.headings = ['Name', 'Value']
|
58
|
+
arr.each.with_index do |row, i|
|
59
|
+
row.each { |col, val| t.add_row [col, val] }
|
60
|
+
t.add_separator if i < arr.size - 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
t.to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
alias tab tabulate
|
68
|
+
alias table tabulate
|
69
|
+
alias to_table tabulate
|
70
|
+
|
71
|
+
def csv(orientation = nil)
|
72
|
+
arr = if self.is_a?(Array)
|
73
|
+
self
|
74
|
+
elsif self.is_a?(ActiveRecord::Relation)
|
75
|
+
self.to_a
|
76
|
+
else
|
77
|
+
[self]
|
78
|
+
end
|
79
|
+
|
80
|
+
arr = arr.map do |e|
|
81
|
+
if e.is_a?(ActiveRecord::Base)
|
82
|
+
values = e.attributes.transform_values do |v|
|
83
|
+
next JSON.pretty_generate(v) if v.is_a?(Enumerable) && v.size > 0
|
84
|
+
next 'NULL' if v.nil?
|
85
|
+
next '""' if v == ''
|
86
|
+
v.to_s
|
87
|
+
end
|
88
|
+
next values
|
89
|
+
end
|
90
|
+
e
|
91
|
+
end
|
92
|
+
|
93
|
+
raise "#{arr} is not an array of hash".red unless arr.all? { |e| e.is_a?(Hash) }
|
94
|
+
raise "#{arr} contains Hashes with different keys".red unless arr.map(&:keys).uniq.size == 1
|
95
|
+
|
96
|
+
orientation = case orientation
|
97
|
+
when :vertical, :v
|
98
|
+
:vertical
|
99
|
+
when :horizontal, :h
|
100
|
+
:horizontal
|
101
|
+
else
|
102
|
+
if arr.first.keys.size > 5
|
103
|
+
:vertical
|
104
|
+
else
|
105
|
+
:horizontal
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
CSV.generate do |csv|
|
110
|
+
case orientation
|
111
|
+
when :horizontal
|
112
|
+
csv << arr.first.keys
|
113
|
+
arr.each { |row| csv << row.values }
|
114
|
+
when :vertical
|
115
|
+
arr.each do |row|
|
116
|
+
row.each { |col, val| csv << [col, val] }
|
117
|
+
csv << []
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def copy
|
124
|
+
Clipboard.copy(self.to_s)
|
125
|
+
end
|
126
|
+
alias cp copy
|
127
|
+
|
128
|
+
alias j to_json
|
129
|
+
|
130
|
+
def jp
|
131
|
+
JSON.pretty_generate(JSON.parse(to_json))
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
::Object.include(Extensions::Object)
|
137
|
+
end
|
138
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiverecordMysqlRepl
|
4
|
+
module Extensions
|
5
|
+
module Tabler
|
6
|
+
::ActiveRecord::Base.subclasses.each do |model|
|
7
|
+
define_method(model.table_name) do
|
8
|
+
model.find(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
define_method(model.table_name.singularize) do
|
12
|
+
model.find(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
model.column_names.each do |column|
|
16
|
+
define_method("#{model.table_name.singularize}_by_#{column}") do
|
17
|
+
model.where(column => self)
|
18
|
+
end
|
19
|
+
|
20
|
+
define_method("#{model.table_name.singularize}_#{column}_like") do
|
21
|
+
model.where("#{column} like ?", "%#{self}%")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
String.include Tabler
|
28
|
+
Symbol.include Tabler
|
29
|
+
Enumerable.include Tabler
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'extensions/object'
|
4
|
+
require_relative 'extensions/global'
|
5
|
+
require_relative 'extensions/active_record'
|
6
|
+
require_relative 'extensions/hash'
|
7
|
+
require_relative 'extensions/tabler'
|
8
|
+
|
9
|
+
module ActiveRecordMysqlRepl
|
10
|
+
module Extensions
|
11
|
+
def self.load_external(dir)
|
12
|
+
Dir.glob("#{dir}/*.rb").each do |file|
|
13
|
+
require file
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/ssh'
|
4
|
+
require 'net/ssh/gateway'
|
5
|
+
|
6
|
+
module ActiveRecordMysqlRepl
|
7
|
+
module SSHTunnel
|
8
|
+
EPHEMERAL_PORT = 0
|
9
|
+
|
10
|
+
def self.tunnel(db_config)
|
11
|
+
return yield(db_config.port) if block_given? unless db_config.bastion
|
12
|
+
|
13
|
+
puts "Establishing ssh tunnel to #{db_config.remote_host}:#{db_config.port} via #{db_config.ssh_user}#{db_config.bastion}".gray
|
14
|
+
|
15
|
+
gateway = Net::SSH::Gateway.new(db_config.bastion, db_config.ssh_user)
|
16
|
+
gateway.open(db_config.remote_host, db_config.port) do |port|
|
17
|
+
yield(port) if block_given?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "active_record_mysql_repl/version"
|
4
|
+
require_relative 'active_record_mysql_repl/config'
|
5
|
+
require_relative 'active_record_mysql_repl/cli'
|
6
|
+
require_relative 'active_record_mysql_repl/database'
|
7
|
+
require_relative 'active_record_mysql_repl/ssh_tunnel'
|
8
|
+
require_relative 'active_record_mysql_repl/version'
|
9
|
+
# extensions should be loaded after database connection is established
|
10
|
+
# require_relative 'active_record_mysql_repl/extensions'
|
11
|
+
|
12
|
+
module ActiveRecordMysqlRepl; end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/nogahighland/active_record_mysql_repl/refs/heads/main/json_schemas/army_schema.json
|
2
|
+
|
3
|
+
database_config: ./databases.sample.yml
|
4
|
+
associations: ./associations.sample.yml
|
5
|
+
extensions_dir:
|
6
|
+
pryrc: ./.pryrc.sample
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/nogahighland/active_record_mysql_repl/refs/heads/main/json_schemas/association_schema.json
|
2
|
+
|
3
|
+
test:
|
4
|
+
users:
|
5
|
+
belongs_to:
|
6
|
+
- name: profile
|
7
|
+
foreign_key: id
|
8
|
+
class_name: UserProfile
|
9
|
+
|
10
|
+
ignore_columns:
|
11
|
+
user:
|
12
|
+
- login_id
|
13
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/nogahighland/active_record_mysql_repl/refs/heads/main/json_schema/databases_schema.json
|
2
|
+
|
3
|
+
test:
|
4
|
+
remote_host: 127.0.0.1
|
5
|
+
database: test
|
6
|
+
port: 3306
|
7
|
+
user: root
|
8
|
+
password: root
|
9
|
+
prompt_color: cyan
|