erdb 1.0.2 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 290243fdf73ae67ca7e00a56eeb0ca349151d91a790b18239f48b21a21943ab4
4
- data.tar.gz: 634235ef5e172d15966d58718199c3b990d0a056c56524a68c6b476fd5d90356
3
+ metadata.gz: fb43b27d324884db21ead2653426adbeba09a183733ee33bb4675b601d49d40c
4
+ data.tar.gz: 32e01870dabcc268910d912afa9d4b5341afd5a3f3b7afed38fc1aea3c1b6bb9
5
5
  SHA512:
6
- metadata.gz: c96446fa6475e30215879bc5bf1a88fbff41740e99253f159df50142ce1c7702c28733ea6569fff70402140e71e9422991c5788f4ff1be4cf9c5fc434d4bb798
7
- data.tar.gz: 0c8070088d064b59a71ee20f755e1407f1f5c382b616400b73dadb3b8badb25f1fff7b53c346348446fb78849665c6e18227b0300786614084aa08dc45d04b80
6
+ metadata.gz: a220a81b743ff5c8ccc3bdec373d788ef6ef64fac8f8db2eebfb0ab95b96d13be351e74340f91534165e9a1d1fa3a8f9b5d5730f34073e8353ec929fb7ab13cb
7
+ data.tar.gz: a445779b1ac54a74c5acc673f5bf3905868a880200843e413df643091da05ac8eb407ae2faaef421b131dce349eaeb5c5e969cb11e1aedd68ca79932c60de114
data/CHANGES.md CHANGED
@@ -1,3 +1,18 @@
1
+ ### 1.1.0
2
+
3
+ - Use `Thor` to make cli.
4
+ - Support `firefox` for automation.
5
+ - Fix incorrect syntax for many-to-many relation in Azimutt.
6
+ - Add path completion in cli.
7
+ - Rename `join_table` to `junction_table`.
8
+ - Add new commands in cli.
9
+ - `erdb help` to show help.
10
+ - `erdb version` to show version.
11
+ - Add new options in cli.
12
+ - `--browser=BROWSER` to specify browser.
13
+ - `--junction-table` to show junction table.
14
+ - `--no-junction-table` to hide junction table.
15
+
1
16
  ### 1.0.1
2
17
 
3
18
  - Prioritize Interruption over Error cuz ActiveRecord is lazy load.
@@ -33,7 +33,7 @@ module ERDB
33
33
  # [
34
34
  # {
35
35
  # name: "table_name",
36
- # is_join_table: false,
36
+ # is_junction_table: false,
37
37
  # columns: [{ name: "column_name", type: "column_type" }, ...],
38
38
  # relations: [
39
39
  # {
@@ -53,7 +53,7 @@ module ERDB
53
53
  end
54
54
 
55
55
  hash = { name: table, columns: columns, relations: relations }
56
- hash[:is_join_table] = join_table?(hash)
56
+ hash[:is_junction_table] = junction_table?(hash)
57
57
  hash
58
58
  end
59
59
  end
@@ -69,12 +69,12 @@ module ERDB
69
69
  private
70
70
 
71
71
  #
72
- # Check current table is a join table or not.
72
+ # Check current table is a junction table or not.
73
73
  #
74
74
  # @param table [Hash] The table to check.
75
- # @return [Boolean] True if the table is a join table, false otherwise.
75
+ # @return [Boolean] True if the table is a junction table, false otherwise.
76
76
  #
77
- def join_table?(table)
77
+ def junction_table?(table)
78
78
  relations = table[:relations]
79
79
 
80
80
  # remove data like id, created_at, updated_at
data/lib/erdb/cli.rb CHANGED
@@ -1,183 +1,218 @@
1
+ require "thor"
1
2
  require "json"
2
3
 
3
4
  module ERDB
4
- class Cli
5
- class << self
6
- #
7
- # Start the CLI.
8
- # @param [Array] args
9
- # @return [void]
10
- #
11
- def start(_args)
12
- ARGV.clear
5
+ class Cli < Thor
6
+ class_option :browser, type: :string, default: :chrome, enum: %w[chrome firefox], desc: "Browser to generate ERD"
7
+ class_option :junction_table, type: :boolean, default: true, desc: "Display junction table in the diagram"
13
8
 
