pg_graph 0.1.3 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 49b8ffc6fb32516ede1d4f33e9c4b4cf06d3966a279eebd3a137959c6da5e37c
4
- data.tar.gz: b08ba8c452e083f1ae581387bc88bc9acea229a0e416b6f935d10e453753e2e9
3
+ metadata.gz: ea523768bdb76ecb1e4d8a8cee3d1b4afbad3e5735964acf63f78d5152050cf5
4
+ data.tar.gz: a29f1abbbdb5b674090a503c9db9ab0357140035b2c5af62a07352d38d5f3009
5
5
  SHA512:
6
- metadata.gz: 7def3f805b7404546af5cdc8d524d14d177e2619b077baeadc513f94b5559d09a8cf7764269e2323acc8944e17132f9db559db4c518cc3f55246c6ad95105cab
7
- data.tar.gz: 32e6f9e13430873f529ef91a2a350407abe7bdf6588fa45b2781a566466d8e7dca3276f6af91c7b56007c1a8b709f1de83fae82cda156dc576bc7565113fb224
6
+ metadata.gz: ad4d4f581462c3b982d7e7504a6277e7585e1d422974a6058ca9ba54c4e30ed48ba5ba38e88504d449caa8690c7664a46366187e2ceab7e19a2f8f26abb944ac
7
+ data.tar.gz: fff6a163e030d2dfd1187c07e6e69d0da9dbef18b4c0ef2fda393dfe7f9725e61c21f47febed69199b553b17f0285f16c30951e623e0143f0dd37d302228fd8b
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"
@@ -32,9 +34,23 @@ SPEC = %(
32
34
  Make pg_graph loads its meta data from a marshalled PgMeta object instead
33
35
  of quering the database
34
36
 
37
+ +i,ignore=SCHEMA
38
+ Exclude objects in the given schema. This option can be repeated
39
+
35
40
  -r,reflections=EFILE
36
41
  Load reflections from FILE
37
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
+
38
54
  -f,format=FORMAT:sql,exec,psql,yaml
39
55
  Input/output format. Can be one of 'sql', 'exec', 'psql', or 'yaml' (default)
40
56
 
@@ -50,7 +66,7 @@ SPEC = %(
50
66
  -k,kind=KIND:meta,type,data
51
67
  Output kind. Can be one of 'meta', 'type' (the default), or 'data'
52
68
 
53
- -t,time
69
+ --time
54
70
  Emit timings for process
55
71
 
56
72
  load! -- DATABASE [FILE]
@@ -70,21 +86,38 @@ SPEC = %(
70
86
  #
71
87
  def load_type(timer, opts, database)
72
88
  tg = timer.group("initialization")
73
- 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? }
74
90
  meta = tg.time("meta") {
75
91
  if opts.meta?
76
92
  PgMeta.load_file(opts.meta)
77
93
  elsif opts.marshal?
78
94
  PgMeta.load_marshal(opts.marshal)
79
95
  else
80
- PgMeta.new(connection)
96
+ PgMeta.new(conn)
81
97
  end
82
98
  }
83
99
  reflector = tg.time("reflector") {
84
- 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
85
117
  }
86
- type = timer.time("type") { PgGraph::Type.new(meta, reflector) }
87
- [connection, type]
118
+
119
+ type = timer.time("type") { PgGraph::Type.new(meta, reflector, ignore: opts.ignore) }
120
+ [conn, type]
88
121
  end
89
122
 
90
123
  opts, args = ShellOpts::ShellOpts.process(SPEC, ARGV)
@@ -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.3"
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
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.3
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-04-24 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