rest_framework 0.9.14 → 0.9.16

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.
@@ -2,13 +2,17 @@
2
2
  module RESTFramework::Mixins::BaseModelControllerMixin
3
3
  BASE64_REGEX = /data:(.*);base64,(.*)/
4
4
  BASE64_TRANSLATE = ->(field, value) {
5
+ return value unless BASE64_REGEX.match?(value)
6
+
5
7
  _, content_type, payload = value.match(BASE64_REGEX).to_a
6
8
  return {
7
9
  io: StringIO.new(Base64.decode64(payload)),
8
10
  content_type: content_type,
9
- filename: "image_#{field}#{Rack::Mime::MIME_TYPES.invert[content_type]}",
11
+ filename: "file_#{field}#{Rack::Mime::MIME_TYPES.invert[content_type]}",
10
12
  }
11
13
  }
14
+ ACTIVESTORAGE_KEYS = [:io, :content_type, :filename, :identify, :key]
15
+
12
16
  include RESTFramework::BaseControllerMixin
13
17
 
14
18
  RRF_BASE_MODEL_CONFIG = {
@@ -43,14 +47,24 @@ module RESTFramework::Mixins::BaseModelControllerMixin
43
47
  native_serializer_associations_limit_query_param: "associations_limit",
44
48
  native_serializer_include_associations_count: false,
45
49
 
46
- # Attributes for default model filtering, ordering, and searching.
47
- filterset_fields: nil,
50
+ # Attributes for filtering, ordering, and searching.
51
+ filter_backends: [
52
+ RESTFramework::QueryFilter,
53
+ RESTFramework::OrderingFilter,
54
+ RESTFramework::SearchFilter,
55
+ ],
56
+ filter_recordset_before_find: true,
57
+ filter_fields: nil,
48
58
  ordering_fields: nil,
49
59
  ordering_query_param: "ordering",
50
60
  ordering_no_reorder: false,
51
61
  search_fields: nil,
52
62
  search_query_param: "search",
53
63
  search_ilike: false,
64
+ ransack_options: nil,
65
+ ransack_query_param: "q",
66
+ ransack_distinct: true,
67
+ ransack_distinct_query_param: "distinct",
54
68
 
55
69
  # Options for association assignment.
56
70
  permit_id_assignment: true,
@@ -58,21 +72,11 @@ module RESTFramework::Mixins::BaseModelControllerMixin
58
72
 
59
73
  # Option for `recordset.create` vs `Model.create` behavior.
60
74
  create_from_recordset: true,
61
-
62
- # Control if filtering is done before find.
63
- filter_recordset_before_find: true,
64
-
65
- # Options for `ransack` filtering.
66
- ransack_options: nil,
67
- ransack_query_param: "q",
68
- ransack_distinct: true,
69
- ransack_distinct_query_param: "distinct",
70
75
  }
71
76
 
72
77
  module ClassMethods
73
78
  IGNORE_VALIDATORS_WITH_KEYS = [:if, :unless].freeze
74
79
 
75
- # Get the model for this controller.
76
80
  def get_model
77
81
  return @model if @model
78
82
  return (@model = self.model) if self.model
@@ -86,8 +90,8 @@ module RESTFramework::Mixins::BaseModelControllerMixin
86
90
  raise RESTFramework::UnknownModelError, self
87
91
  end
88
92
 
89
- # Override `get_label` to include ActiveRecord i18n-translated column names.
90
- def get_label(s)
93
+ # Override to include ActiveRecord i18n-translated column names.
94
+ def label_for(s)
91
95
  return self.get_model.human_attribute_name(s, default: super)
92
96
  end
93
97
 
@@ -115,10 +119,10 @@ module RESTFramework::Mixins::BaseModelControllerMixin
115
119
  end
116
120
 
117
121
  # Get a field's config, including defaults.
118
- def get_field_config(f)
122
+ def field_config_for(f)
119
123
  f = f.to_sym
120
- @_get_field_config ||= {}
121
- return @_get_field_config[f] if @_get_field_config[f]
124
+ @_field_config_for ||= {}
125
+ return @_field_config_for[f] if @_field_config_for[f]
122
126
 
