Skip to content

Commit d6982d5

Browse files
authored
Merge pull request #2 from stas/master
pull in upstream
2 parents a28f6d4 + 6ceac38 commit d6982d5

File tree

14 files changed

+89
-38
lines changed

14 files changed

+89
-38
lines changed

.github/dependabot.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "github-actions"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"
7+

.github/workflows/ci.yml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,14 @@ jobs:
88

99
strategy:
1010
matrix:
11-
ruby: [2.4, 2.7, '3.0']
12-
rails: ['5', '6.0', '6']
11+
ruby: ['3.0', '3.1', '3.2', '3.3']
12+
rails: ['6.1', '7.0', '7.1']
1313
exclude:
14-
- ruby: 2.4
14+
- ruby: 3.2
1515
rails: 6
16-
- ruby: '3.0'
17-
rails: 5
1816

1917
steps:
20-
- uses: actions/checkout@v2
18+
- uses: actions/checkout@v4
2119

2220
- uses: ruby/setup-ruby@v1
2321
with:
@@ -31,7 +29,6 @@ jobs:
3129
rm -rf Gemfile.lock
3230
sudo apt-get update
3331
sudo apt-get install libsqlite3-dev
34-
echo $RAILS_VERSION | grep -q '4' && export SQLITE3_VERSION='~> 1.3.6'
35-
echo $RAILS_VERSION | grep -q '4' && RUBOCOP_VERSION='~> 0.77'
32+
echo $RAILS_VERSION | grep -q '5' && export SQLITE3_VERSION='~> 1.3.6'
3633
bundle
3734
bundle exec rake

