model_probe 1.0.7 → 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
- SHA1:
3
- metadata.gz: 256df9f2a0d516dc276210959300379bdf59a743
4
- data.tar.gz: ec8afcc2bafbf1d2bf86ef0c2529bf0cc4c78316
2
+ SHA256:
3
+ metadata.gz: dfbbaa56b645ab7e076623397e7af297d5113ffd6820b31e0e16468f648c9653
4
+ data.tar.gz: 42b742bd32a1f2a8fda04e7ea1b32425b14d0c58ebc63ee2543b208883f3f379
5
5
  SHA512:
6
- metadata.gz: 156afe3b29d19de7bda2b5ade402b84827f250e2285ad02f23f2808e1122f88aafc8611f5d3dda292bed8de34127bbedc30e0de78d0c50a17099afa47ee3eba6
7
- data.tar.gz: a0d678decaac686cfb1d234436f2504be0b2d54d67f4343d551771e34c19baa0904b41784be02acaf7d519d8322a15ddc61321130a4db7c729f115f3aea2cc05
6
+ metadata.gz: f84ec45449d885f167fd3ca1b6e784b03ffd4e2ace226f1868dd162e817a9223a578456eb11e86be9532a57a0e36aa6d3d848a6b88a68aed0df3a74073419ebe
7
+ data.tar.gz: ffbd50414995be1cb831677959db557ffcb4bffacb46d0ed6f2a742d1e3154bcadfaac740a43d41964fcf6e8d7165b4f3b2364572cee0ead09ca48e0ee0c0ee3
data/Gemfile CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
  gemspec
