pg_graph 0.1.1 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28f5cb17edb384621612f29426084e14f15db45572e5ba80482776b4ca04b3ce
4
- data.tar.gz: e2b1cf4db09e79d2a2d726dbd4ecdaa22fc82ddc4fbd94e9b10cb15220712096
3
+ metadata.gz: ea523768bdb76ecb1e4d8a8cee3d1b4afbad3e5735964acf63f78d5152050cf5
4
+ data.tar.gz: a29f1abbbdb5b674090a503c9db9ab0357140035b2c5af62a07352d38d5f3009
5
5
  SHA512:
6
- metadata.gz: 27ab8c10fbf8717e6086983cab0ce5cc32908c41c4d66402031a5bbac12b9ea623f095a4d9a9b6ac02018b34eac6dc2718be50c7c6449698008ea2b348baa4be
7
- data.tar.gz: 8eca990262bb1e8fde6e5dcab49a40a2677ce368528d9485e4742c924cab1225994ac68d9e8f507f18e671ca413998cb73819b2a671cc2476dd850444754979e
6
+ metadata.gz: ad4d4f581462c3b982d7e7504a6277e7585e1d422974a6058ca9ba54c4e30ed48ba5ba38e88504d449caa8690c7664a46366187e2ceab7e19a2f8f26abb944ac
7
+ data.tar.gz: fff6a163e030d2dfd1187c07e6e69d0da9dbef18b4c0ef2fda393dfe7f9725e61c21f47febed69199b553b17f0285f16c30951e623e0143f0dd37d302228fd8b
data/Gemfile CHANGED
@@ -3,10 +3,3 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in pg_graph.gemspec
4
4
  gemspec
5
5
 
6
- gem "rake", "~> 12.0"
7
- gem "rspec", "~> 3.0"
8
-
9
- gem 'shellopts', :github => 'clrgit/shellopts', branch: "master"
10
- gem 'pg_conn', :github => 'clrgit/pg_conn', branch: "master"
11
- gem 'pg_meta', :github => 'clrgit/pg_meta', branch: "master"
12
-
data/exe/pg_graph CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ #$LOAD_PATH.unshift "/home/clr/prj/shellopts/lib"
4
+
3
5
  require 'shellopts'
4
6
  require 'pg_graph.rb'
5
7
  require "pg_graph/timer.rb"
