Class: PgGraph::Reflector

Inherits:
Object
  • Object
show all
Defined in:
lib/pg_graph/reflector.rb

Constant Summary collapse

REFLECTIONS_SCHEMA =
"public"
REFLECTIONS_TABLE =
"reflections"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(reflections = [], default_reflections: Reflector.default_reflections) ⇒ Reflector

Returns a new instance of Reflector.



115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/pg_graph/reflector.rb', line 115

def initialize(reflections = [], default_reflections: Reflector.default_reflections)
  constrain reflections, [Reflection]
  # @reflection maps from number of reflection components to list of reflections. The
  # keys are initially created in descending to ensure that #values return
  # lists of components sorted from most to least specific. New reflections
  # are inserted at the beginning of the lists to make it possible to
  # override ealier reflections
  @reflections = {}
  (1..4).to_a.reverse.each { |components| @reflections[components] = [] }
  add(default_reflections || [])
  add(reflections)
end

Class Method Details

.default_reflectionsObject



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/pg_graph/reflector.rb', line 222

def self.default_reflections
  @default_reflections ||= begin
    initializers = [
      {"match"=>"/parent_id/", "that"=>"child"},
      {"match"=>"/child_id/", "that"=>"parent"},
      {"match"=>"/parent_(\\w+)_id/", "that"=>"child_$1"},
      {"match"=>"/child_(\\w+)_id/", "that"=>"parent_$1"},
      {"match"=>"kind", "this"=>"kind", "that"=>"$$"},
      {"match"=>"/(\\w+)_kind/", "this"=>"$1", "that"=>"$$"},
      {"match"=>"/(\\w+)_by_id/", "this"=>"$1_by", "that"=>"$1_$$", pluralize: true},
      {"match"=>"/(\\w+)_id/", "this"=>"$1", "that"=>"$$", multi: false},
      {"match"=>"/(\\w+)_id/", "this"=>"$1", "that"=>"$1_of", multi: true, pluralize: false},
      {"match"=>"/(\\w+)/", "this"=>"$1", "that"=>"$$"}, # Kind fields w/o explicit 'kind'
    ]
    initializers.map { |initializer|
      Reflection.load_yaml initializer.merge(default_reflection: true)
    }
  end
end

.default_reflections=(reflections) ⇒ Object



242
243
244
# File 'lib/pg_graph/reflector.rb', line 242

def self.default_reflections=(reflections)
  @default_reflections = reflections
end

.load_file(file, default_reflections: Reflector.default_reflections) ⇒ Object



184
185
186
# File 'lib/pg_graph/reflector.rb', line 184

def self.load_file(file, default_reflections: Reflector.default_reflections)
  load_yaml YAML.load(IO.read(file)), default_reflections: default_reflections
end

.load_table(conn, schema = nil, table = nil, default_reflections: Reflector.default_reflections) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/pg_graph/reflector.rb', line 188

def self.load_table(conn, schema = nil, table = nil, default_reflections: Reflector.default_reflections)
  schema ||= REFLECTIONS_SCHEMA
  table ||= REFLECTIONS_TABLE
  if conn.schema.exist?(schema) && conn.schema.exist_table?(schema, table)
    yaml = conn.structs(%(
      select match, this, that, multi, pluralize, false as "default_reflection"
      from #{schema}.#{table}
      order by ordinal
    )).map(&:to_h)
  else
    yaml = {}
  end
  load_yaml yaml, default_reflections: default_reflections
end

.load_yaml(yaml_array, default_reflections: Reflector.default_reflections) ⇒ Object



179
180
181
182
# File 'lib/pg_graph/reflector.rb', line 179

def self.load_yaml(yaml_array, default_reflections: Reflector.default_reflections)
  reflections = yaml_array.map { |hash| Reflection.load_yaml(hash) }
  Reflector.new(reflections, default_reflections: default_reflections)
end

Instance Method Details

#add(*reflections) ⇒ Object

New reflections are inserted as a block at the head of the list of reflections



137
138
139
140
141
142
143
# File 'lib/pg_graph/reflector.rb', line 137

def add(*reflections)
  @reflection_list = nil
  # reverse makes it possible to use #insert but keep order:
  Array(reflections).flatten.reverse.each { |reflection|
    @reflections[reflection.components].insert(0, reflection)
  }
end

#dupObject



128
# File 'lib/pg_graph/reflector.rb', line 128

def dup() Reflector.new(reflections.dup) end

#empty?Boolean

Return true if the Reflector has no reflections

Returns:

  • (Boolean)


131
132
133
# File 'lib/pg_graph/reflector.rb', line 131

def empty?()
  @reflection_list ? reflections.empty? : !@reflections.values.all?(:empty?)
end

#multi?(uid) ⇒ Boolean

Returns:

  • (Boolean)


171
172
173
# File 'lib/pg_graph/reflector.rb', line 171

def multi?(uid)
  reflection = reflections.find { |reflection| reflection.re.match(uid) }&.multi == false
end

#reflectionsObject

Reflections ordered from most-specific to least-specific and newest to oldest



110
111
112
113
# File 'lib/pg_graph/reflector.rb', line 110

def reflections()
  # assumes @reflection keys are created in descending order
  @reflection_list ||= @reflections.values.flatten
end

#save_table(conn, schema = nil, table = nil) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/pg_graph/reflector.rb', line 203

def save_table(conn, schema = nil, table = nil)
  schema ||= REFLECTIONS_SCHEMA
  table ||= REFLECTIONS_TABLE
  self.class.ensure_table(conn, schema, table)
  values = reflections.select { |r| !r.default_reflection }.map.with_index { |reflection, i|
    values = "(" + Reflection::METHODS.map { |m|
      v = reflection.send(m)
      v.nil? ? "null" : "'#{v}'"
    }.join(", ") + ", #{i})"
  }.join(",\n")

  conn.exec %(
    insert into #{schema}.#{table}
        (match, this, that, multi, pluralize, default_reflection, ordinal)
    values
      #{values}
  )
end

#that(uid, unique, multi = false, table: nil) ⇒ Object

Find ‘that’ field name for the given UID by searching through reflections for a match. The name is pluralized if the matcher returns true or if the matcher returns nil and unique is false. The :table option can be used to override the table name in ‘$$’ rules. This is used in N:M and M:M relations. Returns false if the :that property is set to false in the reflections file. Returns nil if no match was found or if a matching reflection has #continue equal to false



160
161
162
163
164
165
166
167
168
169
# File 'lib/pg_graph/reflector.rb', line 160

def that(uid, unique, multi = false, table: nil)
  constrain uid, String
  if (name, pluralize = do_match(uid, :that, multi, table: table))
    if pluralize.nil? && !unique || pluralize
      PgGraph.inflector.pluralize(name)
    else
      name
    end
  end
end

#this(uid) ⇒ Object

Find ‘that’ field name for the given UID by searching through reflections for a match. Returns nil if no match was found or if a matching reflection has #continue equal to false



148
149
150
151
# File 'lib/pg_graph/reflector.rb', line 148

def this(uid)
  constrain uid, String
  do_match(uid, :this)&.first
end

#to_yamlObject



175
176
177
# File 'lib/pg_graph/reflector.rb', line 175

def to_yaml
  reflections.map { |reflection| reflection.to_yaml }
end