api_responser 1.0.0.8 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: adee3e3b70fb9ad9b4848000ade2e3e920119d0497ab3d427456016ca1c4e6a4
4
- data.tar.gz: 18df245fb706daad93eb2130a6a4472d3686f6c3df167ba80e25f8a0d60e9838
3
+ metadata.gz: e28a527016ea61d6d5050e1bc049556e1803be3b99d63327e525fc244bc68064
4
+ data.tar.gz: 1b7399981de0a77cdcc935f828c098d2654a5a9ee72af1acb866563358a30ae2
5
5
  SHA512:
6
- metadata.gz: 1bf274f302575673ef57194ffca8bf312ebbcaa7411cd3ba12c846275fbc30884b34b33f2de024884f95641d40d4f0426eaf4162726b20f1e5a125c424ab3126
7
- data.tar.gz: 302d3c281bc263de67aa3d477b843f4ed7e473526f057cb92488aae5519124a43ff34dc175050276174d03ad8b39b05e83a2f50bacdbbb1c4e028f7347e70d60
6
+ metadata.gz: 31b9f21ee19f8c88757000df8608f6917ea83e869ac5b26c481d7467ec283f5e7fc0eafdd4291ffe5de35edfeb3784917af29ce150fcf48c081529ea40025f68
7
+ data.tar.gz: a1ffa33b876ba7047f6c75ac8732f1c13479bef7f1a45480c6499bee89fff85b5ffcd5d36fe2375fc9fdc6e9cb2d44b9709d3eec838c07c61ed517b5ff9eab76
@@ -4,9 +4,9 @@ en:
4
4
  access_denied: "Access denied"
5
5
  page_not_found: "Page not found"
6
6
  record_not_found: "Record not found"
7
- record_not_created: "Record not created: %{message}"
8
- record_not_updated: "Record not saved: %{message}"
9
- record_not_deleted: "Record not deleted: %{message}"
7
+ record_not_created: "%{message}"
8
+ record_not_updated: "%{message}"
9
+ record_not_deleted: "%{message}"
10
10
  bad_request: "Bad request"
11
11
  unauthorized: "Unauthorized"
12
12
  forbidden: "Forbidden"
data/lib/api_responser.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'erb'
2
+ require 'json'
1
3
  require 'rails'
2
4
 
3
5
  require_relative 'api_responser/railtie' if defined?(Rails)
@@ -19,11 +21,12 @@ module ApiResponser
19
21
  end
20
22
  end
21
23
 
24
+ # 204 No Content must not carry a body, so a response with records is sent as 200.
22
25
  def record_updated(records = nil)
23
26
  if records.nil?
24
27
  api_success(code: 204)
25
28
  else
26
- success_render(code: 204, message: i18n_message(__method__, status:"success"), records: records)
29
+ success_render(code: 200, message: i18n_message(__method__, status:"success"), records: records)
27
30
  end
28
31
  end
29
32
 
@@ -31,7 +34,7 @@ module ApiResponser
31
34
  if records.nil?
32
35
  api_success(code: 204)
33
36
  else
34
- success_render(code: 204, message: i18n_message(__method__, status:"success"), records: records)
37
+ success_render(code: 200, message: i18n_message(__method__, status:"success"), records: records)
35
38
  end
36
39
  end
37
40
 
@@ -114,7 +117,7 @@ module ApiResponser
114
117
 
115
118
  def success_render(code: 200, message: "", records: nil, records_count: nil)
116
119
  @code = code
117
- @message = message
120
+ @message = json_escape(message)
118
121
  @records = records
119
122
  @records_count = determine_records_count(records, records_count)
120
123
  {json: ERB.new(file_read("success")).result(binding), status: code}
@@ -122,11 +125,18 @@ module ApiResponser
122
125
 
123
126
  def error_render(code: 500, message: "", debug_message: "", report: false)
124
127
  @code = code
125
- @message = message
128
+ @message = json_escape(message)
126
129
  error_handling(code: code, message: message, debug_message: debug_message) if report == true
127
130
  {json: ERB.new(file_read("error")).result(binding), status: code}
