mini_api 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: b21a5cc35f98d6a21450a738d876b0c7fee94d36113e0c3999f15df17c59f643
4
+ data.tar.gz: d60bf59a2ca1d64d4157fa46e9d770ed6b748e7895aa77bb2410936e09de91a6
5
+ SHA512:
6
+ metadata.gz: dcb3b028b14506c7c15dd2df893c25af5dabdfd7f47ca8781c32ebaeb7cf2a3b6c5d17be9a2befeeb2c0c7cc996c774230ba1f9fe11b07464f1a50cd7a6e4a9a
7
+ data.tar.gz: 2af62824d289dd78d9bb9e7a813a319179c56229ed6443e64746e65cf687e796e519a59d00c20c2f9745a3950d455111e0291eb6c7f05e5cd2cd6b635b9de7ab
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2023 Leon Cruz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,182 @@
1
+ ![](https://github.com/leoncruz/api-responder/actions/workflows/tests.yml/badge.svg)
2
+ [![Maintainability](https://api.codeclimate.com/v1/badges/ec2939be693459b7ce4d/maintainability)](https://codeclimate.com/github/leoncruz/api-responder/maintainability)
3
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/ec2939be693459b7ce4d/test_coverage)](https://codeclimate.com/github/leoncruz/api-responder/test_coverage)
4
+
5
+ # Mini Api
6
+ A gem to standardize json responses in Rails applications, highly inspired on [Responders](https:github.com/heartcombo/responders)
7
+
8
+ ## Table of Contents
9
+ - [Usage](#usage)
10
+ - [Respondering json](#respondering-json)
11
+ - [Success and failure actions](#success-and-failure-actions)
12
+ - [Overriding response](#overriding-response)
13
+ - [Pagination](#pagination)
14
+ - [Contributing](#contributing)
15
+ - [License](#license)
16
+
17
+ ## Installation
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem "mini_api"
22
+ ```
23
+
24
+ And then execute:
25
+ ```bash
26
+ $ bundle
27
+ ```
28
+
29
+ Your must install [Kaminari](https://github.com/kaminari/kaminari) to handle pagination
30
+ and [Active Model Serializers](http://github.com/rails-api/active_model_serializers) to handle data serialization
31
+
32
+ ## Usage
33
+
34
+ After install the gem, include the `MiniApi` module into your `ApplicationController` or other parent controller
35
+ ```ruby
36
+ class ApplicationController < ActionController::Base
37
+ include MiniApi
38
+ end
39
+ ```
40
+
41
+ This include three methods in your controllers: `render_json`, `page` and `per_page`
42
+
43
+ The methods `page` and `per_page` will handle the params: `page` and `per_page` respectively
44
+
45
+ ### Respondering json
46
+
47
+ In your controller you only need to call the `render_json` method informing what you want to send. Example:
48
+ ```ruby
49
+ class UsersController < ApplicationController
50
+ def show
51
+ user = User.find(params[:id])
52
+
53
+ render_json user
54
+ end
55
+ end
56
+ ```
57
+
58
+ The generated json will be like:
59
+ ```json
60
+ {
61
+ "success": true,
62
+ "data": { }, // user data here
63
+ "message": ""
64
+ }
65
+ ```
66
+
67
+ If your data is a `ActiveRecord::Relation`, the behavior is the same, with pagination data added to `meta` key
68
+ ```ruby
69
+ class UsersController < ApplicationController
70
+ def index
71
+ users = User.all
72
+
73
+ render_json users
74
+ end
75
+ end
76
+ ```
77
+ The response will be like:
78
+ ```json
79
+ {
80
+ "success": true,
81
+ "data": { }, // users here
82
+ "meta": {
83
+ "current_page": 1,
84
+ "next_page": 2,
85
+ "prev_page": null,
86
+ "total_pages": 10,
87
+ "total_records": 100
88
+ }
89
+ }
90
+ ```
91
+
92
+ ### Success and failure actions
93
+
94
+ Many times, our controller actions need to persist or validate some data coming from request, the default approach to do that is like:
95
+ ```ruby
96
+ class UsersController < ApplicationController
97
+ def new
98
+ user = User.new(user_params)
99
+
100
+ if user.save
101
+ render json: user, status: :created
102
+ else
103
+ render json: user.errors.messages, status: :unprocessable_entity
104
+ end
105
+ end
106
+ end
107
+ ```
108
+ But, with `mini_api`, you could simplify the action doing like:
109
+ ```ruby
110
+ class UsersController < ApplicationController
111
+ def new
112
+ user = User.new(user_params)
113
+
114
+ user.save
115
+
116
+ render_json user
117
+ end
118
+ end
119
+ ```
120
+ If the `user` was created successfully, then the response will be like:
121
+ ```json
122
+ {
123
+ "success": true,
124
+ "data": { }, // user data here
125
+ "message": "User was successfully created."
126
+ }
127
+ ```
128
+ with `status_code` 201
129
+
130
+ But, if user is not valid, then the response will be like:
131
+ ```json
132
+ {
133
+ "success": false,
134
+ "errors": { }, // user errors here
135
+ "message": "User could not be created."
136
+ }
137
+ ```
138
+ witht `status_code` 422
139
+
140
+ The `message` key is different based on actions on informed model: create, update, and destroy
141
+
142
+ You can respond any type of data, but ActiveRecord/ActiveModel::Model and ActiveRecord::Relation has a special treatment as shown above
143
+
144
+ ## Overriding response
145
+
146
+ You can override the `status`, `message` and `sucess` keys simply informing values to `render_json`. Example:
147
+
148
+ ```ruby
149
+ class UsersController < ApplicationController
150
+ def new
151
+ user = User.new(user_params)
152
+
153
+ if user.save
154
+ render_json user, message: 'custom message'
155
+ else
156
+ render_json user.errors.messages, status: :bad_request, success: true
157
+ end
158
+ end
159
+ end
160
+ ```
161
+ This way, the response will contain the informed values
162
+
163
+ ## Pagination
164
+
165
+ This plugin handle pagination using `kaminari` gem. The params to evaluate pagination are `page` and `per_page`
166
+
167
+ if no page is informed, by default will use page `1`
168
+
169
+ Default value of `per_page` is `25`. Only specific values are permitted to `per_page`, they are: 10, 25, 50, 100
170
+
171
+ If the value it is none of those, so the default value is used
172
+
173
+ ## Contributing
174
+
175
+ 1. Fork it
176
+ 2. Create your feature branch (git checkout -b my-new-feature)
177
+ 3. Commit your changes (git commit -am 'Add some feature')
178
+ 4. Push to the branch (git push origin my-new-feature)
179
+ 5. Create new Pull Request
180
+
181
+ ## License
182
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require 'bundler/gem_tasks'
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniApi
4
+ class DefaultResponder
5
+ def initialize(controller, resource, options = {})
6
+ @controller = controller
7
+ @resource = resource
8
+ @options = options
9
+ end
10
+
11
+ def respond
12
+ success = @options[:success] != false
13
+
14
+ @controller.render(
15
+ json: {
16
+ success: success,
17
+ data: @resource,
18
+ message: @options[:message] || nil
19
+ },
20
+ status: @options[:status] || :ok
21
+ )
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniApi
4
+ class KaminariNotInstalled < StandardError; end
5
+ end
@@ -0,0 +1,13 @@
1
+ en:
2
+ mini_api:
3
+ messages:
4
+ actions:
5
+ create:
6
+ notice: '%{resource_name} was successfully created.'
7
+ alert: '%{resource_name} could not be created.'
8
+ update:
9
+ notice: '%{resource_name} was successfully updated.'
10
+ alert: '%{resource_name} could not be updated.'
11
+ destroy:
12
+ notice: '%{resource_name} was successfully destroyed.'
13
+ alert: '%{resource_name} could not be destroyed.'
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mini_api/serialization'
4
+
5
+ module MiniApi
6
+ # class to handle json render of ActiveRecord::Base instances and ActiveModel::Model's
7
+ class ModelResponder
8
+ include Serialization
9
+
10
+ def initialize(controller, resource, options = {})
11
+ @controller = controller
12
+ @resource = resource
13
+ @options = options
14
+ end
15
+
16
+ def respond
17
+ body = {
18
+ success: resource_has_errors? == false,
19
+ message: @options[:message] || default_message
20
+ }
21
+
22
+ body =
23
+ if resource_has_errors?
24
+ { errors: @resource.errors.messages }.merge(body)
25
+ else
26
+ { data: serialiable_body(@resource) }.merge(body)
27
+ end
28
+
29
+ @controller.render json: body, status: status_code
30
+ end
31
+
32
+ private
33
+
34
+ def resource_has_errors?
35
+ !@resource.errors.empty?
36
+ end
37
+
38
+ def status_code
39
+ return @options[:status] if @options[:status].present?
40
+
41
+ return :unprocessable_entity if resource_has_errors?
42
+
43
+ return :created if @resource.previously_new_record?
44
+
45
+ return :no_content unless @resource.persisted?
46
+
47
+ :ok
48
+ end
49
+
50
+ def default_message
51
+ kind = resource_has_errors? ? 'alert' : 'notice'
52
+
53
+ I18n.t(
54
+ kind,
55
+ scope: [:mini_api, :messages, :actions, @controller.action_name],
56
+ resource_name: @resource.class.model_name.human,
57
+ default: ''
58
+ )
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/i18n'
4
+
5
+ module MiniApi
6
+ class Railtie < ::Rails::Railtie
7
+ # load translations
8
+ I18n.load_path << File.expand_path('../mini_api/locales/en.yml', __dir__)
9
+ end
10
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mini_api/exceptions/kaminari_not_installed'
4
+ require 'mini_api/serialization'
5
+
6
+ module MiniApi
7
+ class RelationResponder
8
+ include Serialization
9
+
10
+ def initialize(controller, resource, options = {})
11
+ @controller = controller
12
+ @resource = resource
13
+ @options = options
14
+ end
15
+
16
+ def respond
17
+ meta, collection = extract_meta_and_collection
18
+
19
+ @controller.render json: {
20
+ success: @options[:success] || true,
21
+ data: collection,
22
+ meta: meta
23
+ }, status: @options[:status]
24
+ end
25
+
26
+ private
27
+
28
+ def extract_meta_and_collection
29
+ collection = transform_resource_to_collection
30
+
31
+ [
32
+ {
33
+ current_page: collection.current_page,
34
+ next_page: collection.next_page,
35
+ prev_page: collection.prev_page,
36
+ total_pages: collection.total_pages,
37
+ total_records: collection.total_count
38
+ },
39
+ serialiable_body(collection)
40
+ ]
41
+ end
42
+
43
+ def transform_resource_to_collection
44
+ unless defined?(Kaminari)
45
+ raise KaminariNotInstalled, 'The Kaminari gem is not installed. Install to perform pagination operations'
46
+ end
47
+
48
+ @resource.page(@controller.page).per(@controller.per_page)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mini_api/default_responder'
4
+ require 'mini_api/model_responder'
5
+ require 'mini_api/relation_responder'
6
+
7
+ module MiniApi
8
+ class Responder
9
+ def initialize(controller, resource, options = {})
10
+ @controller = controller
11
+ @resource = resource
12
+ @options = options
13
+ end
14
+
15
+ def respond
16
+ case @resource
17
+ when ActiveRecord::Relation
18
+ relation_responder.respond
19
+ when ActiveRecord::Base, ActiveModel::Model
20
+ model_responder.respond
21
+ else
22
+ default_responder.respond
23
+ end
24
+ end
25
+
26
+ def relation_responder
27
+ RelationResponder.new(@controller, @resource, @options)
28
+ end
29
+
30
+ def model_responder
31
+ ModelResponder.new(@controller, @resource, @options)
32
+ end
33
+
34
+ def default_responder
35
+ DefaultResponder.new(@controller, @resource, @options)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniApi
4
+ #
5
+ # This module is responsible to handler integration with Active Model Serialier gem
6
+ # call the +get_serializer+ method of controller implemented by gem
7
+ # to find the serializer of informed object.
8
+
9
+ module Serialization
10
+ def serialiable_body(resource)
11
+ return resource unless defined?(ActiveModel::Serializer)
12
+
13
+ @controller.get_serializer(resource)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniApi
4
+ VERSION = '0.1.0'
5
+ end
data/lib/mini_api.rb ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mini_api/railtie'
4
+ require 'mini_api/responder'
5
+
6
+ # Entrypoint module
7
+ module MiniApi
8
+ def render_json(resource, options = {})
9
+ responder = Responder.new(self, resource, options)
10
+
11
+ responder.respond
12
+ end
13
+
14
+ def page
15
+ params[:page].to_i || 1
16
+ end
17
+
18
+ def per_page
19
+ if params[:per_page].to_i.in?([10, 25, 50, 100])
20
+ params[:per_page]
21
+ else
22
+ 25
23
+ end
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mini_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Leon Cruz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-06-17 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: 7.0.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 7.0.5
27
+ description: A collection of resources to create restful apis
28
+ email:
29
+ - leon.cruz.teixeira@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - MIT-LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - lib/mini_api.rb
38
+ - lib/mini_api/default_responder.rb
39
+ - lib/mini_api/exceptions/kaminari_not_installed.rb
40
+ - lib/mini_api/locales/en.yml
41
+ - lib/mini_api/model_responder.rb
42
+ - lib/mini_api/railtie.rb
43
+ - lib/mini_api/relation_responder.rb
44
+ - lib/mini_api/responder.rb
45
+ - lib/mini_api/serialization.rb
46
+ - lib/mini_api/version.rb
47
+ homepage:
48
+ licenses:
49
+ - MIT
50
+ metadata:
51
+ rubygems_mfa_required: 'true'
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubygems_version: 3.2.15
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: A collection of resources to create restful apis
71
+ test_files: []