brick 1.0.219 → 1.0.221

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: 78f81d0e3624f8e2b3e387067bf9239134339bdbff20c3386e21c9751e0658a3
4
- data.tar.gz: 301504fb244c153b408addb0651778f1e80d970a19ca1b429e5954fb7deadffe
3
+ metadata.gz: b4aaaeaadb65aad58b8eac139c9e0fa5c8a68995a15151149fdc49196563db7e
4
+ data.tar.gz: f2ad0ef65556495ac9d531e499ca8b38a532aab641d0e46aeca4ea5fc535efd0
5
5
  SHA512:
6
- metadata.gz: '09d2f69ed9f7d8c7f786ab7910d425ef81a09219b1e9388ed7bf1b9369e389653388498f03142dd4f315e32dcc08680e1dbed133bfab648abd78d837019abce2'
7
- data.tar.gz: f3f729e660db050dfa0bf970e62ec2b3be1b77a77759c46f6a11fda10d5623b862a926368237e06d60bf1433b3102a81ff17ef89189fffbed4a8bf637a6f451a
6
+ metadata.gz: 0f09da443c9427e5dd0b442f626bfbeb23011bceec74e6a9a9d52d28506f823e8d4d3fcf9cfdeb2af6a4b67f3b41552b09a23df18469831b2844a718adcee1a7
7
+ data.tar.gz: 07ed1b5a35404a584c79b7c9002159832a4a5ccacc5db330ee41b7ec7ed49a2011eee27c921e5db4cb2f887e33cfbf3ef97a0f66fab5b765a2168ba350ac1c39
data/lib/brick/config.rb CHANGED
@@ -482,6 +482,11 @@ module Brick
482
482
  @mutex.synchronize { @ignore_migration_fks = relations }
483
483
  end
484
484
 
485
+ # Add search page for general Elasticsearch / Opensearch querying
486
+ def add_search
487
+ true
488
+ end
489
+
485
490
  # Add status page showing all resources and what files have been built out for them
486
491
  def add_status
487
492
  true
@@ -744,7 +744,6 @@ module ActiveRecord
744
744
  end
745
745
  end
746
746
 
747
- # core_selects = selects.dup
748
747
  id_for_tables = Hash.new { |h, k| h[k] = [] }
749
748
  field_tbl_names = Hash.new { |h, k| h[k] = {} }
750
749
  used_col_aliases = {} # Used to make sure there is not a name clash
@@ -758,7 +757,6 @@ module ActiveRecord
758
757
  if @_brick_rel_dup.respond_to?(k) # Name already taken?
759
758
  # %%% Use ensure_unique here in this kind of fashion:
760
759
  # cnstr_name = ensure_unique(+"(brick) #{for_tbl}_#{pri_tbl}", nil, bts, hms)
761
- # binding.pry
762
760
  next
763
761
  end
764
762
 
@@ -1290,8 +1288,9 @@ Might want to add this in your brick.rb:
1290
1288
  end
1291
1289
 
1292
1290
  if Object.const_defined?('ActionView')
1293
- require 'brick/frameworks/rails/form_tags'
1294
- require 'brick/frameworks/rails/form_builder'
1291
+ require 'brick/rails'
1292
+ require 'brick/rails/form_tags'
1293
+ require 'brick/rails/form_builder'
1295
1294
  module ::ActionView::Helpers
1296
1295
  module FormTagHelper
1297
1296
  include ::Brick::Rails::FormTags
@@ -1480,7 +1479,7 @@ end
1480
1479
  begin
1481
1480
  if plural_class_name == 'BrickOpenapi' ||
1482
1481
  (
1483
- (::Brick.config.add_status || ::Brick.config.add_orphans) &&
1482
+ (::Brick.config.add_search || ::Brick.config.add_status || ::Brick.config.add_orphans) &&
1484
1483
  plural_class_name == 'BrickGem'
1485
1484
  # Was: ) || (model = self.const_get(full_class_name))
1486
1485
  ) || (model = Object.const_get(full_class_name))
@@ -1741,6 +1740,52 @@ class Object
1741
1740
  built_model = Class.new(base_model) do |new_model_class|
1742
1741
  (schema_module || Object).const_set(chosen_name, new_model_class) unless is_generator
1743
1742
  @_brick_relation = relation
