restme 1.2.1 → 1.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: 9a2a5ee870751a00ca5907e773e8d1aaf34fefd68c66bdcbbd42a0809d38ca19
4
- data.tar.gz: 8ef1ff2ab867c127615d53fa903808cc5579f961204cc9ff9306f217f1c7e4ae
3
+ metadata.gz: a2993fdfed0cd616219142038db44085a76bd4f24e10f4ea758947527613a8d5
4
+ data.tar.gz: 8fa26f48ba3c9fab9bb72d95217e809245b8e0c22aa8aabee009a5aa9d340c60
5
5
  SHA512:
6
- metadata.gz: 062df3cf4301fad020426e57ee07eb373ab2751c03f6aed5928e32db4988f8283c4e20254c993515d617dbb4b1e581eae2492aad4aecb0fd2610b0cc38c3afbe
7
- data.tar.gz: ade0cf78bcce6e49a3ecdeb2eb54513c7761c28bce0df20cdd1630bf61393962b68eb9394bdb6eb5e20c9ff04d619ad94932de1a3218ef1578321ca0ecce9fa0
6
+ metadata.gz: f5b0089277d026cd4580caabe24402c690db46ba2631cdb12c55f4d51a8166a2afc728f82200a6c8c6fabb49b9b3077d163d6aac9697a2aa1d791dbddacb9115
7
+ data.tar.gz: 0d3525d0dbb9a0795396a96e681890698b3ba4bb6e646732880218b5aa378e00714f3125d54e6d656cc9c3f6242fa8aaa14f3c61451b936fac308d7ac0f25ebe
data/Dockerfile CHANGED
@@ -15,6 +15,8 @@ RUN mkdir -p $APP_HOME
15
15
  WORKDIR $APP_HOME
16
16
 
17
17
  ADD Gemfile* $APP_HOME/
18
+ ADD restme.gemspec $APP_HOME/
19
+ ADD lib/restme/version.rb $APP_HOME/lib/restme/version.rb
18
20
 
19
21
  RUN chown -R $(whoami):$(whoami) $APP_HOME
20
22
 
data/README.md CHANGED
@@ -16,7 +16,7 @@ This gem manages your controller's responsibilities for:
16
16
 
17
17
  GEMFILE:
18
18
  ```bash
19
- gem 'restme', '~> 1.2'
19
+ gem 'restme', '~> 1.3.0'
20
20
  ```
21
21
 
22
22
  INSTALL:
@@ -57,6 +57,31 @@ Restme.configure do |config|
57
57
  end
58
58
  ```
59
59
 
60
+ `current_user_variable`
61
+
62
+ Defines the name of the method used to access the currently authenticated user within the controller context.
63
+ This should match the method that returns the logged-in user (for example, :current_user when using authentication libraries like Devise).
64
+ Represent the the field where the role of user is (can be one or many rules)
65
+
66
+ `user_role_field`
67
+
68
+ Defines the attribute on the user model that represents the user's role.
69
+ This field is used to determine authorization rules and may support single or multiple roles, depending on your application's implementation.
70
+
71
+ `pagination_default_per_page`
72
+
73
+ Specifies the default number of records returned per page when pagination parameters are not explicitly provided in the request.
74
+
75
+ `pagination_default_page`
76
+
77
+ Specifies the default page number used when the request does not include a page parameter.
78
+
79
+ `pagination_max_per_page`
80
+
81
+ Defines the maximum number of records allowed per page.
82
+ This acts as a safety limit to prevent clients from requesting excessively large result sets, helping protect application performance and resource usage.
83
+
84
+
60
85
  <br>
61
86
 
62
87
 
@@ -209,6 +234,11 @@ This rule defines which nested_fields are selectable (nested fields are model re
209
234
  ```ruby
210
235
  module ProductsController::Field
211
236
  class Rules
