brick 1.0.50 → 1.0.53

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: d5f5d009df0d4a7ce06a5dec8986d2664671e8b151f7493e5ce3f8f690ec7028
4
- data.tar.gz: 974e86906775728e0ff0c7022cfbd87578b7c3013a8f9a5fe0db2243dcad9bf1
3
+ metadata.gz: 737256fad5c987fa2ac603cf3708da959127a33ab9b27d164ceff858b71987b9
4
+ data.tar.gz: 144352db7686d19d9c330887069287da88de456276cc17aaeeaeb3c1de7a3281
5
5
  SHA512:
6
- metadata.gz: 0f652cf44f9d18eca35304d1660ed3967d39c60dff42bcc05043607fada17d427308b7052a4b474c71d8a65462f86970838771471b6f1dd7a89accabc31ddd27
7
- data.tar.gz: 386030a8bd3c603264ed9f534e1188eb5710fe67777ffc9634a9c318e8bac4153d2716bffbf9c34a7ff63999c3af39b2fa6db3b324abc5093467a33b144b41b8
6
+ metadata.gz: 83ec370e06c18bf9d62ec3ed445daf78dc5cbff825a85ff017cc7b814efb62f86e68022f7577734dd01ddf7e2f74b4b59de4b40b9c07656e83d0859ba6a4333d
7
+ data.tar.gz: 9106373ca7878dcc75c596f1fa44d420980c50cb09f5b63a1110d4e02dc63f02dafe3d1bc0d4de38a75d5678d964e3b8f12a720a722d2f593934e2f6be97f818
@@ -547,12 +547,23 @@ JOIN (SELECT #{selects.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}
547
547
  this_module.const_set(module_name.to_sym, Module.new)
548
548
  end
549
549
  end
550
- if this_module.const_defined?(class_name = module_prefixes.last.to_sym)
551
- this_module.const_get(class_name)
552
- else
553
- # Build STI subclass and place it into the namespace module
554
- this_module.const_set(class_name, klass = Class.new(self))
555
- klass
550
+ begin
551
+ if this_module.const_defined?(class_name = module_prefixes.last.to_sym)
552
+ this_module.const_get(class_name)
553
+ else
554
+ # Build STI subclass and place it into the namespace module
555
+ this_module.const_set(class_name, klass = Class.new(self))
556
+ klass
557
+ end
558
+ rescue NameError => err
559
+ if column_names.include?(inheritance_column)
560
+ puts "Table #{table_name} has column #{inheritance_column} which ActiveRecord expects to use as its special inheritance column."
561
+ puts "Unfortunately the value \"#{type_name}\" does not seem to refer to a valid type name, greatly confusing matters. If that column is intended to be used for data and not STI, consider putting this line into your Brick initializer so that only for this table that column will not clash with ActiveRecord:"
562
+ puts " Brick.sti_type_column = { 'rails_#{inheritance_column}' => ['#{table_name}'] }"
563
+ self
564
+ else
565
+ raise
566
+ end
556
567
  end
557
568
  end
558
569
  end
@@ -603,6 +614,8 @@ Module.class_exec do
603
614
  return possible
604
615
  end
605
616
  class_name = args.first.to_s
617
+ # self.name is nil when a model name is requested in an .erb file
618
+ base_module = (self < ActiveRecord::Migration || !self.name) ? Object : self
606
619
  # See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
607
620
  # checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
608
621
  # that is, checking #qualified_name_for with: from_mod, const_name
@@ -610,14 +623,17 @@ Module.class_exec do
610
623
  # path_suffix = ActiveSupport::Dependencies.qualified_name_for(Object, args.first).underscore
611
624
  # return self._brick_const_missing(*args) if ActiveSupport::Dependencies.search_for_file(path_suffix)
612
625
  # If the file really exists, go and snag it:
613
- if !(is_found = ActiveSupport::Dependencies.search_for_file(class_name.underscore)) && (filepath = (self.name || class_name)&.split('::'))
614
- filepath = (filepath[0..-2] + [class_name]).join('/').underscore + '.rb'
615
- end
616
- if is_found
617
- return self._brick_const_missing(*args)
618
- # elsif ActiveSupport::Dependencies.search_for_file(filepath) # Last-ditch effort to pick this thing up before we fill in the gaps on our own
619
- # my_const = parent.const_missing(class_name) # ends up having: MyModule::MyClass
620
- # return my_const
626
+ if ActiveSupport::Dependencies.search_for_file(class_name.underscore)
627
+ return base_module._brick_const_missing(*args)
628
+ # elsif ActiveSupport::Dependencies.search_for_file(filepath) # Last-ditch effort to pick this thing up before we fill in the gaps on our own
629
+ # my_const = parent.const_missing(class_name) # ends up having: MyModule::MyClass
630
+ # return my_const
631
+ else
632
+ filepath = base_module.name&.split('::')&.[](0..-2) unless base_module == Object
633
+ filepath = ((filepath || []) + [class_name]).join('/').underscore + '.rb'
634
+ if ActiveSupport::Dependencies.search_for_file(filepath) # Last-ditch effort to pick this thing up before we fill in the gaps on our own
635
+ return base_module._brick_const_missing(*args)
636
+ end
621
637
  end
622
638
 
623
639
  relations = ::Brick.relations
@@ -636,7 +652,7 @@ Module.class_exec do
636
652
  Object.send(:build_controller, self, class_name, plural_class_name, model, relations)
637
653
  end
638
654
  elsif (::Brick.enable_models? || ::Brick.enable_controllers?) && # Schema match?
639
- self == Object && # %%% This works for Person::Person -- but also limits us to not being able to allow more than one level of namespacing
655
+ base_module == Object && # %%% This works for Person::Person -- but also limits us to not being able to allow more than one level of namespacing
640
656
  (schema_name = [(singular_table_name = class_name.underscore),
641
657
  (table_name = singular_table_name.pluralize),
642
658
  class_name,
@@ -644,7 +660,7 @@ Module.class_exec do
644
660
  (::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}::") && class_name))
645
661
  # Build out a module for the schema if it's namespaced
646
662
  # schema_name = schema_name.camelize
647
- self.const_set(schema_name.to_sym, (built_module = Module.new))
663
+ base_module.const_set(schema_name.to_sym, (built_module = Module.new))
648
664
 
649
665
  [built_module, "module #{schema_name}; end\n"]
650
666
  # # %%% Perhaps an option to use the first module just as schema, and additional modules as namespace with a table name prefix applied
@@ -654,8 +670,8 @@ Module.class_exec do
654
670
  # See if a file is there in the same way that ActiveSupport::Dependencies#load_missing_constant
655
671
  # checks for it in ~/.rvm/gems/ruby-2.7.5/gems/activesupport-5.2.6.2/lib/active_support/dependencies.rb
656
672
 
657
- if (base_model = ::Brick.config.sti_namespace_prefixes&.fetch("::#{self.name}::", nil)&.constantize) || # Are we part of an auto-STI namespace? ...
658
- self != Object # ... or otherwise already in some namespace?
673
+ if (base_model = ::Brick.config.sti_namespace_prefixes&.fetch("::#{base_module.name}::", nil)&.constantize) || # Are we part of an auto-STI namespace? ...
674
+ base_module != Object # ... or otherwise already in some namespace?
659
675
  schema_name = [(singular_schema_name = name.underscore),
660
676
  (schema_name = singular_schema_name.pluralize),
661
677
  name,
@@ -668,7 +684,7 @@ Module.class_exec do
668
684
  if base_model
669
685
  schema_name = name.underscore # For the auto-STI namespace models
670
686
  table_name = base_model.table_name
671
- Object.send(:build_model, self, inheritable_name, model_name, singular_table_name, table_name, relations, table_name)
687
+ Object.send(:build_model, base_module, inheritable_name, model_name, singular_table_name, table_name, relations, table_name)
672
688
  else
673
689
  # Adjust for STI if we know of a base model for the requested model name
674
690
  # %%% Does not yet work with namespaced model names. Perhaps prefix with plural_class_name when doing the lookups here.
@@ -693,15 +709,15 @@ Module.class_exec do
693
709
  built_class
694
710
  elsif ::Brick.config.sti_namespace_prefixes&.key?("::#{class_name}") && !schema_name
695
711
  # module_prefixes = type_name.split('::')
696
- # path = self.name.split('::')[0..-2] + []
712
+ # path = base_module.name.split('::')[0..-2] + []
697
713
  # module_prefixes.unshift('') unless module_prefixes.first.blank?
698
714
  # candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb')
699
- self._brick_const_missing(*args)
700
- # elsif self != Object
715
+ base_module._brick_const_missing(*args)
716
+ # elsif base_module != Object
701
717
  # module_parent.const_missing(*args)
702
718
  else
703
- puts "MISSING! #{self.name} #{args.inspect} #{table_name}"
704
- self._brick_const_missing(*args)
719
+ puts "MISSING! #{base_module.name} #{args.inspect} #{table_name}"
720
+ base_module._brick_const_missing(*args)
705
721
  end
706
722
  end
707
723
  end
@@ -842,15 +858,15 @@ class Object
842
858
  "#{hmt_fk}_through_#{hm.first[:assoc_name]}"
843
859
  else # Use BT names to provide uniqueness
844
860
  if self.name.underscore.singularize == hm.first[:alternate_name]
845
- # Has previously been:
846
- # # If it folds back on itself then look at the other side
847
- # # (At this point just infer the source be the inverse of the first has_many that
848
- # # we find that is not ourselves. If there are more than two then uh oh, can't
849
- # # yet handle that rare circumstance!)
850
- # other = hms.find { |hm1| hm1 != hm } # .first[:fk]
851
- # options[:source] = other.first[:inverse][:assoc_name].to_sym
852
- # And also has been:
853
- # hm.first[:inverse][:assoc_name].to_sym
861
+ # Has previously been:
862
+ # # If it folds back on itself then look at the other side
863
+ # # (At this point just infer the source be the inverse of the first has_many that
864
+ # # we find that is not ourselves. If there are more than two then uh oh, can't
865
+ # # yet handle that rare circumstance!)
866
+ # other = hms.find { |hm1| hm1 != hm } # .first[:fk]
867
+ # options[:source] = other.first[:inverse][:assoc_name].to_sym
868
+ # And also has been:
869
+ # hm.first[:inverse][:assoc_name].to_sym
854
870
  options[:source] = hm.last.to_sym
855
871
  else
856
872
  through = hm.first[:alternate_name].pluralize
@@ -1100,6 +1116,11 @@ class Object
1100
1116
  if namespace && (idx = lookup_context.prefixes.index(table_name))
1101
1117
  lookup_context.prefixes[idx] = "#{namespace.name.underscore}/#{lookup_context.prefixes[idx]}"
1102
1118
  end
1119
+ @_brick_excl = session[:_brick_exclude]&.split(',')&.each_with_object([]) do |excl, s|
1120
+ if (excl_parts = excl.split('.')).first == table_name
1121
+ s << excl_parts.last
1122
+ end
1123
+ end
1103
1124
  @_brick_bt_descrip = model._br_bt_descrip
1104
1125
  @_brick_hm_counts = model._br_hm_counts
1105
1126
  @_brick_join_array = join_array
@@ -1108,9 +1129,7 @@ class Object
1108
1129
  is_pk_string = nil
1109
1130
  if (pk_col = model&.primary_key)
1110
1131
  code << " def show\n"
1111
- code << (find_by_id = " id = params[:id]&.split(/[\\/,_]/)
1112
- id = id.first if id.is_a?(Array) && id.length == 1
1113
- @#{singular_table_name} = #{model.name}.find(id)\n")
1132
+ code << " #{find_by_name = "find_#{singular_table_name}"}\n"
1114
1133
  code << " end\n"
1115
1134
  self.define_method :show do
1116
1135
  ::Brick.set_db_schema(params)
@@ -1121,25 +1140,59 @@ class Object
1121
1140
  params[:id]&.split(/[\/,_]/)
1122
1141
  end
1123
1142
  id = id.first if id.is_a?(Array) && id.length == 1
1124
- instance_variable_set("@#{singular_table_name}".to_sym, model.find(id))
1143
+ instance_variable_set("@#{singular_table_name}".to_sym, find_obj)
1125
1144
  end
1126
1145
  end
1127
1146
 
1128
1147
  # By default, views get marked as read-only
1129
- unless is_swagger # model.readonly # (relation = relations[model.table_name]).key?(:isView)
1130
- code << " # (Define :new, :create)\n"
1148
+ # unless model.readonly # (relation = relations[model.table_name]).key?(:isView)
1149
+ code << " def new\n"
1150
+ code << " @#{singular_table_name} = #{model.name}.new\n"
1151
+ code << " end\n"
1152
+ self.define_method :new do
1153
+ ::Brick.set_db_schema(params)
1154
+ instance_variable_set("@#{singular_table_name}".to_sym, model.new)
1155
+ end
1156
+
1157
+ params_name_sym = (params_name = "#{singular_table_name}_params").to_sym
1158
+
1159
+ code << " def create\n"
1160
+ code << " @#{singular_table_name} = #{model.name}.create(#{params_name})\n"
1161
+ code << " end\n"
1162
+ self.define_method :create do
1163
+ ::Brick.set_db_schema(params)
1164
+ if (is_json = request.content_type == 'application/json') && (col = params['_brick_exclude'])
1165
+ session[:_brick_exclude] = ((session[:_brick_exclude]&.split(',') || []) + ["#{table_name}.#{col}"]).join(',')
1166
+ render json: { result: ::Brick.exclude_column(table_name, col) }
1167
+ elsif is_json && (col = params['_brick_unexclude'])
1168
+ if (excls = ((session[:_brick_exclude]&.split(',') || []) - ["#{table_name}.#{col}"]).join(',')).empty?
1169
+ session.delete(:_brick_exclude)
1170
+ else
1171
+ session[:_brick_exclude] = excls
1172
+ end
1173
+ render json: { result: ::Brick.unexclude_column(table_name, col) }
1174
+ else
1175
+ instance_variable_set("@#{singular_table_name}".to_sym,
1176
+ model.send(:create, send(params_name_sym)))
1177
+ end
1178
+ end
1131
1179
 
1132
- if model.primary_key
1180
+ if pk_col
1133
1181
  # if (schema = ::Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil)) && ::Brick.db_schemas&.include?(schema)
1134
1182
  # ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?;", schema)
1135
1183
  # end
1136
1184
 
1137
1185
  is_need_params = true
1138
- # code << " # (Define :edit, and :destroy)\n"
1186
+ code << " def edit\n"
1187
+ code << " #{find_by_name}\n"
1188
+ code << " end\n"
1189
+ self.define_method :edit do
1190
+ ::Brick.set_db_schema(params)
1191
+ instance_variable_set("@#{singular_table_name}".to_sym, find_obj)
1192
+ end
1193
+
1139
1194
  code << " def update\n"
1140
- code << find_by_id
1141
- params_name = "#{singular_table_name}_params"
1142
- code << " @#{singular_table_name}.update(#{params_name})\n"
1195
+ code << " #{find_by_name}.update(#{params_name})\n"
1143
1196
  code << " end\n"
1144
1197
  self.define_method :update do
1145
1198
  ::Brick.set_db_schema(params)
@@ -1158,27 +1211,50 @@ class Object
1158
1211
  # return
1159
1212
  end
1160
1213
 
1214
+ instance_variable_set("@#{singular_table_name}".to_sym, (obj = find_obj))
1215
+ obj.send(:update, send(params_name_sym))
1216
+ end
1217
+
1218
+ code << " def destroy\n"
1219
+ code << " #{find_by_name}.destroy\n"
1220
+ code << " end\n"
1221
+ self.define_method :destroy do
1222
+ ::Brick.set_db_schema(params)
1223
+ instance_variable_set("@#{singular_table_name}".to_sym, find_obj.send(:destroy))
1224
+ end
1225
+ end
1226
+
1227
+ code << "private\n" if pk_col || is_need_params
1228
+
1229
+ if pk_col
1230
+ code << " def find_#{singular_table_name}
1231
+ id = params[:id]&.split(/[\\/,_]/)
1232
+ @#{singular_table_name} = #{model.name}.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
1233
+ end\n"
1234
+ self.define_method :find_obj do
1161
1235
  id = is_pk_string ? params[:id] : params[:id]&.split(/[\/,_]/)
1162
- id = id.first if id.is_a?(Array) && id.length == 1
1163
- instance_variable_set("@#{singular_table_name}".to_sym, (obj = model.find(id)))
1164
- obj = obj.first if obj.is_a?(Array)
1165
- obj.send(:update, send(params_name = params_name.to_sym))
1236
+ model.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
1166
1237
  end
1167
1238
  end
1168
1239
 
1169
1240
  if is_need_params
1170
- code << "private\n"
1171
1241
  code << " def #{params_name}\n"
1242
+ permits = model.columns_hash.keys.map(&:to_sym)
1243
+ permits_txt = permits.map(&:inspect) +
1244
+ model.reflect_on_all_associations.select { |assoc| assoc.macro == :has_many && assoc.options[:through] }.map do |assoc|
1245
+ permits << { "#{assoc.name.to_s.singularize}_ids".to_sym => [] }
1246
+ "#{assoc.name.to_s.singularize}_ids: []"
1247
+ end
1172
1248
  code << " params.require(:#{require_name = model.name.underscore.tr('/', '_')
1173
- }).permit(#{model.columns_hash.keys.map { |c| c.to_sym.inspect }.join(', ')})\n"
1249
+ }).permit(#{permits_txt.join(', ')})\n"
1174
1250
  code << " end\n"
1175
1251
  self.define_method(params_name) do
1176
- params.require(require_name.to_sym).permit(model.columns_hash.keys)
1252
+ params.require(require_name.to_sym).permit(permits)
1177
1253
  end
1178
1254
  private params_name
1179
1255
  # Get column names for params from relations[model.table_name][:cols].keys
1180
1256
  end
1181
- end
1257
+ # end
1182
1258
  code << "end # #{namespace_name}#{class_name}\n\n"
1183
1259
  end # class definition
1184
1260
  [built_controller, code]
@@ -58,7 +58,9 @@ module Brick
58
58
  def template_exists?(*args, **options)
59
59
  (::Brick.config.add_orphans && args.first == 'orphans') ||
60
60
  _brick_template_exists?(*args, **options) ||
61
- set_brick_model(args)
61
+ # Do not auto-create a template when it's searching for an application.html.erb, which comes in like: ["edit", ["games", "application"]]
62
+ ((args[1].length == 1 || args[1][-1] != 'application') &&
63
+ set_brick_model(args))
62
64
  end
63
65
 
64
66
  def set_brick_model(find_args)
@@ -130,27 +132,24 @@ module Brick
130
132
  end
131
133
  case args.first
132
134
  when 'index'
133
- hms_columns << if hm_assoc.macro == :has_many
135
+ hm_entry = +"'#{hm_assoc.name}' => [#{assoc_name.inspect}"
136
+ hm_entry << if hm_assoc.macro == :has_many
137
+ if hm_fk_name # %%% Can remove this check when multiple foreign keys to same destination becomes bulletproof
134
138
  set_ct = if skip_klass_hms.key?(assoc_name.to_sym)
135
- 'nil'
136
- else
137
- # Postgres column names are limited to 63 characters
138
- attrib_name = "_br_#{assoc_name}_ct"[0..62]
139
- "#{obj_name}.#{attrib_name} || 0"
140
- end
141
- if hm_fk_name
142
- "<%= ct = #{set_ct}
143
- link_to \"#\{ct || 'View'\} #{assoc_name}\", #{hm_assoc.klass.name.underscore.tr('/', '_').pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, obj_name, pk)} }) unless ct&.zero? %>\n"
144
- else # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
145
- "#{assoc_name}\n"
146
- end
147
- else # has_one
148
- # 0..62 because Postgres column names are limited to 63 characters
149
- "<%= descrips = @_brick_bt_descrip[#{hm.first.inspect}][ho_class = #{hm[1].klass.name}]
150
- ho_txt = ho_class.brick_descrip(#{obj_name}, descrips[0..-2].map { |id| #{obj_name}.send(id.last[0..62]) }, (ho_id_col = descrips.last))
151
- ho_id = ho_id_col.map { |id_col| #{obj_name}.send(id_col.to_sym) }
152
- ho_id&.first ? link_to(ho_txt, send(\"#\{ho_class.base_class.name.underscore.tr('/', '_')\}_path\".to_sym, ho_id)) : ho_txt %>\n"
139
+ 'nil'
140
+ else
141
+ # Postgres column names are limited to 63 characters
142
+ "#{obj_name}.#{"_br_#{assoc_name}_ct"[0..62]} || 0"
143
+ end
144
+ ", #{set_ct}, #{path_keys(hm_assoc, hm_fk_name, obj_name, pk)}"
153
145
  end
146
+ else # has_one
147
+ # 0..62 because Postgres column names are limited to 63 characters
148
+ ", nil, #{path_keys(hm_assoc, hm_fk_name, obj_name, pk)}"
149
+ end
150
+ hm_entry << ']'
151
+ puts hm_entry
152
+ hms_columns << hm_entry
154
153
  when 'show', 'update'
155
154
  hm_stuff << if hm_fk_name
156
155
  "<%= link_to '#{assoc_name}', #{hm_assoc.klass.name.underscore.tr('/', '_').pluralize}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
@@ -176,6 +175,9 @@ module Brick
176
175
  end.html_safe
177
176
  table_options << '<option value="brick_orphans">(Orphans)</option>'.html_safe if is_orphans
178
177
  css = +"<style>
178
+ h1, h3 {
179
+ margin-bottom: 0;
180
+ }
179
181
  #dropper {
180
182
  background-color: #eee;
181
183
  }
@@ -197,19 +199,41 @@ table {
197
199
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
198
200
  }
199
201
 
200
- table thead tr th, table tr th {
202
+ tr th {
201
203
  background-color: #009879;
202
204
  color: #fff;
203
205
  text-align: left;
204
206
  }
205
- #headerTop th:hover, #headerTop th:hover {
207
+ #headerTop tr th {
208
+ position: relative;
209
+ }
210
+ #headerTop tr th .exclude {
211
+ position: absolute;
212
+ display: none;
213
+ top: 0;
214
+ right: 0;
215
+ }
216
+ #headerTop tr th:hover {
206
217
  background-color: #18B090;
207
218
  }
208
- table thead tr th a, table tr th a {
219
+ #exclusions {
220
+ font-size: 0.7em;
221
+ }
222
+ #exclusions div {
223
+ border: 1px solid blue;
224
+ display: inline-block;
225
+ cursor: copy;
226
+ }
227
+ #headerTop tr th:hover .exclude {
228
+ display: inline;
229
+ cursor: pointer;
230
+ color: red;
231
+ }
232
+ tr th a {
209
233
  color: #80FFB8;
210
234
  }
211
235
 
212
- table th, table td {
236
+ tr th, tr td {
213
237
  padding: 0.2em 0.5em;
214
238
  }
215
239
 
@@ -255,6 +279,7 @@ a.big-arrow {
255
279
  }
256
280
  .dimmed {
257
281
  background-color: #C0C0C0;
282
+ text-align: center;
258
283
  }
259
284
  .orphan {
260
285
  color: red;
@@ -283,8 +308,8 @@ input+svg.revert {
283
308
  color: #FFF;
284
309
  }
285
310
  </style>
286
- <% is_includes_dates = nil
287
311
 
312
+ <% is_includes_dates = nil
288
313
  def is_bcrypt?(val)
289
314
  val.is_a?(String) && val.length == 60 && val.start_with?('$2a$')
290
315
  end
@@ -416,8 +441,19 @@ function setHeaderSizes() {
416
441
  for (var i = 0; i < row.childNodes.length; ++i) {
417
442
  node = row.childNodes[i];
418
443
  if (node.nodeType === 1) {
419
- var style = tr.childNodes[i].style;
420
- style.minWidth = style.maxWidth = getComputedStyle(node).width;
444
+ var th = tr.childNodes[i];
445
+ th.style.minWidth = th.style.maxWidth = getComputedStyle(node).width;
446
+ if (#{pk&.present? ? 'i > 0' : 'true'}) {
447
+ // Add <span> at the end
448
+ var span = document.createElement(\"SPAN\");
449
+ span.className = \"exclude\";
450
+ span.innerHTML = \"X\";
451
+ span.addEventListener(\"click\", function (e) {
452
+ e.stopPropagation();
453
+ doFetch(\"POST\", {_brick_exclude: this.parentElement.getAttribute(\"x-order\")});
454
+ });
455
+ th.appendChild(span);
456
+ }
421
457
  }
422
458
  }
423
459
  if (isEmpty) headerTop.appendChild(tr);
@@ -425,6 +461,18 @@ function setHeaderSizes() {
425
461
  grid.style.marginTop = \"-\" + getComputedStyle(headerTop).height;
426
462
  // console.log(\"end\");
427
463
  }
464
+ function doFetch(method, payload, success) {
465
+ payload.authenticity_token = <%= session[:_csrf_token].inspect.html_safe %>;
466
+ if (!success) {
467
+ success = function (p) {p.text().then(function (response) {
468
+ var result = JSON.parse(response).result;
469
+ if (result) location.href = location.href;
470
+ });};
471
+ }
472
+ var options = {method: method, headers: {\"Content-Type\": \"application/json\"}};
473
+ if (payload) options.body = JSON.stringify(payload);
474
+ return fetch(location.href, options).then(success);
475
+ }
428
476
  if (headerTop) {
429
477
  setHeaderSizes();
430
478
  window.addEventListener('resize', function(event) {
@@ -459,8 +507,8 @@ if (headerTop) {
459
507
  });
460
508
  btnImport.addEventListener(\"click\", function () {
461
509
  fetch(changeout(<%= #{path_obj_name}_path(-1, format: :csv).inspect.html_safe %>, \"_brick_schema\", brickSchema), {
462
- method: 'PATCH',
463
- headers: { 'Content-Type': 'text/tab-separated-values' },
510
+ method: \"PATCH\",
511
+ headers: { \"Content-Type\": \"text/tab-separated-values\" },
464
512
  body: droppedTSV
465
513
  }).then(function (tsvResponse) {
466
514
  btnImport.style.display = \"none\";
@@ -538,6 +586,7 @@ if (headerTop) {
538
586
  if (description = (relation = Brick.relations[#{model_name}.table_name])&.fetch(:description, nil)) %><%=
539
587
  description %><br><%
540
588
  end
589
+ # FILTER PARAMETERS
541
590
  if @_brick_params&.present? %>
542
591
  <% if @_brick_params.length == 1 # %%% Does not yet work with composite keys
543
592
  k, id = @_brick_params.first
@@ -549,55 +598,78 @@ if (headerTop) {
549
598
  end
550
599
  end %>
551
600
  (<%= link_to 'See all #{model_plural.split('::').last}', #{path_obj_name.pluralize}_path %>)
601
+ <% end
602
+ # COLUMN EXCLUSIONS
603
+ if @_brick_excl&.present? %>
604
+ <div id=\"exclusions\">Excluded columns:
605
+ <% @_brick_excl.each do |excl| %>
606
+ <div class=\"colExclusion\"><%= excl %></div>
607
+ <% end %>
608
+ </div>
609
+ <script>
610
+ [... document.getElementsByClassName(\"colExclusion\")].forEach(function (excl) {
611
+ excl.addEventListener(\"click\", function () {
612
+ doFetch(\"POST\", {_brick_unexclude: this.innerHTML});
613
+ });
614
+ });
615
+ </script>
552
616
  <% end %>
553
- <br>
554
617
  <table id=\"headerTop\">
555
618
  <table id=\"#{table_name}\">
556
- <thead><tr>#{"<th x-order=\"#{pk.join(',')}\"></th>" if pk.present?}<%
557
- col_order = []
558
- @#{table_name}.columns.each do |col|
559
- next if (#{(pk || []).inspect}.include?(col_name = col.name) && col.type == :integer && !bts.key?(col_name)) ||
619
+ <thead><tr>#{"<th x-order=\"#{pk.join(',')}\"></th>" if pk.present?}<%=
620
+ # Consider getting the name from the association -- hm.first.name -- if a more \"friendly\" alias should be used for a screwy table name
621
+ cols = {#{hms_keys = []
622
+ hms_headers.map do |hm|
623
+ hms_keys << (assoc_name = (assoc = hm.first).name.to_s)
624
+ "#{assoc_name.inspect} => [#{(assoc.options[:through] && !assoc.through_reflection).inspect}, #{assoc.klass.name}, #{hm[1].inspect}, #{hm[2].inspect}]"
625
+ end.join(', ')}}
626
+ col_keys = @#{table_name}.columns.each_with_object([]) do |col, s|
627
+ col_name = col.name
628
+ next if @_brick_incl&.exclude?(col_name) ||
629
+ (#{(pk || []).inspect}.include?(col_name) && col.type == :integer && !bts.key?(col_name)) ||
560
630
  ::Brick.config.metadata_columns.include?(col_name) || poly_cols.include?(col_name)
561
631
 
562
- col_order << col_name
563
- %><th<%= \" title=\\\"#\{col.comment}\\\"\".html_safe if col.respond_to?(:comment) && !col.comment.blank? %><%
564
- if (bt = bts[col_name]) %>
565
- <%= \" x-order=\\\"#\{bt.first}\\\"\".html_safe unless bt[2] # Allow sorting any BT except polymorphics
566
- %>>BT <%
567
- bt[1].each do |bt_pair| %><%=
568
- bt_pair.first.bt_link(bt.first) %> <%
569
- end %><%
570
- else %><%= \" x-order=\\\"#\{col_name}\\\"\".html_safe if true # Currently we always allow click to sort
571
- %>><%= col_name %><%
572
- end
573
- %></th><%
632
+ s << col_name
633
+ cols[col_name] = col
574
634
  end
575
- # Consider getting the name from the association -- h.first.name -- if a more \"friendly\" alias should be used for a screwy table name
576
- %>#{hms_headers.map do |h|
577
- # Currently we always allow click to sort
578
- "<th#{" x-order=\"#{h.first.name}\"" if true}>" +
579
- if h.first.options[:through] && !h.first.through_reflection
580
- "#{h[1]} #{h[2]} %></th>" # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
581
- else
582
- "#{h[1]} <%= link_to('#{h[2]}', #{h.first.klass.name.underscore.tr('/', '_').pluralize}_path) %></th>"
583
- end
584
- end.join
585
- }</tr></thead>
586
-
635
+ unless @_brick_sequence # If no sequence is defined, start with all inclusions
636
+ @_brick_sequence = col_keys + #{(hms_keys).inspect}.reject { |assoc_name| @_brick_incl&.exclude?(assoc_name) }
637
+ end
638
+ @_brick_sequence.reject! { |nm| @_brick_excl.include?(nm) } if @_brick_excl # Reject exclusions
639
+ @_brick_sequence.each_with_object(+'') do |col_name, s|
640
+ if (col = cols[col_name]).is_a?(ActiveRecord::ConnectionAdapters::Column)
641
+ s << '<th'
642
+ s << \" title=\\\"#\{col.comment}\\\"\" if col.respond_to?(:comment) && !col.comment.blank?
643
+ s << if (bt = bts[col_name])
644
+ # Allow sorting for any BT except polymorphics
645
+ \"#\{' x-order=\"' + bt.first.to_s + '\"' unless bt[2]}>BT \" +
646
+ bt[1].map { |bt_pair| bt_pair.first.bt_link(bt.first) }.join(' ')
647
+ else # Normal column
648
+ \"#\{' x-order=\"' + col_name + '\"' if true}>#\{col_name}\"
649
+ end
650
+ elsif col # HM column
651
+ s << \"<th#\{' x-order=\"' + col_name + '\"' if true}>#\{col[2]} \"
652
+ s << (col.first ? \"#\{col[3]}\" : \"#\{link_to(col[3], send(\"#\{col[1].name.underscore.tr('/', '_').pluralize}_path\"))}\")
653
+ else # Bad column name!
654
+ s << \"<th title=\\\"<< Unknown column >>\\\">#\{col_name}\"
655
+ end
656
+ s << '</th>'
657
+ end.html_safe
658
+ %></tr></thead>
587
659
  <tbody>
588
- <% @#{table_name}.each do |#{obj_name}| %>
660
+ <% @#{table_name}.each do |#{obj_name}|
661
+ hms_cols = {#{hms_columns.join(', ')}} %>
589
662
  <tr>#{"
590
663
  <td><%= link_to '⇛', #{path_obj_name}_path(#{obj_pk}), { class: 'big-arrow' } %></td>" if obj_pk}
591
- <% col_order.each do |col_name|
664
+ <% @_brick_sequence.each do |col_name|
592
665
  val = #{obj_name}.attributes[col_name] %>
593
- <td>
594
- <% if (bt = bts[col_name]) %>
595
- <% if bt[2] # Polymorphic?
666
+ <td<%= ' class=\"dimmed\"'.html_safe unless cols.key?(col_name)%>><%
667
+ if (bt = bts[col_name])
668
+ if bt[2] # Polymorphic?
596
669
  bt_class = #{obj_name}.send(\"#\{bt.first\}_type\")
597
670
  base_class = (::Brick.existing_stis[bt_class] || bt_class).constantize.base_class.name.underscore
598
671
  poly_id = #{obj_name}.send(\"#\{bt.first\}_id\")
599
- %><%= link_to(\"#\{bt_class\} ##\{poly_id\}\",
600
- send(\"#\{base_class\}_path\".to_sym, poly_id)) if poly_id %><%
672
+ %><%= link_to(\"#\{bt_class\} ##\{poly_id\}\", send(\"#\{base_class\}_path\".to_sym, poly_id)) if poly_id %><%
601
673
  else
602
674
  bt_txt = (bt_class = bt[1].first.first).brick_descrip(
603
675
  # 0..62 because Postgres column names are limited to 63 characters
@@ -606,14 +678,29 @@ if (headerTop) {
606
678
  bt_txt ||= \"<span class=\\\"orphan\\\">&lt;&lt; Orphaned ID: #\{val} >></span>\".html_safe if val
607
679
  bt_id = bt_id_col.map { |id_col| #{obj_name}.send(id_col.to_sym) } %>
608
680
  <%= bt_id&.first ? link_to(bt_txt, send(\"#\{bt_class.base_class.name.underscore.tr('/', '_')\}_path\".to_sym, bt_id)) : bt_txt %>
609
- <%#= Previously was: bt_obj = bt[1].first.first.find_by(bt[2] => val); link_to(bt_obj.brick_descrip, send(\"#\{bt[1].first.first.name.underscore\}_path\".to_sym, bt_obj.send(bt[1].first.first.primary_key.to_sym))) if bt_obj %>
610
- <% end %>
611
- <% else %>
612
- <%= hide_bcrypt(val) %>
613
- <% end %>
614
- </td>
681
+ <% end
682
+ elsif (hms_col = hms_cols[col_name])
683
+ if hms_col.length == 1 %>
684
+ <%= hms_col.first %>
685
+ <% else
686
+ klass = (col = cols[col_name])[1]
687
+ txt = if col[2] == 'HO'
688
+ descrips = @_brick_bt_descrip[col_name.to_sym][klass]
689
+ ho_txt = klass.brick_descrip(#{obj_name}, descrips[0..-2].map { |id| #{obj_name}.send(id.last[0..62]) }, (ho_id_col = descrips.last))
690
+ ho_id = ho_id_col.map { |id_col| #{obj_name}.send(id_col.to_sym) }
691
+ ho_id&.first ? link_to(ho_txt, send(\"#\{klass.base_class.name.underscore.tr('/', '_')\}_path\".to_sym, ho_id)) : ho_txt
692
+ else
693
+ \"#\{hms_col[1] || 'View'\} #\{hms_col.first}\"
694
+ end %>
695
+ <%= link_to txt, send(\"#\{klass.name.underscore.tr('/', '_').pluralize}_path\".to_sym, hms_col[2]) unless hms_col[1]&.zero? %>
696
+ <% end
697
+ elsif cols.key?(col_name)
698
+ %><%= hide_bcrypt(val) %><%
699
+ else # Bad column name!
700
+ %>?<%
701
+ end
702
+ %></td>
615
703
  <% end %>
616
- #{hms_columns.each_with_object(+'') { |hm_col, s| s << "<td>#{hm_col}</td>" }}
617
704
  </tr>
618
705
  <% end %>
619
706
  </tbody>
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 50
8
+ TINY = 53
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
data/lib/brick.rb CHANGED
@@ -190,6 +190,15 @@ module Brick
190
190
  [bts, hms]
191
191
  end
192
192
 
193
+ def exclude_column(table, col)
194
+ puts "Excluding #{table}.#{col}"
195
+ true
196
+ end
197
+ def unexclude_column(table, col)
198
+ puts "Unexcluding #{table}.#{col}"
199
+ true
200
+ end
201
+
193
202
  # Switches Brick auto-models on or off, for all threads
194
203
  # @api public
195
204
  def enable_models=(value)
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.50
4
+ version: 1.0.53
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-07-28 00:00:00.000000000 Z
11
+ date: 2022-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord