brick 1.0.153 → 1.0.155

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc98614593bc4e4931a445801f4f7764c15e6032daf2765f8d78a47f61b7b3a7
4
- data.tar.gz: 5b3ae1d718df14101c153b335a66578a5b02771714dedd7ebda1022cadebe482
3
+ metadata.gz: fceffbcf863eb9c03b64a0cb4420a33ded5eb7f3908b40b263a345ae9bf5c164
4
+ data.tar.gz: 81f9f96bd1463b8b7e7736ed4716bcf78609bae27485af4de15cb23ae12e27ee
5
5
  SHA512:
6
- metadata.gz: 15852e3d59a95d5ac8614d98a44a70796c5494610447505abe61dcd02010c3701dc9ba48da951e4639e4fa14694cfee4c7455c2a0bd63fc613738886ca908b26
7
- data.tar.gz: bd5cc34b7c004461af1737dbcb4b746889bd7f4da7d5242c3f341793ed4675c33a50bc61108a6f1e24037585298361f8f4f6ac699a25a72c4ec860f1c272e0ba
6
+ metadata.gz: c41a59ca62c80012166225b0cf80505b81fe57ccdfeb92c4add7f227d6d4a9cc0c664aaf5209c31dabd54c3e471fe140fd3dc75bf1441bbed0fca67e9801ffd9
7
+ data.tar.gz: 83c874515b230b0b607da32e41366284b15e830ebd7f7022fb318db412fef94a0339495de54c9ab0277425af2635cc8ab235675074cbb50f703082979ebd28b5
data/lib/brick/config.rb CHANGED
@@ -230,6 +230,14 @@ module Brick
230
230
  @mutex.synchronize { @json_columns = cols }
231
231
  end
232
232
 
233
+ def sidescroll
234
+ @mutex.synchronize { @sidescroll ||= {} }
235
+ end
236
+
237
+ def sidescroll=(scroll)
238
+ @mutex.synchronize { @sidescroll = scroll }
239
+ end
240
+
233
241
  def model_descrips
234
242
  @mutex.synchronize { @model_descrips ||= {} }
235
243
  end
@@ -372,6 +380,14 @@ module Brick
372
380
  @mutex.synchronize { @always_load_fields = field_set }
373
381
  end
374
382
 
383
+ def ignore_migration_fks
384
+ @mutex.synchronize { @ignore_migration_fks || [] }
385
+ end
386
+
387
+ def ignore_migration_fks=(relations)
388
+ @mutex.synchronize { @ignore_migration_fks = relations }
389
+ end
390
+
375
391
  # Add status page showing all resources and what files have been built out for them
376
392
  def add_status
377
393
  true
@@ -864,6 +864,10 @@ tr th {
864
864
  color: #fff;
865
865
  text-align: left;
866
866
  }
867
+ .col-sticky {
868
+ position: sticky;
869
+ left: 0;
870
+ }
867
871
  #headerTop tr th {
868
872
  position: relative;
869
873
  }
@@ -902,6 +906,10 @@ tr td.highlight {
902
906
  background-color: #B0B0FF;
903
907
  }
904
908
 
909
+ table tr .col-sticky {
910
+ background-color: #28B898;
911
+ }
912
+
905
913
  .show-field {
906
914
  background-color: #004998;
907
915
  }