14
- welcome
9
+ desc "create", "Create ERD from database", hide: true
10
+ def create
11
+ ERDB.default_browser = options[:browser].to_sym
12
+ ERDB.show_junction_table = options[:junction_table]
15
13
 
16
- db = select_database
14
+ ARGV.clear
17
15
 
18
- erd_builder = select_diagram_builder
16
+ say Messages.welcome
19
17
 
20
- ERDB.show_join_table = ask_yes_no("\nDo you want to display join tables? (Y/n)", true)
18
+ db = select_database
21
19
 
22
- db.connect
20
+ erd_builder = select_diagram_builder
23
21
 
24
- erd_builder.create(db.to_erdb)
25
- rescue Interrupt
26
- puts "\n\nThank you for using ERDB!"
27
- exit 0
28
- rescue ActiveRecord::NoDatabaseError
29
- puts "\nError: Database not found."
30
- puts "Please make sure the database exists."
31
- exit 1
32
- rescue StandardError => e
33
- puts "Error: #{e.message}"
34
- exit 1
35
- end
22
+ db.connect
36
23
 
37
- private
24
+ erd_builder.create(db.to_erdb)
25
+ rescue Interrupt
26
+ say "\n\nThank you for using ERDB!", :blue
27
+ exit 0
28
+ rescue ActiveRecord::NoDatabaseError
29
+ say "\nError: Database not found.", :red
30
+ say "Please make sure the database exists."
31
+ exit 1
32
+ rescue StandardError => e
33
+ say "Error: #{e.message}", :red
34
+ exit 1
35
+ end
38
36
 
39
- #
40
- # Ask user which database to use.
41
- # @return [Db]
42
- #
43
- def select_database
44
- data = {
45
- sqlite3: { name: "SQLite", gem: "sqlite3" },
46
- postgresql: { name: "PostgreSQL(Gem 'pg' must be installed)", gem: "pg" },
47
- mysql2: { name: "MySQL(Gem 'mysql2' must be installed)", gem: "mysql2" }
48
- }
37
+ default_task :create
49
38
 
50
- puts "Select a database adapter:"
39
+ desc "-v --version", "Show version"
40
+ map %w[-v --version] => :version
41
+ def version
42
+ say "ERDB #{ERDB::VERSION}"
43
+ end
51
44
 
52
- data.each_with_index do |v, i|
53
- puts "#{i + 1}. #{v[1][:name]}"
54
- end
45
+ desc "-h --help", "Show help"
46
+ map %w[-h --help] => :help
47
+ def help
48
+ say Messages.help
49
+ super
50
+ end
55
51
 
56
- response = ask_number(1, data.size)
52
+ def self.exit_on_failure?
53
+ true
54
+ end
57
55
 
58
- adapter = data.keys[response.to_i - 1].to_sym
56
+ private
59
57
 
60
- # check if the gem is installed
61
- # I don't want to include the gem in the gemspec file
62
- # cuz it's dependencies are too big and depend on the native library
63
- # I only include sqlite3 gem cuz it's small and doesn't have any dependencies
64
- gem = data[adapter][:gem]
58
+ #
59
+ # Ask user which database to use.
60
+ # @return [Db]
61
+ #
62
+ def select_database
63
+ data = {
64
+ sqlite3: { name: "SQLite", gem: "sqlite3" },
65
+ postgresql: { name: "PostgreSQL(Gem 'pg' must be installed)", gem: "pg" },
66
+ mysql2: { name: "MySQL(Gem 'mysql2' must be installed)", gem: "mysql2" }
67
+ }
65
68
 
66
- begin
67
- require gem
68
- rescue LoadError
69
- puts "\nError: '#{gem}' gem is not installed."
70
- puts "Please install the gem '#{gem}' first."
71
- puts "Run 'gem install #{gem}' to install the gem."
72
- exit 1
73
- end
69
+ say "Select a database adapter:"
74
70
 
75
- database = if adapter == :sqlite3
76
- ask_file "\nEnter the path to the database file:"
77
- else
78
- ask "\nEnter the database connection string:"
79
- end
71
+ data.each_with_index do |v, i|
72
+ say "#{i + 1}. #{v[1][:name]}"
73
+ end
74
+
75
+ response = ask_number(1, data.size, ">")
76
+
77
+ adapter = data.keys[response.to_i - 1].to_sym
80
78
 
