pg_graph 0.1.2 → 0.1.5

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: 108d2c8460c15baf7814fc56fbf5f3dc2cd3ebf8e0991cda6590d73aef31d103
4
- data.tar.gz: '0931cbd88556516ce7a210312a23227efae4c6003ec4e2c83b265f08762fa6b9'
3
+ metadata.gz: 9bbbf1239ad74e0ccf7709ce4bd1ccf77d6e319c390836aa749000a0ce3360ef
4
+ data.tar.gz: 4c433919870bb792e153105cf23ea4f9c969cb8c1251da5671793c5941ebc084
5
5
  SHA512:
6
- metadata.gz: '00919e561ec71e0ae20a7d3e425b46824b1b67a5ae4812bd6e10162f6e53ce054cb94a05c5e8f062fad7a08d97aa9e3ccd76974679cad6c45abef56857372c69'
7
- data.tar.gz: 97747263d0d3583b223c7bb2727b8403642f41d8e0c9ebdcf3f7aa0cbc013ad0528e91f0a7c8852636a083581141ca6179f5a75e6280b1266c7365dbc85b4d40
6
+ metadata.gz: 26f90ddc3c0302bb0584013ab7abeb2236d3d663541a63adc51f9b7d05baa063477efdac183c52ae998fbe14b91da9ebda0f659b8112c35e2fba125254ebbf0b
7
+ data.tar.gz: a1bd282fd15b758251bab23adf20585b05f2ec58e1ffa39dc91350430bf06f5e8b1c7dda061015376bc42441a9137c52d0d9b30e171b3c2d0a17235c8d1efb9e
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,24 +86,41 @@ 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
- opts, args = ShellOpts::ShellOpts.process(SPEC, ARGV, :version => PgGraph::VERSION)
123
+ opts, args = ShellOpts::ShellOpts.process(SPEC, ARGV)
91
124
 
92
125
  timing = opts.time?
93
126
  timer = Timer::Timer.new
@@ -86,7 +86,7 @@ module PgGraph
86
86
  case name
87
87
  when "character varying", "varchar", "text", "uuid"; String
88
88
  when "smallint", "integer", "int4", "int2"; Integer
89
- when "double precision", "real"; Float
89
+ when "double precision", "real", "float8"; Float
90
90
  when "numeric", "decimal"; BigDecimal
91
91
  when "bool", "boolean"; Boolean
92
92
  when "json"; Hash
@@ -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.2"
2
+ VERSION = "0.1.5"
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.2
4
+ version: 0.1.5
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-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: boolean