pg_meta 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|