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.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rubocop.yml +45 -0
  4. data/.rubocop_todo.yml +52 -0
  5. data/.travis.yml +15 -0
  6. data/Gemfile +12 -0
  7. data/LICENSE.txt +23 -0
  8. data/README.md +494 -0
  9. data/Rakefile +29 -0
  10. data/lib/trailblazer/finder/adapters/active_record/paging.rb +20 -0
  11. data/lib/trailblazer/finder/adapters/active_record/sorting.rb +20 -0
  12. data/lib/trailblazer/finder/adapters/active_record.rb +30 -0
  13. data/lib/trailblazer/finder/adapters/data_mapper/paging.rb +20 -0
  14. data/lib/trailblazer/finder/adapters/data_mapper/sorting.rb +25 -0
  15. data/lib/trailblazer/finder/adapters/data_mapper.rb +30 -0
  16. data/lib/trailblazer/finder/adapters/friendly_id.rb +33 -0
  17. data/lib/trailblazer/finder/adapters/kaminari.rb +18 -0
  18. data/lib/trailblazer/finder/adapters/sequel/paging.rb +20 -0
  19. data/lib/trailblazer/finder/adapters/sequel/sorting.rb +25 -0
  20. data/lib/trailblazer/finder/adapters/sequel.rb +30 -0
  21. data/lib/trailblazer/finder/adapters/will_paginate.rb +18 -0
  22. data/lib/trailblazer/finder/adapters.rb +26 -0
  23. data/lib/trailblazer/finder/base.rb +98 -0
  24. data/lib/trailblazer/finder/errors/block_ignored.rb +11 -0
  25. data/lib/trailblazer/finder/errors/invalid_defined_by_value.rb +11 -0
  26. data/lib/trailblazer/finder/errors/invalid_number.rb +16 -0
  27. data/lib/trailblazer/finder/errors/missing_entity_type.rb +11 -0
  28. data/lib/trailblazer/finder/errors/with_ignored.rb +11 -0
  29. data/lib/trailblazer/finder/features/paging.rb +55 -0
  30. data/lib/trailblazer/finder/features/sorting.rb +66 -0
  31. data/lib/trailblazer/finder/features.rb +22 -0
  32. data/lib/trailblazer/finder/filter.rb +66 -0
  33. data/lib/trailblazer/finder/find.rb +29 -0
  34. data/lib/trailblazer/finder/utils/extra.rb +31 -0
  35. data/lib/trailblazer/finder/utils/params.rb +28 -0
  36. data/lib/trailblazer/finder/utils/parse.rb +25 -0
  37. data/lib/trailblazer/finder/utils/string.rb +35 -0
  38. data/lib/trailblazer/finder/version.rb +5 -0
  39. data/lib/trailblazer/finder.rb +29 -0
  40. data/lib/trailblazer/operation/finder.rb +61 -0
  41. data/spec/spec_helper.rb +15 -0
  42. data/spec/spec_helper_active_record.rb +50 -0
  43. data/spec/spec_helper_data_mapper.rb +35 -0
  44. data/spec/spec_helper_sequel.rb +32 -0
  45. data/spec/support/paging_shared_example.rb +65 -0
  46. data/spec/support/sorting_shared_example.rb +95 -0
  47. data/spec/trailblazer/finder/adapters/active_record/base_spec.rb +112 -0
  48. data/spec/trailblazer/finder/adapters/active_record/paging_spec.rb +64 -0
  49. data/spec/trailblazer/finder/adapters/active_record/sorting_spec.rb +82 -0
  50. data/spec/trailblazer/finder/adapters/data_mapper/base_spec.rb +112 -0
  51. data/spec/trailblazer/finder/adapters/data_mapper/paging_spec.rb +64 -0
  52. data/spec/trailblazer/finder/adapters/data_mapper/sorting_spec.rb +85 -0
  53. data/spec/trailblazer/finder/adapters/friendly_id_spec.rb +46 -0
  54. data/spec/trailblazer/finder/adapters/kaminari_spec.rb +64 -0
  55. data/spec/trailblazer/finder/adapters/sequel/base_spec.rb +112 -0
  56. data/spec/trailblazer/finder/adapters/sequel/paging_spec.rb +64 -0
  57. data/spec/trailblazer/finder/adapters/sequel/sorting_spec.rb +82 -0
  58. data/spec/trailblazer/finder/adapters/will_paginate_spec.rb +71 -0
  59. data/spec/trailblazer/finder/adapters_spec.rb +110 -0
  60. data/spec/trailblazer/finder/base_spec.rb +329 -0
  61. data/spec/trailblazer/finder/features/paging_spec.rb +104 -0
  62. data/spec/trailblazer/finder/features/sorting_spec.rb +100 -0
  63. data/spec/trailblazer/finder/features_spec.rb +55 -0
  64. data/spec/trailblazer/finder/filter_spec.rb +133 -0
  65. data/spec/trailblazer/finder/find_spec.rb +72 -0
  66. data/spec/trailblazer/finder/utils/extra_spec.rb +41 -0
  67. data/spec/trailblazer/finder/utils/params_spec.rb +39 -0
  68. data/spec/trailblazer/finder/utils/parse_spec.rb +33 -0
  69. data/spec/trailblazer/finder/utils/string_spec.rb +25 -0
  70. data/spec/trailblazer/operation/finder_spec.rb +103 -0
  71. data/spec/trailblazer/operation/paging_spec.rb +68 -0
  72. data/spec/trailblazer/operation/sorting_spec.rb +80 -0
  73. data/trailblazer-finder.gemspec +41 -0
  74. metadata +402 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bfa32409fa9cd5e7f8573fac437141c7a53ada65
