api_me 0.10.7 → 0.14.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19d6a0588c78c9c54147a063e94ad1bce087748819f1c422c847d351ce3df156
4
- data.tar.gz: 3dd3e873f6aa2518fee3a85d804d084822dac85dfa2a130c8d472a56d2cf7b5c
3
+ metadata.gz: b406ddb8c9fb3a865f90531fc62819b2e6d3870b45c2e11c20975ead90fb6345
4
+ data.tar.gz: 7a31d26119457065f33a499c0ff6913a3a0eed34c460ec878d60935458b23b76
5
5
  SHA512:
6
- metadata.gz: 1bd2149f84f9f7a76fe8fa39875b6a133ef19e707b634ded5c16607ca1007a5122593ea3b3cb8c8f757f6311828227ff7429f3ac915038fcac91a558ac1a787a
7
- data.tar.gz: 8438532c00323267eb7b4303af85948712b3821e5993b1d212441a7249cd3a00e9fedccdce32ef62b223cb7f716b21a48574390ba757636c93c352d5b4e58434
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
- render(
78
- json: @pagination_object.results,
79
- root: collection_root_key,
80
- each_serializer: serializer_klass,
81
- meta: {
82
- page: @pagination_object.page_meta,
83
- sort: sorting_meta(@filter_scope)
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'], 404)
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: 201, json: @object, root: singular_root_key, serializer: serializer_klass
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'], 404)
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
- head 204
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 204
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 = 422)
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: 403
203
+ render json: payload, status: :forbidden
163
204
  end
164
205
 
165
206
  def resource_not_found
166
- head 404
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.find_by_id!(params[:id])
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!(object_params)
277
+ @object.save!
237
278
  end
238
279
 
239
280
  def update_resource!
240
- @object.save!(object_params)
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
@@ -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
- if sort_reverse === 'true'
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApiMe
4
- VERSION = '0.10.7'
4
+ VERSION = '0.14.1'
5
5
  end
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.10.7
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: 2019-10-14 00:00:00.000000000 Z
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