brick 1.0.220 → 1.0.221

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: 8bc21a2ffe1c7434812f1167f26303a4eeda701766489b5616348a3b4a890a93
4
- data.tar.gz: 1c8e6226c3653732f2ac3e481a8348d30aa78ec7520fa2923d2a11f33719e686
3
+ metadata.gz: b4aaaeaadb65aad58b8eac139c9e0fa5c8a68995a15151149fdc49196563db7e
4
+ data.tar.gz: f2ad0ef65556495ac9d531e499ca8b38a532aab641d0e46aeca4ea5fc535efd0
5
5
  SHA512:
6
- metadata.gz: 391e5b7b6cf05618bf7cc74c55d0df46a5eb0d843cdc4ebfea501e5a7e5904181b4ad8f2440b7c8635da4dfe6225401ad1379cd8a52cfe2b26d9d9f38517afe6
7
- data.tar.gz: 3c4335b074b040def278f5ba48b154cb122e575805c2cd7abda0157a610137f7899db7aed1c11f71923e5af6e84955f261c56d8dfbc0c4ab29c5600d4fb25152
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 && respond_to?(:request) && 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
@@ -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
@@ -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 = 220
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.220
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-23 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