restme_rails 0.2.0 → 0.3.0

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: 4076bb59cd881f2ed821c909c6b3d17d121ce0f84a31f428ad75583ea8f18d9b
4
- data.tar.gz: ed9fdb887e8c91055f444f2def7e1a9d08f266ca1b15a657cb483600f722c8c5
3
+ metadata.gz: 0ca56c1c05d82ce70341d77bc32b9ababe1330b77d00b8dff144d9fc42e3b0f8
4
+ data.tar.gz: 6d78d32795f8f6325b1737d91dd921949f7d890d0d69379faa49a374ffbc2c56
5
5
  SHA512:
6
- metadata.gz: 2d516b60990e0d811d10739113f8a279cea2b9e09ddbc0ad53304d8c25968a47df014325fcdf6ca92f3484a08ab3e3caf4316f60992021e48e2e0500e241a524
7
- data.tar.gz: 355471605a9dec6d37439587f28f7ac28f3859ca895488637e1a49f49128f21431a0ea497de7ecbd4d4c46c14e507b6e1c09399bb24095197347b87078b5a238
6
+ metadata.gz: a3edf6726a7c7e86739b3747e439aacf22995b5405ce305b3fd9b5b716df88c5dd53367436a212ca3fec92986cec712a000947396804bb59861d6964d67f5b57
7
+ data.tar.gz: cfc97baaa14c175b425f72d07117d05d1e0e85aaa08c9b9d7fc0b9881527c1aa296165ec19b78c8ee3688f6bfafc88ac4b98a237e1e804477b7ae0b162939572
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../rules_find"
4
-
5
3
  module RestmeRails
6
4
  module Core
7
5
  module Authorize
@@ -15,28 +13,23 @@ module RestmeRails
15
13
  #
16
14
  # 1. If there is no current user → access is allowed.
17
15
  # 2. If the current user's roles intersect with allowed roles
18
- # for the action → access is allowed.
16
+ # declared via restme_authorize_action DSL → access is allowed.
19
17
  # 3. Otherwise → raises NotAuthorizedError.
20
18
  #
21
19
  # ------------------------------------------------------------
22
20
  # Expected Convention
23
21
  # ------------------------------------------------------------
24
22
  #
25
- # A rules class may exist following the naming convention:
26
- #
27
- # "#{ModelName}Rules::Authorize::Rules"
23
+ # Roles are declared on the controller class using the DSL:
28
24
  #
29
- # Example:
25
+ # class ProductsController < ApplicationController
26
+ # include RestmeRails
30
27
  #
31
- # class ProductRules::Authorize::Rules
32
- # ALLOWED_ROLES_ACTIONS = {
33
- # index: [:admin, :manager],
34
- # create: [:admin]
35
- # }
28
+ # restme_authorize_action :index, %i[admin manager]
29
+ # restme_authorize_action :create, %i[admin]
30
+ # restme_authorize_action %i[index show], %i[admin manager]
36
31
  # end
37
32
  #
38
- # Each controller action maps to an array of allowed roles.
39
- #
40
33
  class Rules
41
34
  attr_reader :context
42
35
 
@@ -65,27 +58,11 @@ module RestmeRails
65
58
  allowed_roles_for_action.intersect?(context.current_user_roles)
66
59
  end
67
60
 
68
- # Returns allowed roles for current action.
69
- #
70
- # If no rules class or constant exists, defaults to empty array.
61
+ # Returns allowed roles for current action from the controller DSL.
71
62
  #
72
63
  # @return [Array<Symbol>]
73
64
  def allowed_roles_for_action
74
- return [] unless rules_class&.const_defined?(:ALLOWED_ROLES_ACTIONS)
75
-
76
- rules_class::ALLOWED_ROLES_ACTIONS[context.action_name] || []
77
- end
78
-
79
- # Dynamically resolves authorization rules class.
80
- #
81
- # Uses RestmeRails::RulesFind to follow naming convention.
82
- #
83
- # @return [Class, nil]
84
- def rules_class
85
- @rules_class ||= RestmeRails::RulesFind.new(
86
- klass: context.model_class,
87
- rule_context: "Authorize"
88
- ).rule_class
65
+ context.controller_class.restme_authorize_actions[context.action_name] || []
89
66
  end
90
67
  end
91
68
  end
@@ -99,10 +99,8 @@ module RestmeRails
99
99
  nil
100
100
  elsif !scope_allowed?
101
101
  unscoped_errors
102
- elsif instance.valid?
103
- nil
104
102
  else
105
- active_record_errors
103
+ instance.valid? ? nil : active_record_errors
106
104
  end
107
105
  end
108
106
 
@@ -1,101 +1,74 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "attachable"
4
- require_relative "../../../rules_find"
3
+ require_relative "select_fields"
4
+ require_relative "select_nested_fields"
5
+ require_relative "select_attachments"
5
6
 
6
7
  module RestmeRails
7
8
  module Core
8
9
  module Scope
9
10
  module Field
10
- # Handles field selection logic for scoped queries.
11
+ # Orchestrates field selection for scoped queries.
11
12
  #
12
- # Responsibilities:
13
+ # Delegates each concern to a focused class:
13
14
  #
14
- # - Filters model attributes based on:
15
- # - fields_select
16
- # - nested_fields_select
17
- # - MODEL_FIELDS_SELECT (whitelist)
18
- # - UNALLOWED_MODEL_FIELDS_SELECT (blacklist)
19
- #
20
- # - Validates unallowed field selections
21
- # - Applies nested preloads
22
- # - Delegates attachment handling to Attachable
15
+ # SelectFields → model attribute selection (fields_select)
16
+ # SelectNestedFields → association preloading (nested_fields_select)
17
+ # SelectAttachments → attachment URL injection (attachment_fields_select)
23
18
  #
24
19
  # Query params supported:
25
20
  #
26
21
  # ?fields_select=id,name,email
27
22
  # ?nested_fields_select=profile,company
23
+ # ?nested_fields_select[profile]=id,name&nested_fields_select[company]=id
28
24
  # ?attachment_fields_select=avatar
29
25
  #
30
- # Expected convention:
31
- #
32
- # A Field Rules class may exist following:
33
- #
34
- # "#{ModelName}Restme::Field::Rules"
35
- #
36
- # It may define:
37
- #
38
- # MODEL_FIELDS_SELECT = [:id, :name]
39
- # UNALLOWED_MODEL_FIELDS_SELECT = [:internal_token]
40
- # NESTED_SELECTABLE_FIELDS = { profile: {}, company: {} }
41
- #
42
26
  class Rules
43
- attr_reader :context, :scope_error_instance, :attachable_instance
27
+ attr_reader :context, :scope_error_instance
44
28
 
45
29
  # @param context [RestmeRails::Context]
46
30
  # @param scope_error_instance [ScopeError]
47
31
  def initialize(context:, scope_error_instance:)
48
32
  @context = context
49
33
  @scope_error_instance = scope_error_instance
50
-
51
- @attachable_instance = RestmeRails::Core::Scope::Field::Attachable.new(
52
- context: context,
53
- attachment_fields_select: attachment_fields_select,
54
- valid_nested_fields_select: valid_nested_fields_select,
55
- scope_error_instance: scope_error_instance
56
- )
57
34
  end
58
35
 
59
- # Applies field selection rules to a given scope.
36
+ # Applies field selection pipeline to the given scope.
60
37
  #
61
38
  # Flow:
62
- # 1. Selects allowed model attributes
63
- # 2. Applies nested preloads
64
- # 3. Applies attachment serialization
39
+ # 1. Applies SELECT clause for model attributes
40
+ # 2. Preloads nested associations
41
+ # 3. Serializes to JSON with attachment URLs injected
65
42
  #
66
43
  # @param user_scope [ActiveRecord::Relation]
67
- # @return [ActiveRecord::Relation, Array<Hash>]
44
+ # @return [Array<Hash>]
68
45
  def process(user_scope)
69
- return user_scope unless select_any_field?
70
-
71
- scoped = user_scope
72
-
73
- scoped = user_scope.select(model_fields_select) if model_fields_select
74
- scoped = select_nested_scope(scoped) if valid_nested_fields_select
75
-
76
- attachable_instance.insert_attachments(scoped)
46
+ scoped = select_fields.process(user_scope)
47
+ scoped = select_nested_fields.process(scoped)
48
+ select_attachments.process(scoped)
77
49
  rescue ActiveModel::MissingAttributeError => e
