brick 1.0.81 → 1.0.83
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 +116 -13
- data/lib/brick/frameworks/rails/engine.rb +23 -9
- data/lib/brick/version_number.rb +1 -1
- data/lib/brick.rb +18 -16
- data/lib/generators/brick/install_generator.rb +3 -1
- 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: fd1f37f0785af48f35bc1292e9eec44cd8ebc6a81a6ed7163e53aeaeb753ccc7
|
4
|
+
data.tar.gz: e532a10b06419ce64369e7a397a8eb4d268d87dc5a18c936847ab693cbf159bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d9dbf20d1bcdc3d3ac2beaf905392ba4990574a8165c1111bdb8f035bd8b53765f1555bd672da134cd342edadb45a2b62151e49cfa6279ebc957f16d34fc449
|
7
|
+
data.tar.gz: 276c8e4b831a866baa1b4e84a288a0137ae19afba54d08d342432f358ff4b7a45f234521c35f6d1a6ab96fd69ceb68aa30ba41bfb4b18caef7a12e25cedec8ed
|
data/lib/brick/extensions.rb
CHANGED
@@ -59,6 +59,10 @@ end
|
|
59
59
|
|
60
60
|
module ActiveRecord
|
61
61
|
class Base
|
62
|
+
def self.is_brick?
|
63
|
+
instance_variables.include?(:@_brick_built) && instance_variable_get(:@_brick_built)
|
64
|
+
end
|
65
|
+
|
62
66
|
def self._assoc_names
|
63
67
|
@_assoc_names ||= {}
|
64
68
|
end
|
@@ -521,7 +525,7 @@ module ActiveRecord
|
|
521
525
|
# CUSTOM COLUMNS
|
522
526
|
# ==============
|
523
527
|
klass._br_cust_cols.each do |k, cc|
|
524
|
-
if respond_to?(k) # Name already taken?
|
528
|
+
if rel_dupe.respond_to?(k) # Name already taken?
|
525
529
|
# %%% Use ensure_unique here in this kind of fashion:
|
526
530
|
# cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", bts, hms)
|
527
531
|
# binding.pry
|
@@ -641,11 +645,13 @@ module ActiveRecord
|
|
641
645
|
[a.table_name, a.foreign_key, a.source_reflection.macro]
|
642
646
|
end
|
643
647
|
next if bail_out
|
648
|
+
|
644
649
|
# count_column is determined from the originating HMT member
|
645
650
|
if (src_ref = hm.source_reflection).nil?
|
646
651
|
puts "*** Warning: Could not determine destination model for this HMT association in model #{klass.name}:\n has_many :#{hm.name}, through: :#{hm.options[:through]}"
|
652
|
+
puts
|
647
653
|
nix << k
|
648
|
-
|
654
|
+
next
|
649
655
|
elsif src_ref.macro == :belongs_to # Traditional HMT using an associative table
|
650
656
|
"br_t#{idx}.#{hm.foreign_key}"
|
651
657
|
else # A HMT that goes HM -> HM, something like Categories -> Products -> LineItems
|
@@ -676,15 +682,15 @@ module ActiveRecord
|
|
676
682
|
pri_tbl.table_name
|
677
683
|
end
|
678
684
|
on_clause = []
|
679
|
-
if fk_col.is_a?(Array) # Composite key?
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
685
|
+
hm_selects = if fk_col.is_a?(Array) # Composite key?
|
686
|
+
fk_col.each_with_index { |fk_col_part, idx| on_clause << "#{tbl_alias}.#{fk_col_part} = #{pri_tbl_name}.#{pri_tbl.primary_key[idx]}" }
|
687
|
+
fk_col.dup
|
688
|
+
else
|
689
|
+
on_clause << "#{tbl_alias}.#{fk_col} = #{pri_tbl_name}.#{pri_tbl.primary_key}"
|
690
|
+
[fk_col]
|
691
|
+
end
|
686
692
|
if poly_type
|
687
|
-
|
693
|
+
hm_selects << poly_type
|
688
694
|
on_clause << "#{tbl_alias}.#{poly_type} = '#{name}'"
|
689
695
|
end
|
690
696
|
unless from_clause
|
@@ -696,9 +702,9 @@ module ActiveRecord
|
|
696
702
|
hm.table_name
|
697
703
|
end
|
698
704
|
end
|
699
|
-
group_bys = ::Brick.is_oracle || is_mssql ?
|
705
|
+
group_bys = ::Brick.is_oracle || is_mssql ? hm_selects : (1..hm_selects.length).to_a
|
700
706
|
join_clause = "LEFT OUTER
|
701
|
-
JOIN (SELECT #{
|
707
|
+
JOIN (SELECT #{hm_selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', ')}, COUNT(#{'DISTINCT ' if hm.options[:through]}#{count_column
|
702
708
|
}) AS c_t_ FROM #{from_clause || hm_table_name} GROUP BY #{group_bys.join(', ')}) #{tbl_alias}"
|
703
709
|
joins!("#{join_clause} ON #{on_clause.join(' AND ')}")
|
704
710
|
end
|
@@ -735,8 +741,10 @@ JOIN (SELECT #{selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', ')},
|
|
735
741
|
else
|
736
742
|
s << v
|
737
743
|
end
|
738
|
-
else # String stuff just comes straight through
|
744
|
+
else # String stuff (which defines a custom ORDER BY) just comes straight through
|
739
745
|
s << v
|
746
|
+
# Avoid "PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list" in Postgres
|
747
|
+
selects << v if is_distinct
|
740
748
|
end
|
741
749
|
end
|
742
750
|
order!(*final_order_by)
|
@@ -805,6 +813,100 @@ JOIN (SELECT #{selects.map { |s| "#{'br_t0.' if from_clause}#{s}" }.join(', ')},
|
|
805
813
|
end
|
806
814
|
end
|
807
815
|
|
816
|
+
if Object.const_defined?('ActionView')
|
817
|
+
module ActionView::Helpers::FormTagHelper
|
818
|
+
def link_to_brick(*args, **kwargs)
|
819
|
+
text = (args.first.is_a?(String) && args.first) || args[1]
|
820
|
+
klass_or_obj = ((args.first.is_a?(ActiveRecord::Relation) ||
|
821
|
+
args.first.is_a?(ActiveRecord::Base) ||
|
822
|
+
args.first.is_a?(Class)) &&
|
823
|
+
args.first) ||
|
824
|
+
@_brick_model
|
825
|
+
# If not provided, do a best-effort to automatically determine the resource class or object
|
826
|
+
sti_type = nil
|
827
|
+
filter_parts = []
|
828
|
+
klass_or_obj ||= begin
|
829
|
+
res_names = ::Brick.relations.each_with_object({}) do |v, s|
|
830
|
+
v_parts = v.first.split('.')
|
831
|
+
v_parts.shift if v_parts.first == 'public'
|
832
|
+
s[v_parts.join('.')] = v.first
|
833
|
+
end
|
834
|
+
c_path_parts = controller_path.split('/')
|
835
|
+
klass = nil
|
836
|
+
while c_path_parts.present?
|
837
|
+
possible_c_path = c_path_parts.join('.')
|
838
|
+
possible_c_path_singular = c_path_parts[0..-2] + [c_path_parts.last.singularize]
|
839
|
+
possible_sti = possible_c_path_singular.join('/').camelize
|
840
|
+
break if (
|
841
|
+
res_name = res_names[possible_c_path] ||
|
842
|
+
((klass = Brick.config.sti_namespace_prefixes.key?("::#{possible_sti}") && possible_sti.constantize) &&
|
843
|
+
(sti_type = possible_sti)) ||
|
844
|
+
# %%% Used to have the more flexible: (DidYouMean::SpellChecker.new(dictionary: res_names.keys).correct(possible_c_path)).first
|
845
|
+
res_names[possible_c_path] || res_names[possible_c_path_singular.join('.')]
|
846
|
+
) &&
|
847
|
+
(
|
848
|
+
klass ||
|
849
|
+
((rel = ::Brick.relations.fetch(res_name, nil)) &&
|
850
|
+
(klass ||= rel[:class_name]&.constantize))
|
851
|
+
)
|
852
|
+
c_path_parts.shift
|
853
|
+
end
|
854
|
+
if klass
|
855
|
+
type_col = klass.inheritance_column # Usually 'type'
|
856
|
+
filter_parts << "#{type_col}=#{sti_type}" if sti_type && klass.column_names.include?(type_col)
|
857
|
+
path_params = request.path_parameters.dup
|
858
|
+
path_params.delete(:controller)
|
859
|
+
path_params.delete(:action)
|
860
|
+
pk = (klass.primary_key || ActiveRecord::Base.primary_key).to_sym
|
861
|
+
# 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) ||
|
862
|
+
if ((id = (path_params[pk] || path_params[:id] || path_params["#{klass.name.underscore}_id".to_sym])) && (obj = klass.find_by(pk => id))) ||
|
863
|
+
(['show', 'edit'].include?(action_name) && (obj = klass.first))
|
864
|
+
obj
|
865
|
+
else
|
866
|
+
# %%% If there is a HMT that refers to some ___id then try to identify an appropriate filter
|
867
|
+
# %%% If there is a polymorphic association that might relate to stuff in the path_params,
|
868
|
+
# try to identify an appropriate ___able_id and ___able_type filter
|
869
|
+
((klass.column_names - [pk.to_s]) & path_params.keys.map(&:to_s)).each do |path_param|
|
870
|
+
filter_parts << "#{path_param}=#{path_params[path_param.to_sym]}"
|
871
|
+
end
|
872
|
+
klass
|
873
|
+
end
|
874
|
+
end
|
875
|
+
rescue
|
876
|
+
end
|
877
|
+
if klass_or_obj
|
878
|
+
if klass_or_obj.is_a?(ActiveRecord::Relation)
|
879
|
+
klass_or_obj.where_values_hash.each do |whr|
|
880
|
+
filter_parts << "#{whr.first}=#{whr.last}" unless whr.last.is_a?(Array)
|
881
|
+
end
|
882
|
+
klass_or_obj = klass_or_obj.klass
|
883
|
+
end
|
884
|
+
filter = "?#{filter_parts.join('&')}" if filter_parts.present?
|
885
|
+
if klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base
|
886
|
+
lt_args = [text || "Index for #{klass_or_obj.name.pluralize}",
|
887
|
+
"#{send("#{klass_or_obj._brick_index}_path")}#{filter}"]
|
888
|
+
else
|
889
|
+
# 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
|
890
|
+
lt_args = [text || "Show this #{klass_or_obj.class.name}",
|
891
|
+
"#{send("#{klass_or_obj.class._brick_index(:singular)}_path", klass_or_obj)}#{filter}"]
|
892
|
+
end
|
893
|
+
link_to(*lt_args, **kwargs)
|
894
|
+
else
|
895
|
+
# puts "Warning: link_to_brick could not find a class for \"#{controller_path}\" -- consider setting @_brick_model within that controller."
|
896
|
+
# if (hits = res_names.keys & instance_variables.map { |v| v.to_s[1..-1] }).present?
|
897
|
+
links = instance_variables.each_with_object([]) do |name, s|
|
898
|
+
iv_name = name.to_s[1..-1]
|
899
|
+
case (val = instance_variable_get(name))
|
900
|
+
when ActiveRecord::Relation, ActiveRecord::Base
|
901
|
+
s << link_to_brick(val, iv_name) if val
|
902
|
+
end
|
903
|
+
end
|
904
|
+
links.join(' ').html_safe
|
905
|
+
end
|
906
|
+
end
|
907
|
+
end
|
908
|
+
end
|
909
|
+
|
808
910
|
if ActiveSupport::Dependencies.respond_to?(:autoload_module!) # %%% Only works with previous non-zeitwerk auto-loading
|
809
911
|
module ActiveSupport::Dependencies
|
810
912
|
class << self
|
@@ -1138,6 +1240,7 @@ class Object
|
|
1138
1240
|
end # class definition
|
1139
1241
|
# Having this separate -- will this now work out better?
|
1140
1242
|
built_model.class_exec do
|
1243
|
+
@_brick_built = true
|
1141
1244
|
hmts&.each do |hmt_fk, hms|
|
1142
1245
|
hmt_fk = hmt_fk.tr('.', '_')
|
1143
1246
|
hms.each do |hm|
|
@@ -130,15 +130,15 @@ module Brick
|
|
130
130
|
next unless @_brick_model.instance_methods.include?(through) &&
|
131
131
|
(associative = @_brick_model._br_associatives.fetch(hm.first, nil))
|
132
132
|
|
133
|
-
tbl_nm = if hm_assoc.
|
134
|
-
|
133
|
+
tbl_nm = if (source = hm_assoc.source_reflection).macro == :belongs_to
|
134
|
+
hm_assoc.through_reflection&.name # for standard HMT, which is HM -> BT
|
135
135
|
else
|
136
|
-
|
136
|
+
source.inverse_of&.name # For HM -> HM style HMT
|
137
137
|
end
|
138
138
|
# If there is no inverse available for the source belongs_to association, make one based on the class name
|
139
139
|
unless tbl_nm
|
140
140
|
tbl_nm = associative.class_name.underscore
|
141
|
-
tbl_nm.slice!(0) if tbl_nm[0] ==
|
141
|
+
tbl_nm.slice!(0) if tbl_nm[0] == '/'
|
142
142
|
tbl_nm = tbl_nm.tr('/', '_').pluralize
|
143
143
|
end
|
144
144
|
"'#{tbl_nm}.#{associative.foreign_key}'"
|
@@ -166,7 +166,13 @@ module Brick
|
|
166
166
|
hms_columns << hm_entry
|
167
167
|
when 'show', 'new', 'update'
|
168
168
|
hm_stuff << if hm_fk_name
|
169
|
-
|
169
|
+
if hm_assoc.klass.column_names.include?(hm_fk_name)
|
170
|
+
"<%= link_to '#{assoc_name}', #{hm_assoc.klass._brick_index}_path({ #{path_keys(hm_assoc, hm_fk_name, "@#{obj_name}", pk)} }) %>\n"
|
171
|
+
else
|
172
|
+
puts "Warning: has_many :#{hm_assoc.name} in model #{hm_assoc.active_record.name} currently looks for a foreign key called \"#{hm_assoc.foreign_key}\". "\
|
173
|
+
"Instead it should use the clause \"foreign_key: :#{hm_assoc.inverse_of&.foreign_key}\"."
|
174
|
+
assoc_name
|
175
|
+
end
|
170
176
|
else # %%% Would be able to remove this when multiple foreign keys to same destination becomes bulletproof
|
171
177
|
assoc_name
|
172
178
|
end
|
@@ -1331,10 +1337,18 @@ document.querySelectorAll(\"input, select\").forEach(function (inp) {
|
|
1331
1337
|
prepend ::Brick::RouteSet
|
1332
1338
|
end
|
1333
1339
|
# Do the root route before the Rails Welcome one would otherwise take precedence
|
1334
|
-
|
1335
|
-
|
1336
|
-
::
|
1337
|
-
|
1340
|
+
if (route = ::Brick.config.default_route_fallback).present?
|
1341
|
+
action = "#{route}#{'#index' unless route.index('#')}"
|
1342
|
+
if ::Brick.config.path_prefix
|
1343
|
+
::Rails.application.routes.append do
|
1344
|
+
send(:namespace, ::Brick.config.path_prefix) do
|
1345
|
+
send(:root, action)
|
1346
|
+
end
|
1347
|
+
end
|
1348
|
+
elsif ::Rails.application.routes.named_routes.send(:routes)[:root].nil?
|
1349
|
+
::Rails.application.routes.append do
|
1350
|
+
send(:root, action)
|
1351
|
+
end
|
1338
1352
|
end
|
1339
1353
|
end
|
1340
1354
|
end
|
data/lib/brick/version_number.rb
CHANGED
data/lib/brick.rb
CHANGED
@@ -113,15 +113,15 @@ if Gem::Specification.all_names.any? { |g| g.start_with?('rails-') }
|
|
113
113
|
require 'brick/frameworks/rails'
|
114
114
|
end
|
115
115
|
module Brick
|
116
|
-
|
117
|
-
|
118
|
-
|
116
|
+
class << self
|
117
|
+
def sti_models
|
118
|
+
@sti_models ||= {}
|
119
|
+
end
|
119
120
|
|
120
|
-
|
121
|
-
|
122
|
-
|
121
|
+
def existing_stis
|
122
|
+
@existing_stis ||= Brick.config.sti_namespace_prefixes.each_with_object({}) { |snp, s| s[snp.first[2..-1]] = snp.last unless snp.first.end_with?('::') }
|
123
|
+
end
|
123
124
|
|
124
|
-
class << self
|
125
125
|
attr_accessor :default_schema, :db_schemas, :routes_done, :is_oracle, :is_eager_loading, :auto_models
|
126
126
|
|
127
127
|
def set_db_schema(params = nil)
|
@@ -203,11 +203,9 @@ module Brick
|
|
203
203
|
skip_hms = {}
|
204
204
|
hms.each do |hmt|
|
205
205
|
if (through = hmt.last.options[:through])
|
206
|
-
|
207
|
-
|
206
|
+
# ::Brick.relations[hmt.last.through_reflection.table_name]
|
207
|
+
skip_hms[through] = nil if hms[through] && model.is_brick?
|
208
208
|
# End up with a hash of HMT names pointing to join-table associations
|
209
|
-
# Last part was: hmt.last.name
|
210
|
-
# Changed up because looking for: hms[:issue_issue_duplicates]
|
211
209
|
model._br_associatives[hmt.first] = hms[through] # || hms["#{(opt = hmt.last.options)[:through].to_s.singularize}_#{opt[:source].to_s.pluralize}".to_sym]
|
212
210
|
elsif hmt.last.inverse_of.nil?
|
213
211
|
puts "SKIPPING #{hmt.last.name.inspect}"
|
@@ -549,7 +547,11 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
549
547
|
module RouteSet
|
550
548
|
def finalize!
|
551
549
|
unless ::Rails.application.routes.named_routes.route_defined?(:brick_status_path)
|
552
|
-
|
550
|
+
path_prefix = ::Brick.config.path_prefix
|
551
|
+
existing_controllers = routes.each_with_object({}) do |r, s|
|
552
|
+
c = r.defaults[:controller]
|
553
|
+
s[c] = nil if c
|
554
|
+
end
|
553
555
|
::Rails.application.routes.append do
|
554
556
|
tables = []
|
555
557
|
views = []
|
@@ -568,7 +570,7 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
568
570
|
|
569
571
|
# %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
|
570
572
|
# If auto-controllers and auto-models are both enabled then this makes sense:
|
571
|
-
controller_prefix = (
|
573
|
+
controller_prefix = (path_prefix ? "#{path_prefix}/" : '')
|
572
574
|
::Brick.relations.each do |k, v|
|
573
575
|
unless !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
|
574
576
|
options = {}
|
@@ -583,9 +585,9 @@ In config/initializers/brick.rb appropriate entries would look something like:
|
|
583
585
|
send(:get, "#{::Brick.api_root}#{v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
|
584
586
|
end
|
585
587
|
# Now the normal routes
|
586
|
-
if
|
587
|
-
# Was: send(:scope, path:
|
588
|
-
send(:namespace,
|
588
|
+
if path_prefix
|
589
|
+
# Was: send(:scope, path: path_prefix) do
|
590
|
+
send(:namespace, path_prefix) do
|
589
591
|
brick_routes_create.call(schema_name, controller_name, v, options)
|
590
592
|
end
|
591
593
|
else
|
@@ -298,7 +298,9 @@ if Object.const_defined?('Brick')
|
|
298
298
|
# # route to go to the :index action for what would be a controller for that table. You can specify any controller
|
299
299
|
# # name and action you wish in order to override this and have that be the default route when none other has been
|
300
300
|
# # specified in routes.rb or elsewhere. (Or just use an empty string in order to disable this behaviour.)
|
301
|
-
#
|
301
|
+
# This defaults to \"customers#index\", and if there was also a prefix set called \"admin\" then it would instead
|
302
|
+
# go to \"admin/customers#index\".
|
303
|
+
# Brick.default_route_fallback = 'customers'
|
302
304
|
# Brick.default_route_fallback = 'orders#outstanding' # Example of a non-RESTful route
|
303
305
|
# Brick.default_route_fallback = '' # Omits setting a default route in the absence of any other
|
304
306
|
end
|
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.83
|
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-10-
|
11
|
+
date: 2022-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|