@@ -916,6 +924,12 @@ table.shadow > tbody > tr {
916
924
  table tbody tr:nth-of-type(even) {
917
925
  background-color: #f3f3f3;
918
926
  }
927
+ table tbody tr:nth-of-type(even) .col-sticky {
928
+ background-color: #fff;
929
+ }
930
+ table tbody tr:nth-of-type(odd) .col-sticky {
931
+ background-color: #f3f3f3;
932
+ }
919
933
 
920
934
  table.shadow > tbody > tr:last-of-type {
921
935
  border-bottom: 2px solid #009879;
@@ -1080,18 +1094,30 @@ function setHeaderSizes() {
1080
1094
  // %%% Grab the TRs from headerTop, clear it out, do this stuff, add them back
1081
1095
  headerTop.innerHTML = \"\"; // %%% Would love to not have to clear it out like this every time! (Currently doing this to support resize events.)
1082
1096
  var isEmpty = headerTop.childElementCount === 0;
1097
+ var numFixed = parseInt(grid.getAttribute(\"x-num-frozen\")) || 0;
1098
+ var fixedColLefts = [0];
1099
+
1083
1100
  // Set up proper sizings of sticky column header
1084
1101
  var node;
1085
1102
  for (var j = 0; j < #{table_name}HtColumns.length; ++j) {
1086
1103
  var row = #{table_name}HtColumns[j];
1087
1104
  var tr = isEmpty ? document.createElement(\"TR\") : headerTop.childNodes[j];
1088
1105
  tr.innerHTML = row.innerHTML.trim();
1106
+ var curLeft = 0.0;
1089
1107
  // Match up widths from the original column headers
1090
1108
  for (var i = 0; i < row.childNodes.length; ++i) {
1091
1109
  node = row.childNodes[i];
1092
1110
  if (node.nodeType === 1) {
1093
1111
  var th = tr.childNodes[i];
1094
1112
  th.style.minWidth = th.style.maxWidth = getComputedStyle(node).width;
1113
+ // Add \"left: __px\" style to the fixed-width column THs
1114
+ if (i <= numFixed) {
1115
+ th.style.position = \"sticky\";
1116
+ th.style.backgroundColor = \"#008061\";
1117
+ th.style.zIndex = \"1\";
1118
+ th.style.left = curLeft + \"px\";
1119
+ fixedColLefts.push(curLeft += node.clientWidth);
1120
+ }
1095
1121
  if (#{pk&.present? ? 'i > 0' : 'true'}) {
1096
1122
  // Add <span> at the end
1097
1123
  var span = document.createElement(\"SPAN\");
@@ -1108,6 +1134,12 @@ function setHeaderSizes() {
1108
1134
  headerCols = tr.childNodes;
1109
1135
  if (isEmpty) headerTop.appendChild(tr);
1110
1136
  }
1137
+ // Add \"left: __px\" style to all fixed-width column TDs
1138
+ [...grid.children[1].children].forEach(function (row) {
1139
+ for (var j = 1; j <= numFixed; ++j) {
1140
+ row.children[j].style.left = fixedColLefts[j] + 'px';
1141
+ }
1142
+ });
1111
1143
  grid.style.marginTop = \"-\" + getComputedStyle(headerTop).height;
1112
1144
  // console.log(\"end\");
1113
1145
  }
@@ -24,8 +24,11 @@ module Brick::Rails::FormTags
24
24
  end
25
25
  end
26
26
 
27
+ nfc = Brick.config.sidescroll.fetch(relation.table_name, nil)&.fetch(:num_frozen_columns, nil) ||
28
+ Brick.config.sidescroll.fetch(:num_frozen_columns, nil) ||
29
+ 0
27
30
  out = "<table id=\"headerTop\"></table>
28
- <table id=\"#{relation.table_name.split('.').last}\" class=\"shadow\">
31
+ <table id=\"#{relation.table_name.split('.').last}\" class=\"shadow\"#{ " x-num-frozen=\"#{nfc}\"" if nfc.positive? }>
29
32
  <thead><tr>"
30
33
  pk = (klass = relation.klass).primary_key || []
31
34
  pk = [pk] unless pk.is_a?(Array)
@@ -95,12 +98,13 @@ module Brick::Rails::FormTags
95
98
  # (After restarting the server it worked fine again.)
96
99
  relation.each do |obj|
97
100
  out << "<tr>\n"
98
- out << "<td>#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
101
+ out << "<td class=\"col-sticky\">#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym,
99
102
  pk.map { |pk_part| obj.send(pk_part.to_sym) }), { class: 'big-arrow' })}</td>\n" if pk.present?
100
- sequence.each do |col_name|
103
+ sequence.each_with_index do |col_name, idx|
101
104
  val = obj.attributes[col_name]
102
105
  bt = bts[col_name]
103
106
  out << '<td'
107
+ (classes ||= []) << 'col-sticky' if idx < nfc
104
108
  (classes ||= []) << 'dimmed' unless cols.key?(col_name) || (cust_col = cust_cols[col_name]) ||
105
109
  (col_name.is_a?(Symbol) && bts.key?(col_name)) # HOT
106
110
  (classes ||= []) << 'right' if val.is_a?(Numeric) && !bt
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 153
8
+ TINY = 155
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
data/lib/brick.rb CHANGED
@@ -501,6 +501,11 @@ module Brick
501
501
  Brick.config.json_columns = cols
502
502
  end
503
503
 
504
+ # @api public
505
+ def sidescroll=(scroll)
506
+ Brick.config.sidescroll = scroll
507
+ end
508
+
504
509
  # DSL templates for individual models to provide prettier descriptions of objects
505
510
  # @api public
506
511
  def model_descrips=(descrips)
@@ -1159,6 +1164,12 @@ ActiveSupport.on_load(:active_record) do
1159
1164
  mattr_accessor :default_timezone, instance_writer: false
1160
1165
  self.default_timezone = :utc
1161
1166
  end
1167
+
1168
+ unless respond_to?(:primary_abstract_class)
1169
+ def self.primary_abstract_class
1170
+ self.abstract_class = true
1171
+ end
1172
+ end
1162
1173
  end
1163
1174
 
1164
1175
  # Rails < 4.0 cannot do #find_by, #find_or_create_by, or do #pluck on multiple columns, so here are the patches:
@@ -35,7 +35,7 @@ module Brick
35
35
  end
36
36
  end
37
37
  possible_additional_references = relations.each_with_object(Hash.new { |h, k| h[k] = [] }) do |relation, s|
38
- this_tnp = tnps&.keys.find { |tnp| relation.first.start_with?(tnp) }
38
+ this_tnp = tnps&.keys&.find { |tnp| relation.first.start_with?(tnp) }
39
39
  model_filename = "app/models/#{ActiveSupport::Inflector.singularize(relation.first)}.rb"
40
40
  relation.last[:cols].each do |col, type|
41
41
  col_down = col.downcase
@@ -244,6 +244,14 @@ if ActiveRecord::Base.respond_to?(:brick_select) && !::Brick.initializer_loaded
244
244
  # Brick.column_sequence = { 'users' => { include: ['email', 'profile.firstname', 'profile.lastname'] },
245
245
  # 'profile' => { exclude: ['birthdate'] } }
246
246
 
247
+ # # When rendering the grid on index pages, a default number of columns to keep as \"sticky\" so that they remain
248
+ # # at the left of the grid while scrolling. By default this is 0 extra columns -- only the link to that
249
+ # # object's show / edit page is sticky. And this would add one extra column in the mix:
250
+ # Brick.sidescroll = { num_frozen_columns: 1 }
251
+ # # As well if you would like to customise this for specific resources, that is possible:
252
+ # Brick.sidescroll = { num_frozen_columns: 0,
253
+ # 'products' => { num_frozen_columns: 2 } }
254
+
247
255
  # # EXTRA FOREIGN KEYS AND OTHER HAS_MANY SETTINGS
248
256
 
249
257
  # # Additional table references which are used to create has_many / belongs_to associations inside auto-created
@@ -123,21 +123,27 @@ module Brick
123
123
  built_schemas = {} # Track all built schemas so we can place an appropriate drop_schema command only in the first
124
124
  # migration in which that schema is referenced, thereby allowing rollbacks to function properly.
125
125
  versions_to_create = [] # Resulting versions to be used when updating the schema_migrations table
126
+ ar_base = Object.const_defined?(:ApplicationRecord) ? ApplicationRecord : Class.new(ActiveRecord::Base)
126
127
  # Start by making migrations for fringe tables (those with no foreign keys).
127
128
  # Continue layer by layer, creating migrations for tables that reference ones already done, until
128
129
  # no more migrations can be created. (At that point hopefully all tables are accounted for.)
129
130
  while (fringe = chosen.reject do |tbl|
131
+ snag_fks = []
130
132
  snags = ::Brick.relations.fetch(tbl, nil)&.fetch(:fks, nil)&.select do |_k, v|
131
133
  v[:is_bt] && !v[:polymorphic] &&
132
134
  tbl != v[:inverse_table] && # Ignore self-referencing associations (stuff like "parent_id")
133
- !done.include?(v[:inverse_table])
135
+ !done.include?(v[:inverse_table]) &&
136
+ ::Brick.config.ignore_migration_fks.exclude?(snag_fk = "#{tbl}.#{v[:fk]}") &&
137
+ snag_fks << snag_fk
138
+ end
139
+ if snags&.present?
140
+ # puts snag_fks.inspect
141
+ stuck[tbl] = snags
134
142
  end
135
- stuck[tbl] = snags if snags&.present?
136
143
  end).present?
137
144
  fringe.each do |tbl|
138
145
  next unless (relation = ::Brick.relations.fetch(tbl, nil))&.fetch(:cols, nil)&.present?
139
146
 
140
- ar_base = Object.const_defined?(:ApplicationRecord) ? ApplicationRecord : Class.new(ActiveRecord::Base)
141
147
  pkey_cols = (rpk = relation[:pkey].values.flatten) & (arpk = [ar_base.primary_key].flatten.sort)
142
148
  # In case things aren't as standard
143
149
  if pkey_cols.empty?
@@ -246,7 +252,8 @@ module Brick
246
252
  suffix << ", index: { name: '#{shorter || idx_name}' }"
247
253
  indexes[shorter || idx_name] = nil
248
254
  end
249
- mig << " t.references :#{fk[:assoc_name]}#{suffix}, foreign_key: { to_table: #{to_table} }\n"
255
+ primary_key = ::Brick.relations[fk[:inverse_table]][:class_name]&.constantize&.primary_key
256
+ mig << " t.references :#{fk[:assoc_name]}#{suffix}, foreign_key: { to_table: #{to_table}#{", primary_key: :#{primary_key}" if primary_key != ar_base.primary_key} }\n"
250
257
  end
251
258
  else
252
259
  next if !id_option&.end_with?('id: false') && pkey_cols&.include?(col)
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'fancy_gets'
5
+
6
+ module Brick
7
+ class SeedsGenerator < ::Rails::Generators::Base
8
+ include FancyGets
9
+
10
+ desc 'Auto-generates a seeds file from existing data.'
11
+
12
+ def brick_seeds
13
+ # %%% If Apartment is active and there's no schema_to_analyse, ask which schema they want
14
+
15
+ ::Brick.mode = :on
16
+ ActiveRecord::Base.establish_connection
17
+
18
+ if (tables = ::Brick.relations.reject { |k, v| v.key?(:isView) && v[:isView] == true }.map(&:first).sort).empty?
19
+ puts "No tables found in database #{ActiveRecord::Base.connection.current_database}."
20
+ return
21
+ end
22
+
23
+ if File.exist?(seed_file_path = "#{::Rails.root}/db/seeds.rb")
24
+ puts "WARNING: seeds file #{seed_file_path} appears to already be present."
25
+ end
26
+
27
+ # Generate a list of tables that can be chosen
28
+ chosen = gets_list(list: tables, chosen: tables.dup)
29
+ schemas = chosen.each_with_object({}) do |v, s|
30
+ if (v_parts = v.split('.')).length > 1
31
+ s[v_parts.first] = nil unless [::Brick.default_schema, 'public'].include?(v_parts.first)
32
+ end
33
+ end
34
+ seeds = +"# Seeds file for #{ActiveRecord::Base.connection.current_database}:"
35
+ done = []
36
+ fks = {}
37
+ stuck = {}
38
+ indexes = {} # Track index names to make sure things are unique
39
+ ar_base = Object.const_defined?(:ApplicationRecord) ? ApplicationRecord : Class.new(ActiveRecord::Base)
40
+ # Start by making entries for fringe models (those with no foreign keys).
41
+ # Continue layer by layer, creating entries for models that reference ones already done, until
42
+ # no more entries can be created. (At that point hopefully all models are accounted for.)
43
+ while (fringe = chosen.reject do |tbl|
44
+ snag_fks = []
45
+ snags = ::Brick.relations.fetch(tbl, nil)&.fetch(:fks, nil)&.select do |_k, v|
46
+ v[:is_bt] && !v[:polymorphic] &&
47
+ tbl != v[:inverse_table] && # Ignore self-referencing associations (stuff like "parent_id")
48
+ !done.include?(v[:inverse_table]) &&
49
+ ::Brick.config.ignore_migration_fks.exclude?(snag_fk = "#{tbl}.#{v[:fk]}") &&
50
+ snag_fks << snag_fk
51
+ end
52
+ if snags&.present?
53
+ # puts snag_fks.inspect
54
+ stuck[tbl] = snags
55
+ end
56
+ end).present?
57
+ seeds << "\n"
58
+ fringe.each do |tbl|
59
+ next unless ::Brick.config.exclude_tables.exclude?(tbl) &&
60
+ (relation = ::Brick.relations.fetch(tbl, nil))&.fetch(:cols, nil)&.present? &&
61
+ (klass = Object.const_get(class_name = relation[:class_name])).table_exists?
62
+
63
+ pkey_cols = (rpk = relation[:pkey].values.flatten) & (arpk = [ar_base.primary_key].flatten.sort)
64
+ # In case things aren't as standard
65
+ if pkey_cols.empty?
66
+ pkey_cols = if rpk.empty? # && relation[:cols][arpk.first]&.first == key_type
67
+ arpk
68
+ elsif rpk.first
69
+ rpk
70
+ end
71
+ end
72
+ schema = if (tbl_parts = tbl.split('.')).length > 1
73
+ if tbl_parts.first == (::Brick.default_schema || 'public')
74
+ tbl_parts.shift
75
+ nil
76
+ else
77
+ tbl_parts.first
78
+ end
79
+ end
80
+
81
+ # %%% For the moment we're skipping polymorphics
82
+ fkeys = relation[:fks].values.select { |assoc| assoc[:is_bt] && !assoc[:polymorphic] }
83
+ # Refer to this table name as a symbol or dotted string as appropriate
84
+ # tbl_code = tbl_parts.length == 1 ? ":#{tbl_parts.first}" : "'#{tbl}'"
85
+ seeds << " # #{class_name}\n"
86
+
87
+ is_empty = true
88
+ klass.order(*pkey_cols).each do |obj|
89
+ is_empty = false
90
+ pk_val = obj.send(pkey_cols.first)
91
+ fk_vals = []
92
+ data = []
93
+ relation[:cols].each do |col, col_type|
94
+ next if !(fk = fkeys.find { |assoc| col == assoc[:fk] }) &&
95
+ pkey_cols.include?(col)
96
+
97
+ if (val = obj.send(col)) && (val.is_a?(Time) || val.is_a?(Date))
98
+ val = val.to_s
99
+ end
100
+ if fk
101
+ fk_vals << "#{fk[:assoc_name]}: #{fk[:inverse_table]}_#{val.inspect}" if val
102
+ else
103
+ data << "#{col}: #{val.inspect}"
104
+ end
105
+ end
106
+ seeds << "#{tbl}_#{pk_val} = #{class_name}.create(#{(fk_vals + data).join(', ')})\n"
107
+ end
108
+ File.open(seed_file_path, "w") { |f| f.write seeds }
109
+ end
110
+ done.concat(fringe)
111
+ chosen -= done
112
+ end
113
+ puts "\n*** Created seeds for #{done.length} models in db/seeds.rb ***"
114
+ end
115
+ end
116
+ 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.153
4
+ version: 1.0.155
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-24 00:00:00.000000000 Z
11
+ date: 2023-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -257,6 +257,7 @@ files:
257
257
  - lib/generators/brick/install_generator.rb
258
258
  - lib/generators/brick/migrations_generator.rb
259
259
  - lib/generators/brick/models_generator.rb
260
+ - lib/generators/brick/seeds_generator.rb
260
261
  - lib/generators/brick/templates/add_object_changes_to_versions.rb.erb
261
262
  - lib/generators/brick/templates/create_versions.rb.erb
262
263
  homepage: https://github.com/lorint/brick