brick 1.0.96 → 1.0.98

Sign up to get free protection for your applications and to get access to all the features.
@@ -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