brick 1.0.96 → 1.0.98

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.
@@ -0,0 +1,219 @@
1
+ module Brick::Rails::FormTags
2
+ # Our super speedy grid
3
+ def brick_grid(relation, bt_descrip, sequence = nil, inclusions, exclusions,
4
+ cols, poly_cols, bts, hms_keys, hms_cols)
5
+ out = "<table id=\"headerTop\"></table>
6
+ <table id=\"#{relation.table_name}\" class=\"shadow\">
7
+ <thead><tr>"
8
+ pk = (klass = relation.klass).primary_key || []
9
+ pk = [pk] unless pk.is_a?(Array)
10
+ if pk.present?
11
+ out << "<th x-order=\"#{pk.join(',')}\"></th>"
12
+ end
13
+
14
+ col_keys = relation.columns.each_with_object([]) do |col, s|
15
+ col_name = col.name
16
+ next if inclusions&.exclude?(col_name) ||
17
+ (pk.include?(col_name) && [:integer, :uuid].include?(col.type) && !bts.key?(col_name)) ||
18
+ ::Brick.config.metadata_columns.include?(col_name) || poly_cols.include?(col_name)
19
+
20
+ s << col_name
21
+ cols[col_name] = col
22
+ end
23
+ unless sequence # If no sequence is defined, start with all inclusions
24
+ cust_cols = klass._br_cust_cols
25
+ # HOT columns, kept as symbols
26
+ hots = klass._br_bt_descrip.keys.select { |k| bts.key?(k) }
27
+ sequence = col_keys + cust_cols.keys + hots + hms_keys.reject { |assoc_name| inclusions&.exclude?(assoc_name) }
28
+ end
29
+ sequence.reject! { |nm| exclusions.include?(nm) } if exclusions
30
+ out << sequence.each_with_object(+'') do |col_name, s|
31
+ if (col = cols[col_name]).is_a?(ActiveRecord::ConnectionAdapters::Column)
32
+ s << '<th'
33
+ s << " title=\"#{col.comment}\"" if col.respond_to?(:comment) && !col.comment.blank?
34
+ s << if (bt = bts[col_name])
35
+ # Allow sorting for any BT except polymorphics
36
+ "#{' x-order="' + bt.first.to_s + '"' unless bt[2]}>BT " +
37
+ bt[1].map { |bt_pair| bt_pair.first.bt_link(bt.first) }.join(' ')
38
+ else # Normal column
39
+ "#{' x-order="' + col_name + '"' if true}>#{col_name}"
40
+ end
41
+ elsif col # HM column
42
+ s << "<th#{' x-order="' + col_name + '"' if true}>#{col[2]} "
43
+ s << (col.first ? "#{col[3]}" : "#{link_to(col[3], send("#{col[1]._brick_index}_path"))}")
44
+ elsif cust_cols.key?(col_name) # Custom column
45
+ s << "<th x-order=\"#{col_name}\">#{col_name}"
46
+ elsif col_name.is_a?(Symbol) && (hot = bts[col_name]) # has_one :through
47
+ s << "<th x-order=\"#{hot.first.to_s}\">HOT " +
48
+ hot[1].map { |hot_pair| hot_pair.first.bt_link(col_name) }.join(' ')
49
+ hot[1].first
50
+ else # Bad column name!
51
+ s << "<th title=\"<< Unknown column >>\">#{col_name}"
52
+ end
53
+ s << '</th>'
54
+ end
55
+ out << "</tr></thead>
56
+ <tbody>"
57
+ # %%% Have once gotten this error with MSSQL referring to http://localhost:3000/warehouse/cold_room_temperatures__archive
58
+ # ActiveRecord::StatementTimeout in Warehouse::ColdRoomTemperatures_Archive#index
59
+ # TinyTds::Error: Adaptive Server connection timed out
60
+ # (After restarting the server it worked fine again.)
61
+ relation.each do |obj|
62
+ out << "<tr>\n"
63
+ out << "<td>#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
64
+ pk.map { |pk_part| obj.send(pk_part.to_sym) }), { class: 'big-arrow' })}</td>\n" if pk.present?
65
+ sequence.each do |col_name|
66
+ val = obj.attributes[col_name]
67
+ out << '<td'
68
+ out << ' class=\"dimmed\"' unless cols.key?(col_name) || (cust_col = cust_cols[col_name]) ||
69
+ (col_name.is_a?(Symbol) && bts.key?(col_name)) # HOT
70
+ out << '>'
71
+ if (bt = bts[col_name])
72
+ if bt[2] # Polymorphic?
73
+ bt_class = obj.send("#{bt.first}_type")
74
+ base_class_underscored = (::Brick.existing_stis[bt_class] || bt_class).constantize.base_class._brick_index(:singular)
75
+ poly_id = obj.send("#{bt.first}_id")
76
+ out << link_to("#{bt_class} ##{poly_id}", send("#{base_class_underscored}_path".to_sym, poly_id)) if poly_id
77
+ else # BT or HOT
78
+ bt_class = bt[1].first.first
79
+ descrips = bt_descrip[bt.first][bt_class]
80
+ bt_id_col = if descrips.nil?
81
+ puts "Caught it in the act for obj / #{col_name}!"
82
+ elsif descrips.length == 1
83
+ [obj.class.reflect_on_association(bt.first)&.foreign_key]
84
+ else
85
+ descrips.last
86
+ end
87
+ bt_txt = bt_class.brick_descrip(
88
+ # 0..62 because Postgres column names are limited to 63 characters
89
+ obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, bt_id_col
90
+ )
91
+ bt_txt ||= "<span class=\"orphan\">&lt;&lt; Orphaned ID: #{val} >></span>" if val
92
+ bt_id = bt_id_col&.map { |id_col| obj.respond_to?(id_sym = id_col.to_sym) ? obj.send(id_sym) : id_col }
93
+ out << (bt_id&.first ? link_to(bt_txt, send("#{bt_class.base_class._brick_index(:singular)}_path".to_sym, bt_id)) : bt_txt || '')
94
+ end
95
+ elsif (hms_col = hms_cols[col_name])
96
+ if hms_col.length == 1
97
+ out << hms_col.first
98
+ else
99
+ klass = (col = cols[col_name])[1]
100
+ if col[2] == 'HO'
101
+ descrips = bt_descrip[col_name.to_sym][klass]
102
+ if (ho_id = (ho_id_col = descrips.last).map { |id_col| obj.send(id_col.to_sym) })&.first
103
+ ho_txt = klass.brick_descrip(obj, descrips[0..-2].map { |id| obj.send(id.last[0..62]) }, ho_id_col)
104
+ out << link_to(ho_txt, send("#{klass.base_class._brick_index(:singular)}_path".to_sym, ho_id))
105
+ end
106
+ else
107
+ if (ct = obj.send(hms_col[1].to_sym)&.to_i)&.positive?
108
+ out << "#{link_to("#{ct || 'View'} #{hms_col.first}",
109
+ send("#{klass._brick_index}_path".to_sym,
110
+ hms_col[2].each_with_object({}) { |v, s| s[v.first] = v.last.is_a?(String) ? v.last : obj.send(v.last) })
111
+ )}\n"
112
+ end
113
+ end
114
+ end
115
+ elsif (col = cols[col_name]).is_a?(ActiveRecord::ConnectionAdapters::Column)
116
+ binding.pry if col.is_a?(Array)
117
+ col_type = col&.sql_type == 'geography' ? col.sql_type : col&.type
118
+ out << display_value(col_type || col&.sql_type, val).to_s
119
+ elsif cust_col
120
+ data = cust_col.first.map { |cc_part| obj.send(cc_part.last) }
121
+ cust_txt = klass.brick_descrip(cust_col[-2], data)
122
+ if (link_id = obj.send(cust_col.last[1]) if cust_col.last)
123
+ out << link_to(cust_txt, send("#{cust_col.last.first._brick_index(:singular)}_path", link_id))
124
+ else
125
+ out << cust_txt
126
+ end
127
+ else # Bad column name!
128
+ out << '?'
129
+ end
130
+ out << '</td>'
131
+ end
132
+ out << '</tr>'
133
+ end
134
+ out << " </tbody>
135
+ </table>
136
+ "
137
+ out.html_safe
138
+ end # brick_grid
139
+
140
+ def link_to_brick(*args, **kwargs)
141
+ return unless ::Brick.config.mode == :on
142
+
143
+ text = ((args.first.is_a?(String) || args.first.is_a?(Proc)) && args.shift) || args[1]
144
+ text = text.call if text.is_a?(Proc)
145
+ klass_or_obj = ((args.first.is_a?(ActiveRecord::Relation) ||
146
+ args.first.is_a?(ActiveRecord::Base) ||
147
+ args.first.is_a?(Class)) &&
148
+ args.first) ||
149
+ @_brick_model
150
+ # If not provided, do a best-effort to automatically determine the resource class or object
151
+ filter_parts = []
152
+ klass_or_obj ||= begin
153
+ klass, sti_type = ::Brick.ctrl_to_klass(controller_path)
154
+ if klass
155
+ type_col = klass.inheritance_column # Usually 'type'
156
+ filter_parts << "#{type_col}=#{sti_type}" if sti_type && klass.column_names.include?(type_col)
157
+ path_params = request.path_parameters.dup
158
+ path_params.delete(:controller)
159
+ path_params.delete(:action)
160
+ pk = (klass.primary_key || ActiveRecord::Base.primary_key).to_sym
161
+ # Used to also have this but it's a bit too permissive to identify a primary key: (path_params.length == 1 && path_params.values.first) ||
162
+ if ((id = (path_params[pk] || path_params[:id] || path_params["#{klass.name.underscore}_id".to_sym])) && (obj = klass.find_by(pk => id))) ||
163
+ (['show', 'edit', 'update', 'destroy'].include?(action_name) && (obj = klass.first))
164
+ obj
165
+ else
166
+ # %%% If there is a HMT that refers to some ___id then try to identify an appropriate filter
167
+ # %%% If there is a polymorphic association that might relate to stuff in the path_params,
168
+ # try to identify an appropriate ___able_id and ___able_type filter
169
+ ((klass.column_names - [pk.to_s]) & path_params.keys.map(&:to_s)).each do |path_param|
170
+ filter_parts << "#{path_param}=#{path_params[path_param.to_sym]}"
171
+ end
172
+ klass
173
+ end
174
+ end
175
+ rescue
176
+ end
177
+ if klass_or_obj
178
+ if klass_or_obj.is_a?(ActiveRecord::Relation)
179
+ klass_or_obj.where_values_hash.each do |whr|
180
+ filter_parts << "#{whr.first}=#{whr.last}" if whr.last && !whr.last.is_a?(Array)
181
+ end
182
+ klass_or_obj = klass_or_obj.klass
183
+ type_col = klass_or_obj.inheritance_column
184
+ if klass_or_obj.column_names.include?(type_col) && klass_or_obj.name != klass_or_obj.base_class.name
185
+ filter_parts << "#{type_col}=#{klass_or_obj.name}"
186
+ end
187
+ end
188
+ filter = "?#{filter_parts.join('&')}" if filter_parts.present?
189
+ if (klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base) ||
190
+ (klass_or_obj&.is_a?(ActiveRecord::Base) && klass_or_obj.new_record? && (klass_or_obj = klass_or_obj.class))
191
+ path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj) : "#{send("#{klass_or_obj.base_class._brick_index}_path")}#{filter}"
192
+ lt_args = [text || "Index for #{klass_or_obj.name.pluralize}", path]
193
+ else
194
+ # If there are multiple incoming parameters then last one is probably the actual ID, and first few might be some nested tree of stuff leading up to it
195
+ path = (proc = kwargs[:show_proc]) ? proc.call(klass_or_obj) : "#{send("#{klass_or_obj.class.base_class._brick_index(:singular)}_path", klass_or_obj)}#{filter}"
196
+ lt_args = [text || "Show this #{klass_or_obj.class.name}", path]
197
+ end
198
+ link_to(*lt_args, **kwargs)
199
+ else
200
+ # puts "Warning: link_to_brick could not find a class for \"#{controller_path}\" -- consider setting @_brick_model within that controller."
201
+ # if (hits = res_names.keys & instance_variables.map { |v| v.to_s[1..-1] }).present?
202
+ links = instance_variables.each_with_object(Hash.new { |h, k| h[k] = [] }) do |name, s|
203
+ iv_name = name.to_s[1..-1]
204
+ case (val = instance_variable_get(name))
205
+ when ActiveRecord::Relation
206
+ s[val.klass] << iv_name
207
+ when ActiveRecord::Base
208
+ s[val] << iv_name
209
+ end
210
+ end
211
+ if links.length == 1 # If there's only one match then use any text that was supplied
212
+ link_to_brick(text || links.first.last.join('/'), links.first.first, **kwargs)
213
+ else
214
+ links.map { |k, v| link_to_brick(v.join('/'), v, **kwargs) }.join(' &nbsp; ').html_safe
215
+ end
216
+ end
217
+ end # link_to_brick
218
+
219
+ end
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 96
8
+ TINY = 98
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
@@ -138,15 +138,16 @@ module Brick
138
138
  def set_db_schema(params = nil)
