brick 1.0.227 → 1.0.229

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: 1640b4022f78ed3bba288342fd3cb4f3c90cdb75c09aa804b9aeb7e91ae64403
4
- data.tar.gz: 1c44e055658540a923db08a4ef0114f1291024acb90c2cad26631c61a9b050a9
3
+ metadata.gz: a5484fa8f1c3c64e6d10340f54ca460668cfa176373b0d2322fe1ede86cfad8c
4
+ data.tar.gz: bda7b20486fb89f0b5775975957ea7f3b0de3b51a635e42e4a21dbec3b71478f
5
5
  SHA512:
6
- metadata.gz: 61345c77cea2b0d61f9828086f3c9e9b464d377945b2a1a78822f4db36bd37a73e052689750b23f54549fd17e0cd5af0a794eec664ba3a593c4a5b90fb12be13
7
- data.tar.gz: b05f1791d999a9e57ee1987a336dde5aefc1bfda029a8afd0a26103b4b64ea89bde3bad1f954f70fa25ec4d1d60aaf53ad7a363801c4695113b4ddbe6d52bf0b
6
+ metadata.gz: c6f01661b3423287dc4ce703790e04d292f902a14dc23e1e3b68a4891c9347adeb2083594717af1e0ca93404a6e7748e2f393885d4d37944b595757ee5a4b6b8
7
+ data.tar.gz: 6967a37c53be78011f1b0e7b5eb69ecf1a7779b1c0c312e2288a6ab701fdb1ab2724345957d178970df06ee26481bd1470e8fb5ede8d14e06d6ac0a4a6418248
@@ -979,7 +979,8 @@ module ActiveRecord
979
979
  next
980
980
  end
981
981
 
982
- tbl_alias = unique63("b_r_#{hm.name}", previous)
982
+ tbl_alias = "b_r_#{hm.name}"
983
+ tbl_alias = unique63(tbl_alias, previous) if is_postgres
983
984
  on_clause = []
984
985
  hm_selects = if !pri_key.is_a?(Array) # Probable standard key?
985
986
  if fk_col.is_a?(Array) # Foreign is composite but not Primary? OK, or choose the first part of the foreign key if nothing else
@@ -1031,19 +1032,28 @@ JOIN (SELECT #{hm_selects.map { |s| _br_quoted_name("#{'br_t0.' if from_clause}#
1031
1032
  unless wheres.empty?
1032
1033
  # Rewrite the wheres to reference table and correlation names built out by AREL
1033
1034
  where_nots = {}
1035
+ where_comparisons = []
1034
1036
  wheres2 = wheres.each_with_object({}) do |v, s|
1035
1037
  is_not = if v.first[-1] == '!'
1036
1038
  v[0] = v[0][0..-2] # Take off ending ! from column name
1037
1039
  end
1038
- if (v_parts = v.first.split('.')).length == 1
1039
- (is_not ? where_nots : s)[v.first] = v.last
1040
+ tbl_and_col_name = if (v_parts = v.first.split('.')).length == 1
1041
+ []
1042
+ else
1043
+ [brick_links[v_parts[0..-2].join('.')].split('.').last]
1044
+ end
1045
+ tbl_and_col_name << v_parts.last
1046
+ if ['>', '<'].include?(first_char = v.last.first[0]) # Greater than or less than?
1047
+ col_name = v.last.first[1..-1]
1048
+ col_name = "'#{col_name}'" unless [:integer, :boolean, :decimal, :float].include?(klass.columns_hash[v.first].type)
1049
+ where_comparisons << "#{tbl_and_col_name.join('.')} #{first_char} #{col_name}"
1040
1050
  else
1041
- tbl_name = brick_links[v_parts[0..-2].join('.')].split('.').last
1042
- (is_not ? where_nots : s)["#{tbl_name}.#{v_parts.last}"] = v.last
1051
+ (is_not ? where_nots : s)[tbl_and_col_name.join('.')] = v.last
1043
1052
  end
1044
1053
  end
1045
1054
  if respond_to?(:where!)
1046
1055
  where!(wheres2) if wheres2.present?
1056
+ where_comparisons.each { |wc| where!(wc) }
1047
1057
  if where_nots.present?
1048
1058
  self.where_clause += WhereClause.new(predicate_builder.build_from_hash(where_nots)).invert
1049
1059
  end
@@ -1106,8 +1116,7 @@ JOIN (SELECT #{hm_selects.map { |s| _br_quoted_name("#{'br_t0.' if from_clause}#
1106
1116
 
1107
1117
  # Build out an AR relation that queries for a list of objects, and include all the appropriate JOINs to later apply DSL using #brick_descrip
1108
1118
  def brick_list
1109
- pks = klass.primary_key.is_a?(String) ? [klass.primary_key] : klass.primary_key
1110
- selects = pks.each_with_object([]) { |pk, s| s << pk unless s.include?(pk) }
1119
+ selects = klass._pk_as_array.each_with_object([]) { |pk, s| s << pk unless s.include?(pk) }
1111
1120
  # Get foreign keys for anything marked to be auto-preloaded, or a self-referencing JOIN
1112
1121
  klass_cols = klass.column_names
1113
1122
  reflect_on_all_associations.each do |a|
@@ -1740,51 +1749,56 @@ class Object
1740
1749
  built_model = Class.new(base_model) do |new_model_class|
1741
1750
  (schema_module || Object).const_set(chosen_name, new_model_class) unless is_generator
1742
1751
  @_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?
1752
+ # Enable Elasticsearch based on the table name?
1753
+ if (@_brick_es_crud = ::Brick.elasticsearch_models&.fetch(table_name, nil))
1754
1754
  include ::Elasticsearch::Model
1755
- code << " include Elasticsearch::Model\n"
1756
1755
  if @_brick_es_crud.index('i') # Enable auto-creation of indexes on import?
1757
1756
  class << self
1758
1757
  alias _original_import import
1759
1758
  def import(options={}, &block)
1760
- self.__elasticsearch__.create_index! unless self.__elasticsearch__.index_exists?
1759
+ unless self.__elasticsearch__.index_exists?
1760
+ self.__elasticsearch__.create_index!
1761
+ ::Brick.elasticsearch_existings << self.table_name
1762
+ end
1761
1763
  _original_import(options={}, &block)
1762
1764
  end
1763
1765
  end
1764
1766
  end
1765
- if @_brick_es_crud.index('c') || @_brick_es_crud.index('u') || @_brick_es_crud.index('d')
1767
+ if ::Elasticsearch::Model.const_defined?('Callbacks') &&
1768
+ @_brick_es_crud.index('c') || @_brick_es_crud.index('u') || @_brick_es_crud.index('d')
1766
1769
  include ::Elasticsearch::Model::Callbacks
1767
- code << " include Elasticsearch::Model::Callbacks\n"
1770
+ is_include_es_callbacks = true
1771
+ end
1772
+ # Create mappings for all text columns
1773
+ mappings do
1774
+ has_mappings = nil
1775
+ new_model_class.columns.select { |col| [:string, :text].include?(col.type) }.each do |string_col|
1776
+ unless has_mappings
1777
+ code << " include Elasticsearch::Model\n"
1778
+ code << " include Elasticsearch::Model::Callbacks\n" if is_include_es_callbacks
1779
+ code << " mappings do\n"
1780
+ has_mappings = true
1781
+ end
1782
+ code << " indexes :#{string_col.name}, type: #{string_col.type.to_s.inspect}\n"
1783
+ indexes string_col.name.to_sym, type: string_col.type.to_s
1784
+ end
1785
+ code << " end\n" if has_mappings
1768
1786
  end
1769
1787
  if @_brick_es_crud.index('r')
1770
- # Customer.__elasticsearch__.search('taco').to_a
1788
+ class << self
1789
+ # Search and hydrate records using only Elasticsearch data
1790
+ define_method :search do |q|
1791
+ self.__elasticsearch__.search(q).raw_response.body['hits']['hits'].map do |hit|
1792
+ obj = self.new(hit['_source'])
1793
+ obj.instance_variable_set(:@new_record, false) # Don't want to accidentally save a new one
1794
+ obj
1795
+ end
1796
+ rescue Elastic::Transport::Transport::Errors::NotFound => e
1797
+ self.create_index! if @_brick_es_crud.index('i')
1798
+ []
1799
+ end
1800
+ end
1771
1801
  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
1802
  end
1789
1803
  if inheritable_name
1790
1804
  new_model_class.define_singleton_method :inherited do |subclass|
@@ -2272,6 +2286,39 @@ class Object
2272
2286
  render json: { data: result } # [ver, result]
2273
2287
  end
2274
2288
 
2289
+ self.define_method :search do
2290
+ # TODO: Make sure at least one index is actually present which allows for reading before attempting
2291
+ if (q = params['qry'] || params['_brick_es']) # Elasticsearch query?
2292
+ @indexes = ::Brick.elasticsearch_existings
2293
+ hits = Elasticsearch::Model.client.search({index: @indexes.join(','), q: q, size: 100})
2294
+ model_infos = {}
2295
+ # Number of indexes used: hits.body['_shards']['total']
2296
+ @count = hits.body['hits']['total']['value']
2297
+ @results = hits.body['hits']['hits'].map do |x|
2298
+ klass = ::Brick.relations[x['_index']][:class_name].constantize
2299
+ model_info = model_infos[klass] ||= [
2300
+ klass.primary_key,
2301
+ klass.brick_parse_dsl(join_array = ::Brick::JoinArray.new, [], translations = {}, false, nil, true)
2302
+ ]
2303
+ obj = klass.new(x['_source'])
2304
+ [
2305
+ klass.name,
2306
+ obj.send(model_info.first),
2307
+ obj.brick_descrip(
2308
+ model_info.last&.first&.map { |col2| obj.send(col2.last) },
2309
+ obj.send(klass.primary_key)
2310
+ ),
2311
+ send("#{klass._brick_index(:singular)}_path".to_sym, obj),
2312
+ x['_score']
2313
+ ]
2314
+ end
2315
+ respond_to do |format|
2316
+ format.js { render json: { result: @results } }
2317
+ format.any {}
2318
+ end
2319
+ end
2320
+ end
2321
+
2275
2322
  return [new_controller_class, code + "end # BrickGem controller\n"]
2276
2323
  when 'BrickOpenapi'
2277
2324
  is_openapi = true
@@ -2584,7 +2631,26 @@ class Object
2584
2631
  render json: { result: ::Brick.unexclude_column(table_name, col) }
2585
2632
  elsif is_json && (q = params['_brick_es']) # Elasticsearch
2586
2633
  # Make sure that the index is actually present and we allow reading before attempting
2587
- es_result = model.__elasticsearch__.search(q).to_a if (es_perms = model.instance_variable_get(:@_brick_es_crud)).index('r')
2634
+ if (es_perms = model.instance_variable_get(:@_brick_es_crud) ||
2635
+ # If a perms instance variable is missing for this model, get the perms ...
2636
+ (set = true && ::Brick.elasticsearch_models&.fetch(model.table_name, nil))
2637
+ ) && (
2638
+ # ... then set it on the model
2639
+ (set && model.instance_variable_set(:@_brick_es_crud, es_perms)) || es_perms
2640
+ )&.index('r')
2641
+ es_result = begin
2642
+ model.__elasticsearch__.search(q)
2643
+ rescue Elastic::Transport::Transport::Errors::NotFound => e
2644
+ if @_brick_es_crud.index('i')
2645
+ self.__elasticsearch__.create_index!
2646
+ # model.import
2647
+ ::Brick.elasticsearch_existings << self.table_name
2648
+ model.__elasticsearch__.search(q)
2649
+ else
2650
+ []
2651
+ end
2652
+ end
2653
+ end
2588
2654
  render json: { result: es_result }
2589
2655
  else
2590
2656
  real_model = model.real_model(params)
@@ -587,8 +587,9 @@ window.addEventListener(\"popstate\", linkSchemas);
587
587
  def lookup_context(*args)
588
588
  ret = _brick_lookup_context(*args)
589
589
  if self.class < AbstractController::Base
590
- request if respond_to?(:request) # ActionMailer does not have +request+
591
- @_lookup_context.instance_variable_set(:@_brick_req_params, params) if request && params.present?
590
+ if respond_to?(:request) # ActionMailer does not have +request+
591
+ @_lookup_context.instance_variable_set(:@_brick_req_params, params) if request && params.present?
592
+ end
592
593
  end
593
594
  ret
594
595
  end
@@ -670,7 +671,9 @@ window.addEventListener(\"popstate\", linkSchemas);
670
671
  def find_template(*args, **options)
671
672
  find_template_err = nil
672
673
  unless (model_name = @_brick_model&.name) ||
673
- (is_search = ::Brick.config.add_search && args[0..1] == ['search', ['brick_gem']]) ||
674
+ (is_search = ::Brick.config.add_search && args[0..1] == ['search', ['brick_gem']] &&
675
+ ::Brick.elasticsearch_existings&.length&.positive?
676
+ ) ||
674
677
  (is_status = ::Brick.config.add_status && args[0..1] == ['status', ['brick_gem']]) ||
675
678
  (is_orphans = ::Brick.config.add_orphans && args[0..1] == ['orphans', ['brick_gem']]) ||
676
679
  (is_crosstab = args[0..1] == ['crosstab', ['brick_gem']])
@@ -791,7 +794,7 @@ window.addEventListener(\"popstate\", linkSchemas);
791
794
  b[0] = '' if b[0].is_a?(Symbol)
792
795
  a.first <=> b.first
793
796
  end.each_with_object(+'') do |rel, s|
794
- next if rel.first.blank? || rel.last[:cols].empty? ||
797
+ next if rel.first.is_a?(Symbol) || rel.first.blank? || rel.last[:cols].empty? ||
795
798
  ::Brick.config.exclude_tables.include?(rel.first)
796
799
 
797
800
  # %%% When table_name_prefixes are use then during rendering empty non-TNP
@@ -804,7 +807,7 @@ window.addEventListener(\"popstate\", linkSchemas);
804
807
  end.html_safe
805
808
  # Options for special Brick pages
806
809
  prefix = "#{::Brick.config.path_prefix}/" if ::Brick.config.path_prefix
807
- [['Search', ::Brick.config.add_search],
810
+ [['Search', is_search],
808
811
  ['Status', ::Brick.config.add_status],
809
812
  ['Orphans', is_orphans],
810
813
  ['Crosstab', is_crosstab]].each do |table_option, show_it|
@@ -1083,13 +1086,14 @@ end
1083
1086
  <% end
1084
1087
  # SEARCH BOX
1085
1088
  if @_brick_es&.index('r') # Must have at least Elasticsearch Read access %>
1086
- <input type=\"text\" id=\"esSearch\"><a href=\"\">S</a>
1089
+ <input type=\"text\" id=\"esSearch\" class=\"dimmed\">
1087
1090
  <script>
1088
- var esSearch = document.getElementById(\"esSearch\")
1091
+ var esSearch = document.getElementById(\"esSearch\");
1092
+ var usedTerms = {};
1089
1093
  var isEsFiltered = false;
1090
1094
  esSearch.addEventListener(\"input\", function () {
1091
1095
  var gridTrs;
1092
- if (this.value.length > 2) { # At least 3 letters in the search term
1096
+ if (this.value.length > 2 && usedTerms[this.value] !== null) { // At least 3 letters in the search term
1093
1097
  var es = doFetch(\"POST\", {_brick_es: this.value},
1094
1098
  function (p) {p.text().then(function (response) {
1095
1099
  var result = JSON.parse(response).result;
@@ -1110,13 +1114,16 @@ end
1110
1114
  if (!isHit) row.style.display = \"none\";
1111
1115
  }
1112
1116
  isEsFiltered = true;
1117
+ esSearch.className = \"\";
1113
1118
  } else {
1114
1119
  if (isEsFiltered) { // Show all rows and gray the search box
1120
+ usedTerms[this.value] = null; // No results for this term
1115
1121
  gridTrs = [... grid.querySelectorAll(\"tr\")];
1116
1122
  for (var i = 1; i < gridTrs.length; ++i) {
1117
1123
  gridTrs[i].style.display = \"table-row\";
1118
1124
  }
1119
1125
  }
1126
+ esSearch.className = \"dimmed\";
1120
1127
  }
1121
1128
  });}
1122
1129
  );
@@ -1126,6 +1133,7 @@ end
1126
1133
  for (var i = 1; i < gridTrs.length; ++i) {
1127
1134
  gridTrs[i].style.display = \"table-row\";
1128
1135
  }
1136
+ esSearch.className = \"dimmed\";
1129
1137
  }
1130
1138
  }
1131
1139
  });
@@ -1210,21 +1218,28 @@ end
1210
1218
  +"#{css}
1211
1219
  <p class=\"flashNotice\"><%= notice if request.respond_to?(:flash) %></p>#{"
1212
1220
  #{schema_options}" if schema_options}
1213
- <select id=\"tbl\">#{table_options}</select>
1214
- <h1>Search</h1>
1221
+ <select id=\"tbl\">#{table_options}</select><br><br>
1222
+ <form method=\"get\">
1223
+ <input type=\"text\" name=\"qry\"><input type=\"submit\", value=\"Search\">
1224
+ </form>
1225
+ <% if @results.present? %>
1226
+ <div id=\"rowCount\"><b><%= @count %> results from: </b><%= @indexes.sort.join(', ') %></div>
1227
+ <% end %>
1215
1228
  <table id=\"resourceName\" class=\"shadow\"><thead><tr>
1216
1229
  <th>Resource</th>
1217
1230
  <th>Description</th>
1218
- <th>ID</th>
1231
+ <th>Score</th>
1219
1232
  </tr></thead>
1220
1233
  <tbody>
1221
- <% # @results.each do |r| %>
1234
+ <% @results&.each do |r| %>
1222
1235
  <tr>
1223
- <td><%= %></td>
1224
- <td<%= %></td>
1225
- <td<%= %></td>
1236
+ <td><%= link_to (r[3]) do %><%= r[0] %><br>
1237
+ <%= r[1] %><% end %>
1238
+ </td>
1239
+ <td><%= r[2] %></td>
1240
+ <td><%= '%.3f' % r[4] %></td>
1226
1241
  </tr>
1227
- <% # end %>
1242
+ <% end %>
1228
1243
  </tbody></table>
1229
1244
  #{script}"
1230
1245
  end
@@ -1644,9 +1659,13 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
1644
1659
  # %%% Create a smart javascript routine which can do this client-side %>
1645
1660
  [... document.getElementsByTagName(\"TH\")].forEach(function (th) {
1646
1661
  th.addEventListener(\"click\", function (e) {
1647
- var xOrder;
1648
- if (xOrder = this.getAttribute(\"x-order\"))
1662
+ var xOrder,
1663
+ currentOrder;
1664
+ if (xOrder = this.getAttribute(\"x-order\")) {
1665
+ if ((currentOrder = changeout(location.href, \"_brick_order\")) === xOrder)
1666
+ xOrder = \"-\" + xOrder;
1649
1667
  location.href = changeout(location.href, \"_brick_order\", xOrder);
1668
+ }
1650
1669
  });
1651
1670
  });
1652
1671
  document.querySelectorAll(\"input, select\").forEach(function (inp) {
@@ -28,7 +28,8 @@ module Brick
28
28
  def method_missing(name, *args, &block)
29
29
  _original_method_missing(name, *args, &block)
30
30
  rescue Elastic::Transport::Transport::Errors::NotFound => e
31
- if (missing_index = args.last&.fetch(:defined_params, nil)&.fetch(:index, nil))
31
+ if (missing_index = args.last&.fetch(:defined_params, nil)&.fetch(:index, nil)) &&
32
+ ::Brick.elasticsearch_models&.fetch(missing_index, nil)&.include?('i')
32
33
  self.indices.create({ index: missing_index,
33
34
  body: { settings: {}, mappings: { properties: {} } } })
34
35
  puts "Auto-creating missing index \"#{missing_index}\""
@@ -39,7 +40,7 @@ module Brick
39
40
  end
40
41
  end
41
42
  end
42
- if ::Elasticsearch.const_defined?('Model')
43
+ if ::Elasticsearch.const_defined?('Model') && ::Brick.elasticsearch_models
43
44
  # By setting the environment variable ELASTICSEARCH_URL then you can specify an Elasticsearch/Opensearch host
44
45
  host = (client = ::Elasticsearch::Model.client).transport.hosts.first
45
46
  es_uri = URI.parse("#{host[:protocol]}://#{host[:host]}:#{host[:port]}")
@@ -47,34 +48,19 @@ module Brick
47
48
  begin
48
49
  cluster_info = client.info.body
49
50
  if (es_ver = cluster_info['version'])
50
- ::Brick.elasticsearch_models = :all
51
51
  puts "Found Elasticsearch gem and #{'local ' unless es_uri}#{es_ver['distribution'].titleize} #{es_ver['number']} installation#{" at #{es_uri}" if es_uri}."
52
- puts "Enable Elasticsearch support by either setting \"::Brick.elasticsearch_models = :all\" or by picking specific models by name."
53
-
54
- # # Auto-create when trying to import and there is a missing index
55
- # ::Elasticsearch::Model::Importing.class_exec do
56
- # end
52
+ if ::Brick.elasticsearch_models.empty?
53
+ puts "Enable Elasticsearch support by either setting \"::Brick.elasticsearch_models = :all\" or by picking specific models by name."
54
+ end
55
+ else
56
+ ::Brick.elasticsearch_models = nil
57
57
  end
58
58
  rescue StandardError => e # Errno::ECONNREFUSED
59
+ ::Brick.elasticsearch_models = nil
59
60
  puts "Found Elasticsearch gem, but could not connect to #{'local ' unless es_uri}Elasticsearch/Opensearch server#{" at #{es_uri}" if es_uri}."
60
61
  end
61
- # require 'net/http'
62
- # begin
63
- # es_uri = ENV['ELASTICSEARCH_URL']
64
- # binding.pry
65
- # cluster_info = JSON.parse(Net::HTTP.get(URI.parse(es_uri || 'http://localhost:9200')))
66
- # if (es_ver = cluster_info['version'])
67
- # ::Brick.elasticsearch_models = :all
68
- # puts "Found Elasticsearch gem and #{'local ' unless es_uri}#{es_ver['distribution'].titleize} #{es_ver['number']} installation#{" at #{es_uri}" if es_uri}."
69
- # puts "Enable Elasticsearch support by either setting \"::Brick.elasticsearch_models = :all\" or by picking specific models by name."
70
- # end
71
- # rescue StandardError => e
72
- # end
73
62
  end
74
63
  end
75
- # client = Elasticsearch::Client.new(host: 'https://my-elasticsearch-host.example')
76
- # client.ping
77
- # client.search(q: 'test')
78
64
 
79
65
  # Overwrite SQLite's #begin_db_transaction so it opens in IMMEDIATE mode instead of
80
66
  # the default DEFERRED mode.
@@ -527,6 +513,38 @@ ORDER BY 1, 2, c.internal_column_id, acc.position"
527
513
  end
528
514
  end
529
515
 
516
+ if ems = ::Brick.elasticsearch_models # ['comments']
517
+ access = case ems
518
+ when Hash, String # Hash is a list of resource names and ES permissions such as 'r' or 'icr'
519
+ ems
520
+ when :all
521
+ 'crud' # All CRUD
522
+ when :full
523
+ 'icrud' # Also able to auto-create indexes
524
+ else
525
+ ''
526
+ end
527
+ # Rewriting this to have all valid indexes and their perms
528
+ ::Brick.elasticsearch_models = unless access.blank?
529
+ # Find all existing indexes
530
+ client = Elastic::Transport::Client.new
531
+ ::Brick.elasticsearch_existings = client.perform_request('GET', '_aliases').body.each_with_object([]) do |entry, s|
532
+ s << entry.first if relations.include?(entry.first)
533
+ entry.last.fetch('aliases', nil)&.each { |k, _v| s << k if relations.include?(k) }
534
+ end
535
+ # Add this either if...
536
+ if access.is_a?(String) # ...they have permissions over absolutely anything,
537
+ relations.each_with_object({}) { |rel, s| s[rel.first] = access unless rel.first.is_a?(Symbol) }
538
+ else # or there are specific permissions for each resource, so find the matching indexes
539
+ client = Elastic::Transport::Client.new
540
+ ::Brick.elasticsearch_existings.each_with_object({}) do |index, s|
541
+ perms = access.is_a?(String) ? access : access[index] || '' # Look up permissions from above
542
+ s[index] = perms unless perms.blank?
543
+ end
544
+ end
545
+ end
546
+ end
547
+
530
548
  if orig_schema && (orig_schema = (orig_schema - ['pg_catalog', 'pg_toast', 'heroku_ext']).first)
531
549
  puts "Now switching back to \"#{orig_schema}\" schema."
532
550
  ActiveRecord::Base.execute_sql("SET SEARCH_PATH = ?", orig_schema)
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 227
8
+ TINY = 229
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
@@ -111,7 +111,7 @@ module Brick
111
111
  :established_drf,
112
112
  :is_oracle, :is_eager_loading, :auto_models, :initializer_loaded,
113
113
  :table_name_lookup,
114
- :elasticsearch_models,
114
+ :elasticsearch_models, :elasticsearch_existings,
115
115
  :routes_done
116
116
  ::Brick.auto_models = []
117
117
 
@@ -292,6 +292,10 @@ to instead be this:
292
292
  s.first[bt_key] = [a.name, a.klass]
293
293
  end
294
294
  else # This gets all forms of has_many and has_one
295
+ if a.scope&.arity&.> 0
296
+ puts "Skipping HM column \"#{a.name}\" because it has a scope which requires #{a.scope.arity} #{'argument'.pluralize(a.scope.arity)}"
297
+ next
298
+ end
295
299
  if through # has_many :through or has_one :through
296
300
  is_invalid_source = nil
297
301
  begin
@@ -346,7 +346,18 @@ if ActiveRecord::Base.respond_to?(:brick_select) && !::Brick.initializer_loaded
346
346
  # Brick.model_descrips = { 'User' => '[profile.firstname] [profile.lastname]' }
347
347
 
348
348
  # # FULL TEXT SEARCH
349
+ # # You can enable Elasticsearch support by adding the elasticsearch-model and elasticsearch-rails gems, and either
350
+ # # having a copy of Opensearch or Elasticsearch locally installed on the same machine listening on port 9200, or by
351
+ # # setting the ELASTICSEARCH_URL environment variable to point to the URI of a search machine.
352
+ # # With that configured, you can pick specific table names and permissions for search and update by putting them in
353
+ # # a hash like this:
354
+ # Brick.elasticsearch_models = { 'notes' => 'crud', 'issues' => 'cru', 'orders' => 'r' }
355
+ # # or to blanketly enable all models to have auto-updating CRUD behaviour when there are ActiveRecord changes, use:
349
356
  # Brick.elasticsearch_models = :all
357
+ # # As well there is another permission available -- the 'i' permission -- which will auto-create an index if it
358
+ # # is missing. If you set 'icrud' for a model it will auto-create an index, or to always do this for all models
359
+ # # then you can specify \"full control\" like this:
360
+ # Brick.elasticsearch_models = :full
350
361
 
351
362
  # # ERD SETTINGS
352
363
 
@@ -377,18 +388,19 @@ if ActiveRecord::Base.respond_to?(:brick_select) && !::Brick.initializer_loaded
377
388
 
378
389
  # # Polymorphic associations are set up by providing a model name and polymorphic association name#{poly}
379
390
 
380
- # # For multi-tenant databases that use a separate schema for each tenant, a single representative database schema
381
- # # can be analysed to determine the range of polymorphic classes that can be used for each association. Hopefully
382
- # # the schema chosen is one loaded with existing data that is representative of all possible polymorphic
383
- # # associations.
391
+ # # MULTITENANCY VIA THE ROS-APARTMENT GEM
392
+
393
+ # # If you are using the ros-apartment gem along with Postgres then you can have automatic detection of polymorphic
394
+ # # type names (model class names listed in a column such as imageable_type) by choosing a schema that is loaded up
395
+ # # with data that represents the full range of the various polymorphic has_many classes that should be associated.
384
396
  # Brick.schema_behavior = :namespaced
385
397
  #{Brick.config.schema_behavior.present? ? " Brick.schema_behavior = { multitenant: { schema_to_analyse: #{
386
398
  Brick.config.schema_behavior[:multitenant]&.fetch(:schema_to_analyse, nil).inspect}" :
387
399
  " # Brick.schema_behavior = { multitenant: { schema_to_analyse: 'engineering'"
388
400
  } } }
389
401
  #{"
390
- # # Note that if you have a real polymorphic model configured then it is better to set the list of classes up in the
391
- # # model file itself with a line like:
402
+ # # Note that for each polymorphic model configured then it is better to set the list of classes up in the model
403
+ # # file itself with a line like:
392
404
  # delegated_type :commentable, type: ['Post', 'Comment']" if ActiveRecord::Base.respond_to?(:delegated_type)}
393
405
 
394
406
  # # DEFAULT ROOT ROUTE
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.227
4
+ version: 1.0.229
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-13 00:00:00.000000000 Z
11
+ date: 2025-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord