trailblazer-finder 0.70.0 → 0.80.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md DELETED
@@ -1,451 +0,0 @@
1
- # Trailblazer Finder
2
-
3
- Provides DSL for creating [Trailblazer](https://github.com/trailblazer/trailblazer) based Finder Objects. But it is designed to be used on its own as a separate gem. It was influenced by popular [Ransack](https://github.com/activerecord-hackery/ransack) gem, but in addition to [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord), it can be used with [DataMapper](https://github.com/datamapper) or [Sequel](https://github.com/jeremyevans/sequel). It also integrates with [Kaminari](https://github.com/kaminari/kaminari) or [Will Paginate](https://github.com/mislav/will_paginate), as well as [FriendlyId](https://github.com/norman/friendly_id)
4
-
5
- [![Gitter Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://gitter.im/trailblazer/chat)
6
- [![Build Status](https://secure.travis-ci.org/trailblazer/trailblazer-finder.svg)](https://travis-ci.org/trailblazer/trailblazer-finder)
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
- * [Predicates](#predicate)
22
- * [Predicates Example](#predicates-example)
23
- * [Paging](#paging)
24
- * [Paging Example](#paging-example)
25
- * [Sorting](#sorting)
26
- * [Sorting Example](#sorting-example)
27
- * [Adapters](#adapters)
28
- * [Adapters Example](#adapters-example)
29
- * [ActiveRecord](#active_record)
30
- * [Active Record Example](#active-record-example)
31
- * [Sequel](#sequel)
32
- * [Sequel Example](#sequel-example)
33
- * [Tips & Tricks](#tips--tricks)
34
- * [ORM's are not required](#results-shortcut)
35
- * [Passing Entity as Argument](#passing-entity-as-argument)
36
- * [Contributing](#contributing)
37
- * [License](LICENSE.txt)
38
-
39
- ## Installation
40
-
41
- Add this line to your application's Gemfile:
42
-
43
- ```ruby
44
- gem 'trailblazer-finder'
45
- ```
46
-
47
- And then execute:
48
-
49
- $ bundle
50
-
51
- Or install it yourself as:
52
-
53
- $ gem install trailblazer-finder
54
-
55
- ### Dependencies
56
- * [Trailblazer-Activity](https://github.com/trailblazer/trailblazer-activity) - required
57
- * [Trailblazer](https://github.com/trailblazer/trailblazer) - [actually optional, but requires 2.1+](https://github.com/trailblazer/trailblazer-finder#usable-without-trailblazer)
58
-
59
- ## Usage
60
-
61
- ### Finder
62
- Just inherit from the ```Trailblazer::Finder``` class.
63
-
64
- Features and adapters are optional. However.
65
-
66
- NOTE: features are only applied if their options are specified in your finder
67
-
68
- An Entity (model) is required, but can:
69
- * be defined in the inherited Finder class
70
- * be given as an option in the call
71
-
72
- Be sure to specify features you wish to use in your finder, before specifying adapters.
73
-
74
- Basically for most use cases, Entity is the entity/model/array of hashes you wish to use finder on
75
-
76
- #### Finder Example
77
-
78
- ```ruby
79
- class Post::Finder < Trailblazer::Finder
80
- # Without defining an ORM everything defaults to dealing with hash objects
81
- adapter :ActiveRecord
82
-
83
- # Optional if you use it as option in the caller, Model/Entity or Array with Hashes
84
- entity { Post }
85
-
86
- # Pagination settings (don't use it if you don't wish to use paging)
87
- paging per_page: 5, min_per_page: 1, max_per_page: 100
88
-
89
- # Property
90
- # Set the properties with their respective options
91
- #
92
- # Params:
93
- # * +name+:: The property name (attribute /column / field - name) (required)
94
- # * +type:+:: Dry Types type, for future validations (required)
95
- #
96
- # * Following is optional (don't use it if you do not wish to use sorting)
97
- # * +sortable:+:: Can this property be sorted on? (default: false)
98
- # * +sort_direction:+:: Specify default sort direction (only usable if sortable is true)
99
- # (default: :desc)
100
- property :id, type: Types::Integer
101
- property :body, type: Types::String, sortable: true
102
- property :title, type: Types::String, sortable: true, sort_direction: :asc
103
-
104
- # Filter By
105
- # A simple way to make custom filter methods
106
- #
107
- # Params:
108
- # * +parameter+:: The parameter to use to call this filter (required)
109
- #
110
- # * Following is optional, matches exact value if not specified
111
- # * +with:+:: Filter method defined in Finder class
112
- # * block:: A block can be given with the code to filter with
113
- filter_by :created_after, with: :apply_created_after
114
- filter_by(:created_before) { |entity, _attribute, value| entity.where('DATE(created_at) <= ?', value) if value.present? }
115
-
116
- private
117
-
118
- def apply_created_after(entity, _attribute, value)
119
- entity.where('DATE(created_at) >= ?', value) if value.present?
120
- end
121
- end
122
- ```
123
-
124
- ### Operation
125
- 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.
126
-
127
- #### Operation Example using Finder Macro
128
- ```ruby
129
- class Post::Index < Trailblazer::Operation
130
- # Runs a Trailblazer Task with a Finder object
131
- # Params:
132
- # +finder_class+:: Finder class to be used
133
- # +action+:: :all, :single (optional, defaults to :all)
134
- # +entity+:: Entity/Model or array (optional if specified in Finder Class, overwrites Finder class entity)
135
- step Finder(Post::Finder, :all, Post)
136
- end
137
- ```
138
-
139
- #### Operation Example using custom step
140
- ```ruby
141
- class Post::Index < Trailblazer::Operation
142
- step :finder!
143
-
144
- # Find all matching results and extend model object with required methods
145
- def finder!(options, params:, **)
146
- options[:finder] = Post::Finder.new(params: params)
147
- end
148
-
149
- # Find first matching row, no method extension on model object
150
- def single_finder!(options, params:, **)
151
-
152
- # Since ID's are usually given directly, patch it into filter
153
- apply_id(params)
154
-
155
- # No paging, sorting, no methods and only returns the first matched result
156
- options[:finder] = Post::Finder.new(params: params).result.first
157
- end
158
-
159
- # Since ID's are usually given directly, patch it into filter
160
- def apply_id(params)
161
- return if params[:id].nil?
162
- params[:id_eq] = params[:id] unless params.key?("id")
163
- end
164
- end
165
- ```
166
-
167
- When using this, result[:finder] will be extended with (not available for :single row)
168
- ```ruby
169
- # accessing results
170
- .count # => number of found results
171
- .result? # => are there any results found
172
- .result # => fetched results
173
-
174
- # params for url generations
175
- .params # => filter values
176
- ```
177
-
178
-
179
- ### Usable without Trailblazer
180
- It's not really tied to [Trailblazer](https://github.com/trailblazer/trailblazer), you can actually use it anywhere by calling the below example directly.
181
- If Trailblazer isn't loaded, the ties to [Trailblazer](https://github.com/trailblazer/trailblazer) won't be executed.
182
-
183
- #### Example without Trailblazer
184
- ```ruby
185
- # If entity is specified in your finder
186
- result = Post::Finder.new(params: params)
187
-
188
- # If entity ism't specified in your finder
189
- result = Post::Finder.new(entity: Post, params: params)
190
- ```
191
-
192
- When using this, result will be extended with (not available for :single row)
193
- ```ruby
194
- # accessing results
195
- .count # => number of found results
196
- .result? # => are there any results found
197
- .result # => fetched results
198
-
199
- # params for url generations
200
- .params # => filter values
201
- ```
202
-
203
- ### Example Project
204
- Coming soon!
205
-
206
- ## Features
207
- Aside of the default filtering behavior, it offers the following optional features as well.
208
-
209
- NOTE: FEATURES NEED TO BE SPECIFIED ON TOP OF YOUR CLASS
210
-
211
- ### Predicates
212
- Simple predicate feature, that enables you to have default predicate filters available for the specified fields.
213
-
214
- At the moment we support:
215
- - eq: equals to
216
- - not_eq: not equals to
217
- - blank: blank (empty/nil/nul)
218
- - not_blank: not blank (empty/nil/null)
219
- - lt: less than (value converts to float)
220
- - lte: less than or equal to (value converts to float)
221
- - gt: greater than (value converts to float)
222
- - gte: greater than or equal to (value converts to float)
223
- - cont: contains specified value
224
- - not_cont: does not contain specified value
225
- - sw: starts with specified value
226
- - not_sw: does not start with specified value
227
- - ew: end with specified value
228
- - not_ew: does not end with specified value
229
-
230
- #### Predicates Example
231
- ```ruby
232
- class Post::Finder < Trailblazer::Finder
233
- entity { Post }
234
-
235
- property :id, type: Types::Integer
236
- property :body, type: Types::String
237
- property :title, type: Types::String
238
- end
239
- ```
240
-
241
- This feature extends the result[:finder] object with the following methods
242
- ```ruby
243
- # Available Predicate filters with the above example
244
- id_eq # => id equals value
245
- id_not_eq # => id not equals value
246
- id_blank # => id blank value
247
- id_not_blank # => id not blank value
248
- id_lt # => id less than value (converts value to float)
249
- id_lte # => id less than or equal to value (converts value to float)
250
- id_gt # => id greater than value (converts value to float)
251
- id_gte # => id greater than or equal to value (converts value to float)
252
- id_cont # => id contains value
253
- id_not_cont # => id does not contain value
254
- id_sw # => id starts with
255
- id_not_sw # => id does not start with
256
- id_ew # => id ends with
257
- id_not_ew # => id does not end with
258
-
259
- body_eq # => body equals value
260
- body_not_eq # => body not equals value
261
- body_blank # => body blank value
262
- body_not_blank # => body not blank value
263
- body_lt # => body less than value (converts value to float)
264
- body_lte # => body less than or equal to value (converts value to float)
265
- body_gt # => body greater than value (converts value to float)
266
- body_gte # => body greater than or equal to value (converts value to float)
267
- body_cont # => body contains value
268
- body_not_cont # => body does not contain value
269
- body_sw # => body starts with
270
- body_not_sw # => body does not start with
271
- body_ew # => body ends with
272
- body_not_ew # => body does not end with
273
-
274
- title_eq # => title equals value
275
- title_not_eq # => title not equals value
276
- title_blank # => title blank value
277
- title_not_blank # => title not blank value
278
- title_lt # => title less than value (converts value to float)
279
- title_lte # => title less than or equal to value (converts value to float)
280
- title_gt # => title greater than value (converts value to float)
281
- title_gte # => title greater than or equal to value (converts value to float)
282
- title_cont # => title contains value
283
- title_not_cont # => title does not contain value
284
- title_sw # => title starts with
285
- title_not_sw # => title does not start with
286
- title_ew # => title ends with
287
- title_not_ew # => title does not end with
288
- ```
289
-
290
- ### Paging
291
- Really simple pagination feature, which uses the plain ```.limit``` and ```.offset``` methods.
292
-
293
- #### Paging Example
294
- ```ruby
295
- class Post::Finder < Trailblazer::Finder
296
- entity { Post }
297
-
298
- paging per_page: 5, min_per_page: 1, max_per_page: 100
299
- end
300
- ```
301
-
302
- ### Sorting
303
- Really simple sorting feature, fixing the pain of dealing with sorting attributes and directions. Can sort by multiple columns/directions.
304
-
305
- #### Sorting Example
306
- ```ruby
307
- class Post::Finder < Trailblazer::Finder
308
- entity { Post }
309
-
310
- # Property
311
- # Set the properties with their respective options
312
- #
313
- # Params:
314
- # * +name+:: The property name (attribute /column / field - name) (required)
315
- # * +type:+:: Dry Types type, for future validations (required)
316
- #
317
- # * Following is optional (don't use it if you do not wish to use sorting)
318
- # * +sortable:+:: Can this property be sorted on? (default: false)
319
- # * +sort_direction:+:: Specify default sort direction (only usable if sortable is true)
320
- # (default: :desc)
321
- property :id, type: Types::Integer
322
- property :body, type: Types::String, sortable: true
323
- property :title, type: Types::String, sortable: true, sort_direction: :asc
324
- end
325
- ```
326
-
327
- This feature extends the result[:finder] object with the following methods
328
- ```ruby
329
- .result # => Posts sorted by title DESC
330
-
331
- # Smart sort checking
332
- .sort?('title') # => true
333
-
334
- # Helpers for seeing current sort direction
335
- .sort_direction_for('title') # => 'asc'
336
- .sort_direction_for('body') # => 'desc'
337
-
338
- # Helpers for seeing reversing sort direction
339
- .reverse_sort_direction_for('title') # => 'desc'
340
- .reverse_sort_direction_for('body') # => 'asc'
341
-
342
- # Params for sorting links (new if none exists, existing params if exists)
343
- .sort_params_for('title')
344
-
345
- # Add Params for sorting links (add to existing / replace with different direction if exists)
346
- .add_sort_params_for('title')
347
-
348
- # Remove Params for sorting links (remove from existing)
349
- .remove_sort_params_for('title')
350
-
351
- # New Params for sorting links (reset)
352
- .new_sort_params_for('title')
353
- ```
354
-
355
- ## Adapters
356
- By default, everything works with an array of hashes. You can change the default behaviour by using adapters.
357
-
358
- Currently supported ORM's:
359
- * [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord)
360
- * [Sequel](https://github.com/jeremyevans/sequel)
361
-
362
- You can specify the adapters you wish to use inside your enherited Finder class.
363
-
364
- ### Adapters Example
365
- ```ruby
366
- class Post::Finder < Trailblazer::Finder
367
- adapter "ActiveRecord"
368
- end
369
- ```
370
-
371
- ### ActiveRecord
372
- 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).
373
-
374
- #### Active Record Example
375
- ```ruby
376
- class Post::Finder < Trailblazer::Finder
377
- adapter "ActiveRecord"
378
- end
379
- ```
380
-
381
- ### Sequel
382
- 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).
383
-
384
- #### Sequel Example
385
- ```ruby
386
- class Post::Finder < Trailblazer::Finder
387
- adapter "Sequel"
388
- end
389
- ```
390
-
391
- ## Tips & Tricks
392
- ### ORM's are not required
393
- Not even for the Paging, Predicates and Sorting features.
394
-
395
- ```ruby
396
- class Post::Finder < Trailblazer::Finder
397
- entity { fetch_product_as_hashes }
398
-
399
- filter_by(:name) { |entity, _attribute, value| entity.select { |product| product[:name] == value } }
400
- filter_by(:category) { |entity, _attribute, value| entity.select { |product| product[:category] == value } }
401
- end
402
- ```
403
-
404
- ### Overwriting Methods
405
- You can have fine grained entity, by overwriting ```initialize``` method:
406
-
407
- ```ruby
408
- class Post::Finder < Trailblazer::Finder
409
- property :name, type: Types::String
410
-
411
- def initialize(options = {})
412
- super options.merge(entity: Product.visible_to(user))
413
- end
414
- end
415
- ```
416
-
417
- You can have fine grained result set, by overwriting ```fetch_result``` method:
418
-
419
- ```ruby
420
- class Post::Finder < Trailblazer::Finder
421
- property :name, type: Types::String
422
-
423
- def fetch_result
424
- super
425
- result.merge! test: "I added this"
426
- end
427
- end
428
- ```
429
-
430
- ## Contributing
431
- 1. Fork it
432
- 2. Create your feature branch
433
- 3. Commit your changes
434
- 4. Push to the branch
435
- 5. Run the tests (`rake`)
436
- 6. Make sure all tests pass, rubocop has no offenses and coverage is 100%
437
- 7. Create new Pull Request
438
-
439
- ## Bugs
440
- Please report them on the [Github issue tracker](http://github.com/trailblazer/trailblazer-finder) for this project.
441
-
442
- If you have a bug to report, please include the following information:
443
-
444
- * **Version information for Trailblazer-Finder, Trailblazer, used Adapters and Ruby.**
445
- * Full stack trace and error message (if you have them).
446
- * Any snippets of relevant model, view or controller code that shows how you are using Trailblazer-Finder.
447
-
448
- If you are able to, it helps even more if you can fork Trailblazer-Finder on Github,
449
- and add a test that reproduces the error you are experiencing.
450
-
451
- For more info on how to report bugs, please see [this article](http://yourbugreportneedsmore.info/).
data/Rakefile DELETED
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
- require "rubocop/rake_task"
6
-
7
- RSpec::Core::RakeTask.new(:spec) do |t|
8
- t.rspec_opts = "--format documentation --format RspecJunitFormatter --out test-reports/spec.xml"
9
- end
10
- RSpec::Core::RakeTask.new(:tests) do |t|
11
- t.rspec_opts = "--format progress --format documentation"
12
- end
13
- RSpec::Core::RakeTask.new(:spec_report) do |t|
14
- t.rspec_opts = "--format html --out reports/rspec_results.html"
15
- end
16
-
17
- RuboCop::RakeTask.new(:rubocop)
18
-
19
- desc "Build the gem"
20
- task :gem do
21
- `gem build trailblazer-finder.gemspec`
22
- end
23
-
24
- desc "Running Tests"
25
- task default: %i[spec]
data/spec/spec_helper.rb DELETED
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/setup"
4
- require "trailblazer/developer"
5
- require "trailblazer/activity"
6
- require "trailblazer/activity/testing"
7
-
8
- RSpec.configure do |config|
9
- config.expect_with(:rspec) { |c| c.syntax = :expect }
10
- config.include Trailblazer::Activity::Testing::Assertions
11
- end
12
-
13
- require "trailblazer/finder"
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "spec_helper"
4
- require "active_record"
5
-
6
- ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
7
-
8
- ActiveRecord::Schema.define do
9
- self.verbose = false
10
-
11
- create_table :products, force: true do |t|
12
- t.string :name
13
- t.string :slug
14
- t.integer :category_id
15
- t.integer :price
16
-
17
- t.timestamps null: true
18
- end
19
-
20
- create_table :categories, force: true do |t|
21
- t.string :title
22
- t.string :slug
23
-
24
- t.timestamps null: true
25
- end
26
- end
27
-
28
- class Product < ActiveRecord::Base
29
- belongs_to :category
30
- end
31
-
32
- class Category < ActiveRecord::Base
33
- end
34
-
35
- module ActiveRecord
36
- class Base
37
- def self.reset_pk_sequence
38
- case ActiveRecord::Base.connection.adapter_name
39
- when "SQLite"
40
- new_max = maximum(primary_key) || 0
41
- update_seq_sql = "update sqlite_sequence set seq = #{new_max} where name = '#{table_name}';"
42
- ActiveRecord::Base.connection.execute(update_seq_sql)
43
- else
44
- exit(1)
45
- end
46
- end
47
- end
48
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "spec_helper"
4
- require "sequel"
5
- Sequel::Model.plugin :timestamps
6
-
7
- DB = Sequel.sqlite
8
-
9
- DB.create_table :s_products do
10
- primary_key :id
11
- String :name
12
- String :slug
13
- Integer :s_category_id
14
- Integer :price
15
- DateTime :created_at
16
- DateTime :updated_at
17
- end
18
-
19
- DB.create_table :s_categories do
20
- primary_key :id
21
- String :title
22
- String :slug
23
- DateTime :created_at
24
- DateTime :updated_at
25
- end
26
-
27
- class SProduct < Sequel::Model
28
- many_to_one :s_category
29
- end
30
-
31
- class SCategory < Sequel::Model
32
- one_to_many :s_product
33
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if defined?(WillPaginate)
4
- module WillPaginate
5
- module ActiveRecord
6
- module RelationMethods
7
- def per(value = nil)
8
- per_page(value)
9
- end
10
- end
11
- end
12
- end
13
- end