123
127
  config = self.field_config&.dig(f) || {}
124
128
 
@@ -145,7 +149,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
145
149
  }.to_h.compact.presence
146
150
  end
147
151
 
148
- return @_get_field_config[f] = config.compact
152
+ return @_field_config_for[f] = config.compact
149
153
  end
150
154
 
151
155
  # Get metadata about the resource's fields.
@@ -171,7 +175,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
171
175
  metadata = {
172
176
  type: nil,
173
177
  kind: nil,
174
- label: self.get_label(f),
178
+ label: self.label_for(f),
175
179
  primary_key: nil,
176
180
  required: nil,
177
181
  read_only: nil,
@@ -303,14 +307,14 @@ module RESTFramework::Mixins::BaseModelControllerMixin
303
307
  end
304
308
 
305
309
  # Serialize any field config.
306
- metadata[:config] = self.get_field_config(f).presence
310
+ metadata[:config] = self.field_config_for(f).presence
307
311
 
308
312
  next [f, metadata.compact]
309
313
  }.to_h
310
314
  end
311
315
 
312
316
  # Get a hash of metadata to be rendered in the `OPTIONS` response.
313
- def get_options_metadata
317
+ def options_metadata
314
318
  return super.merge(
315
319
  {
316
320
  primary_key: self.get_model.primary_key,
@@ -396,9 +400,8 @@ module RESTFramework::Mixins::BaseModelControllerMixin
396
400
  return self.class.get_fields(input_fields: self.class.fields)
397
401
  end
398
402
 
399
- # Pass fields to get dynamic metadata based on which fields are available.
400
- def get_options_metadata
401
- return self.class.get_options_metadata
403
+ def options_metadata
404
+ return self.class.options_metadata
402
405
  end
403
406
 
404
407
  # Get a list of parameters allowed for the current action.
@@ -408,7 +411,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
408
411
  @_get_allowed_parameters = self.allowed_parameters
409
412
  return @_get_allowed_parameters if @_get_allowed_parameters
410
413
 
411
- # For fields, automatically add `_id`/`_ids` and `_attributes` variations for associations.
414
+ # Assemble strong parameters.
412
415
  variations = []
413
416
  hash_variations = {}
414
417
  reflections = self.class.get_model.reflections
@@ -417,12 +420,13 @@ module RESTFramework::Mixins::BaseModelControllerMixin
417
420
 
418
421
  # ActiveStorage Integration: `has_one_attached`.
419
422
  if reflections.key?("#{f}_attachment")
423
+ hash_variations[f] = ACTIVESTORAGE_KEYS
420
424
  next f
421
425
  end
422
426
 
423
427
  # ActiveStorage Integration: `has_many_attached`.
424
428
  if reflections.key?("#{f}_attachments")
425
- hash_variations[f] = []
429
+ hash_variations[f] = ACTIVESTORAGE_KEYS
426
430
  next nil
427
431
  end
428
432
 
@@ -434,6 +438,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
434
438
  # Return field if it's not an association.
435
439
  next f unless ref = reflections[f]
436
440
 
441
+ # Add `_id`/`_ids` variations for associations.
437
442
  if self.permit_id_assignment && id_field = RESTFramework::Utils.get_id_field(f, ref)
438
443
  if id_field.ends_with?("_ids")
439
444
  hash_variations[id_field] = []
@@ -442,31 +447,36 @@ module RESTFramework::Mixins::BaseModelControllerMixin
442
447
  end
443
448
  end
444
449
 
450
+ # Add `_attributes` variations for associations.
445
451
  if self.permit_nested_attributes_assignment
446
- hash_variations["#{f}_attributes"] = self.class.get_field_config(f)[:sub_fields]
452
+ hash_variations["#{f}_attributes"] = (
453
+ self.class.field_config_for(f)[:sub_fields] + ["_destroy"]
454
+ )
447
455
  end
448
456
 
449
- # Associations are not allowed to be submitted in their bare form.
457
+ # Associations are not allowed to be submitted in their bare form (if they are submitted that
458
+ # way, they will be translated to either ID assignment or nested attributes assignment).
450
459
  next nil
451
460
  }.compact
452
461
  @_get_allowed_parameters += variations
453
462
  @_get_allowed_parameters << hash_variations
463
+
454
464
  return @_get_allowed_parameters
455
465
  end
456
466
 
457
- # Get the configured serializer class, or `NativeSerializer` as a default.
458
- def get_serializer_class
467
+ def serializer_class
459
468
  return super || RESTFramework::NativeSerializer
460
469
  end
461
470
 
462
- # Get filtering backends, defaulting to using `ModelQueryFilter`, `ModelOrderingFilter`, and
463
- # `ModelSearchFilter`.
464
- def get_filter_backends
465
- return self.filter_backends || [
466
- RESTFramework::ModelQueryFilter,
467
- RESTFramework::ModelOrderingFilter,
468
- RESTFramework::ModelSearchFilter,
469
- ]
471
+ def apply_filters(data)
472
+ # TODO: Compatibility; remove in 1.0.
473
+ if filtered_data = self.try(:get_filtered_data, data)
474
+ return filtered_data
475
+ end
476
+
477
+ return self.filter_backends&.reduce(data) { |d, filter|
478
+ filter.new(controller: self).filter_data(d)
479
+ } || data
470
480
  end
471
481
 
472
482
  # Use strong parameters to filter the request body using the configured allowed parameters.
@@ -492,6 +502,45 @@ module RESTFramework::Mixins::BaseModelControllerMixin
492
502
  end
493
503
  end
494
504
 
505
+ # ActiveStorage Integration: Translate base64 encoded attachments to upload objects.
506
+ #
507
+ # rubocop:disable Layout/LineLength
508
+ #
509
+ # Example base64 images (red, green, and blue squares):
510
+ # data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mP8z8BQz0AEYBxVSF+FABJADveWkH6oAAAAAElFTkSuQmCC
511
+ # data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNk+M9Qz0AEYBxVSF+FAAhKDveksOjmAAAAAElFTkSuQmCC
512
+ # data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC
513
+ #
514
+ # rubocop:enable Layout/LineLength
515
+ has_many_attached_scalar_data = {}
516
+ self.class.get_model.attachment_reflections.keys.each do |k|
517
+ if data[k].is_a?(Array)
518
+ data[k] = data[k].map { |v|
519
+ if v.is_a?(String)
520
+ v = BASE64_TRANSLATE.call(k, v)
521
+
522
+ # Remember scalars because Rails strong params will remove it.
523
+ if v.is_a?(String)
524
+ has_many_attached_scalar_data[k] ||= []
525
+ has_many_attached_scalar_data[k] << v
526
+ end
527
+ elsif v.is_a?(Hash)
528
+ if v[:io].is_a?(String)
529
+ v[:io] = StringIO.new(Base64.decode64(v[:io]))
530
+ end
531
+ end
532
+
533
+ next v
534
+ }
535
+ elsif data[k].is_a?(Hash)
536
+ if data[k][:io].is_a?(String)
537
+ data[k][:io] = StringIO.new(Base64.decode64(data[k][:io]))
538
+ end
539
+ elsif data[k].is_a?(String)
540
+ data[k] = BASE64_TRANSLATE.call(k, data[k])
541
+ end
542
+ end
543
+
495
544
  # Filter the request body with strong params. If `bulk` is true, then we apply allowed
496
545
  # parameters to the `_json` key of the request body.
497
546
  body_params = if allowed_params == true
@@ -503,6 +552,15 @@ module RESTFramework::Mixins::BaseModelControllerMixin
503
552
  ActionController::Parameters.new(data).permit(*allowed_params)
504
553
  end
505
554
 
555
+ # ActiveStorage Integration: Workaround for Rails strong params not allowing you to permit an
556
+ # array containing a mix of scalars and hashes. This is needed for `has_many_attached`, because
557
+ # API consumers must be able to provide scalar `signed_id` values for existing attachments along
558
+ # with hashes for new attachments. It's worth mentioning that base64 scalars are converted to
559
+ # hashes that conform to the ActiveStorage API.
560
+ has_many_attached_scalar_data.each do |k, v|
561
+ body_params[k].unshift(*v)
562
+ end
563
+
506
564
  # Filter primary key, if configured.
507
565
  if self.filter_pk_from_request_body && bulk_mode != :update
508
566
  body_params.delete(pk)
@@ -511,27 +569,6 @@ module RESTFramework::Mixins::BaseModelControllerMixin
511
569
  # Filter fields in `exclude_body_fields`.
512
570
  (self.exclude_body_fields || []).each { |f| body_params.delete(f) }
513
571
 
514
- # ActiveStorage Integration: Translate base64 encoded attachments to upload objects.
515
- #
516
- # rubocop:disable Layout/LineLength
517
- #
518
- # Example base64 image:
519
- # data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=
520
- #
521
- # rubocop:enable Layout/LineLength
522
- self.class.get_model.attachment_reflections.keys.each do |k|
523
- next unless (body_params[k].is_a?(String) && body_params[k].match?(BASE64_REGEX)) ||
524
- (body_params[k].is_a?(Array) && body_params[k].all? { |v|
525
- v.is_a?(String) && v.match?(BASE64_REGEX)
526
- })
527
-
528
- if body_params[k].is_a?(Array)
529
- body_params[k] = body_params[k].map { |v| BASE64_TRANSLATE.call(k, v) }
530
- else
531
- body_params[k] = BASE64_TRANSLATE.call(k, body_params[k])
532
- end
533
- end
534
-
535
572
  return body_params
536
573
  end
537
574
  alias_method :get_create_params, :get_body_params
@@ -551,13 +588,12 @@ module RESTFramework::Mixins::BaseModelControllerMixin
551
588
 
552
589
  # Get the records this controller has access to *after* any filtering is applied.
553
590
  def get_records
554
- return @records ||= self.get_filtered_data(self.get_recordset)
591
+ return @records ||= self.apply_filters(self.get_recordset)
555
592
  end
556
593
 
557
- # Get a single record by primary key or another column, if allowed. The return value is cached and
558
- # exposed to the view as the `@record` instance variable.
594
+ # Get a single record by primary key or another column, if allowed. The return value is memoized
595
+ # and exposed to the view as the `@record` instance variable.
559
596
  def get_record
560
- # Cache the result.
561
597
  return @record if @record
562
598
 
563
599
  find_by_key = self.class.get_model.primary_key
@@ -582,7 +618,7 @@ module RESTFramework::Mixins::BaseModelControllerMixin
582
618
  self.get_recordset
583
619
  end
584
620
 
585
- # Return the record. Route key is always `:id` by Rails convention.
621
+ # Return the record. Route key is always `:id` by Rails' convention.
586
622
  if is_pk
587
623
  return @record = collection.find(request.path_parameters[:id])
588
624
  else
@@ -609,11 +645,9 @@ module RESTFramework::Mixins::BaseModelControllerMixin
609
645
  # This is kinda slow, so perhaps we should eventually integrate `errors` serialization into
610
646
  # the serializer directly. This would fail for active model serializers, but maybe we don't
611
647
  # care?
612
- serializer_class = self.get_serializer_class
648
+ s = RESTFramework::Utils.wrap_ams(self.serializer_class)
613
649
  serialized_records = records.map do |record|
614
- serializer_class.new(record, controller: self).serialize.merge!(
615
- {errors: record.errors.presence}.compact,
616
- )
650
+ s.new(record, controller: self).serialize.merge!({errors: record.errors.presence}.compact)
617
651
  end
618
652
 
619
653
  return serialized_records
@@ -195,7 +195,7 @@ class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers:
195
195
  attachment_reflections = @model.attachment_reflections
196
196
 
197
197
  fields.each do |f|
198
- field_config = @controller.class.get_field_config(f)
198
+ field_config = @controller.class.field_config_for(f)
199
199
  next if field_config[:write_only]
200
200
 
201
201
  if f.in?(column_names)
@@ -259,18 +259,22 @@ class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers:
259
259
  serializer_methods[f] = f
260
260
  includes_map[f] = {"#{f}_attachment": :blob}
261
261
  self.define_singleton_method(f) do |record|
262
- next record.send(f).attachment&.url
262
+ attached = record.send(f)
263
+ next attached.attachment ? {signed_id: attached.signed_id, url: attached.url} : nil
263
264
  end
264
265
  elsif ref.macro == :has_many_attached
265
266
  serializer_methods[f] = f
266
267
  includes_map[f] = {"#{f}_attachments": :blob}
267
268
  self.define_singleton_method(f) do |record|
268
269
  # Iterating the collection yields attachment objects.
269
- next record.send(f).map(&:url)
270
+ next record.send(f).map { |a| {signed_id: a.signed_id, url: a.url} }
270
271
  end
271
272
  end
272
273
  elsif @model.method_defined?(f)
273
274
  methods << f
275
+ else
276
+ # Assume anything else is a virtual column.
277
+ columns << f
274
278
  end
275
279
  end
276
280
 
@@ -28,7 +28,7 @@ module RESTFramework::Utils
28
28
  [f, {}]
29
29
  }.to_h if metadata[:fields].is_a?(Array)