128
131
  end
129
132
 
133
+ # The JSON templates interpolate @message inside a quoted string literal, so
134
+ # any quote or backslash in it would otherwise produce unparseable JSON.
135
+ # to_json gives us the escaped form; the surrounding quotes come from the template.
136
+ def json_escape(value)
137
+ value.to_s.to_json[1..-2]
138
+ end
139
+
130
140
  def file_read(type = "error")
131
141
  if defined?(Rails) && Rails.root
132
142
  app_view_path = Rails.root.join('app', 'views', 'api_responser', "#{type}.json.erb")
@@ -140,7 +150,7 @@ module ApiResponser
140
150
  end
141
151
 
142
152
  def i18n_message(method_name, status:"error", message: nil)
143
- message = message.full_messages.join(', ') if message.is_a?(ActiveModel::Errors)
153
+ message = message.full_messages.join(', ') if message.respond_to?(:full_messages)
144
154
  message = message.join(', ') if message.is_a?(Array)
145
155
  I18n.t("api_responser.#{status}.#{method_name}", message: message)
146
156
  end
@@ -150,6 +160,17 @@ module ApiResponser
150
160
  end
151
161
 
152
162
  def determine_records_count(records, records_count)
153
- records_count || ((records.is_a?(ActiveRecord::Relation) || (records.is_a?(Array))) ? records.count : (records.nil? || records.blank? ? 0 : 1))
163
+ return records_count if records_count
164
+ return records.count if collection?(records)
165
+
166
+ records.blank? ? 0 : 1
167
+ end
168
+
169
+ # ActiveRecord is not necessarily loaded (e.g. an API app with no database),
170
+ # so the constant is resolved lazily rather than referenced directly.
171
+ def collection?(records)
172
+ return true if records.is_a?(Array)
173
+
174
+ defined?(ActiveRecord::Relation) && records.is_a?(ActiveRecord::Relation)
154
175
  end
155
176
  end
data/readme.md CHANGED
@@ -13,6 +13,11 @@ A gem to standardize API responses in Rails applications.
13
13
 
14
14
  All methods return JSON and status.
15
15
 
16
+ ## Requirements
17
+
18
+ - Ruby >= 3.0
19
+ - Rails >= 7.0 (tested against 7.1 and 8.1)
20
+
16
21
  ## Installation
17
22
 
18
23
  Add this line to your application's Gemfile:
@@ -43,8 +48,8 @@ end
43
48
 
44
49
  ## Success Response list
45
50
  #### List of Items
46
- The *records* argument is required and should be an **Array**. The *records_count* argument is an **Integer** and is optional.\
47
- If *records_count* is not provided, the count will be calculated from the size of the *records* array
51
+ The *records* argument is required and should be an **Array** or an **ActiveRecord::Relation**. The *records_count* argument is an **Integer** and is optional.\
52
+ If *records_count* is not provided, the count will be calculated from the size of *records*. This is useful for pagination, where the rendered page is smaller than the total count
48
53
  ```ruby
49
54
  def record_index(records, records_count = nil)
50
55
  ```
@@ -54,15 +59,23 @@ The *records* argument should be a **Hash**. It should contain only one model
54
59
  def record_show(records)
55
60
  ```
56
61
  #### Item Create / Item Update / Item Delete
57
- *No arguments* are required
62
+ The *records* argument is optional.
63
+
64
+ When called *without arguments*, the response has no body and only a status code is returned
65
+ (**201** for create, **204 No Content** for update and delete).
66
+
67
+ When *records* are passed, the created/updated/deleted record is rendered in the response body
68
+ (**201** for create, **200** for update and delete). Update and delete use **200** rather than **204**
69
+ in this case, because a 204 No Content response is not allowed to carry a body.
70
+
58
71
  ```ruby
59
- def record_created
72
+ def record_created(records = nil)
60
73
  ```
61
74
  ```ruby
62
- def record_updated
75
+ def record_updated(records = nil)
63
76
  ```
64
77
  ```ruby