data/Gemfile.lock CHANGED
@@ -1,19 +1,60 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- model_probe (1.0.7)
4
+ model_probe (1.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- rake (10.4.2)
9
+ ast (2.4.2)
10
+ json (2.6.3)
11
+ language_server-protocol (3.17.0.3)
12
+ lint_roller (1.0.0)
13
+ magic_frozen_string_literal (1.2.0)
14
+ parallel (1.23.0)
15
+ parser (3.2.2.1)
16
+ ast (~> 2.4.1)
17
+ rainbow (3.1.1)
18
+ regexp_parser (2.8.0)
19
+ rexml (3.2.5)
20
+ rubocop (1.50.2)
21
+ json (~> 2.3)
22
+ parallel (~> 1.10)
23
+ parser (>= 3.2.0.0)
24
+ rainbow (>= 2.2.2, < 4.0)
25
+ regexp_parser (>= 1.8, < 3.0)
26
+ rexml (>= 3.2.5, < 4.0)
27
+ rubocop-ast (>= 1.28.0, < 2.0)
28
+ ruby-progressbar (~> 1.7)
29
+ unicode-display_width (>= 2.4.0, < 3.0)
30
+ rubocop-ast (1.28.1)
31
+ parser (>= 3.2.1.0)
32
+ rubocop-performance (1.16.0)
33
+ rubocop (>= 1.7.0, < 2.0)
34
+ rubocop-ast (>= 0.4.0)
35
+ ruby-progressbar (1.13.0)
36
+ standard (1.28.2)
37
+ language_server-protocol (~> 3.17.0.2)
38
+ lint_roller (~> 1.0)
39
+ rubocop (~> 1.50.2)
40
+ standard-custom (~> 1.0.0)
41
+ standard-performance (~> 1.0.1)
42
+ standard-custom (1.0.0)
43
+ lint_roller (~> 1.0)
44
+ standard-performance (1.0.1)
45
+ lint_roller (~> 1.0)
46
+ rubocop-performance (~> 1.16.0)
47
+ standardrb (1.0.1)
48
+ standard
49
+ unicode-display_width (2.4.2)
10
50
 
11
51
  PLATFORMS
12
- ruby
52
+ arm64-darwin-22
13
53
 
14
54
  DEPENDENCIES
55
+ magic_frozen_string_literal
15
56
  model_probe!
16
- rake
57
+ standardrb
17
58
 
18
59
  BUNDLED WITH
19
- 1.16.0
60
+ 2.4.10
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Nathan Hopkins
1
+ Copyright (c) 2023 Nathan Hopkins
2
2
 
3
3
  MIT License
4
4
 
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,140 +1,74 @@
1
1
  # ModelProbe
2
2
 
3
- <a target='_blank' rel='nofollow' href='https://app.codesponsor.io/link/QMSjMHrtPhvfmCnk5Hbikhhr/hopsoft/model_probe'>
4
- <img alt='Sponsor' width='888' height='68' src='https://app.codesponsor.io/embed/QMSjMHrtPhvfmCnk5Hbikhhr/hopsoft/model_probe.svg' />
5
- </a>
6
-
7
- ## Schema introspection for ActiveRecord
8
-
9
- Provides a detailed view of the underlying schema that backs an ActiveRecord model.
10
-
11
- *This functionality can be added to any object that implements [ActiveRecord's columns interface](http://rubydoc.info/docs/rails/ActiveRecord/ModelSchema/ClassMethods#columns-instance_method).*
12
-
13
- ## Installation
14
-
15
- Add this line to your application's Gemfile:
16
-
17
- gem 'model_probe'
18
-
19
- And then execute:
20
-
21
- $ bundle
22
-
23
- Or install it yourself as:
24
-
25
- $ gem install model_probe
26
-
27
- ## Usage
28
-
29
- ```ruby
30
- MyModel.extend ModelProbe
31
- MyModel.probe
32
- MyModel.print_fixture
33
- MyModel.print_model
34
- ```
35
-
36
- ## Rails Integration
37
-
38
- ModelProbe auto initializes in the Rails development environment.
39
- This means your models are implicitly extended with this behavior when developing.
40
-
41
- It also ships with these convenient rake tasks.
42
-
43
- ```sh
44
- rails t -T model_probe
45
-
46
- # rails model_probe:print_fixture[klass] # Print fixture
47
- # rails model_probe:print_model[klass] # Print model
48
- # rails model_probe:probe[klass] # Probe
49
- ```
50
-
51
- ```sh
52
- rails model_probe:probe[User]
53
-
54
- # confirmation_sent_at datetime..timestamp without time zone NULL
55
- # confirmation_token string....character varying NULL
56
- # confirmed_at datetime..timestamp without time zone NULL
57
- # created_at datetime..timestamp without time zone
58
- # current_sign_in_at datetime..timestamp without time zone NULL
59
- # current_sign_in_ip inet......inet NULL
60
- # email string....character varying NULL []
61
- # encrypted_password string....character varying []
62
- # failed_attempts integer...integer [0]
63
- # * id uuid......uuid
64
- # last_sign_in_at datetime..timestamp without time zone NULL
65
- # last_sign_in_ip inet......inet NULL
66
- # locked_at datetime..timestamp without time zone NULL
67
- # payment_platform string....character varying NULL
68
- # payment_platform_id string....character varying NULL
69
- # phone_number_id uuid......uuid
70
- # remember_created_at datetime..timestamp without time zone NULL
71
- # reset_password_sent_at datetime..timestamp without time zone NULL
72
- # reset_password_token string....character varying NULL
73
- # sign_in_count integer...integer [0]
74
- # unconfirmed_email string....character varying NULL
75
- # unlock_token string....character varying NULL
76
- # updated_at datetime..timestamp without time zone
77
- ```
78
-
79
- ```sh
80
- rails model_probe:print_fixture[User]
81
-
82
- # ---
83
- # user:
84
- # confirmation_sent_at: value
85
- # confirmation_token: value
86
- # confirmed_at: value
87
- # current_sign_in_at: value
88
- # current_sign_in_ip: value
89
- # email: ''
90
- # encrypted_password: ''
91
- # failed_attempts: '0'
92
- # last_sign_in_at: value
93
- # last_sign_in_ip: value
94
- # locked_at: value
95
- # payment_platform: value
96
- # payment_platform_id: value
97
- # phone_number_id: value
98
- # reset_password_sent_at: value
99
- # reset_password_token: value
100
- # sign_in_count: '0'
101
- # unconfirmed_email: value
102
- # unlock_token: value
103
- ```
104
-
105
- ```sh
106
- rails model_probe:print_model[User]
107
-
108
- # class User < ApplicationRecord
109
- # # extends ...................................................................
110
- # # includes ..................................................................
111
- #
112
- # # relationships .............................................................
113
- # belongs_to :payment_platform
114
- # belongs_to :phone_number
115
- #
116
- # # validations ...............................................................
117
- # validates :created_at, presence: true
118
- # validates :encrypted_password, presence: true
119
- # validates :failed_attempts, presence: true
120
- # validates :phone_number_id, presence: true
121
- # validates :sign_in_count, presence: true
122
- # validates :updated_at, presence: true
123
- #
124
- # # callbacks .................................................................
125
- # # scopes ....................................................................
126
- # # additional config (i.e. accepts_nested_attribute_for etc...) ..............
127
- #
128
- # # class methods .............................................................
129
- # class << self
130
- # end
131
- #
132
- # # public instance methods ...................................................
133
- #
134
- # # protected instance methods ................................................
135
- # protected
136
- #
137
- # # private instance methods ..................................................
138
- # private
139
- # end
140
- ```
3
+ ## ActiveRecord schema visualization and model organization made easy 🙌
4
+
5
+ Colorized table info for columns, types, nullables, indexes...
6
+ and the actual DDL used by the database to create the table.
7
+ _All this and more with ModelProbe!_
8
+
9
+ 1. Get a clear picture of your model's underlying schema with beautiful and informative schema introspection.
10
+ 1. Generate model class definitions with a well organized, logical structure.
11
+ 1. Create sensible text fixture stubs.
12
+
13
+ <!-- Tocer[start]: Auto-generated, don't remove. -->
14
+
15
+ ## Table of Contents
16
+
17
+ - [Quick Start](#quick-start)
18
+ - [Supported Databases](#supported-databases)
19
+ - [Videos](#videos)
20
+ - [Screenshots](#screenshots)
21
+
22
+ <!-- Tocer[finish]: Auto-generated, don't remove. -->
23
+
24
+ ## Quick Start
25
+
26
+ 1. Add the GEM to your project
27
+
28
+ ```sh
29
+ bundle add model_probe
30
+ ```
31
+
32
+ _ModelProbe auto initializes in the Rails `development` environment._
33
+
34
+ 1. Use in a Rails console
35
+
36
+ ```ruby
37
+ # examples for a User model
38
+ User.probe
39
+ User.print_model
40
+ User.print_fixture
41
+ ```
42
+ 1. Use with Rails (Rake) tasks
43
+
44
+ ```sh
45
+ # examples for a User model
46
+ bin/rails model_probe:probe[User]
47
+ bin/rails model_probe:print_model[User]
48
+ bin/rails model_probe:print_fixture[User]
49
+ ```
50
+
51
+ ## Supported Databases
52
+
53
+ - MySQL
54
+ - PostgreSQL
55
+ - SQLite
56
+ - _...more? contributions welcome ;)_
57
+
58
+ ## Videos
59
+
60
+ [![image](https://ik.imagekit.io/hopsoft/model_probe_intro_Zf04NFJ1-.webp?updatedAt=1683471619001)](https://youtu.be/Q3IdyKateQE)
61
+
62
+ ## Screenshots
63
+
64
+ Introspect your ActiveRecord models to build a deep understanding of the underlying database structure.
65
+
66
+ ![ModelProbe probe](https://ik.imagekit.io/hopsoft/mode_probe_probe_3ouJjft48.webp?updatedAt=1683465723169)
67
+
68
+ Generate a well organized template for your ActiveRecord model's class definition.
69
+
70
+ ![ModelProbe print_model](https://ik.imagekit.io/hopsoft/model_probe_print_model_sGOZWw-D5.webp?updatedAt=1683465723049)
71
+
72
+ Create fixture stubs to use in the test suite.
73
+
74
+ ![ModelProbe print_fixture](https://ik.imagekit.io/hopsoft/model_probe_print_fixture_ZZ2TavUO7.webp?updatedAt=1683465722977)
data/Rakefile CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
@@ -1,21 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModelProbe
2
4
  module Color
3
5
  extend self
4
6
 
5
- colors = {
6
- :gray => "1;30",
7
- :red => 31,
8
- :green => 32,
9
- :yellow => 33,
10
- :blue => 34,
11
- :magenta => 35,
12
- :cyan => 36,
13
- :white => 37
7
+ COLORS = {
8
+ blue: 34,
9
+ cyan: 36,
10
+ gray: "1;30",
11
+ green: 32,
12
+ green_light: 92,
13
+ magenta: 35,
14
+ magenta_light: 95,
15
+ pink: "1;91",
16
+ red: 31,
17
+ red_light: 91,
18
+ white: 37,
19
+ yellow: 33,
20
+ yellow_light: 93
14
21
  }
15
22
 
16
- colors.each do |name, code|
23
+ COLORS.each do |name, code|
17
24
  define_method name do |text|
18
- "\e[#{code}m#{text}\e[0m"
25
+ "\e[#{code}m#{text}\e[0m"
19
26
  end
20
27
  end
21
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  if Rails.env.development?
2
4
  ActiveRecord::Base.extend ModelProbe
3
5
 
@@ -0,0 +1,4 @@
1
+ <%= name.underscore %>_fixture:
2
+ <% fixture_columns.each do |column| -%>
3
+ <%= column.name.underscore %>: <%= column&.default || column.type %>
4
+ <% end -%>
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= name -%> < <%= superclass_name %>
4
+ # extends ..................................................................................................
5
+
6
+ # includes .................................................................................................
7
+
8
+ # constants ................................................................................................
9
+
10
+ # class methods ............................................................................................
11
+ class << self
12
+ end
13
+
14
+ # relationships ............................................................................................
15
+ <% foreign_key_columns.each do |column| -%>
16
+ <%= "belongs_to :#{column.name.sub(/_id\z/, "")}" %>
17
+ <% end -%>
18
+
19
+ # validations ..............................................................................................
20
+ <% validation_columns.each do |column| -%>
21
+ <%= "validates :#{column.name}, presence: true" unless column.null if required_columns.include?(column) %>
22
+ <%= "validates :#{column.name}, length: {maximum: #{column.limit}}" if limit_columns.include?(column) %>
23
+ <% end -%>
24
+
25
+ # callbacks (caution: side effects) ........................................................................
26
+
27
+ # scopes (composable queries) ..............................................................................
28
+
29
+ # additional config (accepts_nested_attribute_for, etc.) ...................................................
30
+
31
+ # public instance methods ..................................................................................
32
+
33
+ # protected instance methods ...............................................................................
34
+
35
+ # private instance methods .................................................................................
36
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ModelProbe
2
- VERSION = "1.0.7"
4
+ VERSION = "1.1.0"
3
5
  end
data/lib/model_probe.rb CHANGED
@@ -1,114 +1,216 @@
1
- require "model_probe/version"
2
- require "model_probe/color"
3
- require "model_probe/railtie" if defined?(Rails)
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require_relative "model_probe/version"
5
+ require_relative "model_probe/color"
6
+ require_relative "model_probe/railtie" if defined?(Rails)
4
7
 
5
8
  module ModelProbe
6
9
  include ModelProbe::Color
7
10
 
8
11
  # Pretty prints column meta data for an ActiveModel
9
12
  def probe
10
- name_pad = columns.map { |c| c.name.length }.max + 1
11
- type_pad = columns.map { |c| c.type.length }.max + 2
12
- sql_type_pad = columns.map { |c| c.sql_type.length }.max + 1
13
-
14
- columns.sort { |a, b| a.name <=> b.name }.map do |column|
15
- name = column.name
16
- name = "* #{name}" if primary_key_column?(column)
17
- print yellow(name.to_s.rjust(name_pad))
18
- print " "
19
- print blue(column.type.to_s.ljust(type_pad, "."))
20
- print magenta(column.sql_type.to_s.ljust(sql_type_pad))
21
- column.null ? print(red("NULL")) : print(" ")
22
- print " [#{column.default}]" if column.default
23
- print " #{gray column.comment}" if column.comment
24
- puts
25
- end
13
+ probe_header
14
+ probe_ddl
15
+ probe_columns
16
+ probe_indexes
17
+ probe_footer
26
18
  nil
27
19
  end
28
20
 
29
21
  # Prints a stub that can be used for a test fixture
30
22
  def print_fixture
31
- values = columns.sort_by(&:name).each_with_object({}) do |column, memo|
32
- next if primary_key_column?(column)
33
- next if timestamp_column?(column)
34
- memo[column.name] = column.default || "value"
35
- end
36
-
37
- hash = { self.name.underscore => values }
38
- puts hash.to_yaml
23
+ template = erb_template("model_probe/templates/fixture.yml.erb")
24
+ puts template.result_with_hash(name: name, fixture_columns: fixture_columns)
39
25
  nil
40
26
  end
41
27
 
42
28
  # Prints a new model definition based on the database schema
43
29
  def print_model
44
- puts "class #{name} < #{superclass.name}"
45
- puts " # extends ..................................................................."
46
- puts " # includes .................................................................."
47
- puts if relation_columns.size > 0
48
- puts " # relationships ............................................................."
49
- relation_columns.sort_by(&:name).each do |column|
50
- next if primary_key_column?(column)
51
- puts " belongs_to :#{column.name.sub(/_id\z/, "")}" if column.name =~ /_id\z/
30
+ template = erb_template("model_probe/templates/model.rb.erb")
31
+ puts template.result_with_hash(
32
+ name: name,
33
+ superclass_name: superclass.name,
34
+ primary_key_columns: primary_key_columns,
35
+ foreign_key_columns: foreign_key_columns,
36
+ relation_columns: relation_columns,
37
+ required_columns: required_columns,
38
+ limit_columns: limit_columns,
39
+ validation_columns: validation_columns
40
+ )
41
+ nil
42
+ end
43
+
44
+ private
45
+
46
+ def erb_template(relative_path)
47
+ template_path = File.expand_path(relative_path, __dir__)
48
+ template_text = File.read(template_path)
49
+ # trim_mode doesn't seem to work regardless of how it's passed with Ruby 2.7.5
50
+ ERB.new template_text, trim_mode: "-"
51
+ end
52
+
53
+ def primary_key_column?(column)
54
+ column.name == primary_key
55
+ end
56
+
57
+ def foreign_key_column?(column)
58
+ return false if primary_key_column?(column)
59
+ column.name.end_with? "_id"
60
+ end
61
+
62
+ def timestamp_column?(column)
63
+ column.type == :datetime && column.name =~ /(created|updated|modified)/
64
+ end
65
+
66
+ def required_column?(column)
67
+ return false if primary_key_column?(column)
68
+ return false if foreign_key_column?(column)
69
+ return false if timestamp_column?(column)
70
+ !column.null
71
+ end
72
+
73
+ def limit_column?(column)
74
+ return false if primary_key_column?(column)
75
+ return false if foreign_key_column?(column)
76
+ return false if timestamp_column?(column)
77
+ %i[text string].include?(column.type) && column.limit.to_i > 0
78
+ end
79
+
80
+ def primary_key_columns
81
+ columns.select { |column| primary_key_column? column }.sort_by(&:name)
82
+ end
83
+
84
+ def foreign_key_columns
85
+ columns.select { |column| foreign_key_column? column }.sort_by(&:name)
86
+ end
87
+
88
+ def relation_columns
89
+ columns.select { |column| relation_column? column }.sort_by(&:name)
90
+ end
91
+
92
+ def required_columns
93
+ columns.select { |column| required_column? column }.sort_by(&:name)
94
+ end
95
+
96
+ def limit_columns
97
+ columns.select { |column| limit_column? column }.sort_by(&:name)
98
+ end
99
+
100
+ def validation_columns
101
+ (required_columns + limit_columns).uniq.sort_by(&:name)
102
+ end
103
+
104
+ def relation_column?(column)
105
+ return false if column.name == primary_key
106
+ column.name.end_with?("_id")
107
+ end
108
+
109
+ def fixture_columns
110
+ columns.sort_by(&:name).select do |column|
111
+ !primary_key_column?(column) && !timestamp_column?(column)
52
112
  end
53
- puts if relation_columns.size > 0 || validation_columns.size > 0
54
- puts " # validations ..............................................................."
55
- validation_columns.sort_by(&:name).each do |column|
56
- next if primary_key_column?(column)
57
- puts " validates :#{column.name}, presence: true" unless column.null
58
- if %i(text string).include?(column.type) && column.limit.to_i > 0
59
- puts " validates :#{column.name}, length: { maximum: #{column.limit} }"
113
+ end
114
+
115
+ def ddl
116
+ config = connection_db_config.configuration_hash
117
+ @ddl ||= begin
118
+ case connection.adapter_name
119
+ when /sqlite/i
120
+ `sqlite3 #{config[:database]} '.schema #{table_name}'`
121
+ when /postgresql/i
122
+ `PGPASSWORD=#{config[:password]} pg_dump --host=#{config[:host]} --username=#{config[:username]} --schema-only --table=#{table_name} #{config[:database]}`
123
+ when /mysql/i
124
+ `mysqldump --host=#{config[:host]} --user=#{config[:username]} --password=#{config[:password]} --no-data --compact #{config[:database]} #{table_name}`
125
+ else
126
+ "DDL output is not yet supported for #{connection.adapter_name}!"
60
127
  end
128
+ rescue => e
129
+ Color.red "Failed to generate DDL string! #{e.message}"
61
130
  end
62
- puts if validation_columns.size > 0
63
- puts " # callbacks ................................................................."
64
- puts " # scopes ...................................................................."
65
- puts " # additional config (i.e. accepts_nested_attribute_for etc...) .............."
66
- puts
67
- puts " # class methods ............................................................."
68
- puts " class << self"
69
- puts " end"
131
+ end
132
+
133
+ def probe_header
134
+ puts Color.gray "".ljust(110, "=")
135
+ print connection.adapter_name
136
+ print Color.gray(" (#{connection.database_version}) - ")
137
+ puts Color.green(table_name)
138
+ puts Color.gray "".ljust(110, "=")
70
139
  puts
71
- puts " # public instance methods ..................................................."
140
+ end
141
+
142
+ def probe_ddl(pad: 2)
143
+ return unless ddl
144
+ lines = ddl.split("\n")
145
+ lines.each do |line|
146
+ next if line.strip.blank?
147
+ next if line.strip.start_with?("--")
148
+ next if line.strip.start_with?("/*")
149
+ print " ".ljust(pad)
150
+ puts Color.gray(line)
151
+ end
72
152
  puts
73
- puts " # protected instance methods ................................................"
74
- puts " protected"
153
+ end
154
+
155
+ def probe_column(column, name_pad:, type_pad:, sql_type_pad:)
156
+ name = column.name
157
+ if primary_key_column?(column)
158
+ print Color.pink("*#{name}".rjust(name_pad))
159
+ else
160
+ print Color.magenta(name.to_s.rjust(name_pad))
161
+ end
162
+ print " "
163
+ print column.type.to_s.ljust(type_pad, ".")
164
+ print Color.gray(column.sql_type.to_s.ljust(sql_type_pad))
165
+ print Color.gray("NULLABLE ") if column.null
166
+ print Color.pink("REQUIRED ") unless column.null
167
+ print Color.gray("default=#{column.default} ") if column.default
168
+ print "- #{Color.gray column.comment}" if column.comment
75
169
  puts
76
- puts " # private instance methods .................................................."
77
- puts " private"
78
- puts "end"
79
- nil
80
170
  end
81
171
 
82
- private
172
+ def probe_columns
173
+ name_pad = columns.map { |c| c.name.length }.max + 2
174
+ type_pad = columns.map { |c| c.type.length }.max + 2
175
+ sql_type_pad = columns.map { |c| c.sql_type.length }.max + 1
83
176
 
84
- def relation_columns
85
- @relation_columns ||= begin
86
- columns.select { |column| relation_column? column }
87
- end
177
+ columns.sort_by(&:name).each do |column|
178
+ probe_column column, name_pad: name_pad, type_pad: type_pad, sql_type_pad: sql_type_pad
88
179
  end
180
+ puts
181
+ end
89
182
 
90
- def validation_columns
91
- @validation_columns ||= begin
92
- columns.select { |column| validation_column? column }
93
- end
183
+ def probe_index(index, name_pad:)
184
+ print Color.yellow_light(index.name.to_s.rjust(name_pad))
185
+ print Color.gray(" [")
186
+ index.columns.each_with_index do |column, index|
187
+ print Color.gray(", ") if index > 0
188
+ print Color.magenta(column)
94
189
  end
190
+ print Color.gray("]")
191
+ print Color.green_light(" UNIQUE") if index.unique
95
192
 
96
- def primary_key_column?(column)
97
- column.name == primary_key
193
+ if index.where
194
+ print Color.gray(" where(#{index.where})")
98
195
  end
99
196
 
100
- def timestamp_column?(column)
101
- column.type == :datetime && column.name =~ /(created|updated|modified)/
197
+ if index.using
198
+ print Color.gray(" using(#{index.using})")
102
199
  end
200
+ puts
201
+ end
103
202
 
104
- def relation_column?(column)
105
- return false if column.name == primary_key
106
- column.name.end_with?("_id")
107
- end
203
+ def probe_indexes
204
+ indexes = connection.indexes(table_name)
205
+ name_pad = indexes.map { |c| c.name.length }.max + 1
108
206
 
109
- def validation_column?(column)
110
- return false if column.name == primary_key
111
- return true unless column.null
112
- %i(text string).include?(column.type) && column.limit.to_i > 0
207
+ indexes.sort_by(&:name).each do |index|
208
+ probe_index index, name_pad: name_pad
113
209
  end
210
+ puts
211
+ end
212
+
213
+ def probe_footer
214
+ puts Color.gray "".ljust(110, "=")
215
+ end
114
216
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :model_probe do
2
4
  desc <<~DESC
3
5
  Probe. Usage: `rails model:probe[User]`
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/model_probe/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "model_probe"
7
+ s.version = ModelProbe::VERSION
8
+ s.authors = ["Nathan Hopkins"]
9
+ s.email = ["natehop@gmail.com"]
10
+ s.summary = "ActiveRecord schema visualization and model organization made easy"
11
+ s.homepage = "http://hopsoft.github.com/model_probe/"
12
+
13
+ s.files = Dir["lib/**/*rb", "lib/tasks/*rake", "[A-Z]*"]
14
+
15
+ s.add_development_dependency "magic_frozen_string_literal"
16
+ s.add_development_dependency "standardrb"
17
+ end
data/tags ADDED
@@ -0,0 +1,106 @@
1
+ !_TAG_EXTRA_DESCRIPTION anonymous /Include tags for non-named objects like lambda/
2
+ !_TAG_EXTRA_DESCRIPTION fileScope /Include tags of file scope/
3
+ !_TAG_EXTRA_DESCRIPTION pseudo /Include pseudo tags/
4
+ !_TAG_EXTRA_DESCRIPTION subparser /Include tags generated by subparsers/
5
+ !_TAG_FIELD_DESCRIPTION epoch /the last modified time of the input file (only for F\/file kind tag)/
6
+ !_TAG_FIELD_DESCRIPTION file /File-restricted scoping/
7
+ !_TAG_FIELD_DESCRIPTION input /input file/
8
+ !_TAG_FIELD_DESCRIPTION name /tag name/
9
+ !_TAG_FIELD_DESCRIPTION pattern /pattern/
10
+ !_TAG_FIELD_DESCRIPTION typeref /Type and name of a variable or typedef/
11
+ !_TAG_FIELD_DESCRIPTION!Ruby mixin /how the class or module is mixed in (mixin:HOW:MODULE)/
12
+ !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
13
+ !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
14
+ !_TAG_KIND_DESCRIPTION!GemSpec g,gem /gems/
15
+ !_TAG_KIND_DESCRIPTION!Markdown S,subsection /level 2 sections/
16
+ !_TAG_KIND_DESCRIPTION!Markdown T,l4subsection /level 4 sections/
17
+ !_TAG_KIND_DESCRIPTION!Markdown c,chapter /chapters/
18
+ !_TAG_KIND_DESCRIPTION!Markdown n,footnote /footnotes/
19
+ !_TAG_KIND_DESCRIPTION!Markdown s,section /sections/
20
+ !_TAG_KIND_DESCRIPTION!Markdown t,subsubsection /level 3 sections/
21
+ !_TAG_KIND_DESCRIPTION!Markdown u,l5subsection /level 5 sections/
22
+ !_TAG_KIND_DESCRIPTION!Rake d,directory /directory tasks/
23
+ !_TAG_KIND_DESCRIPTION!Rake f,File /file tasks/
24
+ !_TAG_KIND_DESCRIPTION!Rake m,multitask /multi tasks/
25
+ !_TAG_KIND_DESCRIPTION!Rake n,namespace /namespaces/
26
+ !_TAG_KIND_DESCRIPTION!Rake t,task /tasks/
27
+ !_TAG_KIND_DESCRIPTION!Rake x,xtask /tasks defined with special constructor/
28
+ !_TAG_KIND_DESCRIPTION!Ruby A,accessor /accessors/
29
+ !_TAG_KIND_DESCRIPTION!Ruby C,constant /constants/
30
+ !_TAG_KIND_DESCRIPTION!Ruby L,library /libraries/
31
+ !_TAG_KIND_DESCRIPTION!Ruby S,singletonMethod /singleton methods/
32
+ !_TAG_KIND_DESCRIPTION!Ruby a,alias /aliases/
33
+ !_TAG_KIND_DESCRIPTION!Ruby c,class /classes/
34
+ !_TAG_KIND_DESCRIPTION!Ruby f,method /methods/
35
+ !_TAG_KIND_DESCRIPTION!Ruby m,module /modules/
36
+ !_TAG_KIND_DESCRIPTION!Sh a,alias /aliases/
37
+ !_TAG_KIND_DESCRIPTION!Sh f,function /functions/
38
+ !_TAG_KIND_DESCRIPTION!Sh h,heredoc /label for here document/
39
+ !_TAG_KIND_DESCRIPTION!Sh s,script /script files/
40
+ !_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/
41
+ !_TAG_OUTPUT_FILESEP slash /slash or backslash/
42
+ !_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
43
+ !_TAG_OUTPUT_VERSION 0.0 /current.age/
44
+ !_TAG_PARSER_VERSION!GemSpec 0.0 /current.age/
45
+ !_TAG_PARSER_VERSION!Markdown 0.0 /current.age/
46
+ !_TAG_PARSER_VERSION!Rake 0.0 /current.age/
47
+ !_TAG_PARSER_VERSION!Ruby 0.0 /current.age/
48
+ !_TAG_PARSER_VERSION!Sh 0.0 /current.age/
49
+ !_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/
50
+ !_TAG_PROC_CWD /Users/nathan/work/hopsoft/model_probe/ //
51
+ !_TAG_PROGRAM_AUTHOR Universal Ctags Team //
52
+ !_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/
53
+ !_TAG_PROGRAM_URL https://ctags.io/ /official site/
54
+ !_TAG_PROGRAM_VERSION 6.0.0 //
55
+ !_TAG_ROLE_DESCRIPTION!GemSpec!gem develDep /specifying development dependency/
56
+ !_TAG_ROLE_DESCRIPTION!GemSpec!gem runtimeDep /specifying runtime dependency/
57
+ !_TAG_ROLE_DESCRIPTION!Ruby!library loaded /loaded by "load" method/
58
+ !_TAG_ROLE_DESCRIPTION!Ruby!library required /loaded by "require" method/
59
+ !_TAG_ROLE_DESCRIPTION!Ruby!library requiredRel /loaded by "require_relative" method/
60
+ !_TAG_ROLE_DESCRIPTION!Sh!heredoc endmarker /end marker/
61
+ !_TAG_ROLE_DESCRIPTION!Sh!script loaded /loaded/
62
+ ActiveRecord schema visualization and model organization made easy 🙌 README.md /^## ActiveRecord schema visualization and model organization made easy 🙌$/;" s chapter:ModelProbe
63
+ COLORS lib/model_probe/color.rb /^ COLORS = {$/;" C module:ModelProbe.Color
64
+ Color lib/model_probe/color.rb /^ module Color$/;" m module:ModelProbe mixin:extend:self
65
+ ModelProbe README.md /^# ModelProbe$/;" c
66
+ ModelProbe lib/model_probe.rb /^module ModelProbe$/;" m mixin:include:ModelProbe.Color
67
+ ModelProbe lib/model_probe/color.rb /^module ModelProbe$/;" m
68
+ ModelProbe lib/model_probe/railtie.rb /^ module ModelProbe$/;" m
69
+ ModelProbe lib/model_probe/version.rb /^module ModelProbe$/;" m
70
+ Quick Start README.md /^## Quick Start$/;" s chapter:ModelProbe
71
+ Railtie lib/model_probe/railtie.rb /^ class Railtie < Rails::Railtie$/;" c module:ModelProbe
72
+ Screenshots README.md /^## Screenshots$/;" s chapter:ModelProbe
73
+ Supported Databases README.md /^## Supported Databases$/;" s chapter:ModelProbe
74
+ Table of Contents README.md /^## Table of Contents$/;" s chapter:ModelProbe
75
+ VERSION lib/model_probe/version.rb /^ VERSION = "1.1.0"$/;" C module:ModelProbe
76
+ Videos README.md /^## Videos$/;" s chapter:ModelProbe
77
+ ddl lib/model_probe.rb /^ def ddl$/;" f module:ModelProbe
78
+ erb_template lib/model_probe.rb /^ def erb_template(relative_path)$/;" f module:ModelProbe
79
+ fixture_columns lib/model_probe.rb /^ def fixture_columns$/;" f module:ModelProbe
80
+ foreign_key_column? lib/model_probe.rb /^ def foreign_key_column?(column)$/;" f module:ModelProbe
81
+ foreign_key_columns lib/model_probe.rb /^ def foreign_key_columns$/;" f module:ModelProbe
82
+ limit_column? lib/model_probe.rb /^ def limit_column?(column)$/;" f module:ModelProbe
83
+ limit_columns lib/model_probe.rb /^ def limit_columns$/;" f module:ModelProbe
84
+ model_probe lib/tasks/model_probe.rake /^namespace :model_probe do$/;" n
85
+ model_probe model_probe.gemspec /^ s.name = "model_probe"$/;" g
86
+ primary_key_column? lib/model_probe.rb /^ def primary_key_column?(column)$/;" f module:ModelProbe
87
+ primary_key_columns lib/model_probe.rb /^ def primary_key_columns$/;" f module:ModelProbe
88
+ print_fixture lib/model_probe.rb /^ def print_fixture$/;" f module:ModelProbe
89
+ print_fixture lib/tasks/model_probe.rake /^ task :print_fixture, [:klass] => :environment do |task, args|$/;" t namespace:model_probe
90
+ print_model lib/model_probe.rb /^ def print_model$/;" f module:ModelProbe
91
+ print_model lib/tasks/model_probe.rake /^ task :print_model, [:klass] => :environment do |task, args|$/;" t namespace:model_probe
92
+ probe lib/model_probe.rb /^ def probe$/;" f module:ModelProbe
93
+ probe lib/tasks/model_probe.rake /^ task :probe, [:klass] => :environment do |task, args|$/;" t namespace:model_probe
94
+ probe_column lib/model_probe.rb /^ def probe_column(column, name_pad:, type_pad:, sql_type_pad:)$/;" f module:ModelProbe
95
+ probe_columns lib/model_probe.rb /^ def probe_columns$/;" f module:ModelProbe
96
+ probe_ddl lib/model_probe.rb /^ def probe_ddl(pad: 2)$/;" f module:ModelProbe
97
+ probe_footer lib/model_probe.rb /^ def probe_footer$/;" f module:ModelProbe
98
+ probe_header lib/model_probe.rb /^ def probe_header$/;" f module:ModelProbe
99
+ probe_index lib/model_probe.rb /^ def probe_index(index, name_pad:)$/;" f module:ModelProbe
100
+ probe_indexes lib/model_probe.rb /^ def probe_indexes$/;" f module:ModelProbe
101
+ relation_column? lib/model_probe.rb /^ def relation_column?(column)$/;" f module:ModelProbe
102
+ relation_columns lib/model_probe.rb /^ def relation_columns$/;" f module:ModelProbe
103
+ required_column? lib/model_probe.rb /^ def required_column?(column)$/;" f module:ModelProbe
104
+ required_columns lib/model_probe.rb /^ def required_columns$/;" f module:ModelProbe
105
+ timestamp_column? lib/model_probe.rb /^ def timestamp_column?(column)$/;" f module:ModelProbe
106
+ validation_columns lib/model_probe.rb /^ def validation_columns$/;" f module:ModelProbe
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: model_probe
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Hopkins
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-12 00:00:00.000000000 Z
11
+ date: 2023-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rake
14
+ name: magic_frozen_string_literal
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -24,7 +24,21 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- description:
27
+ - !ruby/object:Gem::Dependency
28
+ name: standardrb
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description:
28
42
  email:
29
43
  - natehop@gmail.com
30
44
  executables: []
@@ -39,12 +53,16 @@ files:
39
53
  - lib/model_probe.rb
40
54
  - lib/model_probe/color.rb
41
55
  - lib/model_probe/railtie.rb
56
+ - lib/model_probe/templates/fixture.yml.erb
57
+ - lib/model_probe/templates/model.rb.erb
42
58
  - lib/model_probe/version.rb
43
59
  - lib/tasks/model_probe.rake
60
+ - model_probe.gemspec
61
+ - tags
44
62
  homepage: http://hopsoft.github.com/model_probe/
45
63
  licenses: []
46
64
  metadata: {}
47
- post_install_message:
65
+ post_install_message:
48
66
  rdoc_options: []
49
67
  require_paths:
50
68
  - lib
@@ -59,9 +77,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
77
  - !ruby/object:Gem::Version
60
78
  version: '0'
61
79
  requirements: []
62
- rubyforge_project:
63
- rubygems_version: 2.6.13
64
- signing_key:
80
+ rubygems_version: 3.4.10
81
+ signing_key:
65
82
  specification_version: 4
66
- summary: Schema introspection for ActiveModel
83
+ summary: ActiveRecord schema visualization and model organization made easy
67
84
  test_files: []