237
+ # Defines the default fields that will be automatically selected
238
+ # in queries when no explicit field selection is provided.
239
+ # These fields are always included in the response.
240
+ MODEL_FIELDS_SELECT = %i[id].freeze
241
+
212
242
  NESTED_SELECTABLE_FIELDS = {
213
243
  unit: {},
214
244
  establishment: {},
@@ -288,8 +318,6 @@ There are two query parameters available to control pagination:
288
318
  - `per_page`: Defines the number of items per page.
289
319
  - `page`: Sets the current page number.
290
320
 
291
- ℹ️ **Note:** The maximum number of items per page is currently limited to 100.
292
-
293
321
  Example usage:
294
322
 
295
323
  ```bash
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../shared/restme_current_user_role"
3
+ require_relative "../shared/restme_current_user_roles"
4
4
  require_relative "../shared/current_model"
5
5
 
6
6
  module Restme
@@ -8,7 +8,7 @@ module Restme
8
8
  # Defines the rules used to authotize user
9
9
  module Rules
10
10
  include ::Restme::Shared::CurrentModel
11
- include ::Restme::Shared::RestmeCurrentUserRole
11
+ include ::Restme::Shared::RestmeCurrentUserRoles
12
12
 
13
13
  def user_authorized?
14
14
  return true if restme_current_user.blank? || authorize?
@@ -19,8 +19,7 @@ module Restme
19
19
  end
20
20
 
21
21
  def authorize?
22
- allowed_roles_actions[action_name.to_sym]
23
- &.include?(restme_current_user_role&.to_sym)
22
+ (allowed_roles_actions & restme_current_user_roles)&.any?
24
23
  end
25
24
 
26
25
  def authorize_errors
@@ -35,9 +34,9 @@ module Restme
35
34
  end
36
35
 
37
36
  def allowed_roles_actions
38
- return {} unless authorize_rules_class&.const_defined?(:ALLOWED_ROLES_ACTIONS)
37
+ return [] unless authorize_rules_class&.const_defined?(:ALLOWED_ROLES_ACTIONS)
39
38
 
40
- authorize_rules_class::ALLOWED_ROLES_ACTIONS
39
+ authorize_rules_class::ALLOWED_ROLES_ACTIONS[action_name.to_sym] || []
41
40
  end
42
41
 
43
42
  def authorize_rules_class
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../shared/restme_current_user_role"
3
+ require_relative "../shared/restme_current_user_roles"
4
4
  require_relative "../shared/current_model"
5
5
  require_relative "../shared/controller_params"
6
6
 
@@ -10,7 +10,7 @@ module Restme
10
10
  module Rules
11
11
  include ::Restme::Shared::ControllerParams
12
12
  include ::Restme::Shared::CurrentModel
13
- include ::Restme::Shared::RestmeCurrentUserRole
13
+ include ::Restme::Shared::RestmeCurrentUserRoles
14
14
 
15
15
  attr_reader :create_temp_record
16
16
 
@@ -34,7 +34,7 @@ module Restme
34
34
  end
35
35
 
36
36
  def restme_create_status
37
- return :unprocessable_entity if create_record_errors
37
+ return :unprocessable_content if create_record_errors
38
38
 
39
39
  :created
40
40
  end
@@ -64,9 +64,13 @@ module Restme
64
64
  def createable_scope?
65
65
  return true unless restme_current_user
66
66
 
67
- method_scope = "#{creatable_current_action}_#{restme_current_user_role}_scope?"
67
+ restme_create_methods_scopes.any? { |method_scope| create_rules_class.try(method_scope) }
68
+ end
68
69
 
69
- create_rules_class.try(method_scope) || false
70
+ def restme_create_methods_scopes
71
+ @restme_create_methods_scopes ||= restme_current_user_roles.map do |restme_role|
72
+ "#{creatable_current_action}_#{restme_role}_scope?"
73
+ end
70
74
  end
71
75
 
72
76
  def createable_object_errors_messages
@@ -78,7 +82,7 @@ module Restme
78
82
  end
79
83
 
80
84
  def creatable_current_action
81
- return true unless restme_current_user
85
+ return unless create_rules_class
82
86
 
83
87
  current_action.presence_in create_rules_class.class::CREATABLE_ACTIONS_RULES
84
88
  rescue StandardError
@@ -19,6 +19,10 @@ module Restme
19
19
  scoped = select_nested_scope(scoped) if valid_nested_fields_select
20
20
 
21
21
  insert_attachments(scoped)
22
+ rescue ActiveModel::MissingAttributeError => e
23
+ restme_scope_errors({ body: model_fields_select, message: e.message })
24
+
25
+ restme_scope_status(:bad_request)
22
26
  end
23
27
 
24
28
  def select_nested_scope(scoped)
@@ -26,19 +30,29 @@ module Restme
26
30
  end
27
31
 
28
32
  def select_any_field?
29
- fields_select || nested_fields_select || attachment_fields_select
33
+ defined_fields_select || fields_select || nested_fields_select || attachment_fields_select
30
34
  end
31
35
 
32
36
  def model_fields_select
33
- @model_fields_select ||= begin
34
- fields = fields_select&.split(",")
35
- fields = fields&.map { |field| "#{klass.table_name}.#{field}" }&.join(",")
36
- fields || model_attributes
37
+ @model_fields_select ||= select_selected_fields.presence || model_attributes
38
+ end
39
+
40
+ def select_selected_fields
41
+ @select_selected_fields ||= begin
42
+ fields = defined_fields_select | fields_select.split(",")
43
+
44
+ fields.map { |field| "#{klass.table_name}.#{field}" }.join(",")
37
45
  end
38
46
  end
39
47
 
40
48
  def model_attributes
41
- @model_attributes ||= klass.new.attributes.keys
49
+ @model_attributes ||= klass.attribute_names
50
+ end
51
+
52
+ def defined_fields_select
53
+ return [] unless field_class_rules&.const_defined?(:MODEL_FIELDS_SELECT)
54
+
55
+ field_class_rules::MODEL_FIELDS_SELECT || []
42
56
  end
43
57
 
44
58
  def valid_nested_fields_select
@@ -75,7 +89,7 @@ module Restme
75
89
  end
76
90
 
77
91
  def fields_select
78
- @fields_select ||= controller_query_params[:fields_select]
92
+ @fields_select ||= controller_query_params[:fields_select] || ""
79
93
  end
80
94
 
81
95
  def nested_fields_select
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../shared/restme_current_user_role"
3
+ require_relative "../shared/restme_current_user_roles"
4
4
  require_relative "../shared/current_model"
5
5
  require_relative "../shared/controller_params"
6
6
  require_relative "filter/rules"
@@ -20,9 +20,9 @@ module Restme
20
20
  include ::Restme::Scope::Filter::Rules
21
21
  include ::Restme::Shared::ControllerParams
22
22
  include ::Restme::Shared::CurrentModel
23
- include ::Restme::Shared::RestmeCurrentUserRole
23
+ include ::Restme::Shared::RestmeCurrentUserRoles
24
24
 
25
- attr_reader :sortable_scope_response, :paginable_scope_response
25
+ attr_reader :filterable_scope_response
26
26
  attr_writer :restme_scope_errors, :restme_scope_status
27
27
 
28
28
  SCOPE_ERROR_METHODS = %i[
@@ -33,12 +33,16 @@ module Restme
33
33
  ].freeze
34
34
 
35
35
  def pagination_response
36
- @pagination_response ||= restme_pagination_response
36
+ @pagination_response ||= begin
37
+ prepare_model_scope
38
+
39
+ restme_scope_errors.presence || restme_pagination_response
40
+ end
37
41
  end
38
42
 
39
43
  def model_scope_object
40
44
  @model_scope_object ||= begin
41
- model_scope unless any_scope_errors.present?
45
+ prepare_model_scope
42
46
 
43
47
  restme_scope_errors.presence || model_scope.first
44
48
  end
@@ -47,14 +51,16 @@ module Restme
47
51
  private
48
52
 
49
53
  def restme_pagination_response
50
- any_scope_errors
51
-
52
- restme_scope_errors.presence || {
54
+ {
53
55
  objects: model_scope,
54
56
  pagination: pagination
55
57
  }
56
58
  end
57
59
 
60
+ def prepare_model_scope
61
+ model_scope if any_scope_errors.blank?
62
+ end
63
+
58
64
  def any_scope_errors
59
65
  SCOPE_ERROR_METHODS.each { |m| send(m) }
60
66
 
@@ -84,20 +90,31 @@ module Restme
84
90
  end
85
91
 
86
92
  def custom_scope
87
- return filterable_scope_response if filterable_scope_response.blank?
93
+ @filterable_scope_response = filterable_scope(user_scope)
88
94
 
89
- @sortable_scope_response = sortable_scope(filterable_scope_response)
90
- @paginable_scope_response = paginable_scope(sortable_scope_response)
95
+ scope = sortable_scope(filterable_scope_response)
96
+ scope = paginable_scope(scope)
91
97
 
92
- fieldable_scope(paginable_scope_response)
98
+ fieldable_scope(scope)
93
99
  end
94
100
 
95
- def filterable_scope_response
96
- @filterable_scope_response ||= filterable_scope(user_scope)
101
+ def user_scope
102
+ @user_scope ||= none_user_scope || process_user_scope || none_scope
97
103
  end
98
104
 
99
- def user_scope
100
- @user_scope ||= none_user_scope || scope_rules_class.try(method_scope) || none_scope
105
+ def process_user_scope
106
+ scopes = user_scope_methods.map { |m| scope_rules_class.try(m) }
107
+
108
+ processed_scope = scopes.reduce { |combined, s| combined.or(s) }
109
+
110
+ user_scope_methods.many? ? processed_scope&.distinct : processed_scope
111
+ end
112
+
113
+ def user_scope_methods
114
+ @user_scope_methods ||=
115
+ restme_methods_scopes.select do |method_scope|
116
+ scope_rules_class.respond_to?(method_scope)
117
+ end
101
118
  end
102
119
 
103
120
  def none_user_scope
@@ -108,8 +125,10 @@ module Restme
108
125
  klass.none
109
126
  end
110
127
 
111
- def method_scope
112
- "#{restme_current_user_role}_scope"
128
+ def restme_methods_scopes
129
+ @restme_methods_scopes ||= restme_current_user_roles.map do |restme_role|
130
+ "#{restme_role}_scope"
131
+ end
113
132
  end
114
133
 
115
134
  def scope_rules_class
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Restme
4
+ module Shared
5
+ # Returns the roles associated with the user, always normalized as an Array of symbols.
6
+ module RestmeCurrentUserRoles
7
+ def restme_current_user_roles
8
+ Array.wrap(user_roles).map do |role|
9
+ role.respond_to?(:to_sym) ? role.to_sym : role.to_s.to_sym
10
+ end
11
+ end
12
+
13
+ def user_roles
14
+ @user_roles ||= restme_current_user&.try(::Restme::Configuration.user_role_field)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../shared/restme_current_user_role"
3
+ require_relative "../shared/restme_current_user_roles"
4
4
  require_relative "../shared/current_model"
5
5
  require_relative "../shared/controller_params"
6
6
 
@@ -10,7 +10,7 @@ module Restme
10
10
  module Rules
11
11
  include ::Restme::Shared::ControllerParams
12
12
  include ::Restme::Shared::CurrentModel
13
- include ::Restme::Shared::RestmeCurrentUserRole
13
+ include ::Restme::Shared::RestmeCurrentUserRoles
14
14
 
15
15
  attr_reader :update_temp_record
16
16
 
@@ -36,7 +36,7 @@ module Restme
36
36
  end
37
37
 
38
38
  def restme_update_status
39
- return :unprocessable_entity if update_record_errors
39
+ return :unprocessable_content if update_record_errors
40
40
 
41
41
  :ok
42
42
  end
@@ -74,9 +74,13 @@ module Restme
74
74
  def updateable_scope?
75
75
  return true unless restme_current_user
76
76
 
77
- method_scope = "#{updateable_current_action}_#{restme_current_user_role}_scope?"
77
+ restme_update_methods_scopes.any? { |method_scope| update_rules_class.try(method_scope) }
78
+ end
78
79
 
79
- update_rules_class.try(method_scope) || false
80
+ def restme_update_methods_scopes
81
+ @restme_update_methods_scopes ||= restme_current_user_roles.map do |restme_role|
82
+ "#{updateable_current_action}_#{restme_role}_scope?"
83
+ end
80
84
  end
81
85
 
82
86
  def updateable_record_errors_messages
@@ -88,7 +92,7 @@ module Restme
88
92
  end
89
93
 
90
94
  def updateable_current_action
91
- return true unless restme_current_user
95
+ return unless update_rules_class
92
96
 
93
97
  current_action.presence_in update_rules_class.class::UPDATABLE_ACTIONS_RULES
94
98
  rescue StandardError
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restme
4
- VERSION = "1.2.1"
4
+ VERSION = "1.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restme
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - everson-ever
@@ -46,7 +46,7 @@ files:
46
46
  - lib/restme/scope/sort/rules.rb
47
47
  - lib/restme/shared/controller_params.rb
48
48
  - lib/restme/shared/current_model.rb
49
- - lib/restme/shared/restme_current_user_role.rb
49
+ - lib/restme/shared/restme_current_user_roles.rb
50
50
  - lib/restme/update/rules.rb
51
51
  - lib/restme/version.rb
52
52
  - sig/restme.rbs
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Restme
4
- module Shared
5
- # Returns the roles associated with the user, if any exist.
6
- module RestmeCurrentUserRole
7
- def restme_current_user_role
8
- restme_current_user&.try(::Restme::Configuration.user_role_field)
9
- end
10
- end
11
- end
12
- end