65
- def record_deleted
78
+ def record_deleted(records = nil)
66
79
  ```
67
80
 
68
81
 
@@ -108,6 +121,11 @@ The *message* argument is required, while the *debug_message* argument is option
108
121
  The *report* argument is optional and is useful if you would like to handle *debug_message*.\
109
122
  The *message* argument is used to output a message in the JSON response, whereas *debug_message* is useful for providing the real reason for the error (if *report* is **true**).
110
123
 
124
+ The *message* argument accepts a **String**, an **Array** of strings, or a model's
125
+ **errors** object (e.g. `item.errors`), which is joined into a single sentence.
126
+ Messages are escaped before being rendered, so text containing quotes, backslashes
127
+ or newlines is safe to pass through.
128
+
111
129
  ```ruby
112
130
  def record_not_created(message, debug_message = "", report:false)
113
131
  ```
@@ -145,23 +163,29 @@ Templates should be located in **app/views/api_responser/**
145
163
  The default templates are:
146
164
  #### success.json.erb
147
165
  ``` ruby
148
- "data":{
166
+ {"data": {
149
167
  "status": "success",
150
168
  "code": <%= @code %>,
151
169
  "message": "<%= @message %>",
152
170
  "records": <%= @records.to_json %>,
153
171
  "records_count": <%= @records_count %>
154
- }
172
+ }}
155
173
  ```
156
174
  #### error.json.erb
157
175
  ``` ruby
158
- "data":{
176
+ {"data": {
159
177
  "status": "error",
160
178
  "code": <%= @code %>,
161
179
  "message": "<%= @message %>"
162
- }
180
+ }}
163
181
  ```
164
182
 
183
+ **Note on `@message` in custom templates:** `@message` arrives already escaped for
184
+ JSON, so quotes, backslashes and newlines in a message are safe. It is escaped as
185
+ *string contents only* — the surrounding quotes come from the template, so keep
186
+ `@message` wrapped in `"` as shown above. Values you add yourself should be passed
187
+ through `to_json` (as `@records` is) so the rendered body stays valid JSON.
188
+
165
189
  ## Customizing Error Handling
166
190
  The gem provides a default error handler in **ApiResponserHelper**:
167
191
  ``` ruby
@@ -172,7 +196,7 @@ module ApiResponserHelper
172
196
  end
173
197
  ```
174
198
  You can customize this method to handle errors in a way that suits your application's requirements. For example, you might want to log errors to a file or send notifications to an external service.
175
- Helper should be located in **app/helpers/app_responser_helper.rb**
199
+ Helper should be located in **app/helpers/api_responser_helper.rb**
176
200
 
177
201
 
178
202
  ## Localization
metadata CHANGED
@@ -1,27 +1,26 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_responser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.8
4
+ version: '1.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nazim Mehdiyev
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-07-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
- - - "~>"
16
+ - - ">="
18
17
  - !ruby/object:Gem::Version
19
18
  version: '7.0'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
- - - "~>"
23
+ - - ">="
25
24
  - !ruby/object:Gem::Version
26
25
  version: '7.0'
27
26
  - !ruby/object:Gem::Dependency
@@ -76,7 +75,6 @@ licenses:
76
75
  - MIT
77
76
  metadata:
78
77
  source_code_uri: https://github.com/nmehdiyev/ApiResponser
79
- post_install_message:
80
78
  rdoc_options: []
81
79
  require_paths:
82
80
  - lib
@@ -84,15 +82,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
84
82
  requirements:
85
83
  - - ">="
86
84
  - !ruby/object:Gem::Version
87
- version: '0'
85
+ version: '3.0'
88
86
  required_rubygems_version: !ruby/object:Gem::Requirement
89
87
  requirements:
90
88
  - - ">="
91
89
  - !ruby/object:Gem::Version
92
90
  version: '0'
93
91
  requirements: []
94
- rubygems_version: 3.4.10
95
- signing_key:
92
+ rubygems_version: 3.7.2
96
93
  specification_version: 4
97
94
  summary: A gem to standardize API responses in Rails applications
98
95
  test_files: []