erdb 1.0.1 → 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: 0307e8ec28a91d13810f3c57b8a2d018fdc9a150288cc52873bd6e0aa0c2bb30
4
- data.tar.gz: 4b4334a4317eb3fe8ccfd7342ebbccd958bd4232e3182797ffe68ea46f0d4d34
3
+ metadata.gz: fb43b27d324884db21ead2653426adbeba09a183733ee33bb4675b601d49d40c
4
+ data.tar.gz: 32e01870dabcc268910d912afa9d4b5341afd5a3f3b7afed38fc1aea3c1b6bb9
5
5
  SHA512:
6
- metadata.gz: f7eeb962417fab2407479ec254feaea25945f01f07eb20ae5bd5293d2a952ec73b676646b3721879808f65a7a4c8c4765438f85dbb8ed0777e413cb67252fe24
7
- data.tar.gz: 81b3d88b749591513dcae160d0e62bf9eeb4d7e8d1cc4eccbf674a298b242f4b92b19b4797cc7ee80cae364c3bd9256a20029440d28b543b197f81338e9cd468
6
+ metadata.gz: a220a81b743ff5c8ccc3bdec373d788ef6ef64fac8f8db2eebfb0ab95b96d13be351e74340f91534165e9a1d1fa3a8f9b5d5730f34073e8353ec929fb7ab13cb
7
+ data.tar.gz: a445779b1ac54a74c5acc673f5bf3905868a880200843e413df643091da05ac8eb407ae2faaef421b131dce349eaeb5c5e969cb11e1aedd68ca79932c60de114
data/CHANGES.md CHANGED
@@ -1,3 +1,26 @@
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
+
16
+ ### 1.0.1
17
+
18
+ - Prioritize Interruption over Error cuz ActiveRecord is lazy load.
19
+
20
+ ### 1.0.1
21
+
22
+ - Catch database error at runtime
23
+
1
24
  ### 1.0.0
2
25
 
3
26
  - Initial release
@@ -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,186 +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 ActiveRecord::NoDatabaseError
26
- puts "\nError: Database not found."
27
- puts "Please make sure the database exists."
28
- exit 1
29
- rescue RuntimeError => e
30
- puts "Error: #{e.message}"
31
- exit 1
32
- rescue StandardError => e
33
- puts "Error: #{e.message}"
34
- exit 1
35
- rescue Interrupt
36
- puts "\n\nThank you for using ERDB!"
37
- exit 0
38
- end
22
+ db.connect
39
23
 
40
- 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
41
36
 
42
- #
43
- # Ask user which database to use.
44
- # @return [Db]
45
- #
46
- def select_database
47
- data = {
48
- sqlite3: { name: "SQLite", gem: "sqlite3" },
49
- postgresql: { name: "PostgreSQL(Gem 'pg' must be installed)", gem: "pg" },
50
- mysql2: { name: "MySQL(Gem 'mysql2' must be installed)", gem: "mysql2" }
51
- }
37
+ default_task :create
52
38
 
53
- 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
54
44
 
55
- data.each_with_index do |v, i|
56
- puts "#{i + 1}. #{v[1][:name]}"
57
- end
45
+ desc "-h --help", "Show help"
46
+ map %w[-h --help] => :help
47
+ def help
48
+ say Messages.help
49
+ super
50
+ end
51
+
52
+ def self.exit_on_failure?
53
+ true
54
+ end
58
55
 
59
- response = ask_number(1, data.size)
56
+ private
60
57
 
61
- adapter = data.keys[response.to_i - 1].to_sym
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
+ }
62
68
 
63
- # check if the gem is installed
64
- # I don't want to include the gem in the gemspec file
65
- # cuz it's dependencies are too big and depend on the native library
66
- # I only include sqlite3 gem cuz it's small and doesn't have any dependencies
67
- gem = data[adapter][:gem]
69
+ say "Select a database adapter:"
68
70
 
69
- begin
70
- require gem
71
- rescue LoadError
72
- puts "\nError: '#{gem}' gem is not installed."
73
- puts "Please install the gem '#{gem}' first."
74
- puts "Run 'gem install #{gem}' to install the gem."
75
- exit 1
76
- end
71
+ data.each_with_index do |v, i|
72
+ say "#{i + 1}. #{v[1][:name]}"
73
+ end
77
74
 
78
- database = if adapter == :sqlite3
79
- ask_file "\nEnter the path to the database file:"
80
- else
81
- ask "\nEnter the database connection string:"
82
- end
75
+ response = ask_number(1, data.size, ">")
83
76
 
84
- return SQL.new(adapter, database) if %i[sqlite3 mysql2 postgresql].include?(adapter)
77
+ adapter = data.keys[response.to_i - 1].to_sym
85
78
 
86
- raise "Invalid database 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]
84
+
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
87
92
  end
88
93
 
89
- #
90
- # Select a diagram builder.
91
- # @return [ERDProvider]
92
- #
93
- def select_diagram_builder
94
- 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
95
100
 
96
- puts "\nSelect a diagram builder:"
101
+ return SQL.new(adapter, database) if %i[sqlite3 mysql2 postgresql].include?(adapter)
97
102
 
98
- data.each_with_index do |v, i|
99
- puts "#{i + 1}. #{v.name.split('::').last}"
100
- end
103
+ raise "Invalid database adapter"
104
+ end
101
105
 
102
- 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]
103
112
 
104
- data[response.to_i - 1]
105
- end
113
+ say "\nSelect a diagram builder:"
106
114
 
107
- #
108
- # Display welcome message.
109
- #
110
- def welcome
111
- puts <<~WELCOME
112
- .----------------. .----------------. .----------------. .----------------.
113
- | .--------------. || .--------------. || .--------------. || .--------------. |
114
- | | _________ | || | _______ | || | ________ | || | ______ | |
115
- | | |_ ___ | | || | |_ __ \\ | || | |_ ___ `. | || | |_ _ \\ | |
116
- | | | |_ \\_| | || | | |__) | | || | | | `. \\ | || | | |_) | | |
117
- | | | _| _ | || | | __ / | || | | | | | | || | | __'. | |
118
- | | _| |___/ | | || | _| | \\ \\_ | || | _| |___.' / | || | _| |__) | | |
119
- | | |_________| | || | |____| |___| | || | |________.' | || | |_______/ | |
120
- | | | || | | || | | || | | |
121
- | '--------------' || '--------------' || '--------------' || '--------------' |
122
- '----------------' '----------------' '----------------' '----------------'
123
-
124
- ERDB is an automate tool to generate Entity-Relationship Diagrams from a database.
125
- It use Azimutt and DBDiagram to generate the diagrams.
126
-
127
- WELCOME
115
+ data.each_with_index do |v, i|
116
+ say "#{i + 1}. #{v.name.split('::').last}"
128
117
  end
129
118
 
130
- #
131
- # Ask a question to the user.
132
- # @param [String] question
133
- # @return [String]
134
- #
135
- def ask(question = nil)
136
- puts question if question
137
- print "> "
138
- gets.chomp
139
- end
119
+ response = ask_number(1, data.size, ">")
140
120
 
141
- #
142
- # Ask a number input to the user.
143
- # @param [Integer] min
144
- # @param [Integer] max
145
- # @return [Integer]
146
- #
147
- def ask_number(min, max)
148
- response = nil
149
- until response.to_i.between?(min, max)
150
- response = ask
151
- unless response.match(/^\d+$/)
152
- puts "Please enter a number"
153
- response = nil
154
- end
155
-
156
- unless response.to_i.between?(min, max)
157
- puts "Please enter a number between #{min} and #{max}"
158
- response = nil
159
- end
160
- end
161
- response
162
- end
121
+ data[response.to_i - 1]
122
+ end
163
123
 
164
- def ask_file(question)
165
- loop do
166
- file = ask question
167
- 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
168
139
 
169
- 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
170
143
  end
171
144
  end
145
+ response
146
+ end
172
147
 
173
- #
174
- # Ask a yes/no question to the user.
175
- # @param [String] question
176
- # @param [Boolean] default
177
- # @return [Boolean]
178
- #
179
- def ask_yes_no(question, default = true)
180
- result = ask question
181
-
182
- 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
183
159
  end
184
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
185
217
  end
186
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.1".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.1
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