jsonapi-scopes 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d0e74cff9d6b5917c86fc154f8046fd388a843c451fa66b22d09332c6cc67a53
4
+ data.tar.gz: 76f8158ff9f126a0ef1ab84ee4bec8f82716ebf1c9dd5ec43894891db3be472e
5
+ SHA512:
6
+ metadata.gz: 5555c208f5518bc0a8502e50eacf248f6f16844414838d6b43bcf156dabcdd3fe98d5600cea66b8bc1dbf794c6a194ff4052fd04932a85f244a989ee6598ffaa
7
+ data.tar.gz: dc3a47eedd3fcb472b702172f297a850156bd4e1e912fc4cb6d2db6b518397e39ee90ce26c5e8322c2a26b0bbb60487304f84b009523ce069f273c36daa9ec6c
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2019 Guillaume Briday
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/guillaumebriday)
2
+
3
+ # Jsonapi::Scopes
4
+ This gem provides an easy way to use a filter query parameter from the [jsonapi specification](https://jsonapi.org/recommendations/#filtering).
5
+
6
+ ## Installation
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'jsonapi-scopes', git: 'https://github.com/guillaumebriday/jsonapi-scopes.git'
11
+ ```
12
+
13
+ And then execute:
14
+ ```bash
15
+ $ bundle
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### Filter
21
+ The gem add a `filter` method to define public scopes.
22
+ It acts as a regular scope.
23
+
24
+ ```ruby
25
+ class Contact < ActiveRecord::Base
26
+ include Jsonapi::Filter
27
+
28
+ # Respond to `apply_filter`
29
+ filter :first_name, ->(value) {
30
+ where(first_name: value)
31
+ }
32
+
33
+ # Do NOT respond to `apply_filter`
34
+ scope :last_name, ->(value) {
35
+ where(last_name: value)
36
+ }
37
+ end
38
+ ```
39
+
40
+ You can use `apply_filter` in your controller to use the scopes defined with the previous `filter` method :
41
+
42
+ ```ruby
43
+ class ContactsController < ApplicationController
44
+ def index
45
+ @contacts = Contact.apply_filter(params)
46
+ end
47
+ end
48
+ ```
49
+
50
+ Then you can hit `/contacts?filter[first_name]=Bruce` to filter contacts where the last name exactly match `Bruce`.
51
+
52
+ But `/contacts?filter[last_name]=Wayne` will be completely ignored.
53
+
54
+ ### Sorting
55
+ The gem add `default_sort` and `sortable_fields` methods to control sort options. **Both are optional** and can be overriding in controllers.
56
+
57
+ ```ruby
58
+ class Contact < ActiveRecord::Base
59
+ include Jsonapi::Sort
60
+
61
+ sortable_fields :lastname, :firstname # List of allowed attributes
62
+ default_sort lastname: :desc, firstname: :asc # default hash with attributes and directions
63
+ end
64
+ ```
65
+
66
+ You can use `apply_sort` in your controller :
67
+
68
+ ```ruby
69
+ class ContactsController < ApplicationController
70
+ def index
71
+ @contacts = Contact.apply_sort(params)
72
+ end
73
+ end
74
+ ```
75
+
76
+ `apply_sort` accepts a second parameter to override data set with `sortable_fields` and `default_sort` for a specific controller.
77
+ ```ruby
78
+ class ContactsController < ApplicationController
79
+ def index
80
+ @contacts = Contact.apply_sort(params, allowed: :full_name, default: { full_name: :desc })
81
+ # Or @contacts = Contact.apply_sort(params, allowed: [:lastname, :full_name], default: { full_name: :desc })
82
+ end
83
+ end
84
+ ```
85
+
86
+ Then you can hit `/contacts?sort=lastname` to sort contacts by lastname.
87
+
88
+ Or use negative sort `/contacts?sort=-firstname` to sort by firstname in `desc` direction.
89
+
90
+ You can even combine multiple sort `/contacts?sort=lastname,-firstname`
91
+
92
+ ## Contributing
93
+ Do not hesitate to contribute to the project by adapting or adding features ! Bug reports or pull requests are welcome.
94
+
95
+ ## License
96
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rdoc/task'
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = 'rdoc'
13
+ rdoc.title = 'Jsonapi::Scopes'
14
+ rdoc.options << '--line-numbers'
15
+ rdoc.rdoc_files.include('README.md')
16
+ rdoc.rdoc_files.include('lib/**/*.rb')
17
+ end
18
+
19
+ require 'bundler/gem_tasks'
20
+
21
+ require 'rake/testtask'
22
+
23
+ Rake::TestTask.new(:test) do |t|
24
+ t.libs << 'test'
25
+ t.pattern = 'test/**/*_test.rb'
26
+ t.verbose = false
27
+ end
28
+
29
+ task default: :test
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jsonapi/scopes/filters'
4
+ require 'jsonapi/scopes/sorts'
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsonapi
4
+ module Filter
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ @filters ||= []
9
+ end
10
+
11
+ module ClassMethods
12
+ attr_reader :filters
13
+
14
+ def filter(name, *args)
15
+ scope(name, *args)
16
+ @filters << name
17
+ end
18
+
19
+ def apply_filter(params)
20
+ records = self
21
+ filtering_params = params.dig(:filter) || {}
22
+
23
+ filtering_params.each do |key, value|
24
+ records = records.public_send(key, value) if @filters.include?(key.to_sym)
25
+ end
26
+
27
+ records
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsonapi
4
+ module Sort
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ @sortable_fields ||= []
9
+ @default_sort ||= {}
10
+ end
11
+
12
+ module ClassMethods
13
+ def default_sort(sort)
14
+ @default_sort = sort
15
+ end
16
+
17
+ def sortable_fields(*fields)
18
+ @sortable_fields = fields
19
+ end
20
+
21
+ def apply_sort(params, options = { allowed: [], default: {} })
22
+ fields = params.dig(:sort)
23
+
24
+ allowed_fields = [options[:allowed]].flatten.presence || @sortable_fields
25
+ allowed_fields = allowed_fields.map(&:to_sym)
26
+
27
+ default_order = options[:default].presence || @default_sort
28
+ default_order = default_order.transform_keys(&:to_sym)
29
+
30
+ ordered_fields = convert_to_ordered_hash(fields)
31
+ filtered_fields = ordered_fields.select { |key, _| allowed_fields.include?(key) }
32
+
33
+ order = filtered_fields.presence || default_order
34
+
35
+ self.order(order)
36
+ end
37
+
38
+ private
39
+
40
+ def convert_to_ordered_hash(fields)
41
+ fields = fields.to_s.split(',').map(&:squish)
42
+
43
+ fields.each_with_object({}) do |field, hash|
44
+ if field.start_with?('-')
45
+ field = field[1..-1]
46
+ hash[field] = :desc
47
+ else
48
+ hash[field] = :asc
49
+ end
50
+ end.transform_keys(&:to_sym)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsonapi
4
+ module Scopes
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jsonapi-scopes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Guillaume Briday
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description:
42
+ email:
43
+ - guillaumebriday@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - LICENSE
49
+ - README.md
50
+ - Rakefile
51
+ - lib/jsonapi/scopes.rb
52
+ - lib/jsonapi/scopes/filters.rb
53
+ - lib/jsonapi/scopes/sorts.rb
54
+ - lib/jsonapi/scopes/version.rb
55
+ homepage: https://github.com/guillaumebriday/jsonapi-scopes
56
+ licenses:
57
+ - MIT
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubygems_version: 3.0.3
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: A gem to use filters and scopes for JSON:API.
78
+ test_files: []