brick 1.0.17 → 1.0.20

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: 7f1a45a5262526e69cf49f16773049eb348e3971bddf09ecf05927002a265019
4
- data.tar.gz: e9566a423a19ab55d44522b44e8b4abfcc39e92335b6093fe782c6344644bbbe
3
+ metadata.gz: 41b64e2d571540382ce5ecd497f1e1413fde20119d46f1b24e1fae5af428fb45
4
+ data.tar.gz: 1c26ccf7b8df54fd121546e8daeeb9508561328186af46aea8ba1bd819524532
5
5
  SHA512:
6
- metadata.gz: cb89dff96dccc7051bc854fe523b03f818255d81e4f2e2a65eb9808115e719b8bad98e38114f15ee79b32b1f035c95c364a74392948cd2e99f4030e6988e0217
7
- data.tar.gz: cd5507fde2f1f23481696ab051d14f89e5e7e7d3fc2be97af947ff287313b7ada8ca7b4e3f9e0e54dc2ab7f14e7f39a3c18f78cf7fc995c58b3d60cf68487973
6
+ metadata.gz: e103d061b74402f3fe0b944d06b30c20f053166c706a54f15753afc8cb0c703ae71f83bd88191f985a37180cb9bdafdba918178174df08f58f683a723fcfb73a
7
+ data.tar.gz: 13a7dce7f4f5789a0d7a0c6cae4adcaf2f44d22d0f59c355d75102a7f08292d80c781f8d33be121801d375fd33fed8bcdf4ef6b56108ef8f4ab4110c1f49e296
data/lib/brick/config.rb CHANGED
@@ -108,7 +108,7 @@ module Brick
108
108
  end
109
109
 
110
110
  def exclude_tables
111
- @mutex.synchronize { @exclude_tables }
111
+ @mutex.synchronize { @exclude_tables || [] }
112
112
  end
113
113
 
114
114
  def exclude_tables=(value)
@@ -32,32 +32,101 @@
32
32
 
33
33
  # Currently quadrupling up routes
34
34
 
35
+
36
+ # From the North app:
37
+ # undefined method `built_in_role_path' when referencing show on a subclassed STI:
38
+ # http://localhost:3000/roles/3?_brick_schema=cust1
39
+
40
+
35
41
  # ==========================================================
36
42
  # Dynamically create model or controller classes when needed
37
43
  # ==========================================================
38
44
 
39
45
  # By default all models indicate that they are not views
46
+ module Arel
47
+ class Table
48
+ def _arel_table_type
49
+ # AR < 4.2 doesn't have type_caster at all, so rely on an instance variable getting set
50
+ # AR 4.2 - 5.1 have buggy type_caster entries for the root node
51
+ instance_variable_get(:@_arel_table_type) ||
52
+ # 5.2-7.0 does type_caster just fine, no bugs there, but the property with the type differs:
53
+ # 5.2 has "types" as public, 6.0 "types" as private, and >= 6.1 "klass" as private.
54
+ ((tc = send(:type_caster)) && tc.instance_variable_get(:@types)) ||
55
+ tc.send(:klass)
56
+ end
57
+ end
58
+ end
59
+
40
60
  module ActiveRecord
41
61
  class Base
62
+ def self._assoc_names
63
+ @_assoc_names ||= {}
64
+ end
65
+
42
66
  def self.is_view?
43
67
  false
44
68
  end
45
69
 
46
70
  # Used to show a little prettier name for an object
47
- def brick_descrip
48
- klass = self.class
49
- # If available, parse simple DSL attached to a model in order to provide a friendlier name.
50
- # Object property names can be referenced in square brackets like this:
51
- # { 'User' => '[profile.firstname] [profile.lastname]' }
52
-
71
+ def self.brick_get_dsl
53
72
  # If there's no DSL yet specified, just try to find the first usable column on this model
54
- unless ::Brick.config.model_descrips[klass.name]
55
- descrip_col = (klass.columns.map(&:name) - klass._brick_get_fks -
73
+ unless (dsl = ::Brick.config.model_descrips[name])
74
+ descrip_col = (columns.map(&:name) - _brick_get_fks -
56
75
  (::Brick.config.metadata_columns || []) -
57
- [klass.primary_key]).first
58
- ::Brick.config.model_descrips[klass.name] = "[#{descrip_col}]" if descrip_col
76
+ [primary_key]).first
77
+ dsl = ::Brick.config.model_descrips[name] = "[#{descrip_col}]" if descrip_col
59
78
  end
60
- if (dsl ||= ::Brick.config.model_descrips[klass.name])
79
+ dsl
80
+ end
81
+
82
+ # Pass in true or a JoinArray
83
+ def self.brick_parse_dsl(build_array = nil, prefix = [], translations = {})
84
+ build_array = ::Brick::JoinArray.new.tap { |ary| ary.replace([build_array]) } if build_array.is_a?(::Brick::JoinHash)
85
+ build_array = ::Brick::JoinArray.new unless build_array.nil? || build_array.is_a?(Array)
86
+ members = []
87
+ bracket_name = nil
88
+ prefix = [prefix] unless prefix.is_a?(Array)
89
+ if (dsl = ::Brick.config.model_descrips[name] || brick_get_dsl)
90
+ klass = nil
91
+ dsl.each_char do |ch|
92
+ if bracket_name
93
+ if ch == ']' # Time to process a bracketed thing?
94
+ parts = bracket_name.split('.')
95
+ first_parts = parts[0..-2].map { |part| klass = klass.reflect_on_association(part_sym = part.to_sym).klass; part_sym }
96
+ parts = prefix + first_parts + [parts[-1]]
97
+ if parts.length > 1
98
+ s = build_array
99
+ parts[0..-3].each { |v| s = s[v.to_sym] }
100
+ s[parts[-2]] = nil # unless parts[-2].empty? # Using []= will "hydrate" any missing part(s) in our whole series
101
+ translations[parts[0..-2].join('.')] = klass
102
+ end
103
+ members << parts
104
+ bracket_name = nil
105
+ else
106
+ bracket_name << ch
107
+ end
108
+ elsif ch == '['
109
+ bracket_name = +''
110
+ klass = self
111
+ end
112
+ end
113
+ else # With no DSL available, still put this prefix into the JoinArray so we can get primary key (ID) info from this table
114
+ x = prefix.each_with_object(build_array) { |v, s| s[v.to_sym] }
115
+ x[prefix[-1]] = nil unless prefix.empty? # Using []= will "hydrate" any missing part(s) in our whole series
116
+ end
117
+ members
118
+ end
119
+
120
+ # If available, parse simple DSL attached to a model in order to provide a friendlier name.
121
+ # Object property names can be referenced in square brackets like this:
122
+ # { 'User' => '[profile.firstname] [profile.lastname]' }
123
+ def brick_descrip
124
+ self.class.brick_descrip(self)
125
+ end
126
+
127
+ def self.brick_descrip(obj, data = nil, pk_alias = nil)
128
+ if (dsl = ::Brick.config.model_descrips[(klass = self).name] || klass.brick_get_dsl)
129
+ idx = -1
61
130
  caches = {}
62
131
  output = +''
63
132
  is_brackets_have_content = false
@@ -65,18 +134,23 @@ module ActiveRecord
65
134
  dsl.each_char do |ch|
66
135
  if bracket_name
67
136
  if ch == ']' # Time to process a bracketed thing?
68
- obj_name = +''
69
- obj = self
70
- bracket_name.split('.').each do |part|
71
- obj_name += ".#{part}"
72
- obj = if caches.key?(obj_name)
73
- caches[obj_name]
137
+ datum = if data
138
+ data[idx += 1].to_s
74
139
  else
75
- (caches[obj_name] = obj&.send(part.to_sym))
140
+ obj_name = +''
141
+ this_obj = obj
142
+ bracket_name.split('.').each do |part|
143
+ obj_name += ".#{part}"
144
+ this_obj = if caches.key?(obj_name)
145
+ caches[obj_name]
146
+ else
147
+ (caches[obj_name] = this_obj&.send(part.to_sym))
148
+ end
149
+ end
150
+ this_obj&.to_s || ''
76
151
  end
