trailblazer-finder 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 +7 -0
- data/.gitignore +19 -0
- data/.rubocop.yml +45 -0
- data/.rubocop_todo.yml +52 -0
- data/.travis.yml +15 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +23 -0
- data/README.md +494 -0
- data/Rakefile +29 -0
- data/lib/trailblazer/finder/adapters/active_record/paging.rb +20 -0
- data/lib/trailblazer/finder/adapters/active_record/sorting.rb +20 -0
- data/lib/trailblazer/finder/adapters/active_record.rb +30 -0
- data/lib/trailblazer/finder/adapters/data_mapper/paging.rb +20 -0
- data/lib/trailblazer/finder/adapters/data_mapper/sorting.rb +25 -0
- data/lib/trailblazer/finder/adapters/data_mapper.rb +30 -0
- data/lib/trailblazer/finder/adapters/friendly_id.rb +33 -0
- data/lib/trailblazer/finder/adapters/kaminari.rb +18 -0
- data/lib/trailblazer/finder/adapters/sequel/paging.rb +20 -0
- data/lib/trailblazer/finder/adapters/sequel/sorting.rb +25 -0
- data/lib/trailblazer/finder/adapters/sequel.rb +30 -0
- data/lib/trailblazer/finder/adapters/will_paginate.rb +18 -0
- data/lib/trailblazer/finder/adapters.rb +26 -0
- data/lib/trailblazer/finder/base.rb +98 -0
- data/lib/trailblazer/finder/errors/block_ignored.rb +11 -0
- data/lib/trailblazer/finder/errors/invalid_defined_by_value.rb +11 -0
- data/lib/trailblazer/finder/errors/invalid_number.rb +16 -0
- data/lib/trailblazer/finder/errors/missing_entity_type.rb +11 -0
- data/lib/trailblazer/finder/errors/with_ignored.rb +11 -0
- data/lib/trailblazer/finder/features/paging.rb +55 -0
- data/lib/trailblazer/finder/features/sorting.rb +66 -0
- data/lib/trailblazer/finder/features.rb +22 -0
- data/lib/trailblazer/finder/filter.rb +66 -0
- data/lib/trailblazer/finder/find.rb +29 -0
- data/lib/trailblazer/finder/utils/extra.rb +31 -0
- data/lib/trailblazer/finder/utils/params.rb +28 -0
- data/lib/trailblazer/finder/utils/parse.rb +25 -0
- data/lib/trailblazer/finder/utils/string.rb +35 -0
- data/lib/trailblazer/finder/version.rb +5 -0
- data/lib/trailblazer/finder.rb +29 -0
- data/lib/trailblazer/operation/finder.rb +61 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/spec_helper_active_record.rb +50 -0
- data/spec/spec_helper_data_mapper.rb +35 -0
- data/spec/spec_helper_sequel.rb +32 -0
- data/spec/support/paging_shared_example.rb +65 -0
- data/spec/support/sorting_shared_example.rb +95 -0
- data/spec/trailblazer/finder/adapters/active_record/base_spec.rb +112 -0
- data/spec/trailblazer/finder/adapters/active_record/paging_spec.rb +64 -0
- data/spec/trailblazer/finder/adapters/active_record/sorting_spec.rb +82 -0
- data/spec/trailblazer/finder/adapters/data_mapper/base_spec.rb +112 -0
- data/spec/trailblazer/finder/adapters/data_mapper/paging_spec.rb +64 -0
- data/spec/trailblazer/finder/adapters/data_mapper/sorting_spec.rb +85 -0
- data/spec/trailblazer/finder/adapters/friendly_id_spec.rb +46 -0
- data/spec/trailblazer/finder/adapters/kaminari_spec.rb +64 -0
- data/spec/trailblazer/finder/adapters/sequel/base_spec.rb +112 -0
- data/spec/trailblazer/finder/adapters/sequel/paging_spec.rb +64 -0
- data/spec/trailblazer/finder/adapters/sequel/sorting_spec.rb +82 -0
- data/spec/trailblazer/finder/adapters/will_paginate_spec.rb +71 -0
- data/spec/trailblazer/finder/adapters_spec.rb +110 -0
- data/spec/trailblazer/finder/base_spec.rb +329 -0
- data/spec/trailblazer/finder/features/paging_spec.rb +104 -0
- data/spec/trailblazer/finder/features/sorting_spec.rb +100 -0
- data/spec/trailblazer/finder/features_spec.rb +55 -0
- data/spec/trailblazer/finder/filter_spec.rb +133 -0
- data/spec/trailblazer/finder/find_spec.rb +72 -0
- data/spec/trailblazer/finder/utils/extra_spec.rb +41 -0
- data/spec/trailblazer/finder/utils/params_spec.rb +39 -0
- data/spec/trailblazer/finder/utils/parse_spec.rb +33 -0
- data/spec/trailblazer/finder/utils/string_spec.rb +25 -0
- data/spec/trailblazer/operation/finder_spec.rb +103 -0
- data/spec/trailblazer/operation/paging_spec.rb +68 -0
- data/spec/trailblazer/operation/sorting_spec.rb +80 -0
- data/trailblazer-finder.gemspec +41 -0
- metadata +402 -0
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
6
|
+
t.rspec_opts = '--format documentation --format RspecJunitFormatter --out test-reports/spec.xml'
|
7
|
+
end
|
8
|
+
RSpec::Core::RakeTask.new(:tests) do |t|
|
9
|
+
t.rspec_opts = '--format progress --format documentation'
|
10
|
+
end
|
11
|
+
RSpec::Core::RakeTask.new(:spec_report) do |t|
|
12
|
+
t.rspec_opts = '--format html --out reports/rspec_results.html'
|
13
|
+
end
|
14
|
+
|
15
|
+
RuboCop::RakeTask.new(:rubocop)
|
16
|
+
|
17
|
+
desc 'Remove temporary files'
|
18
|
+
task :clean do
|
19
|
+
%x{rm -rf *.gem doc pkg coverage test-reports}
|
20
|
+
%x{rm -f `find . -name '*.rbc'`}
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Build the gem"
|
24
|
+
task :gem do
|
25
|
+
%x{gem build trailblazer-finder.gemspec}
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'Running Tests'
|
29
|
+
task default: %i[clean spec rubocop]
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Adapters
|
4
|
+
module ActiveRecord
|
5
|
+
# ActiveRecord - Paging Adapter
|
6
|
+
module Paging
|
7
|
+
def self.included(base)
|
8
|
+
base.extend Features::Paging::ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def apply_paging(entity_type)
|
14
|
+
entity_type.limit(per_page).offset(([page, 1].max - 1) * per_page)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Adapters
|
4
|
+
module ActiveRecord
|
5
|
+
# ActiveRecord - Sorting Adapter
|
6
|
+
module Sorting
|
7
|
+
def self.included(base)
|
8
|
+
base.extend Features::Sorting::ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def sort_it(entity_type, sort_attribute, sort_direction)
|
14
|
+
entity_type.order sort_attribute.to_s => sort_direction
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'trailblazer/finder/adapters/active_record/paging'
|
2
|
+
require 'trailblazer/finder/adapters/active_record/sorting'
|
3
|
+
|
4
|
+
module Trailblazer
|
5
|
+
class Finder
|
6
|
+
module Adapters
|
7
|
+
# ActiveRecord Adapter
|
8
|
+
module ActiveRecord
|
9
|
+
def self.included(base)
|
10
|
+
base.extend ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# :nocov:
|
15
|
+
def normalize_find_handler(handler, key)
|
16
|
+
case handler
|
17
|
+
when Symbol then ->(entity_type, value) { method(handler).call entity_type, value }
|
18
|
+
when Proc then handler
|
19
|
+
else ->(entity_type, value) { entity_type.where(key.to_sym => value) unless Utils::String.blank?(value) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
# :nocov:
|
23
|
+
end
|
24
|
+
|
25
|
+
include Paging if defined?(Features::Paging::ClassMethods)
|
26
|
+
include Sorting if defined?(Features::Sorting::ClassMethods)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Adapters
|
4
|
+
module DataMapper
|
5
|
+
# Sequel - Paging Adapter
|
6
|
+
module Paging
|
7
|
+
def self.included(base)
|
8
|
+
base.extend Features::Paging::ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def apply_paging(entity_type)
|
14
|
+
entity_type.all(limit: per_page, offset: ([page, 1].max - 1) * per_page)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Adapters
|
4
|
+
module DataMapper
|
5
|
+
# Sequel - Sorting Adapter
|
6
|
+
module Sorting
|
7
|
+
def self.included(base)
|
8
|
+
base.extend Features::Sorting::ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def sort_it(entity_type, sort_attribute, sort_direction)
|
14
|
+
case sort_direction
|
15
|
+
when 'asc', 'ascending'
|
16
|
+
entity_type.all(order: [sort_attribute.to_sym.asc])
|
17
|
+
when 'desc', 'descending'
|
18
|
+
entity_type.all(order: [sort_attribute.to_sym.desc])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'trailblazer/finder/adapters/data_mapper/paging'
|
2
|
+
require 'trailblazer/finder/adapters/data_mapper/sorting'
|
3
|
+
|
4
|
+
module Trailblazer
|
5
|
+
class Finder
|
6
|
+
module Adapters
|
7
|
+
# ActiveRecord Adapter
|
8
|
+
module DataMapper
|
9
|
+
def self.included(base)
|
10
|
+
base.extend ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# :nocov:
|
15
|
+
def normalize_find_handler(handler, key)
|
16
|
+
case handler
|
17
|
+
when Symbol then ->(entity_type, value) { method(handler).call entity_type, value }
|
18
|
+
when Proc then handler
|
19
|
+
else ->(entity_type, value) { entity_type.all(key.to_sym => value) unless Utils::String.blank?(value) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
# :nocov:
|
23
|
+
end
|
24
|
+
|
25
|
+
include Paging if defined?(Features::Paging::ClassMethods)
|
26
|
+
include Sorting if defined?(Features::Sorting::ClassMethods)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
if Gem.loaded_specs.key?('activerecord')
|
2
|
+
module Trailblazer
|
3
|
+
class Finder
|
4
|
+
module Adapters
|
5
|
+
# FriendlyId Adapter
|
6
|
+
module FriendlyId
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
base.class_eval do
|
10
|
+
filter_by :id, with: :apply_slug_filter
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def apply_slug_filter(entity_type, value)
|
15
|
+
return if value.nil?
|
16
|
+
return if value == ''
|
17
|
+
if num?(value)
|
18
|
+
entity_type.where(id: value)
|
19
|
+
else
|
20
|
+
entity_type.where(slug: Utils::String.underscore(value.downcase))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def num?(str)
|
25
|
+
Integer(str)
|
26
|
+
rescue ArgumentError, TypeError
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Adapters
|
4
|
+
# Kaminari Adapter
|
5
|
+
module Kaminari
|
6
|
+
def self.included(base)
|
7
|
+
base.extend Features::Paging::ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def apply_paging(entity_type)
|
13
|
+
entity_type.page(page).per(per_page)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Adapters
|
4
|
+
module Sequel
|
5
|
+
# Sequel - Paging Adapter
|
6
|
+
module Paging
|
7
|
+
def self.included(base)
|
8
|
+
base.extend Features::Paging::ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def apply_paging(entity_type)
|
14
|
+
entity_type.limit(per_page).offset(([page, 1].max - 1) * per_page)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Adapters
|
4
|
+
module Sequel
|
5
|
+
# Sequel - Sorting Adapter
|
6
|
+
module Sorting
|
7
|
+
def self.included(base)
|
8
|
+
base.extend Features::Sorting::ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def sort_it(entity_type, sort_attribute, sort_direction)
|
14
|
+
case sort_direction
|
15
|
+
when 'asc', 'ascending'
|
16
|
+
entity_type.order(sort_attribute.to_sym)
|
17
|
+
when 'desc', 'descending'
|
18
|
+
entity_type.reverse(sort_attribute.to_sym)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'trailblazer/finder/adapters/sequel/paging'
|
2
|
+
require 'trailblazer/finder/adapters/sequel/sorting'
|
3
|
+
|
4
|
+
module Trailblazer
|
5
|
+
class Finder
|
6
|
+
module Adapters
|
7
|
+
# Sequel Adapter
|
8
|
+
module Sequel
|
9
|
+
def self.included(base)
|
10
|
+
base.extend ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# :nocov:
|
15
|
+
def normalize_find_handler(handler, key)
|
16
|
+
case handler
|
17
|
+
when Symbol then ->(entity_type, value) { method(handler).call entity_type, value }
|
18
|
+
when Proc then handler
|
19
|
+
else ->(entity_type, value) { entity_type.where(key.to_sym => value) unless Utils::String.blank?(value) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
# :nocov:
|
23
|
+
end
|
24
|
+
|
25
|
+
include Paging if defined?(Features::Paging::ClassMethods)
|
26
|
+
include Sorting if defined?(Features::Sorting::ClassMethods)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Adapters
|
4
|
+
# WillPaginate Adapter
|
5
|
+
module WillPaginate
|
6
|
+
def self.included(base)
|
7
|
+
base.extend Features::Paging::ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def apply_paging(entity_type)
|
13
|
+
entity_type.paginate per_page: per_page, page: page.zero? ? nil : page
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'trailblazer/finder/adapters/active_record'
|
2
|
+
require 'trailblazer/finder/adapters/sequel'
|
3
|
+
require 'trailblazer/finder/adapters/data_mapper'
|
4
|
+
require 'trailblazer/finder/adapters/kaminari'
|
5
|
+
require 'trailblazer/finder/adapters/will_paginate'
|
6
|
+
require 'trailblazer/finder/adapters/friendly_id'
|
7
|
+
|
8
|
+
module Trailblazer
|
9
|
+
class Finder
|
10
|
+
# Adapters
|
11
|
+
module Adapters
|
12
|
+
def self.included(base)
|
13
|
+
base.extend ClassMethods
|
14
|
+
end
|
15
|
+
|
16
|
+
# ClassMethods
|
17
|
+
module ClassMethods
|
18
|
+
def adapters(*mods)
|
19
|
+
mods.each do |mod|
|
20
|
+
include mod
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'hashie/extensions/deep_locate'
|
2
|
+
|
3
|
+
module Trailblazer
|
4
|
+
class Finder
|
5
|
+
# Base module
|
6
|
+
module Base
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
base.instance_eval do
|
10
|
+
@config = {
|
11
|
+
defaults: {},
|
12
|
+
actions: {},
|
13
|
+
entity_type: nil
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
config = self.class.config
|
20
|
+
entity_type = options[:entity_type] || (config[:entity_type] && instance_eval(&config[:entity_type]))
|
21
|
+
actions = config[:actions] || {}
|
22
|
+
params = Utils::Params.normalize_params(config[:defaults], options[:filter], actions.keys)
|
23
|
+
|
24
|
+
raise Error::MissingEntityType unless entity_type
|
25
|
+
|
26
|
+
@find = Find.new(entity_type, params, actions)
|
27
|
+
end
|
28
|
+
|
29
|
+
def results
|
30
|
+
@results ||= fetch_results
|
31
|
+
end
|
32
|
+
|
33
|
+
def results?
|
34
|
+
results.any?
|
35
|
+
end
|
36
|
+
|
37
|
+
def count
|
38
|
+
@count ||= @find.count self
|
39
|
+
end
|
40
|
+
|
41
|
+
def params(additions = {})
|
42
|
+
if additions.empty?
|
43
|
+
@find.params
|
44
|
+
else
|
45
|
+
@find.params.merge Utils::Params.stringify_keys(additions)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def fetch_results
|
52
|
+
@find.query self
|
53
|
+
end
|
54
|
+
|
55
|
+
# ClassMethods
|
56
|
+
module ClassMethods
|
57
|
+
attr_reader :config
|
58
|
+
|
59
|
+
def inherited(base)
|
60
|
+
base.instance_variable_set '@config', Utils::Extra.deep_copy(config)
|
61
|
+
end
|
62
|
+
|
63
|
+
def entity_type(&block)
|
64
|
+
config[:entity_type] = block
|
65
|
+
end
|
66
|
+
|
67
|
+
def filter_by(name, options = nil, &block)
|
68
|
+
options = { default: options } unless options.is_a?(Hash)
|
69
|
+
|
70
|
+
name = name.to_s
|
71
|
+
default = options[:default]
|
72
|
+
handler = options[:with] || block
|
73
|
+
|
74
|
+
config[:defaults][name] = default unless default.nil?
|
75
|
+
config[:actions][name] = normalize_find_handler(handler, name)
|
76
|
+
|
77
|
+
define_method(name) { @find.param name }
|
78
|
+
end
|
79
|
+
|
80
|
+
def results(*args)
|
81
|
+
new(*args).results
|
82
|
+
end
|
83
|
+
|
84
|
+
def normalize_find_handler(handler, key)
|
85
|
+
case handler
|
86
|
+
when Symbol then ->(entity_type, value) { method(handler).call entity_type, value }
|
87
|
+
when Proc then handler
|
88
|
+
else
|
89
|
+
lambda do |entity_type, value|
|
90
|
+
return if Utils::String.blank?(value)
|
91
|
+
Hashie::Extensions::DeepLocate.deep_locate ->(k, v, _object) { k == key.to_sym && v == value }, entity_type
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Error
|
4
|
+
class InvalidDefinedByValue < ArgumentError
|
5
|
+
def initialize(filter_by, defined_bys, value)
|
6
|
+
super "Invalid value '#{value}' used for defined_by #{filter_by} (expected one of #{defined_bys.join(', ')})"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Error
|
4
|
+
class InvalidNumber < ArgumentError
|
5
|
+
attr_reader :field, :number
|
6
|
+
|
7
|
+
def initialize(field, number)
|
8
|
+
@field = field
|
9
|
+
@number = number
|
10
|
+
|
11
|
+
super "#{field} should be more than 0. Currently '#{number}' is given."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Error
|
4
|
+
class MissingEntityType < ArgumentError
|
5
|
+
def initialize(message = 'No Entity Type provided. Entity Type can be defined on a class level or passed as an option.')
|
6
|
+
super message
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Features
|
4
|
+
module Paging
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :page, :per_page
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@page = [options[:page].to_i, 0].max
|
13
|
+
@per_page = self.class.calculate_per_page options[:per_page]
|
14
|
+
|
15
|
+
super options
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def fetch_results
|
21
|
+
apply_paging super
|
22
|
+
end
|
23
|
+
|
24
|
+
# Adapters will overwite this method
|
25
|
+
def apply_paging(entity_type)
|
26
|
+
entity_type.drop(([page, 1].max - 1) * per_page).first(per_page)
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def per_page(number)
|
31
|
+
raise Error::InvalidNumber.new('Per page', number) unless number > 0
|
32
|
+
config[:per_page] = number
|
33
|
+
end
|
34
|
+
|
35
|
+
def min_per_page(number)
|
36
|
+
raise Error::InvalidNumber.new('Min per page', number) unless number > 0
|
37
|
+
config[:min_per_page] = number
|
38
|
+
end
|
39
|
+
|
40
|
+
def max_per_page(number)
|
41
|
+
raise Error::InvalidNumber.new('Max per page', number) unless number > 0
|
42
|
+
config[:max_per_page] = number
|
43
|
+
end
|
44
|
+
|
45
|
+
def calculate_per_page(given)
|
46
|
+
per_page = (given || config[:per_page] || 25).to_i.abs
|
47
|
+
per_page = [per_page, config[:max_per_page]].min if config[:max_per_page]
|
48
|
+
per_page = [per_page, config[:min_per_page]].max if config[:min_per_page]
|
49
|
+
per_page
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Finder
|
3
|
+
module Features
|
4
|
+
module Sorting
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
base.instance_eval do
|
8
|
+
filter_by :sort do |entity_type, _|
|
9
|
+
sort_it(entity_type, sort_attribute, sort_direction)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Adapters will overwite this method
|
15
|
+
def sort_it(entity_type, sort_attribute, sort_direction)
|
16
|
+
case sort_direction
|
17
|
+
when 'asc', 'ascending'
|
18
|
+
entity_type.sort_by { |a| a[sort_attribute.to_sym] }
|
19
|
+
when 'desc', 'descending'
|
20
|
+
entity_type.sort_by { |a| a[sort_attribute.to_sym] }.reverse
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def sort?(attribute)
|
25
|
+
attribute == sort || sort.to_s.start_with?("#{attribute} ")
|
26
|
+
end
|
27
|
+
|
28
|
+
def sort_attribute
|
29
|
+
@sort_attribute ||= Utils::Extra.ensure_included sort.to_s.split(' ', 2).first, self.class.sort_attributes
|
30
|
+
end
|
31
|
+
|
32
|
+
def sort_direction
|
33
|
+
@sort_direction ||= Utils::Extra.ensure_included sort.to_s.split(' ', 2).last, %w[desc asc]
|
34
|
+
end
|
35
|
+
|
36
|
+
def sort_direction_for(attribute)
|
37
|
+
if sort_attribute == attribute.to_s
|
38
|
+
reverted_sort_direction
|
39
|
+
else
|
40
|
+
'desc'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def sort_params_for(attribute, options = {})
|
45
|
+
options['sort'] = "#{attribute} #{sort_direction_for(attribute)}"
|
46
|
+
params options
|
47
|
+
end
|
48
|
+
|
49
|
+
def reverted_sort_direction
|
50
|
+
sort_direction == 'desc' ? 'asc' : 'desc'
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
def sortable_by(*attributes)
|
55
|
+
config[:sort_attributes] = attributes.map(&:to_s)
|
56
|
+
config[:defaults]['sort'] = "#{config[:sort_attributes].first} desc"
|
57
|
+
end
|
58
|
+
|
59
|
+
def sort_attributes
|
60
|
+
config[:sort_attributes] ||= []
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'trailblazer/finder/features/paging'
|
2
|
+
require 'trailblazer/finder/features/sorting'
|
3
|
+
|
4
|
+
module Trailblazer
|
5
|
+
class Finder
|
6
|
+
# Features
|
7
|
+
module Features
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
# ClassMethods
|
13
|
+
module ClassMethods
|
14
|
+
def features(*mods)
|
15
|
+
mods.each do |mod|
|
16
|
+
include mod
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|