brick 1.0.104 → 1.0.105

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: 6d30161a8558282149a49e7eafbd43bb175ce95574178c65a75323eaacf96422
4
- data.tar.gz: 6d56c5e77c2a0d120bc8e878bde9d33fdfa7d58bed690a21fea176d5d6b55d50
3
+ metadata.gz: 48fc69654d7cbdf5c7feb9ba7803b7a953c6c0ccd3c176614fe9a61994bb39e1
4
+ data.tar.gz: f3a4837d334cf97cb491cacd658cb6e6d8e3f3a5c640f80c151a083bf6811b7a
5
5
  SHA512:
6
- metadata.gz: 14bfffec6fd768d4b3abff2b3ceaeb2a2dca29b167edd64c2e57a63c72eaa70bf46fc8056af25fca86812bfac63d3b793bdeaf7f13a8c2ba7834ac9e65f1fb16
7
- data.tar.gz: 97f7c94051bb068c4d8bdb5e847e53576c8ebbe18d5c4c93e12d52566008ac612bb40f7088ba81fea21337a3c13c29f366fb993b77f2f88f9fbddea4ffb95982
6
+ metadata.gz: e51134710bae78f033b9dcf7827cd7c087cfd642ef8bc6244e2429f22158a80e9f804022bc703f55fa55f85cbc009ff25440289205ade1b415f5ba0af3d44c77
7
+ data.tar.gz: d6069676ad70e8401f7597b92b641f0f95e3c28fffa54747f630932500c778d7b4ac967c5bbd70059647d11d398598362d9c0bb5c82ef0500b1025371104a578
data/lib/brick/config.rb CHANGED
@@ -94,13 +94,13 @@ module Brick
94
94
  @mutex.synchronize { @enable_api = enable }
95
95
  end
96
96
 
97
- def api_root
97
+ def api_roots
98
98
  ver = api_version
99
- @mutex.synchronize { @api_root || "/api/#{ver}/" }
99
+ @mutex.synchronize { @api_roots || ["/api/#{ver}/"] }
100
100
  end
101
101
 
102
- def api_root=(path)
103
- @mutex.synchronize { @api_root = path }
102
+ def api_roots=(path)
103
+ @mutex.synchronize { @api_roots = path }
104
104
  end
105
105
 
106
106
  def api_version
@@ -1526,7 +1526,11 @@ class Object
1526
1526
  self.protect_from_forgery unless: -> { self.request.format.js? }
1527
1527
  unless is_avo
1528
1528
  self.define_method :index do
1529
- if (is_openapi || request.env['REQUEST_PATH'].start_with?(::Brick.api_root)) &&
1529
+ current_api_root = ::Brick.config.api_roots.find do |ar|
1530
+ request.path.start_with?(ar) || # Exact match?
1531
+ request.path.split('/')[-2] == ar.split('/').last # Version at least matches?
1532
+ end
1533
+ if (current_api_root || is_openapi) &&
1530
1534
  !params&.key?('_brick_schema') &&
1531
1535
  (referrer_params = request.env['HTTP_REFERER']&.split('?')&.last&.split('&')&.map { |x| x.split('=') }).present?
1532
1536
  if params
@@ -1538,6 +1542,7 @@ class Object
1538
1542
  _schema, @_is_show_schema_list = ::Brick.set_db_schema(params || api_params)
1539
1543
 
1540
1544
  if is_openapi
1545
+ current_api_ver = current_api_root.split('/').last&.[](1..-1).to_i
1541
1546
  json = { 'openapi': '3.0.1', 'info': { 'title': Rswag::Ui.config.config_object[:urls].last&.fetch(:name, 'API documentation'), 'version': ::Brick.config.api_version },
1542
1547
  'servers': [
1543
1548
  { 'url': '{scheme}://{defaultHost}', 'variables': {
@@ -1546,15 +1551,19 @@ class Object
1546
1551
  } }
1547
1552
  ]
1548
1553
  }
1549
- json['paths'] = relations.inject({}) do |s, relation|
1554
+ json['paths'] = relations.each_with_object({}) do |relation, s|
1550
1555
  unless ::Brick.config.enable_api == false