1743
+ # Enable Elasticsearch for this one?
1744
+ access = ::Brick.elasticsearch_models&.is_a?(Hash) ? ::Brick.elasticsearch_models[name] : ::Brick.elasticsearch_models
1745
+ @_brick_es_crud ||= case access
1746
+ when String
1747
+ access
1748
+ when :all, :full
1749
+ 'icrud' # Auto-create index, and full CRUD
1750
+ else
1751
+ ''
1752
+ end
1753
+ unless @_brick_es_crud.blank?
1754
+ include ::Elasticsearch::Model
1755
+ code << " include Elasticsearch::Model\n"
1756
+ if @_brick_es_crud.index('i') # Enable auto-creation of indexes on import?
1757
+ class << self
1758
+ alias _original_import import
1759
+ def import(options={}, &block)
1760
+ self.__elasticsearch__.create_index! unless self.__elasticsearch__.index_exists?
1761
+ _original_import(options={}, &block)
1762
+ end
1763
+ end
1764
+ end
1765
+ if @_brick_es_crud.index('c') || @_brick_es_crud.index('u') || @_brick_es_crud.index('d')
1766
+ include ::Elasticsearch::Model::Callbacks
1767
+ code << " include Elasticsearch::Model::Callbacks\n"
1768
+ end
1769
+ if @_brick_es_crud.index('r')
1770
+ # Customer.__elasticsearch__.search('taco').to_a
1771
+ end
1772
+ # # Need some mappings for text columns
1773
+ # mappings do
1774
+ # indexes :company_name, type: 'text'
1775
+ # indexes :country, type: 'text'
1776
+ # end
1777
+ # def self.search(q)
1778
+ # s = self.__elasticsearch__.search(q)
1779
+ # binding.pry
1780
+ # s.to_a
1781
+ # # class Elasticsearch::Model::Response::Response
1782
+ # # def to_a
1783
+ # # end
1784
+ # # rescue Elastic::Transport::Transport::Errors::NotFound => e
1785
+ # # self.create_index!
1786
+ # # self.__elasticsearch__.search(q)
1787
+ # end
1788
+ end
1744
1789
  if inheritable_name
1745
1790
  new_model_class.define_singleton_method :inherited do |subclass|
1746
1791
  super(subclass)
@@ -2431,6 +2476,7 @@ class Object
2431
2476
  s << excl_parts.last
2432
2477
  end
2433
2478
  end
2479
+ @_brick_es = real_model.instance_variable_get(:@_brick_es_crud)
2434
2480
  @_brick_bt_descrip = real_model._br_bt_descrip
2435
2481
  @_brick_hm_counts = real_model._br_hm_counts
2436
2482
  @_brick_join_array = join_array
@@ -2530,6 +2576,10 @@ class Object
2530
2576
  session[:_brick_exclude] = excls
2531
2577
  end
2532
2578
  render json: { result: ::Brick.unexclude_column(table_name, col) }
2579
+ elsif is_json && (q = params['_brick_es']) # Elasticsearch
2580
+ # Make sure that the index is actually present and we allow reading before attempting
2581
+ es_result = model.__elasticsearch__.search(q).to_a if (es_perms = model.instance_variable_get(:@_brick_es_crud)).index('r')
2582
+ render json: { result: es_result }
2533
2583
  else
2534
2584
  real_model = model.real_model(params)
2535
2585
  singular_table_name = real_model.name.underscore.split('/').last
@@ -582,7 +582,10 @@ window.addEventListener(\"popstate\", linkSchemas);
582
582
  alias :_brick_lookup_context :lookup_context
583
583
  def lookup_context(*args)
584
584
  ret = _brick_lookup_context(*args)
585
- @_lookup_context.instance_variable_set(:@_brick_req_params, params) if self.class < AbstractController::Base && request && params.present?
585
+ if self.class < AbstractController::Base
586
+ request if respond_to?(:request) # ActionMailer does not have +request+
587
+ @_lookup_context.instance_variable_set(:@_brick_req_params, params) if request && params.present?
588
+ end
586
589
  ret
587
590
  end
588
591
  end
@@ -592,12 +595,14 @@ window.addEventListener(\"popstate\", linkSchemas);
592
595
  # Used by Rails 5.0 and above
593
596
  alias :_brick_template_exists? :template_exists?
594
597
  def template_exists?(*args, **options)
598
+ (::Brick.config.add_search && args.first == 'search') ||
595
599
  (::Brick.config.add_status && args.first == 'status') ||
596
600
  (::Brick.config.add_orphans && args.first == 'orphans') ||
597
601
  (args.first == 'crosstab') ||
598
602
  _brick_template_exists?(*args, **options) ||
599
- # Do not auto-create a template when it's searching for an application.html.erb, which comes in like: ["edit", ["games", "application"]]
600
- ((args[1].length == 1 || args[1][-1] != 'application') &&
603
+ # By default do not auto-create a template when it's searching for an application.html.erb, which comes in like: ["edit", ["games", "application"]]
604
+ # (Although specifying a class name for controllers_inherit_from will override this.)
605
+ ((args[1].length == 1 || ::Brick.config.controllers_inherit_from.present? || args[1][-1] != 'application') &&
601
606
  set_brick_model(args, @_brick_req_params))
602
607
  end
603
608
 
@@ -654,6 +659,7 @@ window.addEventListener(\"popstate\", linkSchemas);
654
659
  def find_template(*args, **options)
655
660
  find_template_err = nil
656
661
  unless (model_name = @_brick_model&.name) ||
662
+ (is_search = ::Brick.config.add_search && args[0..1] == ['search', ['brick_gem']]) ||
657
663
  (is_status = ::Brick.config.add_status && args[0..1] == ['status', ['brick_gem']]) ||
658
664
  (is_orphans = ::Brick.config.add_orphans && args[0..1] == ['orphans', ['brick_gem']]) ||
659
665
  (is_crosstab = args[0..1] == ['crosstab', ['brick_gem']])
@@ -775,10 +781,14 @@ window.addEventListener(\"popstate\", linkSchemas);
775
781
  end
776
782
  s << "<option value=\"#{::Brick._brick_index(rel.first, nil, '/', nil, true)}\">#{rel.first}#{rowcount}</option>"
777
783
  end.html_safe
784
+ # Options for special Brick pages
778
785
  prefix = "#{::Brick.config.path_prefix}/" if ::Brick.config.path_prefix
779
- table_options << "<option value=\"#{prefix}brick_status\">(Status)</option>".html_safe if ::Brick.config.add_status
780
- table_options << "<option value=\"#{prefix}brick_orphans\">(Orphans)</option>".html_safe if is_orphans
781
- table_options << "<option value=\"#{prefix}brick_crosstab\">(Crosstab)</option>".html_safe if is_crosstab
786
+ [['Search', ::Brick.config.add_search],
787
+ ['Status', ::Brick.config.add_status],
788
+ ['Orphans', is_orphans],
789
+ ['Crosstab', is_crosstab]].each do |table_option, show_it|
790
+ table_options << "<option value=\"#{prefix}brick_#{table_option.downcase}\">(#{table_option})</option>".html_safe if show_it
791
+ end
782
792
  css = +''
783
793
  css << ::Brick::Rails::BRICK_CSS
784
794
  css << "<script>
@@ -1048,6 +1058,64 @@ end
1048
1058
  });
1049
1059
  });
1050
1060
  </script>
1061
+ <% end
1062
+ # SEARCH BOX
1063
+ if @_brick_es&.index('r') # Must have at least Elasticsearch Read access %>
1064
+ <input type=\"text\" id=\"esSearch\"><a href=\"\">S</a>
1065
+ <script>
1066
+ var esSearch = document.getElementById(\"esSearch\")
1067
+ var isEsFiltered = false;
1068
+ esSearch.addEventListener(\"input\", function () {
1069
+ var gridTrs;
1070
+ if (this.value.length > 2) { # At least 3 letters in the search term
1071
+ var es = doFetch(\"POST\", {_brick_es: this.value},
1072
+ function (p) {p.text().then(function (response) {
1073
+ var result = JSON.parse(response).result;
1074
+ if (result.length > 0) {
1075
+ // Show only rows that have matches
1076
+ gridTrs = [... grid.querySelectorAll(\"tr\")];
1077
+ for (var i = 1; i < gridTrs.length; ++i) {
1078
+ var row = gridTrs[i];
1079
+ // Check all results to see if this one is in the list
1080
+ var rid = row.getAttribute(\"x-id\");
1081
+ var isHit = false;
1082
+ for (var j = 0; j < result.length; ++j) {
1083
+ if (rid == result[j]._id) {
1084
+ isHit = true;
1085
+ break;
1086
+ }
1087
+ }
1088
+ if (!isHit) row.style.display = \"none\";
1089
+ }
1090
+ isEsFiltered = true;
1091
+ } else {
1092
+ if (isEsFiltered) { // Show all rows and gray the search box
1093
+ gridTrs = [... grid.querySelectorAll(\"tr\")];
1094
+ for (var i = 1; i < gridTrs.length; ++i) {
1095
+ gridTrs[i].style.display = \"table-row\";
1096
+ }
1097
+ }
1098
+ }
1099
+ });}
1100
+ );
1101
+ } else {
1102
+ if (isEsFiltered) { // Show all rows and gray the search box
1103
+ gridTrs = [... grid.querySelectorAll(\"tr\")];
1104
+ for (var i = 1; i < gridTrs.length; ++i) {
1105
+ gridTrs[i].style.display = \"table-row\";
1106
+ }
1107
+ }
1108
+ }
1109
+ });
1110
+ esSearch.addEventListener(\"keypress\", function (e) {
1111
+ if (e.keyCode == 13) {
1112
+ // debugger
1113
+ // Go to search results page
1114
+ // var es = doFetch(\"POST\", {_brick_es: this.value});
1115
+ // console.log(es);
1116
+ }
1117
+ });
1118
+ </script>
1051
1119
  <% end %>
1052
1120
  </div></div>
1053
1121
  #{::Brick::Rails.erd_markup(@_brick_model, prefix) if @_brick_model}
@@ -1113,6 +1181,32 @@ end
1113
1181
  </html>
1114
1182
  "
1115
1183
 
1184
+
1185
+ when 'search'
1186
+ if is_search
1187
+ # Search page - query across all indexes that appear to be related to models
1188
+ +"#{css}
1189
+ <p class=\"flashNotice\"><%= notice if request.respond_to?(:flash) %></p>#{"
1190
+ #{schema_options}" if schema_options}
1191
+ <select id=\"tbl\">#{table_options}</select>
1192
+ <h1>Search</h1>
1193
+ <table id=\"resourceName\" class=\"shadow\"><thead><tr>
1194
+ <th>Resource</th>
1195
+ <th>Description</th>
1196
+ <th>ID</th>
1197
+ </tr></thead>
1198
+ <tbody>
1199
+ <% # @results.each do |r| %>
1200
+ <tr>
1201
+ <td><%= %></td>
1202
+ <td<%= %></td>
1203
+ <td<%= %></td>
1204
+ </tr>
1205
+ <% # end %>
1206
+ </tbody></table>
1207
+ #{script}"
1208
+ end
1209
+
1116
1210
  when 'status'
1117
1211
  if is_status
1118
1212
  # Status page - list of all resources and 5 things they do or don't have present, and what is turned on and off
@@ -1167,7 +1261,7 @@ end
1167
1261
  %></td>
1168
1262
  <td<%= ' class=\"dimmed\"'.html_safe unless r[6] %>><%= # Views
1169
1263
  %></td>
1170
- <tr>
1264
+ </tr>
1171
1265
  <% end %>
1172
1266
  </tbody></table>
1173
1267
  #{script}"
@@ -1197,7 +1291,7 @@ end
1197
1291
  decipher.iv = "\xB4,\r2\x19\xF5\xFE/\aR\x1A\x8A\xCFV\v\x8C"
1198
1292
  decipher.key = Digest::SHA256.hexdigest(::Brick.config.license).scan(/../).map { |x| x.hex }.pack('c*')
1199
1293
  brick_path = Gem::Specification.find_by_name('brick').gem_dir
1200
- decipher.update(File.binread("#{brick_path}/lib/brick/frameworks/rails/crosstab.brk"))[16..-1]
1294
+ decipher.update(File.binread("#{brick_path}/lib/brick/rails/crosstab.brk"))[16..-1]
1201
1295
  else
1202
1296
  'Crosstab Charting not yet activated -- enter a valid license key in brick.rb'
1203
1297
  end
@@ -1237,7 +1331,7 @@ end
1237
1331
  end %>#{"
1238
1332
  #{schema_options}" if schema_options}
1239
1333
  <select id=\"tbl\">#{table_options}</select>
1240
- <table id=\"resourceName\"><td><h1><%= page_title %></h1></td>
1334
+ <table id=\"resourceName\"><tr><td><h1><%= page_title %></h1></td>
1241
1335
  <% rel = Brick.relations[#{model_name}.table_name]
1242
1336
  if (in_app = rel.fetch(:existing, nil)&.fetch(:show, nil))
1243
1337
  begin
@@ -1270,7 +1364,7 @@ end %>#{"
1270
1364
  ) %></td>
1271
1365
  <% end
1272
1366
  end %>
1273
- </table>
1367
+ </tr></table>
1274
1368
  <%
1275
1369
  if (description = rel&.fetch(:description, nil)) %>
1276
1370
  <span class=\"__brick\"><%= description %></span><br><%
@@ -99,9 +99,10 @@ module Brick::Rails::FormTags
99
99
  # proxy = relation.instance_variable_get(:@proxy) || relation.instance_variable_set(:@proxy, {})
100
100
  bi = relation.instance_variable_get(:@_brick_includes)
101
101
  relation.each do |obj|
102
- out << "<tr>\n"
103
- out << "<td class=\"col-sticky alternating-gray\">#{link_to('', send("#{klass._brick_index(:singular)}_path".to_sym,
104
- pk.map { |pk_part| obj.send(pk_part.to_sym) }), { class: 'big-arrow' })}</td>\n" if pk.present?
102
+ rid = pk.map { |pk_part| obj.send(pk_part.to_sym) }
103
+ out << "<tr x-id=\"#{rid.join('/')}\">\n"
104
+ out << "<td class=\"col-sticky alternating-gray\">#{link_to('⇛', send("#{klass._brick_index(:singular)}_path".to_sym, rid),
105
+ { class: 'big-arrow' })}</td>\n" if pk.present?
105
106
  ac = obj.instance_variable_get(:@association_cache) || obj.instance_variable_set(:@association_cache, {})
106
107
  # included =
107
108
  bi&.each do |bi_key|
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # require 'brick/frameworks/rails/controller'
4
- require 'brick/frameworks/rails/engine'
3
+ # require 'brick/rails/controller'
4
+ require 'brick/rails/engine'
5
5
 
6
6
  module ::Brick::Rails
7
7
  class << self
@@ -8,10 +8,75 @@ module Brick
8
8
 
9
9
  # return if ActiveRecord::Base.connection.current_database == 'postgres'
10
10
 
11
+ # Utilise Elasticsearch indexes, if any
12
+ if Object.const_defined?('Elasticsearch')
13
+ if ::Elasticsearch.const_defined?('Client')
14
+ # Allow Elasticsearch gem > 7.10 to work with Opensearch
15
+ ::Elasticsearch::Client.class_exec do
16
+ alias _original_initialize initialize
17
+ def initialize(arguments = {}, &block)
18
+ _original_initialize(arguments, &block)
19
+ @verified = true
20
+ @transport
21
+ end
22
+
23
+ # Auto-create when there is a missing index
24
+ alias _original_method_missing method_missing
25
+ def method_missing(name, *args, &block)
26
+ _original_method_missing(name, *args, &block)
27
+ rescue Elastic::Transport::Transport::Errors::NotFound => e
28
+ if (missing_index = args.last&.fetch(:defined_params, nil)&.fetch(:index, nil))
29
+ self.indices.create({ index: missing_index,
30
+ body: { settings: {}, mappings: { properties: {} } } })
31
+ puts "Auto-creating missing index \"#{missing_index}\""
32
+ _original_method_missing(name, *args, &block)
33
+ else
34
+ raise e
35
+ end
36
+ end
37
+ end
38
+ end
39
+ if ::Elasticsearch.const_defined?('Model')
40
+ # By setting the environment variable ELASTICSEARCH_URL then you can specify an Elasticsearch/Opensearch host
41
+ host = (client = ::Elasticsearch::Model.client).transport.hosts.first
42
+ es_uri = URI.parse("#{host[:protocol]}://#{host[:host]}:#{host[:port]}")
43
+ es_uri = nil if es_uri.to_s == 'http://localhost:9200'
44
+ begin
45
+ cluster_info = client.info.body
46
+ if (es_ver = cluster_info['version'])
47
+ ::Brick.elasticsearch_models = :all
48
+ puts "Found Elasticsearch gem and #{'local ' unless es_uri}#{es_ver['distribution'].titleize} #{es_ver['number']} installation#{" at #{es_uri}" if es_uri}."
49
+ puts "Enable Elasticsearch support by either setting \"::Brick.elasticsearch_models = :all\" or by picking specific models by name."
50
+
51
+ # # Auto-create when trying to import and there is a missing index
52
+ # ::Elasticsearch::Model::Importing.class_exec do
53
+ # end
54
+ end
55
+ rescue StandardError => e # Errno::ECONNREFUSED
56
+ puts "Found Elasticsearch gem, but could not connect to #{'local ' unless es_uri}Elasticsearch/Opensearch server#{" at #{es_uri}" if es_uri}."
57
+ end
58
+ # require 'net/http'
59
+ # begin
60
+ # es_uri = ENV['ELASTICSEARCH_URL']
61
+ # binding.pry
62
+ # cluster_info = JSON.parse(Net::HTTP.get(URI.parse(es_uri || 'http://localhost:9200')))
63
+ # if (es_ver = cluster_info['version'])
64
+ # ::Brick.elasticsearch_models = :all
65
+ # puts "Found Elasticsearch gem and #{'local ' unless es_uri}#{es_ver['distribution'].titleize} #{es_ver['number']} installation#{" at #{es_uri}" if es_uri}."
66
+ # puts "Enable Elasticsearch support by either setting \"::Brick.elasticsearch_models = :all\" or by picking specific models by name."
67
+ # end
68
+ # rescue StandardError => e
69
+ # end
70
+ end
71
+ end
72
+ # client = Elasticsearch::Client.new(host: 'https://my-elasticsearch-host.example')
73
+ # client.ping
74
+ # client.search(q: 'test')
75
+
11
76
  # Overwrite SQLite's #begin_db_transaction so it opens in IMMEDIATE mode instead of
12
77
  # the default DEFERRED mode.
13
78
  # https://discuss.rubyonrails.org/t/failed-write-transaction-upgrades-in-sqlite3/81480/2
14
- if ActiveRecord::Base.connection.adapter_name == 'SQLite'
79
+ if ActiveRecord::Base.connection.adapter_name == 'SQLite' && ActiveRecord.version >= Gem::Version.new('5.1')
15
80
  arca = ::ActiveRecord::ConnectionAdapters
16
81
  db_statements = arca::SQLite3.const_defined?('DatabaseStatements') ? arca::SQLite3::DatabaseStatements : arca::SQLite3::SchemaStatements
17
82
  # Rails 7.1 and later
@@ -410,7 +475,7 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
410
475
  v[:resource] = proposed_name_parts.last.underscore
411
476
  if colliding_thing
412
477
  message_start = if colliding_thing.is_a?(Module) && Object.const_defined?(:Rails) &&
413
- colliding_thing.constants.find { |c| colliding_thing.const_get(c) < ::Rails::Application }
478
+ colliding_thing.constants.find { |c| (ctc = colliding_thing.const_get(c)).is_a?(Class) && ctc < ::Rails::Application }
414
479
  "The module for the Rails application itself, \"#{colliding_thing.name}\","
415
480
  else
416
481
  "Non-AR #{colliding_thing.class.name.downcase} \"#{colliding_thing.name}\""
@@ -1,10 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Brick
4
- class << self
5
- attr_accessor :routes_done
6
- end
7
-
8
4
  module RouteMapper
9
5
  def add_brick_routes
10
6
  routeset_to_use = ::Rails.application.routes
@@ -276,6 +272,15 @@ module Brick
276
272
  end
277
273
 
278
274
  if (named_routes = instance_variable_get(:@set).named_routes).respond_to?(:find)
275
+ # Generic Elasticsearch / Opensearch query page
276
+ if ::Brick.config.add_search && (search_as = "#{controller_prefix.tr('/', '_')}brick_search".to_sym)
277
+ (
278
+ !(search_route = instance_variable_get(:@set).named_routes.find { |route| route.first == search_as }&.last) ||
279
+ !search_route.ast.to_s.include?("/#{controller_prefix}brick_search/")
280
+ )
281
+ get("/#{controller_prefix}brick_search", to: 'brick_gem#search', as: search_as.to_s)
282
+ end
283
+
279
284
  if ::Brick.config.add_status && (status_as = "#{controller_prefix.tr('/', '_')}brick_status".to_sym)
280
285
  (
281
286
  !(status_route = instance_variable_get(:@set).named_routes.find { |route| route.first == status_as }&.last) ||
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 219
8
+ TINY = 221
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
@@ -92,7 +92,7 @@ require 'brick/config'
92
92
  require 'brick/reflect_tables'
93
93
  if Gem::Dependency.new('rails').matching_specs.present?
94
94
  require 'rails'
95
- require 'brick/frameworks/rails'
95
+ # require 'brick/rails'
96
96
  end
97
97
  module Brick
98
98
  ALL_API_ACTIONS = [:index, :show, :create, :update, :destroy]
@@ -110,7 +110,9 @@ module Brick
110
110
  attr_accessor :default_schema, :db_schemas, :test_schema,
111
111
  :established_drf,
112
112
  :is_oracle, :is_eager_loading, :auto_models, :initializer_loaded,
113
- :table_name_lookup
113
+ :table_name_lookup,
114
+ :elasticsearch_models,
115
+ :routes_done
114
116
  ::Brick.auto_models = []
115
117
 
116
118
  def get_possible_schemas
@@ -342,6 +342,9 @@ if ActiveRecord::Base.respond_to?(:brick_select) && !::Brick.initializer_loaded
342
342
  # # user, then you can use model_descrips like this, putting expressions with property references in square brackets:
343
343
  # Brick.model_descrips = { 'User' => '[profile.firstname] [profile.lastname]' }
344
344
 
345
+ # # FULL TEXT SEARCH
346
+ # Brick.elasticsearch_models = :all
347
+
345
348
  # # ERD SETTINGS
346
349
 
347
350
  # # By default the Entity Relationship Diagram fragment which is available to be shown on the Grid page includes
@@ -64,6 +64,8 @@ module Brick
64
64
  stuck = {}
65
65
  indexes = {} # Track index names to make sure things are unique
66
66
  ar_base = Object.const_defined?(:ApplicationRecord) ? ApplicationRecord : Class.new(ActiveRecord::Base)
67
+ atrt_idx = 0 # ActionText::RichText unique index number
68
+ @has_atrts = nil # Any ActionText::RichText present?
67
69
  # Start by making entries for fringe models (those with no foreign keys).
68
70
  # Continue layer by layer, creating entries for models that reference ones already done, until
69
71
  # no more entries can be created. (At that point hopefully all models are accounted for.)
@@ -88,6 +90,16 @@ module Brick
88
90
  end
89
91
  ).present?
90
92
  seeds << "\n"
93
+ # Search through the fringe to see if we should bump special dependent classes forward to the next fringe.
94
+ # (Currently only ActiveStorage::Attachment if there's also an ActiveStorage::VariantRecord in the same
95
+ # fringe, and always have ActionText::EncryptedRichText at the very end.)
96
+ fringe_classes = fringe.map { |f| f.klass.name }
97
+ unless (asa_idx = fringe_classes.index('ActiveStorage::Attachment')).nil?
98
+ fringe.slice!(asa_idx) if fringe_classes.include?('ActiveStorage::VariantRecord')
99
+ end
100
+ unless (atert_idx = fringe_classes.index('ActionText::EncryptedRichText')).nil?
101
+ fringe.slice!(atert_idx) if fringe_classes.length > 1
102
+ end
91
103
  fringe.each do |seed_model|
92
104
  tbl = seed_model.table_name
93
105
  next unless ::Brick.config.exclude_tables.exclude?(tbl) &&
@@ -122,12 +134,14 @@ module Brick
122
134
  klass.order(*pkey_cols).each do |obj|
123
135
  unless has_rows
124
136
  has_rows = true
125
- seeds << " puts 'Seeding: #{seed_model.klass.name}'\n"
137
+ seeds << " puts 'Seeding: #{klass.name}'\n"
126
138
  end
127
139
  is_empty = false
128
140
  pk_val = obj.send(pkey_cols.first)
141
+ var_name = "#{tbl.gsub('.', '__')}_#{brick_escape(pk_val)}"
129
142
  fk_vals = []
130
143
  data = []
144
+ updates = []
131
145
  relation[:cols].each do |col, _col_type|
132
146
  next if !(fk = fkeys.find { |assoc| col == assoc[:fk] }) &&
133
147
  pkey_cols.include?(col)
@@ -142,11 +156,52 @@ module Brick
142
156
  inv_tbl = fk[:inverse_table].gsub('.', '__')
143
157
  fk_vals << "#{fk[:assoc_name]}: #{inv_tbl}_#{brick_escape(val)}" if val
144
158
  else
145
- val = val.to_s if val.is_a?(ActiveStorage::Filename)
146
- data << "#{col}: #{val.inspect}"
159
+ val = case val.class.name
160
+ when 'ActiveStorage::Filename'
161
+ val.to_s.inspect
162
+ when 'ActionText::RichText'
163
+ ensure_has_atrts(updates)
164
+ atrt_var = "atrt#{atrt_idx += 1}"
165
+ atrt_create = "(#{atrt_var} = #{val.class.name}.create(name: #{val.name.inspect}, body: #{val.to_trix_html.inspect
166
+ }, record_type: #{val.record_type.inspect}, record_id: #{var_name}.#{pkey_cols.first
167
+ }, created_at: DateTime.parse('#{val.created_at.inspect}'), updated_at: DateTime.parse('#{val.updated_at.inspect}')))"
168
+ updates << "#{var_name}.update(#{col}: #{atrt_create})\n"
169
+ # obj.send(col)&.embeds_blobs&.each do |blob|
170
+ updates << "atrt_ids[[#{val.id}, '#{val.class.name}']] = #{atrt_var}.id\n"
171
+ # end
172
+ next
173
+ else
174
+ val.inspect
175
+ end
176
+ data << "#{col}: #{val}" unless val == 'nil'
177
+ end
178
+ end
179
+ case klass.name
180
+ when 'ActiveStorage::VariantRecord'
181
+ ensure_has_atrts(updates)
182
+ updates << "atrt_ids[[#{obj.id}, '#{klass.name}']] = #{var_name}.id\n"
183
+ end
184
+ # Make sure that ActiveStorage::Attachment and ActionText::EncryptedRichText get
185
+ # wired up to the proper record_id
186
+ if klass.name == 'ActiveStorage::Attachment' || klass.name == 'ActionText::EncryptedRichText'
187
+ record_class = data.find { |d| d.start_with?('record_type: ') }[14..-2]
188
+ record_id = data.find { |d| d.start_with?('record_id: ') }[11..-1]
189
+ data.reject! { |d| d.start_with?('record_id: ') || d.start_with?('created_at: ') || d.start_with?('updated_at: ') }
190
+ data << "record_id: atrt_ids[[#{record_id}, '#{record_class}']]"
191
+ seeds << "#{var_name} = #{klass.name}.find_or_create_by(#{(fk_vals + data).join(', ')}) do |asa|
192
+ asa.created_at = DateTime.parse('#{obj.created_at.inspect}')#{"
193
+ asa.updated_at = DateTime.parse('#{obj.updated_at.inspect}')" if obj.respond_to?(:updated_at)}
194
+ end\n"
195
+ else
196
+ seeds << "#{var_name} = #{seed_model.klass.name}.create(#{(fk_vals + data).join(', ')})\n"
197
+ klass.attachment_reflections.each do |k, v|
198
+ if (attached = obj.send(k))
199
+ ensure_has_atrts(updates)
200
+ updates << "atrt_ids[[#{obj.id}, '#{klass.name}']] = #{var_name}.id\n"
201
+ end
147
202
  end
148
203
  end
149
- seeds << "#{tbl.gsub('.', '__')}_#{brick_escape(pk_val)} = #{seed_model.klass.name}.create(#{(fk_vals + data).join(', ')})\n"
204
+ updates.each { |update| seeds << update } # Anything that needs patching up after-the-fact
150
205
  end
151
206
  seeds << " # (Skipping #{seed_model.klass.name} as it has no rows)\n" unless has_rows
152
207
  File.open(seed_file_path, "w") { |f| f.write seeds }
@@ -190,5 +245,12 @@ module Brick
190
245
  val
191
246
  end
192
247
  end
248
+
249
+ def ensure_has_atrts(array)
250
+ unless @has_atrts
251
+ array << "atrt_ids = {}\n"
252
+ @has_atrts = true
253
+ end
254
+ end
193
255
  end
194
256
  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.219
4
+ version: 1.0.221
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-07 00:00:00.000000000 Z
11
+ date: 2024-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -190,14 +190,14 @@ dependencies:
190
190
  requirements:
191
191
  - - "~>"
192
192
  - !ruby/object:Gem::Version
193
- version: '1.4'
193
+ version: 2.3.0
194
194
  type: :development
195
195
  prerelease: false
196
196
  version_requirements: !ruby/object:Gem::Requirement
