api-response_builder 0.1.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f94d9f6432b2633b6770e26ab87ebd19fdb919761b7dcea2ed9885cf80973012
4
+ data.tar.gz: 6322f9a05cf5e372a249d827b444c4d44a07c82f9b10099c48f56ed57754167c
5
+ SHA512:
6
+ metadata.gz: df5a399358707342ed4105b981bff36f13aee7575601719a06cfc2546169cdbde9cbaebdb3affd3c18d3f50c3ad8785129d13541d764d08248ddca1aad1196f3
7
+ data.tar.gz: 9afb01c0539ed5e7127c65cfabf046c32ff8c756b7a4624904e41b0753cf1a7e85ebf6c58bfbcffb88642f33d7412048acd00536b9ed2cfa78e0e1777ed57e69
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+
3
+ # git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in api-response_builder.gemspec
6
+ gemspec
7
+
8
+ gem 'i18n', '~> 0.9.1'
9
+
10
+ gem 'activerecord', '~> 5.0', '>= 5.0.0.1'
11
+
12
+ gem 'active_model_serializers', '~> 0.10.7'
13
+
14
+ # gem 'mysql2'
@@ -0,0 +1,97 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ api-response_builder (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ actionpack (5.1.4)
10
+ actionview (= 5.1.4)
11
+ activesupport (= 5.1.4)
12
+ rack (~> 2.0)
13
+ rack-test (>= 0.6.3)
14
+ rails-dom-testing (~> 2.0)
15
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
16
+ actionview (5.1.4)
17
+ activesupport (= 5.1.4)
18
+ builder (~> 3.1)
19
+ erubi (~> 1.4)
20
+ rails-dom-testing (~> 2.0)
21
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
22
+ active_model_serializers (0.10.7)
23
+ actionpack (>= 4.1, < 6)
24
+ activemodel (>= 4.1, < 6)
25
+ case_transform (>= 0.2)
26
+ jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
27
+ activemodel (5.1.4)
28
+ activesupport (= 5.1.4)
29
+ activerecord (5.1.4)
30
+ activemodel (= 5.1.4)
31
+ activesupport (= 5.1.4)
32
+ arel (~> 8.0)
33
+ activesupport (5.1.4)
34
+ concurrent-ruby (~> 1.0, >= 1.0.2)
35
+ i18n (~> 0.7)
36
+ minitest (~> 5.1)
37
+ tzinfo (~> 1.1)
38
+ arel (8.0.0)
39
+ builder (3.2.3)
40
+ case_transform (0.2)
41
+ activesupport
42
+ concurrent-ruby (1.0.5)
43
+ crass (1.0.3)
44
+ diff-lcs (1.3)
45
+ erubi (1.7.0)
46
+ i18n (0.9.1)
47
+ concurrent-ruby (~> 1.0)
48
+ jsonapi-renderer (0.2.0)
49
+ loofah (2.1.1)
50
+ crass (~> 1.0.2)
51
+ nokogiri (>= 1.5.9)
52
+ mini_portile2 (2.3.0)
53
+ minitest (5.10.3)
54
+ mysql2 (0.4.6)
55
+ nokogiri (1.8.1)
56
+ mini_portile2 (~> 2.3.0)
57
+ rack (2.0.3)
58
+ rack-test (0.8.2)
59
+ rack (>= 1.0, < 3)
60
+ rails-dom-testing (2.0.3)
61
+ activesupport (>= 4.2.0)
62
+ nokogiri (>= 1.6)
63
+ rails-html-sanitizer (1.0.3)
64
+ loofah (~> 2.0)
65
+ rake (10.5.0)
66
+ rspec (3.7.0)
67
+ rspec-core (~> 3.7.0)
68
+ rspec-expectations (~> 3.7.0)
69
+ rspec-mocks (~> 3.7.0)
70
+ rspec-core (3.7.0)
71
+ rspec-support (~> 3.7.0)
72
+ rspec-expectations (3.7.0)
73
+ diff-lcs (>= 1.2.0, < 2.0)
74
+ rspec-support (~> 3.7.0)
75
+ rspec-mocks (3.7.0)
76
+ diff-lcs (>= 1.2.0, < 2.0)
77
+ rspec-support (~> 3.7.0)
78
+ rspec-support (3.7.0)
79
+ thread_safe (0.3.6)
80
+ tzinfo (1.2.4)
81
+ thread_safe (~> 0.1)
82
+
83
+ PLATFORMS
84
+ ruby
85
+
86
+ DEPENDENCIES
87
+ active_model_serializers (~> 0.10.7)
88
+ activerecord (~> 5.0, >= 5.0.0.1)
89
+ api-response_builder!
90
+ bundler (~> 1.16)
91
+ i18n (~> 0.9.1)
92
+ mysql2
93
+ rake (~> 10.0)
94
+ rspec (~> 3.0)
95
+
96
+ BUNDLED WITH
97
+ 1.16.1
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Dass Mk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,350 @@
1
+ # Api::ResponseBuilder
2
+
3
+ Module to build response object for Rails 5 API Applications.
4
+ Depends on `active_model_serializers` gem.
5
+
6
+ Pass a valid / invalid ActiveRecord object or an instance of ActiveRecord::Relation and get response object in following structure.
7
+
8
+ Properties | Description
9
+ ------------ | -------------
10
+ status | 'failure' (or) 'success'
11
+ body | Serialized Object
12
+ messages | Error Description (if any)
13
+ status_code | HTTP Status Code
14
+
15
+
16
+ ## Example
17
+
18
+ ```ruby
19
+ #app/controllers/api/v1/application_controller.rb
20
+ module Api
21
+ module V1
22
+ class ApplicationController < ActionController::API
23
+
24
+ def serializer_responder(resource, config = {})
25
+ response = ::Api::ResponseBuilder::Main.new(resource, config, params).response
26
+ render json: response, status: response[:status_code]
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+
33
+ # app/serializers/v1/user_serializer.rb
34
+ module V1
35
+ # Serializer for User model
36
+ class UserSerializer < ::ActiveModel::Serializer
37
+ attributes :id,
38
+ :firstname,
39
+ :lastname,
40
+ :phone_number,
41
+ :email
42
+ end
43
+ end
44
+
45
+
46
+ # app/controllers/api/v1/users_controller.rb
47
+ module Api
48
+ module V1
49
+ # Defines endpoints for CRUD operations on user model
50
+ class UsersController < ::Api::V1::ApplicationController
51
+
52
+ def index
53
+ users = User.all
54
+ serializer_responder(users, serializer: ::V1::UserSerializer)
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+
61
+ ```
62
+
63
+ Response Object for API endpoint `/api/v1/users` will be
64
+
65
+ ```json
66
+ {
67
+ "status": "success",
68
+ "body" :
69
+ [
70
+ {
71
+ "id": 1,
72
+ "firstname": "Kalidas",
73
+ "lastname": "M",
74
+ "phone_number": "+919876543210",
75
+ "email": "kalidasm610@gmail.com"
76
+ },
77
+ {
78
+ "id": 2,
79
+ "firstname": "Dass",
80
+ "lastname": "Mk",
81
+ "phone_number": "+919876543211",
82
+ "email": ""
83
+ }
84
+ ],
85
+ "status_code": "ok"
86
+ }
87
+ ```
88
+
89
+ ## Installation
90
+
91
+ Add this line to your application's Gemfile:
92
+
93
+ ```ruby
94
+ gem 'api-response_builder'
95
+ ```
96
+
97
+ And then execute:
98
+
99
+ $ bundle
100
+
101
+ Or install it yourself as:
102
+
103
+ $ gem install api-response_builder
104
+
105
+ ## Usage
106
+
107
+ #### ApplicationController - Setup
108
+ By taking advantage of Ruby's inheritance and Rails's app structure, few instance methods in application controller can be used to handle all scenarios for rendering json response across entire application.
109
+
110
+ ```ruby
111
+ module Api
112
+ module V1
113
+ class ApplicationController < ActionController::API
114
+ # action callbacks
115
+
116
+ # Global Exception Handler for Api Exceptions and StandardError
117
+ rescue_from ::Api::Exception, StandardError do |e|
118
+ handle_api_exception(e)
119
+ end
120
+
121
+ # Global exception handlers for ActiveRecord Exceptions
122
+ rescue_from ::ActiveRecord::RecordNotFound, with: :render_record_not_found
123
+ rescue_from ::ActiveRecord::RecordInvalid, with: :render_record_invalid
124
+ rescue_from ::ActiveRecord::RecordNotDestroyed, with: :render_forbidden
125
+
126
+ # Public methods
127
+ def handle_api_exception(e)
128
+ response = ::Api::ResponseBuilder::Main.new(e, {}, params).response
129
+ render json: response, status: response[:status_code]
130
+ end
131
+
132
+ def render_record_not_found
133
+ handle_api_exception ::Api::Exception.new(
134
+ ::Api::Exception.record_not_found
135
+ )
136
+ end
137
+
138
+ def render_internal_server_error
139
+ handle_api_exception ::Api::Exception.new(
140
+ ::Api::Exception.internal_server_error
141
+ )
142
+ end
143
+
144
+ def render_record_invalid(e)
145
+ # If exception message is not included in arguments, locale message
146
+ # (if present) for record invalid exception will be returned in resp
147
+ exception_message = e.record.errors.full_messages
148
+ exception = ::Api::Exception.new(
149
+ ::Api::Exception.record_invalid, exception_message
150
+ )
151
+ handle_api_exception(exception)
152
+ end
153
+
154
+ def render_forbidden
155
+ handle_api_exception ::Api::Exception.new(
156
+ ::Api::Exception.forbidden_resource
157
+ )
158
+ end
159
+
160
+ def serializer_responder(resource, config = {})
161
+ response = ::Api::ResponseBuilder::Main.new(resource, config, params).response
162
+ # response object also contains corresponding http status code under the key :status_code
163
+ # Including `Http Status Code` as part of API response is generally considered as good practice
164
+ # If all your api responses should have `200 OK` as status code, omit status key in render method
165
+ # Rails, by default, set status code as `200 OK`
166
+ render json: response, status: response[:status_code]
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ ```
173
+
174
+
175
+ #### Passing Additional Information to Response
176
+
177
+ In some cases, meta information may be passed along with response. For example, passing total count of resources for pagination.
178
+
179
+ In order to pass meta info, include `:meta` key-value pair in config object passed to `::Api::ResponseBuilder::Main`.
180
+ It will be reflected in response object under `meta` key
181
+
182
+ Note:
183
+ * config[:serializer] should be `ActiveModel::Serializer` class (if serializer is not passed, it will serialize entire object)
184
+ * `config[:meta]` will accept only hash
185
+
186
+
187
+ ```ruby
188
+ # app/controllers/api/v1/users_controller.rb
189
+ def index
190
+ users = User.all
191
+
192
+ config = {}
193
+ config[:serializer] = UserSerializer
194
+ config[:meta] = { total_count: users.count }
195
+ serializer_responder(users, config)
196
+ end
197
+
198
+ # simple way
199
+ serializer_responder(users, { serializer: UserSerializer, meta: { total_count: users.count } })
200
+ ```
201
+
202
+ ##### Response
203
+
204
+ ```json
205
+ {
206
+ "status": "success",
207
+ "body" :
208
+ [
209
+ {
210
+ "id": 1,
211
+ "firstname": "Kalidas",
212
+ "lastname": "M",
213
+ "phone_number": "+919876543210",
214
+ "email": "kalidasm610@gmail.com"
215
+ },
216
+ {
217
+ "id": 2,
218
+ "firstname": "Dass",
219
+ "lastname": "Mk",
220
+ "phone_number": "+919876543211",
221
+ "email": ""
222
+ }
223
+ ],
224
+ "status_code": "ok",
225
+ "meta": {
226
+ "total_count": 2
227
+ }
228
+ }
229
+ ```
230
+
231
+
232
+ #### API Exceptions
233
+
234
+ Following exceptions are handled by this gem as there are the most commonly used.
235
+
236
+ EXCEPTION | HTTP Status Code
237
+ ------------ | -------------
238
+ INTERNAL_SERVER_ERROR | :internal_server_error (500)
239
+ RECORD_NOT_FOUND | :not_found (404)
240
+ RECORD_INVALID | ::unprocessable_entity (422)
241
+ RECORD_NOT_DESTROYED | :forbidden (403)
242
+ FORBIDDEN_RESOURCE | :forbidden (403)
243
+ UNAUTHORIZED_ACCESS | :unauthorized (401)
244
+
245
+ For futher information, take a look at the [Code](https://github.com/kalidasm/rails-api-response-builder/blob/master/lib/api/exception.rb)
246
+
247
+
248
+ ##### Error Messages for API Exceptions
249
+
250
+ All the error messages for these exceptions are rendered from I18n locales (`en.api_response.messages.#{key}`)
251
+
252
+ Key represents lower-cased version of exception names listed above
253
+
254
+ Example :
255
+
256
+ ```yml
257
+ en:
258
+ api_response:
259
+ messages:
260
+ internal_server_error: "Internal server error."
261
+ record_not_found: "The resource you are looking for does not exist."
262
+ record_invalid: "Validation Failed"
263
+ record_not_destroyed: "Failed to delete the resource"
264
+ forbidden_resource: "You don't have sufficient privileges to perform this operation"
265
+ unauthorized_access: "You are not authorized to perform this operation"
266
+ ```
267
+
268
+ ##### Raising API Exception inside controllers
269
+
270
+ ```ruby
271
+ def update_credit_score
272
+ # code to authorization info for current user
273
+ unless user_authorized?
274
+ raise ::Api::Exception.new(
275
+ ::Api::Exception.unauthorized_access
276
+ )
277
+ end
278
+ # Code to Update the credit score
279
+ end
280
+ ```
281
+
282
+ Response would be
283
+
284
+ ```json
285
+ {
286
+ "status": "failure",
287
+ "messages": {
288
+ "errors": ["You are not authorized to perform this operation"]
289
+ },
290
+ "status_code": "unprocessable_entity",
291
+ "meta": {}
292
+ }
293
+ ```
294
+
295
+ If you want to override the default error message in a specific scenario,
296
+
297
+ ```ruby
298
+ def update_credit_score
299
+ # code to authorization info for current user
300
+ unless user_authorized?
301
+ custom_error_message = 'You are not allowed to update credit score'
302
+
303
+ raise ::Api::Exception.new(
304
+ ::Api::Exception.unauthorized_access, custom_error_message
305
+ )
306
+ end
307
+ # Code to Update the credit score
308
+ end
309
+ ```
310
+ ```json
311
+ {
312
+ "status": "failure",
313
+ "messages": {
314
+ "errors": ["You are not allowed to update credit score"]
315
+ },
316
+ "status_code": "unprocessable_entity",
317
+ "meta": {}
318
+ }
319
+ ```
320
+
321
+ #### Response Status - Failure / Success
322
+
323
+ Response Object contains a key named `status`. It will have only any one of
324
+ * failure
325
+ * success
326
+
327
+ Status will be `failure` when
328
+ * Resource has any errors
329
+ * Resource is an instance of `::Api::Exception`
330
+ * StandardError is raised
331
+
332
+ Status will be `success` when
333
+ * Resource do not have any errors
334
+ * Resource is *not* an instance of `::Api::Exception`
335
+ * No Exception been raised
336
+
337
+
338
+ ## Development
339
+
340
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
341
+
342
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
343
+
344
+ ## Contributing
345
+
346
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kalidasm/api-response_builder.
347
+
348
+ ## License
349
+
350
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,37 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "api/response_builder/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "api-response_builder"
8
+ spec.version = Api::ResponseBuilder::VERSION
9
+ spec.authors = ["Dass Mk"]
10
+ spec.email = ["kalidasm610@gmail.com"]
11
+
12
+ spec.summary = %q{API Response Builder to keep controllers super-slim}
13
+ spec.description = %q{API Response Builder to keep controllers super-slim}
14
+ spec.homepage = "https://github.com/kalidasm/rails-api-response-builder"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against " \
23
+ # "public gem pushes."
24
+ # end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.16"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_development_dependency "rspec", "~> 3.0"
36
+ spec.add_development_dependency "i18n", "~> 0.7.0"
37
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "api/response_builder"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+ # Base API exception class to create custom exceptions dynamically
3
+ # with a code and message passed
4
+ require 'i18n'
5
+
6
+ module Api
7
+ class Exception < ::StandardError
8
+ attr_accessor :messages
9
+
10
+ def initialize(error_constant, messages = nil)
11
+ @error_constant = error_constant
12
+ @messages = {
13
+ @error_constant[:key].to_s => [messages || @error_constant[:message]]
14
+ }
15
+ end
16
+
17
+ def full_messages
18
+ @messages.values.flatten
19
+ end
20
+
21
+ def status_code
22
+ @error_constant[:status_code]
23
+ end
24
+
25
+ class << self
26
+ def error_constants
27
+ {
28
+ INTERNAL_SERVER_ERROR: {
29
+ message: t(:internal_server_error),
30
+ key: :internal_server_error,
31
+ status_code: :internal_server_error
32
+ },
33
+ RECORD_NOT_FOUND: {
34
+ message: t(:record_not_found),
35
+ key: :record_not_found,
36
+ status_code: :not_found
37
+ },
38
+ RECORD_INVALID: {
39
+ message: t(:record_invalid),
40
+ key: :record_invalid,
41
+ status_code: :unprocessable_entity
42
+ },
43
+ RECORD_NOT_DESTROYED: {
44
+ message: t(:record_not_destroyed),
45
+ key: :record_not_destroyed,
46
+ status_code: :forbidden
47
+ },
48
+ FORBIDDEN_RESOURCE: {
49
+ message: t(:forbidden_resource),
50
+ key: :forbidden_resource,
51
+ status_code: :forbidden
52
+ },
53
+ UNAUTHORIZED_ACCESS: {
54
+ message: t(:unauthorized_access),
55
+ key: :unauthorized_access,
56
+ status_code: :unauthorized
57
+ }
58
+ }
59
+ end
60
+
61
+ private
62
+
63
+ def t(key)
64
+ ::I18n.config.available_locales = :en
65
+ ::I18n.t("api_response.messages.#{key}")
66
+ end
67
+ end
68
+
69
+ error_constants.each do |key, value|
70
+ define_singleton_method(key.downcase) { value }
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,15 @@
1
+ require "api/response_builder/version"
2
+ require "api/response_builder/base"
3
+ require "api/response_builder/data"
4
+ require "api/response_builder/main"
5
+ require "api/response_builder/messages"
6
+ require "api/response_builder/status"
7
+ require "api/response_builder/status_code"
8
+ require "api/exception"
9
+ require "active_record"
10
+ require "active_model_serializers"
11
+
12
+ module Api
13
+ module ResponseBuilder
14
+ end
15
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ module Api
3
+ module ResponseBuilder
4
+ # Base Class for building api response
5
+ class Base
6
+ attr_accessor :resource,
7
+ :config
8
+
9
+ def initialize(resource, config = {})
10
+ @resource = resource
11
+ @config = config
12
+ end
13
+
14
+ protected
15
+
16
+ def active_model_object?
17
+ @resource.is_a?(::ActiveRecord::Base)
18
+ end
19
+
20
+ def hash_object?
21
+ @resource.is_a?(Hash)
22
+ end
23
+
24
+ def resource_has_errors?
25
+ active_model_object? && @resource.errors.any?
26
+ end
27
+
28
+ def collection?
29
+ @resource.is_a?(::ActiveRecord::Relation) || @resource.is_a?(Array)
30
+ end
31
+
32
+ def exception?
33
+ @resource.is_a?(Exception)
34
+ end
35
+
36
+ def other_exception?
37
+ !api_exception? && @resource.is_a?(StandardError)
38
+ end
39
+
40
+ def api_exception?
41
+ @resource.is_a?(::Api::Exception)
42
+ end
43
+
44
+ def invalid_resource?
45
+ return @resource.errors.any? if active_model_object?
46
+ false
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ module Api
3
+ module ResponseBuilder
4
+ # Class which helps in building api response body
5
+ class Data < ::Api::ResponseBuilder::Base
6
+ attr_accessor :data
7
+
8
+ def initialize(resource, config = {})
9
+ super(resource, config)
10
+ @data = nil
11
+ set_data
12
+ end
13
+
14
+ private
15
+
16
+ def set_data
17
+ return if exception?
18
+ set_object_data
19
+ set_collection_data
20
+ end
21
+
22
+ def set_collection_data
23
+ return unless collection?
24
+ @data = ::ActiveModel::Serializer::CollectionSerializer.
25
+ new(resource, config)
26
+ end
27
+
28
+ def set_object_data
29
+ return if invalid_resource?
30
+ serializer = config[:serializer]
31
+ @data = serializer ? serializer.new(resource, config) : resource
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ module Api
3
+ module ResponseBuilder
4
+ # Class which helps in building whole api response
5
+ class Main
6
+ attr_accessor :resource,
7
+ :config,
8
+ :response,
9
+ :params
10
+
11
+ def initialize(resource, config = {}, params = {})
12
+ @resource = resource
13
+ @config = config
14
+ @response = {}
15
+ @params = params
16
+ set_response
17
+ end
18
+
19
+ def set_response
20
+ prepare_response
21
+ response_data = ::Api::ResponseBuilder::Data.new(resource, config).data
22
+ messages = ::Api::ResponseBuilder::Messages.new(resource, config).messages
23
+ status_code = ::Api::ResponseBuilder::StatusCode.new(resource, config).status_code
24
+
25
+ @response[:body] = response_data if response_data.present?
26
+ @response[:messages] = messages if messages.present?
27
+ @response[:status_code] = status_code
28
+ @response[:meta] = (config[:meta].to_h)
29
+ end
30
+
31
+ def prepare_response
32
+ status_msg = ::Api::ResponseBuilder::Status.new(resource, config).status_message
33
+ @response = status_msg.present? ? status_msg : {}
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module Api
3
+ module ResponseBuilder
4
+ # Class which helps in building messges for api response
5
+ class Messages < ::Api::ResponseBuilder::Base
6
+ attr_accessor :messages
7
+
8
+ def initialize(resource, config = {})
9
+ super(resource, config)
10
+ @messages = {}
11
+ set_messages
12
+ end
13
+
14
+ private
15
+
16
+ def set_messages
17
+ add_errors_if_any
18
+ return unless other_exception?
19
+ exception = ::Api::Exception.internal_server_error
20
+ @messages[:errors] = ::Api::Exception.new(exception, resource.message).
21
+ full_messages
22
+ end
23
+
24
+ def add_errors_if_any
25
+ @messages[:errors] = resource.full_messages if api_exception?
26
+ return unless resource_has_errors?
27
+ @messages[:errors] = resource.errors.full_messages
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module Api
3
+ module ResponseBuilder
4
+ # Class which helps in building status of api response
5
+ class Status < ::Api::ResponseBuilder::Base
6
+ attr_accessor :status_message
7
+
8
+ def initialize(resource, config = {})
9
+ super(resource, config)
10
+ @status_message = { status: "success" }
11
+ set_status_message
12
+ end
13
+
14
+ private
15
+
16
+ def set_status_message
17
+ return unless resource_has_errors? || api_exception? || other_exception?
18
+ @status_message[:status] = "failure"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ module Api
3
+ module ResponseBuilder
4
+ # Class which helps in building status code of api response
5
+ class StatusCode < ::Api::ResponseBuilder::Base
6
+ attr_accessor :status_code,
7
+ :resource
8
+
9
+ def initialize(resource, config = {})
10
+ super(resource, config)
11
+ @resource = resource
12
+ @status_code = :ok
13
+ set_status_code
14
+ end
15
+
16
+ private
17
+
18
+ def set_status_code
19
+ return unless resource_has_errors? || api_exception? || other_exception?
20
+ if resource_has_errors?
21
+ @status_code = :unprocessable_entity
22
+ elsif api_exception?
23
+ # resource is an instance of ApiException class
24
+ @status_code = @resource.status_code
25
+ else
26
+ # other exception which is probably internal server error
27
+ @status_code = :internal_server_error
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ module Api
2
+ module ResponseBuilder
3
+ VERSION = "0.1.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: api-response_builder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Dass Mk
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: i18n
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.7.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.7.0
69
+ description: API Response Builder to keep controllers super-slim
70
+ email:
71
+ - kalidasm610@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - Gemfile.lock
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - api-response_builder.gemspec
85
+ - bin/console
86
+ - bin/setup
87
+ - lib/api/exception.rb
88
+ - lib/api/response_builder.rb
89
+ - lib/api/response_builder/base.rb
90
+ - lib/api/response_builder/data.rb
91
+ - lib/api/response_builder/main.rb
92
+ - lib/api/response_builder/messages.rb
93
+ - lib/api/response_builder/status.rb
94
+ - lib/api/response_builder/status_code.rb
95
+ - lib/api/response_builder/version.rb
96
+ homepage: https://github.com/kalidasm/rails-api-response-builder
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.7.3
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: API Response Builder to keep controllers super-slim
120
+ test_files: []