rest_framework 2.0.0.beta2 → 2.0.0.beta4

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: 639dcc597e17e5148cde3f7511637913be7b431899f59a5cc7f504533969233e
4
- data.tar.gz: 0c732ab6c80f9c06b29e4a30e29087ff4fa65206df15b7826ffd9f0d2781b774
3
+ metadata.gz: 4b09674d62848416f77b39e23bc7b586e105786c7605320ad61761fe76ebc033
4
+ data.tar.gz: d8242a8db125185c3b1d778f4b4f2697a174d0bf17f796329cc4a0dfa11be99e
5
5
  SHA512:
6
- metadata.gz: 656e302db2252a27a30508e1f5c1d2caf2d603288d4ca3e2ec0995355a4a6501b18bc8320463a17113568a562ead98c3896c949a00aa03cc83a6c71f2f62d738
7
- data.tar.gz: f760d0140360ca1856dca0ab87b8de1903e09fd7cc442c1b40c9150b7abaf312f43ae67a52a5d0881af9e1f98489a074a82ebaef87b527c99e6e8f7175c67740
6
+ metadata.gz: 8399d7751f5116a6196853e64c704f153c33e0a79923fa6fa0c12a3867f0669f3f7651f6ea4528b5bd976a76ea7e2e2604a87fcb1c39116d0148c79e34d36d30
7
+ data.tar.gz: f32a2db5595e8aee9dc069aa4fcaf6f96fffc35cb69b4ebc2d76589951a5dc1fdcdeae46631ad5af1506dbb365d07749461e12702ca8b36d7f1afa12e3922e83
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0.beta2
1
+ 2.0.0.beta4
@@ -651,6 +651,15 @@ module RESTFramework::Controller
651
651
  self.get_fields.reject { |f| cfg[f]&.[](:write_only) }
652
652
  end
653
653
 
654
+ # The fields a client may write (create/update): `get_fields` minus read_only fields. Excluding
655
+ # them here — before strong parameters expand associations into `_id`/`_ids`/`_attributes` keys —
656
+ # keeps a read_only association from ever producing a permitted (and otherwise unstrippable, since
657
+ # those keys don't match a field name) assignment key.
658
+ def writable_fields
659
+ cfg = self.class.field_configuration
660
+ self.get_fields.reject { |f| cfg[f]&.[](:read_only) }
661
+ end
662
+
654
663
  # `readable_fields` restricted to real columns, for query surfaces that build SQL directly
655
664
  # (find_by, search) and would raise on a virtual/method field.
656
665
  def readable_columns
@@ -673,12 +682,14 @@ module RESTFramework::Controller
673
682
  @_get_allowed_parameters = self.class.allowed_parameters
674
683
  return @_get_allowed_parameters if @_get_allowed_parameters
675
684
 
676
- # Assemble strong parameters. Read-only fields are permitted here and stripped later per-action
677
- # by `_rrf_strip_read_only_fields` (which keeps the primary key on bulk update to find records).
685
+ # Assemble strong parameters from writable fields only, so read-only fields never produce a
686
+ # permitted key including the `_id`/`_ids`/`_attributes` variations an association expands
687
+ # into, which a later key-name-based filter couldn't catch. Bulk update re-permits the primary
688
+ # key (see `get_body_params`) since it needs it to locate each record.
678
689
  variations = []
679
690
  hash_variations = {}
680
691
  reflections = self.class.model.reflections