README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ The available features include:
3939
[sparse fields](https://jsonapi.org/format/#fetching-sparse-fieldsets))
4040
* [filtering](https://jsonapi.org/format/#fetching-filtering) and
4141
[sorting](https://jsonapi.org/format/#fetching-sorting) of the data
42-
(powered by Ransack)
42+
(powered by Ransack, soft-dependency)
4343
* [pagination](https://jsonapi.org/format/#fetching-pagination) support
4444

4545
## But how?
@@ -49,6 +49,16 @@ and [Ransack](https://github.com/activerecord-hackery/ransack).
4949

5050
Thanks to everyone who worked on these amazing projects!
5151

52+
## Sponsors
53+
54+
I'm grateful for the following companies for supporting this project!
55+
56+
<p align="center">
57+
<a href="https://www.luneteyewear.com"><img src="https://user-images.githubusercontent.com/112147/136836142-2bfba96e-447f-4eb6-b137-2445aee81b37.png"/></a>
58+
<a href="https://www.startuplandia.io"><img src="https://user-images.githubusercontent.com/112147/136836147-93f8ab17-2465-4477-a7ab-e38255483c66.png"/></a>
59+
</p>
60+
61+
5262
## Installation
5363

5464
Add this line to your application's Gemfile:
@@ -223,6 +233,12 @@ class MyController < ActionController::Base
223233
end
224234
```
225235

236+
This allows you to run queries like:
237+
238+
```bash
239+
$ curl -X GET /api/resources?fields[model]=model_attr,relationship
240+
```
241+
226242
### Filtering and sorting
227243

228244
`JSONAPI::Filtering` uses the power of
@@ -231,6 +247,8 @@ to filter and sort over a collection of records.
231247
The support is pretty extended and covers also relationships and composite
232248
matchers.
233249

250+
Please add `ransack` to your `Gemfile` in order to benefit from this functionality!
251+
234252
Here's an example:
235253

236254
```ruby
@@ -264,7 +282,7 @@ grouping. To enable expressions along with filters, use the option flags:
264282
```ruby
265283
options = { sort_with_expressions: true }
266284
jsonapi_filter(User.all, allowed_fields, options) do |filtered|
267-
render jsonapi: result.group('id').to_a
285+
render jsonapi: filtered.result.group('id').to_a
268286
end
269287
```
270288

@@ -314,7 +332,7 @@ If you want to change the default number of items per page or define a custom lo
314332
```ruby
315333
def jsonapi_page_size(pagination_params)
316334
per_page = pagination_params[:size].to_f.to_i
317-
per_page = 30 if per_page > 30
335+
per_page = 30 if per_page > 30 || per_page < 1
318336
per_page
319337
end
320338
```

Rakefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ require 'rubocop/rake_task'
44
require 'yaml'
55
require 'yardstick'
66

7+
# rubocop:disable Rails/RakeEnvironment
78
desc('Documentation stats and measurements')
89
task('qa:docs') do
910
yaml = YAML.load_file(File.expand_path('../.yardstick.yml', __FILE__))
@@ -13,6 +14,7 @@ task('qa:docs') do
1314
coverage = Yardstick.round_percentage(measure.coverage * 100)
1415
exit(1) if coverage < config.threshold
1516
end
17+
# rubocop:enable Rails/RakeEnvironment
1618

1719
desc('Codestyle check and linter')
1820
RuboCop::RakeTask.new('qa:code') do |task|
@@ -30,5 +32,5 @@ else
3032
task(qa: ['qa:docs', 'qa:code'])
3133
end
3234

33-
RSpec::Core::RakeTask.new(spec: :qa)
34-
task(default: :spec)
35+
RSpec::Core::RakeTask.new(:spec)
36+
task(default: %w[qa spec])

jsonapi.rb.gemspec

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,17 @@ Gem::Specification.new do |spec|
2020
spec.files += %w(LICENSE.txt README.md)
2121
spec.require_paths = ['lib']
2222

23+
spec.post_install_message = (
24+
'Install manually `ransack` gem before using `JSONAPI::Filtering`!'
25+
)
26+
2327
spec.add_dependency 'jsonapi-serializer'
24-
spec.add_dependency 'ransack'
2528
spec.add_dependency 'rack'
2629

2730
spec.add_development_dependency 'bundler'
28-
spec.add_development_dependency 'rails', ENV['RAILS_VERSION']
31+
spec.add_development_dependency 'ransack'
32+
spec.add_development_dependency 'railties', ENV['RAILS_VERSION']
33+
spec.add_development_dependency 'activerecord', ENV['RAILS_VERSION']
2934
spec.add_development_dependency 'sqlite3', ENV['SQLITE3_VERSION']
3035
spec.add_development_dependency 'ffaker'
3136
spec.add_development_dependency 'rspec', '~> 3.0'

lib/jsonapi/deserialization.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def jsonapi_deserialize(document, options = {})
6565
rel_name = jsonapi_inflector.singularize(assoc_name)
6666

6767
if assoc_data.is_a?(Array)
68-
parsed["#{rel_name}_ids"] = assoc_data.map { |ri| ri['id'] }.compact
68+
parsed["#{rel_name}_ids"] = assoc_data.filter_map { |ri| ri['id'] }
6969
next
7070
end
7171

lib/jsonapi/error_serializer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class ErrorSerializer
88
set_type :error
99

1010
# Object/Hash attribute helpers.
11-
[:status, :source, :title, :detail].each do |attr_name|
11+
[:status, :source, :title, :detail, :code].each do |attr_name|
1212
attribute attr_name do |object|
1313
object.try(attr_name) || object.try(:fetch, attr_name, nil)
1414
end

lib/jsonapi/fetching.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def jsonapi_fields
1717
end
1818

1919
params[:fields].each do |k, v|
20-
extracted[k] = v.to_s.split(',').map(&:strip).compact
20+
extracted[k] = v.to_s.split(',').filter_map(&:strip)
2121
end
2222

2323
extracted
@@ -29,7 +29,7 @@ def jsonapi_fields
2929
#
3030
# @return [Array]
3131
def jsonapi_include
32-
params['include'].to_s.split(',').map(&:strip).compact
32+
params['include'].to_s.split(',').filter_map(&:strip)
3333
end
3434
end
3535
end

lib/jsonapi/filtering.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
require 'ransack/predicate'
2-
require_relative 'patches'
1+
begin
2+
require 'active_record'
3+
require 'ransack'
4+
require_relative 'patches'
5+
rescue LoadError
6+
end
37

48
# Filtering and sorting support
59
module JSONAPI

lib/jsonapi/rails.rb

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def self.install!
3737
# @return [NilClass]
3838
def self.add_errors_renderer!
3939
ActionController::Renderers.add(:jsonapi_errors) do |resource, options|
40-
self.content_type ||= Mime[:jsonapi]
40+
self.content_type = Mime[:jsonapi] if self.media_type.nil?
4141

4242
many = JSONAPI::Rails.is_collection?(resource, options[:is_collection])
4343
resource = [resource] unless many
@@ -47,7 +47,7 @@ def self.add_errors_renderer!
4747
) unless resource.is_a?(ActiveModel::Errors)
4848

4949
errors = []
50-
model = resource.instance_variable_get('@base')
50+
model = resource.instance_variable_get(:@base)
5151

5252
if respond_to?(:jsonapi_serializer_class, true)
5353
model_serializer = jsonapi_serializer_class(model, false)
@@ -56,8 +56,8 @@ def self.add_errors_renderer!
5656
end
5757

5858
details = {}
59-
if ::Rails::VERSION::MAJOR >= 6 && ::Rails::VERSION::MINOR >= 1
60-
resource.map do |error|
59+
if ::Rails.gem_version >= Gem::Version.new('6.1')
60+
resource.each do |error|
6161
attr = error.attribute
6262
details[attr] ||= []
6363
details[attr] << error.detail.merge(message: error.message)
@@ -90,7 +90,7 @@ def self.add_errors_renderer!
9090
# @return [NilClass]
9191
def self.add_renderer!
9292
ActionController::Renderers.add(:jsonapi) do |resource, options|
93-
self.content_type ||= Mime[:jsonapi]
93+
self.content_type = Mime[:jsonapi] if self.media_type.nil?
9494

9595
JSONAPI_METHODS_MAPPING.to_a[0..1].each do |opt, method_name|
9696
next unless respond_to?(method_name, true)
@@ -100,7 +100,7 @@ def self.add_renderer!
100100
# If it's an empty collection, return it directly.
101101
many = JSONAPI::Rails.is_collection?(resource, options[:is_collection])
102102
if many && !resource.any?
103-
return options.slice(:meta, :links).merge(data: []).to_json
103+
return options.slice(:meta, :links).compact.merge(data: []).to_json
104104
end
105105

106106
JSONAPI_METHODS_MAPPING.to_a[2..-1].each do |opt, method_name|
@@ -121,15 +121,13 @@ def self.add_renderer!
121121

122122
# Checks if an object is a collection
123123
#
124-
# Stolen from [JSONAPI::Serializer], instance method.
124+
# Basically forwards it to a [JSONAPI::Serializer] as there's no public API
125125
#
126126
# @param resource [Object] to check
127127
# @param force_is_collection [NilClass] flag to overwrite
128128
# @return [TrueClass] upon success
129129
def self.is_collection?(resource, force_is_collection = nil)
130-
return force_is_collection unless force_is_collection.nil?
131-
132-
resource.respond_to?(:size) && !resource.respond_to?(:each_pair)
130+
JSONAPI::ErrorSerializer.is_collection?(resource, force_is_collection)
133131
end
134132

135133
# Resolves resource serializer class

0 commit comments

Comments
 (0)