1556
+ next if (api_vers = relation.last.fetch(:api, nil)) &&
1557
+ !(api_ver_path = api_vers[current_api_ver])
1558
+
1559
+ relation_name = api_ver_path || relation.first.tr('.', '/')
1551
1560
  table_description = relation.last[:description]
1552
- s["#{::Brick.config.api_root}#{relation.first.tr('.', '/')}"] = {
1561
+ s["#{current_api_root}#{relation_name}"] = {
1553
1562
  'get': {
1554
1563
  'summary': "list #{relation.first}",
1555
1564
  'description': table_description,
1556
1565
  'parameters': relation.last[:cols].map do |k, v|
1557
- param = { 'name' => k, 'schema': { 'type': v.first } }
1566
+ param = { in: 'query', 'name' => k, 'schema': { 'type': v.first } }
1558
1567
  if (col_descrip = relation.last.fetch(:col_descrips, nil)&.fetch(k, nil))
1559
1568
  param['description'] = col_descrip
1560
1569
  end
@@ -1564,7 +1573,7 @@ class Object
1564
1573
  }
1565
1574
  }
1566
1575
 
1567
- s["#{::Brick.config.api_root}#{relation.first.tr('.', '/')}/{id}"] = {
1576
+ s["#{current_api_root}#{relation_name}/{id}"] = {
1568
1577
  'patch': {
1569
1578
  'summary': "update a #{relation.first.singularize}",
1570
1579
  'description': table_description,
@@ -1578,7 +1587,6 @@ class Object
1578
1587
  'responses': { '200': { 'description': 'successful' } }
1579
1588
  }
1580
1589
  } unless relation.last.fetch(:isView, nil)
1581
- s
1582
1590
  end
1583
1591
  end
1584
1592
  render inline: json.to_json, content_type: request.format
@@ -1592,9 +1600,10 @@ class Object
1592
1600
  end
1593
1601
  render inline: exported_csv, content_type: request.format
1594
1602
  return
1595
- elsif request.format == :js || request.path.start_with?('/api/') # Asking for JSON?
1603
+ elsif request.format == :js || current_api_root # Asking for JSON?
1604
+ # %%% Add: where, order, page, page_size, offset, limit
1596
1605
  data = (model.is_view? || !Object.const_defined?('DutyFree')) ? model.limit(1000) : model.df_export(model.brick_import_template)
1597
- render inline: data.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
1606
+ render inline: { data: data }.to_json, content_type: request.format == '*/*' ? 'application/json' : request.format
1598
1607
  return
1599
1608
  end
1600
1609
 
@@ -464,7 +464,7 @@ window.addEventListener(\"popstate\", linkSchemas);
464
464
  table_options << "<option value=\"#{prefix}brick_orphans\">(Orphans)</option>".html_safe if is_orphans
465
465
  table_options << "<option value=\"#{prefix}brick_orphans\">(Crosstab)</option>".html_safe if is_crosstab
466
466
  css = +"<style>
467
- #titleBox {
467
+ #titleSticky {
468
468
  position: sticky;
469
469
  display: inline-block;
470
470
  left: 0;
@@ -881,6 +881,7 @@ if (grid) {
881
881
  // });
882
882
  }
883
883
  function setHeaderSizes() {
884
+ document.getElementById(\"titleBox\").style.width = grid.clientWidth;
884
885
  // console.log(\"start\");
885
886
  // See if the headerTop is already populated
886
887
  // %%% Grab the TRs from headerTop, clear it out, do this stuff, add them back
@@ -1127,7 +1128,7 @@ erDiagram
1127
1128
  %></title>
1128
1129
  </head>
1129
1130
  <body>
1130
- <div id=\"titleBox\">
1131
+ <div id=\"titleBox\"><div id=\"titleSticky\">
1131
1132
  <p style=\"color: green\"><%= notice %></p>#{"
1132
1133
  #{schema_options}" if schema_options}
1133
1134
  <select id=\"tbl\">#{table_options}</select>
@@ -1182,7 +1183,7 @@ erDiagram
1182
1183
  });
1183
1184
  </script>
1184
1185
  <% end %>
1185
- </div>
1186
+ </div></div>
1186
1187
  #{erd_markup}
1187
1188
 
1188
1189
  <%= # Consider getting the name from the association -- hm.first.name -- if a more \"friendly\" alias should be used for a screwy table name
@@ -5,7 +5,7 @@ module Brick
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 104
8
+ TINY = 105
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
@@ -334,12 +334,17 @@ module Brick
334
334
 
335
335
  # @api public
336
336
  def api_root=(path)
337
- Brick.config.api_root = path
337
+ Brick.config.api_roots = [path]
338
338
  end
339
339
 
340
340
  # @api public
341
- def api_root
342
- Brick.config.api_root
341
+ def api_roots=(paths)
342
+ Brick.config.api_roots = paths
343
+ end
344
+
345
+ # @api public
346
+ def api_roots
347
+ Brick.config.api_roots
343
348
  end
344
349
 
345
350
  # @api public
@@ -657,19 +662,65 @@ In config/initializers/brick.rb appropriate entries would look something like:
657
662
  # Turn something like {"::Spouse"=>"Person", "::Friend"=>"Person"} into {"Person"=>["Spouse", "Friend"]}
658
663
  s[v.last] << v.first[2..-1] unless v.first.end_with?('::')
659
664
  end
665
+ versioned_views = {} # Track which views have already been done for each api_root
660
666
  ::Brick.relations.each do |k, v|
661
667
  next if !(controller_name = v.fetch(:resource, nil)&.pluralize) || existing_controllers.key?(controller_name)
662
668
 
669
+ schema_name = v.fetch(:schema, nil)
663
670
  options = {}
664
671
  options[:only] = [:index, :show] if v.key?(:isView)
665
- # First do the API routes
672
+ # First do the API routes if necessary
666
673
  full_resource = nil
667
- if (schema_name = v.fetch(:schema, nil))
668
- full_resource = "#{schema_name}/#{v[:resource]}"
669
- send(:get, "#{::Brick.api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
670
- else
671
- # Normally goes to something like: /api/v1/employees
672
- send(:get, "#{::Brick.api_root}#{v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" }) if Object.const_defined?('Rswag::Ui')
674
+ ::Brick.api_roots&.each do |api_root|
675
+ api_done_views = (versioned_views[api_root] ||= {})
676
+ found = nil
677
+ view_relation = nil
678
+ # If it's a view then see if there's a versioned one available by searching for resource names
679
+ # versioned with the closest number (equal to or less than) compared with our API version number.
680
+ if v.key?(:isView) && (ver = k.match(/^v([\d_]*)/).captures.first)[-1] == '_'
681
+ next if api_done_views.key?(unversioned = k[ver.length + 1..-1])
682
+
683
+ # # if ().length.positive? # Does it have a version number?
684
+ # try_num = (ver_num = (ver = ver[1..-1].gsub('_', '.')).to_d)
685
+
686
+ # Expect that the last item in the path generally holds versioning information
687
+ api_ver = api_root.split('/')[-1]&.gsub('_', '.')
688
+ vn_idx = api_ver.rindex(/[^\d._]/) # Position of the first numeric digit at the end of the version number
689
+ # Was: .to_d
690
+ api_ver_num = api_ver[vn_idx + 1..-1].gsub('_', '.').to_i # Attempt to turn something like "v3" into the decimal value 3
691
+ # puts [api_ver, vn_idx, api_ver_num, unversioned].inspect
692
+
693
+ next if ver.to_i > api_ver_num # Don't surface any newer views in an older API
694
+
695
+ api_ver_num -= 1 until api_ver_num.zero? ||
696
+ (view_relation = ::Brick.relations.fetch(
697
+ found = "v#{api_ver_num}_#{k[ver.length + 1..-1]}", nil
698
+ ))
699
+ api_done_views[unversioned] = nil # Mark that for this API version this view is done
700
+
701
+ # puts "Found #{found}" if view_relation
702
+ # If we haven't found "v3_view_name" or "v2_view_name" or so forth, at the last
703
+ # fall back to simply looking for "v_view_name", and then finally "view_name".
704
+ unversioned = "v_#{unversioned}"
705
+ view_relation ||= ::Brick.relations.fetch(found = unversioned,
706
+ ::Brick.relations.fetch(found = unversioned, nil)
707
+ )
708
+ if found && view_relation && k != (found = unversioned)
709
+ view_relation[:api][api_ver_num] = found
710
+ end
711
+ end
712
+
713
+ # view_ver_num = if (first_part = k.split('_').first) =~ /^v[\d_]+/
714
+ # first_part[1..-1].gsub('_', '.').to_i
715
+ # end
716
+ controller_name = view_relation.fetch(:resource, nil)&.pluralize if view_relation
717
+ if schema_name
718
+ full_resource = "#{schema_name}/#{found || v[:resource]}"
719
+ send(:get, "#{api_root}#{full_resource}", { to: "#{controller_prefix}#{schema_name}/#{controller_name}#index" })
720
+ else
721
+ # Normally goes to something like: /api/v1/employees
722
+ send(:get, "#{api_root}#{found || v[:resource]}", { to: "#{controller_prefix}#{controller_name}#index" })
723
+ end
673
724
  end
674
725
 
675
726
  # Track routes being built
@@ -716,15 +767,19 @@ In config/initializers/brick.rb appropriate entries would look something like:
716
767
  unless ::Brick.routes_done
717
768
  if Object.const_defined?('Rswag::Ui')
718
769
  rswag_path = ::Rails.application.routes.routes.find { |r| r.app.app == Rswag::Ui::Engine }&.instance_variable_get(:@path_formatter)&.instance_variable_get(:@parts)&.join
719
- if (doc_endpoint = Rswag::Ui.config.config_object[:urls]&.last)
770
+ first_endpoint_parts = nil
771
+ (doc_endpoints = Rswag::Ui.config.config_object[:urls]&.uniq!)&.each do |doc_endpoint|
720
772
  puts "Mounting OpenApi 3.0 documentation endpoint for \"#{doc_endpoint[:name]}\" on #{doc_endpoint[:url]}"
721
773
  send(:get, doc_endpoint[:url], { to: 'brick_openapi#index' })
722
774
  endpoint_parts = doc_endpoint[:url]&.split('/')
723
- if rswag_path && endpoint_parts
724
- puts "API documentation now available when navigating to: /#{endpoint_parts&.find(&:present?)}/index.html"
775
+ first_endpoint_parts ||= endpoint_parts
776
+ end
777
+ if doc_endpoints.present?
778
+ if rswag_path && first_endpoint_parts
779
+ puts "API documentation now available when navigating to: /#{first_endpoint_parts&.find(&:present?)}/index.html"
725
780
  else
726
781
  puts "In order to make documentation available you can put this into your routes.rb:"
727
- puts " mount Rswag::Ui::Engine => '/#{endpoint_parts&.find(&:present?) || 'api-docs'}'"
782
+ puts " mount Rswag::Ui::Engine => '/#{first_endpoint_parts&.find(&:present?) || 'api-docs'}'"
728
783
  end
729
784
  else
730
785
  sample_path = rswag_path || '/api-docs'
@@ -159,8 +159,9 @@ if ActiveRecord::Base.respond_to?(:brick_select)
159
159
  # Brick.enable_views = true # Setting this to \"false\" will disable views in development
160
160
 
161
161
  # # If The Brick sees that RSwag gem is present, it allows for API resources to be automatically served out.
162
- # # You can configure the root path for these resources:
163
- # ::Brick.api_root = '/api/v1/'
162
+ # # You can configure one or more root path(s) for these resources, and when there are multiple then an attempt
163
+ # # is made to return data from that version of the view or table name, or the most recent prior to that version:
164
+ # ::Brick.api_roots = ['/api/v1/']
164
165
  # # You may also want to add an OpenAPI 3.0 documentation endpoint using Rswag::Ui:
165
166
  # Rswag::Ui.configure do |config|
166
167
  # config.swagger_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs'
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.104
4
+ version: 1.0.105
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-01-15 00:00:00.000000000 Z
11
+ date: 2023-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
166
  version: 1.42.0
167
+ - !ruby/object:Gem::Dependency
168
+ name: mysql2
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '0.5'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '0.5'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: pg
169
183
  requirement: !ruby/object:Gem::Requirement