681
- @_get_allowed_parameters = self.get_fields.map { |f|
692
+ @_get_allowed_parameters = self.writable_fields.map { |f|
682
693
  f = f.to_s
683
694
  config = self.class.field_configuration[f]
684
695
 
@@ -823,37 +834,12 @@ module RESTFramework::Controller
823
834
  body_params[k].unshift(*v)
824
835
  end
825
836
 
826
- # Filter read-only fields. For bulk actions the permitted structure is `{ _json: [...] }`, so we
827
- # strip read-only keys from each element rather than the top-level hash (whose only key is
828
- # `_json`). Bulk update keeps the primary key, which it needs to locate each record.
829
- if bulk_action
830
- keep = bulk_action == :update ? [ pk.to_s ] : []
831
- body_params[:_json]&.each do |element|
832
- next unless element.is_a?(ActionController::Parameters)
833
-
834
- self._rrf_strip_read_only_fields(element, keep: keep)
835
- end
836
- else
837
- self._rrf_strip_read_only_fields(body_params)
838
- end
839
-
840
837
  body_params
841
838
  end
842
839
  alias_method :get_create_params, :get_body_params
843
840
  alias_method :get_update_params, :get_body_params
844
841
  alias_method :get_destroy_params, :get_body_params
845
842
 
846
- # Remove read-only fields from a permitted params hash in place. `keep` lists field names to
847
- # preserve even when read-only (e.g. the primary key on bulk update, used to locate records).
848
- def _rrf_strip_read_only_fields(params, keep: [])
849
- params.delete_if do |f, _|
850
- next false if f.in?(keep)
851
-
852
- cfg = self.class.field_configuration[f]
853
- cfg && cfg[:read_only]
854
- end
855
- end
856
-
857
843
  # Get the set of records this controller has access to.
858
844
  def get_recordset
859
845
  return self.class.recordset if self.class.recordset
@@ -184,6 +184,39 @@ class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers:
184
184
  (ref.klass.method_defined?(field) && !ref.klass.reflect_on_association(field.to_sym))
185
185
  end
186
186
 
187
+ # Recursively translate an association's fields into a `serializable_hash` config
188
+ # (`only`/`methods`/`include`). Columns go to `only` and plain methods to `methods`; a field that
189
+ # is itself an association is recursed into (as a nested `include`) only when it has its own entry
190
+ # in `field_config[:field_config]`. Otherwise it falls through to a method and serializes as
191
+ # before (its full `as_json`), so deeper nesting is opt-in and never narrows the default output.
192
+ def _build_association_config(model, fields, field_config)
193
+ nested = field_config[:field_config] || {}
194
+ only = []
195
+ methods = []
196
+ includes = {}
197
+
198
+ fields.each do |sf|
199
+ sf = sf.to_s
200
+ sub_field_config = nested[sf.to_sym] || nested[sf]
201
+
202
+ if sf.in?(model.column_names)
203
+ only << sf
204
+ elsif sub_field_config && (ref = model.reflect_on_association(sf.to_sym)) && !ref.polymorphic?
205
+ sub_fields = sub_field_config[:fields]&.map(&:to_s) ||
206
+ RESTFramework::Utils.association_fields_for(ref)
207
+ includes[sf] = self._build_association_config(ref.klass, sub_fields, sub_field_config)
208
+ elsif model.method_defined?(sf)
209
+ methods << sf
210
+ else
211
+ only << sf
212
+ end
213
+ end
214
+
215
+ config = { only: only, methods: methods }
216
+ config[:include] = includes if includes.any?
217
+ config
218
+ end
219
+
187
220
  # Get a serializer configuration from the controller. `@controller` and `@model` must be set.
188
221
  def _get_controller_serializer_config
189
222
  columns = []
@@ -208,16 +241,13 @@ class RESTFramework::Serializers::NativeSerializer < RESTFramework::Serializers:
208
241
  if f.in?(column_names)
209
242
  columns << f
210
243
  elsif ref = reflections[f]
211
- sub_columns = []
212
- sub_methods = []
213
- self._effective_association_fields(f, ref, field_config).each do |sf|
214
- if !ref.polymorphic? && sf.in?(ref.klass.column_names)
215
- sub_columns << sf
216
- else
217
- sub_methods << sf
218
- end
244
+ effective_fields = self._effective_association_fields(f, ref, field_config)
245
+ sub_config = if ref.polymorphic?
246
+ # No single target class to introspect, so serialize every field as a method.
247
+ { only: [], methods: effective_fields }
248
+ else
249
+ self._build_association_config(ref.klass, effective_fields, field_config)
219
250
  end
220
- sub_config = { only: sub_columns, methods: sub_methods }
221
251
 
222
252
  # Apply certain rules regarding collection associations.
223
253
  if ref.collection?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rest_framework
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta2
4
+ version: 2.0.0.beta4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregory N. Schmit
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-31 00:00:00.000000000 Z
11
+ date: 2026-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails