brick 1.0.111 → 1.0.113

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: cb97e4a946526ed99a24e67b1e65d5f6490a40c97dc170dd0680166b087d1ccb
4
- data.tar.gz: 48d9c4f2afcd737d9a857f8c771448639c1b0d2d568dc966bbe4b85a97cb4610
3
+ metadata.gz: f6cede45ed1fd983a2199fb279dffad0ecd02e51c7ed31cec6d46fccbc799e7c
4
+ data.tar.gz: 65eb5df73a7869b1bbe3ac128a69b4fda257e9dc098e77dc65c75d8f593756d7
5
5
  SHA512:
6
- metadata.gz: 383d9996affbbd4fa049ab125490879143299a5477e98f921d450ea6b5b466d66a4cd210aac8db9c105a6eaf5f10eae8ac181d84d78fb6fc0b4c4151ac9dcee3
7
- data.tar.gz: b9902fc959b0de870ef5c402fe75e76d939d0c7c5abc7b8d3c16451b87f56929eb6e329153c6b5697936f10228752ba3b2f5f2a7de6aa5c9c77c105c34dd2cfa
6
+ metadata.gz: 0e506c2b4d00a1402e232f48c9eb95fc1edfb201a4b2d46ce8fd2a29d0f6a62e5d954ba84dfad4ccab2ff2c5c972d32478873e1f5c7ddf19bdfcca24894b0d1e
7
+ data.tar.gz: ee7dd85c423043b10efb60075716e3464db2645ac642fa43f457cf1b05c98a52cb7645e58e3a8208be8fd94b3a4b74f6b7d5e3102d890d087542a04b7e8cc7ec
@@ -1602,11 +1602,11 @@ class Object
1602
1602
  else
1603
1603
  relation.last[:cols]
1604
1604
  end
1605
- { :index => [:get, 'list'], :create => [:post, 'create a'] }.each do |k, v|
1605
+ { :index => [:get, 'list', true], :create => [:post, 'create a', false] }.each do |k, v|
1606
1606
  unless actions&.exclude?(k)
1607
1607
  this_resource = (s["#{current_api_root}#{relation_name}"] ||= {})
