brick 1.0.190 → 1.0.192
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/brick/config.rb +8 -0
- data/lib/brick/extensions.rb +175 -48
- data/lib/brick/frameworks/rails/engine.rb +34 -14
- data/lib/brick/frameworks/rails/form_builder.rb +2 -1
- data/lib/brick/frameworks/rails/form_tags.rb +13 -4
- data/lib/brick/frameworks/rails.rb +6 -0
- data/lib/brick/route_mapper.rb +348 -0
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +10 -335
- data/lib/generators/brick/controllers_generator.rb +91 -0
- data/lib/generators/brick/migration_builder.rb +224 -160
- data/lib/generators/brick/migrations_generator.rb +1 -0
- data/lib/generators/brick/models_generator.rb +2 -2
- data/lib/generators/brick/salesforce_migrations_generator.rb +101 -0
- data/lib/generators/brick/salesforce_schema.rb +105 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 148c8f46702d06322322c04487a1045e6d7a3927611394a84ffe138be78adb5e
|
4
|
+
data.tar.gz: 716bfaae50dc99809bc3aee4eecf5ff1c44325bca54271db225a765cdd97fb56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d97d94ed8d951718b34d1fc0d3ee8d023b3765bafc759fd41161155c927014ddec54a67e371363bbb17b18a614f4be16862d9cb51be313438050a3ce6e3356a2
|
7
|
+
data.tar.gz: efbd8b062eccd5d5441aa8e9fe6162bce076e188761f5c7877f9ca454899fc13873fa6cdf4db21cd2154e24c824487f56a085f41402432bd99486e17213e7f00
|
data/lib/brick/config.rb
CHANGED
@@ -399,6 +399,14 @@ module Brick
|
|
399
399
|
@mutex.synchronize { @not_nullables = columns }
|
400
400
|
end
|
401
401
|
|
402
|
+
def omit_empty_tables_in_dropdown
|
403
|
+
@mutex.synchronize { @omit_empty_tables_in_dropdown }
|
404
|
+
end
|
405
|
+
|
406
|
+
def omit_empty_tables_in_dropdown=(field_set)
|
407
|
+
@mutex.synchronize { @omit_empty_tables_in_dropdown = field_set }
|
408
|
+
end
|
409
|
+
|
402
410
|
def always_load_fields
|
403
411
|
@mutex.synchronize { @always_load_fields || {} }
|
404
412
|
end
|
data/lib/brick/extensions.rb
CHANGED
@@ -82,8 +82,10 @@ module ActiveRecord
|
|
82
82
|
|
83
83
|
def json_column?(col)
|
84
84
|
col.type == :json || ::Brick.config.json_columns[table_name]&.include?(col.name) ||
|
85
|
-
(
|
86
|
-
|
85
|
+
(
|
86
|
+
respond_to?(:attribute_types) && (attr_types = attribute_types[col.name]).respond_to?(:coder) &&
|
87
|
+
(attr_types.coder.is_a?(Class) ? attr_types.coder : attr_types.coder&.class)&.name&.end_with?('JSON')
|
88
|
+
)
|
87
89
|
end
|
88
90
|
|
89
91
|
def brick_foreign_type(assoc)
|
@@ -284,7 +286,10 @@ module ActiveRecord
|
|
284
286
|
end
|
285
287
|
this_obj&.to_s || ''
|
286
288
|
end
|
287
|
-
|
289
|
+
begin
|
290
|
+
is_brackets_have_content = true unless datum.blank?
|
291
|
+
rescue
|
292
|
+
end
|
288
293
|
output << (datum || '')
|
289
294
|
bracket_name = nil
|
290
295
|
else
|
@@ -329,27 +334,10 @@ module ActiveRecord
|
|
329
334
|
end
|
330
335
|
|
331
336
|
# Providing a relation object allows auto-modules built from table name prefixes to work
|
332
|
-
def self._brick_index(mode = nil, separator =
|
337
|
+
def self._brick_index(mode = nil, separator = nil, relation = nil)
|
333
338
|
return if abstract_class?
|
334
339
|
|
335
|
-
|
336
|
-
tbl_parts.shift if ::Brick.apartment_multitenant && tbl_parts.length > 1 && tbl_parts.first == ::Brick.apartment_default_tenant
|
337
|
-
if (aps = relation&.fetch(:auto_prefixed_schema, nil)) && tbl_parts.last.start_with?(aps)
|
338
|
-
last_part = tbl_parts.last[aps.length..-1]
|
339
|
-
aps = aps[0..-2] if aps[-1] == '_'
|
340
|
-
tbl_parts[-1] = aps
|
341
|
-
tbl_parts << last_part
|
342
|
-
end
|
343
|
-
path_prefix = []
|
344
|
-
if ::Brick.config.path_prefix
|
345
|
-
tbl_parts.unshift(::Brick.config.path_prefix)
|
346
|
-
path_prefix << ::Brick.config.path_prefix
|
347
|
-
end
|
348
|
-
index = tbl_parts.map(&:underscore).join(separator)
|
349
|
-
# Rails applies an _index suffix to that route when the resource name isn't something plural
|
350
|
-
index << '_index' if mode != :singular && separator == '_' &&
|
351
|
-
index == (path_prefix + [name&.underscore&.tr('/', '_') || '_']).join(separator)
|
352
|
-
index
|
340
|
+
::Brick._brick_index(table_name, mode, separator, relation)
|
353
341
|
end
|
354
342
|
|
355
343
|
def self.brick_import_template
|
@@ -686,7 +674,7 @@ module ActiveRecord
|
|
686
674
|
(cust_col_override || klass._br_cust_cols).each do |k, cc|
|
687
675
|
if rel_dupe.respond_to?(k) # Name already taken?
|
688
676
|
# %%% Use ensure_unique here in this kind of fashion:
|
689
|
-
# cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", bts, hms)
|
677
|
+
# cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", nil, bts, hms)
|
690
678
|
# binding.pry
|
691
679
|
next
|
692
680
|
end
|
@@ -806,6 +794,7 @@ module ActiveRecord
|
|
806
794
|
|
807
795
|
# Add derived table JOIN for the has_many counts
|
808
796
|
nix = []
|
797
|
+
previous = []
|
809
798
|
klass._br_hm_counts.each do |k, hm|
|
810
799
|
count_column = if hm.options[:through]
|
811
800
|
# Build the chain of JOINs going to the final destination HMT table
|
@@ -832,7 +821,7 @@ module ActiveRecord
|
|
832
821
|
through_sources.push(this_hm = src_ref.active_record.reflect_on_association(thr))
|
833
822
|
end
|
834
823
|
through_sources.push(src_ref) unless src_ref.belongs_to?
|
835
|
-
from_clause = +"#{through_sources.first.table_name} br_t0"
|
824
|
+
from_clause = +"#{_br_quoted_name(through_sources.first.table_name)} br_t0"
|
836
825
|
fk_col = through_sources.shift.foreign_key
|
837
826
|
|
838
827
|
idx = 0
|
@@ -900,7 +889,7 @@ module ActiveRecord
|
|
900
889
|
next
|
901
890
|
end
|
902
891
|
|
903
|
-
tbl_alias = "b_r_#{hm.name}"
|
892
|
+
tbl_alias = unique63("b_r_#{hm.name}", previous)
|
904
893
|
on_clause = []
|
905
894
|
hm_selects = if fk_col.is_a?(Array) # Composite key?
|
906
895
|
fk_col.each_with_index { |fk_col_part, idx| on_clause << "#{tbl_alias}.#{fk_col_part} = #{pri_tbl.table_name}.#{pri_key[idx]}" }
|
@@ -1105,6 +1094,19 @@ Might want to add this in your brick.rb:
|
|
1105
1094
|
def shift_or_first(ary)
|
1106
1095
|
ary.length > 1 ? ary.shift : ary.first
|
1107
1096
|
end
|
1097
|
+
|
1098
|
+
def unique63(name, previous)
|
1099
|
+
name = name[0..62] if name.length > 63
|
1100
|
+
unique_num = 1
|
1101
|
+
loop do
|
1102
|
+
break unless previous.include?(name)
|
1103
|
+
|
1104
|
+
unique_suffix = "_#{unique_num += 1}"
|
1105
|
+
name = "#{name[0..name.length - unique_suffix.length - 1]}#{unique_suffix}"
|
1106
|
+
end
|
1107
|
+
previous << name
|
1108
|
+
name
|
1109
|
+
end
|
1108
1110
|
end
|
1109
1111
|
|
1110
1112
|
module Inheritance
|
@@ -1360,7 +1362,7 @@ end
|
|
1360
1362
|
end
|
1361
1363
|
rescue NameError # If the const_get for the model has failed...
|
1362
1364
|
skip_controller = true
|
1363
|
-
# ... then just fall through and allow it to fail when trying to
|
1365
|
+
# ... then just fall through and allow it to fail when trying to load the ____Controller class normally.
|
1364
1366
|
end
|
1365
1367
|
end
|
1366
1368
|
unless skip_controller
|
@@ -1715,7 +1717,8 @@ class Object
|
|
1715
1717
|
# options[:class_name] = hm.first[:inverse_table].singularize.camelize
|
1716
1718
|
# options[:foreign_key] = hm.first[:fk].to_sym
|
1717
1719
|
far_assoc = relations[hm.first[:inverse_table]][:fks].find { |_k, v| v[:assoc_name] == hm[1] }
|
1718
|
-
|
1720
|
+
# Was: ::Brick.namify(far_assoc.last[:inverse_table], :underscore).camelize
|
1721
|
+
options[:class_name] = relations[far_assoc.last[:inverse_table]][:class_name]
|
1719
1722
|
options[:foreign_key] = far_assoc.last[:fk].to_sym
|
1720
1723
|
end
|
1721
1724
|
options[:source] ||= hm[1].to_sym unless hmt_name.singularize == hm[1]
|
@@ -1744,7 +1747,7 @@ class Object
|
|
1744
1747
|
options[:optional] = true if assoc.key?(:optional)
|
1745
1748
|
if assoc.key?(:polymorphic) ||
|
1746
1749
|
# If a polymorphic association is missing but could be established then go ahead and put it into place.
|
1747
|
-
relations
|
1750
|
+
relations.fetch(assoc[:inverse_table], nil)&.fetch(:class_name, nil)&.constantize&.reflect_on_all_associations&.find { |inv_assoc| !inv_assoc.belongs_to? && inv_assoc.options[:as].to_s == assoc[:assoc_name] }
|
1748
1751
|
assoc[:polymorphic] ||= true
|
1749
1752
|
options[:polymorphic] = true
|
1750
1753
|
else
|
@@ -1802,9 +1805,9 @@ class Object
|
|
1802
1805
|
::Brick.config.schema_behavior[:multitenant] && singular_table_parts.first == 'public'
|
1803
1806
|
singular_table_parts.shift
|
1804
1807
|
end
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
1808
|
+
if need_class_name
|
1809
|
+
options[:class_name] = "::#{assoc[:primary_class]&.name || ::Brick.relations[inverse_table][:class_name]}"
|
1810
|
+
end
|
1808
1811
|
if need_fk # Funky foreign key?
|
1809
1812
|
options_fk_key = :foreign_key
|
1810
1813
|
if assoc[:fk].is_a?(Array)
|
@@ -2559,6 +2562,8 @@ class Object
|
|
2559
2562
|
assoc_name = assoc_parts.join('.')
|
2560
2563
|
else
|
2561
2564
|
class_name_parts = ::Brick.namify(hm_assoc[:inverse_table], :underscore).split('.')
|
2565
|
+
last_idx = class_name_parts.length - 1
|
2566
|
+
class_name_parts[last_idx] = class_name_parts[last_idx].singularize
|
2562
2567
|
real_name = class_name_parts.map(&:camelize).join('::')
|
2563
2568
|
needs_class = (real_name != hm_assoc[:inverse_table].camelize)
|
2564
2569
|
end
|
@@ -2645,6 +2650,10 @@ end.class_exec do
|
|
2645
2650
|
orig_schema = nil
|
2646
2651
|
if (relations = ::Brick.relations).keys == [:db_name]
|
2647
2652
|
::Brick.remove_instance_variable(:@_additional_references_loaded) if ::Brick.instance_variable_defined?(:@_additional_references_loaded)
|
2653
|
+
|
2654
|
+
# --------------------------------------------
|
2655
|
+
# 1. Load three initializers early
|
2656
|
+
# (inflectsions.rb, brick.rb, apartment.rb)
|
2648
2657
|
# Very first thing, load inflections since we'll be using .pluralize and .singularize on table and model names
|
2649
2658
|
if File.exist?(inflections = ::Rails.root&.join('config/initializers/inflections.rb') || '')
|
2650
2659
|
load inflections
|
@@ -2695,6 +2704,8 @@ end.class_exec do
|
|
2695
2704
|
# Only for Postgres (Doesn't work in sqlite3 or MySQL)
|
2696
2705
|
# puts ActiveRecord::Base.execute_sql("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
2697
2706
|
|
2707
|
+
# ---------------------------
|
2708
|
+
# 2. Figure out schema things
|
2698
2709
|
is_postgres = nil
|
2699
2710
|
is_mssql = ActiveRecord::Base.connection.adapter_name == 'SQLServer'
|
2700
2711
|
case ActiveRecord::Base.connection.adapter_name
|
@@ -2767,6 +2778,8 @@ end.class_exec do
|
|
2767
2778
|
|
2768
2779
|
::Brick.db_schemas ||= {}
|
2769
2780
|
|
2781
|
+
# ---------------------
|
2782
|
+
# 3. Tables and columns
|
2770
2783
|
# %%% Retrieve internal ActiveRecord table names like this:
|
2771
2784
|
# ActiveRecord::Base.internal_metadata_table_name, ActiveRecord::Base.schema_migrations_table_name
|
2772
2785
|
# For if it's not SQLite -- so this is the Postgres and MySQL version
|
@@ -2896,20 +2909,34 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
2896
2909
|
# end
|
2897
2910
|
# end
|
2898
2911
|
# schema = ::Brick.default_schema # Reset back for this next round of fun
|
2912
|
+
|
2913
|
+
# ---------------------------------------------
|
2914
|
+
# 4. Foreign key info
|
2915
|
+
# (done in two parts which get JOINed together in Ruby code)
|
2899
2916
|
kcus = nil
|
2917
|
+
entry_type = nil
|
2900
2918
|
case ActiveRecord::Base.connection.adapter_name
|
2901
2919
|
when 'PostgreSQL', 'Mysql2', 'Trilogy', 'SQLServer'
|
2902
|
-
#
|
2920
|
+
# Part 1 -- all KCUs
|
2903
2921
|
sql = "SELECT CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, ORDINAL_POSITION,
|
2904
2922
|
TABLE_NAME, COLUMN_NAME
|
2905
2923
|
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE#{"
|
2906
|
-
WHERE CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema }"
|
2924
|
+
WHERE CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema }#{"
|
2925
|
+
WHERE CONSTRAINT_SCHEMA = '#{ActiveRecord::Base.connection.current_database&.tr("'", "''")}'" if is_mysql
|
2926
|
+
}"
|
2907
2927
|
kcus = ActiveRecord::Base.execute_sql(sql).each_with_object({}) do |v, s|
|
2908
|
-
|
2909
|
-
|
2910
|
-
|
2928
|
+
if (entry_type ||= v.is_a?(Array) ? :array : :hash) == :hash
|
2929
|
+
key = "#{v['constraint_name']}.#{v['constraint_schema']}.#{v['constraint_catalog']}.#{v['ordinal_position']}"
|
2930
|
+
key << ".#{v['table_name']}.#{v['column_name']}" unless is_postgres || is_mssql
|
2931
|
+
s[key] = [v['constraint_schema'], v['table_name']]
|
2932
|
+
else # Array
|
2933
|
+
key = "#{v[2]}.#{v[1]}.#{v[0]}.#{v[3]}"
|
2934
|
+
key << ".#{v[4]}.#{v[5]}" unless is_postgres || is_mssql
|
2935
|
+
s[key] = [v[1], v[4]]
|
2936
|
+
end
|
2911
2937
|
end
|
2912
2938
|
|
2939
|
+
# Part 2 -- fk_references
|
2913
2940
|
sql = "SELECT kcu.CONSTRAINT_SCHEMA, kcu.TABLE_NAME, kcu.COLUMN_NAME,
|
2914
2941
|
#{# These will get filled in with real values (effectively doing the JOIN in Ruby)
|
2915
2942
|
is_postgres || is_mssql ? 'NULL as primary_schema, NULL as primary_table' :
|
@@ -2921,7 +2948,8 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
2921
2948
|
ON kcu.CONSTRAINT_CATALOG = rc.CONSTRAINT_CATALOG
|
2922
2949
|
AND kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
|
2923
2950
|
AND kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME#{"
|
2924
|
-
WHERE kcu.CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema
|
2951
|
+
WHERE kcu.CONSTRAINT_SCHEMA = COALESCE(current_setting('SEARCH_PATH'), 'public')" if is_postgres && schema}#{"
|
2952
|
+
WHERE kcu.CONSTRAINT_SCHEMA = '#{ActiveRecord::Base.connection.current_database&.tr("'", "''")}'" if is_mysql}"
|
2925
2953
|
fk_references = ActiveRecord::Base.execute_sql(sql)
|
2926
2954
|
when 'SQLite'
|
2927
2955
|
sql = "SELECT NULL AS constraint_schema, m.name, fkl.\"from\", NULL AS primary_schema, fkl.\"table\", m.name || '_' || fkl.\"from\" AS constraint_name
|
@@ -2949,11 +2977,13 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
2949
2977
|
end
|
2950
2978
|
::Brick.is_oracle = true if ActiveRecord::Base.connection.adapter_name == 'OracleEnhanced'
|
2951
2979
|
# ::Brick.default_schema ||= schema ||= 'public' if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
2952
|
-
::Brick.default_schema ||= 'public' if
|
2980
|
+
::Brick.default_schema ||= 'public' if is_postgres
|
2953
2981
|
fk_references&.each do |fk|
|
2954
2982
|
fk = fk.values unless fk.is_a?(Array)
|
2955
|
-
# Virtually JOIN
|
2956
|
-
|
2983
|
+
# Virtually JOIN KCUs to fk_references in order to fill in the primary schema and primary table
|
2984
|
+
kcu_key = "#{fk[6]}.#{fk[7]}.#{fk[8]}.#{fk[9]}"
|
2985
|
+
kcu_key << ".#{fk[3]}.#{fk[4]}" unless is_postgres || is_mssql
|
2986
|
+
if (kcu = kcus&.fetch(kcu_key, nil))
|
2957
2987
|
fk[3] = kcu[0]
|
2958
2988
|
fk[4] = kcu[1]
|
2959
2989
|
end
|
@@ -3000,18 +3030,86 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
|
|
3000
3030
|
&.find { |k1, _v1| singular.start_with?(k1) && singular.length > k1.length }
|
3001
3031
|
).present?
|
3002
3032
|
v[:auto_prefixed_schema] = tnp.first
|
3003
|
-
v[:resource] = rel_name.last[
|
3004
|
-
[tnp.last, singular[
|
3033
|
+
# v[:resource] = rel_name.last[tnp.first.length..-1]
|
3034
|
+
[tnp.last, singular[tnp.first.length..-1]]
|
3005
3035
|
else
|
3006
|
-
v[:resource] = rel_name.last
|
3036
|
+
# v[:resource] = rel_name.last
|
3007
3037
|
[singular]
|
3008
3038
|
end
|
3009
|
-
|
3039
|
+
proposed_name_parts = (schema_names + name_parts).map { |p| ::Brick.namify(p, :underscore).camelize }
|
3040
|
+
# Find out if the proposed name leads to a module or class that already exists and is not an AR class
|
3041
|
+
colliding_thing = nil
|
3042
|
+
loop do
|
3043
|
+
klass = Object
|
3044
|
+
proposed_name_parts.each do |part|
|
3045
|
+
if klass.const_defined?(part)
|
3046
|
+
klass = klass.const_get(part)
|
3047
|
+
else
|
3048
|
+
klass = nil
|
3049
|
+
break
|
3050
|
+
end
|
3051
|
+
end
|
3052
|
+
break if !klass || (klass < ActiveRecord::Base) # Break if all good -- no conflicts
|
3053
|
+
|
3054
|
+
# Find a unique name since there's already something that's non-AR with that same name
|
3055
|
+
last_idx = proposed_name_parts.length - 1
|
3056
|
+
proposed_name_parts[last_idx] = ::Brick.ensure_unique(proposed_name_parts[last_idx], 'X')
|
3057
|
+
colliding_thing ||= klass
|
3058
|
+
end
|
3059
|
+
v[:class_name] = proposed_name_parts.join('::')
|
3060
|
+
# Was: v[:resource] = v[:class_name].underscore.tr('/', '.').pluralize
|
3061
|
+
v[:resource] = proposed_name_parts.last.underscore.pluralize
|
3062
|
+
if colliding_thing
|
3063
|
+
message_start = if colliding_thing.is_a?(Module) && Object.const_defined?(:Rails) &&
|
3064
|
+
colliding_thing.constants.find { |c| colliding_thing.const_get(c) < Rails::Application }
|
3065
|
+
"The module for the Rails application itself, \"#{colliding_thing.name}\","
|
3066
|
+
else
|
3067
|
+
"Non-AR #{colliding_thing.class.name.downcase} \"#{colliding_thing.name}\""
|
3068
|
+
end
|
3069
|
+
puts "WARNING: #{message_start} already exists.\n Will set up to auto-create model #{v[:class_name]} for table #{k}."
|
3070
|
+
end
|
3010
3071
|
# Track anything that's out-of-the-ordinary
|
3011
3072
|
table_name_lookup[v[:class_name]] = k unless v[:class_name].underscore.pluralize == k
|
3012
3073
|
end
|
3013
3074
|
::Brick.load_additional_references if ::Brick.initializer_loaded
|
3014
3075
|
|
3076
|
+
if is_postgres
|
3077
|
+
params = []
|
3078
|
+
ActiveRecord::Base.execute_sql("-- inherited and partitioned tables counts
|
3079
|
+
SELECT n.nspname, parent.relname,
|
3080
|
+
((SUM(child.reltuples::float) / greatest(SUM(child.relpages), 1))) *
|
3081
|
+
(SUM(pg_relation_size(child.oid))::float / (current_setting('block_size')::float))::integer AS rowcount
|
3082
|
+
FROM pg_inherits
|
3083
|
+
INNER JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
|
3084
|
+
INNER JOIN pg_class child ON pg_inherits.inhrelid = child.oid
|
3085
|
+
INNER JOIN pg_catalog.pg_namespace n ON n.oid = parent.relnamespace#{
|
3086
|
+
if schema
|
3087
|
+
params = params << schema
|
3088
|
+
"
|
3089
|
+
WHERE n.nspname = COALESCE(?, 'public')"
|
3090
|
+
end}
|
3091
|
+
GROUP BY n.nspname, parent.relname, child.reltuples, child.relpages, child.oid
|
3092
|
+
|
3093
|
+
UNION ALL
|
3094
|
+
|
3095
|
+
-- table count
|
3096
|
+
SELECT n.nspname, pg_class.relname,
|
3097
|
+
(pg_class.reltuples::float / greatest(pg_class.relpages, 1)) *
|
3098
|
+
(pg_relation_size(pg_class.oid)::float / (current_setting('block_size')::float))::integer AS rowcount
|
3099
|
+
FROM pg_class
|
3100
|
+
INNER JOIN pg_catalog.pg_namespace n ON n.oid = pg_class.relnamespace#{
|
3101
|
+
if schema
|
3102
|
+
params = params << schema
|
3103
|
+
"
|
3104
|
+
WHERE n.nspname = COALESCE(?, 'public')"
|
3105
|
+
end}
|
3106
|
+
GROUP BY n.nspname, pg_class.relname, pg_class.reltuples, pg_class.relpages, pg_class.oid", params).each do |tblcount|
|
3107
|
+
# %%% What is the default schema here?
|
3108
|
+
prefix = "#{tblcount['nspname']}." unless tblcount['nspname'] == (schema || 'public')
|
3109
|
+
relations.fetch("#{prefix}#{tblcount['relname']}", nil)&.[]=(:rowcount, tblcount['rowcount'].to_i.round)
|
3110
|
+
end
|
3111
|
+
end
|
3112
|
+
|
3015
3113
|
if orig_schema && (orig_schema = (orig_schema - ['pg_catalog', 'pg_toast', 'heroku_ext']).first)
|
3016
3114
|
puts "Now switching back to \"#{orig_schema}\" schema."
|
3017
3115
|
ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", orig_schema)
|
@@ -3151,7 +3249,7 @@ module Brick
|
|
3151
3249
|
# For any appended references (those that come from config), arrive upon a definitely unique constraint name
|
3152
3250
|
pri_tbl = is_class ? fk[4][:class].underscore : pri_tbl
|
3153
3251
|
pri_tbl = "#{bt_assoc_name}_#{pri_tbl}" if pri_tbl&.singularize != bt_assoc_name
|
3154
|
-
cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", bts, hms)
|
3252
|
+
cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", nil, bts, hms)
|
3155
3253
|
missing = []
|
3156
3254
|
missing << fk[1] unless relations.key?(fk[1])
|
3157
3255
|
missing << primary_table unless is_class || relations.key?(primary_table)
|
@@ -3285,15 +3383,17 @@ module Brick
|
|
3285
3383
|
end
|
3286
3384
|
end
|
3287
3385
|
|
3288
|
-
def ensure_unique(name, *sources)
|
3386
|
+
def ensure_unique(name, delimiter, *sources)
|
3289
3387
|
base = name
|
3290
|
-
|
3388
|
+
delimiter ||= '_'
|
3389
|
+
# By default ends up building this regex: /_(\d+)$/
|
3390
|
+
if (added_num = name.slice!(Regexp.new("#{delimiter}(\d+)$")))
|
3291
3391
|
added_num = added_num[1..-1].to_i
|
3292
3392
|
else
|
3293
3393
|
added_num = 1
|
3294
3394
|
end
|
3295
3395
|
while (
|
3296
|
-
name = "#{base}
|
3396
|
+
name = "#{base}#{delimiter}#{added_num += 1}"
|
3297
3397
|
sources.each_with_object(nil) do |v, s|
|
3298
3398
|
s || case v
|
3299
3399
|
when Hash
|
@@ -3371,6 +3471,33 @@ module Brick
|
|
3371
3471
|
end
|
3372
3472
|
end
|
3373
3473
|
|
3474
|
+
def _brick_index(tbl_name, mode = nil, separator = nil, relation = nil)
|
3475
|
+
separator ||= '_'
|
3476
|
+
res_name = (tbl_name_parts = tbl_name.split('.'))[0..-2].first
|
3477
|
+
res_name << '.' if res_name
|
3478
|
+
(res_name ||= +'') << (relation || ::Brick.relations.fetch(tbl_name, nil))&.fetch(:resource, nil) || tbl_name_parts.last
|
3479
|
+
|
3480
|
+
res_parts = ((mode == :singular) ? res_name.singularize : res_name).split('.')
|
3481
|
+
res_parts.shift if ::Brick.apartment_multitenant && res_parts.length > 1 && res_parts.first == ::Brick.apartment_default_tenant
|
3482
|
+
if (aps = relation&.fetch(:auto_prefixed_schema, nil)) && res_parts.last.start_with?(aps)
|
3483
|
+
last_part = res_parts.last[aps.length..-1]
|
3484
|
+
aps = aps[0..-2] if aps[-1] == '_'
|
3485
|
+
res_parts[-1] = aps
|
3486
|
+
res_parts << last_part
|
3487
|
+
end
|
3488
|
+
path_prefix = []
|
3489
|
+
if ::Brick.config.path_prefix
|
3490
|
+
res_parts.unshift(::Brick.config.path_prefix)
|
3491
|
+
path_prefix << ::Brick.config.path_prefix
|
3492
|
+
end
|
3493
|
+
index = res_parts.map(&:underscore).join(separator)
|
3494
|
+
index = index.tr('_', 'x') if separator == 'x'
|
3495
|
+
# Rails applies an _index suffix to that route when the resource name isn't something plural
|
3496
|
+
index << '_index' if mode != :singular && separator == '_' &&
|
3497
|
+
index == (path_prefix + [name&.underscore&.tr('/', '_') || '_']).join(separator)
|
3498
|
+
index
|
3499
|
+
end
|
3500
|
+
|
3374
3501
|
def find_col_renaming(api_ver_path, relation_name)
|
3375
3502
|
::Brick.config.api_column_renaming&.fetch(
|
3376
3503
|
api_ver_path,
|
@@ -345,7 +345,8 @@ function linkSchemas() {
|
|
345
345
|
end
|
346
346
|
::Brick.relations.each do |k, v|
|
347
347
|
unless k.is_a?(Symbol) || existing.key?(class_name = v[:class_name]) || Brick.config.exclude_tables.include?(k) ||
|
348
|
-
class_name.blank? || class_name.include?('::')
|
348
|
+
class_name.blank? || class_name.include?('::') ||
|
349
|
+
['ActiveAdminComment', 'MotorAlert', 'MotorAlertLock', 'MotorApiConfig', 'MotorAudit', 'MotorConfig', 'MotorDashboard', 'MotorForm', 'MotorNote', 'MotorNoteTag', 'MotorNoteTagTag', 'MotorNotification', 'MotorQuery', 'MotorReminder', 'MotorResource', 'MotorTag', 'MotorTaggableTag'].include?(class_name)
|
349
350
|
Object.const_get("#{class_name}Resource")
|
350
351
|
end
|
351
352
|
end
|
@@ -774,9 +775,18 @@ window.addEventListener(\"popstate\", linkSchemas);
|
|
774
775
|
end
|
775
776
|
# %%% If we are not auto-creating controllers (or routes) then omit by default, and if enabled anyway, such as in a development
|
776
777
|
# environment or whatever, then get either the controllers or routes list instead
|
777
|
-
|
778
|
-
|
779
|
-
|
778
|
+
table_rels = if ::Brick.config.omit_empty_tables_in_dropdown
|
779
|
+
::Brick.relations.reject { |k, v| k.is_a?(Symbol) || v[:rowcount] == 0 }
|
780
|
+
else
|
781
|
+
::Brick.relations
|
782
|
+
end
|
783
|
+
table_options = table_rels.sort do |a, b|
|
784
|
+
a[0] = '' if a[0].is_a?(Symbol)
|
785
|
+
b[0] = '' if b[0].is_a?(Symbol)
|
786
|
+
a.first <=> b.first
|
787
|
+
end.each_with_object(+'') do |rel, s|
|
788
|
+
next if rel.first.blank? || rel.last[:cols].empty? ||
|
789
|
+
::Brick.config.exclude_tables.include?(rel.first)
|
780
790
|
|
781
791
|
tbl_parts = rel.first.split('.')
|
782
792
|
if (aps = rel.last.fetch(:auto_prefixed_schema, nil))
|
@@ -784,16 +794,16 @@ window.addEventListener(\"popstate\", linkSchemas);
|
|
784
794
|
aps = aps[0..-2] if aps[-1] == '_'
|
785
795
|
tbl_parts[-2] = aps
|
786
796
|
end
|
787
|
-
if tbl_parts.first == apartment_default_schema
|
788
|
-
tbl_parts.shift
|
789
|
-
end
|
797
|
+
tbl_parts.shift if tbl_parts.first == apartment_default_schema
|
790
798
|
# %%% When table_name_prefixes are use then during rendering empty non-TNP
|
791
799
|
# entries get added at some point when an attempt is made to find the table.
|
792
800
|
# Will have to hunt that down at some point.
|
793
|
-
|
794
|
-
|
795
|
-
|
801
|
+
if (rowcount = rel.last.fetch(:rowcount, nil))
|
802
|
+
rowcount = rowcount > 0 ? " (#{rowcount})" : nil
|
803
|
+
end
|
804
|
+
s << "<option value=\"#{::Brick._brick_index(rel.first, nil, '/')}\">#{rel.first}#{rowcount}</option>"
|
796
805
|
end.html_safe
|
806
|
+
prefix = "#{::Brick.config.path_prefix}/" if ::Brick.config.path_prefix
|
797
807
|
table_options << "<option value=\"#{prefix}brick_status\">(Status)</option>".html_safe if ::Brick.config.add_status
|
798
808
|
table_options << "<option value=\"#{prefix}brick_orphans\">(Orphans)</option>".html_safe if is_orphans
|
799
809
|
table_options << "<option value=\"#{prefix}brick_crosstab\">(Crosstab)</option>".html_safe if is_crosstab
|
@@ -1476,7 +1486,7 @@ end
|
|
1476
1486
|
%>
|
1477
1487
|
<tr>
|
1478
1488
|
<td><%= begin
|
1479
|
-
kls = Object.const_get(::Brick.relations.fetch(r[0], nil)&.fetch(:class_name, nil))
|
1489
|
+
kls = Object.const_get((rel = ::Brick.relations.fetch(r[0], nil))&.fetch(:class_name, nil))
|
1480
1490
|
rescue
|
1481
1491
|
end
|
1482
1492
|
if kls.is_a?(Class) && (path_helper = respond_to?(bi_path = \"#\{kls._brick_index}_path\".to_sym) ? bi_path : nil)
|
@@ -1489,7 +1499,10 @@ end
|
|
1489
1499
|
else
|
1490
1500
|
' class=\"dimmed\"'
|
1491
1501
|
end&.html_safe %>><%= # Table
|
1492
|
-
|
1502
|
+
if (rowcount = rel&.fetch(:rowcount, nil))
|
1503
|
+
rowcount = (rowcount > 0 ? \" (#\{rowcount})\" : nil)
|
1504
|
+
end
|
1505
|
+
\"#\{r[1]}#\{rowcount}\" %></td>
|
1493
1506
|
<td<%= lines = r[2]&.map { |line| \"#\{line.first}:#\{line.last}\" }
|
1494
1507
|
' class=\"dimmed\"'.html_safe unless r[2] %>><%= # Migration
|
1495
1508
|
lines&.join('<br>')&.html_safe %></td>
|
@@ -1571,7 +1584,13 @@ end %>#{"
|
|
1571
1584
|
#{schema_options}" if schema_options}
|
1572
1585
|
<select id=\"tbl\">#{table_options}</select>
|
1573
1586
|
<table id=\"resourceName\"><td><h1><%= page_title %></h1></td>
|
1574
|
-
<%
|
1587
|
+
<% rel = Brick.relations[#{model_name}.table_name]
|
1588
|
+
if (in_app = rel.fetch(:existing, nil)&.fetch(:show, nil))
|
1589
|
+
in_app = send(\"#\{in_app}_path\", #{pk.is_a?(String) ? "obj.#{pk}" : '[' + pk.map { |pk_part| "obj.#{pk_part}" }.join(', ') + ']' }) if in_app.is_a?(Symbol) %>
|
1590
|
+
<td><%= link_to(::Brick::Rails::IN_APP.html_safe, in_app) %></td>
|
1591
|
+
<% end
|
1592
|
+
|
1593
|
+
if Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) %>
|
1575
1594
|
<td><%= link_to_brick(
|
1576
1595
|
::Brick::Rails::AVO_SVG.html_safe,
|
1577
1596
|
{ show_proc: Proc.new do |obj, relation|
|
@@ -1596,7 +1615,7 @@ end %>#{"
|
|
1596
1615
|
end %>
|
1597
1616
|
</table>
|
1598
1617
|
<%
|
1599
|
-
if (description =
|
1618
|
+
if (description = rel&.fetch(:description, nil)) %>
|
1600
1619
|
<span class=\"__brick\"><%= description %></span><br><%
|
1601
1620
|
end
|
1602
1621
|
%><%= link_to \"(See all #\{model_name.pluralize})\", see_all_path, { class: '__brick' } %>
|
@@ -1927,6 +1946,7 @@ document.querySelectorAll(\"input, select\").forEach(function (inp) {
|
|
1927
1946
|
end
|
1928
1947
|
|
1929
1948
|
if ::Brick.enable_routes?
|
1949
|
+
require 'brick/route_mapper'
|
1930
1950
|
ActionDispatch::Routing::RouteSet.class_exec do
|
1931
1951
|
# In order to defer auto-creation of any routes that already exist, calculate Brick routes only after having loaded all others
|
1932
1952
|
prepend ::Brick::RouteSet
|
@@ -148,7 +148,8 @@ module Brick::Rails::FormBuilder
|
|
148
148
|
'(hidden)'
|
149
149
|
else
|
150
150
|
if val.is_a?(String)
|
151
|
-
val = val.dup.force_encoding('UTF-8').
|
151
|
+
return ::Brick::Rails.display_binary(val) unless (val_utf8 = val.dup.force_encoding('UTF-8')).valid_encoding?
|
152
|
+
val = val_utf8.strip
|
152
153
|
return CGI.escapeHTML(val) if is_xml
|
153
154
|
|
154
155
|
if val.length > max_len
|
@@ -2,7 +2,7 @@ module Brick::Rails::FormTags
|
|
2
2
|
# Our super speedy grid
|
3
3
|
def brick_grid(relation = nil, sequence = nil, inclusions = nil, exclusions = nil,
|
4
4
|
cols = {}, bt_descrip: nil, poly_cols: nil, bts: {}, hms_keys: [], hms_cols: {},
|
5
|
-
show_header: nil, show_row_count: nil, show_erd_button: nil, show_new_button: nil, show_avo_button: nil, show_aa_button: nil)
|
5
|
+
show_header: nil, show_row_count: nil, show_erd_button: nil, show_in_app_button: nil, show_new_button: nil, show_avo_button: nil, show_aa_button: nil)
|
6
6
|
# When a relation is not provided, first see if one exists which matches the controller name
|
7
7
|
unless (relation ||= instance_variable_get("@#{controller_name}".to_sym))
|
8
8
|
# Failing that, dig through the instance variables with hopes to find something that is an ActiveRecord::Relation
|
@@ -33,6 +33,7 @@ module Brick::Rails::FormTags
|
|
33
33
|
out = +"<div id=\"headerTopContainer\"><table id=\"headerTop\"></table>
|
34
34
|
"
|
35
35
|
klass = relation.klass
|
36
|
+
rel = ::Brick.relations&.fetch(relation.table_name, nil)
|
36
37
|
unless show_header == false
|
37
38
|
out << " <div id=\"headerTopAddNew\">
|
38
39
|
<div id=\"headerButtonBox\">
|
@@ -43,6 +44,11 @@ module Brick::Rails::FormTags
|
|
43
44
|
end
|
44
45
|
unless show_erd_button == false
|
45
46
|
out << " <div id=\"imgErd\" title=\"Show ERD\"></div>
|
47
|
+
"
|
48
|
+
end
|
49
|
+
if rel && show_in_app_button != false && (in_app = rel.fetch(:existing, nil)&.fetch(:index, nil))
|
50
|
+
in_app = send("#{in_app}_path") if in_app.is_a?(Symbol)
|
51
|
+
out << " <td title=\"Show in app\">#{link_to(::Brick::Rails::IN_APP.html_safe, in_app)}</td>
|
46
52
|
"
|
47
53
|
end
|
48
54
|
if show_avo_button != false && Object.const_defined?('Avo') && ::Avo.respond_to?(:railtie_namespace) && klass.name.exclude?('::')
|
@@ -151,7 +157,7 @@ module Brick::Rails::FormTags
|
|
151
157
|
# ActiveRecord::StatementTimeout in Warehouse::ColdRoomTemperatures_Archive#index
|
152
158
|
# TinyTds::Error: Adaptive Server connection timed out
|
153
159
|
# (After restarting the server it worked fine again.)
|
154
|
-
|
160
|
+
row_count = 0
|
155
161
|
relation.each do |obj|
|
156
162
|
out << "<tr>\n"
|
157
163
|
out << "<td class=\"col-sticky\">#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
|
@@ -252,13 +258,16 @@ module Brick::Rails::FormTags
|
|
252
258
|
out << '</td>'
|
253
259
|
end
|
254
260
|
out << '</tr>'
|
255
|
-
|
261
|
+
row_count += 1
|
262
|
+
end
|
263
|
+
if rel && (total_row_count = rel.fetch(:rowcount, nil))
|
264
|
+
total_row_count = total_row_count > row_count ? " (out of #{total_row_count})" : nil
|
256
265
|
end
|
257
266
|
out << " </tbody>
|
258
267
|
</table>
|
259
268
|
<script>
|
260
269
|
var rowCount = document.getElementById(\"rowCount\");
|
261
|
-
if (rowCount) rowCount.innerHTML = \"#{pluralize(
|
270
|
+
if (rowCount) rowCount.innerHTML = \"#{pluralize(row_count, "row")}#{total_row_count} \";
|
262
271
|
</script>
|
263
272
|
"
|
264
273
|
|