toller 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4ac81008c0c129af6b87f8021fa9f9b6c11b88b6023d6ce2775c74ed384ce68d
4
+ data.tar.gz: 99355eb92d24c18a5b59fde10658463aeea8d1a075cce28da6408b5b4757c86e
5
+ SHA512:
6
+ metadata.gz: f373b0612a6d960bbc2dfcc3975cf8e9e2e5c9346836f99f98f7607ce96f36eaa627b3f3116f18d699b158e4410a80ce85c33d959202798bc0c6b11f76b719ab
7
+ data.tar.gz: a3d9ba5fe8083d69a79a8853e9ad49a8abe8fae09d9a5106580362ef433ff60c899a964b2a4c0827af3e7375f3a1ed220671e55f3c526b450742db96cf449be8
@@ -0,0 +1,20 @@
1
+ Copyright 2020 David Freerksen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,68 @@
1
+ # Toller
2
+
3
+ URL based filtering and sorting. See the wiki for usage information.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'toller'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ $ bundle install
17
+ ```
18
+
19
+ ## Filtering
20
+
21
+ Filters are not automagically set up for you. You define the filters you want.
22
+
23
+ ### Filter Types
24
+
25
+ * integer - Filter on an integer column
26
+ * boolean - Filter on a boolean column
27
+ * string - Filter on a string column
28
+ * text - Filter on a text column
29
+ * date - Filter on a date column
30
+ * time - Filter on a time column
31
+ * datetime - Filter on a datetime column
32
+ * scope - Filter on an ActiveRecord scope
33
+
34
+ ## Sort
35
+
36
+ Sorting is not automagically set up for you. You define the sorting you want.
37
+
38
+ Sort parameters are passed in the URL as such `?sort=position`. Multiple sort parameters can be passed like so `?sort=-published_at,title`.
39
+
40
+ ### Sort Types
41
+
42
+ Every sort must have a type. Valid sort types are:
43
+
44
+ * integer - Sort on an integer column
45
+ * string - Sort on a string column
46
+ * text - Sort on a text column
47
+ * date - Sort on a date column
48
+ * time - Sort on a time column
49
+ * datetime - Sort on a datetime column
50
+ * scope - Sort on an ActiveRecord scope
51
+
52
+ ## Testing
53
+
54
+ ```bash
55
+ $ bin/test
56
+ ```
57
+
58
+ ## Contributing
59
+
60
+ 1. Fork it ([https://github.com/dfrerksen/recieve/fork](https://github.com/dfrerksen/recieve/fork))
61
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
62
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
63
+ 4. Push to the branch (`git push origin my-new-feature`)
64
+ 5. Create a new Pull Request
65
+
66
+ ## License
67
+
68
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,12 @@
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 'bundler/gem_tasks'
10
+
11
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
12
+ load 'rails/tasks/engine.rake'
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'toller/filter'
4
+ require 'toller/filters/mutators/boolean'
5
+ require 'toller/filters/mutators/date'
6
+ require 'toller/filters/mutators/datetime'
7
+ require 'toller/filters/mutators/integer'
8
+ require 'toller/filters/mutators/time'
9
+ require 'toller/filters/scope_handler'
10
+ require 'toller/filters/where_handler'
11
+ require 'toller/filtrator'
12
+ require 'toller/sort'
13
+ require 'toller/sorts/order_handler'
14
+ require 'toller/sorts/scope_handler'
15
+
16
+ ##
17
+ # Toller
18
+ #
19
+ # Query param based filtering and sorting
20
+ #
21
+ module Toller
22
+ extend ActiveSupport::Concern
23
+
24
+ def filtrate(collection)
25
+ Filtrator.filter(collection, filter_params, sort_params, retrievals)
26
+ end
27
+
28
+ included do
29
+ # none
30
+ end
31
+
32
+ class_methods do
33
+ def filter_on(parameter, type:, **options)
34
+ _filters << Filter.new(parameter, type, options)
35
+ end
36
+
37
+ def sort_on(parameter, type:, **options)
38
+ _filters << Sort.new(parameter, type, options)
39
+ end
40
+
41
+ def _filters
42
+ @_filters ||= []
43
+ end
44
+ end
45
+
46
+ def filter_params
47
+ params.fetch(filter_param_key.to_sym, {})
48
+ end
49
+
50
+ def sort_params
51
+ params.fetch(sort_param_key.to_sym, '').split(',')
52
+ end
53
+
54
+ def filter_param_key
55
+ :filters
56
+ end
57
+
58
+ def sort_param_key
59
+ :sort
60
+ end
61
+
62
+ private
63
+
64
+ def retrievals
65
+ self.class.ancestors.flat_map { |klass| klass.try(:_filters) }.compact
66
+ end
67
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ ##
5
+ # Filter
6
+ #
7
+ class Filter
8
+ attr_reader :parameter, :properties, :type
9
+
10
+ def initialize(parameter, type, options)
11
+ @parameter = parameter
12
+ @type = type
13
+ @properties = options.reverse_merge(
14
+ field: parameter,
15
+ default: false,
16
+ scope_name: nil
17
+ )
18
+ end
19
+
20
+ def apply!(collection, value)
21
+ if type == :scope
22
+ Filters::ScopeHandler.new.call(collection, value, properties)
23
+ else
24
+ Filters::WhereHandler.new.call(collection, type, value, properties)
25
+ end
26
+ end
27
+
28
+ def default
29
+ properties[:default]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ module Filters
5
+ module Mutators
6
+ ##
7
+ # Boolean filter mutator
8
+ #
9
+ module Boolean
10
+ module_function
11
+
12
+ def call(value)
13
+ %w[1 t true y yes].include?(value)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ module Filters
5
+ module Mutators
6
+ ##
7
+ # Date filter mutator
8
+ #
9
+ module Date
10
+ module_function
11
+
12
+ def call(value)
13
+ range_dots = inclusive_or_exclusive_range(value)
14
+
15
+ return value if range_dots.blank?
16
+
17
+ range(value, range_dots)
18
+ end
19
+
20
+ def range(value, dots)
21
+ Range.new(*value.split(dots))
22
+ end
23
+
24
+ def inclusive_or_exclusive_range(value)
25
+ return '...' if value.include?('...')
26
+ return '..' if value.include?('..')
27
+
28
+ nil
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ module Filters
5
+ module Mutators
6
+ ##
7
+ # Datetime filter mutator
8
+ #
9
+ module Datetime
10
+ module_function
11
+
12
+ def call(value)
13
+ range_dots = inclusive_or_exclusive_range(value)
14
+
15
+ return value if range_dots.blank?
16
+
17
+ range(value, range_dots)
18
+ end
19
+
20
+ def range(value, dots)
21
+ Range.new(*value.split(dots))
22
+ end
23
+
24
+ def inclusive_or_exclusive_range(value)
25
+ return '...' if value.include?('...')
26
+ return '..' if value.include?('..')
27
+
28
+ nil
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ module Filters
5
+ module Mutators
6
+ ##
7
+ # Integer filter mutator
8
+ #
9
+ module Integer
10
+ module_function
11
+
12
+ def call(value)
13
+ return value unless range?(value)
14
+
15
+ range(value)
16
+ end
17
+
18
+ def range?(value)
19
+ range_dots = inclusive_or_exclusive_range(value)
20
+
21
+ range_dots.present?
22
+ end
23
+
24
+ def range(value)
25
+ Range.new(*value.split(inclusive_or_exclusive_range(value)))
26
+ end
27
+
28
+ def inclusive_or_exclusive_range(value)
29
+ return '...' if value.include?('...')
30
+ return '..' if value.include?('..')
31
+
32
+ nil
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ module Filters
5
+ module Mutators
6
+ ##
7
+ # Time filter mutator
8
+ #
9
+ module Time
10
+ module_function
11
+
12
+ def call(value)
13
+ range_dots = inclusive_or_exclusive_range(value)
14
+
15
+ return value if range_dots.blank?
16
+
17
+ range(value, range_dots)
18
+ end
19
+
20
+ def range(value, dots)
21
+ Range.new(*value.split(dots))
22
+ end
23
+
24
+ def inclusive_or_exclusive_range(value)
25
+ return '...' if value.include?('...')
26
+ return '..' if value.include?('..')
27
+
28
+ nil
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ module Filters
5
+ ##
6
+ # Scope handler for filter
7
+ #
8
+ class ScopeHandler
9
+ def call(collection, value, properties)
10
+ scoped_name = properties[:scope_name] || properties[:field]
11
+
12
+ collection.public_send(scoped_name, value)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ module Filters
5
+ ##
6
+ # Where handler for filter
7
+ #
8
+ class WhereHandler
9
+ def call(collection, type, value, properties)
10
+ field_name = properties[:field]
11
+
12
+ mutated_value = value_mutator(type, value)
13
+
14
+ collection.where(field_name => mutated_value)
15
+ end
16
+
17
+ private
18
+
19
+ def value_mutator(type, value)
20
+ return value unless %i[boolean date datetime integer time].include?(type)
21
+
22
+ send("#{type}_mutator", value)
23
+ end
24
+
25
+ def boolean_mutator(value)
26
+ Mutators::Boolean.call(value)
27
+ end
28
+
29
+ def integer_mutator(value)
30
+ Mutators::Integer.call(value)
31
+ end
32
+
33
+ def date_mutator(value)
34
+ Mutators::Date.call(value)
35
+ end
36
+
37
+ def time_mutator(value)
38
+ Mutators::Time.call(value)
39
+ end
40
+
41
+ def datetime_mutator(value)
42
+ Mutators::Datetime.call(value)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ ##
5
+ # Filtrator
6
+ #
7
+ class Filtrator
8
+ attr_reader :collection, :filter_params, :retrievals, :sort_params
9
+
10
+ def self.filter(collection, filter_params, sort_params, retrievals)
11
+ new(collection, filter_params, sort_params, retrievals).filter
12
+ end
13
+
14
+ def initialize(collection, filter_params, sort_params, retrievals)
15
+ @collection = collection
16
+ @filter_params = filter_params
17
+ @sort_params = sort_params
18
+ @retrievals = retrievals
19
+ end
20
+
21
+ def filter
22
+ active_retrievals.reduce(collection) do |items, retrieval|
23
+ param_value = if retrieval.is_a?(Filter)
24
+ filter_params.fetch(retrieval.parameter, nil)
25
+ else
26
+ sort_params.include?("-#{retrieval.parameter}") ? :desc : :asc
27
+ end
28
+
29
+ retrieval.apply!(items, param_value)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def active_retrievals
36
+ retrievals.select do |retrieval|
37
+ retrieval.is_a?(Filter) ? filtering_activated?(retrieval) : sorting_activated?(retrieval)
38
+ end
39
+ end
40
+
41
+ def filtering_activated?(retrieval)
42
+ return true if filter_params.blank? && retrieval.default
43
+
44
+ filter_params.fetch(retrieval.parameter, nil).present?
45
+ end
46
+
47
+ def sorting_activated?(retrieval)
48
+ return true if sort_params.blank? && retrieval.default
49
+
50
+ string_parameter = retrieval.parameter.to_s
51
+
52
+ sort_params.include?(string_parameter) || sort_params.include?("-#{string_parameter}")
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ ##
5
+ # Sort
6
+ #
7
+ class Sort
8
+ attr_reader :parameter, :properties, :type
9
+
10
+ def initialize(parameter, type, options)
11
+ @parameter = parameter
12
+ @type = type
13
+ @properties = options.reverse_merge(
14
+ field: parameter,
15
+ default: false,
16
+ scope_name: nil
17
+ )
18
+ end
19
+
20
+ def apply!(collection, direction = :asc)
21
+ if type == :scope
22
+ Sorts::ScopeHandler.new.call(collection, direction, properties)
23
+ else
24
+ Sorts::OrderHandler.new.call(collection, direction, properties)
25
+ end
26
+ end
27
+
28
+ def default
29
+ properties[:default]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ module Sorts
5
+ ##
6
+ # Order handler for filter
7
+ #
8
+ class OrderHandler
9
+ def call(collection, direction, properties)
10
+ field_name = properties[:field]
11
+
12
+ collection.order(field_name => direction)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ module Sorts
5
+ ##
6
+ # Scope handler for sort
7
+ #
8
+ class ScopeHandler
9
+ def call(collection, direction, properties)
10
+ scoped_name = properties[:scope_name] || properties[:field]
11
+
12
+ collection.public_send(scoped_name, direction)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toller
4
+ VERSION = '0.1.0'
5
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: toller
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - David Freerksen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-09-23 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: 6.0.3
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 6.0.3.2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 6.0.3
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 6.0.3.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: sqlite3
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ description: Description of Toller.
48
+ email:
49
+ - dfreerksen@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - MIT-LICENSE
55
+ - README.md
56
+ - Rakefile
57
+ - lib/toller.rb
58
+ - lib/toller/filter.rb
59
+ - lib/toller/filters/mutators/boolean.rb
60
+ - lib/toller/filters/mutators/date.rb
61
+ - lib/toller/filters/mutators/datetime.rb
62
+ - lib/toller/filters/mutators/integer.rb
63
+ - lib/toller/filters/mutators/time.rb
64
+ - lib/toller/filters/scope_handler.rb
65
+ - lib/toller/filters/where_handler.rb
66
+ - lib/toller/filtrator.rb
67
+ - lib/toller/sort.rb
68
+ - lib/toller/sorts/order_handler.rb
69
+ - lib/toller/sorts/scope_handler.rb
70
+ - lib/toller/version.rb
71
+ homepage: https://github.com/dfreerksen/toller
72
+ licenses:
73
+ - MIT
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubygems_version: 3.0.3
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: Summary of Toller.
94
+ test_files: []