brick 1.0.20 → 1.0.23
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 +4 -4
- data/lib/brick/config.rb +14 -0
- data/lib/brick/extensions.rb +169 -97
- data/lib/brick/frameworks/rails/engine.rb +174 -45
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +8 -9
- data/lib/generators/brick/install_generator.rb +6 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24731ac9bd56261179224610406247cc4438c61e411aa3fe96205b2e97fc8cb8
|
4
|
+
data.tar.gz: 785d12e36fcf788c05cff3a02ca2ec17fc418b0ea15b54cb6923c83c6f362d3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '090a3c4d1b3cb4761799e05adf5caa0c83f60cf985f97ed6856beb05620014e9e7401689719209488092c6c4a7605e53781cdb71ad9e7ec204ccaddca0d92cca'
|
7
|
+
data.tar.gz: 8d1f9512c948359e7d73cd7cc32302f226df7e3e744fab562e01e16170830be28d0e2e15bcd7b437f66ed79e366b0ff33b7f578e4c76ffde1dd62cdcec059069
|
data/lib/brick/config.rb
CHANGED
@@ -74,6 +74,20 @@ module Brick
|
|
74
74
|
@mutex.synchronize { @exclude_hms = skips }
|
75
75
|
end
|
76
76
|
|
77
|
+
# Skip showing counts for these specific has_many associations when building auto-generated #index views
|
78
|
+
def skip_index_hms
|
79
|
+
@mutex.synchronize { @skip_index_hms || {} }
|
80
|
+
end
|
81
|
+
|
82
|
+
def skip_index_hms=(skips)
|
83
|
+
@mutex.synchronize do
|
84
|
+
@skip_index_hms ||= skips.each_with_object({}) do |v, s|
|
85
|
+
class_name, assoc_name = v.split('.')
|
86
|
+
(s[class_name] ||= {})[assoc_name.to_sym] = nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
77
91
|
# Associations to treat as a has_one
|
78
92
|
def has_ones
|
79
93
|
@mutex.synchronize { @has_ones }
|
data/lib/brick/extensions.rb
CHANGED
@@ -26,18 +26,13 @@
|
|
26
26
|
|
27
27
|
# colour coded origins
|
28
28
|
|
29
|
-
# Drag something like
|
29
|
+
# Drag something like HierModel#name onto the rows and have it automatically add five columns -- where type=zone / where type = section / etc
|
30
30
|
|
31
31
|
# Support for Postgres / MySQL enums (add enum to model, use model enums to make a drop-down in the UI)
|
32
32
|
|
33
33
|
# Currently quadrupling up routes
|
34
34
|
|
35
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
|
-
|
41
36
|
# ==========================================================
|
42
37
|
# Dynamically create model or controller classes when needed
|
43
38
|
# ==========================================================
|
@@ -166,16 +161,36 @@ module ActiveRecord
|
|
166
161
|
if is_brackets_have_content
|
167
162
|
output
|
168
163
|
elsif pk_alias
|
169
|
-
|
170
|
-
|
164
|
+
id = []
|
165
|
+
pk_alias.each do |pk_alias_part|
|
166
|
+
if (pk_part = obj.send(pk_alias_part))
|
167
|
+
id << pk_part
|
168
|
+
end
|
169
|
+
end
|
170
|
+
if id.present?
|
171
|
+
"#{klass.name} ##{id.join(', ')}"
|
171
172
|
end
|
172
|
-
# elsif klass.primary_key
|
173
|
-
# "#{klass.name} ##{obj.send(klass.primary_key)}"
|
174
173
|
else
|
175
174
|
obj.to_s
|
176
175
|
end
|
177
176
|
end
|
178
177
|
|
178
|
+
def self.bt_link(assoc_name)
|
179
|
+
model_underscore = name.underscore
|
180
|
+
assoc_name = CGI.escapeHTML(assoc_name.to_s)
|
181
|
+
model_path = Rails.application.routes.url_helpers.send("#{model_underscore.pluralize}_path".to_sym)
|
182
|
+
link = Class.new.extend(ActionView::Helpers::UrlHelper).link_to(name, model_path)
|
183
|
+
model_underscore == assoc_name ? link : "#{assoc_name}-#{link}".html_safe
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.brick_import_template
|
187
|
+
template = constants.include?(:IMPORT_TEMPLATE) ? self::IMPORT_TEMPLATE : suggest_template(false, false, 0)
|
188
|
+
# Add the primary key to the template as being unique (unless it's already there)
|
189
|
+
template[:uniques] = [pk = primary_key.to_sym]
|
190
|
+
template[:all].unshift(pk) unless template[:all].include?(pk)
|
191
|
+
template
|
192
|
+
end
|
193
|
+
|
179
194
|
private
|
180
195
|
|
181
196
|
def self._brick_get_fks
|
@@ -267,27 +282,6 @@ module ActiveRecord
|
|
267
282
|
# , is_add_bts, is_add_hms
|
268
283
|
)
|
269
284
|
is_add_bts = is_add_hms = true
|
270
|
-
wheres = {}
|
271
|
-
has_hm = false
|
272
|
-
params.each do |k, v|
|
273
|
-
case (ks = k.split('.')).length
|
274
|
-
when 1
|
275
|
-
next unless klass._brick_get_fks.include?(k)
|
276
|
-
when 2
|
277
|
-
assoc_name = ks.first.to_sym
|
278
|
-
# Make sure it's a good association name and that the model has that column 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()
|
287
|
-
end
|
288
|
-
wheres[k] = v.split(',')
|
289
|
-
end
|
290
|
-
# distinct! if has_hm
|
291
285
|
|
292
286
|
# %%% Skip the metadata columns
|
293
287
|
if selects&.empty? # Default to all columns
|
@@ -301,56 +295,101 @@ module ActiveRecord
|
|
301
295
|
if is_add_bts || is_add_hms
|
302
296
|
bts, hms, associatives = ::Brick.get_bts_and_hms(klass)
|
303
297
|
bts.each do |_k, bt|
|
304
|
-
# join_array
|
298
|
+
# join_array will receive this relation name when calling #brick_parse_dsl
|
305
299
|
bt_descrip[bt.first] = [bt.last, bt.last.brick_parse_dsl(join_array, bt.first, translations)]
|
306
300
|
end
|
301
|
+
skip_klass_hms = ::Brick.config.skip_index_hms[klass.name] || {}
|
307
302
|
hms.each do |k, hm|
|
308
|
-
|
309
|
-
|
303
|
+
next if skip_klass_hms.key?(k)
|
304
|
+
|
305
|
+
hm_counts[k] = hm
|
310
306
|
end
|
311
307
|
end
|
312
|
-
|
308
|
+
|
309
|
+
wheres = {}
|
310
|
+
params.each do |k, v|
|
311
|
+
case (ks = k.split('.')).length
|
312
|
+
when 1
|
313
|
+
next unless klass._brick_get_fks.include?(k)
|
314
|
+
when 2
|
315
|
+
assoc_name = ks.first.to_sym
|
316
|
+
# Make sure it's a good association name and that the model has that column name
|
317
|
+
next unless klass.reflect_on_association(assoc_name)&.klass&.columns&.any? { |col| col.name == ks.last }
|
318
|
+
|
319
|
+
join_array[assoc_name] = nil # Store this relation name in our special collection for .joins()
|
320
|
+
end
|
321
|
+
wheres[k] = v.split(',')
|
322
|
+
end
|
323
|
+
|
313
324
|
if join_array.present?
|
314
325
|
left_outer_joins!(join_array) # joins!(join_array)
|
315
326
|
# Without working from a duplicate, touching the AREL ast tree sets the @arel instance variable, which causes the relation to be immutable.
|
316
327
|
(rel_dupe = dup)._arel_alias_names
|
317
328
|
core_selects = selects.dup
|
318
|
-
groups = []
|
319
329
|
chains = rel_dupe._brick_chains
|
320
|
-
id_for_tables = {}
|
330
|
+
id_for_tables = Hash.new { |h, k| h[k] = [] }
|
331
|
+
field_tbl_names = Hash.new { |h, k| h[k] = {} }
|
321
332
|
bt_columns = bt_descrip.each_with_object([]) do |v, s|
|
322
|
-
tbl_name =
|
323
|
-
if (id_col = v.last.first.primary_key) && !id_for_tables.key?(tbl_name
|
324
|
-
|
325
|
-
|
326
|
-
|
333
|
+
tbl_name = field_tbl_names[v.first][v.last.first] ||= shift_or_first(chains[v.last.first])
|
334
|
+
if (id_col = v.last.first.primary_key) && !id_for_tables.key?(v.first) # was tbl_name
|
335
|
+
# Accommodate composite primary key by allowing id_col to come in as an array
|
336
|
+
(id_col.is_a?(Array) ? id_col : [id_col]).each do |id_part|
|
337
|
+
selects << "#{"#{tbl_name}.#{id_part}"} AS \"#{(id_alias = "_brfk_#{v.first}__#{id_part}")}\""
|
338
|
+
id_for_tables[v.first] << id_alias
|
339
|
+
end
|
340
|
+
v.last << id_for_tables[v.first]
|
327
341
|
end
|
328
342
|
if (col_name = v.last[1].last&.last)
|
343
|
+
field_tbl_name = nil
|
329
344
|
v.last[1].map { |x| [translations[x[0..-2].map(&:to_s).join('.')], x.last] }.each_with_index do |sel_col, idx|
|
330
|
-
|
345
|
+
field_tbl_name ||= field_tbl_names[v.first][sel_col.first] ||= shift_or_first(chains[sel_col.first])
|
331
346
|
# col_name is weak when there are multiple, using sel_col.last instead
|
332
|
-
|
333
|
-
selects << "#{unaliased} AS \"#{(col_alias = "_brfk_#{tbl_name2}__#{sel_col.last}")}\""
|
347
|
+
selects << "#{"#{field_tbl_name}.#{sel_col.last}"} AS \"#{(col_alias = "_brfk_#{v.first}__#{sel_col.last}")}\""
|
334
348
|
v.last[1][idx] << col_alias
|
335
349
|
end
|
336
350
|
end
|
337
351
|
end
|
338
|
-
group!(core_selects + groups) if hm_counts.any? # + bt_columns
|
339
352
|
join_array.each do |assoc_name|
|
340
353
|
# %%% Need to support {user: :profile}
|
341
354
|
next unless assoc_name.is_a?(Symbol)
|
342
355
|
|
343
|
-
klass = reflect_on_association(assoc_name)&.klass
|
344
|
-
table_alias = chains[klass].length > 1 ? chains[klass].shift : chains[klass].first
|
356
|
+
table_alias = shift_or_first(chains[klass = reflect_on_association(assoc_name)&.klass])
|
345
357
|
_assoc_names[assoc_name] = [table_alias, klass]
|
346
358
|
end
|
347
|
-
|
348
|
-
|
349
|
-
|
359
|
+
end
|
360
|
+
# Add derived table JOIN for the has_many counts
|
361
|
+
hm_counts.each do |k, hm|
|
362
|
+
associative = nil
|
363
|
+
count_column = if hm.options[:through]
|
364
|
+
fk_col = (associative = associatives[hm.name]).foreign_key
|
365
|
+
hm.foreign_key
|
366
|
+
else
|
367
|
+
fk_col = hm.foreign_key
|
368
|
+
hm.klass.primary_key || '*'
|
369
|
+
end
|
370
|
+
tbl_alias = "_br_#{hm.name}"
|
371
|
+
pri_tbl = hm.active_record
|
372
|
+
if fk_col.is_a?(Array) # Composite key?
|
373
|
+
on_clause = []
|
374
|
+
fk_col.each_with_index { |fk_col_part, idx| on_clause << "#{tbl_alias}.#{fk_col_part} = #{pri_tbl.table_name}.#{pri_tbl.primary_key[idx]}" }
|
375
|
+
joins!("LEFT OUTER
|
376
|
+
JOIN (SELECT #{fk_col.join(', ')}, COUNT(#{count_column}) AS _ct_ FROM #{associative&.table_name || hm.klass.table_name} GROUP BY #{(1..fk_col.length).to_a.join(', ')}) AS #{tbl_alias}
|
377
|
+
ON #{on_clause.join(' AND ')}")
|
378
|
+
else
|
379
|
+
joins!("LEFT OUTER
|
380
|
+
JOIN (SELECT #{fk_col}, COUNT(#{count_column}) AS _ct_ FROM #{associative&.table_name || hm.klass.table_name} GROUP BY 1) AS #{tbl_alias}
|
381
|
+
ON #{tbl_alias}.#{fk_col} = #{pri_tbl.table_name}.#{pri_tbl.primary_key}")
|
350
382
|
end
|
351
383
|
end
|
384
|
+
where!(wheres) unless wheres.empty?
|
352
385
|
wheres unless wheres.empty? # Return the specific parameters that we did use
|
353
386
|
end
|
387
|
+
|
388
|
+
private
|
389
|
+
|
390
|
+
def shift_or_first(ary)
|
391
|
+
ary.length > 1 ? ary.shift : ary.first
|
392
|
+
end
|
354
393
|
end
|
355
394
|
|
356
395
|
module Inheritance
|
@@ -549,27 +588,30 @@ class Object
|
|
549
588
|
# Do the bulk of the has_many / belongs_to processing, and store details about HMT so they can be done at the very last
|
550
589
|
hmts = fks.each_with_object(Hash.new { |h, k| h[k] = [] }) do |fk, hmts|
|
551
590
|
# The key in each hash entry (fk.first) is the constraint name
|
552
|
-
|
553
|
-
inverse_assoc_name = assoc[:inverse]&.fetch(:assoc_name, nil)
|
591
|
+
inverse_assoc_name = (assoc = fk.last)[:inverse]&.fetch(:assoc_name, nil)
|
554
592
|
options = {}
|
555
593
|
singular_table_name = ActiveSupport::Inflector.singularize(assoc[:inverse_table])
|
556
594
|
macro = if assoc[:is_bt]
|
557
595
|
# Try to take care of screwy names if this is a belongs_to going to an STI subclass
|
558
|
-
if (primary_class = assoc.fetch(:primary_class, nil)) &&
|
559
|
-
|
560
|
-
|
561
|
-
|
596
|
+
assoc_name = if (primary_class = assoc.fetch(:primary_class, nil)) &&
|
597
|
+
sti_inverse_assoc = primary_class.reflect_on_all_associations.find do |a|
|
598
|
+
a.macro == :has_many && a.options[:class_name] == self.name && assoc[:fk] = a.foreign_key
|
599
|
+
end
|
600
|
+
sti_inverse_assoc.options[:inverse_of]&.to_s || assoc_name
|
601
|
+
else
|
602
|
+
assoc[:assoc_name]
|
603
|
+
end
|
562
604
|
need_class_name = singular_table_name.underscore != assoc_name
|
563
605
|
need_fk = "#{assoc_name}_id" != assoc[:fk]
|
564
606
|
if (inverse = assoc[:inverse])
|
565
607
|
inverse_assoc_name, _x = _brick_get_hm_assoc_name(relations[assoc[:inverse_table]], inverse)
|
566
608
|
if (has_ones = ::Brick.config.has_ones&.fetch(inverse[:alternate_name].camelize, nil))&.key?(singular_inv_assoc_name = ActiveSupport::Inflector.singularize(inverse_assoc_name))
|
567
609
|
inverse_assoc_name = if has_ones[singular_inv_assoc_name]
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
610
|
+
need_inverse_of = true
|
611
|
+
has_ones[singular_inv_assoc_name]
|
612
|
+
else
|
613
|
+
singular_inv_assoc_name
|
614
|
+
end
|
573
615
|
end
|
574
616
|
end
|
575
617
|
:belongs_to
|
@@ -580,12 +622,12 @@ class Object
|
|
580
622
|
need_fk = "#{ActiveSupport::Inflector.singularize(assoc[:inverse][:inverse_table])}_id" != assoc[:fk]
|
581
623
|
# fks[table_name].find { |other_assoc| other_assoc.object_id != assoc.object_id && other_assoc[:assoc_name] == assoc[assoc_name] }
|
582
624
|
if (has_ones = ::Brick.config.has_ones&.fetch(model_name, nil))&.key?(singular_assoc_name = ActiveSupport::Inflector.singularize(assoc_name))
|
583
|
-
assoc_name = if has_ones[singular_assoc_name]
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
625
|
+
assoc_name = if (custom_assoc_name = has_ones[singular_assoc_name])
|
626
|
+
need_class_name = custom_assoc_name != singular_assoc_name
|
627
|
+
custom_assoc_name
|
628
|
+
else
|
629
|
+
singular_assoc_name
|
630
|
+
end
|
589
631
|
:has_one
|
590
632
|
else
|
591
633
|
:has_many
|
@@ -622,31 +664,34 @@ class Object
|
|
622
664
|
end
|
623
665
|
hmts.each do |hmt_fk, fks|
|
624
666
|
fks.each do |fk|
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
667
|
+
through = fk.first[:assoc_name]
|
668
|
+
hmt_name = if fks.length > 1
|
669
|
+
if fks[0].first[:inverse][:assoc_name] == fks[1].first[:inverse][:assoc_name] # Same BT names pointing back to us? (Most common scenario)
|
670
|
+
"#{hmt_fk}_through_#{fk.first[:assoc_name]}"
|
671
|
+
else # Use BT names to provide uniqueness
|
672
|
+
through = fk.first[:alternate_name].pluralize
|
673
|
+
singular_assoc_name = fk.first[:inverse][:assoc_name].singularize
|
674
|
+
"#{singular_assoc_name}_#{hmt_fk}"
|
675
|
+
end
|
676
|
+
else
|
677
|
+
hmt_fk
|
678
|
+
end
|
679
|
+
source = fk.last unless hmt_name.singularize == fk.last
|
680
|
+
code << " has_many :#{hmt_name}, through: #{(assoc_name = through.to_sym).to_sym.inspect}#{", source: :#{source}" if source}\n"
|
637
681
|
options = { through: assoc_name }
|
638
682
|
options[:source] = source.to_sym if source
|
639
|
-
self.send(:has_many,
|
640
|
-
end
|
641
|
-
end
|
642
|
-
# Not NULLables
|
643
|
-
relation[:cols].each do |col, datatype|
|
644
|
-
if (datatype[3] && ar_pks.exclude?(col) && ::Brick.config.metadata_columns.exclude?(col)) ||
|
645
|
-
::Brick.config.not_nullables.include?("#{matching}.#{col}")
|
646
|
-
code << " validates :#{col}, presence: true\n"
|
647
|
-
self.send(:validates, col.to_sym, { presence: true })
|
683
|
+
self.send(:has_many, hmt_name.to_sym, **options)
|
648
684
|
end
|
649
685
|
end
|
686
|
+
# # Not NULLables
|
687
|
+
# # %%% For the minute we've had to pull this out because it's been troublesome implementing the NotNull validator
|
688
|
+
# relation[:cols].each do |col, datatype|
|
689
|
+
# if (datatype[3] && ar_pks.exclude?(col) && ::Brick.config.metadata_columns.exclude?(col)) ||
|
690
|
+
# ::Brick.config.not_nullables.include?("#{matching}.#{col}")
|
691
|
+
# code << " validates :#{col}, not_null: true\n"
|
692
|
+
# self.send(:validates, col.to_sym, { not_null: true })
|
693
|
+
# end
|
694
|
+
# end
|
650
695
|
end
|
651
696
|
code << "end # model #{model_name}\n\n"
|
652
697
|
end # class definition
|
@@ -665,14 +710,26 @@ class Object
|
|
665
710
|
code << " @#{table_name} = #{model.name}#{model.primary_key ? ".order(#{model.primary_key.inspect})" : '.all'}\n"
|
666
711
|
code << " @#{table_name}.brick_select(params)\n"
|
667
712
|
code << " end\n"
|
713
|
+
self.protect_from_forgery unless: -> { self.request.format.js? }
|
668
714
|
self.define_method :index do
|
669
715
|
::Brick.set_db_schema(params)
|
670
|
-
|
716
|
+
if request.format == :csv # Asking for a template?
|
717
|
+
require 'csv'
|
718
|
+
exported_csv = CSV.generate(force_quotes: false) do |csv_out|
|
719
|
+
model.df_export(model.brick_import_template).each { |row| csv_out << row }
|
720
|
+
end
|
721
|
+
render inline: exported_csv, content_type: request.format
|
722
|
+
return
|
723
|
+
elsif request.format == :js # Asking for JSON?
|
724
|
+
render inline: model.df_export(model.brick_import_template).to_json, content_type: request.format
|
725
|
+
return
|
726
|
+
end
|
727
|
+
|
728
|
+
ar_relation = model.primary_key ? model.order("#{model.table_name}.#{model.primary_key}") : model.all
|
671
729
|
@_brick_params = ar_relation.brick_select(params, (selects = []), (bt_descrip = {}), (hm_counts = {}), (join_array = ::Brick::JoinArray.new))
|
672
730
|
# %%% Add custom HM count columns
|
673
731
|
# %%% What happens when the PK is composite?
|
674
|
-
counts = hm_counts.each_with_object([]) { |v, s| s << "
|
675
|
-
puts counts.inspect
|
732
|
+
counts = hm_counts.each_with_object([]) { |v, s| s << "_br_#{v.first}._ct_ AS _br_#{v.first}_ct" }
|
676
733
|
# *selects,
|
677
734
|
instance_variable_set("@#{table_name}".to_sym, ar_relation.dup._select!(*selects, *counts))
|
678
735
|
# binding.pry
|
@@ -705,6 +762,22 @@ class Object
|
|
705
762
|
code << " end\n"
|
706
763
|
self.define_method :update do
|
707
764
|
::Brick.set_db_schema(params)
|
765
|
+
|
766
|
+
if request.format == :csv # Importing CSV?
|
767
|
+
require 'csv'
|
768
|
+
# See if internally it's likely a TSV file (tab-separated)
|
769
|
+
tab_counts = []
|
770
|
+
5.times { tab_counts << request.body.readline.count("\t") unless request.body.eof? }
|
771
|
+
request.body.rewind
|
772
|
+
separator = "\t" if tab_counts.length > 0 && tab_counts.uniq.length == 1 && tab_counts.first > 0
|
773
|
+
result = model.df_import(CSV.parse(request.body, { col_sep: separator || :auto }), model.brick_import_template)
|
774
|
+
# render inline: exported_csv, content_type: request.format
|
775
|
+
return
|
776
|
+
# elsif request.format == :js # Asking for JSON?
|
777
|
+
# render inline: model.df_export(true).to_json, content_type: request.format
|
778
|
+
# return
|
779
|
+
end
|
780
|
+
|
708
781
|
instance_variable_set("@#{singular_table_name}".to_sym, (obj = model.find(params[:id].split(','))))
|
709
782
|
obj = obj.first if obj.is_a?(Array)
|
710
783
|
obj.send(:update, send(params_name = params_name.to_sym))
|
@@ -730,7 +803,8 @@ class Object
|
|
730
803
|
|
731
804
|
def _brick_get_hm_assoc_name(relation, hm_assoc)
|
732
805
|
if relation[:hm_counts][hm_assoc[:assoc_name]]&.> 1
|
733
|
-
|
806
|
+
plural = ActiveSupport::Inflector.pluralize(hm_assoc[:alternate_name])
|
807
|
+
[hm_assoc[:alternate_name] == name.underscore ? "#{hm_assoc[:assoc_name].singularize}_#{plural}" : plural, true]
|
734
808
|
else
|
735
809
|
[ActiveSupport::Inflector.pluralize(hm_assoc[:inverse_table]), nil]
|
736
810
|
end
|
@@ -987,18 +1061,16 @@ module Brick
|
|
987
1061
|
|
988
1062
|
return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[0] == exclusion[0] && fk[1] == exclusion[1] && primary_table == exclusion[2] }
|
989
1063
|
|
990
|
-
|
991
|
-
if (assoc_hm = hms.fetch(cnstr_name, nil))
|
1064
|
+
if (assoc_hm = hms.fetch((hm_cnstr_name = "hm_#{cnstr_name}"), nil))
|
992
1065
|
assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[1]] : assoc_hm[:fk].concat(fk[1])
|
993
1066
|
assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
|
994
1067
|
assoc_hm[:inverse] = assoc_bt
|
995
1068
|
else
|
996
|
-
assoc_hm = hms[
|
1069
|
+
assoc_hm = hms[hm_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
1070
|
hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
|
998
1071
|
hm_counts[fk[0]] = hm_counts.fetch(fk[0]) { 0 } + 1
|
999
1072
|
end
|
1000
1073
|
assoc_bt[:inverse] = assoc_hm
|
1001
|
-
# hms[cnstr_name] << { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0] }
|
1002
1074
|
end
|
1003
1075
|
end
|
1004
1076
|
end
|
@@ -64,6 +64,14 @@ module Brick
|
|
64
64
|
is_template_exists
|
65
65
|
end
|
66
66
|
|
67
|
+
def path_keys(fk_name, obj_name, pk)
|
68
|
+
if fk_name.is_a?(Array) && pk.is_a?(Array) # Composite keys?
|
69
|
+
fk_name.zip(pk.map { |pk_part| "#{obj_name}.#{pk_part}" })
|
70
|
+
else
|
71
|
+
[[fk_name, "#{obj_name}.#{pk}"]]
|
72
|
+
end.map { |x| "#{x.first}: #{x.last}"}.join(', ')
|
73
|
+
end
|
74
|
+
|
67
75
|
alias :_brick_find_template :find_template
|
68
76
|
def find_template(*args, **options)
|
69
77
|
return _brick_find_template(*args, **options) unless @_brick_model
|
@@ -72,29 +80,36 @@ module Brick
|
|
72
80
|
pk = @_brick_model.primary_key
|
73
81
|
obj_name = model_name.underscore
|
74
82
|
table_name = model_name.pluralize.underscore
|
83
|
+
template_link = nil
|
75
84
|
bts, hms, associatives = ::Brick.get_bts_and_hms(@_brick_model) # This gets BT and HM and also has_many :through (HMT)
|
76
|
-
hms_columns =
|
85
|
+
hms_columns = [] # Used for 'index'
|
86
|
+
skip_klass_hms = ::Brick.config.skip_index_hms[model_name] || {}
|
77
87
|
hms_headers = hms.each_with_object([]) do |hm, s|
|
78
|
-
hm_assoc = hm.last
|
88
|
+
hm_stuff = [(hm_assoc = hm.last), "H#{hm_assoc.macro == :has_one ? 'O' : 'M'}#{'T' if hm_assoc.options[:through]}", (assoc_name = hm.first)]
|
89
|
+
hm_fk_name = if hm_assoc.options[:through]
|
90
|
+
associative = associatives[hm_assoc.name]
|
91
|
+
"'#{associative.name}.#{associative.foreign_key}'"
|
92
|
+
else
|
93
|
+
hm_assoc.foreign_key
|
94
|
+
end
|
79
95
|
if args.first == 'index'
|
80
|
-
hm_fk_name = if hm_assoc.options[:through]
|
81
|
-
associative = associatives[hm_assoc.name]
|
82
|
-
"'#{associative.name}.#{associative.foreign_key}'"
|
83
|
-
else
|
84
|
-
hm_assoc.foreign_key
|
85
|
-
end
|
86
96
|
hms_columns << if hm_assoc.macro == :has_many
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
97
|
+
set_ct = if skip_klass_hms.key?(assoc_name.to_sym)
|
98
|
+
'nil'
|
99
|
+
else
|
100
|
+
# Postgres column names are limited to 63 characters
|
101
|
+
attrib_name = "_br_#{assoc_name}_ct"[0..62]
|
102
|
+
"#{obj_name}.#{attrib_name} || 0"
|
103
|
+
end
|
104
|
+
"<%= ct = #{set_ct}
|
105
|
+
link_to \"#\{ct || 'View'\} #{assoc_name}\", #{hm_assoc.klass.name.underscore.pluralize}_path({ #{path_keys(hm_fk_name, obj_name, pk)} }) unless ct&.zero? %>\n"
|
91
106
|
else # has_one
|
92
|
-
"
|
93
|
-
<%= obj = #{obj_name}.#{hm.first}; link_to(obj.brick_descrip, obj) if obj %>
|
94
|
-
</td>\n"
|
107
|
+
"<%= obj = #{obj_name}.#{hm.first}; link_to(obj.brick_descrip, obj) if obj %>\n"
|
95
108
|
end
|
109
|
+
elsif args.first == 'show'
|
110
|
+
hm_stuff << "<%= link_to '#{assoc_name}', #{hm_assoc.klass.name.underscore.pluralize}_path({ #{path_keys(hm_fk_name, "@#{obj_name}&.first&", pk)} }) %>\n"
|
96
111
|
end
|
97
|
-
s <<
|
112
|
+
s << hm_stuff
|
98
113
|
end
|
99
114
|
|
100
115
|
schema_options = ::Brick.db_schemas.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
|
@@ -103,6 +118,13 @@ module Brick
|
|
103
118
|
table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables)
|
104
119
|
.each_with_object(+'') { |v, s| s << "<option value=\"#{v.underscore.pluralize}\">#{v}</option>" }.html_safe
|
105
120
|
css = +"<style>
|
121
|
+
#dropper {
|
122
|
+
background-color: #eee;
|
123
|
+
}
|
124
|
+
#btnImport {
|
125
|
+
display: none;
|
126
|
+
}
|
127
|
+
|
106
128
|
table {
|
107
129
|
border-collapse: collapse;
|
108
130
|
margin: 25px 0;
|
@@ -114,9 +136,12 @@ table {
|
|
114
136
|
|
115
137
|
table thead tr th, table tr th {
|
116
138
|
background-color: #009879;
|
117
|
-
color: #
|
139
|
+
color: #fff;
|
118
140
|
text-align: left;
|
119
141
|
}
|
142
|
+
table thead tr th a, table tr th a {
|
143
|
+
color: #80FFB8;
|
144
|
+
}
|
120
145
|
|
121
146
|
table th, table td {
|
122
147
|
padding: 0.2em 0.5em;
|
@@ -125,6 +150,9 @@ table th, table td {
|
|
125
150
|
.show-field {
|
126
151
|
background-color: #004998;
|
127
152
|
}
|
153
|
+
.show-field a {
|
154
|
+
color: #80B8D2;
|
155
|
+
}
|
128
156
|
|
129
157
|
table tbody tr {
|
130
158
|
border-bottom: thin solid #dddddd;
|
@@ -177,10 +205,10 @@ def hide_bcrypt(val)
|
|
177
205
|
end %>"
|
178
206
|
|
179
207
|
if ['index', 'show', 'update'].include?(args.first)
|
180
|
-
# Example: <% bts = { "site_id" => [:site, Site, "id"], "study_id" => [:study, Study, "id"], "study_country_id" => [:study_country, StudyCountry, "id"], "user_id" => [:user, User, "id"], "role_id" => [:role, Role, "id"] } %>
|
181
208
|
css << "<% bts = { #{bts.each_with_object([]) { |v, s| s << "#{v.first.inspect} => [#{v.last.first.inspect}, #{v.last[1].name}, #{v.last[1].primary_key.inspect}]"}.join(', ')} } %>"
|
182
209
|
end
|
183
210
|
|
211
|
+
# %%% When doing schema select, if there's an ID then remove it, or if we're on a new page go to index
|
184
212
|
script = "<script>
|
185
213
|
var schemaSelect = document.getElementById(\"schema\");
|
186
214
|
var brickSchema;
|
@@ -237,11 +265,106 @@ function changeout(href, param, value) {
|
|
237
265
|
</script>"
|
238
266
|
inline = case args.first
|
239
267
|
when 'index'
|
268
|
+
obj_pk = if pk&.is_a?(Array) # Composite primary key?
|
269
|
+
"[#{pk.map { |pk_part| "#{obj_name}.#{pk_part}" }.join(', ')}]"
|
270
|
+
elsif pk
|
271
|
+
"#{obj_name}.#{pk}"
|
272
|
+
end
|
273
|
+
if Object.const_defined?('DutyFree')
|
274
|
+
template_link = "
|
275
|
+
<%= link_to 'CSV', #{table_name}_path(format: :csv) %> <a href=\"#\" id=\"sheetsLink\">Sheets</a>
|
276
|
+
<div id=\"dropper\" contenteditable=\"true\"></div>
|
277
|
+
<input type=\"button\" id=\"btnImport\" value=\"Import\">"
|
278
|
+
end
|
240
279
|
"#{css}
|
241
280
|
<p style=\"color: green\"><%= notice %></p>#{"
|
242
281
|
<select id=\"schema\">#{schema_options}</select>" if ::Brick.db_schemas.length > 1}
|
243
282
|
<select id=\"tbl\">#{table_options}</select>
|
244
|
-
<h1>#{model_name.pluralize}</h1
|
283
|
+
<h1>#{model_name.pluralize}</h1>#{template_link}
|
284
|
+
<script>
|
285
|
+
var dropperDiv = document.getElementById(\"dropper\");
|
286
|
+
var btnImport = document.getElementById(\"btnImport\");
|
287
|
+
var droppedTSV;
|
288
|
+
if (dropperDiv) { // Other interesting events: blur keyup input
|
289
|
+
dropperDiv.addEventListener(\"paste\", function (evt) {
|
290
|
+
droppedTSV = evt.clipboardData.getData('text/plain');
|
291
|
+
var html = evt.clipboardData.getData('text/html');
|
292
|
+
var tbl = html.substring(html.indexOf(\"<tbody>\") + 7, html.lastIndexOf(\"</tbody>\"));
|
293
|
+
console.log(tbl);
|
294
|
+
btnImport.style.display = droppedTSV.length > 0 ? \"block\" : \"none\";
|
295
|
+
});
|
296
|
+
btnImport.addEventListener(\"click\", function () {
|
297
|
+
fetch(changeout(<%= #{obj_name}_path(-1, format: :csv).inspect.html_safe %>, \"_brick_schema\", brickSchema), {
|
298
|
+
method: 'PATCH',
|
299
|
+
headers: { 'Content-Type': 'text/tab-separated-values' },
|
300
|
+
body: droppedTSV
|
301
|
+
}).then(function (tsvResponse) {
|
302
|
+
btnImport.style.display = \"none\";
|
303
|
+
console.log(\"toaster\", tsvResponse);
|
304
|
+
});
|
305
|
+
});
|
306
|
+
}
|
307
|
+
var sheetUrl;
|
308
|
+
var spreadsheetId;
|
309
|
+
var sheetsLink = document.getElementById(\"sheetsLink\");
|
310
|
+
function gapiLoaded() {
|
311
|
+
// Have a click on the sheets link to bring up the sign-in window. (Must happen from some kind of user click.)
|
312
|
+
sheetsLink.addEventListener(\"click\", async function (evt) {
|
313
|
+
evt.preventDefault();
|
314
|
+
await gapi.load(\"client\", function () {
|
315
|
+
gapi.client.init({ // Load the discovery doc to initialize the API
|
316
|
+
clientId: \"487319557829-fgj4u660igrpptdji7ev0r5hb6kh05dh.apps.googleusercontent.com\",
|
317
|
+
scope: \"https://www.googleapis.com/auth/spreadsheets https://www.googleapis.com/auth/drive.file\",
|
318
|
+
discoveryDocs: [\"https://sheets.googleapis.com/$discovery/rest?version=v4\"]
|
319
|
+
}).then(function () {
|
320
|
+
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSignInStatus);
|
321
|
+
updateSignInStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
|
322
|
+
});
|
323
|
+
});
|
324
|
+
});
|
325
|
+
}
|
326
|
+
|
327
|
+
async function updateSignInStatus(isSignedIn) {
|
328
|
+
if (isSignedIn) {
|
329
|
+
console.log(\"turds!\");
|
330
|
+
await gapi.client.sheets.spreadsheets.create({
|
331
|
+
properties: {
|
332
|
+
title: #{table_name.inspect},
|
333
|
+
},
|
334
|
+
sheets: [
|
335
|
+
// sheet1, sheet2, sheet3
|
336
|
+
]
|
337
|
+
}).then(function (response) {
|
338
|
+
sheetUrl = response.result.spreadsheetUrl;
|
339
|
+
spreadsheetId = response.result.spreadsheetId;
|
340
|
+
sheetsLink.setAttribute(\"href\", sheetUrl); // response.result.spreadsheetUrl
|
341
|
+
console.log(\"x1\", sheetUrl);
|
342
|
+
|
343
|
+
// Get JSON data
|
344
|
+
fetch(changeout(<%= #{table_name}_path(format: :js).inspect.html_safe %>, \"_brick_schema\", brickSchema)).then(function (response) {
|
345
|
+
response.json().then(function (data) {
|
346
|
+
gapi.client.sheets.spreadsheets.values.append({
|
347
|
+
spreadsheetId: spreadsheetId,
|
348
|
+
range: \"Sheet1\",
|
349
|
+
valueInputOption: \"RAW\",
|
350
|
+
insertDataOption: \"INSERT_ROWS\"
|
351
|
+
}, {
|
352
|
+
range: \"Sheet1\",
|
353
|
+
majorDimension: \"ROWS\",
|
354
|
+
values: data,
|
355
|
+
}).then(function (response2) {
|
356
|
+
// console.log(\"beefcake\", response2);
|
357
|
+
});
|
358
|
+
});
|
359
|
+
});
|
360
|
+
});
|
361
|
+
window.open(sheetUrl, '_blank');
|
362
|
+
}
|
363
|
+
}
|
364
|
+
</script>
|
365
|
+
<script async defer src=\"https://apis.google.com/js/api.js\" onload=\"gapiLoaded()\"></script>
|
366
|
+
|
367
|
+
|
245
368
|
<% if @_brick_params&.present? %><h3>where <%= @_brick_params.each_with_object([]) { |v, s| s << \"#\{v.first\} = #\{v.last.inspect\}\" }.join(', ') %></h3><% end %>
|
246
369
|
<table id=\"#{table_name}\">
|
247
370
|
<thead><tr>#{'<th></th>' if pk}
|
@@ -249,26 +372,27 @@ function changeout(href, param, value) {
|
|
249
372
|
<% next if col == '#{pk}' || ::Brick.config.metadata_columns.include?(col) %>
|
250
373
|
<th>
|
251
374
|
<% if (bt = bts[col]) %>
|
252
|
-
BT <%=
|
375
|
+
BT <%= bt[1].bt_link(bt.first) %>
|
253
376
|
<% else %>
|
254
377
|
<%= col %>
|
255
378
|
<% end %>
|
256
379
|
</th>
|
257
380
|
<% end %>
|
258
|
-
|
381
|
+
<%# Consider getting the name from the association -- h.first.name -- if a more \"friendly\" alias should be used for a screwy table name %>
|
382
|
+
#{hms_headers.map { |h| "<th>#{h[1]} <%= link_to('#{h[2]}', #{h.first.klass.name.underscore.pluralize}_path) %></th>\n" }.join}
|
259
383
|
</tr></thead>
|
260
384
|
|
261
385
|
<tbody>
|
262
386
|
<% @#{table_name}.each do |#{obj_name}| %>
|
263
387
|
<tr>#{"
|
264
|
-
<td><%= link_to '⇛', #{obj_name}_path(#{
|
388
|
+
<td><%= link_to '⇛', #{obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
|
265
389
|
<% #{obj_name}.attributes.each do |k, val| %>
|
266
390
|
<% next if k == '#{pk}' || ::Brick.config.metadata_columns.include?(k) || k.start_with?('_brfk_') || (k.start_with?('_br_') && k.end_with?('_ct')) %>
|
267
391
|
<td>
|
268
392
|
<% if (bt = bts[k]) %>
|
269
|
-
<%# binding.pry
|
270
|
-
<% bt_txt = bt[1].brick_descrip(#{obj_name}, @_brick_bt_descrip[bt.first][1].map { |z| #{obj_name}.send(z.last) }, @_brick_bt_descrip[bt.first][2]) %>
|
271
|
-
<% bt_id_col = @_brick_bt_descrip[bt.first][2]; bt_id = #{obj_name}.send(bt_id_col) if bt_id_col %>
|
393
|
+
<%# binding.pry # Postgres column names are limited to 63 characters %>
|
394
|
+
<% bt_txt = bt[1].brick_descrip(#{obj_name}, @_brick_bt_descrip[bt.first][1].map { |z| #{obj_name}.send(z.last[0..62]) }, @_brick_bt_descrip[bt.first][2]) %>
|
395
|
+
<% bt_id_col = @_brick_bt_descrip[bt.first][2]; bt_id = #{obj_name}.send(*bt_id_col) if bt_id_col&.present? %>
|
272
396
|
<%= bt_id ? link_to(bt_txt, send(\"#\{bt_obj_path_base = bt[1].name.underscore\}_path\".to_sym, bt_id)) : bt_txt %>
|
273
397
|
<%#= Previously was: bt_obj = bt[1].find_by(bt[2] => val); link_to(bt_obj.brick_descrip, send(\"#\{bt_obj_path_base = bt[1].name.underscore\}_path\".to_sym, bt_obj.send(bt[1].primary_key.to_sym))) if bt_obj %>
|
274
398
|
<% else %>
|
@@ -276,7 +400,7 @@ function changeout(href, param, value) {
|
|
276
400
|
<% end %>
|
277
401
|
</td>
|
278
402
|
<% end %>
|
279
|
-
|
403
|
+
<td>#{hms_columns.join('</td><td>')}</td>
|
280
404
|
<!-- td>X</td -->
|
281
405
|
</tr>
|
282
406
|
</tbody>
|
@@ -294,12 +418,13 @@ function changeout(href, param, value) {
|
|
294
418
|
<%= link_to '(See all #{obj_name.pluralize})', #{table_name}_path %>
|
295
419
|
<% if obj %>
|
296
420
|
<%= # path_options = [obj.#{pk}]
|
297
|
-
# path_options << { '_brick_schema': } if
|
421
|
+
# path_options << { '_brick_schema': } if
|
298
422
|
# url = send(:#{model_name.underscore}_path, obj.#{pk})
|
299
|
-
form_for(obj) do |f| %>
|
423
|
+
form_for(obj.becomes(#{model_name})) do |f| %>
|
300
424
|
<table>
|
301
425
|
<% @#{obj_name}.first.attributes.each do |k, val| %>
|
302
426
|
<tr>
|
427
|
+
<%# %%% Accommodate composite keys %>
|
303
428
|
<% next if k == '#{pk}' || ::Brick.config.metadata_columns.include?(k) %>
|
304
429
|
<th class=\"show-field\">
|
305
430
|
<% if (bt = bts[k])
|
@@ -308,9 +433,10 @@ function changeout(href, param, value) {
|
|
308
433
|
# %%% Only do this if the user has permissions to edit this bt field
|
309
434
|
if bt.length < 4
|
310
435
|
bt << (option_detail = [[\"(No #\{bt_name\} chosen)\", '^^^brick_NULL^^^']])
|
311
|
-
|
436
|
+
# %%% Accommodate composite keys for obj.pk at the end here
|
437
|
+
bt[1].order(bt[1].primary_key).each { |obj| option_detail << [obj.brick_descrip, obj.send(bt[1].primary_key)] }
|
312
438
|
end %>
|
313
|
-
BT <%=
|
439
|
+
BT <%= bt[1].bt_link(bt.first) %>
|
314
440
|
<% else %>
|
315
441
|
<%= k %>
|
316
442
|
<% end %>
|
@@ -346,21 +472,24 @@ function changeout(href, param, value) {
|
|
346
472
|
</table>
|
347
473
|
<% end %>
|
348
474
|
|
349
|
-
#{hms_headers.
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
475
|
+
#{hms_headers.each_with_object(+'') do |hm, s|
|
476
|
+
if (pk = hm.first.klass.primary_key)
|
477
|
+
s << "<table id=\"#{hm_name = hm.first.name.to_s}\">
|
478
|
+
<tr><th>#{hm[3]}</th></tr>
|
479
|
+
<% collection = @#{obj_name}.first.#{hm_name}
|
480
|
+
collection = collection.is_a?(ActiveRecord::Associations::CollectionProxy) ? collection.order(#{pk.inspect}) : [collection]
|
481
|
+
if collection.empty? %>
|
482
|
+
<tr><td>(none)</td></tr>
|
483
|
+
<% else %>
|
484
|
+
<% collection.uniq.each do |#{hm_singular_name = hm_name.singularize.underscore}| %>
|
485
|
+
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass.name.underscore}_path(#{hm_singular_name}.#{pk})) %></td></tr>
|
486
|
+
<% end %>
|
487
|
+
<% end %>
|
488
|
+
</table>"
|
489
|
+
else
|
490
|
+
s
|
491
|
+
end
|
492
|
+
end}
|
364
493
|
<% end %>
|
365
494
|
#{script}"
|
366
495
|
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -103,11 +103,8 @@ module Brick
|
|
103
103
|
|
104
104
|
def get_bts_and_hms(model)
|
105
105
|
bts, hms = model.reflect_on_all_associations.each_with_object([{}, {}]) do |a, s|
|
106
|
-
# So that we can map an association name to any special alias name used in an AREL query
|
107
|
-
ans = (model._assoc_names[a.name] ||= [])
|
108
106
|
next if !const_defined?(a.name.to_s.singularize.camelize) && ::Brick.config.exclude_tables.include?(a.plural_name)
|
109
107
|
|
110
|
-
ans << a.klass unless ans.include?(a.klass)
|
111
108
|
case a.macro
|
112
109
|
when :belongs_to
|
113
110
|
s.first[a.foreign_key] = [a.name, a.klass]
|
@@ -129,9 +126,7 @@ module Brick
|
|
129
126
|
skip_hms[hmt.last.name] = nil
|
130
127
|
end
|
131
128
|
end
|
132
|
-
skip_hms.each
|
133
|
-
puts hms.delete(k).inspect
|
134
|
-
end
|
129
|
+
skip_hms.each { |k, _v| hms.delete(k) }
|
135
130
|
[bts, hms, associatives]
|
136
131
|
end
|
137
132
|
|
@@ -240,6 +235,12 @@ module Brick
|
|
240
235
|
end
|
241
236
|
end
|
242
237
|
|
238
|
+
# Skip showing counts for these specific has_many associations when building auto-generated #index views
|
239
|
+
# @api public
|
240
|
+
def skip_index_hms=(value)
|
241
|
+
Brick.config.skip_index_hms = value
|
242
|
+
end
|
243
|
+
|
243
244
|
# Associations to treat as a has_one
|
244
245
|
# @api public
|
245
246
|
def has_ones=(hos)
|
@@ -421,9 +422,7 @@ ActiveSupport.on_load(:active_record) do
|
|
421
422
|
end
|
422
423
|
|
423
424
|
result = result.map do |attributes|
|
424
|
-
|
425
|
-
|
426
|
-
columns.zip(values).map do |column, value|
|
425
|
+
columns.zip(klass.initialize_attributes(attributes).values).map do |column, value|
|
427
426
|
column.type_cast(value)
|
428
427
|
end
|
429
428
|
end
|
@@ -111,11 +111,16 @@ module Brick
|
|
111
111
|
# # to be the primary key.)
|
112
112
|
#{bar}
|
113
113
|
|
114
|
-
# # Skip creating a has_many association for these
|
114
|
+
# # Skip creating a has_many association for these (only retain the belongs_to built from this additional_reference).
|
115
115
|
# # (Uses the same exact three-part format as would define an additional_reference)
|
116
116
|
# # Say for instance that we didn't care to display the favourite colours that users have:
|
117
117
|
# Brick.exclude_hms = [['users', 'favourite_colour_id', 'colours']]
|
118
118
|
|
119
|
+
# # Skip showing counts for these specific has_many associations when building auto-generated #index views.
|
120
|
+
# # When there are related tables with a significant number of records, this can lessen the load on the database
|
121
|
+
# # considerably, sometimes fixing what might appear to be an index page that just \"hangs\" for no apparent reason.
|
122
|
+
Brick.skip_index_hms = ['User.litany_of_woes']
|
123
|
+
|
119
124
|
# # By default primary tables involved in a foreign key relationship will indicate a \"has_many\" relationship pointing
|
120
125
|
# # back to the foreign table. In order to represent a \"has_one\" association instead, an override can be provided
|
121
126
|
# # using the primary model name and the association name which you instead want to have treated as a \"has_one\":
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.23
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lorin Thwaits
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|