139
139
  # If Apartment::Tenant.current is not still the default (usually 'public') then an elevator has brought us into
140
140
  # a different tenant. If so then don't allow schema navigation.
141
- chosen = if (is_show_schema_list = (apartment_multitenant && Apartment::Tenant.current == ::Brick.default_schema)) &&
141
+ chosen = if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' &&
142
+ (current_schema = (ActiveRecord::Base.execute_sql('SELECT current_schemas(true)').first['current_schemas'][1..-2]
143
+ .split(',') - ['pg_catalog', 'pg_toast', 'heroku_ext']).first) &&
144
+ (is_show_schema_list = (apartment_multitenant && current_schema == ::Brick.default_schema)) &&
142
145
  (schema = (params ? params['_brick_schema'] : ::Brick.default_schema)) &&
143
146
  ::Brick.db_schemas&.key?(schema)
144
147
  Apartment::Tenant.switch!(schema)
145
148
  schema
146
- elsif ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
147
- # Just return the current schema
148
- current_schema = ActiveRecord::Base.execute_sql('SELECT current_schemas(true)').first['current_schemas'][1..-2].split(',')
149
- (current_schema - ['pg_catalog', 'pg_toast', 'heroku_ext']).first
149
+ else
150
+ current_schema # Just return the current schema
150
151
  end
