mini_api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []