rails-schema 0.1.7 → 0.1.8
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/CLAUDE.md +2 -1
- data/lib/rails/schema/assets/app.js +23 -2
- data/lib/rails/schema/assets/style.css +28 -0
- data/lib/rails/schema/extractor/column_reader.rb +17 -2
- data/lib/rails/schema/extractor/schema_file_parser.rb +8 -0
- data/lib/rails/schema/extractor/structure_sql_parser.rb +96 -2
- data/lib/rails/schema/transformer/graph_builder.rb +4 -2
- data/lib/rails/schema/transformer/node.rb +4 -2
- data/lib/rails/schema/version.rb +1 -1
- data/lib/rails/schema.rb +14 -7
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d97a36c459cf91334bda4a6feb1bb99222432a71d75cf8fa368215facf465012
|
|
4
|
+
data.tar.gz: 5851a6d08e91c981fc15801a4cb805bb107079e8ede44c6ea5dc6da033302253
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 968db5097006071930a3972b96d7aa6b0a616133999aee055e45466612f6141dff125f6e38adb426c60dbf4dfc4ab31e1171613637577164a825e4db2be0684e
|
|
7
|
+
data.tar.gz: 735d4895a9e7b80ade035acfeab3439b810b572e5ff4835dfe1e0c2160d727aa59f9616910eecd72316813c448e429f40f50546ea33096b78f7b6ac1d3f4bd0e
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.1.8] - 2026-06-14
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- SQL view support — models backed by a SQL view (`CREATE VIEW`, e.g. `self.table_name = "some_view"`) now appear on the diagram instead of being silently dropped. `StructureSqlParser` parses `CREATE VIEW` statements (column names from the SQLite hint comment or the `SELECT` list); columns are read from live DB introspection when a connection is available (accurate types) and fall back to the parsed names offline. View nodes are visually distinguished with a "VIEW" badge and a dashed border, plus a "VIEW" tag in the detail panel.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- SQLite `structure.sql` parsing — `StructureSqlParser` now correctly extracts all columns from SQLite-format dumps, which place the entire `CREATE TABLE` on a single line. Previously only the primary key was read (every model rendered with just its `id`). Column splitting is now parenthesis- and quote-aware, so commas inside type modifiers (`decimal(5,4)`), `FOREIGN KEY` clauses, and string literals no longer break parsing; inline C-style comments (`/* ... */`) are stripped. PostgreSQL `pg_dump` output continues to parse as before.
|
|
16
|
+
|
|
7
17
|
## [0.1.7] - 2026-04-25
|
|
8
18
|
|
|
9
19
|
### Added
|
data/CLAUDE.md
CHANGED
|
@@ -47,9 +47,10 @@ end
|
|
|
47
47
|
### Data Extraction Strategy
|
|
48
48
|
|
|
49
49
|
1. **`db/schema.rb`** — `SchemaFileParser` parses with regex (table names, columns, types, nullable, defaults, PKs)
|
|
50
|
-
2. **`db/structure.sql`** — `StructureSqlParser` parses SQL `CREATE TABLE` statements, maps SQL types to Rails types
|
|
50
|
+
2. **`db/structure.sql`** — `StructureSqlParser` parses SQL `CREATE TABLE` statements, maps SQL types to Rails types. Column splitting is parenthesis/quote-aware (a top-level-comma splitter), so it handles both PostgreSQL `pg_dump` (one column per line) and SQLite (whole table on one line) dumps. Also parses `CREATE VIEW` statements — view names go into `#views` (a Set), and view column names come from the SQLite hint comment (`/* viewname(col,...) */`) or the `SELECT` list
|
|
51
51
|
3. **ActiveRecord reflection API** — `AssociationReader` uses `reflect_on_all_associations` for associations
|
|
52
52
|
4. **`Model.columns`** — `ColumnReader` falls back to this when table not found in schema_data
|
|
53
|
+
5. **SQL views** — `parse_schema` returns `[columns, view_names]`. `ColumnReader.new(view_tables:)` reads view columns from live DB introspection first (accurate types), falling back to the parsed names offline. `GraphBuilder.new(view_tables:)` sets `Node#view`, which the frontend renders with a "VIEW" badge + dashed border. Views are always included (no config flag); the model just needs `self.table_name` pointing at the view
|
|
53
54
|
|
|
54
55
|
### Model Discovery
|
|
55
56
|
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
var NODE_HEADER_HEIGHT = 36;
|
|
59
59
|
var NODE_COLUMN_HEIGHT = 18;
|
|
60
60
|
var NODE_PADDING = 8;
|
|
61
|
+
var VIEW_BADGE_WIDTH = 34;
|
|
61
62
|
|
|
62
63
|
// Layout persistence
|
|
63
64
|
function computeFingerprint() {
|
|
@@ -707,7 +708,7 @@
|
|
|
707
708
|
|
|
708
709
|
// Background rect
|
|
709
710
|
nGroups.append("rect")
|
|
710
|
-
.attr("class", "node-rect")
|
|
711
|
+
.attr("class", function(d) { return "node-rect" + (d.view ? " is-view" : ""); })
|
|
711
712
|
.attr("width", NODE_WIDTH)
|
|
712
713
|
.attr("height", function(d) { return d._height; })
|
|
713
714
|
.attr("x", -NODE_WIDTH / 2)
|
|
@@ -755,6 +756,24 @@
|
|
|
755
756
|
.attr("dominant-baseline", "central")
|
|
756
757
|
.text(function(d) { return truncateText(d.table_name, NODE_WIDTH - NODE_PADDING * 2, "10px " + getComputedStyle(document.body).fontFamily); });
|
|
757
758
|
|
|
759
|
+
// VIEW badge — a pill floating just above the node, centered, for view-backed models
|
|
760
|
+
var viewNodes = nGroups.filter(function(d) { return d.view; });
|
|
761
|
+
viewNodes.append("rect")
|
|
762
|
+
.attr("class", "node-view-badge-bg")
|
|
763
|
+
.attr("width", VIEW_BADGE_WIDTH)
|
|
764
|
+
.attr("height", 13)
|
|
765
|
+
.attr("rx", 3)
|
|
766
|
+
.attr("ry", 3)
|
|
767
|
+
.attr("x", -VIEW_BADGE_WIDTH / 2)
|
|
768
|
+
.attr("y", function(d) { return -d._height / 2 - 16; });
|
|
769
|
+
viewNodes.append("text")
|
|
770
|
+
.attr("class", "node-view-badge")
|
|
771
|
+
.attr("x", 0)
|
|
772
|
+
.attr("y", function(d) { return -d._height / 2 - 9; })
|
|
773
|
+
.attr("text-anchor", "middle")
|
|
774
|
+
.attr("dominant-baseline", "central")
|
|
775
|
+
.text("VIEW");
|
|
776
|
+
|
|
758
777
|
// Columns
|
|
759
778
|
nGroups.each(function(d) {
|
|
760
779
|
var g = d3.select(this);
|
|
@@ -1034,7 +1053,9 @@
|
|
|
1034
1053
|
html += '<h2 title="' + escapeHtml(node.id) + '">' + escapeHtml(node.id) + '</h2>';
|
|
1035
1054
|
html += '<button id="detail-close" onclick="window.__closeDetail()">×</button>';
|
|
1036
1055
|
html += '</div>';
|
|
1037
|
-
html += '<div class="detail-table" title="' + escapeHtml(node.table_name) + '">' + escapeHtml(node.table_name)
|
|
1056
|
+
html += '<div class="detail-table" title="' + escapeHtml(node.table_name) + '">' + escapeHtml(node.table_name);
|
|
1057
|
+
if (node.view) html += ' <span class="detail-view-tag">VIEW</span>';
|
|
1058
|
+
html += '</div>';
|
|
1038
1059
|
|
|
1039
1060
|
html += '<h3>Columns</h3>';
|
|
1040
1061
|
html += '<ul class="column-list">';
|
|
@@ -364,6 +364,22 @@ body {
|
|
|
364
364
|
stroke-width: 2.5;
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
+
.node-rect.is-view {
|
|
368
|
+
stroke-dasharray: 5 3;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.node-view-badge-bg {
|
|
372
|
+
fill: var(--accent);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.node-view-badge {
|
|
376
|
+
fill: #ffffff;
|
|
377
|
+
font-size: 8px;
|
|
378
|
+
font-weight: 700;
|
|
379
|
+
letter-spacing: 0.5px;
|
|
380
|
+
font-family: inherit;
|
|
381
|
+
}
|
|
382
|
+
|
|
367
383
|
.node-header-rect {
|
|
368
384
|
fill: var(--node-header-bg);
|
|
369
385
|
rx: 8;
|
|
@@ -504,6 +520,18 @@ body {
|
|
|
504
520
|
white-space: nowrap;
|
|
505
521
|
}
|
|
506
522
|
|
|
523
|
+
#detail-content .detail-view-tag {
|
|
524
|
+
display: inline-block;
|
|
525
|
+
font-size: 9px;
|
|
526
|
+
font-weight: 700;
|
|
527
|
+
letter-spacing: 0.5px;
|
|
528
|
+
color: var(--node-header-text);
|
|
529
|
+
background: var(--accent);
|
|
530
|
+
border-radius: 3px;
|
|
531
|
+
padding: 1px 5px;
|
|
532
|
+
vertical-align: middle;
|
|
533
|
+
}
|
|
534
|
+
|
|
507
535
|
#detail-content h3 {
|
|
508
536
|
font-size: 13px;
|
|
509
537
|
font-weight: 600;
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "set"
|
|
4
|
+
|
|
3
5
|
module Rails
|
|
4
6
|
module Schema
|
|
5
7
|
module Extractor
|
|
6
8
|
class ColumnReader
|
|
7
|
-
def initialize(schema_data: nil)
|
|
9
|
+
def initialize(schema_data: nil, view_tables: nil)
|
|
8
10
|
@schema_data = schema_data
|
|
11
|
+
@view_tables = view_tables ? Set.new(view_tables) : Set.new
|
|
9
12
|
end
|
|
10
13
|
|
|
11
14
|
def read(model)
|
|
12
|
-
if @
|
|
15
|
+
if @view_tables.include?(model.table_name)
|
|
16
|
+
read_view(model)
|
|
17
|
+
elsif @schema_data&.key?(model.table_name)
|
|
13
18
|
@schema_data[model.table_name]
|
|
14
19
|
else
|
|
15
20
|
read_from_model(model)
|
|
@@ -18,6 +23,16 @@ module Rails
|
|
|
18
23
|
|
|
19
24
|
private
|
|
20
25
|
|
|
26
|
+
# Views have no column types in their DDL, so prefer live DB
|
|
27
|
+
# introspection (accurate names + types) and fall back to the parsed
|
|
28
|
+
# column names when no database connection is available.
|
|
29
|
+
def read_view(model)
|
|
30
|
+
from_db = read_from_model(model)
|
|
31
|
+
return from_db unless from_db.empty?
|
|
32
|
+
|
|
33
|
+
@schema_data&.fetch(model.table_name, []) || []
|
|
34
|
+
end
|
|
35
|
+
|
|
21
36
|
def read_from_model(model)
|
|
22
37
|
model.columns.map do |col|
|
|
23
38
|
{
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "set"
|
|
4
|
+
|
|
3
5
|
module Rails
|
|
4
6
|
module Schema
|
|
5
7
|
module Extractor
|
|
6
8
|
class SchemaFileParser
|
|
9
|
+
# schema.rb does not declare SQL views, so this is always empty; the
|
|
10
|
+
# reader exists so SchemaFileParser and StructureSqlParser share an
|
|
11
|
+
# interface (see Rails::Schema.parse_schema).
|
|
12
|
+
attr_reader :views
|
|
13
|
+
|
|
7
14
|
def initialize(schema_path = nil)
|
|
8
15
|
@schema_path = schema_path
|
|
16
|
+
@views = Set.new
|
|
9
17
|
end
|
|
10
18
|
|
|
11
19
|
def parse
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "set"
|
|
4
|
+
|
|
3
5
|
module Rails
|
|
4
6
|
module Schema
|
|
5
7
|
module Extractor
|
|
@@ -23,8 +25,14 @@ module Rails
|
|
|
23
25
|
CONSTRAINT_RE = /\A(CONSTRAINT|UNIQUE|CHECK|EXCLUDE|FOREIGN\s+KEY)\b/i.freeze
|
|
24
26
|
PK_CONSTRAINT_RE = /PRIMARY\s+KEY\s*\(([^)]+)\)/i.freeze
|
|
25
27
|
|
|
28
|
+
# Set of table names that are backed by SQL views (CREATE VIEW), not
|
|
29
|
+
# real tables. Populated by #parse / #parse_content. Lets downstream
|
|
30
|
+
# code badge view-backed models on the diagram.
|
|
31
|
+
attr_reader :views
|
|
32
|
+
|
|
26
33
|
def initialize(structure_path = nil)
|
|
27
34
|
@structure_path = structure_path
|
|
35
|
+
@views = Set.new
|
|
28
36
|
end
|
|
29
37
|
|
|
30
38
|
def parse
|
|
@@ -36,6 +44,7 @@ module Rails
|
|
|
36
44
|
|
|
37
45
|
def parse_content(content)
|
|
38
46
|
tables = {}
|
|
47
|
+
@views = Set.new
|
|
39
48
|
|
|
40
49
|
content.scan(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?([\w."]+)\s*\((.*?)\)\s*;/mi) do |table_name, body|
|
|
41
50
|
name = extract_table_name(table_name)
|
|
@@ -44,11 +53,52 @@ module Rails
|
|
|
44
53
|
tables[name] = columns
|
|
45
54
|
end
|
|
46
55
|
|
|
56
|
+
parse_views(content, tables)
|
|
47
57
|
tables
|
|
48
58
|
end
|
|
49
59
|
|
|
50
60
|
private
|
|
51
61
|
|
|
62
|
+
# Views carry no column-type information in their DDL, so offline we can
|
|
63
|
+
# only recover column names (from the SQLite hint comment or the SELECT
|
|
64
|
+
# list). ColumnReader prefers live DB introspection for views when a
|
|
65
|
+
# connection is available; these names are the offline fallback.
|
|
66
|
+
def parse_views(content, tables)
|
|
67
|
+
content.scan(/CREATE\s+VIEW\s+([\w."]+)\s+AS\b(.*?);/mi) do |raw_name, body|
|
|
68
|
+
name = extract_table_name(raw_name)
|
|
69
|
+
@views << name
|
|
70
|
+
tables[name] = view_columns(name, body)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def view_columns(name, body)
|
|
75
|
+
view_column_names(name, body).map do |col_name|
|
|
76
|
+
{ name: col_name, type: "", nullable: true, default: nil, primary: false }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def view_column_names(name, body)
|
|
81
|
+
if (match = body.match(%r{/\*\s*#{Regexp.escape(name)}\s*\(([^)]*)\)\s*\*/}))
|
|
82
|
+
match[1].split(",").map { |c| unquote(c.strip) }
|
|
83
|
+
else
|
|
84
|
+
select_list_aliases(body)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def select_list_aliases(body)
|
|
89
|
+
match = body.match(/\bSELECT\b(.*?)\bFROM\b/mi)
|
|
90
|
+
return [] unless match
|
|
91
|
+
|
|
92
|
+
split_columns(match[1]).filter_map { |item| column_alias(item) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def column_alias(item)
|
|
96
|
+
item = strip_comments(item).strip
|
|
97
|
+
return nil if item.empty? || item.include?("*")
|
|
98
|
+
|
|
99
|
+
item[/\bAS\s+"?(\w+)"?\s*\z/i, 1] || item[/"?(\w+)"?\s*\z/, 1]
|
|
100
|
+
end
|
|
101
|
+
|
|
52
102
|
def resolve_path
|
|
53
103
|
return @structure_path if @structure_path
|
|
54
104
|
return ::Rails.root.join("db", "structure.sql").to_s if defined?(::Rails.root) && ::Rails.root
|
|
@@ -67,8 +117,8 @@ module Rails
|
|
|
67
117
|
def parse_table_body(body)
|
|
68
118
|
columns = []
|
|
69
119
|
pk_columns = []
|
|
70
|
-
body.
|
|
71
|
-
line =
|
|
120
|
+
split_columns(body).each do |segment|
|
|
121
|
+
line = segment.strip
|
|
72
122
|
next if line.empty?
|
|
73
123
|
|
|
74
124
|
if (pk = extract_pk_constraint(line))
|
|
@@ -81,6 +131,50 @@ module Rails
|
|
|
81
131
|
[columns, pk_columns]
|
|
82
132
|
end
|
|
83
133
|
|
|
134
|
+
# Splits a table body on top-level commas, ignoring commas inside
|
|
135
|
+
# parentheses (e.g. decimal(5,4), FK clauses) or quoted strings. This
|
|
136
|
+
# handles both PostgreSQL (one column per line) and SQLite (whole table
|
|
137
|
+
# on a single line) structure.sql dumps.
|
|
138
|
+
def split_columns(body)
|
|
139
|
+
segments = []
|
|
140
|
+
current = +""
|
|
141
|
+
state = { depth: 0, squote: false, dquote: false }
|
|
142
|
+
strip_comments(body).each_char do |ch|
|
|
143
|
+
if split_point?(ch, state)
|
|
144
|
+
segments << current
|
|
145
|
+
current = +""
|
|
146
|
+
else
|
|
147
|
+
current << ch
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
segments << current
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def split_point?(char, state)
|
|
154
|
+
toggle_quote(char, state)
|
|
155
|
+
return false if quoted?(state)
|
|
156
|
+
|
|
157
|
+
case char
|
|
158
|
+
when "(" then state[:depth] += 1
|
|
159
|
+
when ")" then state[:depth] -= 1
|
|
160
|
+
when "," then return state[:depth].zero?
|
|
161
|
+
end
|
|
162
|
+
false
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def toggle_quote(char, state)
|
|
166
|
+
state[:squote] = !state[:squote] if char == "'" && !state[:dquote]
|
|
167
|
+
state[:dquote] = !state[:dquote] if char == '"' && !state[:squote]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def quoted?(state)
|
|
171
|
+
state[:squote] || state[:dquote]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def strip_comments(sql)
|
|
175
|
+
sql.gsub(%r{/\*.*?\*/}m, "")
|
|
176
|
+
end
|
|
177
|
+
|
|
84
178
|
def extract_pk_constraint(line)
|
|
85
179
|
return unless (match = line.match(PK_CONSTRAINT_RE))
|
|
86
180
|
|
|
@@ -7,10 +7,11 @@ module Rails
|
|
|
7
7
|
module Transformer
|
|
8
8
|
class GraphBuilder
|
|
9
9
|
def initialize(column_reader: Extractor::ColumnReader.new, association_reader: Extractor::AssociationReader.new,
|
|
10
|
-
configuration: ::Rails::Schema.configuration)
|
|
10
|
+
configuration: ::Rails::Schema.configuration, view_tables: nil)
|
|
11
11
|
@column_reader = column_reader
|
|
12
12
|
@association_reader = association_reader
|
|
13
13
|
@group_proc = configuration.resolved_group_proc
|
|
14
|
+
@view_tables = view_tables ? Set.new(view_tables) : Set.new
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def build(models)
|
|
@@ -47,7 +48,8 @@ module Rails
|
|
|
47
48
|
id: unique_id,
|
|
48
49
|
table_name: model.table_name,
|
|
49
50
|
columns: @column_reader.read(model),
|
|
50
|
-
group: group
|
|
51
|
+
group: group,
|
|
52
|
+
view: @view_tables.include?(model.table_name)
|
|
51
53
|
)
|
|
52
54
|
end
|
|
53
55
|
|
|
@@ -4,18 +4,20 @@ module Rails
|
|
|
4
4
|
module Schema
|
|
5
5
|
module Transformer
|
|
6
6
|
class Node
|
|
7
|
-
attr_reader :id, :table_name, :columns, :group
|
|
7
|
+
attr_reader :id, :table_name, :columns, :group, :view
|
|
8
8
|
|
|
9
|
-
def initialize(id:, table_name:, columns: [], group: [])
|
|
9
|
+
def initialize(id:, table_name:, columns: [], group: [], view: false)
|
|
10
10
|
@id = id
|
|
11
11
|
@table_name = table_name
|
|
12
12
|
@columns = columns
|
|
13
13
|
@group = group
|
|
14
|
+
@view = view
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def to_h
|
|
17
18
|
h = { id: @id, table_name: @table_name, columns: @columns }
|
|
18
19
|
h[:group] = @group unless @group.empty?
|
|
20
|
+
h[:view] = true if @view
|
|
19
21
|
h
|
|
20
22
|
end
|
|
21
23
|
end
|
data/lib/rails/schema/version.rb
CHANGED
data/lib/rails/schema.rb
CHANGED
|
@@ -52,10 +52,10 @@ module Rails
|
|
|
52
52
|
private
|
|
53
53
|
|
|
54
54
|
def generate_active_record(output:)
|
|
55
|
-
schema_data = parse_schema
|
|
55
|
+
schema_data, views = parse_schema
|
|
56
56
|
models = Extractor::ModelScanner.new(schema_data: schema_data).scan
|
|
57
|
-
column_reader = Extractor::ColumnReader.new(schema_data: schema_data)
|
|
58
|
-
graph_data = Transformer::GraphBuilder.new(column_reader: column_reader).build(models)
|
|
57
|
+
column_reader = Extractor::ColumnReader.new(schema_data: schema_data, view_tables: views)
|
|
58
|
+
graph_data = Transformer::GraphBuilder.new(column_reader: column_reader, view_tables: views).build(models)
|
|
59
59
|
graph_data[:metadata][:mode] = "active_record"
|
|
60
60
|
generator = Renderer::HtmlGenerator.new(graph_data: graph_data)
|
|
61
61
|
generator.render_to_file(output)
|
|
@@ -84,17 +84,24 @@ module Rails
|
|
|
84
84
|
generator.render_to_file(output)
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
# Returns [columns_by_table, view_table_names]. For :auto, falls through
|
|
88
|
+
# to structure.sql when schema.rb yields nothing.
|
|
87
89
|
def parse_schema
|
|
88
90
|
case configuration.schema_format
|
|
89
91
|
when :ruby
|
|
90
|
-
Extractor::SchemaFileParser.new
|
|
92
|
+
parse_with(Extractor::SchemaFileParser.new)
|
|
91
93
|
when :sql
|
|
92
|
-
Extractor::StructureSqlParser.new
|
|
94
|
+
parse_with(Extractor::StructureSqlParser.new)
|
|
93
95
|
when :auto
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
columns, views = parse_with(Extractor::SchemaFileParser.new)
|
|
97
|
+
columns.empty? ? parse_with(Extractor::StructureSqlParser.new) : [columns, views]
|
|
96
98
|
end
|
|
97
99
|
end
|
|
100
|
+
|
|
101
|
+
def parse_with(parser)
|
|
102
|
+
columns = parser.parse
|
|
103
|
+
[columns, parser.views]
|
|
104
|
+
end
|
|
98
105
|
end
|
|
99
106
|
end
|
|
100
107
|
end
|