rack-reducer 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +170 -104
  3. data/lib/rack/reducer/middleware.rb +24 -0
  4. data/lib/rack/reducer/reduction.rb +5 -17
  5. data/lib/rack/reducer.rb +7 -7
  6. data/spec/_hanami_example/apps/web/application.rb +326 -0
  7. data/spec/_hanami_example/apps/web/config/routes.rb +4 -0
  8. data/spec/_hanami_example/apps/web/controllers/artists/index.rb +12 -0
  9. data/spec/_hanami_example/apps/web/views/application_layout.rb +7 -0
  10. data/spec/_hanami_example/config/boot.rb +2 -0
  11. data/spec/_hanami_example/config/environment.rb +29 -0
  12. data/spec/_hanami_example/lib/hanami_example/entities/artist.rb +2 -0
  13. data/spec/_hanami_example/lib/hanami_example/repositories/artist_repository.rb +9 -0
  14. data/spec/_hanami_example/lib/hanami_example.rb +5 -0
  15. data/spec/{rails_example → _rails_example}/app/channels/application_cable/channel.rb +0 -0
  16. data/spec/{rails_example → _rails_example}/app/channels/application_cable/connection.rb +0 -0
  17. data/spec/{rails_example → _rails_example}/app/controllers/application_controller.rb +0 -0
  18. data/spec/_rails_example/app/controllers/artists_controller.rb +8 -0
  19. data/spec/{rails_example → _rails_example}/app/jobs/application_job.rb +0 -0
  20. data/spec/{rails_example → _rails_example}/app/mailers/application_mailer.rb +0 -0
  21. data/spec/{rails_example → _rails_example}/app/models/application_record.rb +0 -0
  22. data/spec/_rails_example/app/models/rails_example/artist.rb +20 -0
  23. data/spec/{rails_example → _rails_example}/config/application.rb +0 -0
  24. data/spec/{rails_example → _rails_example}/config/boot.rb +0 -0
  25. data/spec/{rails_example → _rails_example}/config/environment.rb +0 -0
  26. data/spec/{rails_example → _rails_example}/config/environments/development.rb +0 -0
  27. data/spec/{rails_example → _rails_example}/config/environments/production.rb +0 -0
  28. data/spec/{rails_example → _rails_example}/config/environments/test.rb +0 -0
  29. data/spec/{rails_example → _rails_example}/config/initializers/application_controller_renderer.rb +0 -0
  30. data/spec/{rails_example → _rails_example}/config/initializers/backtrace_silencers.rb +0 -0
  31. data/spec/{rails_example → _rails_example}/config/initializers/cors.rb +0 -0
  32. data/spec/{rails_example → _rails_example}/config/initializers/filter_parameter_logging.rb +0 -0
  33. data/spec/{rails_example → _rails_example}/config/initializers/inflections.rb +0 -0
  34. data/spec/{rails_example → _rails_example}/config/initializers/mime_types.rb +0 -0
  35. data/spec/{rails_example → _rails_example}/config/initializers/wrap_parameters.rb +0 -0
  36. data/spec/{rails_example → _rails_example}/config/puma.rb +0 -0
  37. data/spec/{rails_example → _rails_example}/config/routes.rb +0 -0
  38. data/spec/{rails_example → _rails_example}/db/seeds.rb +0 -0
  39. data/spec/behavior.rb +2 -2
  40. data/spec/hanami_spec.rb +6 -0
  41. data/spec/middleware_spec.rb +16 -8
  42. data/spec/rails_spec.rb +1 -2
  43. data/spec/roda_spec.rb +15 -0
  44. data/spec/sinatra_functional_spec.rb +3 -9
  45. data/spec/sinatra_mixin_spec.rb +0 -2
  46. data/spec/spec_helper.rb +12 -1
  47. metadata +121 -62
  48. data/spec/fixtures.rb +0 -19
  49. data/spec/rails_example/app/controllers/artists_controller.rb +0 -53
  50. data/spec/rails_example/app/models/artist.rb +0 -18
  51. data/spec/rails_example/config/initializers/schema.rb +0 -13
  52. data/spec/rails_example/test/test_helper.rb +0 -10
@@ -0,0 +1,326 @@
1
+ require 'hanami/helpers'
2
+ require 'hanami/assets'
3
+
4
+ module Web
5
+ class Application < Hanami::Application
6
+ configure do
7
+ ##
8
+ # BASIC
9
+ #
10
+
11
+ # Define the root path of this application.
12
+ # All paths specified in this configuration are relative to path below.
13
+ #
14
+ root __dir__
15
+
16
+ # Relative load paths where this application will recursively load the
17
+ # code.
18
+ #
19
+ # When you add new directories, remember to add them here.
20
+ #
21
+ load_paths << [
22
+ 'controllers',
23
+ 'views'
24
+ ]
25
+
26
+ # Handle exceptions with HTTP statuses (true) or don't catch them (false).
27
+ # Defaults to true.
28
+ # See: http://www.rubydoc.info/gems/hanami-controller/#Exceptions_management
29
+ #
30
+ # handle_exceptions true
31
+
32
+ ##
33
+ # HTTP
34
+ #
35
+
36
+ # Routes definitions for this application
37
+ # See: http://www.rubydoc.info/gems/hanami-router#Usage
38
+ #
39
+ routes 'config/routes'
40
+
41
+ # URI scheme used by the routing system to generate absolute URLs
42
+ # Defaults to "http"
43
+ #
44
+ # scheme 'https'
45
+
46
+ # URI host used by the routing system to generate absolute URLs
47
+ # Defaults to "localhost"
48
+ #
49
+ # host 'example.org'
50
+
51
+ # URI port used by the routing system to generate absolute URLs
52
+ # Argument: An object coercible to integer, defaults to 80 if the scheme
53
+ # is http and 443 if it's https
54
+ #
55
+ # This should only be configured if app listens to non-standard ports
56
+ #
57
+ # port 443
58
+
59
+ # Enable cookies
60
+ # Argument: boolean to toggle the feature
61
+ # A Hash with options
62
+ #
63
+ # Options:
64
+ # :domain - The domain (String - nil by default, not required)
65
+ # :path - Restrict cookies to a relative URI
66
+ # (String - nil by default)
67
+ # :max_age - Cookies expiration expressed in seconds
68
+ # (Integer - nil by default)
69
+ # :secure - Restrict cookies to secure connections
70
+ # (Boolean - Automatically true when using HTTPS)
71
+ # See #scheme and #ssl?
72
+ # :httponly - Prevent JavaScript access (Boolean - true by default)
73
+ #
74
+ # cookies true
75
+ # or
76
+ # cookies max_age: 300
77
+
78
+ # Enable sessions
79
+ # Argument: Symbol the Rack session adapter
80
+ # A Hash with options
81
+ #
82
+ # See: http://www.rubydoc.info/gems/rack/Rack/Session/Cookie
83
+ #
84
+ # sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']
85
+
86
+ # Configure Rack middleware for this application
87
+ #
88
+ # middleware.use Rack::Protection
89
+
90
+ # Default format for the requests that don't specify an HTTP_ACCEPT header
91
+ # Argument: A symbol representation of a mime type, defaults to :html
92
+ #
93
+ default_request_format :json
94
+
95
+ # Default format for responses that don't consider the request format
96
+ # Argument: A symbol representation of a mime type, defaults to :html
97
+ #
98
+ default_response_format :json
99
+
100
+ # HTTP Body parsers
101
+ # Parse non GET responses body for a specific mime type
102
+ # Argument: Symbol, which represent the format of the mime type
103
+ # (only `:json` is supported)
104
+ # Object, the parser
105
+ #
106
+ # body_parsers :json
107
+
108
+ # When it's true and the router receives a non-encrypted request (http),
109
+ # it redirects to the secure equivalent (https). Disabled by default.
110
+ #
111
+ # force_ssl true
112
+
113
+ ##
114
+ # TEMPLATES
115
+ #
116
+
117
+ # The layout to be used by all views
118
+ #
119
+ layout :application # It will load Web::Views::ApplicationLayout
120
+
121
+ # The relative path to templates
122
+ #
123
+ templates 'templates'
124
+
125
+ ##
126
+ # ASSETS
127
+ #
128
+ assets do
129
+ # JavaScript compressor
130
+ #
131
+ # Supported engines:
132
+ #
133
+ # * :builtin
134
+ # * :uglifier
135
+ # * :yui
136
+ # * :closure
137
+ #
138
+ # See: http://hanamirb.org/guides/assets/compressors
139
+ #
140
+ # In order to skip JavaScript compression comment the following line
141
+ javascript_compressor :builtin
142
+
143
+ # Stylesheet compressor
144
+ #
145
+ # Supported engines:
146
+ #
147
+ # * :builtin
148
+ # * :yui
149
+ # * :sass
150
+ #
151
+ # See: http://hanamirb.org/guides/assets/compressors
152
+ #
153
+ # In order to skip stylesheet compression comment the following line
154
+ stylesheet_compressor :builtin
155
+
156
+ # Specify sources for assets
157
+ #
158
+ sources << [
159
+ 'assets'
160
+ ]
161
+ end
162
+
163
+ ##
164
+ # SECURITY
165
+ #
166
+
167
+ # X-Frame-Options is a HTTP header supported by modern browsers.
168
+ # It determines if a web page can or cannot be included via <frame> and
169
+ # <iframe> tags by untrusted domains.
170
+ #
171
+ # Web applications can send this header to prevent Clickjacking attacks.
172
+ #
173
+ # Read more at:
174
+ #
175
+ # * https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options
176
+ # * https://www.owasp.org/index.php/Clickjacking
177
+ #
178
+ security.x_frame_options 'DENY'
179
+
180
+ # X-Content-Type-Options prevents browsers from interpreting files as
181
+ # something else than declared by the content type in the HTTP headers.
182
+ #
183
+ # Read more at:
184
+ #
185
+ # * https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#X-Content-Type-Options
186
+ # * https://msdn.microsoft.com/en-us/library/gg622941%28v=vs.85%29.aspx
187
+ # * https://blogs.msdn.microsoft.com/ie/2008/09/02/ie8-security-part-vi-beta-2-update
188
+ #
189
+ security.x_content_type_options 'nosniff'
190
+
191
+ # X-XSS-Protection is a HTTP header to determine the behavior of the
192
+ # browser in case an XSS attack is detected.
193
+ #
194
+ # Read more at:
195
+ #
196
+ # * https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
197
+ # * https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#X-XSS-Protection
198
+ #
199
+ security.x_xss_protection '1; mode=block'
200
+
201
+ # Content-Security-Policy (CSP) is a HTTP header supported by modern
202
+ # browsers. It determines trusted sources of execution for dynamic
203
+ # contents (JavaScript) or other web related assets: stylesheets, images,
204
+ # fonts, plugins, etc.
205
+ #
206
+ # Web applications can send this header to mitigate Cross Site Scripting
207
+ # (XSS) attacks.
208
+ #
209
+ # The default value allows images, scripts, AJAX, fonts and CSS from the
210
+ # same origin, and does not allow any other resources to load (eg object,
211
+ # frame, media, etc).
212
+ #
213
+ # Inline JavaScript is NOT allowed. To enable it, please use:
214
+ # "script-src 'unsafe-inline'".
215
+ #
216
+ # Content Security Policy introduction:
217
+ #
218
+ # * http://www.html5rocks.com/en/tutorials/security/content-security-policy/
219
+ # * https://www.owasp.org/index.php/Content_Security_Policy
220
+ # * https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29
221
+ #
222
+ # Inline and eval JavaScript risks:
223
+ #
224
+ # * http://www.html5rocks.com/en/tutorials/security/content-security-policy/#inline-code-considered-harmful
225
+ # * http://www.html5rocks.com/en/tutorials/security/content-security-policy/#eval-too
226
+ #
227
+ # Content Security Policy usage:
228
+ #
229
+ # * http://content-security-policy.com/
230
+ # * https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Using_Content_Security_Policy
231
+ #
232
+ # Content Security Policy references:
233
+ #
234
+ # * https://developer.mozilla.org/en-US/docs/Web/Security/CSP/CSP_policy_directives
235
+ #
236
+ security.content_security_policy %{
237
+ form-action 'self';
238
+ frame-ancestors 'self';
239
+ base-uri 'self';
240
+ default-src 'none';
241
+ script-src 'self';
242
+ connect-src 'self';
243
+ img-src 'self' https: data:;
244
+ style-src 'self' 'unsafe-inline' https:;
245
+ font-src 'self';
246
+ object-src 'none';
247
+ plugin-types application/pdf;
248
+ child-src 'self';
249
+ frame-src 'self';
250
+ media-src 'self'
251
+ }
252
+
253
+ ##
254
+ # FRAMEWORKS
255
+ #
256
+
257
+ # Configure the code that will yield each time Web::Action is included
258
+ # This is useful for sharing common functionality
259
+ #
260
+ # See: http://www.rubydoc.info/gems/hanami-controller#Configuration
261
+ controller.prepare do
262
+ # include MyAuthentication # included in all the actions
263
+ # before :authenticate! # run an authentication before callback
264
+ end
265
+
266
+ # Configure the code that will yield each time Web::View is included
267
+ # This is useful for sharing common functionality
268
+ #
269
+ # See: http://www.rubydoc.info/gems/hanami-view#Configuration
270
+ view.prepare do
271
+ include Hanami::Helpers
272
+ include Web::Assets::Helpers
273
+ end
274
+ end
275
+
276
+ ##
277
+ # DEVELOPMENT
278
+ #
279
+ configure :development do
280
+ # Don't handle exceptions, render the stack trace
281
+ handle_exceptions false
282
+ end
283
+
284
+ ##
285
+ # TEST
286
+ #
287
+ configure :test do
288
+ # Don't handle exceptions, render the stack trace
289
+ handle_exceptions false
290
+ end
291
+
292
+ ##
293
+ # PRODUCTION
294
+ #
295
+ configure :production do
296
+ # scheme 'https'
297
+ # host 'example.org'
298
+ # port 443
299
+
300
+ assets do
301
+ # Don't compile static assets in production mode (eg. Sass, ES6)
302
+ #
303
+ # See: http://www.rubydoc.info/gems/hanami-assets#Configuration
304
+ compile false
305
+
306
+ # Use fingerprint file name for asset paths
307
+ #
308
+ # See: http://hanamirb.org/guides/assets/overview
309
+ fingerprint true
310
+
311
+ # Content Delivery Network (CDN)
312
+ #
313
+ # See: http://hanamirb.org/guides/assets/content-delivery-network
314
+ #
315
+ # scheme 'https'
316
+ # host 'cdn.example.org'
317
+ # port 443
318
+
319
+ # Subresource Integrity
320
+ #
321
+ # See: http://hanamirb.org/guides/assets/content-delivery-network/#subresource-integrity
322
+ subresource_integrity :sha256
323
+ end
324
+ end
325
+ end
326
+ end
@@ -0,0 +1,4 @@
1
+ # Configure your routes here
2
+ # See: http://hanamirb.org/guides/routing/overview/
3
+ #
4
+ get '/artists', to: 'artists#index'
@@ -0,0 +1,12 @@
1
+ require_relative '../../../../lib/hanami_example'
2
+
3
+ module Web::Controllers::Artists
4
+ class Index
5
+ include Web::Action
6
+
7
+ def call(params)
8
+ @artists = ArtistRepository.new.reduce(params).to_a
9
+ self.body = @artists.to_json
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module Web
2
+ module Views
3
+ class ApplicationLayout
4
+ include Web::Layout
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ require_relative './environment'
2
+ Hanami.boot
@@ -0,0 +1,29 @@
1
+ require 'bundler/setup'
2
+ require 'hanami/setup'
3
+ require 'hanami/model'
4
+ require_relative '../lib/hanami_example'
5
+ require_relative '../apps/web/application'
6
+
7
+ Hanami.configure do
8
+ mount Web::Application, at: '/'
9
+
10
+ model do
11
+ ##
12
+ # Database adapter
13
+ #
14
+ # Available options:
15
+ #
16
+ # * SQL adapter
17
+ # adapter :sql, 'sqlite://db/hanami_example_development.sqlite3'
18
+ # adapter :sql, 'postgresql://localhost/hanami_example_development'
19
+ # adapter :sql, 'mysql://localhost/hanami_example_development'
20
+ #
21
+ adapter :sql, "sqlite://#{__dir__}/../../fixtures.sqlite"
22
+
23
+ ##
24
+ # Migrations
25
+ #
26
+ migrations 'db/migrations'
27
+ schema 'db/schema.sql'
28
+ end
29
+ end
@@ -0,0 +1,2 @@
1
+ class Artist < Hanami::Entity
2
+ end
@@ -0,0 +1,9 @@
1
+ class ArtistRepository < Hanami::Repository
2
+ def query
3
+ { dataset: artists.dataset, filters: SEQUEL_QUERY[:filters] }
4
+ end
5
+
6
+ def reduce(params)
7
+ Rack::Reducer.call(params, query)
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'hanami_example/entities/artist'
2
+ require_relative 'hanami_example/repositories/artist_repository'
3
+
4
+ module HanamiExample
5
+ end
@@ -0,0 +1,8 @@
1
+ class ArtistsController < ApplicationController
2
+ # GET /artists
3
+ def index
4
+ @artists = RailsExample::Artist.reduce(params)
5
+
6
+ render json: @artists
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ module RailsExample
2
+ class Artist < ApplicationRecord
3
+ scope :by_name, lambda { |name|
4
+ where('lower(name) like ?', "%#{name.downcase}%")
5
+ }
6
+ def self.search_genre(genre)
7
+ where('lower(genre) like ?', "%#{genre.downcase}%")
8
+ end
9
+
10
+ extend Rack::Reducer
11
+ reduces all, filters: [
12
+ # filters can call class methods...
13
+ ->(genre:) { search_genre(genre) },
14
+ # or scopes...
15
+ ->(name:) { by_name(name) },
16
+ # or inline ActiveRecord queries
17
+ ->(order:) { order(order.to_sym) }
18
+ ]
19
+ end
20
+ end
File without changes
data/spec/behavior.rb CHANGED
@@ -3,7 +3,7 @@ shared_examples_for Rack::Reducer do
3
3
 