78
50
  raise RestmeRails::MissingAttributeError, e.message
79
51
  end
80
52
 
81
- # Registers errors from field and field attachment
53
+ # Validates field selections and registers errors on scope_error_instance.
82
54
  #
83
55
  # @return [Boolean, nil]
84
56
  def errors
85
- unallowed_select_fields_errors
86
- unallowed_attachment_fields_errors
57
+ unallowed_select_fields_errors || select_attachments.errors
87
58
  end
88
59
 
89
60
  private
90
61
 
91
- # Registers error if client selects invalid fields.
62
+ # Combines unallowed model fields and nested associations into a
63
+ # single error entry to preserve the original error format.
92
64
  #
93
65
  # @return [Boolean, nil]
94
66
  def unallowed_select_fields_errors
95
- return if unallowed_fields_selected.blank?
67
+ unallowed = select_nested_fields.unallowed + select_fields.unallowed
68
+ return if unallowed.blank?
96
69
 
97
70
  scope_error_instance.add_error(
98
- body: unallowed_fields_selected,
71
+ body: unallowed,
99
72
  message: "Selected not allowed fields"
100
73
  )
101
74
 
@@ -104,167 +77,20 @@ module RestmeRails
104
77
  true
105
78
  end
106
79
 
107
- # Delegates attachment validation to Attachable.
108
- #
109
- # @return [void]
110
- def unallowed_attachment_fields_errors
111
- attachable_instance.unallowed_attachment_fields_errors
112
- end
113
-
114
- # Registers ActiveModel::MissingAttributeError
115
- #
116
- # @return [void]
117
- def add_scope_error(message)
118
- scope_error_instance.add_error(
119
- body: model_fields_select,
120
- message: message
121
- )
122
-
123
- scope_error_instance.add_status(:bad_request)
124
- end
125
-
126
- # Applies nested association preloads.
127
- #
128
- # @param scoped [ActiveRecord::Relation]
129
- # @return [ActiveRecord::Relation]
130
- def select_nested_scope(scoped)
131
- scoped.preload(valid_nested_fields_select)
132
- end
133
-
134
- # Determines whether any field selection param exists.
135
- #
136
- # @return [Boolean]
137
- def select_any_field?
138
- defined_fields_select ||
139
- fields_select ||
140
- nested_fields_select ||
141
- attachment_fields_select
142
- end
143
-
144
- # Final model fields to select.
145
- #
146
- # If client specifies fields_select, merge with defined whitelist.
147
- # Otherwise fallback to all allowed attributes.
148
- #
149
- # @return [Array<String>]
150
- def model_fields_select
151
- @model_fields_select ||= select_selected_fields.presence || model_attributes
152
- end
153
-
154
- # Merges whitelist + client selection.
155
- #
156
- # @return [Array<String>]
157
- def select_selected_fields
158
- @select_selected_fields ||= defined_fields_select |
159
- fields_select.split(",").map(&:to_s)
160
- end
161
-
162
- # Returns allowed model attributes excluding blacklisted ones.
163
- #
164
- # @return [Array<String>]
165
- def model_attributes
166
- @model_attributes ||= context.model_class.attribute_names -
167
- unallowed_model_fields_select
168
- end
169
-
170
- # Fields explicitly defined via MODEL_FIELDS_SELECT.
171
- #
172
- # @return [Array<String>]
173
- def defined_fields_select
174
- return [] unless field_class_rules&.const_defined?(:MODEL_FIELDS_SELECT)
175
-
176
- (field_class_rules::MODEL_FIELDS_SELECT || []).map(&:to_s)
177
- end
178
-
179
- # Fields blacklisted via UNALLOWED_MODEL_FIELDS_SELECT.
180
- #
181
- # @return [Array<String>]
182
- def unallowed_model_fields_select
183
- return [] unless field_class_rules&.const_defined?(:UNALLOWED_MODEL_FIELDS_SELECT)
184
-
185
- (field_class_rules::UNALLOWED_MODEL_FIELDS_SELECT || []).map(&:to_s)
186
- end
187
-
188
- # Valid nested associations based on whitelist.
189
- #
190
- # @return [Array<Symbol>, nil]
191
- def valid_nested_fields_select
192
- @valid_nested_fields_select ||= nested_fields_select
193
- &.split(",")
194
- &.select { |field| nested_selectable_fields_keys.key?(field.to_sym) }
195
- &.map(&:to_sym)
196
- end
197
-
198
- # Aggregates all invalid selections.
199
- #
200
- # @return [Array<Symbol>]
201
- def unallowed_fields_selected
202
- unallowed_nested_fields_select + unallowed_fields_select
203
- end
204
-
205
- # Nested associations not allowed.
206
- #
207
- # @return [Array<Symbol>]
208
- def unallowed_nested_fields_select
209
- return [] if nested_fields_select.blank?
210
-
211
- nested_fields_select.split(",").map(&:to_sym) -
212
- valid_nested_fields_select
213
- end
214
-
215
- # Model attributes not allowed.
216
- #
217
- # @return [Array<Symbol>]
218
- def unallowed_fields_select
219
- return [] if fields_select.blank?
220
-
221
- fields_select.split(",").map(&:to_sym) -
222
- model_attributes.map(&:to_sym)
223
- end
224
-
225
- # Query param: fields_select
226
- #
227
- # @return [String]
228
- def fields_select
229
- @fields_select ||= context.query_params[:fields_select] || ""
230
- end
231
-
232
- # Query param: nested_fields_select
233
- #
234
- # @return [String, nil]
235
- def nested_fields_select
236
- @nested_fields_select ||= context.query_params[:nested_fields_select]
80
+ def select_fields
81
+ @select_fields ||= SelectFields.new(context: context)
237
82
  end
238
83
 
239
- # Query param: attachment_fields_select
240
- #
241
- # @return [Array<Symbol>, nil]
242
- def attachment_fields_select
243
- @attachment_fields_select ||= context.query_params[:attachment_fields_select]
244
- &.split(",")
245
- &.map(&:to_sym)
246
- end
247
-
248
- # Returns allowed nested associations defined in rules.
249
- #
250
- # @return [Hash, nil]
251
- def nested_selectable_fields_keys
252
- @nested_selectable_fields_keys ||= if field_class_rules&.const_defined?(:NESTED_SELECTABLE_FIELDS)
253
- field_class_rules::NESTED_SELECTABLE_FIELDS
254
- end
84
+ def select_nested_fields
85
+ @select_nested_fields ||= SelectNestedFields.new(context: context)
255
86
  end
256
87
 
257
- # Dynamically resolves Field Rules class.
258
- #
259
- # Uses:
260
- # RestmeRails::RulesFind
261
- #
262
- # @return [Class, nil]
263
- def field_class_rules
264
- @field_class_rules ||= RestmeRails::RulesFind.new(
265
- klass: context.model_class,
266
- rule_context: "Field"
267
- ).rule_class
88
+ def select_attachments
89
+ @select_attachments ||= SelectAttachments.new(
90
+ context: context,
91
+ scope_error_instance: scope_error_instance,
92
+ valid_nested_fields_select: select_nested_fields.valid_nested_fields_select
93
+ )
268
94
  end
269
95
  end
270
96
  end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RestmeRails