30
30
  metadata[:fields]&.each do |field, cfg|
31
- cfg[:label] = controller.get_label(field) unless cfg[:label]
31
+ cfg[:label] = controller.label_for(field) unless cfg[:label]
32
32
  end
33
33
  end
34
34
 
@@ -47,7 +47,7 @@ module RESTFramework::Utils
47
47
 
48
48
  # Insert action label if it's not provided.
49
49
  if controller
50
- metadata[:label] ||= controller.get_label(k)
50
+ metadata[:label] ||= controller.label_for(k)
51
51
  end
52
52
 
53
53
  next [
@@ -159,9 +159,10 @@ module RESTFramework::Utils
159
159
  )
160
160
  parsed_fields += fields_hash[:include].map(&:to_s) if fields_hash[:include]
161
161
  parsed_fields -= fields_hash[:exclude].map(&:to_s) if fields_hash[:exclude]
162
+ parsed_fields -= fields_hash[:except].map(&:to_s) if fields_hash[:except]
162
163
 
163
164
  # Warn for any unknown keys.
164
- (fields_hash.keys - [:only, :include, :exclude]).each do |k|
165
+ (fields_hash.keys - [:only, :except, :include, :exclude]).each do |k|
165
166
  Rails.logger.warn("RRF: Unknown key in fields hash: #{k}")