1608
1608
  this_resource[v.first] = {
1609
- 'summary': "#{v[1]} #{relation.first}",
1609
+ 'summary': "#{v[1]} #{relation.first.send(v[2] ? :pluralize : :singularize)}",
1610
1610
  'description': table_description,
1611
1611
  'parameters': renamed_columns.map do |k2, v2|
1612
1612
  param = { in: 'query', 'name': k2, 'schema': { 'type': v2.first } }
@@ -1841,7 +1841,19 @@ class Object
1841
1841
  end
1842
1842
 
1843
1843
  instance_variable_set("@#{singular_table_name}".to_sym, (obj = find_obj))
1844
- obj.send(:update, send(params_name_sym))
1844
+ upd_params = send(params_name_sym)
1845
+ if (json_cols = model.columns.select { |c| c.type == :json }.map(&:name)).present?
1846
+ upd_hash = upd_params.to_h
1847
+ json_cols.each do |c|
1848
+ begin
1849
+ upd_hash[c] = JSON.parse(upd_hash[c]) # At least attempt to turn this into a parsed hash or array object
1850
+ rescue
1851
+ end
1852
+ end
1853
+ obj.send(:update, upd_hash)
1854
+ else
1855
+ obj.send(:update, upd_params)
1856
+ end
1845
1857
  end
1846
1858
 
1847
1859
  code << " def destroy\n"
@@ -1865,11 +1877,14 @@ class Object
1865
1877
  @#{singular_table_name} = #{model.name}.find(id.is_a?(Array) && id.length == 1 ? id.first : id)
1866
1878
  end\n"
1867
1879
  self.define_method :find_obj do
1868
- id = if model.columns_hash[pk.first]&.type == :string
1869
- is_pk_string = true
1880
+ id = if pk.length == 1 # && model.columns_hash[pk.first]&.type == :string
1870
1881
  params[:id].gsub('^^sl^^', '/')
1871
1882
  else
1872
- params[:id]&.split(/[\/,_]/).map do |val_part|
1883
+ if model.columns_hash[pk.first]&.type == :string
1884
+ params[:id]&.split('/')
1885
+ else
1886
+ params[:id]&.split(/[\/,_]/)
1887
+ end.map do |val_part|
1873
1888
  val_part.gsub('^^sl^^', '/')
1874
1889
  end
1875
1890
  end
@@ -322,18 +322,26 @@ window.addEventListener(\"popstate\", linkSchemas);
322
322
  end
323
323
 
324
324
  def set_brick_model(find_args)
325
- # Need to return true if we can fill in the blanks for a missing one
326
- # args will be something like: ["index", ["categories"]]
327
- if (class_name = find_args[1].last&.singularize)
328
- find_args[1][find_args[1].length - 1] = class_name # Make sure the last item, defining the class name, is singular
329
- if (model = find_args[1].map(&:camelize).join('::').constantize) && (
330
- ['index', 'show'].include?(find_args.first) || # Everything has index and show
331
- # Only CUD stuff has create / update / destroy
332
- (!model.is_view? && ['new', 'create', 'edit', 'update', 'destroy'].include?(find_args.first))
333
- )
334
- @_brick_model = model
325
+ # Return an appropriate model for a given view template request.
326
+ # find_args will generally be something like: ["index", ["categories"]]
327
+ # and must cycle through all of find_args[1] because in some cases such as with Devise we get something like:
328
+ # ["create", ["users/sessions", "sessions", "devise/sessions", "devise", "application"], false, []]
329
+ find_args[1]&.any? do |resource_name|
330
+ if (class_name = (resource_parts = resource_name.split('/')).last&.singularize)
331
+ resource_parts[-1] = class_name # Make sure the last part, defining the class name, is singular
332
+ begin
333
+ if (model = Object.const_get(resource_parts.map(&:camelize).join('::')))&.is_a?(Class) && (
334
+ ['index', 'show'].include?(find_args.first) || # Everything has index and show
335
+ # Only CUD stuff has create / update / destroy
336
+ (!model.is_view? && ['new', 'create', 'edit', 'update', 'destroy'].include?(find_args.first))
337
+ )
338
+ @_brick_model = model
339
+ end
340
+ rescue
341
+ end
335
342
  end
336
343
  end
344
+ @_brick_model
337
345
  end
338
346
 
339
347
  def path_keys(hm_assoc, fk_name, pk)
@@ -356,6 +364,7 @@ window.addEventListener(\"popstate\", linkSchemas);
356
364
 
357
365
  alias :_brick_find_template :find_template
358
366
  def find_template(*args, **options)
367
+ find_template_err = nil
359
368
  unless (model_name = @_brick_model&.name) ||
360
369
  (is_status = ::Brick.config.add_status && args[0..1] == ['status', ['brick_gem']]) ||
361
370
  (is_orphans = ::Brick.config.add_orphans && args[0..1] == ['orphans', ['brick_gem']]) ||
@@ -364,10 +373,11 @@ window.addEventListener(\"popstate\", linkSchemas);
364
373
  if (possible_template = _brick_find_template(*args, **options))
365
374
  return possible_template
366
375
  end
367
- rescue
376
+ rescue StandardError => e
377
+ find_template_err = e # Can come up with stuff like Devise which has its own view templates
368
378
  end
369
379
  # Used to also have: ActionView.version < ::Gem::Version.new('5.0') &&
370
- model_name = (args[1].is_a?(Array) ? set_brick_model(args) : nil)&.name
380
+ model_name = set_brick_model(args)&.name
371
381
  end
372
382
 
373
383
  if @_brick_model
@@ -638,15 +648,19 @@ input+svg.revert {
638
648
  </style>
639
649
 
640
650
  <% is_includes_dates = nil
651
+ is_includes_json = nil
641
652
  def is_bcrypt?(val)
642
653
  val.is_a?(String) && val.length == 60 && val.start_with?('$2a$')
643
654
  end
644
- def hide_bcrypt(val, max_len = 200)
655
+ def hide_bcrypt(val, is_xml = nil, max_len = 200)
645
656
  if is_bcrypt?(val)
646
657
  '(hidden)'
647
658
  else
648
659
  if val.is_a?(String)
649
- if (val = val.dup.strip).length > max_len
660
+ val = val.dup.force_encoding('UTF-8').strip
661
+ return CGI.escapeHTML(val) if is_xml
662
+
663
+ if val.length > max_len
650
664
  if val[0] == '<' # Seems to be HTML?
651
665
  cur_len = 0
652
666
  cur_idx = 0
@@ -693,7 +707,6 @@ def hide_bcrypt(val, max_len = 200)
693
707
  end
694
708
  val = \"#\{val}...\"
695
709
  end
696
- val = val.dup.force_encoding('UTF-8') unless val.encoding.name == 'UTF-8'
697
710
  val
698
711
  else
699
712
  val.to_s
@@ -758,7 +771,7 @@ def display_value(col_type, val)
758
771
  display_binary(val) if val
759
772
  else
760
773
  if col_type
761
- hide_bcrypt(val)
774
+ hide_bcrypt(val, col_type == :xml)
762
775
  else
763
776
  '?'
764
777
  end
@@ -1154,7 +1167,7 @@ erDiagram
1154
1167
  td_count += 1 %>
1155
1168
  <td><%= link_to_brick(
1156
1169
  avo_svg,
1157
- { index_proc: Proc.new do |avo_model, relation|
1170
+ { index_proc: Proc.new do |_avo_model, relation|
1158
1171
  path_helper = \"resources_#\{relation.fetch(:auto_prefixed_schema, nil)}#\{model.model_name.route_key}_path\".to_sym
1159
1172
  ::Avo.railtie_routes_url_helpers.send(path_helper) if ::Avo.railtie_routes_url_helpers.respond_to?(path_helper)
1160
1173
  end,
@@ -1429,18 +1442,16 @@ end
1429
1442
  when :string, :text %>
1430
1443
  <% if is_bcrypt?(val) # || .readonly?
1431
1444
  is_revert = false %>
1432
- <%= hide_bcrypt(val, 1000) %>
1445
+ <%= hide_bcrypt(val, nil, 1000) %>
1433
1446
  <% else %>
1434
1447
  <%= f.text_field(k.to_sym, html_options) %>
1435
1448
  <% end %>
1436
1449
  <% when :boolean %>
1437
1450
  <%= f.check_box k.to_sym %>
1438
1451
  <% when :integer, :decimal, :float %>
1439
- <%= if col_type == :integer
1440
- f.text_field k.to_sym, { pattern: '\\d*', class: 'check-validity' }
1441
- else
1442
- f.number_field k.to_sym
1443
- end %>
1452
+ <%= digit_pattern = col_type == :integer ? '\\d*' : '\\d*(?:\\.\\d*|)'
1453
+ # Used to do this for float / decimal: f.number_field k.to_sym
1454
+ f.text_field k.to_sym, { pattern: digit_pattern, class: 'check-validity' } %>
1444
1455
  <% when *dt_pickers.keys
1445
1456
  is_includes_dates = true %>
1446
1457
  <%= f.text_field k.to_sym, { class: dt_pickers[col_type] } %>
@@ -1468,6 +1479,13 @@ end
1468
1479
  end %>
1469
1480
  <% when :primary_key
1470
1481
  is_revert = false %>
1482
+ <% when :json
1483
+ is_includes_json = true %>
1484
+ <%= # Because there are so danged many quotes in JSON, escape them specially by converting to backticks.
1485
+ # (and previous to this, escape backticks with our own goofy code of ^^br_btick__ )
1486
+ val_str = val.is_a?(String) ? val : val.to_json # Clean up bogus JSON if necessary
1487
+ json_field = f.hidden_field k.to_sym, { class: 'jsonpicker', value: val_str.gsub('`', '^^br_btick__').tr('\"', '`').html_safe } %>
1488
+ <div id=\"_br_json_<%= f.field_id(k) %>\"></div>
1471
1489
  <% else %>
1472
1490
  <%= is_revert = false
1473
1491
  display_value(col_type, val).html_safe %>
@@ -1542,7 +1560,12 @@ end}
1542
1560
  </body>
1543
1561
  </html>
1544
1562
  "
1545
-
1563
+ else # args.first isn't index / show / edit / new / orphans / status
1564
+ if find_template_err # Can surface when gems have their own view templates
1565
+ raise find_template_err
1566
+ else # Can surface if someone made their own controller which has a screwy action
1567
+ puts "Couldn't work with action #{args.first}"
1568
+ end
1546
1569
  end
1547
1570
  unless is_crosstab
1548
1571
  inline << "
@@ -1566,6 +1589,35 @@ flatpickr(\".timepicker\", {enableTime: true, noCalendar: true});
1566
1589
  <link rel=\"stylesheet\" type=\"text/css\" href=\"https://cdnjs.cloudflare.com/ajax/libs/slim-select/1.27.1/slimselect.min.css\">
1567
1590
  <% end %>
1568
1591
 
1592
+ <% # Started with v0.14.4 of vanilla-jsoneditor
1593
+ if is_includes_json %>
1594
+ <link rel=\"stylesheet\" type=\"text/css\" href=\"https://cdn.jsdelivr.net/npm/vanilla-jsoneditor/themes/jse-theme-default.min.css\">
1595
+ <script type=\"module\">
1596
+ import { JSONEditor } from \"https://cdn.jsdelivr.net/npm/vanilla-jsoneditor/index.min.js\";
1597
+ document.querySelectorAll(\"input.jsonpicker\").forEach(function (inp) {
1598
+ var jsonDiv;
1599
+ if (jsonDiv = document.getElementById(\"_br_json_\" + inp.id)) {
1600
+ new JSONEditor({
1601
+ target: jsonDiv,
1602
+ props: {
1603
+ // Instead of text can also do: { json: JSONValue }
1604
+ // Other options: name: \"taco\", mode: \"tree\", navigationBar: false, mainMenuBar: false, statusBar: false, search: false, templates:, history: false
1605
+ content: {text: inp.value.replace(/`/g, '\"').replace(/\\^\\^br_btick__/g, \"`\")},
1606
+ onChange: (function (inp2) {
1607
+ return function (updatedContent, previousContent, contentErrors, patchResult) {
1608
+ // console.log('onChange', updatedContent.json, updatedContent.text);
1609
+ inp2.value = updatedContent.text || JSON.stringify(updatedContent.json);
1610
+ };
1611
+ })(inp)
1612
+ }
1613
+ });
1614
+ } else {
1615
+ console.log(\"Could not find JSON picker for \" + inp.id);
1616
+ }
1617
+ });
1618
+ </script>
1619
+ <% end %>
1620
+
1569
1621
  <% if true # @_brick_erd
1570
1622
  %>
1571
1623
  <script>
@@ -116,7 +116,7 @@ module Brick::Rails::FormTags
116
116
  end
117
117
  end
118
118
  elsif (col = cols[col_name]).is_a?(ActiveRecord::ConnectionAdapters::Column)
119
- binding.pry if col.is_a?(Array)
119
+ # binding.pry if col.is_a?(Array)
120
120
  col_type = col&.sql_type == 'geography' ? col.sql_type : col&.type
121
121
  out << display_value(col_type || col&.sql_type, val).to_s
122
122
  elsif cust_col
@@ -201,7 +201,8 @@ module Brick::Rails::FormTags
201
201
  end
202
202
  filter = "?#{filter_parts.join('&')}" if filter_parts.present?
203
203
  app_routes = Rails.application.routes # In case we're operating in another engine, reference the application since Brick routes are placed there.
204
- relation = ::Brick.relations.fetch(rel_name || args.first.table_name, nil)
204
+ klass = klass_or_obj.is_a?(ActiveRecord::Base) ? klass_or_obj.class : klass_or_obj
205
+ relation = ::Brick.relations.fetch(rel_name || klass.table_name, nil)
205
206
  if (klass_or_obj&.is_a?(Class) && klass_or_obj < ActiveRecord::Base) ||
206
207
  (klass_or_obj&.is_a?(ActiveRecord::Base) && klass_or_obj.new_record? && (klass_or_obj = klass_or_obj.class))
207
208
  path = (proc = kwargs[:index_proc]) ? proc.call(klass_or_obj, relation) : "#{app_routes.path_for(controller: klass_or_obj.base_class._brick_index(nil, '/', relation), action: :index)}#{filter}"
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 111
8
+ TINY = 113
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
@@ -654,270 +654,270 @@ In config/initializers/brick.rb appropriate entries would look something like:
654
654
 
655
655
  module RouteSet
656
656
  def finalize!
657
- # %%% Was: ::Rails.application.routes.named_routes.route_defined?(:brick_status_path)
658
- unless ::Rails.application.routes.named_routes.names.include?(:brick_status)
659
- path_prefix = ::Brick.config.path_prefix
660
- existing_controllers = routes.each_with_object({}) do |r, s|
661
- c = r.defaults[:controller]
662
- s[c] = nil if c
657
+ routeset_to_use = ::Rails.application.routes
658
+ return super unless self == routeset_to_use
659
+
660
+ path_prefix = ::Brick.config.path_prefix
661
+ existing_controllers = routes.each_with_object({}) do |r, s|
662
+ c = r.defaults[:controller]
663
+ s[c] = nil if c
664
+ end
665
+ append do
666
+ tables = []
667
+ views = []
668
+ table_class_length = 38 # Length of "Classes that can be built from tables:"
669
+ view_class_length = 37 # Length of "Classes that can be built from views:"
670
+
671
+ brick_namespace_create = lambda do |path_names, res_name, options|
672
+ if path_names&.present?
673
+ if (path_name = path_names.pop).is_a?(Array)
674
+ module_name = path_name[1]
675
+ path_name = path_name.first
676
+ end
677
+ send(:scope, { module: module_name || path_name, path: path_name, as: path_name }) do
678
+ brick_namespace_create.call(path_names, res_name, options)
679
+ end
680
+ else
681
+ send(:resources, res_name.to_sym, **options)
682
+ end
663
683
  end
664
- ::Rails.application.routes.append do
665
- tables = []
666
- views = []
667
- table_class_length = 38 # Length of "Classes that can be built from tables:"
668
- view_class_length = 37 # Length of "Classes that can be built from views:"
669
-
670
- brick_namespace_create = lambda do |path_names, res_name, options|
671
- if path_names&.present?
672
- if (path_name = path_names.pop).is_a?(Array)
673
- module_name = path_name[1]
674
- path_name = path_name.first
675
- end
676
- send(:scope, { module: module_name || path_name, path: path_name, as: path_name }) do
677
- brick_namespace_create.call(path_names, res_name, options)
678
- end
684
+
685
+ # %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
686
+ # If auto-controllers and auto-models are both enabled then this makes sense:
687
+ controller_prefix = (path_prefix ? "#{path_prefix}/" : '')
688
+ sti_subclasses = ::Brick.config.sti_namespace_prefixes.each_with_object(Hash.new { |h, k| h[k] = [] }) do |v, s|
689
+ # Turn something like {"::Spouse"=>"Person", "::Friend"=>"Person"} into {"Person"=>["Spouse", "Friend"]}
690
+ s[v.last] << v.first[2..-1] unless v.first.end_with?('::')
691
+ end
692
+ versioned_views = {} # Track which views have already been done for each api_root
693
+ ::Brick.relations.each do |k, v|
694
+ if (schema_name = v.fetch(:schema, nil))
695
+ schema_prefix = "#{schema_name}."
696
+ end
697
+
698
+ next if !(resource_name = v.fetch(:resource, nil)) ||
699
+ existing_controllers.key?(
700
+ controller_prefix + (resource_name = "#{schema_prefix&.tr('.', '/')}#{resource_name}".pluralize)
701
+ )
702
+
703
+ object_name = k.split('.').last # Take off any first schema part
704
+
705
+ full_schema_prefix = if (aps = v.fetch(:auto_prefixed_schema, nil))
706
+ aps = aps[0..-2] if aps[-1] == '_'
707
+ (schema_prefix&.dup || +'') << "#{aps}."
708
+ else
709
+ schema_prefix
710
+ end
711
+
712
+ # Track routes being built
713
+ if (class_name = v.fetch(:class_name, nil))
714
+ if v.key?(:isView)
715
+ view_class_length = class_name.length if class_name.length > view_class_length
716
+ views
679
717
  else
680
- send(:resources, res_name.to_sym, **options)
681
- end
718
+ table_class_length = class_name.length if class_name.length > table_class_length
719
+ tables
720
+ end << [class_name, aps, resource_name]
682
721
  end
683
722
 
684
- # %%% TODO: If no auto-controllers then enumerate the controllers folder in order to build matching routes
685
- # If auto-controllers and auto-models are both enabled then this makes sense:
686
- controller_prefix = (path_prefix ? "#{path_prefix}/" : '')
687
- sti_subclasses = ::Brick.config.sti_namespace_prefixes.each_with_object(Hash.new { |h, k| h[k] = [] }) do |v, s|
688
- # Turn something like {"::Spouse"=>"Person", "::Friend"=>"Person"} into {"Person"=>["Spouse", "Friend"]}
689
- s[v.last] << v.first[2..-1] unless v.first.end_with?('::')
690
- end
691
- versioned_views = {} # Track which views have already been done for each api_root
692
- ::Brick.relations.each do |k, v|
693
- if (schema_name = v.fetch(:schema, nil))
694
- schema_prefix = "#{schema_name}."
695
- end
723
+ options = {}
724
+ options[:only] = [:index, :show] if v.key?(:isView)
725
+
726
+ # First do the normal routes
727
+ prefixes = []
728
+ prefixes << [aps, v[:class_name]&.split('::')[-2]&.underscore] if aps
729
+ prefixes << schema_name if schema_name
730
+ prefixes << path_prefix if path_prefix
731
+ brick_namespace_create.call(prefixes, v[:resource], options)
732
+ sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
733
+ brick_namespace_create.call(prefixes, sc.underscore.tr('/', '_').pluralize, options)
734
+ end
696
735
 
697
- next if !(resource_name = v.fetch(:resource, nil)) ||
698
- existing_controllers.key?(controller_name = (
699
- resource_name = "#{schema_prefix&.tr('.', '/')}#{resource_name}"
700
- ).pluralize)
701
-
702
- object_name = k.split('.').last # Take off any first schema part
703
-
704
- full_schema_prefix = if (aps = v.fetch(:auto_prefixed_schema, nil))
705
- aps = aps[0..-2] if aps[-1] == '_'
706
- (schema_prefix&.dup || +'') << "#{aps}."
707
- else
708
- schema_prefix
709
- end
710
-
711
- # Track routes being built
712
- if (class_name = v.fetch(:class_name, nil))
713
- if v.key?(:isView)
714
- view_class_length = class_name.length if class_name.length > view_class_length
715
- views
736
+ # Now the API routes if necessary
737
+ full_resource = nil
738
+ ::Brick.api_roots&.each do |api_root|
739
+ api_done_views = (versioned_views[api_root] ||= {})
740
+ found = nil
741
+ test_ver_num = nil
742
+ view_relation = nil
743
+ # If it's a view then see if there's a versioned one available by searching for resource names
744
+ # versioned with the closest number (equal to or less than) compared with our API version number.
745
+ if v.key?(:isView)
746
+ if (ver = object_name.match(/^v([\d_]*)/)&.captures&.first) && ver[-1] == '_'
747
+ core_object_name = object_name[ver.length + 1..-1]
748
+ next if api_done_views.key?(unversioned = "#{schema_prefix}v_#{core_object_name}")
749
+
750
+ # Expect that the last item in the path generally holds versioning information
751
+ api_ver = api_root.split('/')[-1]&.gsub('_', '.')
752
+ vn_idx = api_ver.rindex(/[^\d._]/) # Position of the first numeric digit at the end of the version number
753
+ # Was: .to_d
754
+ test_ver_num = api_ver_num = api_ver[vn_idx + 1..-1].gsub('_', '.').to_i # Attempt to turn something like "v3" into the decimal value 3
755
+ # puts [api_ver, vn_idx, api_ver_num, unversioned].inspect
756
+
757
+ next if ver.to_i > api_ver_num # Don't surface any newer views in an older API
758
+
759
+ test_ver_num -= 1 until test_ver_num.zero? ||
760
+ (view_relation = ::Brick.relations.fetch(
761
+ found = "#{schema_prefix}v#{test_ver_num}_#{core_object_name}", nil
762
+ ))
763
+ api_done_views[unversioned] = nil # Mark that for this API version this view is done
764
+
765
+ # puts "Found #{found}" if view_relation
766
+ # If we haven't found "v3_view_name" or "v2_view_name" or so forth, at the last
767
+ # fall back to simply looking for "v_view_name", and then finally "view_name".
768
+ no_v_prefix_name = "#{schema_prefix}#{core_object_name}"
769
+ standard_prefix = 'v_'
716
770
  else
717
- table_class_length = class_name.length if class_name.length > table_class_length
718
- tables
719
- end << [class_name, aps, resource_name]
720
- end
721
-
722
- options = {}
723
- options[:only] = [:index, :show] if v.key?(:isView)
724
-
725
- # First do the normal routes
726
- prefixes = []
727
- prefixes << [aps, v[:class_name]&.split('::')[-2]&.underscore] if aps
728
- prefixes << schema_name if schema_name
729
- prefixes << path_prefix if path_prefix
730
- brick_namespace_create.call(prefixes, v[:resource], options)
731
- sti_subclasses.fetch(class_name, nil)&.each do |sc| # Add any STI subclass routes for this relation
732
- brick_namespace_create.call(prefixes, sc.underscore.tr('/', '_').pluralize, options)
771
+ core_object_name = object_name
772
+ end
773
+ if (rvp = ::Brick.config.api_remove_view_prefix) && core_object_name.start_with?(rvp)
774
+ core_object_name.slice!(0, rvp.length)
775
+ end
776
+ no_prefix_name = "#{schema_prefix}#{core_object_name}"
777
+ unversioned = "#{schema_prefix}#{standard_prefix}#{::Brick.config.api_add_view_prefix}#{core_object_name}"
778
+ else
779
+ unversioned = k
733
780
  end
734
781
 
735
- # Now the API routes if necessary
736
- full_resource = nil
737
- ::Brick.api_roots&.each do |api_root|
738
- api_done_views = (versioned_views[api_root] ||= {})
739
- found = nil
740
- test_ver_num = nil
741
- view_relation = nil
742
- # If it's a view then see if there's a versioned one available by searching for resource names
743
- # versioned with the closest number (equal to or less than) compared with our API version number.
744
- if v.key?(:isView)
745
- if (ver = object_name.match(/^v([\d_]*)/)&.captures&.first) && ver[-1] == '_'
746
- core_object_name = object_name[ver.length + 1..-1]
747
- next if api_done_views.key?(unversioned = "#{schema_prefix}v_#{core_object_name}")
748
-
749
- # Expect that the last item in the path generally holds versioning information
750
- api_ver = api_root.split('/')[-1]&.gsub('_', '.')
751
- vn_idx = api_ver.rindex(/[^\d._]/) # Position of the first numeric digit at the end of the version number
752
- # Was: .to_d
753
- test_ver_num = api_ver_num = api_ver[vn_idx + 1..-1].gsub('_', '.').to_i # Attempt to turn something like "v3" into the decimal value 3
754
- # puts [api_ver, vn_idx, api_ver_num, unversioned].inspect
755
-
756
- next if ver.to_i > api_ver_num # Don't surface any newer views in an older API
757
-
758
- test_ver_num -= 1 until test_ver_num.zero? ||
759
- (view_relation = ::Brick.relations.fetch(
760
- found = "#{schema_prefix}v#{test_ver_num}_#{core_object_name}", nil
761
- ))
762
- api_done_views[unversioned] = nil # Mark that for this API version this view is done
763
-
764
- # puts "Found #{found}" if view_relation
765
- # If we haven't found "v3_view_name" or "v2_view_name" or so forth, at the last
766
- # fall back to simply looking for "v_view_name", and then finally "view_name".
767
- no_v_prefix_name = "#{schema_prefix}#{core_object_name}"
768
- standard_prefix = 'v_'
769
- else
770
- core_object_name = object_name
771
- end
772
- if (rvp = ::Brick.config.api_remove_view_prefix) && core_object_name.start_with?(rvp)
773
- core_object_name.slice!(0, rvp.length)
782
+ view_relation ||= ::Brick.relations.fetch(found = unversioned, nil) ||
783
+ (no_v_prefix_name && ::Brick.relations.fetch(found = no_v_prefix_name, nil)) ||
784
+ (no_prefix_name && ::Brick.relations.fetch(found = no_prefix_name, nil))
785
+ if view_relation
786
+ actions = view_relation.key?(:isView) ? [:index, :show] : ::Brick::ALL_API_ACTIONS # By default all actions are allowed
787
+ # Call proc that limits which endpoints get surfaced based on version, table or view name, method (get list / get one / post / patch / delete)
788
+ # Returning nil makes it do nothing, false makes it skip creating this endpoint, and an array of up to
789
+ # these 3 things controls and changes the nature of the endpoint that gets built:
790
+ # (updated api_name, name of different relation to route to, allowed actions such as :index, :show, :create, etc)
791
+ proc_result = if (filter = ::Brick.config.api_filter).is_a?(Proc)
792
+ begin
793
+ num_args = filter.arity.negative? ? 6 : filter.arity
794
+ filter.call(*[unversioned, k, view_relation, actions, api_ver_num, found, test_ver_num][0...num_args])
795
+ rescue StandardError => e
796
+ puts "::Brick.api_filter Proc error: #{e.message}"
797
+ end
798
+ end
799
+ # proc_result expects to receive back: [updated_api_name, to_other_relation, allowed_actions]
800
+
801
+ case proc_result
802
+ when NilClass
803
+ # Do nothing differently than what normal behaviour would be
804
+ when FalseClass # Skip implementing this endpoint
805
+ view_relation[:api][api_ver_num] = nil
806
+ next
807
+ when Array # Did they give back an array of actions?
808
+ unless proc_result.any? { |pr| ::Brick::ALL_API_ACTIONS.exclude?(pr) }
809
+ proc_result = [unversioned, to_relation, proc_result]
774
810
  end
775
- no_prefix_name = "#{schema_prefix}#{core_object_name}"
776
- unversioned = "#{schema_prefix}#{standard_prefix}#{::Brick.config.api_add_view_prefix}#{core_object_name}"
811
+ # Otherwise don't change this array because it's probably legit
812
+ when String
813
+ proc_result = [proc_result] # Treat this as the surfaced api_name (path) they want to use for this endpoint
777
814
  else
778
- unversioned = k
815
+ puts "::Brick.api_filter Proc warning: Unable to parse this result returned: \n #{proc_result.inspect}"
816
+ proc_result = nil # Couldn't understand what in the world was returned
779
817
  end
780
818
 
781
- view_relation ||= ::Brick.relations.fetch(found = unversioned, nil) ||
782
- (no_v_prefix_name && ::Brick.relations.fetch(found = no_v_prefix_name, nil)) ||
783
- (no_prefix_name && ::Brick.relations.fetch(found = no_prefix_name, nil))
784
- if view_relation
785
- actions = view_relation.key?(:isView) ? [:index, :show] : ::Brick::ALL_API_ACTIONS # By default all actions are allowed
786
- # Call proc that limits which endpoints get surfaced based on version, table or view name, method (get list / get one / post / patch / delete)
787
- # Returning nil makes it do nothing, false makes it skip creating this endpoint, and an array of up to
788
- # these 3 things controls and changes the nature of the endpoint that gets built:
789
- # (updated api_name, name of different relation to route to, allowed actions such as :index, :show, :create, etc)
790
- proc_result = if (filter = ::Brick.config.api_filter).is_a?(Proc)
791
- begin
792
- num_args = filter.arity.negative? ? 6 : filter.arity
793
- filter.call(*[unversioned, k, view_relation, actions, api_ver_num, found, test_ver_num][0...num_args])
794
- rescue StandardError => e
795
- puts "::Brick.api_filter Proc error: #{e.message}"
796
- end
797
- end
798
- # proc_result expects to receive back: [updated_api_name, to_other_relation, allowed_actions]
799
-
800
- case proc_result
801
- when NilClass
802
- # Do nothing differently than what normal behaviour would be
803
- when FalseClass # Skip implementing this endpoint
804
- view_relation[:api][api_ver_num] = nil
805
- next
806
- when Array # Did they give back an array of actions?
807
- unless proc_result.any? { |pr| ::Brick::ALL_API_ACTIONS.exclude?(pr) }
808
- proc_result = [unversioned, to_relation, proc_result]
819
+ if proc_result&.present?
820
+ if proc_result[1] # to_other_relation
821
+ if (new_view_relation = ::Brick.relations.fetch(proc_result[1], nil))
822
+ k = proc_result[1] # Route this call over to this different relation
823
+ view_relation = new_view_relation
824
+ else
825
+ puts "::Brick.api_filter Proc warning: Unable to find new suggested relation with name #{proc_result[1]} -- sticking with #{k} instead."
809
826
  end
810
- # Otherwise don't change this array because it's probably legit
811
- when String
812
- proc_result = [proc_result] # Treat this as the surfaced api_name (path) they want to use for this endpoint
813
- else
814
- puts "::Brick.api_filter Proc warning: Unable to parse this result returned: \n #{proc_result.inspect}"
815
- proc_result = nil # Couldn't understand what in the world was returned
816
827
  end
817
-
818
- if proc_result&.present?
819
- if proc_result[1] # to_other_relation
820
- if (new_view_relation = ::Brick.relations.fetch(proc_result[1], nil))
821
- k = proc_result[1] # Route this call over to this different relation
822
- view_relation = new_view_relation
823
- else
824
- puts "::Brick.api_filter Proc warning: Unable to find new suggested relation with name #{proc_result[1]} -- sticking with #{k} instead."
825
- end
826
- end
827
- if proc_result.first&.!=(k) # updated_api_name -- a different name than this relation would normally have
828
- found = proc_result.first
829
- end
830
- actions &= proc_result[2] if proc_result[2] # allowed_actions
828
+ if proc_result.first&.!=(k) # updated_api_name -- a different name than this relation would normally have
829
+ found = proc_result.first
831
830
  end
832
- (view_relation[:api][api_ver_num] ||= {})[unversioned] = actions # Add to the list of API paths this resource responds to
833
-
834
- # view_ver_num = if (first_part = k.split('_').first) =~ /^v[\d_]+/
835
- # first_part[1..-1].gsub('_', '.').to_i
836
- # end
837
- controller_name = if (last = view_relation.fetch(:resource, nil)&.pluralize)
838
- "#{full_schema_prefix}#{last}"
839
- else
840
- found
841
- end.tr('.', '/')
842
-
843
- { :index => 'get', :create => 'post' }.each do |action, method|
844
- if actions.include?(action)
845
- # Normally goes to something like: /api/v1/employees
846
- send(method, "#{api_root}#{unversioned.tr('.', '/')}", { to: "#{controller_prefix}#{controller_name}##{action}" })
847
- end
831
+ actions &= proc_result[2] if proc_result[2] # allowed_actions
832
+ end
833
+ (view_relation[:api][api_ver_num] ||= {})[unversioned] = actions # Add to the list of API paths this resource responds to
834
+
835
+ # view_ver_num = if (first_part = k.split('_').first) =~ /^v[\d_]+/
836
+ # first_part[1..-1].gsub('_', '.').to_i
837
+ # end
838
+ controller_name = if (last = view_relation.fetch(:resource, nil)&.pluralize)
839
+ "#{full_schema_prefix}#{last}"
840
+ else
841
+ found
842
+ end.tr('.', '/')
843
+
844
+ { :index => 'get', :create => 'post' }.each do |action, method|
845
+ if actions.include?(action)
846
+ # Normally goes to something like: /api/v1/employees
847
+ send(method, "#{api_root}#{unversioned.tr('.', '/')}", { to: "#{controller_prefix}#{controller_name}##{action}" })
848
848
  end
849
- # %%% We do not yet surface the #show action
850
- if (id_col = view_relation[:pk]&.first) # ID-dependent stuff
851
- { :update => ['put', 'patch'], :destroy => ['delete'] }.each do |action, methods|
852
- if actions.include?(action)
853
- methods.each do |method|
854
- send(method, "#{api_root}#{unversioned.tr('.', '/')}/:#{id_col}", { to: "#{controller_prefix}#{controller_name}##{action}" })
855
- end
849
+ end
850
+ # %%% We do not yet surface the #show action
851
+ if (id_col = view_relation[:pk]&.first) # ID-dependent stuff
852
+ { :update => ['put', 'patch'], :destroy => ['delete'] }.each do |action, methods|
853
+ if actions.include?(action)
854
+ methods.each do |method|
855
+ send(method, "#{api_root}#{unversioned.tr('.', '/')}/:#{id_col}", { to: "#{controller_prefix}#{controller_name}##{action}" })
856
856
  end
857
857
  end
858
858
  end
859
859
  end
860
860
  end
861
861
  end
862
+ end
862
863
 
863
- if ::Brick.config.add_status && instance_variable_get(:@set).named_routes.names.exclude?(:brick_status)
864
- get("/#{controller_prefix}brick_status", to: 'brick_gem#status', as: 'brick_status')
865
- end
864
+ if ::Brick.config.add_status && instance_variable_get(:@set).named_routes.names.exclude?(:brick_status)
865
+ get("/#{controller_prefix}brick_status", to: 'brick_gem#status', as: 'brick_status')
866
+ end
866
867
 
867
- if ::Brick.config.add_orphans && instance_variable_get(:@set).named_routes.names.exclude?(:brick_orphans)
868
- get("/#{controller_prefix}brick_orphans", to: 'brick_gem#orphans', as: 'brick_orphans')
869
- end
868
+ if ::Brick.config.add_orphans && instance_variable_get(:@set).named_routes.names.exclude?(:brick_orphans)
869
+ get("/#{controller_prefix}brick_orphans", to: 'brick_gem#orphans', as: 'brick_orphans')
870
+ end
870
871
 
871
- if instance_variable_get(:@set).named_routes.names.exclude?(:brick_crosstab)
872
- get("/#{controller_prefix}brick_crosstab", to: 'brick_gem#crosstab', as: 'brick_crosstab')
873
- get("/#{controller_prefix}brick_crosstab/data", to: 'brick_gem#crosstab_data')
874
- end
872
+ if instance_variable_get(:@set).named_routes.names.exclude?(:brick_crosstab)
873
+ get("/#{controller_prefix}brick_crosstab", to: 'brick_gem#crosstab', as: 'brick_crosstab')
874
+ get("/#{controller_prefix}brick_crosstab/data", to: 'brick_gem#crosstab_data')
875
+ end
875
876
 
876
- unless ::Brick.routes_done
877
- if Object.const_defined?('Rswag::Ui')
878
- rswag_path = ::Rails.application.routes.routes.find { |r| r.app.app == Rswag::Ui::Engine }&.instance_variable_get(:@path_formatter)&.instance_variable_get(:@parts)&.join
879
- first_endpoint_parts = nil
880
- (doc_endpoints = Rswag::Ui.config.config_object[:urls])&.each do |doc_endpoint|
881
- puts "Mounting OpenApi 3.0 documentation endpoint for \"#{doc_endpoint[:name]}\" on #{doc_endpoint[:url]}"
882
- send(:get, doc_endpoint[:url], { to: 'brick_openapi#index' })
883
- endpoint_parts = doc_endpoint[:url]&.split('/')
884
- first_endpoint_parts ||= endpoint_parts
885
- end
886
- if doc_endpoints.present?
887
- if rswag_path && first_endpoint_parts
888
- puts "API documentation now available when navigating to: /#{first_endpoint_parts&.find(&:present?)}/index.html"
889
- else
890
- puts "In order to make documentation available you can put this into your routes.rb:"
891
- puts " mount Rswag::Ui::Engine => '/#{first_endpoint_parts&.find(&:present?) || 'api-docs'}'"
892
- end
877
+ unless ::Brick.routes_done
878
+ if Object.const_defined?('Rswag::Ui')
879
+ rswag_path = routeset_to_use.routes.find { |r| r.app.app == Rswag::Ui::Engine }&.instance_variable_get(:@path_formatter)&.instance_variable_get(:@parts)&.join
880
+ first_endpoint_parts = nil
881
+ (doc_endpoints = Rswag::Ui.config.config_object[:urls])&.each do |doc_endpoint|
882
+ puts "Mounting OpenApi 3.0 documentation endpoint for \"#{doc_endpoint[:name]}\" on #{doc_endpoint[:url]}"
883
+ send(:get, doc_endpoint[:url], { to: 'brick_openapi#index' })
884
+ endpoint_parts = doc_endpoint[:url]&.split('/')
885
+ first_endpoint_parts ||= endpoint_parts
886
+ end
887
+ if doc_endpoints.present?
888
+ if rswag_path && first_endpoint_parts
889
+ puts "API documentation now available when navigating to: /#{first_endpoint_parts&.find(&:present?)}/index.html"
893
890
  else
894
- sample_path = rswag_path || '/api-docs'
891
+ puts "In order to make documentation available you can put this into your routes.rb:"
892
+ puts " mount Rswag::Ui::Engine => '/#{first_endpoint_parts&.find(&:present?) || 'api-docs'}'"
893
+ end
894
+ else
895
+ sample_path = rswag_path || '/api-docs'
896
+ puts
897
+ puts "Brick: rswag-ui gem detected -- to make OpenAPI 3.0 documentation available from a path such as '#{sample_path}/v1/swagger.json',"
898
+ puts ' put code such as this in an initializer:'
899
+ puts ' Rswag::Ui.configure do |config|'
900
+ puts " config.swagger_endpoint '#{sample_path}/v1/swagger.json', 'API V1 Docs'"
901
+ puts ' end'
902
+ unless rswag_path
895
903
  puts
896
- puts "Brick: rswag-ui gem detected -- to make OpenAPI 3.0 documentation available from a path such as '#{sample_path}/v1/swagger.json',"
897
- puts ' put code such as this in an initializer:'
898
- puts ' Rswag::Ui.configure do |config|'
899
- puts " config.swagger_endpoint '#{sample_path}/v1/swagger.json', 'API V1 Docs'"
900
- puts ' end'
901
- unless rswag_path
902
- puts
903
- puts ' and put this into your routes.rb:'
904
- puts " mount Rswag::Ui::Engine => '/api-docs'"
905
- end
904
+ puts ' and put this into your routes.rb:'
905
+ puts " mount Rswag::Ui::Engine => '/api-docs'"
906
906
  end
907
907
  end
908
+ end
908
909
 
909
- ::Brick.routes_done = true
910
- puts "\n" if tables.present? || views.present?
911
- if tables.present?
912
- puts "Classes that can be built from tables:#{' ' * (table_class_length - 38)} Path:"
913
- puts "======================================#{' ' * (table_class_length - 38)} ====="
914
- ::Brick.display_classes(controller_prefix, tables, table_class_length)
915
- end
916
- if views.present?
917
- puts "Classes that can be built from views:#{' ' * (view_class_length - 37)} Path:"
918
- puts "=====================================#{' ' * (view_class_length - 37)} ====="
919
- ::Brick.display_classes(controller_prefix, views, view_class_length)
920
- end
910
+ ::Brick.routes_done = true
911
+ puts "\n" if tables.present? || views.present?
912
+ if tables.present?
913
+ puts "Classes that can be built from tables:#{' ' * (table_class_length - 38)} Path:"
914
+ puts "======================================#{' ' * (table_class_length - 38)} ====="
915
+ ::Brick.display_classes(controller_prefix, tables, table_class_length)
916
+ end
917
+ if views.present?
918
+ puts "Classes that can be built from views:#{' ' * (view_class_length - 37)} Path:"
919
+ puts "=====================================#{' ' * (view_class_length - 37)} ====="
920
+ ::Brick.display_classes(controller_prefix, views, view_class_length)
921
921
  end
922
922
  end
923
923
  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.111
4
+ version: 1.0.113
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-02-08 00:00:00.000000000 Z
11
+ date: 2023-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord