erdb 1.0.1 → 1.1.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: 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