4
+ data.tar.gz: a08dd296e2660da08cfb5785fd17c215d9aec2e0
5
+ SHA512:
6
+ metadata.gz: 402be8f3aa3855ecd06513c19de2cabb6ccecf7f0cd138d261cc4660adcdc9e2af06a37c597ca425a6038a9885a3335be85df53114caeb64c5944e8e1a2e24b7
7
+ data.tar.gz: 2531ad3dcce5f87ec7668532d8754f91a6f318cd1b3a6bbc65ad421d2366ea4a70515c4bb7b1d4fc948eb22d01daa8c91a4ff12a3bd36890505c9c786a158da0
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ test-reports
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ bitbucket-pipelines.yml
data/.rubocop.yml ADDED
@@ -0,0 +1,45 @@
1
+ require: rubocop-rspec
2
+
3
+ inherit_from: .rubocop_todo.yml
4
+
5
+ AllCops:
6
+ Exclude:
7
+ - Rakefile
8
+ - coverage
9
+ - test-reports
10
+ - bitbucket-pipelines.yml
11
+ - trailblazer-finder.gemspec
12
+ - spec/trailblazer/finder/base_spec.rb
13
+ - lib/trailblazer/operation/finder.rb
14
+
15
+ # Disables "Line is too long"
16
+ LineLength:
17
+ Enabled: false
18
+
19
+ # Disables Module has too many lines
20
+ ModuleLength:
21
+ Enabled: false
22
+
23
+ # Disables "Missing top-level class documentation comment"
24
+ Documentation:
25
+ Enabled: false
26
+
27
+ # Disables "Use each_with_object instead of inject"
28
+ Style/EachWithObject:
29
+ Enabled: false
30
+
31
+ # Disables "Prefer reduce over inject."
32
+ Style/CollectionMethods:
33
+ Enabled: false
34
+
35
+ # Disables "Block has too many lines."
36
+ Metrics/BlockLength:
37
+ Enabled: false
38
+
39
+ # Disables "Example has too many lines."
40
+ RSpec/ExampleLength:
41
+ Enabled: false
42
+
43
+ # Disables "Too many expectations."
44
+ RSpec/MultipleExpectations:
45
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,52 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2018-03-05 19:59:14 +0100 using RuboCop version 0.53.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Cop supports --auto-correct.
11
+ # Configuration parameters: Include, TreatCommentsAsGroupSeparators.
12
+ # Include: **/*.gemfile, **/Gemfile, **/gems.rb
13
+ Bundler/OrderedGems:
14
+ Exclude:
15
+ - 'Gemfile'
16
+
17
+ # Offense count: 1
18
+ # Cop supports --auto-correct.
19
+ Layout/EmptyLines:
20
+ Exclude:
21
+ - 'spec/trailblazer/operation/finder_spec.rb'
22
+
23
+ # Offense count: 3
24
+ RSpec/DescribeClass:
25
+ Exclude:
26
+ - 'spec/trailblazer/operation/finder_spec.rb'
27
+ - 'spec/trailblazer/operation/paging_spec.rb'
28
+ - 'spec/trailblazer/operation/sorting_spec.rb'
29
+
30
+ # Offense count: 14
31
+ # Cop supports --auto-correct.
32
+ # Configuration parameters: AutoCorrect, EnforcedStyle.
33
+ # SupportedStyles: nested, compact
34
+ Style/ClassAndModuleChildren:
35
+ Exclude:
36
+ - 'spec/trailblazer/operation/finder_spec.rb'
37
+ - 'spec/trailblazer/operation/paging_spec.rb'
38
+ - 'spec/trailblazer/operation/sorting_spec.rb'
39
+
40
+ # Offense count: 10
41
+ # Cop supports --auto-correct.
42
+ # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
43
+ # SupportedStyles: single_quotes, double_quotes
44
+ Style/StringLiterals:
45
+ Exclude:
46
+ - 'Gemfile'
47
+
48
+ # Offense count: 89
49
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
50
+ # URISchemes: http, https
51
+ Metrics/LineLength:
52
+ Max: 128
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem install bundler
4
+ matrix:
5
+ include:
6
+ - rvm: 2.1
7
+ gemfile: Gemfile
8
+ - rvm: 2.2
9
+ gemfile: Gemfile
10
+ - rvm: 2.3.1
11
+ gemfile: Gemfile
12
+ - rvm: 2.4.1
13
+ gemfile: Gemfile
14
+ - rvm: 2.5.0
15
+ gemfile: Gemfile
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'coveralls', require: false
6
+
7
+ # Had to add this for a bit, since none of the latest changes have been pushed to gems yet
8
+ gem "trailblazer", github: "trailblazer/trailblazer"
9
+ gem "trailblazer-operation", github: "trailblazer/trailblazer-operation"
10
+ gem "trailblazer-activity", github: "trailblazer/trailblazer-activity"
11
+ gem "trailblazer-macro", github: "trailblazer/trailblazer-macro"
12
+ gem "trailblazer-macro-contract", github: "trailblazer/trailblazer-macro-contract"
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2018 Trailblazer GmbH and contributors, released under the MIT
2
+ license.
3
+
4
+ MIT License
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,494 @@
1
+
2
+ # Trailblazer Finder
3
+
4
+ Description should come here
5
+
6
+ [![Gitter Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://gitter.im/trailblazer/chat) [![Build Status](https://secure.travis-ci.org/trailblazer/trailblazer-finder.svg)](https://travis-ci.org/trailblazer/trailblazer-finder) [![Coverage Status](https://coveralls.io/repos/github/trailblazer/trailblazer-finder/badge.svg?branch=master)](https://coveralls.io/github/trailblazer/trailblazer-finder?branch=master)
7
+
8
+ ## Table of Contents
9
+
10
+ * [Installation](#installation)
11
+ * [Dependencies](#dependencies)
12
+ * [Usage](#usage)
13
+ * [Finder](#finder)
14
+ * [Finder Example](#finder-example)
15
+ * [Operation](#operation)
16
+ * [Operation Example](#operation-example)
17
+ * [Usable without Trailblazer](#usable-without-trailblazer)
18
+ * [Example without Trailblazer](#example-without-trailblazer)
19
+ * [Example Project](#example-project)
20
+ * [Features](#features)
21
+ * [Paging](#paging)
22
+ * [Paging Example](#paging-example)
23
+ * [Sorting](#sorting)
24
+ * [Sorting Example](#sorting-example)
25
+ * [Adapters](#adapters)
26
+ * [Adapters Example](#adapters-example)
27
+ * [ActiveRecord](#active_record)
28
+ * [Active Record Example](#active-record-example)
29
+ * [DataMapper](#data_mapper)
30
+ * [Data Mapper Example](#data-mapper-example)
31
+ * [Sequel](#sequel)
32
+ * [Sequel Example](#sequel-example)
33
+ * [Kaminari](#kaminari)
34
+ * [Kaminari Example](#kaminari-example)
35
+ * [WillPaginate](#will_paginate)
36
+ * [Will Paginate Example](#will-paginate-example)
37
+ * [FriendlyId](#friendly_id)
38
+ * [Friendly Id Example](#friendly-id-example)
39
+ * [Tips & Tricks](#tips--tricks)
40
+ * [ORM's are not required](#results-shortcut)
41
+ * [Passing Entity Type as Argument](#passing-entity_type-as-argument)
42
+ * [Contributing](#contributing)
43
+ * [License](#license)
44
+
45
+ ## Installation
46
+
47
+ Add this line to your application's Gemfile:
48
+
49
+ ```ruby
50
+ gem 'trailblazer-finder'
51
+ ```
52
+
53
+ And then execute:
54
+
55
+ $ bundle
56
+
57
+ Or install it yourself as:
58
+
59
+ $ gem install trailblazer-finder
60
+
61
+ ###
62
+ * [Hashie](https://github.com/intridea/hashie) - used to search deep within hashes/arrays
63
+ * [Trailblazer](https://github.com/trailblazer/trailblazer) - [actually optional, but requires 2.1+](https://github.com/trailblazer/trailblazer-finder#usable-without-trailblazer)
64
+
65
+ ## Usage
66
+
67
+ ### Finder
68
+ Just inherit from the ```Trailblazer::Finder``` class.
69
+
70
+ Features and adapters are optional. However.
71
+
72
+ NOTE: features only work if they're specified on top of your Finder class
73
+
74
+ An Entity Type is required, but can:
75
+ * be defined in the inherited Finder class
76
+ * be given as an option in the call
77
+
78
+ Basically for most use cases, Entity Type is the entity/model/array of hashes you wish to use finder on
79
+
80
+ #### Finder Example
81
+
82
+ ```ruby
83
+ class Post::Finder < Trailblazer::Finder
84
+ # Optional features
85
+ features Paging, Sorting
86
+
87
+ # Optional if you use it as option in the caller, Model/Entity or Array with Hashes
88
+ entity_type { Post }
89
+
90
+ # Without defining an ORM everything defaults to dealing with an
91
+ # array with Hashes
92
+ adapters ActiveRecord, Kaminari
93
+
94
+ # Pagination settings (remove if not using the Paging feature)
95
+ per_page 25
96
+ min_per_page 10
97
+ max_per_page 100
98
+
99
+ # Sortable attributes (remove if not using the Sorting feature)
100
+ sortable_by :id, :title, :created_at
101
+
102
+ # Runs filter_by to filter results
103
+ #
104
+ # Params:
105
+ # * +attribute+:: The attribute to be filtered on
106
+ #
107
+ # * Following is optional, matches exact value if not specified
108
+ # * +with:+:: Filter method defined in Finder class
109
+ # * +defined_by:+:: Array of filter Methods defined in Finder class
110
+ # method name: apply_filter_name_with_array_value
111
+ # * block:: A block can be given with the code to filter with
112
+ filter_by :id
113
+ filter_by :body, with: :apply_body_filter
114
+ filter_by :created_after, with: :apply_created_after
115
+ filter_by :created_before, with: :apply_created_before
116
+ filter_by :is_hot, defined_by: %i[true false]
117
+ filter_by(:title) { |entity_type, value| entity_type.where title: value }
118
+ filter_by(:published, false) do |entity_type, value|
119
+ value ? entity_type.where('published = false') : entity_type.where('published = true')
120
+ end
121
+
122
+ private
123
+
124
+ def apply_body_filter(entity_type, value)
125
+ return unless value.present?
126
+ entity_type.where 'lower(body) LIKE ?', Utils::Parse.term(value.downcase)
127
+ end
128
+
129
+ def apply_created_after(entity_type, value)
130
+ entity_type.where('DATE(created_at) >= ?', value) if value.present?
131
+ end
132
+
133
+ def apply_created_before(entity_type, value)
134
+ entity_type.where('DATE(created_at) <= ?', value) if value.present?
135
+ end
136
+
137
+ def apply_is_hot_with_true(entity_type)
138
+ entity_type.where 'views > 100'
139
+ end
140
+
141
+ def apply_is_hot_with_false(entity_type)
142
+ entity_type.where 'views < 100'
143
+ end
144
+ end
145
+ ```
146
+
147
+ ### Operation
148
+ The only tie this has to [Trailblazer 2.1](https://github.com/trailblazer/trailblazer), is this part to be honest, which doesn't get loaded in case [Trailblazer](https://github.com/trailblazer/trailblazer) isn't loaded in your environment. The idea was actually to create it specifically for use with [Trailblazer 2.1](https://github.com/trailblazer/trailblazer), hence the name Trailblazer-Finder.
149
+
150
+ #### Operation Example using Finder Macro
151
+ ```ruby
152
+ class Post::Index < Trailblazer::Operation
153
+ # Runs a Trailblazer Task with a Finder object
154
+ # Params:
155
+ # +finder_class+:: Finder class to be used
156
+ # +action+:: :all, :single (optional, defaults to :all)
157
+ # +entity_type+:: Entity/Model or array (optional if specified in Finder Class, overwrites Finder class entity_type)
158
+ step Finder(Post::Finder, :all, Post)
159
+ end
160
+ ```
161
+
162
+ #### Operation Example using custom step
163
+ ```ruby
164
+ class Post::Index < Trailblazer::Operation
165
+ step :finder!
166
+
167
+ # Find all matching results and extend model object with required methods
168
+ def finder!(options, params:, **)
169
+ options[:finder] = Post::Finder.new(filter: params['f'], page: params['page'], per_page: params['per_page'])
170
+ end
171
+
172
+ # Find first matching row, no method extension on model object
173
+ def single_finder!(options, params:, **)
174
+
175
+ # Since ID's are usually given directly, patch it into filter
176
+ apply_id(params)
177
+
178
+ # No paging, sorting, no methods and only returns the first matched result
179
+ options[:finder] = Post::Finder.new(filter: params['f']).results.first
180
+ end
181
+
182
+ # Since ID's are usually given directly, patch it into filter
183
+ def apply_id(params)
184
+ return if params[:id].nil?
185
+ params[:f] = {} unless params.key?('f')
186
+ params[:f][:id] = params[:id] unless params[:f].key?('id')
187
+ end
188
+ end
189
+ ```
190
+
191
+ When using this, result[:finder] will be extended with (not available for :single row)
192
+ ```ruby
193
+ # accessing filters
194
+ .name # => name filter
195
+ .created_at # => created at filter
196
+
197
+ # accessing results
198
+ .count # => number of found results
199
+ .results? # => are there any results found
200
+ .results # => fetched results
201
+
202
+ # params for url generations
203
+ .params # => filter values
204
+ .params active: false # => overwrites the 'active' filter
205
+ ```
206
+
207
+
208
+ ### Usable without Trailblazer
209
+ It's not really tied to [Trailblazer](https://github.com/trailblazer/trailblazer), you can actually use it anywhere by calling the below example directly.
210
+ If Trailblazer isn't loaded, the ties to [Trailblazer](https://github.com/trailblazer/trailblazer) won't be executed.
211
+
212
+ #### Example without Trailblazer
213
+ ```ruby
214
+ result = Post::Finder.new(filter: params[:f], page: params[:page], per_page: params[:per_page])
215
+ ```
216
+
217
+ When using this, result will be extended with (not available for :single row)
218
+ ```ruby
219
+ # accessing filters
220
+ .name # => name filter
221
+ .created_at # => created at filter
222
+
223
+ # accessing results
224
+ .count # => number of found results
225
+ .results? # => are there any results found
226
+ .results # => fetched results
227
+
228
+ # params for url generations
229
+ .params # => filter values
230
+ .params active: false # => overwrites the 'active' filter
231
+ ```
232
+
233
+ ### Example Project
234
+ Coming soon!
235
+
236
+ ## Features
237
+ Aside of the default filtering behaviour, it offers the following optional features as well.
238
+
239
+ NOTE: FEATURES NEED TO BE SPECIFIED ON TOP OF YOUR CLASS
240
+
241
+ ### Paging
242
+
243
+ Really simple pagination plugin, which uses the plain ```.limit``` and ```.offset``` methods.
244
+
245
+ #### Paging Example
246
+ ```ruby
247
+ class Post::Finder < Trailblazer::Finder
248
+ features Paging
249
+
250
+ filter_by :name
251
+ filter_by :category_name
252
+
253
+ # per page defaults to 25 (so not required)
254
+ per_page 10
255
+
256
+ # Minimum items per page (not required)
257
+ min_per_page 5
258
+
259
+ # Maximum items per page (not required)
260
+ max_per_page 100
261
+ end
262
+ ```
263
+
264
+ This feature extends the result[:finder] object with the following methods
265
+ ```ruby
266
+ .page # => page number
267
+ .per_page # => per page (10)
268
+ .results # => paginated page results
269
+ ```
270
+
271
+ ### Sorting Plugin
272
+
273
+ Fixing the pain of dealing with sorting attributes and directions.
274
+
275
+
276
+ #### Sorting Example
277
+ ```ruby
278
+ class Post::Finder < Trailblazer::Finder
279
+ features Sorting
280
+
281
+ sortable_by :name, :body
282
+ end
283
+ ```
284
+
285
+ This feature extends the result[:finder] object with the following methods
286
+ ```ruby
287
+ .results # => Posts sorted by title DESC
288
+ .sort_attribute # => 'title'
289
+ .sort_direction # => 'desc'
290
+
291
+ # Smart sort checking
292
+ .sort?('title') # => true
293
+ .sort?('title desc') # => true
294
+ .sort?('title asc') # => false
295
+
296
+ # Helpers for dealing with reversing sort direction
297
+ .reverted_sort_direction # => 'asc'
298
+ .sort_direction_for('title') # => 'asc'
299
+ .sort_direction_for('body') # => 'desc'
300
+
301
+ # Params for sorting links
302
+ .sort_params_for('title')
303
+ ```
304
+
305
+ ## Adapters
306
+ By default, everything works with an array of hashes. You can change the default behaviour by using adapters. Adapters are not just tied to the ORM's, we also have a few adapters included that make it easier to work along with existing gems such as [Kaminari](https://github.com/kaminari/kaminari), [WillPaginate](https://github.com/mislav/will_paginate/) and [FriendlyId](https://github.com/norman/friendly_id).
307
+
308
+ Currently supported ORM's:
309
+ * [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord)
310
+ * [DataMapper](https://github.com/datamapper)
311
+ * [Sequel](https://github.com/jeremyevans/sequel)
312
+
313
+ You can specify the adapters you wish to use inside your enherited Finder class.
314
+
315
+ ### Adapters Example
316
+ ```ruby
317
+ class Post::Finder < Trailblazer::Finder
318
+ # Features, in case you use any, need to be specified before adapters
319
+ adapters ActiveRecord, Kaminari, FriendlyId
320
+ end
321
+ ```
322
+
323
+ ### ActiveRecord
324
+ The only thing the [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord) adapter does, is overwrite one specific method for Paging (limit/offset) and Sorting (order) each, as well as change the default filter behaviour (from select to where). These are overwritten by the adapter to match the specific use case of [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord).
325
+
326
+ #### Active Record Example
327
+ ```ruby
328
+ class Post::Finder < Trailblazer::Finder
329
+ # Features, in case you use any, need to be specified before adapters
330
+ adapters ActiveRecord
331
+ end
332
+ ```
333
+
334
+ ### DataMapper
335
+ The only thing the [DataMapper](https://github.com/datamapper) adapter does, is overwrite one specific method for Paging (limit/offset) and Sorting (order) each, as well as change the default filter behaviour (from select to where). These are overwritten by the adapter to match the specific use case of [DataMapper](https://github.com/datamapper).
336
+
337
+ #### Data Mapper Example
338
+ ```ruby
339
+ class Post::Finder < Trailblazer::Finder
340
+ # Features, in case you use any, need to be specified before adapters
341
+ adapters DataMapper
342
+ end
343
+ ```
344
+
345
+ ### Sequel
346
+ The only thing the [Sequel](https://github.com/jeremyevans/sequel) adapter does, is overwrite one specific method for Paging (limit/offset) and Sorting (order) each, as well as change the default filter behaviour (from select to where). These are overwritten by the adapter to match the specific use case of [Sequel](https://github.com/jeremyevans/sequel).
347
+
348
+ #### Sequel Example
349
+ ```ruby
350
+ class Post::Finder < Trailblazer::Finder
351
+ # Features, in case you use any, need to be specified before adapters
352
+ adapters Sequel
353
+ end
354
+ ```
355
+
356
+ ### Kaminari
357
+ The only thing the [Kaminari](https://github.com/kaminari/kaminari) adapter does, is overwrite one specific method for Paging (limit/offset). These are overwritten by the adapter to match the specific use case of [Kaminari](https://github.com/kaminari/kaminari).
358
+
359
+ **Note**
360
+ Not usable without the Paging feature enabled, and requires an ORM that's supported by [Kaminari](https://github.com/kaminari/kaminari), directly or by using additional gems.
361
+
362
+ #### Kaminari Example
363
+ ```ruby
364
+ class Post::Finder < Trailblazer::Finder
365
+ features Paging
366
+ adapters ActiveRecord, Kaminari
367
+ end
368
+ ```
369
+ **Note**
370
+ To use [Kaminari](https://github.com/kaminari/kaminari) in combination with [Cells](https://github.com/trailblazer/cells), you'll currently have to use [kaminari-cells](https://github.com/apotonick/kaminari-cells) and monkey-patch Kaminari by using in an initiliazer:
371
+ ```ruby
372
+ Kaminari::Helpers::Paginator.class_eval do
373
+ def render(&block)
374
+ instance_eval(&block) if @options[:total_pages] > 1
375
+ end
376
+ end
377
+ ```
378
+
379
+ ### WillPaginate
380
+ The only thing the [WillPaginate](https://github.com/mislav/will_paginate/) adapter does, is overwrite one specific method for Paging (limit/offset). These are overwritten by the adapter to match the specific use case of [WillPaginate](https://github.com/mislav/will_paginate/).
381
+
382
+ **Note**
383
+ Not usable without the Paging feature enabled, and requires an ORM that's supported by [WillPaginate](https://github.com/mislav/will_paginate/), directly or by using additional gems.
384
+
385
+ #### Will Paginate Example
386
+ ```ruby
387
+ class Post::Finder < Trailblazer::Finder
388
+ features Paging
389
+ adapters ActiveRecord, WillPaginate
390
+ end
391
+ ```
392
+
393
+ ### FriendlyId
394
+ The [FriendlyId](https://github.com/norman/friendly_id) adapter was written cause I personally use it a lot, as well as to show an example of what kind of adapters we could potentially write in the future to compliment the filters with.
395
+
396
+ Basically the [FriendlyId](https://github.com/norman/friendly_id) adapter adds the following filter, which automatically checks wether the id is an integer or slug and makes sure the right filter is applied on the row set:
397
+ ```ruby
398
+ filter_by :id, with: :apply_slug_filter
399
+ ```
400
+
401
+ **Note**
402
+ Currently only tested with ActiveRecord.
403
+
404
+ **Note**
405
+ Do not set a filter_by for id or slug when using this adapter.
406
+
407
+ #### Friendly Id Example
408
+ ```ruby
409
+ class Post::Finder < Trailblazer::Finder
410
+ # Features, in case you use any, need to be specified before adapters
411
+ adapters ActiveRecord, FriendlyId
412
+ end
413
+ ```
414
+
415
+ ## Tips & Tricks
416
+ ### ORM's are not required
417
+ Not even for the Paging and Sorting features, however using additional adapters such as Kaminari, WillPaginate and FriendlyId won't work well with this.
418
+
419
+ ```ruby
420
+ class Post::Finder < Trailblazer::Finder
421
+ # Features, in case you use any, need to be specified before adapters
422
+ entity_type { fetch_product_as_hashes }
423
+
424
+ filter_by(:name) { |entity_type, value| entity_type.select { |product| product[:name] == value } }
425
+ filter_by(:category) { |entity_type, value| entity_type.select { |product| product[:category] == value } }
426
+ end
427
+ ```
428
+
429
+ ### Overwriting Methods
430
+ You can have fine grained entity_type, by overwriting ```initialize``` method:
431
+
432
+ ```ruby
433
+ class Post::Finder < Trailblazer::Finder
434
+ # Features, in case you use any, need to be specified before adapters
435
+
436
+ filter_by :name
437
+ filter_by :category_name
438
+
439
+ def initialize(user, options = {})
440
+ super options.merge(entity_type: Product.visible_to(user))
441
+ end
442
+ end
443
+ ```
444
+
445
+ ### Utils / Helpers
446
+ Coming soon
447
+
448
+ ## Contributing
449
+ 1. Fork it
450
+ 2. Create your feature branch
451
+ 3. Commit your changes
452
+ 4. Push to the branch
453
+ 5. Run the tests (`rake`)
454
+ 6. Make sure all tests pass, rubocop has no offenses and coverage is 100%
455
+ 7. Create new Pull Request
456
+
457
+ ## Bugs
458
+ Please report them on the [Github issue tracker](http://github.com/trailblazer/trailblazer-finder) for this project.
459
+
460
+ If you have a bug to report, please include the following information:
461
+
462
+ * **Version information for Trailblazer-Finder, Trailblazer, used Adapters and Ruby.**
463
+ * Full stack trace and error message (if you have them).
464
+ * Any snippets of relevant model, view or controller code that shows how you are using Trailblazer-Finder.
465
+
466
+ If you are able to, it helps even more if you can fork Trailblazer-Finder on Github,
467
+ and add a test that reproduces the error you are experiencing.
468
+
469
+ For more info on how to report bugs, please see [this article](http://yourbugreportneedsmore.info/).
470
+
471
+ ## License
472
+ Copyright (c) 2018 Trailblazer GmbH and contributors, released under the MIT
473
+ license.
474
+
475
+ MIT License
476
+
477
+ Permission is hereby granted, free of charge, to any person obtaining
478
+ a copy of this software and associated documentation files (the
479
+ "Software"), to deal in the Software without restriction, including
480
+ without limitation the rights to use, copy, modify, merge, publish,
481
+ distribute, sublicense, and/or sell copies of the Software, and to
482
+ permit persons to whom the Software is furnished to do so, subject to
483
+ the following conditions:
484
+
485
+ The above copyright notice and this permission notice shall be
486
+ included in all copies or substantial portions of the Software.
487
+
488
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
489
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
490
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
491
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
492
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
493
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
494
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.