otpor 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d65aa7219b3a7e0218aceb14abc70ffa459989de2421eb8dddf56eb9e902462f
4
+ data.tar.gz: 491bb18692f97fe0525c01f87ba99a89f78576208b8fa16e2d35eb92d90808e7
5
+ SHA512:
6
+ metadata.gz: bedbe5c976532ce83c83ee2e99d864cf9be6f223ac2834d382791128a09022ec4fab145e728c980b819b25900390989a5f66cde56b3587cca37346f2e44cbe81
7
+ data.tar.gz: 55b496f6d2b6bfb4d56f3a92f7e0456d6741d941cf53764357ae53cbe1f6705d6088df1a0d50b850c43db7b19c13fded9a7c7d37dc862de5d89c725cb33b433e
@@ -0,0 +1,22 @@
1
+ module Otpor
2
+ module ActiveRecordValidationError
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ after_validation :log_validation_errors, if: -> { errors.any? }
7
+ end
8
+
9
+ private
10
+
11
+ def log_validation_errors
12
+ error_messages = []
13
+ errors.each do |error|
14
+ error_messages << { attribute: error.attribute, message: error.message, type: error.options.dig(:type) || error.type, entity: self.class.name, entity_id: self.id || 'new record'}
15
+ end
16
+ return unless error_messages.present?
17
+
18
+ RequestStore.store[:errors] ||= error_messages
19
+ RequestStore.store[:status_symbol] ||= :unprocessable_content
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,124 @@
1
+ module Otpor
2
+ module JsonResponse
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_action :capture_initial_instance_variables
7
+ around_action :rescue_exceptions
8
+ rescue_from ActionView::MissingTemplate, with: :handle_missing_partial
9
+ end
10
+
11
+ def handle_missing_partial; end
12
+
13
+ def default_render(*args)
14
+ super and return unless request.format.json?
15
+
16
+ @errors ||= RequestStore.store[:errors] || nil
17
+ @notes ||= nil
18
+
19
+ @status_symbol = RequestStore.store[:status_symbol] if RequestStore.store[:status_symbol]
20
+ @status_code = Rack::Utils::SYMBOL_TO_STATUS_CODE[@status_symbol] if @status_symbol
21
+ @status_code ||= response.status
22
+ @status = {
23
+ name: http_status_name(@status_code),
24
+ code: @status_code,
25
+ type: http_status_type(@status_code)
26
+ }
27
+
28
+ partial_exists = lookup_context.exists?("#{controller_path}/#{action_name}", [], true)
29
+
30
+ @data_partial ||= "#{controller_path}/#{action_name}" if @status[:type].eql?("Success") && partial_exists
31
+
32
+ @instance_variables = capture_new_instance_variables
33
+
34
+ @meta ||= infer_meta
35
+
36
+ render template: "shared/response", formats: :json, status: @status_code
37
+ end
38
+
39
+ private
40
+
41
+ def http_status_name(status_code)
42
+ Rack::Utils::HTTP_STATUS_CODES[status_code]
43
+ end
44
+
45
+ def rescue_exceptions
46
+ yield
47
+ rescue StandardError => e
48
+ @status_code = determine_status_code(e)
49
+ @status = {
50
+ name: http_status_name(@status_code),
51
+ code: @status_code,
52
+ type: http_status_type(@status_code)
53
+ }
54
+ @errors ||= RequestStore.store[:errors] || nil
55
+ @exception_log = [{ message: e.message, backtrace: e.backtrace[0, 5] }] if Rails.env.development?
56
+ render template: "shared/response", formats: :json, status: @status_code
57
+ end
58
+
59
+ def determine_status_code(exception)
60
+ case exception
61
+ when ActiveRecord::RecordNotFound
62
+ 404
63
+ when ActionController::RoutingError, ActionController::UnknownFormat
64
+ 404
65
+ when ActiveRecord::RecordInvalid, ActionController::ParameterMissing
66
+ 422
67
+ else
68
+ 500
69
+ end
70
+ end
71
+
72
+ def http_status_type(status_code)
73
+ case status_code
74
+ when 100..199
75
+ "Informational"
76
+ when 200..299
77
+ "Success"
78
+ when 300..399
79
+ "Redirection"
80
+ when 400..499
81
+ "Client Error"
82
+ when 500..599
83
+ "Server Error"
84
+ else
85
+ "Unknown"
86
+ end
87
+ end
88
+
89
+ def capture_initial_instance_variables
90
+ @initial_instance_variables = instance_variables
91
+ end
92
+
93
+ def capture_new_instance_variables
94
+ current_instance_variables = instance_variables
95
+ new_vars = current_instance_variables - @initial_instance_variables
96
+ new_vars -= %i[@initial_instance_variables @errors @notes @data_partial @status
97
+ @_response_body @new_instance_variables]
98
+ new_instance_vars = {}
99
+ new_vars.each do |var|
100
+ new_instance_vars[var] = instance_variable_get(var)
101
+ end
102
+ new_instance_vars
103
+ end
104
+
105
+ def infer_meta
106
+ @instance_variables.each do |key, value|
107
+ next unless value.respond_to?(:total_pages)
108
+
109
+ return {
110
+ pagination: {
111
+ total_pages: value.total_pages,
112
+ total_count: value.total_count,
113
+ current_page: value.current_page,
114
+ next_page: value.next_page,
115
+ prev_page: value.prev_page,
116
+ per_page: value.limit_value
117
+ }
118
+ }
119
+ end
120
+
121
+ nil
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,14 @@
1
+ json.status do
2
+ json.name @status[:name]
3
+ json.code @status[:code]
4
+ json.type @status[:type]
5
+ end
6
+
7
+ json.data do
8
+ json.partial! @data_partial if @data_partial.present?
9
+ end
10
+
11
+ json.errors @errors
12
+ json.notes @notes
13
+ json.meta @meta
14
+ json.exception_log @exception_log
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Otpor
4
+ VERSION = "0.1.0"
5
+ end
data/lib/otpor.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "request_store"
4
+ require_relative "otpor/version"
5
+ require_relative "otpor/json_response"
6
+ require_relative "otpor/active_record_validation_error"
7
+
8
+ module Otpor
9
+ class Engine < ::Rails::Engine
10
+ initializer "otpor.load_view_paths" do |app|
11
+ ActiveSupport.on_load(:action_controller) do
12
+ append_view_path Otpor::Engine.root.join("lib", "otpor", "templates")
13
+ end
14
+ end
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: otpor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Renan Garcia
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-09-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: kaminari
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.2.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: jbuilder
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 2.12.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.12.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: request_store
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: This gem includes a concern that defines a default Jbuilder template
98
+ for controller actions that include it, rendering consistent JSON responses.
99
+ email:
100
+ - renan.almeida.garcia@icloud.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - lib/otpor.rb
106
+ - lib/otpor/active_record_validation_error.rb
107
+ - lib/otpor/json_response.rb
108
+ - lib/otpor/templates/shared/response.json.jbuilder
109
+ - lib/otpor/version.rb
110
+ homepage: https://github.com/renan-garcia/otpor
111
+ licenses:
112
+ - MIT
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubygems_version: 3.5.17
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: 'Otpor: JSON Response Concern'
133
+ test_files: []