4
4
  it 'responds with unfiltered data when filter params are empty' do
5
5
  get('/artists') do |res|
6
- ARTISTS.each { |artist| expect(res.body).to include(artist[:name]) }
6
+ DB[:artists].each { |artist| expect(res.body).to include(artist[:name]) }
7
7
  end
8
8
  end
9
9
 
@@ -37,7 +37,7 @@ shared_examples_for Rack::Reducer do
37
37
 
38
38
  it 'can sort as well as filter' do
39
39
  get '/artists?order=genre' do |response|
40
- genre = JSON.parse(response.body).dig(0, 'genre')
40
+ genre = JSON.parse(response.body)[0]['genre']
41
41
  expect(genre).to eq('alt-soul')
42
42
  end
43
43
  end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+ require_relative '_hanami_example/config/boot'
3
+
4
+ describe Hanami.app do
5
+ it_behaves_like Rack::Reducer
6
+ end
@@ -1,18 +1,22 @@
1
1
  require 'spec_helper'
2
- require_relative 'fixtures'
3
2
  require 'json'
4
3
 
5
4
  # mount Rack::Reducer as middleware, let it filter data into env['rack.reduction'],
6
5
  # and respond with env['rack.reduction'].to_json
7
6
  module MiddlewareTest
8
- def self.app
7
+ DEFAULTS = {
8
+ dataset: DB[:artists].all,
9
+ filters: [
10
+ ->(genre:) { select { |item| item[:genre].match(/#{genre}/i) } },
11
+ ->(name:) { select { |item| item[:name].match(/#{name}/i) } },
12
+ ->(order:) { sort_by { |item| item[order.to_sym] } }
13
+ ]
14
+ }
15
+
16
+ def self.app(options = {}, key = options[:key] || 'rack.reduction')
9
17
  Rack::Builder.new do
10
- use Rack::Reducer, dataset: ARTISTS, filters: [
11
- ->(genre:) { select { |item| item[:genre].match(/#{genre}/i) } },
12
- ->(name:) { select { |item| item[:name].match(/#{name}/i) } },
13
- ->(order:) { sort_by { |item| item[order.to_sym] } }
14
- ]
15
- run ->(env) { [200, {}, [env['rack.reduction'].to_json]] }
18
+ use Rack::Reducer, DEFAULTS.merge(options)
19
+ run ->(env) { [200, {}, [env[key].to_json]] }
16
20
  end
17
21
  end
18
22
  end
@@ -20,3 +24,7 @@ end
20
24
  describe MiddlewareTest.app do
21
25
  it_behaves_like Rack::Reducer
22
26
  end
27
+
28
+ describe MiddlewareTest.app(key: 'some.custom.key') do
29
+ it_behaves_like Rack::Reducer
30
+ end
data/spec/rails_spec.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'spec_helper'
2
- ENV['RACK_ENV'] = ENV['RAILS_ENV'] = 'test'
3
- require_relative 'rails_example/config/environment'
2
+ require_relative '_rails_example/config/environment'
4
3
 
5
4
  describe Rails.application do
6
5
  it_behaves_like Rack::Reducer
data/spec/roda_spec.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'roda'
3
+
4
+ class RodaTest < Roda
5
+ plugin :json
6
+ route do |r|
7
+ r.on 'artists' do
8
+ r.get { Rack::Reducer.call(r.params, SEQUEL_QUERY).to_a }
9
+ end
10
+ end
11
+ end
12
+
13
+ describe RodaTest do
14
+ it_behaves_like Rack::Reducer
15
+ end
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require_relative 'fixtures'
3
2
  require 'sinatra/base'
4
3
  require 'json'
5
4
 
@@ -9,13 +8,8 @@ class SinatraFunctional < Sinatra::Base
9
8
  end
10
9
 
11
10
  get '/artists' do
12
- @artists = Rack::Reducer.call(params, dataset: Artist, filters: [
13
- ->(genre:) { grep(:genre, "%#{genre}%", case_insensitive: true) },
14
- ->(name:) { grep(:name, "%#{name}%", case_insensitive: true) },
15
- ->(order: 'genre') { order(order.to_sym) }
16
- ])
17
-
18
- @artists.all.to_json
11
+ @artists = Rack::Reducer.call(params, SEQUEL_QUERY).to_a
12
+ @artists.to_json
19
13
  end
20
14
  end
21
15
 
@@ -25,7 +19,7 @@ describe SinatraFunctional do
25
19
 
26
20
  it 'applies a default order' do
27
21
  get '/artists' do |response|
28
- genre = JSON.parse(response.body).dig(0, 'genre')
22
+ genre = JSON.parse(response.body)[0]['genre']
29
23
  expect(genre).to eq('alt-soul')
30
24
  end
31
25
  end
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require_relative 'fixtures'
3
2
  require 'sinatra/base'
4
3
  require 'json'
5
4
 
@@ -12,7 +11,6 @@ class SinatraMixin < Sinatra::Base
12
11
  ->(name:) { grep(:name, "%#{name}%", case_insensitive: true) },
13
12
  ->(order:) { order(order.to_sym) }
14
13
  ]
15
-
16
14
  end
17
15
 
18
16
  get '/artists' do
data/spec/spec_helper.rb CHANGED
@@ -3,8 +3,19 @@ Bundler.setup
3
3
  require 'pry'
4
4
  require 'rack/test'
5
5
  require 'rack/reducer'
6
- require_relative 'fixtures'
6
+ require 'sequel'
7
7
  require_relative 'behavior'
8
+ ENV['RACK_ENV'] = ENV['RAILS_ENV'] = 'test'
9
+ DB = Sequel.connect "sqlite://#{__dir__}/fixtures.sqlite"
10
+
11
+ SEQUEL_QUERY = {
12
+ dataset: DB[:artists],
13
+ filters: [
14
+ ->(genre:) { grep(:genre, "%#{genre}%", case_insensitive: true) },
15
+ ->(name:) { grep(:name, "%#{name}%", case_insensitive: true) },
16
+ ->(order: 'genre') { order(order.to_sym) }
17
+ ]
18
+ }.freeze
8
19
 
9
20
  RSpec.configure do |config|
10
21
  config.color = true