151
152
  [chosen == ::Brick.default_schema ? nil : chosen, is_show_schema_list]
152
153
  end
@@ -1257,35 +1258,33 @@ module ActiveRecord
1257
1258
  # For AR >= 4.2
1258
1259
  if self.const_defined?('JoinDependency')
1259
1260
  class JoinDependency
1260
- if ActiveRecord.version >= ::Gem::Version.new('6.0')
1261
- # An intelligent .eager_load() and .includes() that creates t0_r0 style aliases only for the columns
1262
- # used in .select(). To enable this behaviour, include the flag :_brick_eager_load as the first
1263
- # entry in your .select().
1264
- # More information: https://discuss.rubyonrails.org/t/includes-and-select-for-joined-data/81640
1265
- def apply_column_aliases(relation)
1261
+ # An intelligent .eager_load() and .includes() that creates t0_r0 style aliases only for the columns
1262
+ # used in .select(). To enable this behaviour, include the flag :_brick_eager_load as the first
1263
+ # entry in your .select().
1264
+ # More information: https://discuss.rubyonrails.org/t/includes-and-select-for-joined-data/81640
1265
+ def apply_column_aliases(relation)
1266
+ if (sel_vals = relation.select_values.map(&:to_s)).first == '_brick_eager_load'
1266
1267
  used_cols = {}
