brick 1.0.16 → 1.0.19
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/config.rb +5 -5
- data/lib/brick/extensions.rb +77 -30
- data/lib/brick/frameworks/rails/engine.rb +144 -60
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +25 -2
- data/lib/generators/brick/install_generator.rb +3 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57a1d4e3774c90984cfc435d9ed87e42b74c364b01fd7ced7df740f5005b935e
|
4
|
+
data.tar.gz: 8222088ef88ca48cd90144c8503638644195f2d593ff95d7b2e3530304ac6fb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b785c34e1a21563232ae54f6af25989fe02b73f92eb1327b27d6f21e80b7e353a5995305c4c1188dda885465a196f87fb5979a13c3e6a0219d4895dc59593dd
|
7
|
+
data.tar.gz: bd2018d48dad15f51d7a87e39d2710bde9a5becbe969e741004bf730f57faecbd84a81a2f75cfdd60c2ff48f239fe6c5c8d3f47b4b2f79e355b7f100f9bb7943
|
data/lib/brick/config.rb
CHANGED
@@ -66,12 +66,12 @@ module Brick
|
|
66
66
|
end
|
67
67
|
|
68
68
|
# Skip creating a has_many association for these
|
69
|
-
def
|
70
|
-
@mutex.synchronize { @
|
69
|
+
def exclude_hms
|
70
|
+
@mutex.synchronize { @exclude_hms }
|
71
71
|
end
|
72
72
|
|
73
|
-
def
|
74
|
-
@mutex.synchronize { @
|
73
|
+
def exclude_hms=(skips)
|
74
|
+
@mutex.synchronize { @exclude_hms = skips }
|
75
75
|
end
|
76
76
|
|
77
77
|
# Associations to treat as a has_one
|
@@ -108,7 +108,7 @@ module Brick
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def exclude_tables
|
111
|
-
@mutex.synchronize { @exclude_tables }
|
111
|
+
@mutex.synchronize { @exclude_tables || [] }
|
112
112
|
end
|
113
113
|
|
114
114
|
def exclude_tables=(value)
|
data/lib/brick/extensions.rb
CHANGED
@@ -137,7 +137,6 @@ module ActiveRecord
|
|
137
137
|
alias _brick_find_sti_class find_sti_class
|
138
138
|
def find_sti_class(type_name)
|
139
139
|
if ::Brick.sti_models.key?(type_name)
|
140
|
-
# puts ['X', self.name, type_name].inspect
|
141
140
|
_brick_find_sti_class(type_name)
|
142
141
|
else
|
143
142
|
# This auto-STI is more of a brute-force approach, building modules where needed
|
@@ -149,7 +148,7 @@ module ActiveRecord
|
|
149
148
|
if ::Brick.config.sti_namespace_prefixes&.key?("::#{module_name}::") ||
|
150
149
|
::Brick.config.sti_namespace_prefixes&.key?("#{module_name}::")
|
151
150
|
_brick_find_sti_class(type_name)
|
152
|
-
elsif File.
|
151
|
+
elsif File.exist?(candidate_file = Rails.root.join('app/models' + module_prefixes.map(&:underscore).join('/') + '.rb'))
|
153
152
|
_brick_find_sti_class(type_name) # Find this STI class normally
|
154
153
|
else
|
155
154
|
# Build missing prefix modules if they don't yet exist
|
@@ -243,10 +242,10 @@ class Object
|
|
243
242
|
|
244
243
|
# Adjust for STI if we know of a base model for the requested model name
|
245
244
|
table_name = if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
245
|
+
base_model.table_name
|
246
|
+
else
|
247
|
+
ActiveSupport::Inflector.pluralize(singular_table_name)
|
248
|
+
end
|
250
249
|
|
251
250
|
# Maybe, just maybe there's a database table that will satisfy this need
|
252
251
|
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
|
@@ -454,7 +453,7 @@ class Object
|
|
454
453
|
|
455
454
|
if model.primary_key
|
456
455
|
code << " def show\n"
|
457
|
-
code << " @#{singular_table_name} = #{model.name}.find(params[:id].split(','))\n"
|
456
|
+
code << (find_by_id = " @#{singular_table_name} = #{model.name}.find(params[:id].split(','))\n")
|
458
457
|
code << " end\n"
|
459
458
|
self.define_method :show do
|
460
459
|
::Brick.set_db_schema(params)
|
@@ -463,9 +462,36 @@ class Object
|
|
463
462
|
end
|
464
463
|
|
465
464
|
# By default, views get marked as read-only
|
466
|
-
unless (relation = relations[model.table_name]).key?(:isView)
|
467
|
-
code << " # (Define :new, :create
|
468
|
-
|
465
|
+
unless false # model.readonly # (relation = relations[model.table_name]).key?(:isView)
|
466
|
+
code << " # (Define :new, :create)\n"
|
467
|
+
|
468
|
+
if model.primary_key
|
469
|
+
is_need_params = true
|
470
|
+
# code << " # (Define :edit, and :destroy)\n"
|
471
|
+
code << " def update\n"
|
472
|
+
code << find_by_id
|
473
|
+
params_name = "#{singular_table_name}_params"
|
474
|
+
code << " @#{singular_table_name}.update(#{params_name})\n"
|
475
|
+
code << " end\n"
|
476
|
+
self.define_method :update do
|
477
|
+
::Brick.set_db_schema(params)
|
478
|
+
instance_variable_set("@#{singular_table_name}".to_sym, (obj = model.find(params[:id].split(','))))
|
479
|
+
obj = obj.first if obj.is_a?(Array)
|
480
|
+
obj.send(:update, send(params_name = params_name.to_sym))
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
if is_need_params
|
485
|
+
code << "private\n"
|
486
|
+
code << " def params\n"
|
487
|
+
code << " params.require(:#{singular_table_name}).permit(#{model.columns_hash.keys.map { |c| c.to_sym.inspect }.join(', ')})\n"
|
488
|
+
code << " end\n"
|
489
|
+
self.define_method(params_name) do
|
490
|
+
params.require(singular_table_name.to_sym).permit(model.columns_hash.keys)
|
491
|
+
end
|
492
|
+
private params_name
|
493
|
+
# Get column names for params from relations[model.table_name][:cols].keys
|
494
|
+
end
|
469
495
|
end
|
470
496
|
code << "end # #{class_name}\n\n"
|
471
497
|
end # class definition
|
@@ -495,9 +521,9 @@ module ActiveRecord::ConnectionHandling
|
|
495
521
|
end
|
496
522
|
|
497
523
|
def _brick_reflect_tables
|
498
|
-
|
499
|
-
|
500
|
-
|
524
|
+
if (relations = ::Brick.relations).empty?
|
525
|
+
# Only for Postgres? (Doesn't work in sqlite3)
|
526
|
+
# puts ActiveRecord::Base.connection.execute("SELECT current_setting('SEARCH_PATH')").to_a.inspect
|
501
527
|
|
502
528
|
schema_sql = 'SELECT NULL AS table_schema;'
|
503
529
|
case ActiveRecord::Base.connection.adapter_name
|
@@ -587,6 +613,25 @@ module ActiveRecord::ConnectionHandling
|
|
587
613
|
end
|
588
614
|
end
|
589
615
|
|
616
|
+
# # Add unique OIDs
|
617
|
+
# if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
618
|
+
# ActiveRecord::Base.execute_sql(
|
619
|
+
# "SELECT c.oid, n.nspname, c.relname
|
620
|
+
# FROM pg_catalog.pg_namespace AS n
|
621
|
+
# INNER JOIN pg_catalog.pg_class AS c ON n.oid = c.relnamespace
|
622
|
+
# WHERE c.relkind IN ('r', 'v')"
|
623
|
+
# ).each do |r|
|
624
|
+
# next if ['pg_catalog', 'information_schema', ''].include?(r['nspname']) ||
|
625
|
+
# ['ar_internal_metadata', 'schema_migrations'].include?(r['relname'])
|
626
|
+
# relation = relations.fetch(r['relname'], nil)
|
627
|
+
# if relation
|
628
|
+
# (relation[:oid] ||= {})[r['nspname']] = r['oid']
|
629
|
+
# else
|
630
|
+
# puts "Where is #{r['nspname']} #{r['relname']} ?"
|
631
|
+
# end
|
632
|
+
# end
|
633
|
+
# end
|
634
|
+
|
590
635
|
case ActiveRecord::Base.connection.adapter_name
|
591
636
|
when 'PostgreSQL', 'Mysql2'
|
592
637
|
sql = ActiveRecord::Base.send(:sanitize_sql_array, [
|
@@ -629,10 +674,12 @@ module ActiveRecord::ConnectionHandling
|
|
629
674
|
puts "\nClasses that can be built from views:"
|
630
675
|
views.keys.each { |k| puts ActiveSupport::Inflector.singularize(k).camelize }
|
631
676
|
end
|
632
|
-
# pp relations; nil
|
633
677
|
|
634
|
-
#
|
635
|
-
|
678
|
+
# Try to load the initializer pretty danged early
|
679
|
+
if File.exist?(brick_initialiser = Rails.root.join('config/initializers/brick.rb'))
|
680
|
+
load brick_initialiser
|
681
|
+
::Brick.load_additional_references
|
682
|
+
end
|
636
683
|
end
|
637
684
|
end
|
638
685
|
|
@@ -677,7 +724,7 @@ module Brick
|
|
677
724
|
missing << fk[0] unless relations.key?(fk[0])
|
678
725
|
missing << primary_table unless is_class || relations.key?(primary_table)
|
679
726
|
unless missing.empty?
|
680
|
-
tables = relations.reject { |
|
727
|
+
tables = relations.reject { |_k, v| v.fetch(:isView, nil) }.keys.sort
|
681
728
|
puts "Brick: Additional reference #{fk.inspect} refers to non-existent #{'table'.pluralize(missing.length)} #{missing.join(' and ')}. (Available tables include #{tables.join(', ')}.)"
|
682
729
|
return
|
683
730
|
end
|
@@ -686,7 +733,7 @@ module Brick
|
|
686
733
|
puts "Brick: Additional reference #{fk.inspect} refers to non-existent column #{fk[1]}. (Columns present in #{fk[0]} are #{columns.join(', ')}.)"
|
687
734
|
return
|
688
735
|
end
|
689
|
-
if (redundant = bts.find { |
|
736
|
+
if (redundant = bts.find { |_k, v| v[:inverse]&.fetch(:inverse_table, nil) == fk[0] && v[:fk] == fk[1] && v[:inverse_table] == primary_table })
|
690
737
|
if is_class && !redundant.last.key?(:class)
|
691
738
|
redundant.last[:primary_class] = primary_class # Round out this BT so it can find the proper :source for a HMT association that references an STI subclass
|
692
739
|
else
|
@@ -708,19 +755,19 @@ module Brick
|
|
708
755
|
# assoc_bt[:inverse_of] = primary_class.reflect_on_all_associations.find { |a| a.foreign_key == bt[1] }
|
709
756
|
end
|
710
757
|
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
assoc_bt[:inverse] = assoc_hm
|
758
|
+
return if is_class || ::Brick.config.exclude_hms&.any? { |exclusion| fk[0] == exclusion[0] && fk[1] == exclusion[1] && primary_table == exclusion[2] }
|
759
|
+
|
760
|
+
cnstr_name = "hm_#{cnstr_name}"
|
761
|
+
if (assoc_hm = hms.fetch(cnstr_name, nil))
|
762
|
+
assoc_hm[:fk] = assoc_hm[:fk].is_a?(String) ? [assoc_hm[:fk], fk[1]] : assoc_hm[:fk].concat(fk[1])
|
763
|
+
assoc_hm[:alternate_name] = "#{assoc_hm[:alternate_name]}_#{bt_assoc_name}" unless assoc_hm[:alternate_name] == bt_assoc_name
|
764
|
+
assoc_hm[:inverse] = assoc_bt
|
765
|
+
else
|
766
|
+
assoc_hm = hms[cnstr_name] = { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0], inverse: assoc_bt }
|
767
|
+
hm_counts = relation.fetch(:hm_counts) { relation[:hm_counts] = {} }
|
768
|
+
hm_counts[fk[0]] = hm_counts.fetch(fk[0]) { 0 } + 1
|
723
769
|
end
|
770
|
+
assoc_bt[:inverse] = assoc_hm
|
724
771
|
# hms[cnstr_name] << { is_bt: false, fk: fk[1], assoc_name: fk[0], alternate_name: bt_assoc_name, inverse_table: fk[0] }
|
725
772
|
end
|
726
773
|
end
|
@@ -32,7 +32,7 @@ module Brick
|
|
32
32
|
::Brick.additional_references = app.config.brick.fetch(:additional_references, nil)
|
33
33
|
|
34
34
|
# Skip creating a has_many association for these
|
35
|
-
::Brick.
|
35
|
+
::Brick.exclude_hms = app.config.brick.fetch(:exclude_hms, nil)
|
36
36
|
|
37
37
|
# Has one relationships
|
38
38
|
::Brick.has_ones = app.config.brick.fetch(:has_ones, nil)
|
@@ -51,14 +51,11 @@ module Brick
|
|
51
51
|
# Need to return true if we can fill in the blanks for a missing one
|
52
52
|
# args will be something like: ["index", ["categories"]]
|
53
53
|
model = args[1].map(&:camelize).join('::').singularize.constantize
|
54
|
-
if (
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
)
|
60
|
-
)
|
61
|
-
instance_variable_set(:@_brick_model, model)
|
54
|
+
if is_template_exists = model && (
|
55
|
+
['index', 'show'].include?(args.first) || # Everything has index and show
|
56
|
+
# Only CRU stuff has create / update / destroy
|
57
|
+
(!model.is_view? && ['new', 'create', 'edit', 'update', 'destroy'].include?(args.first))
|
58
|
+
) && instance_variable_set(:@_brick_model, model)
|
62
59
|
end
|
63
60
|
end
|
64
61
|
is_template_exists
|
@@ -76,33 +73,32 @@ module Brick
|
|
76
73
|
bts, hms = ::Brick.get_bts_and_hms(@_brick_model)
|
77
74
|
# Mark has_manys that go to an associative ("join") table so that they are skipped in the UI,
|
78
75
|
# as well as any possible polymorphic associations
|
79
|
-
|
76
|
+
exclude_hms = {}
|
80
77
|
associatives = hms.each_with_object({}) do |hmt, s|
|
81
78
|
if (through = hmt.last.options[:through])
|
82
|
-
|
79
|
+
exclude_hms[through] = nil
|
83
80
|
s[hmt.first] = hms[through] # End up with a hash of HMT names pointing to join-table associations
|
84
81
|
elsif hmt.last.inverse_of.nil?
|
85
82
|
puts "SKIPPING #{hmt.last.name.inspect}"
|
86
83
|
# %%% If we don't do this then below associative.name will find that associative is nil
|
87
|
-
|
84
|
+
exclude_hms[hmt.last.name] = nil
|
88
85
|
end
|
89
86
|
end
|
90
87
|
|
91
|
-
schema_options = ::Brick.db_schemas.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
|
92
88
|
hms_columns = +'' # Used for 'index'
|
93
89
|
hms_headers = hms.each_with_object([]) do |hm, s|
|
94
|
-
next if
|
90
|
+
next if exclude_hms.key?((hm_assoc = hm.last).name)
|
95
91
|
|
96
92
|
if args.first == 'index'
|
97
|
-
hm_fk_name = if
|
98
|
-
associative = associatives[
|
93
|
+
hm_fk_name = if hm_assoc.options[:through]
|
94
|
+
associative = associatives[hm_assoc.name]
|
99
95
|
"'#{associative.name}.#{associative.foreign_key}'"
|
100
96
|
else
|
101
|
-
|
97
|
+
hm_assoc.foreign_key
|
102
98
|
end
|
103
|
-
hms_columns << if
|
99
|
+
hms_columns << if hm_assoc.macro == :has_many
|
104
100
|
"<td>
|
105
|
-
<%= link_to \"#\{#{obj_name}.#{hm.first}.count\} #{hm.first}\", #{
|
101
|
+
<%= link_to \"#\{#{obj_name}.#{hm.first}.count\} #{hm.first}\", #{hm_assoc.klass.name.underscore.pluralize}_path({ #{hm_fk_name}: #{obj_name}.#{pk} }) unless #{obj_name}.#{hm.first}.count.zero? %>
|
106
102
|
</td>\n"
|
107
103
|
else # has_one
|
108
104
|
"<td>
|
@@ -110,9 +106,15 @@ module Brick
|
|
110
106
|
</td>\n"
|
111
107
|
end
|
112
108
|
end
|
113
|
-
s << [
|
109
|
+
s << [hm_assoc, "H#{hm_assoc.macro == :has_one ? 'O' : 'M'}#{'T' if hm_assoc.options[:through]} #{hm.first}"]
|
114
110
|
end
|
115
|
-
|
111
|
+
end
|
112
|
+
if @_brick_model
|
113
|
+
schema_options = ::Brick.db_schemas.each_with_object(+'') { |v, s| s << "<option value=\"#{v}\">#{v}</option>" }.html_safe
|
114
|
+
# %%% If we are not auto-creating controllers (or routes) then omit by default, and if enabled anyway, such as in a development
|
115
|
+
# environment or whatever, then get either the controllers or routes list instead
|
116
|
+
table_options = (::Brick.relations.keys - ::Brick.config.exclude_tables)
|
117
|
+
.each_with_object(+'') { |v, s| s << "<option value=\"#{v.underscore.pluralize}\">#{v}</option>" }.html_safe
|
116
118
|
css = "<style>
|
117
119
|
table {
|
118
120
|
border-collapse: collapse;
|
@@ -155,15 +157,43 @@ table tbody tr.active-row {
|
|
155
157
|
}
|
156
158
|
|
157
159
|
a.show-arrow {
|
160
|
+
font-size: 1.5em;
|
161
|
+
text-decoration: none;
|
162
|
+
}
|
163
|
+
a.big-arrow {
|
158
164
|
font-size: 2.5em;
|
159
165
|
text-decoration: none;
|
160
166
|
}
|
161
|
-
|
167
|
+
.wide-input {
|
168
|
+
display: block;
|
169
|
+
overflow: hidden;
|
170
|
+
}
|
171
|
+
.wide-input input[type=text] {
|
172
|
+
width: 100%;
|
173
|
+
}
|
174
|
+
.dimmed {
|
175
|
+
background-color: #C0C0C0;
|
176
|
+
}
|
177
|
+
input[type=submit] {
|
178
|
+
background-color: #004998;
|
179
|
+
color: #FFF;
|
180
|
+
}
|
181
|
+
.right {
|
182
|
+
text-align: right;
|
183
|
+
}
|
184
|
+
</style>
|
185
|
+
<% def is_bcrypt?(val)
|
186
|
+
val.is_a?(String) && val.length == 60 && val.start_with?('$2a$')
|
187
|
+
end
|
188
|
+
def hide_bcrypt(val)
|
189
|
+
is_bcrypt?(val) ? '(hidden)' : val
|
190
|
+
end %>"
|
162
191
|
|
163
192
|
script = "<script>
|
164
193
|
var schemaSelect = document.getElementById(\"schema\");
|
194
|
+
var brickSchema;
|
165
195
|
if (schemaSelect) {
|
166
|
-
|
196
|
+
brickSchema = changeout(location.href, \"_brick_schema\");
|
167
197
|
if (brickSchema) {
|
168
198
|
[... document.getElementsByTagName(\"A\")].forEach(function (a) { a.href = changeout(a.href, \"_brick_schema\", brickSchema); });
|
169
199
|
}
|
@@ -173,8 +203,39 @@ if (schemaSelect) {
|
|
173
203
|
location.href = changeout(location.href, \"_brick_schema\", this.value);
|
174
204
|
});
|
175
205
|
}
|
206
|
+
[... document.getElementsByTagName(\"FORM\")].forEach(function (form) {
|
207
|
+
if (brickSchema)
|
208
|
+
form.action = changeout(form.action, \"_brick_schema\", brickSchema);
|
209
|
+
form.addEventListener('submit', function (ev) {
|
210
|
+
[... ev.target.getElementsByTagName(\"SELECT\")].forEach(function (select) {
|
211
|
+
if (select.value === \"^^^brick_NULL^^^\")
|
212
|
+
select.value = null;
|
213
|
+
});
|
214
|
+
return true;
|
215
|
+
});
|
216
|
+
});
|
217
|
+
|
218
|
+
var tblSelect = document.getElementById(\"tbl\");
|
219
|
+
if (tblSelect) {
|
220
|
+
tblSelect.value = changeout(location.href);
|
221
|
+
tblSelect.addEventListener(\"change\", function () {
|
222
|
+
var lhr = changeout(location.href, null, this.value);
|
223
|
+
if (brickSchema)
|
224
|
+
lhr = changeout(lhr, \"_brick_schema\", schemaSelect.value);
|
225
|
+
location.href = lhr;
|
226
|
+
});
|
227
|
+
}
|
228
|
+
|
176
229
|
function changeout(href, param, value) {
|
177
230
|
var hrefParts = href.split(\"?\");
|
231
|
+
if (param === undefined || param === null) {
|
232
|
+
hrefParts = hrefParts[0].split(\"://\");
|
233
|
+
var pathParts = hrefParts[hrefParts.length - 1].split(\"/\");
|
234
|
+
if (value === undefined)
|
235
|
+
return pathParts[1];
|
236
|
+
else
|
237
|
+
return hrefParts[0] + \"://\" + pathParts[0] + \"/\" + value;
|
238
|
+
}
|
178
239
|
var params = hrefParts.length > 1 ? hrefParts[1].split(\"&\") : [];
|
179
240
|
params = params.reduce(function (s, v) { var parts = v.split(\"=\"); s[parts[0]] = parts[1]; return s; }, {});
|
180
241
|
if (value === undefined) return params[param];
|
@@ -188,10 +249,11 @@ function changeout(href, param, value) {
|
|
188
249
|
"#{css}
|
189
250
|
<p style=\"color: green\"><%= notice %></p>#{"
|
190
251
|
<select id=\"schema\">#{schema_options}</select>" if ::Brick.db_schemas.length > 1}
|
252
|
+
<select id=\"tbl\">#{table_options}</select>
|
191
253
|
<h1>#{model_name.pluralize}</h1>
|
192
254
|
<% if @_brick_params&.present? %><h3>where <%= @_brick_params.each_with_object([]) { |v, s| s << \"#\{v.first\} = #\{v.last.inspect\}\" }.join(', ') %></h3><% end %>
|
193
255
|
<table id=\"#{table_name}\">
|
194
|
-
<thead><tr>#{"<th></th>" if pk
|
256
|
+
<thead><tr>#{"<th></th>" if pk}
|
195
257
|
<% bts = { #{bts.each_with_object([]) { |v, s| s << "#{v.first.inspect} => [#{v.last.first.inspect}, #{v.last[1].name}, #{v.last[1].primary_key.inspect}]"}.join(', ')} }
|
196
258
|
@#{table_name}.columns.map(&:name).each do |col| %>
|
197
259
|
<% next if col == '#{pk}' || ::Brick.config.metadata_columns.include?(col) %>
|
@@ -209,7 +271,7 @@ function changeout(href, param, value) {
|
|
209
271
|
<tbody>
|
210
272
|
<% @#{table_name}.each do |#{obj_name}| %>
|
211
273
|
<tr>#{"
|
212
|
-
<td><%= link_to '⇛', #{obj_name}_path(#{obj_name}.#{pk}), { class: '
|
274
|
+
<td><%= link_to '⇛', #{obj_name}_path(#{obj_name}.#{pk}), { class: 'big-arrow' } %></td>" if pk}
|
213
275
|
<% #{obj_name}.attributes.each do |k, val| %>
|
214
276
|
<% next if k == '#{pk}' || ::Brick.config.metadata_columns.include?(k) %>
|
215
277
|
<td>
|
@@ -218,9 +280,9 @@ function changeout(href, param, value) {
|
|
218
280
|
# send(\"#\{bt_obj_class = bt[1].name.underscore\}_path\".to_sym, bt_obj.send(bt[1].primary_key.to_sym))
|
219
281
|
# Otherwise we get stuff like:
|
220
282
|
# ActionView::Template::Error (undefined method `vehicle_path' for #<ActionView::Base:0x0000000033a888>) %>
|
221
|
-
<%= bt_obj = bt[1].find_by(bt
|
283
|
+
<%= bt_obj = bt[1].find_by(bt[2] => val); link_to(bt_obj.brick_descrip, send(\"#\{bt_obj_class = bt[1].name.underscore\}_path\".to_sym, bt_obj.send(bt[1].primary_key.to_sym))) if bt_obj %>
|
222
284
|
<% else %>
|
223
|
-
<%= val %>
|
285
|
+
<%= hide_bcrypt(val) %>
|
224
286
|
<% end %>
|
225
287
|
</td>
|
226
288
|
<% end %>
|
@@ -233,47 +295,83 @@ function changeout(href, param, value) {
|
|
233
295
|
|
234
296
|
#{"<hr><%= link_to \"New #{obj_name}\", new_#{obj_name}_path %>" unless @_brick_model.is_view?}
|
235
297
|
#{script}"
|
236
|
-
when 'show'
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
298
|
+
when 'show', 'update'
|
299
|
+
"#{css}
|
300
|
+
<p style=\"color: green\"><%= notice %></p>#{"
|
301
|
+
<select id=\"schema\">#{schema_options}</select>" if ::Brick.db_schemas.length > 1}
|
302
|
+
<select id=\"tbl\">#{table_options}</select>
|
303
|
+
<h1>#{model_name}: <%= (obj = @#{obj_name}&.first)&.brick_descrip || controller_name %></h1>
|
304
|
+
<%= link_to '(See all #{obj_name.pluralize})', #{table_name}_path %>
|
305
|
+
<% if obj %>
|
306
|
+
<%= # path_options = [obj.#{pk}]
|
307
|
+
# path_options << { '_brick_schema': } if
|
308
|
+
# url = send(:#{model_name.underscore}_path, obj.#{pk})
|
309
|
+
form_for(obj) do |f| %>
|
242
310
|
<table>
|
243
311
|
<% bts = { #{bts.each_with_object([]) { |v, s| s << "#{v.first.inspect} => [#{v.last.first.inspect}, #{v.last[1].name}, #{v.last[1].primary_key.inspect}]"}.join(', ')} }
|
244
312
|
@#{obj_name}.first.attributes.each do |k, val| %>
|
245
313
|
<tr>
|
246
314
|
<% next if k == '#{pk}' || ::Brick.config.metadata_columns.include?(k) %>
|
247
315
|
<th class=\"show-field\">
|
248
|
-
<% if (bt = bts[k])
|
249
|
-
|
316
|
+
<% if (bt = bts[k])
|
317
|
+
# Add a final member in this array with descriptive options to be used in <select> drop-downs
|
318
|
+
bt_name = bt[1].name
|
319
|
+
# %%% Only do this if the user has permissions to edit this bt field
|
320
|
+
if bt.length < 4
|
321
|
+
bt << (option_detail = [[\"(No #\{bt_name\} chosen)\", '^^^brick_NULL^^^']])
|
322
|
+
bt[1].order(:#{pk}).each { |obj| option_detail << [obj.brick_descrip, obj.#{pk}] }
|
323
|
+
end %>
|
324
|
+
BT <%= \"#\{bt.first\}-\" unless bt_name.underscore == bt.first.to_s %><%= bt_name %>
|
250
325
|
<% else %>
|
251
326
|
<%= k %>
|
252
327
|
<% end %>
|
253
328
|
</th>
|
254
329
|
<td>
|
255
|
-
<% if (bt = bts[k])
|
256
|
-
|
257
|
-
|
258
|
-
<%= val %>
|
330
|
+
<% if (bt = bts[k]) # bt_obj.brick_descrip
|
331
|
+
html_options = { prompt: \"Select #\{bt_name\}\" }
|
332
|
+
html_options[:class] = 'dimmed' unless val %>
|
333
|
+
<%= f.select k.to_sym, bt[3], { value: val || '^^^brick_NULL^^^' }, html_options %>
|
334
|
+
<%= bt_obj = bt[1].find_by(bt[2] => val); link_to('⇛', send(\"#\{bt_obj_class = bt_name.underscore\}_path\".to_sym, bt_obj.send(bt[1].primary_key.to_sym)), { class: 'show-arrow' }) if bt_obj %>
|
335
|
+
<% else case #{model_name}.column_for_attribute(k).type
|
336
|
+
when :string, :text %>
|
337
|
+
<% if is_bcrypt?(val) # || .readonly? %>
|
338
|
+
<%= hide_bcrypt(val) %>
|
339
|
+
<% else %>
|
340
|
+
<div class=\"wide-input\"><%= f.text_field k.to_sym %></div>
|
341
|
+
<% end %>
|
342
|
+
<% when :boolean %>
|
343
|
+
<%= f.check_box k.to_sym %>
|
344
|
+
<% when :integer, :decimal, :float, :date, :datetime, :time, :timestamp
|
345
|
+
# What happens when keys are UUID?
|
346
|
+
# Postgres naturally uses the +uuid_generate_v4()+ function from the uuid-ossp extension
|
347
|
+
# If it's not yet enabled then: enable_extension 'uuid-ossp'
|
348
|
+
# ActiveUUID gem created a new :uuid type %>
|
349
|
+
<%= val %>
|
350
|
+
<% when :binary, :primary_key %>
|
351
|
+
<% end %>
|
259
352
|
<% end %>
|
260
353
|
</td>
|
261
354
|
</tr>
|
262
|
-
|
355
|
+
<% end %>
|
356
|
+
<tr><td colspan=\"2\" class=\"right\"><%= f.submit %></td></tr>
|
263
357
|
</table>
|
358
|
+
<% end %>
|
264
359
|
|
265
360
|
#{hms_headers.map do |hm|
|
266
361
|
next unless (pk = hm.first.klass.primary_key)
|
267
362
|
"<table id=\"#{hm_name = hm.first.name.to_s}\">
|
268
363
|
<tr><th>#{hm.last}</th></tr>
|
269
|
-
<%
|
364
|
+
<% collection = @#{obj_name}.first.#{hm_name}
|
365
|
+
collection = collection.is_a?(ActiveRecord::Associations::CollectionProxy) ? collection.order(#{pk.inspect}) : [collection]
|
366
|
+
if collection.empty? %>
|
270
367
|
<tr><td>(none)</td></tr>
|
271
368
|
<% else %>
|
272
|
-
<% collection.
|
273
|
-
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{
|
369
|
+
<% collection.uniq.each do |#{hm_singular_name = hm_name.singularize.underscore}| %>
|
370
|
+
<tr><td><%= link_to(#{hm_singular_name}.brick_descrip, #{hm.first.klass.name.underscore}_path(#{hm_singular_name}.#{pk})) %></td></tr>
|
274
371
|
<% end %>
|
275
372
|
<% end %>
|
276
373
|
</table>" end.join}
|
374
|
+
<% end %>
|
277
375
|
#{script}"
|
278
376
|
|
279
377
|
end
|
@@ -295,23 +393,9 @@ function changeout(href, param, value) {
|
|
295
393
|
end
|
296
394
|
end
|
297
395
|
|
298
|
-
#
|
299
|
-
|
300
|
-
|
301
|
-
::Brick._add_bt_and_hm(fk[0..2])
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
# Find associative tables that can be set up for has_many :through
|
306
|
-
::Brick.relations.each do |_key, tbl|
|
307
|
-
tbl_cols = tbl[:cols].keys
|
308
|
-
fks = tbl[:fks].each_with_object({}) { |fk, s| s[fk.last[:fk]] = [fk.last[:assoc_name], fk.last[:inverse_table]] if fk.last[:is_bt]; s }
|
309
|
-
# Aside from the primary key and the metadata columns created_at, updated_at, and deleted_at, if this table only has
|
310
|
-
# foreign keys then it can act as an associative table and thus be used with has_many :through.
|
311
|
-
if fks.length > 1 && (tbl_cols - fks.keys - (::Brick.config.metadata_columns || []) - (tbl[:pkey].values.first || [])).length.zero?
|
312
|
-
fks.each { |fk| tbl[:hmt_fks][fk.first] = fk.last }
|
313
|
-
end
|
314
|
-
end
|
396
|
+
# Just in case it hadn't been done previously when we tried to load the brick initialiser,
|
397
|
+
# go make sure we've loaded additional references (virtual foreign keys).
|
398
|
+
::Brick.load_additional_references
|
315
399
|
end
|
316
400
|
end
|
317
401
|
end
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -208,12 +208,12 @@ module Brick
|
|
208
208
|
# Skip creating a has_many association for these
|
209
209
|
# (Uses the same exact three-part format as would define an additional_reference)
|
210
210
|
# @api public
|
211
|
-
def
|
211
|
+
def exclude_hms=(skips)
|
212
212
|
if skips
|
213
213
|
skips = skips.call if skips.is_a?(Proc)
|
214
214
|
skips = skips.to_a unless skips.is_a?(Array)
|
215
215
|
skips = [skips] unless skips.empty? || skips.first.is_a?(Array)
|
216
|
-
Brick.config.
|
216
|
+
Brick.config.exclude_hms = skips
|
217
217
|
end
|
218
218
|
end
|
219
219
|
|
@@ -244,6 +244,29 @@ module Brick
|
|
244
244
|
Brick.config.sti_namespace_prefixes = snp
|
245
245
|
end
|
246
246
|
|
247
|
+
# Load additional references (virtual foreign keys)
|
248
|
+
# This is attempted early if a brick initialiser file is found, and then again as a failsafe at the end of our engine's initialisation
|
249
|
+
# %%% Maybe look for differences the second time 'round and just add new stuff instead of entirely deferring
|
250
|
+
def load_additional_references
|
251
|
+
return if @_additional_references_loaded
|
252
|
+
|
253
|
+
if (ars = ::Brick.config.additional_references)
|
254
|
+
ars.each { |fk| ::Brick._add_bt_and_hm(fk[0..2]) }
|
255
|
+
@_additional_references_loaded = true
|
256
|
+
end
|
257
|
+
|
258
|
+
# Find associative tables that can be set up for has_many :through
|
259
|
+
::Brick.relations.each do |_key, tbl|
|
260
|
+
tbl_cols = tbl[:cols].keys
|
261
|
+
fks = tbl[:fks].each_with_object({}) { |fk, s| s[fk.last[:fk]] = [fk.last[:assoc_name], fk.last[:inverse_table]] if fk.last[:is_bt]; s }
|
262
|
+
# Aside from the primary key and the metadata columns created_at, updated_at, and deleted_at, if this table only has
|
263
|
+
# foreign keys then it can act as an associative table and thus be used with has_many :through.
|
264
|
+
if fks.length > 1 && (tbl_cols - fks.keys - (::Brick.config.metadata_columns || []) - (tbl[:pkey].values.first || [])).length.zero?
|
265
|
+
fks.each { |fk| tbl[:hmt_fks][fk.first] = fk.last }
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
247
270
|
|
248
271
|
# Returns Brick's `::Gem::Version`, convenient for comparisons. This is
|
249
272
|
# recommended over `::Brick::VERSION::STRING`.
|
@@ -18,7 +18,7 @@ module Brick
|
|
18
18
|
desc 'Generates an initializer file for configuring Brick'
|
19
19
|
|
20
20
|
def create_initializer_file
|
21
|
-
unless File.
|
21
|
+
unless File.exist?(filename = 'config/initializers/brick.rb')
|
22
22
|
# See if we can make suggestions for additional_references
|
23
23
|
resembles_fks = []
|
24
24
|
possible_additional_references = (relations = ::Brick.relations).each_with_object([]) do |v, s|
|
@@ -47,7 +47,7 @@ module Brick
|
|
47
47
|
if (relations.fetch(f_table = col_down, nil) ||
|
48
48
|
relations.fetch(f_table = ActiveSupport::Inflector.pluralize(col_down), nil)) &&
|
49
49
|
# Looks pretty promising ... just make sure a model file isn't present
|
50
|
-
!File.
|
50
|
+
!File.exist?("app/models/#{ActiveSupport::Inflector.singularize(v.first)}.rb")
|
51
51
|
s << "['#{v.first}', '#{col}', '#{f_table}']"
|
52
52
|
else
|
53
53
|
resembles_fks << "#{v.first}.#{col}"
|
@@ -114,7 +114,7 @@ module Brick
|
|
114
114
|
# # Skip creating a has_many association for these
|
115
115
|
# # (Uses the same exact three-part format as would define an additional_reference)
|
116
116
|
# # Say for instance that we didn't care to display the favourite colours that users have:
|
117
|
-
# Brick.
|
117
|
+
# Brick.exclude_hms = [['users', 'favourite_colour_id', 'colours']]
|
118
118
|
|
119
119
|
# # By default primary tables involved in a foreign key relationship will indicate a \"has_many\" relationship pointing
|
120
120
|
# # back to the foreign table. In order to represent a \"has_one\" association instead, an override can be provided
|
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.19
|
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-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|