77
- end
78
- is_brackets_have_content = true unless (obj&.to_s).blank?
79
- output << (obj&.to_s || '')
152
+ is_brackets_have_content = true unless (datum).blank?
153
+ output << (datum || '')
80
154
  bracket_name = nil
81
155
  else
82
156
  bracket_name << ch
@@ -91,10 +165,14 @@ module ActiveRecord
91
165
  end
92
166
  if is_brackets_have_content
93
167
  output
94
- elsif klass.primary_key
95
- "#{klass.name} ##{send(klass.primary_key)}"
168
+ elsif pk_alias
169
+ if (id = obj.send(pk_alias))
170
+ "#{klass.name} ##{id}"
171
+ end
172
+ # elsif klass.primary_key
173
+ # "#{klass.name} ##{obj.send(klass.primary_key)}"
96
174
  else
97
- to_s
175
+ obj.to_s
98
176
  end
99
177
  end
100
178
 
@@ -106,9 +184,91 @@ module ActiveRecord
106
184
  end
107
185
 
108
186
  class Relation
109
- def brick_where(params)
187
+ attr_reader :_brick_chains
188
+
189
+ # CLASS STUFF
190
+ def _recurse_arel(piece, prefix = '')
191
+ names = []
192
+ # Our JOINs mashup of nested arrays and hashes
193
+ # binding.pry if defined?(@arel)
194
+ case piece
195
+ when Array
196
+ names += piece.inject([]) { |s, v| s + _recurse_arel(v, prefix) }
197
+ when Hash
198
+ names += piece.inject([]) do |s, v|
199
+ new_prefix = "#{prefix}#{v.first}_"
200
+ s << [v.last.shift, new_prefix]
201
+ s + _recurse_arel(v.last, new_prefix)
202
+ end
203
+
204
+ # ActiveRecord AREL objects
205
+ when Arel::Nodes::Join # INNER or OUTER JOIN
206
+ # rubocop:disable Style/IdenticalConditionalBranches
207
+ if piece.right.is_a?(Arel::Table) # Came in from AR < 3.2?
208
+ # Arel 2.x and older is a little curious because these JOINs work "back to front".
209
+ # The left side here is either another earlier JOIN, or at the end of the whole tree, it is
210
+ # the first table.
211
+ names += _recurse_arel(piece.left)
212
+ # The right side here at the top is the very last table, and anywhere else down the tree it is
213
+ # the later "JOIN" table of this pair. (The table that comes after all the rest of the JOINs
214
+ # from the left side.)
215
+ names << [piece.right._arel_table_type, (piece.right.table_alias || piece.right.name)]
216
+ else # "Normal" setup, fed from a JoinSource which has an array of JOINs
217
+ # The left side is the "JOIN" table
218
+ names += _recurse_arel(piece.left)
219
+ # The expression on the right side is the "ON" clause
220
+ # on = piece.right.expr
221
+ # # Find the table which is not ourselves, and thus must be the "path" that led us here
222
+ # parent = piece.left == on.left.relation ? on.right.relation : on.left.relation
223
+ # binding.pry if piece.left.is_a?(Arel::Nodes::TableAlias)
224
+ table = piece.left
225
+ if table.is_a?(Arel::Nodes::TableAlias)
226
+ alias_name = table.right
227
+ table = table.left
228
+ end
229
+ (_brick_chains[table._arel_table_type] ||= []) << (alias_name || table.table_alias || table.name)
230
+ # puts "YES! #{self.object_id}"
231
+ end
232
+ # rubocop:enable Style/IdenticalConditionalBranches
233
+ when Arel::Table # Table
234
+ names << [piece._arel_table_type, (piece.table_alias || piece.name)]
235
+ when Arel::Nodes::TableAlias # Alias
236
+ # Can get the real table name from: self._recurse_arel(piece.left)
237
+ names << [piece.left._arel_table_type, piece.right.to_s] # This is simply a string; the alias name itself
238
+ when Arel::Nodes::JoinSource # Leaving this until the end because AR < 3.2 doesn't know at all about JoinSource!
239
+ # Spin up an empty set of Brick alias name chains at the start
240
+ @_brick_chains = {}
241
+ # The left side is the "FROM" table
242
+ # names += _recurse_arel(piece.left)
243
+ names << [piece.left._arel_table_type, (piece.left.table_alias || piece.left.name)]
244
+ # The right side is an array of all JOINs
245
+ piece.right.each { |join| names << _recurse_arel(join) }
246
+ end
247
+ names
248
+ end
249
+
250
+ # INSTANCE STUFF
251
+ def _arel_alias_names
252
+ # %%% If with Rails 3.1 and older you get "NoMethodError: undefined method `eq' for nil:NilClass"
253
+ # when trying to call relation.arel, then somewhere along the line while navigating a has_many
254
+ # relationship it can't find the proper foreign key.
255
+ core = arel.ast.cores.first
256
+ # Accommodate AR < 3.2
257
+ if core.froms.is_a?(Arel::Table)
258
+ # All recent versions of AR have #source which brings up an Arel::Nodes::JoinSource
259
+ _recurse_arel(core.source)
260
+ else
261
+ # With AR < 3.2, "froms" brings up the top node, an Arel::Nodes::InnerJoin
262
+ _recurse_arel(core.froms)
263
+ end
264
+ end
265
+
266
+ def brick_select(params, selects = nil, bt_descrip = {}, hm_counts = {}, join_array = ::Brick::JoinArray.new
267
+ # , is_add_bts, is_add_hms
268
+ )
269
+ is_add_bts = is_add_hms = true
110
270
  wheres = {}