4
+ module Core
5
+ module Scope
6
+ module Field
7
+ # Handles ActiveStorage attachment serialization for scoped queries.
8
+ #
9
+ # Responsibilities:
10
+ #
11
+ # - Validates requested attachment fields against the model
12
+ # - Preloads attachment blobs to avoid N+1
13
+ # - Injects *_url fields into the serialized JSON output
14
+ # - Applies nested field restrictions via as_json options
15
+ #
16
+ # Important:
17
+ # This class does NOT dynamically define methods on the model.
18
+ # Attachment URLs are injected directly into the serialized hash.
19
+ #
20
+ # Query param:
21
+ #
22
+ # ?attachment_fields_select=avatar,cover
23
+ #
24
+ # Response:
25
+ #
26
+ # { avatar_url: "https://...", cover_url: "https://..." }
27
+ #
28
+ class SelectAttachments
29
+ # @param context [RestmeRails::Context]
30
+ # @param scope_error_instance [RestmeRails::ScopeError]
31
+ # @param valid_nested_fields_select [Hash, nil]
32
+ def initialize(context:, scope_error_instance:, valid_nested_fields_select:)
33
+ @context = context
34
+ @scope_error_instance = scope_error_instance
35
+ @valid_nested_fields_select = valid_nested_fields_select
36
+ end
37
+
38
+ # Serializes the scope to JSON, injecting attachment URLs when requested.
39
+ #
40
+ # @param scope [ActiveRecord::Relation]
41
+ # @return [Array<Hash>]
42
+ def process(scope)
43
+ return scope.as_json(json_options) if attachment_fields_select.blank?
44
+
45
+ records = scope.includes(attachment_includes)
46
+
47
+ serialize_with_attachments(records)
48
+ end
49
+
50
+ # Registers a bad_request error for attachment fields that do not
51
+ # exist in the model's attachment_reflections.
52
+ #
53
+ # @return [void]
54
+ def errors
55
+ return if unallowed_attachment_fields.blank?
56
+
57
+ scope_error_instance.add_error(
58
+ body: unallowed_attachment_fields,
59
+ message: "Selected not allowed attachment fields"
60
+ )
61
+
62
+ scope_error_instance.add_status(:bad_request)
63
+ end
64
+
65
+ private
66
+
67
+ attr_reader :context, :scope_error_instance, :valid_nested_fields_select
68
+
69
+ # Base as_json options including nested field restrictions.
70
+ #
71
+ # @return [Hash]
72
+ def json_options
73
+ { include: nested_include_options }
74
+ end
75
+
76
+ # Builds the include: hash for as_json with optional field restriction.
77
+ #
78
+ # Examples:
79
+ # { profile: nil, company: [:id, :name] }
80
+ # → { profile: {}, company: { only: [:id, :name] } }
81
+ #
82
+ # @return [Hash]
83
+ def nested_include_options
84
+ return {} if valid_nested_fields_select.blank?
85
+
86
+ valid_nested_fields_select.transform_values do |fields|
87
+ fields ? { only: fields } : {}
88
+ end
89
+ end
90
+
91
+ # Serializes records and injects attachment URLs into each hash.
92
+ #
93
+ # Only fields present in model_attachment_fields are dispatched via
94
+ # public_send, regardless of pipeline call order.
95
+ #
96
+ # @param records [ActiveRecord::Relation]
97
+ # @return [Array<Hash>]
98
+ def serialize_with_attachments(records)
99
+ allowed_fields = attachment_fields_select & model_attachment_fields
100
+
101
+ records.map do |record|
102
+ base_hash = record.as_json(json_options)
103
+
104
+ allowed_fields.each do |field|
105
+ attachment = record.public_send(field)
106
+ base_hash["#{field}_url"] = attachment&.attached? ? attachment.url : nil
107
+ end
108
+
109
+ base_hash
110
+ end
111
+ end
112
+
113
+ # Builds the includes structure for ActiveStorage eager loading.
114
+ #
115
+ # Example: [:avatar] → [{ avatar_attachment: :blob }]
116
+ #
117
+ # @return [Array<Hash>]
118
+ def attachment_includes
119
+ attachment_fields_select.map do |field|
120
+ { "#{field}_attachment": :blob }
121
+ end
122
+ end
123
+
124
+ # All attachment names declared in the model via has_one_attached / has_many_attached.
125
+ #
126
+ # @return [Array<Symbol>]
127
+ def model_attachment_fields
128
+ @model_attachment_fields ||= context.model_class
129
+ .attachment_reflections
130
+ .map { |_name, reflection| reflection.name }
131
+ end
132
+
133
+ # Attachment fields requested but not present in the model.
134
+ #
135
+ # @return [Array<Symbol>]
136
+ def unallowed_attachment_fields
137
+ return [] if attachment_fields_select.blank?
138
+
139
+ attachment_fields_select - model_attachment_fields
140
+ end
141
+
142
+ # Query param: attachment_fields_select
143
+ #
144
+ # @return [Array<Symbol>, nil]
145
+ def attachment_fields_select
146
+ @attachment_fields_select ||= context.query_params[:attachment_fields_select]
147
+ &.split(",")
148
+ &.map(&:to_sym)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../../rules_find"
4
+
5
+ module RestmeRails
6
+ module Core
7
+ module Scope
8
+ module Field
9
+ # Handles model attribute selection for scoped queries.
10
+ #
11
+ # Responsibilities:
12
+ #
13
+ # - Applies SELECT clause based on requested and allowed fields
14
+ # - Merges whitelist (MODEL_FIELDS_SELECT) with client selection
15
+ # - Enforces blacklist (UNALLOWED_MODEL_FIELDS_SELECT)
16
+ # - Exposes unallowed selections for error aggregation in Rules
17
+ #
18
+ # Query param:
19
+ #
20
+ # ?fields_select=id,name,email
21
+ #
22
+ # Convention — optional class per model:
23
+ #
24
+ # "#{ModelName}Restme::Field::Rules"
25
+ #
26
+ # May define:
27
+ #
28
+ # MODEL_FIELDS_SELECT = [:id, :name]
29
+ # UNALLOWED_MODEL_FIELDS_SELECT = [:internal_token]
30
+ #
31
+ class SelectFields
32
+ # @param context [RestmeRails::Context]
33
+ def initialize(context:)
34
+ @context = context
35
+ end
36
+
37
+ # Applies SELECT clause to the scope.
38
+ #
39
+ # @param scope [ActiveRecord::Relation]
40
+ # @return [ActiveRecord::Relation]
41
+ def process(scope)
42
+ scope.select(model_fields_select)
43
+ end
44
+
45
+ # Fields that were requested but are not allowed.
46
+ # Used by Rules to build a single combined error.
47
+ #
48
+ # @return [Array<Symbol>]
49
+ def unallowed
50
+ return [] if fields_select.blank?
51
+
52
+ fields_select.split(",").map(&:to_sym) - model_attributes.map(&:to_sym)
53
+ end
54
+
55
+ # Final list of model fields to apply in SELECT.
56
+ #
57
+ # Merges whitelist with client selection, falling back to all
58
+ # allowed attributes when no explicit selection is made.
59
+ #
60
+ # @return [Array<String>]
61
+ def model_fields_select
62
+ @model_fields_select ||= select_selected_fields.presence || model_attributes
63
+ end
64
+
65
+ private
66
+
67
+ attr_reader :context
68
+
69
+ # Merges whitelist + client selection.
70
+ #
71
+ # @return [Array<String>]
72
+ def select_selected_fields
73
+ @select_selected_fields ||= defined_fields_select |
74
+ fields_select.split(",").map(&:to_s)
75
+ end
76
+
77
+ # All model column names excluding blacklisted fields.
78
+ #
79
+ # @return [Array<String>]
80
+ def model_attributes
81
+ @model_attributes ||= context.model_class.attribute_names -
82
+ unallowed_model_fields_select
83
+ end
84
+
85
+ # Fields whitelisted via MODEL_FIELDS_SELECT.
86
+ #
87
+ # @return [Array<String>]
88
+ def defined_fields_select
89
+ return [] unless field_class_rules&.const_defined?(:MODEL_FIELDS_SELECT)
90
+
91
+ (field_class_rules::MODEL_FIELDS_SELECT || []).map(&:to_s)
92
+ end
93
+
94
+ # Fields blacklisted via UNALLOWED_MODEL_FIELDS_SELECT.
95
+ #
96
+ # @return [Array<String>]
97
+ def unallowed_model_fields_select
98
+ return [] unless field_class_rules&.const_defined?(:UNALLOWED_MODEL_FIELDS_SELECT)
99
+
100
+ (field_class_rules::UNALLOWED_MODEL_FIELDS_SELECT || []).map(&:to_s)
101
+ end
102
+
103
+ # Query param: fields_select
104
+ #
105
+ # @return [String]
106
+ def fields_select
107
+ @fields_select ||= context.query_params[:fields_select] || ""
108
+ end
109
+
110
+ # Dynamically resolves the Field Rules class for the model.
111
+ #
112
+ # @return [Class, nil]
113
+ def field_class_rules
114
+ @field_class_rules ||= RestmeRails::RulesFind.new(
115
+ klass: context.model_class,
116
+ rule_context: "Field"
117
+ ).rule_class
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end