brick 1.0.153 → 1.0.155

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