api_me 0.10.6 → 0.14.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 +4 -4
- data/lib/api_me.rb +60 -26
- data/lib/api_me/csv_stream_writer.rb +32 -0
- data/lib/api_me/sorting.rb +17 -3
- data/lib/api_me/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9646af7af8c9ba9da48b8655d133eaea222e669a916435eb86670c2ab19aa848
|
4
|
+
data.tar.gz: c580cea45bc61f08bb8cadb103db8090b7d87f7e560d7859d990dfa256735f6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e5bf1017206e3569e3998b419e4cd310ba80ab6787f2d71e317bc3880e99a1f32e29d3eb4d4355dc357a9c1429a1aa2f35e8a7fc6003da0faa65f0726e59189
|
7
|
+
data.tar.gz: 482033012c3342badf0c2b1fbeb099a04b32d217ec50af2d4940bc1bb33bd96c6e26bc5d6ef7ddebeefeac7314415e022e998f40196d97c2656b7e899b33c8f9
|
data/lib/api_me.rb
CHANGED
@@ -9,6 +9,7 @@ require 'api_me/version'
|
|
9
9
|
require 'api_me/base_filter'
|
10
10
|
require 'api_me/sorting'
|
11
11
|
require 'api_me/pagination'
|
12
|
+
require 'api_me/csv_stream_writer'
|
12
13
|
|
13
14
|
module ApiMe
|
14
15
|
extend ActiveSupport::Concern
|
@@ -17,6 +18,8 @@ module ApiMe
|
|
17
18
|
included do
|
18
19
|
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
|
19
20
|
rescue_from ActiveRecord::RecordInvalid, with: :handle_active_record_errors
|
21
|
+
rescue_from ActiveRecord::RecordNotDestroyed, with: :handle_active_record_errors
|
22
|
+
rescue_from ActiveRecord::ReadOnlyRecord, with: :handle_active_record_errors
|
20
23
|
rescue_from ActiveRecord::RecordNotFound, with: :resource_not_found
|
21
24
|
end
|
22
25
|
|
@@ -74,15 +77,39 @@ module ApiMe
|
|
74
77
|
@sorted_scope = sort_scope(@filter_scope)
|
75
78
|
@pagination_object = paginate_scope(@sorted_scope, page_params)
|
76
79
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
80
|
+
respond_to do |format|
|
81
|
+
format.csv do
|
82
|
+
@csv_includes_scope = csv_includes_scope(@sorted_scope)
|
83
|
+
response.headers['Content-Type'] = 'text/csv'
|
84
|
+
response.headers['Cache-Control'] = 'no-cache'
|
85
|
+
# Hack to work around https://github.com/rack/rack/issues/1619
|
86
|
+
response.headers['Last-Modified'] = Time.current.httpdate
|
87
|
+
# Disable gzip in nginx
|
88
|
+
response.headers['X-Accel-Buffering'] = 'no'
|
89
|
+
response.headers['Content-Disposition'] = "attachment; filename=\"#{csv_filename}\""
|
90
|
+
|
91
|
+
::ApiMe::CsvStreamWriter.generate(response.stream) do |csv|
|
92
|
+
# headers
|
93
|
+
csv << csv_headers
|
94
|
+
@csv_includes_scope.find_each do |object|
|
95
|
+
csv << object.try('to_csv')
|
96
|
+
end
|
97
|
+
end
|
98
|
+
ensure
|
99
|
+
response.stream.close
|
100
|
+
end
|
101
|
+
format.all do
|
102
|
+
render(
|
103
|
+
json: @pagination_object.results,
|
104
|
+
root: collection_root_key,
|
105
|
+
each_serializer: serializer_klass,
|
106
|
+
meta: {
|
107
|
+
page: @pagination_object.page_meta,
|
108
|
+
sort: sorting_meta(@filter_scope)
|
109
|
+
}
|
110
|
+
)
|
111
|
+
end
|
112
|
+
end
|
86
113
|
end
|
87
114
|
|
88
115
|
def show
|
@@ -93,10 +120,7 @@ module ApiMe
|
|
93
120
|
end
|
94
121
|
|
95
122
|
def new
|
96
|
-
|
97
|
-
authorize_resource @object
|
98
|
-
|
99
|
-
render_errors(['new endpoint not supported'], 404)
|
123
|
+
render_errors(['new endpoint not supported'], :not_found)
|
100
124
|
end
|
101
125
|
|
102
126
|
def create
|
@@ -104,22 +128,20 @@ module ApiMe
|
|
104
128
|
authorize_resource @object
|
105
129
|
create_resource!
|
106
130
|
|
107
|
-
render status:
|
131
|
+
render status: :created, json: @object, root: singular_root_key, serializer: serializer_klass
|
108
132
|
end
|
109
133
|
|
110
134
|
def edit
|
111
|
-
|
112
|
-
authorize_resource @object
|
113
|
-
|
114
|
-
render_errors(['edit endpoint not supported'], 404)
|
135
|
+
render_errors(['edit endpoint not supported'], :not_found)
|
115
136
|
end
|
116
137
|
|
117
138
|
def update
|
118
139
|
@object = find_resource
|
140
|
+
@object.assign_attributes(object_params)
|
119
141
|
authorize_resource @object
|
120
142
|
update_resource!
|
121
143
|
|
122
|
-
|
144
|
+
render status: :ok, json: @object, root: singular_root_key, serializer: serializer_klass
|
123
145
|
end
|
124
146
|
|
125
147
|
def destroy
|
@@ -127,11 +149,23 @@ module ApiMe
|
|
127
149
|
authorize_resource @object
|
128
150
|
destroy_resource!
|
129
151
|
|
130
|
-
head
|
152
|
+
head :no_content
|
131
153
|
end
|
132
154
|
|
133
155
|
protected
|
134
156
|
|
157
|
+
def csv_filename
|
158
|
+
"#{model_klass.name.dasherize}-#{Time.zone.now.to_date.to_s(:default)}.csv"
|
159
|
+
end
|
160
|
+
|
161
|
+
def csv_headers
|
162
|
+
model_klass.respond_to?('csv_headers') ? model_klass.csv_headers : []
|
163
|
+
end
|
164
|
+
|
165
|
+
def csv_includes_scope(scope)
|
166
|
+
scope
|
167
|
+
end
|
168
|
+
|
135
169
|
def singular_root_key
|
136
170
|
model_klass.name.singularize.underscore
|
137
171
|
end
|
@@ -152,7 +186,7 @@ module ApiMe
|
|
152
186
|
params[:sort]
|
153
187
|
end
|
154
188
|
|
155
|
-
def render_errors(errors, status =
|
189
|
+
def render_errors(errors, status = :unprocessable_entity)
|
156
190
|
render(json: { errors: errors }, status: status)
|
157
191
|
end
|
158
192
|
|
@@ -164,11 +198,11 @@ module ApiMe
|
|
164
198
|
|
165
199
|
def user_not_authorized
|
166
200
|
payload = { message: "User is not allowed to access #{params[:action]} on this resource" }
|
167
|
-
render json: payload, status:
|
201
|
+
render json: payload, status: :forbidden
|
168
202
|
end
|
169
203
|
|
170
204
|
def resource_not_found
|
171
|
-
head
|
205
|
+
head :not_found
|
172
206
|
end
|
173
207
|
|
174
208
|
def model_klass
|
@@ -230,7 +264,7 @@ module ApiMe
|
|
230
264
|
end
|
231
265
|
|
232
266
|
def find_resource
|
233
|
-
@find_resource ||= model_klass.
|
267
|
+
@find_resource ||= model_klass.find_by!(id: params[:id])
|
234
268
|
end
|
235
269
|
|
236
270
|
def build_resource
|
@@ -238,11 +272,11 @@ module ApiMe
|
|
238
272
|
end
|
239
273
|
|
240
274
|
def create_resource!
|
241
|
-
@object.save!
|
275
|
+
@object.save!
|
242
276
|
end
|
243
277
|
|
244
278
|
def update_resource!
|
245
|
-
@object.
|
279
|
+
@object.save!
|
246
280
|
end
|
247
281
|
|
248
282
|
def destroy_resource!
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'csv'
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module ApiMe
|
5
|
+
class CsvStreamWriter
|
6
|
+
# @!attribute [r] stream
|
7
|
+
# @return [IO]
|
8
|
+
attr_reader :stream
|
9
|
+
|
10
|
+
# Provides a similar interface to CSV.generate but compatible with an IO stream
|
11
|
+
# @example
|
12
|
+
# CsvStreamWriter.generate(stream) do |csv|
|
13
|
+
# csv << ['foo', 'bar']
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# @param [IO]
|
17
|
+
# @yield [CsvStreamWriter] csv
|
18
|
+
def self.generate(stream)
|
19
|
+
yield new(stream)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [IO]
|
23
|
+
def initialize(stream)
|
24
|
+
@stream = stream
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param [Array<String>]
|
28
|
+
def <<(row)
|
29
|
+
stream.write CSV.generate_line(row)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/api_me/sorting.rb
CHANGED
@@ -2,13 +2,16 @@
|
|
2
2
|
|
3
3
|
module ApiMe
|
4
4
|
class Sorting
|
5
|
-
attr_accessor :sort_criteria, :sort_reverse, :scope
|
5
|
+
attr_accessor :sort_criteria, :sort_reverse, :scope, :custom_sort_options
|
6
6
|
|
7
|
-
def initialize(scope:, sort_params:)
|
7
|
+
def initialize(scope:, sort_params:, custom_sort_options: {})
|
8
8
|
self.scope = scope
|
9
|
+
|
9
10
|
return unless sort_params
|
11
|
+
|
10
12
|
self.sort_criteria = sort_params[:criteria] || default_sort_criteria
|
11
13
|
self.sort_reverse = sort_params[:reverse] || false
|
14
|
+
self.custom_sort_options = custom_sort_options
|
12
15
|
end
|
13
16
|
|
14
17
|
def results
|
@@ -39,7 +42,14 @@ module ApiMe
|
|
39
42
|
end
|
40
43
|
|
41
44
|
def sorted_scope(criteria)
|
42
|
-
|
45
|
+
criteria_key = criteria.to_sym
|
46
|
+
if custom_sort_options.key?(criteria_key)
|
47
|
+
if sort_reverse == 'true'
|
48
|
+
custom_sort_scope(criteria_key).order(Arel.sql("#{custom_sort_options[criteria_key][:column]} DESC"))
|
49
|
+
else
|
50
|
+
custom_sort_scope(criteria_key).order(Arel.sql("#{custom_sort_options[criteria_key][:column]} ASC"))
|
51
|
+
end
|
52
|
+
elsif sort_reverse == 'true'
|
43
53
|
scope.order(criteria => :desc)
|
44
54
|
else
|
45
55
|
scope.order(criteria => :asc)
|
@@ -48,6 +58,10 @@ module ApiMe
|
|
48
58
|
|
49
59
|
private
|
50
60
|
|
61
|
+
def custom_sort_scope(criteria)
|
62
|
+
custom_sort_options[criteria].key?(:joins) ? scope.joins(custom_sort_options[criteria][:joins]) : scope
|
63
|
+
end
|
64
|
+
|
51
65
|
def default_sort_criteria
|
52
66
|
'id'
|
53
67
|
end
|
data/lib/api_me/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api_me
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Clopton
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-12-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -162,6 +162,7 @@ files:
|
|
162
162
|
- Rakefile
|
163
163
|
- lib/api_me.rb
|
164
164
|
- lib/api_me/base_filter.rb
|
165
|
+
- lib/api_me/csv_stream_writer.rb
|
165
166
|
- lib/api_me/engine.rb
|
166
167
|
- lib/api_me/model.rb
|
167
168
|
- lib/api_me/pagination.rb
|
@@ -199,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
200
|
version: '0'
|
200
201
|
requirements: []
|
201
202
|
rubyforge_project:
|
202
|
-
rubygems_version: 2.7.6
|
203
|
+
rubygems_version: 2.7.6.2
|
203
204
|
signing_key:
|
204
205
|
specification_version: 4
|
205
206
|
summary: Api Me
|