111
- rel_joins = []
271
+ has_hm = false
112
272
  params.each do |k, v|
113
273
  case (ks = k.split('.')).length
114
274
  when 1
@@ -116,28 +276,90 @@ module ActiveRecord
116
276
  when 2
117
277
  assoc_name = ks.first.to_sym
118
278
  # Make sure it's a good association name and that the model has that column name
119
- next unless klass.reflect_on_association(assoc_name)&.klass&.columns&.map(&:name)&.include?(ks.last)
120
-
121
- rel_joins << assoc_name unless rel_joins.include?(assoc_name)
279
+ next unless (assoc = klass.reflect_on_association(assoc_name))&.klass&.columns&.map(&:name)&.include?(ks.last)
280
+
281
+ # So that we can map an association name to any special alias name used in an AREL query
282
+ ans = (assoc.klass._assoc_names[assoc_name] ||= [])
283
+ ans << assoc.klass unless ans.include?(assoc.klass)
284
+ # There is some potential for duplicates when there is an HM-based where in play. De-duplicate if so.
285
+ has_hm ||= assoc.macro == :has_many
286
+ join_array[assoc_name] = nil # Store this relation name in our special collection for .joins()
122
287
  end
123
288
  wheres[k] = v.split(',')
124
289
  end
125
- unless wheres.empty?
126
- where!(wheres)
127
- joins!(rel_joins) unless rel_joins.empty?
128
- wheres # Return the specific parameters that we did use
290
+ # distinct! if has_hm
291
+
292
+ # %%% Skip the metadata columns
293
+ if selects&.empty? # Default to all columns
294
+ columns.each do |col|
295
+ selects << "#{table.name}.#{col.name}"
296
+ end
297
+ end
298
+
299
+ # Search for BT, HM, and HMT DSL stuff
300
+ translations = {}
301
+ if is_add_bts || is_add_hms
302
+ bts, hms, associatives = ::Brick.get_bts_and_hms(klass)
303
+ bts.each do |_k, bt|
304
+ # join_array[bt.first] = nil # Store this relation name in our special collection for .joins()
305
+ bt_descrip[bt.first] = [bt.last, bt.last.brick_parse_dsl(join_array, bt.first, translations)]
306
+ end
307
+ hms.each do |k, hm|
308
+ join_array[k] = nil # Store this relation name in our special collection for .joins()
309
+ hm_counts[k] = nil # Placeholder that will be filled in once we know the proper table alias
310
+ end
311
+ end
312
+ where!(wheres) unless wheres.empty?
313
+ if join_array.present?
314
+ left_outer_joins!(join_array) # joins!(join_array)
315
+ # Without working from a duplicate, touching the AREL ast tree sets the @arel instance variable, which causes the relation to be immutable.
316
+ (rel_dupe = dup)._arel_alias_names
317
+ core_selects = selects.dup
318
+ groups = []
319
+ chains = rel_dupe._brick_chains
320
+ id_for_tables = {}
321
+ bt_columns = bt_descrip.each_with_object([]) do |v, s|
322
+ tbl_name = chains[v.last.first].first
323
+ if (id_col = v.last.first.primary_key) && !id_for_tables.key?(tbl_name)
324
+ groups << (unaliased = "#{tbl_name}.#{id_col}")
325
+ selects << "#{unaliased} AS \"#{(id_alias = id_for_tables[tbl_name] = "_brfk_#{v.first}__#{id_col}")}\""
326
+ v.last << id_alias
327
+ end
328
+ if (col_name = v.last[1].last&.last)
329
+ v.last[1].map { |x| [translations[x[0..-2].map(&:to_s).join('.')], x.last] }.each_with_index do |sel_col, idx|
330
+ groups << (unaliased = "#{tbl_name = chains[sel_col.first].first}.#{sel_col.last}")
331
+ # col_name is weak when there are multiple, using sel_col.last instead
332
+ tbl_name2 = tbl_name.start_with?('public.') ? tbl_name[7..-1] : tbl_name
333
+ selects << "#{unaliased} AS \"#{(col_alias = "_brfk_#{tbl_name2}__#{sel_col.last}")}\""
334
+ v.last[1][idx] << col_alias
335
+ end
336
+ end
337
+ end
338
+ group!(core_selects + groups) if hm_counts.any? # + bt_columns
339
+ join_array.each do |assoc_name|
340
+ # %%% Need to support {user: :profile}
341
+ next unless assoc_name.is_a?(Symbol)
342
+
343
+ klass = reflect_on_association(assoc_name)&.klass
344
+ table_alias = chains[klass].length > 1 ? chains[klass].shift : chains[klass].first
345
+ _assoc_names[assoc_name] = [table_alias, klass]
346
+ end
347
+ # Copy entries over
348
+ hm_counts.keys.each do |k|
349
+ hm_counts[k] = _assoc_names[k]
350
+ end
129
351
  end
