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