@@ -12,14 +14,17 @@ SPEC = %(
12
14
  pg_graph is a utility that uses the PgGraph module to load, dump, or clean a
13
15
  database. It uses a variety of formats that can be executed by psql(1)
14
16
  ('psql') or by the Postgres library exec() call ('exec', 'sql'). It can also
15
- read and write Yaml data ('yaml'). The dump command can also write the meta
16
- data and type of the database in yaml format which is useful for debugging.
17
+ read and write Yaml data ('yaml') that is useful for exchanging data with
18
+ other programs
17
19
 
18
- The default is to dump the type in yaml format, this is also the typical use
19
- of pg_graph because dump of data is better done using 'pg_dump -a <database>'
20
- except in the special case when the data contains circular foreign-key
21
- constraints and the load command is run by an ordinary user. The load command
22
- is useful when you want to read Yaml data from other programs into the database
20
+ Note that when you're loading or dumping data in one of the SQL formats,
21
+ you're almost always better off using postgres' own tools
22
+ (pg_dump/pg_restore/pg_sql), except in the special case when the data
23
+ contains circular foreign-key constraints and you don't have superuser
24
+ privileges
25
+
26
+ The dump command can also write the type of the database in yaml format. This
27
+ is the default
23
28
 
24
29
  -m,meta=EFILE @ Load meta data from YAML file
25
30
  Make pg_graph loads its meta data in YAML format from FILE instead of
@@ -29,9 +34,23 @@ SPEC = %(
29
34
  Make pg_graph loads its meta data from a marshalled PgMeta object instead
30
35
  of quering the database
31
36
 
37
+ +i,ignore=SCHEMA
38
+ Exclude objects in the given schema. This option can be repeated
39
+
32
40
  -r,reflections=EFILE
33
41
  Load reflections from FILE
34
42
 
43
+ -t,reflections-table=TABLE_UID?
44
+ Read/write reflections in the given table. The table argument can be both a
45
+ full UID (eg. "my_schema.my_reflections") or just a table name in the public
46
+ schema. Default is "public.reflections". The table and schema is created
47
+ if not present.
48
+
49
+ If this option is used together with the --reflections option, the
50
+ reflections are read from th e given file and written back to the database.
51
+ If both options are missing, pg_graph will look for the default table and
52
+ load it if present
53
+
35
54
  -f,format=FORMAT:sql,exec,psql,yaml
36
55
  Input/output format. Can be one of 'sql', 'exec', 'psql', or 'yaml' (default)
37
56
 
@@ -47,7 +66,7 @@ SPEC = %(
47
66
  -k,kind=KIND:meta,type,data
48
67
  Output kind. Can be one of 'meta', 'type' (the default), or 'data'
49
68
 
50
- -t,time
69
+ --time
51
70
  Emit timings for process
52
71
 
53
72
  load! -- DATABASE [FILE]
@@ -67,24 +86,41 @@ SPEC = %(
67
86
  #
68
87
  def load_type(timer, opts, database)
69
88
  tg = timer.group("initialization")
70
- connection = tg.time("connect") { PgConn.new(database) if !opts.meta? && !opts.marshal? }
89
+ conn = tg.time("connect") { PgConn.new(database) if !opts.meta? && !opts.marshal? }
71
90
  meta = tg.time("meta") {
72
91
  if opts.meta?
73
92
  PgMeta.load_file(opts.meta)
74
93
  elsif opts.marshal?
75
94
  PgMeta.load_marshal(opts.marshal)
76
95
  else
77
- PgMeta.new(connection)
96
+ PgMeta.new(conn)
78
97
  end
79
98
  }
80
99
  reflector = tg.time("reflector") {
81
- opts.reflections? ? PgGraph::Reflector.load_file(opts.reflections) : nil
100
+ table, schema = opts.reflections_table&.split(".")&.reverse
101
+ schema ||= PgGraph::Reflector::REFLECTIONS_SCHEMA
102
+ table ||= PgGraph::Reflector::REFLECTIONS_TABLE
103
+
104
+ if opts.reflections?
105
+ r = PgGraph::Reflector.load_file(opts.reflections)
106
+ r.save_table(conn, schema, table) if opts.reflections_table?
107
+ r
108
+ else
109
+ if opts.reflections_table?
110
+ PgGraph::Reflector.load_table(conn, schema, table)
111
+ elsif conn.schema.exist_table?(schema, table)
112
+ PgGraph::Reflector.load_table(conn, schema, table)
113
+ else
114
+ nil
115
+ end
116
+ end
82
117
  }
83
- type = timer.time("type") { PgGraph::Type.new(meta, reflector) }
84
- [connection, type]
118
+
119
+ type = timer.time("type") { PgGraph::Type.new(meta, reflector, ignore: opts.ignore) }
120
+ [conn, type]
85
121
  end
86
122
 
87
- opts, args = ShellOpts::ShellOpts.process(SPEC, ARGV, :version => PgGraph::VERSION)
123
+ opts, args = ShellOpts::ShellOpts.process(SPEC, ARGV)
88
124
 
89
125
  timing = opts.time?
90
126
  timer = Timer::Timer.new
data/lib/data/data.rb CHANGED
@@ -160,7 +160,7 @@ module PgGraph::Data
160
160
  def write(connection, ids: {}, delete: :all)
161
161
  constrain connection, PgConn
162
162
  constrain ids, String => Integer
163
- connection.exec(to_exec_sql(ids: ids, delete: :all))
163
+ connection.exec(to_exec_sql(ids: ids, delete: delete))
164
164
  self
165
165
  end
166
166
 
@@ -26,12 +26,16 @@ module PgGraph
26
26
  # matches
27
27
  attr_reader :components
28
28
 
29
+ # True if this is a default reflection
30
+ attr_reader :default_reflection
31
+
29
32
  # +this+ and +that+ are template strings, nil, or false
30
- def initialize(match, this, that, pluralize = false)
33
+ def initialize(match, this, that, pluralize = false, default_reflection = false)
31
34
  constrain match, Regexp, String
32
35
  constrain this, String, NilClass
33
36
  constrain that, String, FalseClass, NilClass
34
37
  constrain pluralize, TrueClass, FalseClass, NilClass
38
+ constrain default_reflection, TrueClass, FalseClass, NilClass
35
39
  @match = match.is_a?(Regexp) ? match.source : match
36
40
  if @match =~ /^\/(.*)\/$/
37
41
  re = $1
@@ -45,10 +49,11 @@ module PgGraph
45
49
  @this = this
46
50
  @that = that
47
51
  @pluralize = pluralize || pluralize.nil?
52
+ @default_reflection = default_reflection || false
48
53
  end
49
54
 
50
55
  def to_yaml
51
- { match: match, this: this, that: that, pluralize: pluralize }
56
+ { match: match, this: this, that: that, pluralize: pluralize, default_reflection: default_reflection }
52
57
  end
53
58
 
54
59
  def ==(other)
@@ -66,11 +71,13 @@ module PgGraph
66
71
  }
67
72
  end
68
73
 
69
- private
70
- METHODS = %w(match this that pluralize).map(&:to_sym)
74
+ METHODS = %w(match this that pluralize default_reflection).map(&:to_sym)
71
75
  end
72
76
 
73
77
  class Reflector
78
+ REFLECTIONS_SCHEMA = "public"
79
+ REFLECTIONS_TABLE = "reflections"
80
+
74
81
  # Reflections ordered from most-specific to least-specific and newest to oldest
75
82
  def reflections()
76
83
  # assumes @reflection keys are created in descending order
@@ -140,6 +147,40 @@ module PgGraph
140
147
  load_yaml YAML.load(IO.read(file)), default_reflections: default_reflections
141
148
  end
142
149
 
150
+ def self.load_table(conn, schema = nil, table = nil, default_reflections: Reflector.default_reflections)
151
+ schema ||= REFLECTIONS_SCHEMA
152
+ table ||= REFLECTIONS_TABLE
153
+ if conn.schema.exist?(schema) && conn.schema.exist_table?(schema, table)
154
+ yaml = conn.structs(%(
155
+ select match, this, that, pluralize, false as "default_reflection"
156
+ from #{schema}.#{table}
157
+ order by ordinal
158
+ )).map(&:to_h)
159
+ else
160
+ yaml = {}
161
+ end
162
+ load_yaml yaml, default_reflections: default_reflections
163
+ end
164
+
165
+ def save_table(conn, schema = nil, table = nil)
166
+ schema ||= REFLECTIONS_SCHEMA
167
+ table ||= REFLECTIONS_TABLE
168
+ self.class.ensure_table(conn, schema, table)
169
+ values = reflections.select { |r| !r.default_reflection }.map.with_index { |reflection, i|
170
+ values = "(" + Reflection::METHODS.map { |m|
171
+ v = reflection.send(m)
172
+ v.nil? ? "null" : "'#{v}'"
173
+ }.join(", ") + ", #{i})"
174
+ }.join(",\n")
175
+
176
+ conn.exec %(
177
+ insert into #{schema}.#{table}
178
+ (match, this, that, pluralize, default_reflection, ordinal)
179
+ values
180
+ #{values}
181
+ )
182
+ end
183
+
143
184
  def self.default_reflections
144
185
  @default_reflections ||= begin
145
186
  initializers = [
@@ -153,7 +194,9 @@ module PgGraph
153
194
  {"match"=>"/(\\w+)_id/", "this"=>"$1", "that"=>"$$"},
154
195
  {"match"=>"/(\\w+)/", "this"=>"$1", "that"=>"$$"}, # Kind fields w/o explicit 'kind'
155
196
  ]
156
- initializers.map { |initializer| Reflection.load_yaml(initializer) }
197
+ initializers.map { |initializer|
198
+ Reflection.load_yaml initializer.merge(default_reflection: true)
199
+ }
157
200
  end
158
201
  end
159
202
 
@@ -179,6 +222,29 @@ module PgGraph
179
222
  end
180
223
  }
181
224
  end
225
+
226
+ def self.create_table(conn, schema, table)
227
+ conn.exec %(
228
+ create table #{schema}.#{table} (
229
+ id integer generated by default as identity primary key,
230
+ match varchar not null,
231
+ this varchar,
232
+ that varchar not null,
233
+ pluralize boolean not null,
234
+ default_reflection boolean not null,
235
+ ordinal integer not null -- zero-based
236
+ )
237
+ )
238
+ end
239
+
240
+ def self.ensure_table(conn, schema, table)
241
+ conn.schema.create(schema) if !conn.schema.exist?(schema)
242
+ if conn.schema.exist_table?(schema, table)
243
+ conn.exec "truncate #{schema}.#{table}"
244
+ else
245
+ create_table(conn, schema, table)
246
+ end
247
+ end
182
248
  end
183
249
  end
184
250
 
@@ -1,3 +1,3 @@
1
1
  module PgGraph
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.4"
3
3
  end
data/lib/pg_graph.rb CHANGED
@@ -39,26 +39,29 @@ module PgGraph
39
39
  # new(meta, reflect = nil)
40
40
  # new(pg_conn, reflect = nil)
41
41
  #
42
- # The +reflect+ argument can be a Reflector object, an yaml array, a file
43
- # name or nil. The +ignore+ option is a list of schema names to exclude
44
- # from the type system
42
+ # The +reflections+ argument can be a Reflector object, an yaml array, a
43
+ # file name, a PgConn object, or nil. If +reflections+ is a PgConn object,
44
+ # the +schema+ argument should be set to the schema containing the
45
+ # 'reflections' table. The +ignore+ option is a list of schema names to
46
+ # exclude from the type system
45
47
  #
46
48
  # Note that together with ::=== and Database#is_a? this makes
47
49
  # Type::Database pretend it is an instance of the Type module
48
50
  #
49
- def self.new(arg, reflect = nil, ignore: [])
51
+ def self.new(arg, reflections = nil, schema = nil, ignore: [])
50
52
  constrain arg, PgMeta, PgConn
51
- constrain reflect, Reflector, Array, String, NilClass
53
+ constrain reflections, Reflector, Array, String, PgConn, NilClass
52
54
  meta =
53
55
  case arg
54
56
  when PgMeta; arg
55
57
  when PgConn; PgMeta.new(arg)
56
58
  end
57
59
  reflector =
58
- case reflect
59
- when Reflector; reflect
60
- when Array; Reflector.load_yaml(reflect)
61
- when String; Reflector.load_file(reflect)
60
+ case reflections
61
+ when Reflector; reflections
62
+ when Array; Reflector.load_yaml(reflections)
63
+ when String; Reflector.load_file(reflections)
64
+ when PgConn; Reflector.load_table(reflections, schema)
62
65
  when NilClass; Reflector.new
63
66
  end
64
67
  Database.new(meta.name, reflector).read(meta, ignore: ignore)
data/lib/type/type.rb CHANGED
@@ -173,7 +173,7 @@ module PgGraph::Type
173
173
  def initialize(
174
174
  schema, name,
175
175
  mm_table: false, nm_table: false, depending_materialized_views: [])
176
- PgGraph.inflector.plural?(name) or raise Error, "Table names should be plural: #{name.inspect}"
176
+ PgGraph.inflector.plural?(name) or raise Error, "Table names should be plural: #{schema.name}.#{name}"
177
177
  super(schema, name)
178
178
  @path = "#{schema.name}.#{name}"
179
179
  @mm_table = mm_table || nm_table
data/pg_graph.gemspec CHANGED
@@ -35,6 +35,10 @@ Gem::Specification.new do |spec|
35
35
  spec.add_dependency "pg_conn"
36
36
  spec.add_dependency "pg_meta"
37
37
 
38
+ spec.add_development_dependency "rake"
39
+ spec.add_development_dependency "rspec"
40
+ spec.add_development_dependency "simplecov"
41
+
38
42
  # Also un-comment in spec/spec_helper to use simplecov
39
43
  # spec.add_development_dependency "simplecov"
40
44
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_graph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claus Rasmussen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-06 00:00:00.000000000 Z
11
+ date: 2022-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: boolean
@@ -136,6 +136,48 @@ dependencies:
136
136
  - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rake
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rspec
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: simplecov
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
139
181
  description: dwpg_graph gem
140
182
  email:
141
183
  - claus.l.rasmussen@gmail.com