166
167
  end
167
168
 
@@ -233,4 +234,13 @@ module RESTFramework::Utils
233
234
 
234
235
  return nil
235
236
  end
237
+
238
+ # Wrap a serializer with an adapter if it is an ActiveModel::Serializer.
239
+ def self.wrap_ams(s)
240
+ if defined?(ActiveModel::Serializer) && (s < ActiveModel::Serializer)
241
+ return RESTFramework::ActiveModelSerializerAdapterFactory.for(s)
242
+ end
243
+
244
+ return s
245
+ end
236
246
  end
@@ -31,57 +31,57 @@ module RESTFramework
31
31
  EXTERNAL_ASSETS = {
32
32
  # Bootstrap
33
33
  "bootstrap.min.css" => {
34
- url: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/css/bootstrap.min.css",
35
- sri: "sha384-aFq/bzH65dt+w6FI2ooMVUpc+21e0SRygnTpmBvdBgSdnuTN7QbdgL+OapgHtvPp",
34
+ url: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css",
35
+ sri: "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH",
36
36
  },
37
37
  "bootstrap.min.js" => {
38
- url: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/js/bootstrap.bundle.min.js",
39
- sri: "sha384-qKXV1j0HvMUeCBQ+QVp7JcfGl760yU08IQ+GpUo5hlbpg51QRiuqHAJz8+BrxE/N",
38
+ url: "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js",
39
+ sri: "sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz",
40
40
  },
41
41
 
42
42
  # Bootstrap Icons
43
43
  "bootstrap-icons.min.css" => {
44
- url: "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.4/font/bootstrap-icons.min.css",
44
+ url: "https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css",
45
45
  inline_fonts: true,
46
46
  },
47
47
 
48
48
  # Highlight.js
49
49
  "highlight.min.js" => {
50
- url: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js",
51
- sri: "sha512-bgHRAiTjGrzHzLyKOnpFvaEpGzJet3z4tZnXGjpsCcqOnAH6VGUx9frc5bcIhKTVLEiCO6vEhNAgx5jtLUYrfA==",
50
+ url: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.0/highlight.min.js",
51
+ sri: "sha512-6QBAC6Sxc4IF04SvIg0k78l5rP5YgVjmHX2NeArelbxM3JGj4imMqfNzEta3n+mi7iG3nupdLnl3QrbfjdXyTg==",
52
52
  },
53
53
  "highlight-json.min.js" => {
54
- url: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/json.min.js",
55
- sri: "sha512-0xYvyncS9OLE7GOpNBZFnwyh9+bq4HVgk4yVVYI678xRvE22ASicF1v6fZ1UiST+M6pn17MzFZdvVCI3jTHSyw==",
54
+ url: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.0/languages/json.min.js",
55
+ sri: "sha512-8JO7/pRnd1Ce8OBXWQg85e5wNPJdBaQdN8w4oDa+HelMXaLwCxTdbzdWHmJtWR9AmcI6dOln4FS5/KrzpxqqfQ==",
56
56
  },
57
57
  "highlight-xml.min.js" => {
58
- url: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/xml.min.js",
59
- sri: "sha512-5zBcw+OKRkaNyvUEPlTSfYylVzgpi7KpncY36b0gRudfxIYIH0q0kl2j26uCUB3YBRM6ytQQEZSgRg+ZlBTmdA==",
58
+ url: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.0/languages/xml.min.js",
59
+ sri: "sha512-/vq6wbS2Qkv8Hj4mP3Jd/m6MbnIrquzZiUt9tIluQfe332IQeFDrSIK7j2cjAyn6/9Ntb2WMPbo1CAxu26NViA==",
60
60
  },
61
61
  "highlight-a11y-dark.min.css" => {
62
- url: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/a11y-dark.min.css",
62
+ url: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.0/styles/a11y-dark.min.css",
63
63
  sri: "sha512-Vj6gPCk8EZlqnoveEyuGyYaWZ1+jyjMPg8g4shwyyNlRQl6d3L9At02ZHQr5K6s5duZl/+YKMnM3/8pDhoUphg==",
64
64
  extra_tag_attrs: {class: "rrf-dark-mode"},
65
65
  },
66
66
  "highlight-a11y-light.min.css" => {
67
- url: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/a11y-light.min.css",
67
+ url: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.0/styles/a11y-light.min.css",
68
68
  sri: "sha512-WDk6RzwygsN9KecRHAfm9HTN87LQjqdygDmkHSJxVkVI7ErCZ8ZWxP6T8RvBujY1n2/E4Ac+bn2ChXnp5rnnHA==",
69
69
  extra_tag_attrs: {class: "rrf-light-mode"},
70
70
  },
71
71
 
72
72
  # NeatJSON
73
73
  "neatjson.min.js" => {
74
- url: "https://cdn.jsdelivr.net/npm/neatjson@0.10.5/javascript/neatjson.min.js",
74
+ url: "https://cdn.jsdelivr.net/npm/neatjson@0.10.6/javascript/neatjson.min.js",
75
75
  exclude_from_docs: true,
76
76
  },
77
77
 
78
78
  # Trix
79
79
  "trix.min.css" => {
80
- url: "https://unpkg.com/trix@2.0.0/dist/trix.css",
80
+ url: "https://unpkg.com/trix@2.0.8/dist/trix.css",
81
81
  exclude_from_docs: true,
82
82
  },
83
83
  "trix.min.js" => {
84
- url: "https://unpkg.com/trix@2.0.0/dist/trix.umd.min.js",
84
+ url: "https://unpkg.com/trix@2.0.8/dist/trix.umd.min.js",
85
85
  exclude_from_docs: true,
86
86
  },
87
87
  }.map { |name, cfg|