api_me 0.10.7 → 0.14.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 +4 -4
- data/lib/api_me.rb +62 -21
- 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: b406ddb8c9fb3a865f90531fc62819b2e6d3870b45c2e11c20975ead90fb6345
|
4
|
+
data.tar.gz: 7a31d26119457065f33a499c0ff6913a3a0eed34c460ec878d60935458b23b76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d5fdc0406b3ba44ead6d00957966cfa3f734f62444594dd66ad3149ca115ebe916e828c8e3ba70cc3107537f67c58ef8e78fbaf6e0f463034be4737e793249c
|
7
|
+
data.tar.gz: 41267c7fb138975facca66c4f56929e7872459e6986f283a3f344efd4f4342f7423a07086724b685c7185967b029afd2fb4d66d8a7206211d4bc312dd8e547b5
|
data/lib/api_me.rb
CHANGED
@@ -9,14 +9,19 @@ 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
|
15
|
-
include ::Pundit
|
16
16
|
|
17
17
|
included do
|
18
|
+
include ::Pundit
|
19
|
+
include ActionController::Live
|
20
|
+
|
18
21
|
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
|
19
22
|
rescue_from ActiveRecord::RecordInvalid, with: :handle_active_record_errors
|
23
|
+
rescue_from ActiveRecord::RecordNotDestroyed, with: :handle_active_record_errors
|
24
|
+
rescue_from ActiveRecord::ReadOnlyRecord, with: :handle_active_record_errors
|
20
25
|
rescue_from ActiveRecord::RecordNotFound, with: :resource_not_found
|
21
26
|
end
|
22
27
|
|
@@ -74,15 +79,39 @@ module ApiMe
|
|
74
79
|
@sorted_scope = sort_scope(@filter_scope)
|
75
80
|
@pagination_object = paginate_scope(@sorted_scope, page_params)
|
76
81
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
82
|
+
respond_to do |format|
|
83
|
+
format.csv do
|
84
|
+
@csv_includes_scope = csv_includes_scope(@sorted_scope)
|
85
|
+
response.headers['Content-Type'] = 'text/csv'
|
86
|
+
response.headers['Cache-Control'] = 'no-cache'
|
87
|
+
# Hack to work around https://github.com/rack/rack/issues/1619
|
88
|
+
response.headers['Last-Modified'] = Time.current.httpdate
|
89
|
+
# Disable gzip in nginx
|
90
|
+
response.headers['X-Accel-Buffering'] = 'no'
|
91
|
+
response.headers['Content-Disposition'] = "attachment; filename=\"#{csv_filename}\""
|
92
|
+
|
93
|
+
::ApiMe::CsvStreamWriter.generate(response.stream) do |csv|
|
94
|
+
# headers
|
95
|
+
csv << csv_headers
|
96
|
+
@csv_includes_scope.find_each do |object|
|
97
|
+
csv << object.try('to_csv')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
ensure
|
101
|
+
response.stream.close
|
102
|
+
end
|
103
|
+
format.all do
|
104
|
+
render(
|
105
|
+
json: @pagination_object.results,
|
106
|
+
root: collection_root_key,
|
107
|
+
each_serializer: serializer_klass,
|
108
|
+
meta: {
|
109
|
+
page: @pagination_object.page_meta,
|
110
|
+
sort: sorting_meta(@filter_scope)
|
111
|
+
}
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
86
115
|
end
|
87
116
|
|
88
117
|
def show
|
@@ -93,7 +122,7 @@ module ApiMe
|
|
93
122
|
end
|
94
123
|
|
95
124
|
def new
|
96
|
-
render_errors(['new endpoint not supported'],
|
125
|
+
render_errors(['new endpoint not supported'], :not_found)
|
97
126
|
end
|
98
127
|
|
99
128
|
def create
|
@@ -101,11 +130,11 @@ module ApiMe
|
|
101
130
|
authorize_resource @object
|
102
131
|
create_resource!
|
103
132
|
|
104
|
-
render status:
|
133
|
+
render status: :created, json: @object, root: singular_root_key, serializer: serializer_klass
|
105
134
|
end
|
106
135
|
|
107
136
|
def edit
|
108
|
-
render_errors(['edit endpoint not supported'],
|
137
|
+
render_errors(['edit endpoint not supported'], :not_found)
|
109
138
|
end
|
110
139
|
|
111
140
|
def update
|
@@ -114,7 +143,7 @@ module ApiMe
|
|
114
143
|
authorize_resource @object
|
115
144
|
update_resource!
|
116
145
|
|
117
|
-
|
146
|
+
render status: :ok, json: @object, root: singular_root_key, serializer: serializer_klass
|
118
147
|
end
|
119
148
|
|
120
149
|
def destroy
|
@@ -122,11 +151,23 @@ module ApiMe
|
|
122
151
|
authorize_resource @object
|
123
152
|
destroy_resource!
|
124
153
|
|
125
|
-
head
|
154
|
+
head :no_content
|
126
155
|
end
|
127
156
|
|
128
157
|
protected
|
129
158
|
|
159
|
+
def csv_filename
|
160
|
+
"#{model_klass.name.dasherize}-#{Time.zone.now.to_date.to_s(:default)}.csv"
|
161
|
+
end
|
162
|
+
|
163
|
+
def csv_headers
|
164
|
+
model_klass.respond_to?('csv_headers') ? model_klass.csv_headers : []
|
165
|
+
end
|
166
|
+
|
167
|
+
def csv_includes_scope(scope)
|
168
|
+
scope
|
169
|
+
end
|
170
|
+
|
130
171
|
def singular_root_key
|
131
172
|
model_klass.name.singularize.underscore
|
132
173
|
end
|
@@ -147,7 +188,7 @@ module ApiMe
|
|
147
188
|
params[:sort]
|
148
189
|
end
|
149
190
|
|
150
|
-
def render_errors(errors, status =
|
191
|
+
def render_errors(errors, status = :unprocessable_entity)
|
151
192
|
render(json: { errors: errors }, status: status)
|
152
193
|
end
|
153
194
|
|
@@ -159,11 +200,11 @@ module ApiMe
|
|
159
200
|
|
160
201
|
def user_not_authorized
|
161
202
|
payload = { message: "User is not allowed to access #{params[:action]} on this resource" }
|
162
|
-
render json: payload, status:
|
203
|
+
render json: payload, status: :forbidden
|
163
204
|
end
|
164
205
|
|
165
206
|
def resource_not_found
|
166
|
-
head
|
207
|
+
head :not_found
|
167
208
|
end
|
168
209
|
|
169
210
|
def model_klass
|
@@ -225,7 +266,7 @@ module ApiMe
|
|
225
266
|
end
|
226
267
|
|
227
268
|
def find_resource
|
228
|
-
@find_resource ||= model_klass.
|
269
|
+
@find_resource ||= model_klass.find_by!(id: params[:id])
|
229
270
|
end
|
230
271
|
|
231
272
|
def build_resource
|
@@ -233,11 +274,11 @@ module ApiMe
|
|
233
274
|
end
|
234
275
|
|
235
276
|
def create_resource!
|
236
|
-
@object.save!
|
277
|
+
@object.save!
|
237
278
|
end
|
238
279
|
|
239
280
|
def update_resource!
|
240
|
-
@object.save!
|
281
|
+
@object.save!
|
241
282
|
end
|
242
283
|
|
243
284
|
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.1
|
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: 2021-02-03 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
|