81
- return SQL.new(adapter, database) if %i[sqlite3 mysql2 postgresql].include?(adapter)
79
+ # check if the gem is installed
80
+ # I don't want to include the gem in the gemspec file
81
+ # cuz it's dependencies are too big and depend on the native library
82
+ # I only include sqlite3 gem cuz it's small and doesn't have any dependencies
83
+ gem = data[adapter][:gem]
82
84
 
83
- raise "Invalid database adapter"
85
+ begin
86
+ require gem
87
+ rescue LoadError
88
+ say "\nError: '#{gem}' gem is not installed."
89
+ say "Please install the gem '#{gem}' first."
90
+ say "Run 'gem install #{gem}' to install the gem."
91
+ exit 1
84
92
  end
85
93
 
86
- #
87
- # Select a diagram builder.
88
- # @return [ERDProvider]
89
- #
90
- def select_diagram_builder
91
- data = [Azimutt, DBDiagram]
94
+ database = if adapter == :sqlite3
95
+ say "\nEnter the path to the database file:"
96
+ ask_file "> "
97
+ else
98
+ ask "\nEnter the database connection string:\n>"
99
+ end
92
100
 
93
- puts "\nSelect a diagram builder:"
101
+ return SQL.new(adapter, database) if %i[sqlite3 mysql2 postgresql].include?(adapter)
94
102
 
95
- data.each_with_index do |v, i|
96
- puts "#{i + 1}. #{v.name.split('::').last}"
97
- end
103
+ raise "Invalid database adapter"
104
+ end
98
105
 
99
- response = ask_number(1, data.size)
106
+ #
107
+ # Select a diagram builder.
108
+ # @return [ERDProvider]
109
+ #
110
+ def select_diagram_builder
111
+ data = [Azimutt, DBDiagram]
100
112
 
101
- data[response.to_i - 1]
102
- end
113
+ say "\nSelect a diagram builder:"
103
114
 
104
- #
105
- # Display welcome message.
106
- #
107
- def welcome
108
- puts <<~WELCOME
109
- .----------------. .----------------. .----------------. .----------------.
110
- | .--------------. || .--------------. || .--------------. || .--------------. |
111
- | | _________ | || | _______ | || | ________ | || | ______ | |
112
- | | |_ ___ | | || | |_ __ \\ | || | |_ ___ `. | || | |_ _ \\ | |
113
- | | | |_ \\_| | || | | |__) | | || | | | `. \\ | || | | |_) | | |
114
- | | | _| _ | || | | __ / | || | | | | | | || | | __'. | |
115
- | | _| |___/ | | || | _| | \\ \\_ | || | _| |___.' / | || | _| |__) | | |
116
- | | |_________| | || | |____| |___| | || | |________.' | || | |_______/ | |
117
- | | | || | | || | | || | | |
118
- | '--------------' || '--------------' || '--------------' || '--------------' |
119
- '----------------' '----------------' '----------------' '----------------'
120
-
121
- ERDB is an automate tool to generate Entity-Relationship Diagrams from a database.
122
- It use Azimutt and DBDiagram to generate the diagrams.
123
-
124
- WELCOME
115
+ data.each_with_index do |v, i|
116
+ say "#{i + 1}. #{v.name.split('::').last}"
125
117
  end
126
118
 
127
- #
128
- # Ask a question to the user.
129
- # @param [String] question
130
- # @return [String]
131
- #
132
- def ask(question = nil)
133
- puts question if question
134
- print "> "
135
- gets.chomp
136
- end
119
+ response = ask_number(1, data.size, ">")
137
120
 
138
- #
139
- # Ask a number input to the user.
140
- # @param [Integer] min
141
- # @param [Integer] max
142
- # @return [Integer]
143
- #
144
- def ask_number(min, max)
145
- response = nil
146
- until response.to_i.between?(min, max)
147
- response = ask
148
- unless response.match(/^\d+$/)
149
- puts "Please enter a number"
150
- response = nil
151
- end
152
-
153
- unless response.to_i.between?(min, max)
154
- puts "Please enter a number between #{min} and #{max}"
155
- response = nil
156
- end
157
- end
158
- response
159
- end
121
+ data[response.to_i - 1]
122
+ end
160
123
 
161
- def ask_file(question)
162
- loop do
163
- file = ask question
164
- return file if File.exist?(file)
124
+ #
125
+ # Ask a number input to the user.
126
+ # @param [Integer] min
127
+ # @param [Integer] max
128
+ # @param [String] question
129
+ # @return [Integer]
130
+ #
131
+ def ask_number(min, max, question)
132
+ response = nil
133
+ until response.to_i.between?(min, max)
134
+ response = ask question
135
+ unless response.match(/^\d+$/)
136
+ say "Please enter a number"
137
+ response = nil
138
+ end
165
139
 
166
- puts "Invalid file path"
140
+ unless response.to_i.between?(min, max)
141
+ say "Please enter a number between #{min} and #{max}"
142
+ response = nil
167
143
  end
168
144
  end
145
+ response
146
+ end
169
147
 
170
- #
171
- # Ask a yes/no question to the user.
172
- # @param [String] question
173
- # @param [Boolean] default
174
- # @return [Boolean]
175
- #
176
- def ask_yes_no(question, default = true)
177
- result = ask question
178
-
179
- result.empty? ? default : result.downcase == "y"
148
+ #
149
+ # Ask a file path to the user.
150
+ # @param [String] question
151
+ # @return [String]
152
+ #
153
+ def ask_file(question)
154
+ loop do
155
+ file = ask question
156
+ return file if File.exist?(file)
157
+
158
+ say "File not found", :red
180
159
  end
181
160
  end
161
+
162
+ #
163
+ # Ask a yes/no question to the user.
164
+ # @param [String] question
165
+ # @param [Boolean] default
166
+ # @return [Boolean]
167
+ #
168
+ def ask_yes_no(question, default = true)
169
+ result = ask question
170
+
171
+ result.empty? ? default : result.downcase == "y"
172
+ end
173
+ end
174
+
175
+ #
176
+ # All the messages used in the CLI
177
+ #
178
+ class Messages
179
+ def self.welcome
180
+ <<~WELCOME
181
+ .----------------. .----------------. .----------------. .----------------.
182
+ | .--------------. || .--------------. || .--------------. || .--------------. |
183
+ | | _________ | || | _______ | || | ________ | || | ______ | |
184
+ | | |_ ___ | | || | |_ __ \\ | || | |_ ___ `. | || | |_ _ \\ | |
185
+ | | | |_ \\_| | || | | |__) | | || | | | `. \\ | || | | |_) | | |
186
+ | | | _| _ | || | | __ / | || | | | | | | || | | __'. | |
187
+ | | _| |___/ | | || | _| | \\ \\_ | || | _| |___.' / | || | _| |__) | | |
188
+ | | |_________| | || | |____| |___| | || | |________.' | || | |_______/ | |
189
+ | | | || | | || | | || | | |
190
+ | '--------------' || '--------------' || '--------------' || '--------------' |
191
+ '----------------' '----------------' '----------------' '----------------'
192
+ #{about}
193
+ ERDB will use chrome as the default browser to automate the process.
194
+ Use 'erdb --help' to see the available options.
195
+
196
+ WELCOME
197
+ end
198
+
199
+ def self.help
200
+ <<~HELP
201
+ #{about}
202
+ Usage:
203
+ erdb [options]
204
+
205
+ Examples:
206
+ erdb --browser=firefox --no-junction-table
207
+
208
+ HELP
209
+ end
210
+
211
+ def self.about
212
+ <<~ABOUT
213
+ ERDB is an automate tool to generate Entity-Relationship Diagrams from a database.
214
+ It use Azimutt and DBDiagram to generate the diagrams.
215
+ ABOUT
216
+ end
182
217
  end
183
218
  end
@@ -24,8 +24,6 @@ module ERDB
24
24
  # @return [void]
25
25
  #
26
26
  def start_automation(data)
27
- browser = Watir::Browser.new(ERDB.default_browser)
28
-
29
27
  browser.goto "https://azimutt.app/new"
30
28
 
31
29
  browser.span(text: "From scratch (db design)").click
@@ -55,14 +53,7 @@ module ERDB
55
53
 
56
54
  btn.click if btn.exists?
57
55
 
58
- puts "Enter 'q' to exit."
59
-
60
- loop do
61
- v = gets.chomp
62
- break if v == "q"
63
- end
64
-
65
- browser.close
56
+ wait_to_quit
66
57
  end
