brick 1.0.5 → 1.0.8
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 +1 -1
- data/lib/brick/extensions.rb +62 -14
- data/lib/brick/frameworks/rails/engine.rb +17 -11
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +6 -2
- 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: 7e06e6aaa9b7fef7730d56404912ab186a29f6e698f6dbb0d9c551cf2008c0b1
|
4
|
+
data.tar.gz: 7e546071063729d5b85b2f146bb401c7cbfc462f6dea0a701ed8c839cb883de5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83499e7959f98bb323f44593f6472ba1996815ed5f286a119f5adfbd2b690be3f9cd3556166cff3efda250fd609b6d06f4054aea599b0e9587191b4cb1d7a3cb
|
7
|
+
data.tar.gz: 6ded71697d4cce4d931afb54869639d6c2d0b32462806c0c0c7826ceb46fd5f7d7c2476f29e5c9302c83482e0293916dd82e553fa847482de52c39e06dc2d210
|
data/lib/brick/config.rb
CHANGED
data/lib/brick/extensions.rb
CHANGED
@@ -80,6 +80,18 @@ module ActiveRecord
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
83
|
+
|
84
|
+
module Inheritance
|
85
|
+
module ClassMethods
|
86
|
+
private
|
87
|
+
|
88
|
+
alias _brick_find_sti_class find_sti_class
|
89
|
+
def find_sti_class(type_name)
|
90
|
+
::Brick.sti_models[type_name] = { base: self } unless type_name.blank?
|
91
|
+
_brick_find_sti_class(type_name)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
83
95
|
end
|
84
96
|
|
85
97
|
# Object.class_exec do
|
@@ -113,6 +125,11 @@ class Object
|
|
113
125
|
singular_table_name = ActiveSupport::Inflector.underscore(model_name)
|
114
126
|
table_name = ActiveSupport::Inflector.pluralize(singular_table_name)
|
115
127
|
|
128
|
+
# Adjust for STI if we know of a base model for the requested model name
|
129
|
+
if (base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil))
|
130
|
+
table_name = base_model.table_name
|
131
|
+
end
|
132
|
+
|
116
133
|
# Maybe, just maybe there's a database table that will satisfy this need
|
117
134
|
if (matching = [table_name, singular_table_name, plural_class_name, model_name].find { |m| relations.key?(m) })
|
118
135
|
build_model(model_name, singular_table_name, table_name, relations, matching)
|
@@ -138,8 +155,9 @@ class Object
|
|
138
155
|
if table_name == singular_table_name && !ActiveSupport::Inflector.inflections.uncountable.include?(table_name)
|
139
156
|
raise NameError.new("Class name for a model that references table \"#{matching}\" should be \"#{ActiveSupport::Inflector.singularize(model_name)}\".")
|
140
157
|
end
|
141
|
-
|
142
|
-
|
158
|
+
base_model = ::Brick.sti_models[model_name]&.fetch(:base, nil) || ActiveRecord::Base
|
159
|
+
code = +"class #{model_name} < #{base_model.name}\n"
|
160
|
+
built_model = Class.new(base_model) do |new_model_class|
|
143
161
|
Object.const_set(model_name.to_sym, new_model_class)
|
144
162
|
# Accommodate singular or camel-cased table names such as "order_detail" or "OrderDetails"
|
145
163
|
code << " self.table_name = '#{self.table_name = matching}'\n" unless table_name == matching
|
@@ -176,8 +194,10 @@ class Object
|
|
176
194
|
end
|
177
195
|
|
178
196
|
fks = relation[:fks] || {}
|
179
|
-
|
180
|
-
|
197
|
+
# Do the bulk of the has_many / belongs_to processing, and store details about HMT so they can be done at the very last
|
198
|
+
hmts = fks.each_with_object(Hash.new { |h, k| h[k] = [] }) do |fk, hmts|
|
199
|
+
# The key in each hash entry (fk.first) is the constraint name
|
200
|
+
assoc_name = (assoc = fk.last)[:assoc_name]
|
181
201
|
inverse_assoc_name = assoc[:inverse][:assoc_name]
|
182
202
|
options = {}
|
183
203
|
singular_table_name = ActiveSupport::Inflector.singularize(assoc[:inverse_table])
|
@@ -185,7 +205,7 @@ class Object
|
|
185
205
|
need_class_name = singular_table_name.underscore != assoc_name
|
186
206
|
need_fk = "#{assoc_name}_id" != assoc[:fk]
|
187
207
|
inverse_assoc_name, _x = _brick_get_hm_assoc_name(relations[assoc[:inverse_table]], assoc[:inverse])
|
188
|
-
if (has_ones = ::Brick.config.has_ones
|
208
|
+
if (has_ones = ::Brick.config.has_ones&.fetch(assoc[:inverse][:alternate_name].camelize, nil))&.key?(singular_inv_assoc_name = ActiveSupport::Inflector.singularize(inverse_assoc_name))
|
189
209
|
inverse_assoc_name = if has_ones[singular_inv_assoc_name]
|
190
210
|
need_inverse_of = true
|
191
211
|
has_ones[singular_inv_assoc_name]
|
@@ -215,23 +235,51 @@ class Object
|
|
215
235
|
# Figure out if we need to specially call out the class_name and/or foreign key
|
216
236
|
# (and if either of those then definitely also a specific inverse_of)
|
217
237
|
options[:class_name] = singular_table_name.camelize if need_class_name
|
218
|
-
|
238
|
+
# Work around a bug in CPK where self-referencing belongs_to associations double up their foreign keys
|
239
|
+
if need_fk # Funky foreign key?
|
240
|
+
options[:foreign_key] = if assoc[:fk].is_a?(Array)
|
241
|
+
assoc_fk = assoc[:fk].uniq
|
242
|
+
assoc_fk.length < 2 ? assoc_fk.first : assoc_fk
|
243
|
+
else
|
244
|
+
assoc[:fk].to_sym
|
245
|
+
end
|
246
|
+
end
|
219
247
|
options[:inverse_of] = inverse_assoc_name.to_sym if need_class_name || need_fk || need_inverse_of
|
220
|
-
assoc_name = assoc_name.to_sym
|
221
|
-
code << " #{macro} #{assoc_name.inspect}#{options.map { |k, v| ", #{k}: #{v.inspect}" }.join}\n"
|
222
|
-
self.send(macro, assoc_name, **options)
|
223
248
|
|
224
|
-
#
|
249
|
+
# Prepare a list of entries for "has_many :through"
|
225
250
|
if macro == :has_many
|
226
251
|
relations[assoc[:inverse_table]][:hmt_fks].each do |k, hmt_fk|
|
227
252
|
next if k == assoc[:fk]
|
228
253
|
|
229
|
-
|
230
|
-
code << " has_many :#{hmt_fk}, through: #{assoc_name.inspect}\n"
|
231
|
-
self.send(:has_many, hmt_fk.to_sym, **{ through: assoc_name })
|
254
|
+
hmts[ActiveSupport::Inflector.pluralize(hmt_fk.last)] << [assoc, hmt_fk.first]
|
232
255
|
end
|
233
256
|
end
|
257
|
+
|
258
|
+
# And finally create a has_one, has_many, or belongs_to for this association
|
259
|
+
assoc_name = assoc_name.to_sym
|
260
|
+
code << " #{macro} #{assoc_name.inspect}#{options.map { |k, v| ", #{k}: #{v.inspect}" }.join}\n"
|
261
|
+
self.send(macro, assoc_name, **options)
|
262
|
+
hmts
|
263
|
+
end
|
264
|
+
hmts.each do |hmt_fk, fks|
|
265
|
+
fks.each do |fk|
|
266
|
+
source = nil
|
267
|
+
this_hmt_fk = if fks.length > 1
|
268
|
+
singular_assoc_name = ActiveSupport::Inflector.singularize(fk.first[:inverse][:assoc_name])
|
269
|
+
source = fk.last
|
270
|
+
through = ActiveSupport::Inflector.pluralize(fk.first[:alternate_name])
|
271
|
+
"#{singular_assoc_name}_#{hmt_fk}"
|
272
|
+
else
|
273
|
+
through = fk.first[:assoc_name]
|
274
|
+
hmt_fk
|
275
|
+
end
|
276
|
+
code << " has_many :#{this_hmt_fk}, through: #{(assoc_name = through.to_sym).to_sym.inspect}#{", source: :#{source}" if source}\n"
|
277
|
+
options = { through: assoc_name }
|
278
|
+
options[:source] = source.to_sym if source
|
279
|
+
self.send(:has_many, this_hmt_fk.to_sym, **options)
|
280
|
+
end
|
234
281
|
end
|
282
|
+
|
235
283
|
code << "end # model #{model_name}\n\n"
|
236
284
|
end # class definition
|
237
285
|
[built_model, code]
|
@@ -275,7 +323,7 @@ class Object
|
|
275
323
|
end
|
276
324
|
|
277
325
|
def _brick_get_hm_assoc_name(relation, hm_assoc)
|
278
|
-
if relation[:hm_counts][hm_assoc[:assoc_name]]
|
326
|
+
if relation[:hm_counts][hm_assoc[:assoc_name]]&.> 1
|
279
327
|
[ActiveSupport::Inflector.pluralize(hm_assoc[:alternate_name]), true]
|
280
328
|
else
|
281
329
|
[ActiveSupport::Inflector.pluralize(hm_assoc[:inverse_table]), nil]
|
@@ -66,17 +66,23 @@ module Brick
|
|
66
66
|
associatives = hms.select { |k, v| v.options[:through] }.each_with_object({}) do |hmt, s|
|
67
67
|
s[hmt.first] = hms.delete(hmt.last.options[:through]) # End up with a hash of HMT names pointing to join-table associations
|
68
68
|
end
|
69
|
-
hms_headers = hms.each_with_object(+'') { |hm, s| s << "<th>
|
69
|
+
hms_headers = hms.each_with_object(+'') { |hm, s| s << "<th>H#{hm.last.macro == :has_one ? 'O' : 'M'}#{'T' if hm.last.options[:through]} #{hm.first}</th>\n" }
|
70
70
|
hms_columns = hms.each_with_object(+'') do |hm, s|
|
71
71
|
hm_fk_name = if hm.last.options[:through]
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
s <<
|
72
|
+
associative = associatives[hm.last.name]
|
73
|
+
"'#{associative.name}.#{associative.foreign_key}'"
|
74
|
+
else
|
75
|
+
hm.last.foreign_key
|
76
|
+
end
|
77
|
+
s << if hm.last.macro == :has_many
|
78
|
+
"<td>
|
78
79
|
<%= link_to \"#\{#{obj_name}.#{hm.first}.count\} #{hm.first}\", #{hm.last.klass.name.underscore.pluralize}_path({ #{hm_fk_name}: #{obj_name}.#{pk} }) unless #{obj_name}.#{hm.first}.count.zero? %>
|
79
80
|
</td>\n"
|
81
|
+
else # has_one
|
82
|
+
"<td>
|
83
|
+
<%= obj = #{obj_name}.#{hm.first}; link_to(obj.brick_descrip, obj) if obj %>
|
84
|
+
</td>\n"
|
85
|
+
end
|
80
86
|
end
|
81
87
|
|
82
88
|
inline = case args.first
|
@@ -89,7 +95,7 @@ module Brick
|
|
89
95
|
<table id=\"#{table_name}\">
|
90
96
|
<tr>
|
91
97
|
<% is_first = true; is_need_id_col = nil
|
92
|
-
bts = { #{bts.each_with_object([]) { |v, s| s << "#{v.first.inspect} => [#{v.last.first.inspect}, #{v.last.
|
98
|
+
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(', ')} }
|
93
99
|
@#{table_name}.columns.map(&:name).each do |col| %>
|
94
100
|
<% next if col == '#{pk}' || ::Brick.config.metadata_columns.include?(col) %>
|
95
101
|
<th>
|
@@ -125,9 +131,9 @@ module Brick
|
|
125
131
|
<% next if k == '#{pk}' || ::Brick.config.metadata_columns.include?(k) %>
|
126
132
|
<td>
|
127
133
|
<% if (bt = bts[k]) %>
|
128
|
-
<%= obj = bt[1].find_by(bt.last => val); link_to
|
134
|
+
<%= obj = bt[1].find_by(bt.last => val); link_to(obj.brick_descrip, obj) if obj %>
|
129
135
|
<% elsif is_first %>
|
130
|
-
<%= is_first = false; link_to val, #{obj_name} %>
|
136
|
+
<%= is_first = false; link_to val, #{obj_name}_path(#{obj_name}.#{pk}) %>
|
131
137
|
<% else %>
|
132
138
|
<%= val %>
|
133
139
|
<% end %>
|
@@ -188,7 +194,7 @@ module Brick
|
|
188
194
|
# Find associative tables that can be set up for has_many :through
|
189
195
|
::Brick.relations.each do |_key, tbl|
|
190
196
|
tbl_cols = tbl[:cols].keys
|
191
|
-
fks = tbl[:fks].each_with_object({}) { |fk, s| s[fk.last[:fk]] = fk.last[:inverse_table] if fk.last[:is_bt]; s }
|
197
|
+
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 }
|
192
198
|
# Aside from the primary key and the metadata columns created_at, updated_at, and deleted_at, if this table only has
|
193
199
|
# foreign keys then it can act as an associative table and thus be used with has_many :through.
|
194
200
|
if fks.length > 1 && (tbl_cols - fks.keys - (::Brick.config.metadata_columns || []) - (tbl[:pkey].values.first || [])).length.zero?
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -81,6 +81,10 @@ if Gem::Specification.all_names.any? { |g| g.start_with?('rails-') }
|
|
81
81
|
require 'brick/frameworks/rails'
|
82
82
|
end
|
83
83
|
module Brick
|
84
|
+
def self.sti_models
|
85
|
+
@sti_models ||= {}
|
86
|
+
end
|
87
|
+
|
84
88
|
class << self
|
85
89
|
# All tables and views (what Postgres calls "relations" including column and foreign key info)
|
86
90
|
def relations
|
@@ -107,7 +111,7 @@ module Brick
|
|
107
111
|
end
|
108
112
|
|
109
113
|
s.first[a.foreign_key] = [a.name, a.klass]
|
110
|
-
when :has_many
|
114
|
+
when :has_many, :has_one
|
111
115
|
s.last[a.name] = a
|
112
116
|
end
|
113
117
|
s
|
@@ -192,7 +196,7 @@ module Brick
|
|
192
196
|
end
|
193
197
|
end
|
194
198
|
|
195
|
-
# Associations to treat
|
199
|
+
# Associations to treat as a has_one
|
196
200
|
# @api public
|
197
201
|
def has_ones=(hos)
|
198
202
|
if hos
|
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.8
|
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-03-
|
11
|
+
date: 2022-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|