197
197
  requirements:
198
198
  - - "~>"
199
199
  - !ruby/object:Gem::Version
200
- version: '1.4'
200
+ version: 2.3.0
201
201
  description: 'Auto-create models, views, controllers, and routes with this slick Rails
202
202
  extension
203
203
 
@@ -212,14 +212,14 @@ files:
212
212
  - lib/brick/config.rb
213
213
  - lib/brick/extensions.rb
214
214
  - lib/brick/frameworks/cucumber.rb
215
- - lib/brick/frameworks/rails.rb
216
- - lib/brick/frameworks/rails/controller.rb
217
- - lib/brick/frameworks/rails/crosstab.brk
218
- - lib/brick/frameworks/rails/engine.rb
219
- - lib/brick/frameworks/rails/form_builder.rb
220
- - lib/brick/frameworks/rails/form_tags.rb
221
215
  - lib/brick/frameworks/rspec.rb
222
216
  - lib/brick/join_array.rb
217
+ - lib/brick/rails.rb
218
+ - lib/brick/rails/controller.rb
219
+ - lib/brick/rails/crosstab.brk
220
+ - lib/brick/rails/engine.rb
221
+ - lib/brick/rails/form_builder.rb
222
+ - lib/brick/rails/form_tags.rb
223
223
  - lib/brick/reflect_tables.rb
224
224
  - lib/brick/route_mapper.rb
225
225
  - lib/brick/serializers/json.rb
@@ -257,7 +257,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
257
257
  - !ruby/object:Gem::Version
258
258
  version: 1.3.6
259
259
  requirements: []
260
- rubygems_version: 3.3.26
260
+ rubygems_version: 3.2.33
261
261
  signing_key:
262
262
  specification_version: 4
263
263
  summary: Create a Rails app from data alone
File without changes