67
58
 
68
59
  #
@@ -74,7 +65,7 @@ module ERDB
74
65
  def to_aml(tables)
75
66
  str = ""
76
67
  tables.each_with_index do |table, i|
77
- if table[:is_join_table] && !ERDB.show_join_table?
68
+ if table[:is_junction_table] && !ERDB.show_junction_table
78
69
  str += to_many_to_many_str(table)
79
70
  next
80
71
  end
@@ -123,7 +114,7 @@ module ERDB
123
114
  relations.each do |other|
124
115
  next if relation == other
125
116
 
126
- str += "\nfk: #{relation} -> #{other}\n"
117
+ str += "\nfk #{relation} -> #{other}\n"
127
118
  end
128
119
  end
129
120
 
@@ -24,8 +24,6 @@ module ERDB
24
24
  # @return [void]
25
25
  #
26
26
  def start_automation(data)
27
- browser = Watir::Browser.new(ERDB.default_browser)
28
-
29
27
  browser.goto "https://dbdiagram.io/d"
30
28
 
31
29
  editor = browser.div(class: "view-lines monaco-mouse-cursor-text")
@@ -45,14 +43,7 @@ module ERDB
45
43
 
46
44
  Clipboard.copy(old_clipboard)
47
45
 
48
- puts "Enter 'q' to exit."
49
-
50
- loop do
51
- v = gets.chomp
52
- break if v == "q"
53
- end
54
-
55
- browser.close
46
+ wait_to_quit
56
47
  end
57
48
 
58
49
  #
@@ -64,7 +55,7 @@ module ERDB
64
55
  def to_dbdiagram_format(tables)
65
56
  str = ""
66
57
  tables.each_with_index do |table, i|
67
- if table[:is_join_table] && !ERDB.show_join_table?
58
+ if table[:is_junction_table] && !ERDB.show_junction_table
68
59
  str += to_many_to_many_str(table)
69
60
  next
70
61
  end
@@ -1,11 +1,38 @@
1
+ require "watir"
2
+
1
3
  module ERDB
2
4
  class ERDProvider
3
- #
4
- # Create a new ER Diagram.
5
- # @param [Hash] tables
6
- #
7
- def create(tables)
8
- raise NotImplementedError
5
+ class << self
6
+ #
7
+ # Create a new ER Diagram.
8
+ # @param [Hash] tables
9
+ #
10
+ def create(tables)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ private
15
+
16
+ #
17
+ # @return [Watir::Browser]
18
+ #
19
+ def browser
20
+ @browser ||= Watir::Browser.new(ERDB.default_browser)
21
+ end
22
+
23
+ #
24
+ # Wait for user to enter 'q' to quit.
25
+ #
26
+ def wait_to_quit
27
+ puts "Enter 'q' to exit."
28
+
29
+ loop do
30
+ v = gets.chomp
31
+ break if v == "q"
32
+ end
33
+
34
+ browser.close
35
+ end
9
36
  end
10
37
  end
11
38
  end
data/lib/erdb/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ERDB
2
- VERSION = "1.0.2".freeze
2
+ VERSION = "1.1.0".freeze
3
3
  end
data/lib/erdb.rb CHANGED
@@ -9,7 +9,7 @@ module ERDB
9
9
  autoload :DBDiagram, File.expand_path("erdb/providers/dbdiagram", __dir__)
10
10
 
11
11
  class << self
12
- attr_writer :default_timeout, :default_browser, :show_join_table
12
+ attr_writer :default_timeout, :default_browser, :show_junction_table
13
13
 
14
14
  #
15
15
  # Default wait time for wait methods.
@@ -28,11 +28,11 @@ module ERDB
28
28
  end
29
29
 
30
30
  #
31
- # Show join table in the diagram.
31
+ # Show junction table in the diagram.
32
32
  # @return [Boolean]
33
33
  #
34
- def show_join_table?
35
- @show_join_table.nil? ? true : @show_join_table
34
+ def show_junction_table
35
+ @show_junction_table.nil? ? true : @show_junction_table
36
36
  end
37
37
  end
38
38
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wai Yan Phyo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-02 00:00:00.000000000 Z
11
+ date: 2023-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.2'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: watir
57
71
  requirement: !ruby/object:Gem::Requirement