erdb 1.0.2 → 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: 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