1267
- if (sel_vals = relation.select_values.map(&:to_s)).first == '_brick_eager_load'
1268
- # Find and expand out all column names being used in select(...)
1269
- new_select_values = sel_vals.each_with_object([]) do |col, s|
1270
- next if col == '_brick_eager_load'
1268
+ # Find and expand out all column names being used in select(...)
1269
+ new_select_values = sel_vals.each_with_object([]) do |col, s|
1270
+ next if col == '_brick_eager_load'
1271
1271
 
1272
- if col.include?(' ') # Some expression? (No chance for a simple column reference)
1273
- s << col # Just pass it through
1274
- else
1275
- col = if (col_parts = col.split('.')).length == 1
1276
- [col]
1277
- else
1278
- [col_parts[0..-2].join('.'), col_parts.last]
1279
- end
1280
- used_cols[col] = nil
1281
- end
1282
- end
1283
- if new_select_values.present?
1284
- relation.select_values = new_select_values
1272
+ if col.include?(' ') # Some expression? (No chance for a simple column reference)
1273
+ s << col # Just pass it through
1285
1274
  else
1286
- relation.select_values.clear
1275
+ col = if (col_parts = col.split('.')).length == 1
1276
+ [col]
1277
+ else
1278
+ [col_parts[0..-2].join('.'), col_parts.last]
1279
+ end
1280
+ used_cols[col] = nil
1287
1281
  end
1288
1282
  end
1283
+ if new_select_values.present?
1284
+ relation.select_values = new_select_values
1285
+ else
1286
+ relation.select_values.clear
1287
+ end
1289
1288
 
1290
1289
  @aliases ||= Aliases.new(join_root.each_with_index.map do |join_part, i|
1291
1290
  join_alias = join_part.table&.table_alias || join_part.table_name
@@ -1315,9 +1314,9 @@ module ActiveRecord
1315
1314
  end
1316
1315
  Aliases::Table.new(join_part, columns)
1317
1316
  end)
1318
-
1319
- relation._select!(-> { @aliases.columns })
1320
1317
  end
1318
+
1319
+ relation._select!(-> { aliases.columns })
1321
1320
  end
1322
1321
 
1323
1322
  private
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.96
4
+ version: 1.0.98
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-25 00:00:00.000000000 Z
11
+ date: 2022-12-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -216,6 +216,7 @@ files:
216
216
  - lib/brick/frameworks/rails/controller.rb
217
217
  - lib/brick/frameworks/rails/crosstab.brk
218
218
  - lib/brick/frameworks/rails/engine.rb
219
+ - lib/brick/frameworks/rails/form_tags.rb
219
220
  - lib/brick/frameworks/rspec.rb
220
221
  - lib/brick/join_array.rb
221
222
  - lib/brick/serializers/json.rb