active_record_mysql_repl 0.1.4 → 0.1.5
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/README.md +135 -10
- data/lib/active_record_mysql_repl/cli/erd.rb +1 -1
- data/lib/active_record_mysql_repl/cli/main.rb +14 -14
- data/lib/active_record_mysql_repl/cli/zsh_completion.rb +23 -23
- data/lib/active_record_mysql_repl/cli.rb +4 -4
- data/lib/active_record_mysql_repl/config.rb +6 -6
- data/lib/active_record_mysql_repl/database/association.rb +9 -9
- data/lib/active_record_mysql_repl/database/configs.rb +2 -2
- data/lib/active_record_mysql_repl/database/connection.rb +11 -13
- data/lib/active_record_mysql_repl/database/loader.rb +9 -10
- data/lib/active_record_mysql_repl/database.rb +4 -4
- data/lib/active_record_mysql_repl/extensions/active_record.rb +8 -9
- data/lib/active_record_mysql_repl/extensions/global.rb +0 -1
- data/lib/active_record_mysql_repl/extensions/hash.rb +8 -5
- data/lib/active_record_mysql_repl/extensions/object.rb +53 -54
- data/lib/active_record_mysql_repl/extensions/tabler.rb +2 -3
- data/lib/active_record_mysql_repl/extensions.rb +5 -6
- data/lib/active_record_mysql_repl/ssh_tunnel.rb +5 -3
- data/lib/active_record_mysql_repl/version.rb +1 -1
- data/lib/active_record_mysql_repl.rb +4 -5
- data/sample_config/.army.sample/associations.sample.yml +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c3f840dac882b20496edf1781709416bf8e81f4ed801683892b6a54431cbd4c
|
4
|
+
data.tar.gz: 5f947005433e288cc043e0375849d1ff51e335b863e81fbf890cb66fc3056658
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae1b915b0668004ac07f597da9fae0a9b35b0103699e7e224fcb3b25ce2924700818850de6fb70fc5ecc3fe6d99ead005c1471bcb7f89744f942567b3092c66d
|
7
|
+
data.tar.gz: 37091f71892c3cf3c1fdf62aed7202ab44f72ad5cb87bf1f2e8f99bff52c0f007434e226d565229f03ed312f42e9ce23d6d7b27904e72b27d272ea2c3d29cba3
|
data/README.md
CHANGED
@@ -383,7 +383,6 @@ https://github.com/nogahighland/active_record_mysql_repl/blob/main/sample_config
|
|
383
383
|
<details><summary>output:</summary>
|
384
384
|
|
385
385
|
```rb
|
386
|
-
|
387
386
|
D, [2024-12-12T18:23:41.382606 #2816] DEBUG -- : Order Load (8.6ms) SELECT `orders`.* FROM `orders` ORDER BY `orders`.`id` DESC LIMIT 1
|
388
387
|
D, [2024-12-12T18:23:41.389954 #2816] DEBUG -- : User Load (2.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = '1' LIMIT 1
|
389
388
|
D, [2024-12-12T18:23:41.392060 #2816] DEBUG -- : UserProfile Load (1.8ms) SELECT `user_profiles`.* FROM `user_profiles` WHERE `user_profiles`.`id` = '1' LIMIT 1
|
@@ -397,22 +396,148 @@ D, [2024-12-12T18:23:41.392060 #2816] DEBUG -- : UserProfile Load (1.8ms) SEL
|
|
397
396
|
|
398
397
|
---
|
399
398
|
|
399
|
+
Even though `users.login_id` column exists, because the column is not an external key it should be ignored from the association.
|
400
|
+
The column is ignored because the custom association is defined here and `User#login` causes a `NoMethodError`
|
400
401
|
|
402
|
+
https://github.com/nogahighland/active_record_mysql_repl/blob/f52f04770425723f632220bb8031bf4b402020a6/sample_config/.army.sample/associations.sample.yml#L10-L13
|
401
403
|
|
402
404
|
```rb
|
403
|
-
[
|
405
|
+
[1] test(main)> User.last.login
|
404
406
|
```
|
405
407
|
|
406
408
|
<details><summary>output:</summary>
|
407
409
|
|
408
410
|
```rb
|
409
|
-
D, [2024-12-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
411
|
+
D, [2024-12-12T22:17:49.291618 #37808] DEBUG -- : User Load (2.5ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
|
412
|
+
NoMethodError: undefined method `login' for an instance of User
|
413
|
+
from /Users/hiroki.kishi/develop/private/active_record_mysql_repl/vendor/bundle/ruby/3.3.0/gems/activemodel-7.2.2/lib/active_model/attribute_methods.rb:512:in `method_missing'
|
414
|
+
```
|
415
|
+
|
416
|
+
</details>
|
417
|
+
|
418
|
+
---
|
419
|
+
|
420
|
+
You can use transaction by globally defined `transaction`.
|
421
|
+
|
422
|
+
```rb
|
423
|
+
[7] test(main)> u2 = User.new(id:2, profile_id: up2, login_id: 'login2')
|
424
|
+
[9] test(main)> up2 = UserProfile.new(id:2, name: 'user2');
|
425
|
+
[10] test(main)> transaction { [u2, up2].map(&:save) }
|
426
|
+
```
|
427
|
+
|
428
|
+
<details><summary>output:</summary>
|
429
|
+
|
430
|
+
```sql
|
431
|
+
[9] test(main)> up2 = UserProfile.new(id:2, name: 'user2');
|
432
|
+
[10] test(main)> transaction { [u2, up2].map(&:save) }
|
433
|
+
D, [2024-12-12T22:25:32.266790 #37808] DEBUG -- : TRANSACTION (2.2ms) BEGIN
|
434
|
+
D, [2024-12-12T22:25:32.272857 #37808] DEBUG -- : User Create (8.3ms) INSERT INTO `users` (`id`, `login_id`) VALUES ('2', 'login2')
|
435
|
+
D, [2024-12-12T22:25:32.275213 #37808] DEBUG -- : UserProfile Create (2.1ms) INSERT INTO `user_profiles` (`id`, `name`) VALUES ('2', 'user2')
|
436
|
+
D, [2024-12-12T22:25:32.281765 #37808] DEBUG -- : TRANSACTION (6.4ms) COMMIT
|
437
|
+
[
|
438
|
+
[0] true,
|
439
|
+
[1] true
|
440
|
+
]
|
441
|
+
```
|
442
|
+
|
443
|
+
</details>
|
444
|
+
|
445
|
+
---
|
446
|
+
|
447
|
+
The following example shows the following steps:
|
448
|
+
|
449
|
+
1. Find the users with id 1 and 2
|
450
|
+
2. Change the login_id of the users
|
451
|
+
3. Show the changes
|
452
|
+
4. Save the changes in a transaction
|
453
|
+
|
454
|
+
```rb
|
455
|
+
[11] test(main)> u1, u2 = User.find([1, 2])
|
456
|
+
```
|
457
|
+
|
458
|
+
<details><summary>Output:</summary>
|
459
|
+
|
460
|
+
```rb
|
461
|
+
D, [2024-12-12T22:27:55.979311 #37808] DEBUG -- : User Load (11.7ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN ('1', '2')
|
462
|
+
[
|
463
|
+
[0] #<User:0x0000000144b325a0> {
|
464
|
+
:id => "1",
|
465
|
+
:login_id => "user1",
|
466
|
+
:profile_id => 1
|
467
|
+
},
|
468
|
+
[1] #<User:0x0000000144b32460> {
|
469
|
+
:id => "2",
|
470
|
+
:login_id => "login2",
|
471
|
+
:profile_id => nil
|
472
|
+
}
|
473
|
+
]
|
474
|
+
```
|
475
|
+
|
476
|
+
</details>
|
477
|
+
|
478
|
+
```rb
|
479
|
+
[12] test(main)> [u1, u2].each { |u| u.login_id = "login_id#{u.id}" }
|
480
|
+
```
|
481
|
+
|
482
|
+
<details><summary>Output:</summary>
|
483
|
+
|
484
|
+
```rb
|
485
|
+
[
|
486
|
+
[0] #<User:0x0000000144b325a0> {
|
487
|
+
:id => "1",
|
488
|
+
:login_id => "login_id1",
|
489
|
+
:profile_id => 1
|
490
|
+
},
|
491
|
+
[1] #<User:0x0000000144b32460> {
|
492
|
+
:id => "2",
|
493
|
+
:login_id => "login_id2",
|
494
|
+
:profile_id => nil
|
495
|
+
}
|
496
|
+
]
|
497
|
+
```
|
498
|
+
|
499
|
+
</details>
|
500
|
+
|
501
|
+
```rb
|
502
|
+
[13] test(main)> [u1, u2].map(&:changes)
|
503
|
+
```
|
504
|
+
|
505
|
+
<details><summary>Output:</summary>
|
506
|
+
|
507
|
+
```rb
|
508
|
+
[
|
509
|
+
[0] {
|
510
|
+
"login_id" => [
|
511
|
+
[0] "user1",
|
512
|
+
[1] "login_id1"
|
513
|
+
]
|
514
|
+
},
|
515
|
+
[1] {
|
516
|
+
"login_id" => [
|
517
|
+
[0] "login2",
|
518
|
+
[1] "login_id2"
|
519
|
+
]
|
520
|
+
}
|
521
|
+
]
|
522
|
+
```
|
523
|
+
|
524
|
+
</details>
|
525
|
+
|
526
|
+
```rb
|
527
|
+
[14] test(main)> transaction { [u1, u2].map(&:save) }
|
528
|
+
```
|
529
|
+
|
530
|
+
<details><summary>Output:</summary>
|
531
|
+
|
532
|
+
```rb
|
533
|
+
D, [2024-12-12T22:29:14.179176 #37808] DEBUG -- : TRANSACTION (1.2ms) BEGIN
|
534
|
+
D, [2024-12-12T22:29:14.188392 #37808] DEBUG -- : User Update (9.1ms) UPDATE `users` SET `users`.`login_id` = 'login_id1' WHERE `users`.`id` = '1'
|
535
|
+
D, [2024-12-12T22:29:14.191429 #37808] DEBUG -- : User Update (2.6ms) UPDATE `users` SET `users`.`login_id` = 'login_id2' WHERE `users`.`id` = '2'
|
536
|
+
D, [2024-12-12T22:29:14.196802 #37808] DEBUG -- : TRANSACTION (5.2ms) COMMIT
|
537
|
+
[
|
538
|
+
[0] true,
|
539
|
+
[1] true
|
540
|
+
]
|
416
541
|
```
|
417
542
|
|
418
543
|
</details>
|
@@ -600,7 +725,7 @@ D, [2024-12-11T23:49:22.406631 #96381] DEBUG -- : (1.3ms) select 1 as num
|
|
600
725
|
You can define your own extension script. For example `.upcase_name` is defined on `UserProfile` by the sample extension which is specified in the `.army.sample.yml` file.
|
601
726
|
|
602
727
|
- https://github.com/nogahighland/active_record_mysql_repl/blob/9f7c91774b176e1204ed434dad2867721982c660/sample_config/.army.sample.yml#L5
|
603
|
-
- https://github.com/nogahighland/active_record_mysql_repl/blob/
|
728
|
+
- https://github.com/nogahighland/active_record_mysql_repl/blob/acaa44684ea4fd135225580431417d6ccd41a988/sample_config/.army.sample/extensions/hello.rb#L1-L9
|
604
729
|
|
605
730
|
```rb
|
606
731
|
[3] test(main)> UserProfile.last.upcase_name
|
@@ -1,10 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require "pry"
|
4
|
+
require "colorize"
|
5
|
+
require "active_support"
|
6
|
+
require "active_support/core_ext"
|
7
|
+
require "fileutils"
|
8
8
|
|
9
9
|
module ActiveRecordMysqlRepl
|
10
10
|
module CLI
|
@@ -15,34 +15,34 @@ module ActiveRecordMysqlRepl
|
|
15
15
|
return
|
16
16
|
end
|
17
17
|
|
18
|
-
if args[0] ==
|
18
|
+
if args[0] == "--zsh-completion"
|
19
19
|
puts ZshCompletion.generate
|
20
20
|
return
|
21
21
|
end
|
22
22
|
|
23
23
|
opts = CLI::Options.parse(args)
|
24
|
-
army_config = Config.load(opts[:c] || File.join(Dir.home,
|
24
|
+
army_config = Config.load(opts[:c] || File.join(Dir.home, ".army.yml"))
|
25
25
|
|
26
26
|
db_configs = Database::Configs.load(army_config.database_config)
|
27
27
|
db_config_key = opts[:d]
|
28
28
|
db_config = db_configs[db_config_key]
|
29
29
|
return puts db_configs.keys unless db_config
|
30
30
|
|
31
|
-
generate_erf = opts[:e] ==
|
31
|
+
generate_erf = opts[:e] == "erd"
|
32
32
|
|
33
33
|
SSHTunnel.tunnel(db_config) do |port|
|
34
34
|
Database::Connection.connect(db_config, port) do
|
35
35
|
Database::Loader.load_tables(db_config_key, army_config, port)
|
36
36
|
|
37
37
|
if generate_erf
|
38
|
-
require
|
38
|
+
require "rails_erd/diagram/graphviz"
|
39
39
|
puts "Generating ERD for #{db_config_key}_erd.pdf".gray
|
40
40
|
RailsERD::Diagram::Graphviz.create
|
41
|
-
FileUtils.mv(
|
42
|
-
|
41
|
+
FileUtils.mv("erd.pdf", "#{db_config_key}_erd.pdf")
|
42
|
+
next
|
43
43
|
end
|
44
44
|
|
45
|
-
require
|
45
|
+
require "active_record_mysql_repl/extensions"
|
46
46
|
if army_config.extensions_dir.present?
|
47
47
|
puts "Loading custom extensions from #{army_config.extensions_dir}".gray
|
48
48
|
Extensions.load_external(army_config.extensions_dir)
|
@@ -57,8 +57,8 @@ module ActiveRecordMysqlRepl
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def self.clear_screen
|
60
|
-
system(
|
61
|
-
system(
|
60
|
+
system("clear")
|
61
|
+
system("cls")
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -1,29 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
COMPLETION = <<~COMPLETION
|
4
|
-
#!/usr/bin/env bash
|
5
|
-
|
6
|
-
function _army() {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
}
|
21
|
-
|
22
|
-
function _database {
|
23
|
-
|
24
|
-
}
|
25
|
-
|
26
|
-
compdef _army army
|
4
|
+
#!/usr/bin/env bash
|
5
|
+
|
6
|
+
function _army() {
|
7
|
+
_arguments \
|
8
|
+
'-c[path to .armyrc file]:.armyrc:_files' \
|
9
|
+
'-d[Database name]:database:->database' \
|
10
|
+
'-e[output erd]:output erd:->erd'
|
11
|
+
|
12
|
+
case "$state" in
|
13
|
+
database)
|
14
|
+
slice=("${words[@]:1}")
|
15
|
+
_values 'Database name' $(_database ${slice})
|
16
|
+
;;
|
17
|
+
erd)
|
18
|
+
_values 'Generate ERD' erd ''
|
19
|
+
esac
|
20
|
+
}
|
21
|
+
|
22
|
+
function _database {
|
23
|
+
army ${@}
|
24
|
+
}
|
25
|
+
|
26
|
+
compdef _army army
|
27
27
|
COMPLETION
|
28
28
|
|
29
29
|
module ActiveRecordMysqlRepl
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
3
|
+
require_relative "cli/options"
|
4
|
+
require_relative "cli/main"
|
5
|
+
require_relative "cli/zsh_completion"
|
6
|
+
require_relative "cli/erd"
|
7
7
|
|
8
8
|
module ActiveRecordMysqlRepl
|
9
9
|
module CLI; end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "yaml"
|
4
4
|
|
5
5
|
module ActiveRecordMysqlRepl
|
6
6
|
class Config
|
@@ -9,7 +9,7 @@ module ActiveRecordMysqlRepl
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def database_config
|
12
|
-
raise
|
12
|
+
raise "database_config is not defined" unless @database_config
|
13
13
|
File.join(@army_config_dir, @database_config)
|
14
14
|
end
|
15
15
|
|
@@ -29,10 +29,10 @@ module ActiveRecordMysqlRepl
|
|
29
29
|
|
30
30
|
def initialize(path, config)
|
31
31
|
@army_config_dir = File.dirname(File.absolute_path(path))
|
32
|
-
@database_config = config[
|
33
|
-
@associations = config[
|
34
|
-
@extensions_dir = config[
|
35
|
-
@pryrc = config[
|
32
|
+
@database_config = config["database_config"]
|
33
|
+
@associations = config["associations"]
|
34
|
+
@extensions_dir = config["extensions_dir"]
|
35
|
+
@pryrc = config["pryrc"]
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -36,18 +36,18 @@ module ActiveRecordMysqlRepl
|
|
36
36
|
columns.each do |column|
|
37
37
|
next if association_setting.present? && association_settings.ignore_columns(table_name).include?(column.name)
|
38
38
|
|
39
|
-
associatable = column.name.gsub(/_id$/,
|
40
|
-
next if associatable.blank? || associatable ==
|
39
|
+
associatable = column.name.gsub(/_id$/, "") if column.name.end_with?("_id")
|
40
|
+
next if associatable.blank? || associatable == "class" # reserved word
|
41
41
|
|
42
|
-
if analyzed_tables.
|
42
|
+
if analyzed_tables.key?(associatable.pluralize)
|
43
43
|
table.belongs_to << associatable.singularize if associatable
|
44
44
|
analyzed_tables[associatable.pluralize].has_many << table_name.pluralize
|
45
45
|
else
|
46
|
-
associatable_table_name = associatable.split(
|
47
|
-
if analyzed_tables.
|
48
|
-
|
46
|
+
associatable_table_name = associatable.split("_").last
|
47
|
+
table.belongs_to << if analyzed_tables.key?(associatable_table_name.pluralize)
|
48
|
+
{name: associatable, class_name: associatable_table_name.classify, foreign_key: :id}
|
49
49
|
else
|
50
|
-
|
50
|
+
{name: associatable, class_name: table_name.singularize.classify, foreign_key: :id}
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -71,12 +71,12 @@ module ActiveRecordMysqlRepl
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def [](table)
|
74
|
-
table = (@association.keys - [
|
74
|
+
table = (@association.keys - ["ignore_columns"]) & [table]
|
75
75
|
@association[table.first] if table.present?
|
76
76
|
end
|
77
77
|
|
78
78
|
def ignore_columns(table)
|
79
|
-
@association.fetch(
|
79
|
+
@association.fetch("ignore_columns", {}).fetch(table, [])
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
@@ -27,7 +27,7 @@ module ActiveRecordMysqlRepl
|
|
27
27
|
private
|
28
28
|
|
29
29
|
class Config
|
30
|
-
attr_reader
|
30
|
+
attr_reader(*%i[
|
31
31
|
bastion
|
32
32
|
ssh_user
|
33
33
|
remote_host
|
@@ -36,7 +36,7 @@ module ActiveRecordMysqlRepl
|
|
36
36
|
user
|
37
37
|
password
|
38
38
|
prompt_color
|
39
|
-
]
|
39
|
+
])
|
40
40
|
|
41
41
|
def initialize(config)
|
42
42
|
@bastion = config["bastion"]
|
@@ -7,28 +7,26 @@ module ActiveRecordMysqlRepl
|
|
7
7
|
|
8
8
|
def self.connect(db_config, port)
|
9
9
|
conn = {
|
10
|
-
adapter:
|
11
|
-
host:
|
10
|
+
adapter: "mysql2",
|
11
|
+
host: "127.0.0.1",
|
12
12
|
port: port,
|
13
13
|
username: db_config.user,
|
14
14
|
password: db_config.password,
|
15
|
-
database: db_config.database
|
15
|
+
database: db_config.database
|
16
16
|
}
|
17
17
|
|
18
|
-
ActiveRecord::Base.logger = Logger.new(
|
19
|
-
|
18
|
+
ActiveRecord::Base.logger = Logger.new($stdout)
|
19
|
+
ActiveRecord::Base.establish_connection(conn)
|
20
20
|
|
21
21
|
puts "Ensureing connection to #{db_config.database} on port 127.0.0.1:#{port}".gray
|
22
22
|
|
23
23
|
tables = nil
|
24
|
-
(1..MAX_RETRY-1).to_a.each do |time|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
next
|
31
|
-
end
|
24
|
+
(1..MAX_RETRY - 1).to_a.each do |time|
|
25
|
+
tables = ActiveRecord::Base.connection.tables
|
26
|
+
rescue => e
|
27
|
+
puts "#{e}, Retry #{time}/#{MAX_RETRY}".red
|
28
|
+
sleep time * time
|
29
|
+
next
|
32
30
|
end
|
33
31
|
|
34
32
|
if tables.blank?
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "json"
|
4
|
+
require "active_record"
|
5
|
+
require "active_support/all"
|
6
6
|
|
7
7
|
module ActiveRecordMysqlRepl
|
8
8
|
module Database
|
@@ -13,13 +13,12 @@ module ActiveRecordMysqlRepl
|
|
13
13
|
tables = ActiveRecord::Base.connection.tables
|
14
14
|
association_settings = army_config.associations.present? ? Associations.load(army_config.associations) : {}
|
15
15
|
association_setting = association_settings[db_config_key]
|
16
|
-
analyzed_tables =
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
16
|
+
analyzed_tables = if association_setting.present?
|
17
|
+
Associations.analyze(tables, association_setting)
|
18
|
+
else
|
19
|
+
Associations.analyze(tables)
|
20
|
+
end
|
21
|
+
|
23
22
|
skipped = []
|
24
23
|
analyzed_tables.each do |analyzed_table|
|
25
24
|
# defer model definition for tables with `has_many: :xxx, through: xxx` associations
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
3
|
+
require_relative "database/configs"
|
4
|
+
require_relative "database/association"
|
5
|
+
require_relative "database/connection"
|
6
|
+
require_relative "database/loader"
|
7
7
|
|
8
8
|
module ActiveRecordMysqlRepl
|
9
9
|
module Database; end
|
@@ -5,29 +5,28 @@ module ActiverecordMysqlRepl
|
|
5
5
|
module ActiveRecord
|
6
6
|
module Base
|
7
7
|
def as(name = nil)
|
8
|
-
|
8
|
+
reflect_on_all_associations(name).map(&:name)
|
9
9
|
end
|
10
10
|
|
11
11
|
def describe
|
12
12
|
[
|
13
|
-
"# #{
|
14
|
-
exec_sql("DESCRIBE #{
|
15
|
-
exec_sql("SHOW INDEX FROM #{
|
13
|
+
"# #{table_name}",
|
14
|
+
exec_sql("DESCRIBE #{table_name}").tab(:h),
|
15
|
+
exec_sql("SHOW INDEX FROM #{table_name}").tab(:h)
|
16
16
|
].join("\n")
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
alias_method :desc, :describe
|
20
|
+
alias_method :d, :describe
|
21
21
|
|
22
22
|
def show_ddl
|
23
|
-
exec_sql("SHOW CREATE TABLE #{
|
23
|
+
exec_sql("SHOW CREATE TABLE #{table_name}")[0]["Create Table"]
|
24
24
|
end
|
25
25
|
|
26
|
-
|
26
|
+
alias_method :ddl, :show_ddl
|
27
27
|
end
|
28
28
|
|
29
29
|
::ActiveRecord::Base.extend(Base)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
|
-
|
@@ -4,17 +4,20 @@ module ActiverecordMysqlRepl
|
|
4
4
|
module Extensions
|
5
5
|
module Hash
|
6
6
|
def method_missing(name, *args)
|
7
|
-
if (
|
7
|
+
if (keys & [name, name.to_s]).present?
|
8
8
|
self[name] || self[name.to_s]
|
9
|
-
elsif name.to_s.end_with?(
|
9
|
+
elsif name.to_s.end_with?("=") && keys.include?(name.to_s[0..-2])
|
10
10
|
name = name[0..-2]
|
11
|
-
self[name] = args.first if
|
12
|
-
self[name.to_sym] = args.first if
|
11
|
+
self[name] = args.first if key?(name)
|
12
|
+
self[name.to_sym] = args.first if key?(name.to_sym)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
def respond_to_missing?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
16
20
|
::Hash.include(Hash)
|
17
21
|
end
|
18
22
|
end
|
19
23
|
end
|
20
|
-
|
@@ -1,28 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "terminal-table"
|
4
|
+
require "csv"
|
5
|
+
require "clipboard"
|
6
|
+
require "json"
|
7
7
|
|
8
8
|
module ActiverecordMysqlRepl
|
9
9
|
module Extensions
|
10
10
|
module Object
|
11
11
|
def tabulate(orientation = nil)
|
12
|
-
arr = if
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
arr = if is_a?(Array)
|
13
|
+
self
|
14
|
+
elsif is_a?(::ActiveRecord::Relation)
|
15
|
+
to_a
|
16
|
+
else
|
17
|
+
[self]
|
18
|
+
end
|
19
19
|
|
20
20
|
arr = arr.map do |e|
|
21
21
|
if e.is_a?(::ActiveRecord::Base)
|
22
|
-
values =
|
22
|
+
values = e.attributes.transform_values do |v|
|
23
23
|
next JSON.pretty_generate(v) if v.is_a?(Enumerable) && v.size > 0
|
24
|
-
next
|
25
|
-
next '""' if v ==
|
24
|
+
next "NULL" if v.nil?
|
25
|
+
next '""' if v == ""
|
26
26
|
v.to_s
|
27
27
|
end
|
28
28
|
next values
|
@@ -34,17 +34,17 @@ module ActiverecordMysqlRepl
|
|
34
34
|
raise "#{arr} contains Hashes with different keys".red unless arr.map(&:keys).uniq.size == 1
|
35
35
|
|
36
36
|
orientation = case orientation
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
48
|
|
49
49
|
t = Terminal::Table.new
|
50
50
|
|
@@ -54,7 +54,7 @@ module ActiverecordMysqlRepl
|
|
54
54
|
t.rows = arr.map(&:values)
|
55
55
|
|
56
56
|
when :vertical
|
57
|
-
t.headings = [
|
57
|
+
t.headings = ["Name", "Value"]
|
58
58
|
arr.each.with_index do |row, i|
|
59
59
|
row.each { |col, val| t.add_row [col, val] }
|
60
60
|
t.add_separator if i < arr.size - 1
|
@@ -64,25 +64,25 @@ module ActiverecordMysqlRepl
|
|
64
64
|
t.to_s
|
65
65
|
end
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
alias_method :tab, :tabulate
|
68
|
+
alias_method :table, :tabulate
|
69
|
+
alias_method :to_table, :tabulate
|
70
70
|
|
71
71
|
def csv(orientation = nil)
|
72
|
-
arr = if
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
72
|
+
arr = if is_a?(Array)
|
73
|
+
self
|
74
|
+
elsif is_a?(::ActiveRecord::Relation)
|
75
|
+
to_a
|
76
|
+
else
|
77
|
+
[self]
|
78
|
+
end
|
79
79
|
|
80
80
|
arr = arr.map do |e|
|
81
81
|
if e.is_a?(::ActiveRecord::Base)
|
82
|
-
values =
|
82
|
+
values = e.attributes.transform_values do |v|
|
83
83
|
next JSON.pretty_generate(v) if v.is_a?(Enumerable) && v.size > 0
|
84
|
-
next
|
85
|
-
next '""' if v ==
|
84
|
+
next "NULL" if v.nil?
|
85
|
+
next '""' if v == ""
|
86
86
|
v.to_s
|
87
87
|
end
|
88
88
|
next values
|
@@ -94,17 +94,17 @@ module ActiverecordMysqlRepl
|
|
94
94
|
raise "#{arr} contains Hashes with different keys".red unless arr.map(&:keys).uniq.size == 1
|
95
95
|
|
96
96
|
orientation = case orientation
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
108
|
|
109
109
|
CSV.generate do |csv|
|
110
110
|
case orientation
|
@@ -121,11 +121,11 @@ module ActiverecordMysqlRepl
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def copy
|
124
|
-
Clipboard.copy(
|
124
|
+
Clipboard.copy(to_s)
|
125
125
|
end
|
126
|
-
|
126
|
+
alias_method :cp, :copy
|
127
127
|
|
128
|
-
|
128
|
+
alias_method :j, :to_json
|
129
129
|
|
130
130
|
def jp
|
131
131
|
JSON.pretty_generate(JSON.parse(to_json))
|
@@ -135,4 +135,3 @@ module ActiverecordMysqlRepl
|
|
135
135
|
|
136
136
|
::Object.include(Extensions::Object)
|
137
137
|
end
|
138
|
-
|
@@ -13,11 +13,11 @@ module ActiverecordMysqlRepl
|
|
13
13
|
end
|
14
14
|
|
15
15
|
model.column_names.each do |column|
|
16
|
-
define_method("#{model.table_name.singularize}_by_#{column}") do
|
16
|
+
define_method(:"#{model.table_name.singularize}_by_#{column}") do
|
17
17
|
model.where(column => self)
|
18
18
|
end
|
19
19
|
|
20
|
-
define_method("#{model.table_name.singularize}_#{column}_like") do
|
20
|
+
define_method(:"#{model.table_name.singularize}_#{column}_like") do
|
21
21
|
model.where("#{column} like ?", "%#{self}%")
|
22
22
|
end
|
23
23
|
end
|
@@ -29,4 +29,3 @@ module ActiverecordMysqlRepl
|
|
29
29
|
Enumerable.include Tabler
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
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
8
|
|
9
9
|
module ActiveRecordMysqlRepl
|
10
10
|
module Extensions
|
@@ -15,4 +15,3 @@ module ActiveRecordMysqlRepl
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
@@ -1,14 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "net/ssh"
|
4
|
+
require "net/ssh/gateway"
|
5
5
|
|
6
6
|
module ActiveRecordMysqlRepl
|
7
7
|
module SSHTunnel
|
8
8
|
EPHEMERAL_PORT = 0
|
9
9
|
|
10
10
|
def self.tunnel(db_config)
|
11
|
-
|
11
|
+
unless db_config.bastion
|
12
|
+
return yield(db_config.port) if block_given?
|
13
|
+
end
|
12
14
|
|
13
15
|
puts "Establishing ssh tunnel to #{db_config.remote_host}:#{db_config.port} via #{db_config.ssh_user}#{db_config.bastion}".gray
|
14
16
|
|
@@ -1,11 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "active_record_mysql_repl/version"
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
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"
|
9
8
|
# extensions should be loaded after database connection is established
|
10
9
|
# require_relative 'active_record_mysql_repl/extensions'
|
11
10
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_mysql_repl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hiroki Kishi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.50.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: standard
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: activerecord
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|