352
+ wheres unless wheres.empty? # Return the specific parameters that we did use
130
353
  end
131
354
  end
132
355
 
133
356
  module Inheritance
134
357
  module ClassMethods
135
- private
358
+ private
136
359
 
137
360
  alias _brick_find_sti_class find_sti_class
138
361
  def find_sti_class(type_name)
139
362
  if ::Brick.sti_models.key?(type_name)
140
- # puts ['X', self.name, type_name].inspect
141
363
  _brick_find_sti_class(type_name)
142
364
  else
143
365
  # This auto-STI is more of a brute-force approach, building modules where needed
@@ -146,10 +368,8 @@ module ActiveRecord
146
368
  module_prefixes = type_name.split('::')
147
369
  module_prefixes.unshift('') unless module_prefixes.first.blank?
148
370
  module_name = module_prefixes[0..-2].join('::')
149
- if ::Brick.config.sti_namespace_prefixes&.key?("::#{module_name}::") ||
150
- ::Brick.config.sti_namespace_prefixes&.key?("#{module_name}::")
151
- _brick_find_sti_class(type_name)
152
- elsif File.exists?(candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb'))
371
+ if (snp = ::Brick.config.sti_namespace_prefixes)&.key?("::#{module_name}::") || snp&.key?("#{module_name}::") ||
372
+ File.exist?(candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb'))
153
373
  _brick_find_sti_class(type_name) # Find this STI class normally
154
374
  else
155
375
  # Build missing prefix modules if they don't yet exist
@@ -228,10 +448,9 @@ class Object
228
448
  end
229
449
 
230
450
  relations = ::Brick.instance_variable_get(:@relations)[ActiveRecord::Base.connection_pool.object_id] || {}
231
- is_controllers_enabled = ::Brick.enable_controllers? || (ENV['RAILS_ENV'] || ENV['RACK_ENV']) == 'development'
232
- result = if is_controllers_enabled && class_name.end_with?('Controller') && (plural_class_name = class_name[0..-11]).length.positive?
451
+ result = if ::Brick.enable_controllers? && class_name.end_with?('Controller') && (plural_class_name = class_name[0..-11]).length.positive?
233
452
  # Otherwise now it's up to us to fill in the gaps
234
- if (model = ActiveSupport::Inflector.singularize(plural_class_name).constantize)
453
+ if (model = plural_class_name.singularize.constantize)
235
454
  # if it's a controller and no match or a model doesn't really use the same table name, eager load all models and try to find a model class of the right name.
236
455
  build_controller(class_name, plural_class_name, model, relations)
237
456
  end
@@ -243,10 +462,10 @@ class Object
243
462
 
244
463
  # Adjust for STI if we know of a base model for the requested model name
245
464
  table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
246
- base_model.table_name
247
- else
248
- ActiveSupport::Inflector.pluralize(singular_table_name)
249
- end
465
+ base_model.table_name
466
+ else
467
+ ActiveSupport::Inflector.pluralize(singular_table_name)
468
+ end
250
469
 
251
470
  # Maybe, just maybe there's a database table that will satisfy this need
252
471
  if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
@@ -282,6 +501,7 @@ class Object
282
501
  end
283
502
  return
284
503
  end
504
+
285
505
  if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
286
506
  is_sti = true
287
507
  else
@@ -443,18 +663,27 @@ class Object
443
663
 
444
664
  code << " def index\n"
445
665
  code << " @#{table_name} = #{model.name}#{model.primary_key ? ".order(#{model.primary_key.inspect})" : '.all'}\n"
446
- code << " @#{table_name}.brick_where(params)\n"
666
+ code << " @#{table_name}.brick_select(params)\n"
447
667
  code << " end\n"
448
668
  self.define_method :index do
449
669
  ::Brick.set_db_schema(params)
450
- ar_relation = model.primary_key ? model.order(model.primary_key) : model.all
451
- instance_variable_set(:@_brick_params, ar_relation.brick_where(params))
452
- instance_variable_set("@#{table_name}".to_sym, ar_relation)
670
+ ar_relation = model.all # model.primary_key ? model.order(model.primary_key) : model.all
671
+ @_brick_params = ar_relation.brick_select(params, (selects = []), (bt_descrip = {}), (hm_counts = {}), (join_array = ::Brick::JoinArray.new))
672
+ # %%% Add custom HM count columns
673
+ # %%% What happens when the PK is composite?
674
+ counts = hm_counts.each_with_object([]) { |v, s| s << "COUNT(DISTINCT #{v.last.first}.#{v.last.last.primary_key}) AS _br_#{v.first}_ct" }
675
+ puts counts.inspect
676
+ # *selects,
677
+ instance_variable_set("@#{table_name}".to_sym, ar_relation.dup._select!(*selects, *counts))
678
+ # binding.pry
679
+ @_brick_bt_descrip = bt_descrip
680
+ @_brick_hm_counts = hm_counts
681
+ @_brick_join_array = join_array
453
682
  end
454
683
 
455
684
  if model.primary_key
456
685
  code << " def show\n"
457
- code << " @#{singular_table_name} = #{model.name}.find(params[:id].split(','))\n"
686
+ code << (find_by_id = " @#{singular_table_name} = #{model.name}.find(params[:id].split(','))\n")
458
687
  code << " end\n"
459
688
  self.define_method :show do
460
689
  ::Brick.set_db_schema(params)
@@ -463,9 +692,36 @@ class Object
463
692
  end
464
693
 
465
694
  # By default, views get marked as read-only
466
- unless (relation = relations[model.table_name]).key?(:isView)
467
- code << " # (Define :new, :create, :edit, :update, and :destroy)\n"
468
- # Get column names for params from relations[model.table_name][:cols].keys
695
+ unless false # model.readonly # (relation = relations[model.table_name]).key?(:isView)
696
+ code << " # (Define :new, :create)\n"
697
+
698
+ if model.primary_key
699
+ is_need_params = true
700
+ # code << " # (Define :edit, and :destroy)\n"
701
+ code << " def update\n"
702
+ code << find_by_id
703
+ params_name = "#{singular_table_name}_params"
704
+ code << " @#{singular_table_name}.update(#{params_name})\n"
705
+ code << " end\n"
706
+ self.define_method :update do
707
+ ::Brick.set_db_schema(params)
708
+ instance_variable_set("@#{singular_table_name}".to_sym, (obj = model.find(params[:id].split(','))))
709
+ obj = obj.first if obj.is_a?(Array)
710
+ obj.send(:update, send(params_name = params_name.to_sym))
711
+ end
712
+ end
713
+
714
+ if is_need_params
715
+ code << "private\n"
716
+ code << " def params\n"
717
+ code << " params.require(:#{singular_table_name}).permit(#{model.columns_hash.keys.map { |c| c.to_sym.inspect }.join(', ')})\n"
718
+ code << " end\n"
719
+ self.define_method(params_name) do
720
+ params.require(singular_table_name.to_sym).permit(model.columns_hash.keys)
721
+ end
722
+ private params_name
723
+ # Get column names for params from relations[model.table_name][:cols].keys
724
+ end
469
725
  end
470
726
  code << "end # #{class_name}\n\n"
471
727
  end # class definition
@@ -495,9 +751,9 @@ module ActiveRecord::ConnectionHandling
495
751
  end
496
752
 
497
753
  def _brick_reflect_tables
498
- if (relations = ::Brick.relations).empty?
499
- # Only for Postgres? (Doesn't work in sqlite3)
500
- # puts ActiveRecord::Base.connection.execute("SELECT current_setting('SEARCH_PATH')").to_a.inspect
754
+ if (relations = ::Brick.relations).empty?
755
+ # Only for Postgres? (Doesn't work in sqlite3)
756
+ # puts ActiveRecord::Base.connection.execute("SELECT current_setting('SEARCH_PATH')").to_a.inspect
501
757
 
502
758
  schema_sql = 'SELECT NULL AS table_schema;'
503
759
  case ActiveRecord::Base.connection.adapter_name
@@ -587,6 +843,25 @@ module ActiveRecord::ConnectionHandling
587
843
  end
588
844
  end
589
845
 
846
+ # # Add unique OIDs
847
+ # if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
848
+ # ActiveRecord::Base.execute_sql(
849
+ # "SELECT c.oid, n.nspname, c.relname
850
+ # FROM pg_catalog.pg_namespace AS n
851
+ # INNER JOIN pg_catalog.pg_class AS c ON n.oid = c.relnamespace
852
+ # WHERE c.relkind IN ('r', 'v')"
853
+ # ).each do |r|
854
+ # next if ['pg_catalog', 'information_schema', ''].include?(r['nspname']) ||
855
+ # ['ar_internal_metadata', 'schema_migrations'].include?(r['relname'])
856
+ # relation = relations.fetch(r['relname'], nil)
857
+ # if relation
858
+ # (relation[:oid] ||= {})[r['nspname']] = r['oid']
859
+ # else
860
+ # puts "Where is #{r['nspname']} #{r['relname']} ?"
861
+ # end
862
+ # end
863
+ # end
864
+
590
865
  case ActiveRecord::Base.connection.adapter_name
591
866
  when 'PostgreSQL', 'Mysql2'
592
867
  sql = ActiveRecord::Base.send(:sanitize_sql_array, [
@@ -629,14 +904,12 @@ module ActiveRecord::ConnectionHandling
629
904
  puts "\nClasses that can be built from views:"
630
905
  views.keys.each { |k| puts ActiveSupport::Inflector.singularize(k).camelize }
631
906
  end
907
+
632
908
  # Try to load the initializer pretty danged early
633
909
  if File.exist?(brick_initialiser = Rails.root.join('config/initializers/brick.rb'))
634
910
  load brick_initialiser
635
911
  ::Brick.load_additional_references
636
912
  end
637
-
638
- # relations.keys.each { |k| ActiveSupport::Inflector.singularize(k).camelize.constantize }
639
- # Layout table describes permissioned hierarchy throughout
640
913
  end
641
914
  end
642
915
 
@@ -681,7 +954,7 @@ module Brick
681
954
  missing << fk[0] unless relations.key?(fk[0])
682
955
  missing << primary_table unless is_class || relations.key?(primary_table)
683
956
  unless missing.empty?
684
- tables = relations.reject { |k, v| v.fetch(:isView, nil) }.keys.sort
957
+ tables = relations.reject { |_k, v| v.fetch(:isView, nil) }.keys.sort
685
958
  puts "Brick: Additional reference #{fk.inspect} refers to non-existent #{'table'.pluralize(missing.length)} #{missing.join(' and ')}. (Available tables include #{tables.join(', ')}.)"
686
959
  return
687
960
  end
@@ -690,7 +963,7 @@ module Brick
690
963
  puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[1]}. (Columns present in #{fk[0]} are #{columns.join(', ')}.)"
691
964
  return
692
965
  end
693
- if (redundant = bts.find { |k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[0] && v[:fk] == fk[1] && v[:inverse_table] == primary_table })
966
+ if (redundant = bts.find { |_k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[0] && v[:fk] == fk[1] && v[:inverse_table] == primary_table })
694
967
  if is_class && !redundant.last.key?(:class)
695
968
  redundant.last[:primary_class] = primary_class # Round out this BT so it can find the proper :source for a HMT association that references an STI subclass
696
969
  else
@@ -712,19 +985,19 @@ module Brick
712
985
  # assoc_bt[:inverse_of] = primary_class.reflect_on_all_associations.find { |a| a.foreign_key == bt[1] }
713
986
  end
714
987
 
715
- unless is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[0] == exclusion[0] && fk[1] == exclusion[1] && primary_table == exclusion[2] }
716
- cnstr_name = "hm_#{cnstr_name}"
717
- if (assoc_hm = hms.fetch(cnstr_name, nil))
718
- assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[1]] : assoc_hm[:fk].concat(fk[1])
719
- assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
720
- assoc_hm[:inverse] = assoc_bt
721
- else
722
- assoc_hm = hms[cnstr_name] = { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0], inverse: assoc_bt }
723
- hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
724
- hm_counts[fk[0]] = hm_counts.fetch(fk[0]) { 0 } + 1
725
- end
726
- assoc_bt[:inverse] = assoc_hm
988
+ return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[0] == exclusion[0] && fk[1] == exclusion[1] && primary_table == exclusion[2] }
989
+
990
+ cnstr_name = "hm_#{cnstr_name}"
991
+ if (assoc_hm = hms.fetch(cnstr_name, nil))
992
+ assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[1]] : assoc_hm[:fk].concat(fk[1])
993
+ assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
994
+ assoc_hm[:inverse] = assoc_bt
995
+ else
996
+ assoc_hm = hms[cnstr_name] = { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0], inverse: assoc_bt }
997
+ hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
998
+ hm_counts[fk[0]] = hm_counts.fetch(fk[0]) { 0 } + 1
727
999
  end
1000
+ assoc_bt[:inverse] = assoc_hm
728
1001
  # hms[cnstr_name] << { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0] }
729
1002
  end
730
1003
  end