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.
- checksums.yaml +4 -4
- data/lib/brick/extensions.rb +165 -178
- data/lib/brick/frameworks/rails/controller.rb +1 -1
- data/lib/brick/frameworks/rails/engine.rb +269 -197
- data/lib/brick/frameworks/rails/form_tags.rb +219 -0
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +30 -31
- metadata +3 -2
@@ -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\"><< 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(' ').html_safe
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end # link_to_brick
|
218
|
+
|
219
|
+
end
|
data/lib/brick/version_number.rb
CHANGED
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
|
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
|
-
|
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
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
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
|
-
|
1268
|
-
|
1269
|
-
|
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
|
-
|
1273
|
-
|
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
|
-
|
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.
|
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
|
+
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
|