pg_meta 0.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 +7 -0
- data/.gitignore +19 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/README.md +35 -0
- data/Rakefile +6 -0
- data/TODO +3 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/pg_meta +28 -0
- data/lib/ext/hash.rb +9 -0
- data/lib/pg_meta/dump.rb +37 -0
- data/lib/pg_meta/load_conn.rb +356 -0
- data/lib/pg_meta/load_yaml.rb +76 -0
- data/lib/pg_meta/meta.rb +572 -0
- data/lib/pg_meta/version.rb +3 -0
- data/lib/pg_meta.rb +43 -0
- data/pg_meta.gemspec +50 -0
- metadata +160 -0
data/lib/pg_meta/meta.rb
ADDED
@@ -0,0 +1,572 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module PgMeta
|
4
|
+
class Node
|
5
|
+
# Database object. Equal to self for Database objects
|
6
|
+
attr_reader :root
|
7
|
+
|
8
|
+
# Parent object. nil for Database objects
|
9
|
+
attr_reader :parent
|
10
|
+
|
11
|
+
# Unique name within parent context. This is usually what we understand as
|
12
|
+
# the name of an object but functions have the full signature as "name"
|
13
|
+
attr_reader :name
|
14
|
+
|
15
|
+
def uid()
|
16
|
+
@uid ||= [parent.uid, name].compact.join(".")
|
17
|
+
end
|
18
|
+
|
19
|
+
def guid()
|
20
|
+
@guid ||= [parent.guid, name].compact.join(".")
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(parent, name)
|
24
|
+
@root = parent&.root || self
|
25
|
+
@parent = parent
|
26
|
+
@name = name
|
27
|
+
parent && parent.root.send(:add_node, self) # Add object to global database lookup
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_h() raise StandardError, "Undefined method" end
|
31
|
+
def to_yaml() to_h.to_yaml end
|
32
|
+
|
33
|
+
def inspect() "#<#{self.class}:#{guid}>" end
|
34
|
+
|
35
|
+
protected
|
36
|
+
def attrs_to_h(*attrs)
|
37
|
+
h = {}
|
38
|
+
Array(attrs).flatten.each { |attr|
|
39
|
+
value = self.send(attr)
|
40
|
+
h[attr] =
|
41
|
+
case value
|
42
|
+
when Array
|
43
|
+
if value.first.is_a?(Symbol)
|
44
|
+
value
|
45
|
+
else
|
46
|
+
value.map(&:name)
|
47
|
+
end
|
48
|
+
when Hash
|
49
|
+
value.values.map { |element| element.to_h }
|
50
|
+
when Node
|
51
|
+
value.uid
|
52
|
+
else
|
53
|
+
value
|
54
|
+
end
|
55
|
+
}
|
56
|
+
h
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def add_node(node) raise StandardError, "Undefined method" end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Database < Node
|
64
|
+
# A database has no UID
|
65
|
+
def uid() nil end
|
66
|
+
|
67
|
+
# GUID is the name of the database
|
68
|
+
def guid() name end
|
69
|
+
|
70
|
+
# Owner of the database. Defined by #load_conn
|
71
|
+
attr_reader :owner
|
72
|
+
|
73
|
+
# Hash of schemas
|
74
|
+
attr_reader :schemas
|
75
|
+
|
76
|
+
# Hash of hidden schemas
|
77
|
+
attr_reader :hidden_schemas
|
78
|
+
|
79
|
+
# Redefine Node#parent
|
80
|
+
def parent() nil end
|
81
|
+
|
82
|
+
def initialize(name, owner = nil)
|
83
|
+
super(nil, name)
|
84
|
+
@owner = owner
|
85
|
+
@schemas = {}
|
86
|
+
@hidden_schemas = {}
|
87
|
+
@nodes = {}
|
88
|
+
end
|
89
|
+
|
90
|
+
# Lookup database object by UID
|
91
|
+
def [](uid) @nodes[uid] end
|
92
|
+
|
93
|
+
# Return true if the given UID exists
|
94
|
+
def exist?(uid) @nodes.key?(uid) end
|
95
|
+
|
96
|
+
# Make Database pretend to be an instance of the PgMeta module
|
97
|
+
def is_a?(klass) klass == PgMeta or super end
|
98
|
+
|
99
|
+
def to_h() attrs_to_h(:name, :owner, :schemas) end
|
100
|
+
def to_marshal() Marshal.dump(self) end
|
101
|
+
|
102
|
+
private
|
103
|
+
def add_node(node)
|
104
|
+
@nodes[node.uid] = node
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Schema < Node
|
109
|
+
# Database of the schema
|
110
|
+
alias_method :database, :parent
|
111
|
+
|
112
|
+
# Owner of the schema
|
113
|
+
attr_reader :owner
|
114
|
+
|
115
|
+
# Hash of tables _and_ views. TODO: Rename #relations
|
116
|
+
attr_reader :tables
|
117
|
+
|
118
|
+
# Hash of views
|
119
|
+
def views() @views ||= @tables.select { |_, table| table.view? } end
|
120
|
+
|
121
|
+
# Hash of functions
|
122
|
+
attr_reader :functions
|
123
|
+
|
124
|
+
# Hash of procedures
|
125
|
+
attr_reader :procedures
|
126
|
+
|
127
|
+
def initialize(database, name, owner, hidden: false)
|
128
|
+
super(database, name)
|
129
|
+
@owner = owner
|
130
|
+
@tables = {}
|
131
|
+
@functions = {}
|
132
|
+
@procedures = {}
|
133
|
+
@hidden = hidden
|
134
|
+
database.schemas[name] = self
|
135
|
+
end
|
136
|
+
|
137
|
+
# True if schema is hidden. This can be set dynamically
|
138
|
+
def hidden?() @hidden end
|
139
|
+
|
140
|
+
# Set hidden property
|
141
|
+
def hidden=(hide)
|
142
|
+
if @hidden
|
143
|
+
database.schemas[name] = database.hidden_schemas.delete(name) if !hide
|
144
|
+
else
|
145
|
+
database.hidden_schemas[name] = database.schemas.delete(name) if hide
|
146
|
+
end
|
147
|
+
@hidden = hide
|
148
|
+
end
|
149
|
+
|
150
|
+
def to_h() attrs_to_h(:name, :owner, :tables, :views, :functions, :procedures) end
|
151
|
+
end
|
152
|
+
|
153
|
+
class Table < Node
|
154
|
+
# Schema of the table
|
155
|
+
alias_method :schema, :parent
|
156
|
+
|
157
|
+
# True iff table is a real table and not a view
|
158
|
+
def table?() true end
|
159
|
+
|
160
|
+
# True iff table is a view
|
161
|
+
def view?() !table? end
|
162
|
+
|
163
|
+
# True iff table is a materialized view
|
164
|
+
def materialized?() false end
|
165
|
+
|
166
|
+
# True if the table/view is insertable
|
167
|
+
def insertable?() @is_insertable end
|
168
|
+
|
169
|
+
# True if the table/view is typed
|
170
|
+
def typed?() @is_typed end
|
171
|
+
|
172
|
+
# Hash of columns
|
173
|
+
attr_reader :columns
|
174
|
+
|
175
|
+
# The primary key column. nil if the table has multiple primary key columns
|
176
|
+
def primary_key_column
|
177
|
+
return @primary_key_column if @primary_key_column != :undefined
|
178
|
+
if primary_key_columns.size == 1
|
179
|
+
@primary_key_column = primary_key_columns.first
|
180
|
+
else
|
181
|
+
@primary_key_column = nil
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# List of primary key columns
|
186
|
+
#
|
187
|
+
# Note: Assigned by PrimaryKeyConstraint#initialize
|
188
|
+
attr_reader :primary_key_columns
|
189
|
+
|
190
|
+
# Hash of all constraints
|
191
|
+
attr_reader :constraints
|
192
|
+
|
193
|
+
# List of primary key constraints (there is only one element)
|
194
|
+
attr_reader :primary_key_constraints
|
195
|
+
|
196
|
+
# Hash of unique constraints. Maps from constraint name to UniqueConstraint
|
197
|
+
# object. Note that because constraint names are unpredictable, you'll most
|
198
|
+
# often use +unqiue_constraints.values?
|
199
|
+
attr_reader :unique_constraints
|
200
|
+
|
201
|
+
# Hash of check constraints. Maps from constraint name to CheckConstraint
|
202
|
+
# object object
|
203
|
+
attr_reader :check_constraints
|
204
|
+
|
205
|
+
# Hash of referential constraints. Maps from constraint name to
|
206
|
+
# ReferentialConstraint object
|
207
|
+
attr_reader :referential_constraints
|
208
|
+
|
209
|
+
# Hash of triggers. Maps from trigger name to Trigger object
|
210
|
+
attr_reader :triggers
|
211
|
+
|
212
|
+
# List of tables that directly or indirectly depends on this table. Note
|
213
|
+
# that the tables are sorted by name to make testing in rspec easier
|
214
|
+
def depending_tables() @depending_tables ||= @depending_tables_hash.keys.sort_by(&:uid) end
|
215
|
+
|
216
|
+
# List of views that directly or indirectly depends on this table. This is
|
217
|
+
# the opposite of View#defining_tables. Note that the tables are sorted by
|
218
|
+
# name to make testing in rspec easier
|
219
|
+
def depending_views() @depending_views ||= @depending_views_hash.keys.sort_by(&:uid) end
|
220
|
+
|
221
|
+
def initialize(schema, name, is_insertable, is_typed)
|
222
|
+
super(schema, name)
|
223
|
+
@is_insertable = is_insertable
|
224
|
+
@is_typed = is_typed
|
225
|
+
@columns = {}
|
226
|
+
@primary_key_column = :undefined
|
227
|
+
@primary_key_columns = []
|
228
|
+
@constraints = {}
|
229
|
+
@primary_key_constraints = []
|
230
|
+
@unique_constraints = {}
|
231
|
+
@check_constraints = {}
|
232
|
+
@referential_constraints = {}
|
233
|
+
@triggers = {}
|
234
|
+
@depending_tables_hash = {}
|
235
|
+
@depending_views_hash = {}
|
236
|
+
schema.tables[name] = self
|
237
|
+
end
|
238
|
+
|
239
|
+
def to_h
|
240
|
+
attrs_to_h(
|
241
|
+
:name, :table?, :view?, :materialized?, :insertable?, :typed?,
|
242
|
+
:columns, :primary_key_columns, :constraints,
|
243
|
+
:referential_constraints, :depending_tables, :depending_views, :triggers)
|
244
|
+
end
|
245
|
+
|
246
|
+
protected
|
247
|
+
# Only non-empty for view objects
|
248
|
+
def defining_tables() [] end
|
249
|
+
|
250
|
+
private
|
251
|
+
# Accessed by MetaDb::load_conn
|
252
|
+
def add_depending_table(table)
|
253
|
+
@depending_tables = nil
|
254
|
+
@depending_tables_hash[table] = true
|
255
|
+
end
|
256
|
+
|
257
|
+
# Accessed by MetaDb::load_conn
|
258
|
+
def add_depending_view(view)
|
259
|
+
@depending_views = nil
|
260
|
+
@depending_views_hash[view] = true
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class View < Table
|
265
|
+
# List of views and tables used directly in the definition of this view
|
266
|
+
attr_reader :defining_relations
|
267
|
+
|
268
|
+
# List of tables used directly or indirectly in the definition of this view
|
269
|
+
attr_reader :defining_tables
|
270
|
+
|
271
|
+
def table?() false end
|
272
|
+
|
273
|
+
def initialize(schema, name, is_insertable, is_typed)
|
274
|
+
super
|
275
|
+
@defining_relations = []
|
276
|
+
@defining_tables = []
|
277
|
+
end
|
278
|
+
|
279
|
+
def to_h()
|
280
|
+
h = super
|
281
|
+
h[:defining_relations] = defining_relations.map(&:uid)
|
282
|
+
h[:defining_tables] = defining_tables.map(&:uid)
|
283
|
+
h
|
284
|
+
end
|
285
|
+
|
286
|
+
private
|
287
|
+
# Used by MetaDb::load_conn
|
288
|
+
def add_defining_relation(relation)
|
289
|
+
@defining_relations << relation
|
290
|
+
@defining_tables << relation if relation.table?
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
class MaterializedView < View
|
295
|
+
def materialized?() true end
|
296
|
+
end
|
297
|
+
|
298
|
+
class Column < Node
|
299
|
+
# Table of the column
|
300
|
+
alias_method :table, :parent
|
301
|
+
|
302
|
+
# Ordinal number of the column
|
303
|
+
attr_reader :ordinal
|
304
|
+
|
305
|
+
# Type of the column
|
306
|
+
attr_reader :type
|
307
|
+
|
308
|
+
# Element type if type is an array and nil otherwise
|
309
|
+
attr_reader :element_type
|
310
|
+
|
311
|
+
# Number of dimensions if an array, 0 if not
|
312
|
+
attr_reader :dimensions
|
313
|
+
|
314
|
+
# Default value
|
315
|
+
attr_reader :default
|
316
|
+
|
317
|
+
# True if column is an identity column
|
318
|
+
def identity?() @is_identity end
|
319
|
+
|
320
|
+
# True if column is auto generated
|
321
|
+
def generated?() @is_generated end
|
322
|
+
|
323
|
+
# True if column is nullable
|
324
|
+
def nullable?() @is_nullable end
|
325
|
+
|
326
|
+
# True if column is updatable
|
327
|
+
def updatable?() @is_updatable end
|
328
|
+
|
329
|
+
# True if column is an array
|
330
|
+
def array?() !@element_type.nil? end
|
331
|
+
|
332
|
+
# True if column is unique (not that this information is not stored in the
|
333
|
+
# Column but in a table constraint)
|
334
|
+
def unique?()
|
335
|
+
table.unique_constraints.values.any? { |constraint| constraint.columns == [self] }
|
336
|
+
end
|
337
|
+
|
338
|
+
# True if column is the single primary key of the table and false otherwise. Always nil for tables
|
339
|
+
# with multiple primary keys
|
340
|
+
def primary_key?()
|
341
|
+
return nil if table.primary_key_columns.size != 1
|
342
|
+
table.table? && self == table.primary_key_column || false
|
343
|
+
end
|
344
|
+
|
345
|
+
# True is column is (part of) the primary key of the table
|
346
|
+
def primary_key_column?() table.table? && table.primary_key_columns.include?(self) end
|
347
|
+
|
348
|
+
# True if column is referencing other records. This includes columns in
|
349
|
+
# multi-column references
|
350
|
+
def reference?()
|
351
|
+
@reference ||= !references.empty?
|
352
|
+
end
|
353
|
+
|
354
|
+
# True if column is a single-column reference to another record and is of
|
355
|
+
# type varchar or text
|
356
|
+
def kind?()
|
357
|
+
@kind ||=
|
358
|
+
references.size == 1 &&
|
359
|
+
references.first.referencing_columns.size == 1 &&
|
360
|
+
!references.first.referenced_columns.first.primary_key?
|
361
|
+
end
|
362
|
+
|
363
|
+
# List of referential constraints that involve this column
|
364
|
+
def references
|
365
|
+
@references ||= begin
|
366
|
+
table.referential_constraints.values.select { |constraint|
|
367
|
+
constraint.referencing_columns.include?(self)
|
368
|
+
}
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def initialize(
|
373
|
+
table, name, ordinal, type, element_type, dimensions, default,
|
374
|
+
is_identity, is_generated, is_nullable, is_updatable)
|
375
|
+
super(table, name)
|
376
|
+
@type, @element_type, @dimensions, @ordinal, @default, @is_identity,
|
377
|
+
@is_generated, @is_nullable, @is_updatable =
|
378
|
+
type, element_type, dimensions, ordinal, default, is_identity,
|
379
|
+
is_generated, is_nullable, is_updatable
|
380
|
+
table.columns[name] = self
|
381
|
+
end
|
382
|
+
|
383
|
+
def to_h()
|
384
|
+
attrs_to_h(
|
385
|
+
:name, :ordinal, :type, :element_type, :dimensions, :default, :identity?, :generated?,
|
386
|
+
:nullable?, :updatable?, :primary_key?, :reference?, :kind?)
|
387
|
+
end
|
388
|
+
|
389
|
+
# Compare columns by table and ordinal. FIXME: Compare by schema too (and what's with the 'super'?)
|
390
|
+
def <=>(other)
|
391
|
+
if other.is_a?(Column) && table == other.table
|
392
|
+
ordinal <=> other.ordinal
|
393
|
+
else
|
394
|
+
super
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
class Constraint < Node
|
400
|
+
# Table of the constraint
|
401
|
+
alias_method :table, :parent
|
402
|
+
|
403
|
+
# Constraint column. Raise an error if the constraint is multi-column
|
404
|
+
def column
|
405
|
+
columns.size == 1 or raise "Multicolumn constraint"
|
406
|
+
columns.first
|
407
|
+
end
|
408
|
+
|
409
|
+
# List of columns in the constraint. Empty for CheckConstraint objects
|
410
|
+
# except not null constraints
|
411
|
+
attr_reader :columns
|
412
|
+
|
413
|
+
# Constraint kind. Either :primary_key, :unique, :check, or :referential
|
414
|
+
def kind() CONSTRAINT_KINDS[self.class] end
|
415
|
+
|
416
|
+
def initialize(table, name, columns)
|
417
|
+
super(table, name)
|
418
|
+
@table = table
|
419
|
+
@columns = columns
|
420
|
+
table.constraints[name] = self
|
421
|
+
end
|
422
|
+
|
423
|
+
def to_h() attrs_to_h(:name, :kind, :columns) end
|
424
|
+
end
|
425
|
+
|
426
|
+
class PrimaryKeyConstraint < Constraint
|
427
|
+
def initialize(table, name, columns)
|
428
|
+
super
|
429
|
+
columns.each { |c| c.table.primary_key_columns << c }
|
430
|
+
table.primary_key_constraints << self
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
class UniqueConstraint < Constraint
|
435
|
+
def initialize(table, name, columns)
|
436
|
+
super
|
437
|
+
table.unique_constraints[name] = self
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
# Note that #columns is always empty for check constraints
|
442
|
+
class CheckConstraint < Constraint
|
443
|
+
# SQL check expression
|
444
|
+
attr_reader :expression
|
445
|
+
|
446
|
+
# Half-baked SQL-to-ruby expression transpiler
|
447
|
+
def ruby_expression # Very simple
|
448
|
+
@ruby ||= sql.sub(/\((.*)\)/, "\\1").gsub(/\((\w+) IS NOT NULL\)/, "!\\1.nil?").gsub(/ OR /, " || ")
|
449
|
+
end
|
450
|
+
|
451
|
+
# True if this is a not-null check constraint. In that case, #columns will
|
452
|
+
# contain the column which is otherwise empty for CheckConstraint objects.
|
453
|
+
# Note that the column is not registered directly in Postgres meta tables
|
454
|
+
# and have to be parsed from the expression
|
455
|
+
def not_null?() !columns.empty? end
|
456
|
+
|
457
|
+
def initialize(table, name, expression)
|
458
|
+
super(table, name, [])
|
459
|
+
@expression = expression
|
460
|
+
table.check_constraints[name] = self
|
461
|
+
if @expression =~ /^\(\((\S+) IS NOT NULL\)\)$/
|
462
|
+
columns = [table.columns[$1]]
|
463
|
+
else
|
464
|
+
columns = []
|
465
|
+
end
|
466
|
+
super(table, name, columns)
|
467
|
+
end
|
468
|
+
|
469
|
+
def to_h() attrs_to_h(:name, :kind, :expression) end
|
470
|
+
end
|
471
|
+
|
472
|
+
class ReferentialConstraint < Constraint
|
473
|
+
# The referencing table
|
474
|
+
alias_method :referencing_table, :table
|
475
|
+
|
476
|
+
# The referencing columns. Can't be empty
|
477
|
+
alias_method :referencing_columns, :columns
|
478
|
+
|
479
|
+
# The referenced constraint
|
480
|
+
attr_reader :referenced_constraint
|
481
|
+
|
482
|
+
# The referenced table
|
483
|
+
def referenced_table() referenced_constraint.table end
|
484
|
+
|
485
|
+
# The referenced columns
|
486
|
+
def referenced_columns() referenced_constraint.columns end
|
487
|
+
|
488
|
+
def initialize(referencing_table, name, referencing_columns, referenced_constraint)
|
489
|
+
super(referencing_table, name, referencing_columns)
|
490
|
+
@referenced_constraint = referenced_constraint
|
491
|
+
table.referential_constraints[name] = self
|
492
|
+
end
|
493
|
+
|
494
|
+
def to_h() attrs_to_h(:name, :kind, :referencing_columns, :referenced_constraint) end
|
495
|
+
end
|
496
|
+
|
497
|
+
class Function < Node
|
498
|
+
# Schema of the function
|
499
|
+
alias_method :schema, :parent
|
500
|
+
|
501
|
+
# Owner of the function
|
502
|
+
attr_reader :owner
|
503
|
+
|
504
|
+
# Security (:definer or :invoker)
|
505
|
+
attr_reader :security
|
506
|
+
|
507
|
+
# True if security is 'definer'
|
508
|
+
def suid?() security == 'definer' end
|
509
|
+
|
510
|
+
# True if this is a function
|
511
|
+
def function?() true end
|
512
|
+
|
513
|
+
# True if this is a procedure
|
514
|
+
def procedure?() !function? end
|
515
|
+
|
516
|
+
def initialize(schema, name, owner, security)
|
517
|
+
super(schema, name)
|
518
|
+
@owner = owner
|
519
|
+
@security = security.to_sym
|
520
|
+
if function?
|
521
|
+
schema.functions[name] = self
|
522
|
+
else
|
523
|
+
schema.procedures[name] = self
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
def to_h() attrs_to_h(:name, :owner, :security, :function?) end
|
528
|
+
end
|
529
|
+
|
530
|
+
class Procedure < Function
|
531
|
+
def function?() false end
|
532
|
+
end
|
533
|
+
|
534
|
+
class Trigger < Node
|
535
|
+
# Table of trigger
|
536
|
+
alias_method :table, :parent
|
537
|
+
|
538
|
+
# Trigger function
|
539
|
+
attr_reader :function
|
540
|
+
|
541
|
+
# Trigger level (:stmt or :row)
|
542
|
+
attr_reader :level
|
543
|
+
|
544
|
+
# When trigger is fired (:before, :after, or :instead)
|
545
|
+
attr_reader :timing
|
546
|
+
|
547
|
+
# Array of events (:insert, :update, :delete, or :truncate) causing the trigger to fire
|
548
|
+
attr_reader :events
|
549
|
+
|
550
|
+
# Note that trigger names have a '()' suffixed. This avoid namespace
|
551
|
+
# collisions with field names
|
552
|
+
def initialize(table, name, function, level, timing, events)
|
553
|
+
super(table, name)
|
554
|
+
@name = "#{name}()"
|
555
|
+
@function = function
|
556
|
+
@level = level.to_sym
|
557
|
+
@timing = timing.to_sym
|
558
|
+
@events = events
|
559
|
+
table.triggers[@name] = self
|
560
|
+
end
|
561
|
+
|
562
|
+
def to_h() attrs_to_h(:name, :function, :level, :timing, :events) end
|
563
|
+
end
|
564
|
+
|
565
|
+
CONSTRAINT_KINDS = {
|
566
|
+
PrimaryKeyConstraint => :primary_key,
|
567
|
+
UniqueConstraint => :unique,
|
568
|
+
CheckConstraint => :check,
|
569
|
+
ReferentialConstraint => :referential
|
570
|
+
}
|
571
|
+
end
|
572
|
+
|
data/lib/pg_meta.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
require "pg"
|
3
|
+
require "pg_conn"
|
4
|
+
|
5
|
+
require "ext/hash.rb"
|
6
|
+
|
7
|
+
require "pg_meta/version.rb"
|
8
|
+
require "pg_meta/meta.rb"
|
9
|
+
require "pg_meta/load_conn.rb"
|
10
|
+
require "pg_meta/load_yaml.rb"
|
11
|
+
require "pg_meta/dump.rb"
|
12
|
+
|
13
|
+
module PgMeta
|
14
|
+
class Error < StandardError; end
|
15
|
+
|
16
|
+
# :call-seq:
|
17
|
+
# initialize(pg_conn_connection)
|
18
|
+
# initialize(*pg_conn_initializers)
|
19
|
+
#
|
20
|
+
# Initialize a Database object
|
21
|
+
#
|
22
|
+
def self.new(*args)
|
23
|
+
Database.load_conn(PgConn.ensure(*args))
|
24
|
+
end
|
25
|
+
|
26
|
+
# Load data from a YAML object
|
27
|
+
def self.load_yaml(yaml)
|
28
|
+
Database.load_yaml(yaml)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Load data from a YAML file
|
32
|
+
def self.load_file(file)
|
33
|
+
load_yaml(YAML.load(IO.read file))
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.load_marshal(file)
|
37
|
+
Marshal.load(IO.read file)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Make the PgMeta module pretend to have PgMeta::Database object instances
|
41
|
+
def self.===(element) element.is_a?(PgMeta::Database) or super end
|
42
|
+
end
|
43
|
+
|
data/pg_meta.gemspec
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "pg_meta/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "pg_meta"
|
8
|
+
spec.version = PgMeta::VERSION
|
9
|
+
spec.authors = ["Claus Rasmussen"]
|
10
|
+
spec.email = ["claus.l.rasmussen@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{pg_meta gem}
|
13
|
+
spec.description = %q{pg_meta gem}
|
14
|
+
spec.homepage = "http://www.nowhere.com/"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
# if spec.respond_to?(:metadata)
|
19
|
+
#
|
20
|
+
# spec.metadata["homepage_uri"] = spec.homepage
|
21
|
+
# else
|
22
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
23
|
+
# "public gem pushes."
|
24
|
+
# end
|
25
|
+
|
26
|
+
# Specify which files should be added to the gem when it is released.
|
27
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
28
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
29
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
30
|
+
end
|
31
|
+
spec.bindir = "exe"
|
32
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
33
|
+
spec.require_paths = ["lib"]
|
34
|
+
|
35
|
+
spec.add_development_dependency "bundler"
|
36
|
+
spec.add_development_dependency "rake"
|
37
|
+
spec.add_development_dependency "rspec"
|
38
|
+
|
39
|
+
# spec.add_dependency GEM [, VERSION]
|
40
|
+
spec.add_dependency "indented_io"
|
41
|
+
spec.add_dependency "pg"
|
42
|
+
spec.add_dependency "pg_conn", "0.2.1"
|
43
|
+
spec.add_dependency "shellopts", "2.0.6"
|
44
|
+
|
45
|
+
# spec.add_development_dependency GEM [, VERSION]
|
46
|
+
|
47
|
+
# Also un-comment in spec/spec_helper to use simplecov
|
48
|
+
# spec.add_development_dependency "simplecov"
|
49
|
+
|
50
|
+
end
|