rack-reducer 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +112 -252
- data/lib/rack/reducer/middleware.rb +17 -2
- data/lib/rack/reducer/reduction.rb +19 -16
- data/lib/rack/reducer/refinements.rb +13 -1
- data/lib/rack/reducer/version.rb +3 -1
- data/lib/rack/reducer/warnings.rb +27 -0
- data/lib/rack/reducer.rb +51 -20
- data/spec/benchmarks.rb +51 -21
- data/spec/fixtures.rb +30 -0
- data/spec/middleware_spec.rb +55 -23
- data/spec/rails_spec.rb +33 -3
- data/spec/reducer_spec.rb +104 -0
- data/spec/spec_helper.rb +6 -15
- metadata +34 -136
- data/lib/rack/reducer/parser.rb +0 -26
- data/spec/_hanami_example/apps/web/application.rb +0 -326
- data/spec/_hanami_example/apps/web/config/routes.rb +0 -4
- data/spec/_hanami_example/apps/web/controllers/artists/index.rb +0 -12
- data/spec/_hanami_example/apps/web/views/application_layout.rb +0 -7
- data/spec/_hanami_example/config/boot.rb +0 -2
- data/spec/_hanami_example/config/environment.rb +0 -29
- data/spec/_hanami_example/lib/hanami_example/entities/artist.rb +0 -2
- data/spec/_hanami_example/lib/hanami_example/repositories/artist_repository.rb +0 -9
- data/spec/_hanami_example/lib/hanami_example.rb +0 -5
- data/spec/_rails_example/app/channels/application_cable/channel.rb +0 -4
- data/spec/_rails_example/app/channels/application_cable/connection.rb +0 -4
- data/spec/_rails_example/app/controllers/application_controller.rb +0 -2
- data/spec/_rails_example/app/controllers/artists_controller.rb +0 -8
- data/spec/_rails_example/app/jobs/application_job.rb +0 -2
- data/spec/_rails_example/app/mailers/application_mailer.rb +0 -4
- data/spec/_rails_example/app/models/application_record.rb +0 -3
- data/spec/_rails_example/app/models/rails_example/artist.rb +0 -21
- data/spec/_rails_example/config/application.rb +0 -35
- data/spec/_rails_example/config/boot.rb +0 -3
- data/spec/_rails_example/config/environment.rb +0 -5
- data/spec/_rails_example/config/environments/development.rb +0 -47
- data/spec/_rails_example/config/environments/production.rb +0 -83
- data/spec/_rails_example/config/environments/test.rb +0 -42
- data/spec/_rails_example/config/initializers/application_controller_renderer.rb +0 -8
- data/spec/_rails_example/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/_rails_example/config/initializers/cors.rb +0 -16
- data/spec/_rails_example/config/initializers/filter_parameter_logging.rb +0 -4
- data/spec/_rails_example/config/initializers/inflections.rb +0 -16
- data/spec/_rails_example/config/initializers/mime_types.rb +0 -4
- data/spec/_rails_example/config/initializers/wrap_parameters.rb +0 -14
- data/spec/_rails_example/config/puma.rb +0 -56
- data/spec/_rails_example/config/routes.rb +0 -4
- data/spec/_rails_example/db/seeds.rb +0 -7
- data/spec/behavior.rb +0 -51
- data/spec/hanami_spec.rb +0 -6
- data/spec/roda_spec.rb +0 -13
- data/spec/sinatra_functional_spec.rb +0 -26
- data/spec/sinatra_mixin_spec.rb +0 -20
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Reducer
|
5
|
+
module Warnings
|
6
|
+
MESSAGES = {
|
7
|
+
new: [
|
8
|
+
'Rack::Reducer.new will become an alias of ::create in v2.',
|
9
|
+
'To mount middleware that will still work in 2.0, write',
|
10
|
+
'"use Rack::Reducer::Middleware" instead of "use Rack::Reducer"',
|
11
|
+
],
|
12
|
+
reduces: [
|
13
|
+
'Rack::Reducer’s mixin-style is deprecated and may be removed in v2.',
|
14
|
+
'To keep using Rack::Reducer in your models, use a Reducer constant.',
|
15
|
+
'class MyModel',
|
16
|
+
' MyReducer = Rack::Reducer.create(dataset, *filter_functions)',
|
17
|
+
'end',
|
18
|
+
'MyModel::MyReducer.call(params)',
|
19
|
+
]
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
def self.[](key)
|
23
|
+
MESSAGES.fetch(key, []).join("\n")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/rack/reducer.rb
CHANGED
@@ -1,38 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'reducer/reduction'
|
2
4
|
require_relative 'reducer/middleware'
|
5
|
+
require_relative 'reducer/warnings'
|
3
6
|
|
4
7
|
module Rack
|
5
|
-
#
|
8
|
+
# Declaratively filter data via URL params, in any Rack app.
|
6
9
|
module Reducer
|
7
|
-
#
|
10
|
+
# Create a Reduction object that can filter +dataset+ via +#apply+.
|
11
|
+
# @param [Object] dataset an ActiveRecord::Relation, Sequel::Dataset,
|
12
|
+
# or other class with chainable methods
|
13
|
+
# @param [Array<Proc>] filters An array of lambdas whose keyword arguments
|
14
|
+
# name the URL params you will use as filters
|
15
|
+
# @return Rack::Reducer::Reduction
|
16
|
+
# @example Create a reducer and use it in a Sinatra app
|
17
|
+
# DB = Sequel.connect(ENV['DATABASE_URL'])
|
18
|
+
# MyReducer = Rack::Reducer.create(
|
19
|
+
# DB[:artists],
|
20
|
+
# lambda { |name:| where(name: name) },
|
21
|
+
# lambda { |genre:| where(genre: genre) },
|
22
|
+
# )
|
23
|
+
#
|
24
|
+
# get '/artists' do
|
25
|
+
# @artists = MyReducer.apply(params)
|
26
|
+
# @artists.to_json
|
27
|
+
# end
|
28
|
+
def self.create(dataset, *filters)
|
29
|
+
Reduction.new(dataset, *filters)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Filter a dataset without creating a Reducer first.
|
33
|
+
# Note that this approach is a bit slower and less memory-efficient than
|
34
|
+
# creating a Reducer via ::create. Use ::create when you can.
|
35
|
+
#
|
8
36
|
# @param params [Hash] Rack-compatible URL params
|
9
37
|
# @param dataset [Object] A dataset, e.g. one of your App's models
|
10
38
|
# @param filters [Array<Proc>] An array of lambdas with keyword arguments
|
11
39
|
# @example Call Rack::Reducer as a function in a Sinatra app
|
12
|
-
#
|
13
|
-
# dataset: Artist,
|
14
|
-
# filters: [
|
40
|
+
# get '/artists' do
|
41
|
+
# @artists = Rack::Reducer.call(params, dataset: Artist.all, filters: [
|
15
42
|
# lambda { |name:| where(name: name) },
|
16
43
|
# lambda { |genre:| where(genre: genre) },
|
17
|
-
# ]
|
18
|
-
# }
|
19
|
-
# get '/artists' do
|
20
|
-
# @artists = Rack::Reducer.call(params, ArtistReducer)
|
44
|
+
# ])
|
21
45
|
# end
|
22
46
|
def self.call(params, dataset:, filters:)
|
23
|
-
Reduction.new(
|
24
|
-
params: params,
|
25
|
-
filters: filters,
|
26
|
-
dataset: dataset
|
27
|
-
).reduce
|
47
|
+
Reduction.new(dataset, *filters).apply(params)
|
28
48
|
end
|
29
49
|
|
30
50
|
# Mount Rack::Reducer as middleware
|
51
|
+
# @deprecated
|
52
|
+
# Rack::Reducer.new will become an alias of ::create in v2.0.
|
53
|
+
# To mount middleware that will still work in 2.0, write
|
54
|
+
# "use Rack::Reducer::Middleware" instead of "use Rack::Reducer"
|
31
55
|
def self.new(app, options = {})
|
56
|
+
warn "#{caller(1..1).first}}\n#{Warnings[:new]}"
|
32
57
|
Middleware.new(app, options)
|
33
58
|
end
|
34
59
|
|
35
60
|
# Extend Rack::Reducer to get +reduce+ and +reduces+ as class-methods
|
61
|
+
#
|
36
62
|
# @example Make an "Artists" model reducible
|
37
63
|
# class Artist < SomeORM::Model
|
38
64
|
# extend Rack::Reducer
|
@@ -41,15 +67,20 @@ module Rack
|
|
41
67
|
# lambda { |genre:| where(genre: genre) },
|
42
68
|
# ]
|
43
69
|
# end
|
44
|
-
#
|
45
70
|
# Artist.reduce(params)
|
71
|
+
#
|
72
|
+
# @deprecated
|
73
|
+
# Rack::Reducer's mixin-style is deprecated and may be removed in 2.0.
|
74
|
+
# To keep using Rack::Reducer in your models, create a Reducer constant.
|
75
|
+
# class MyModel < ActiveRecord::Base
|
76
|
+
# MyReducer = Rack::Reducer.create(dataset, *filter_functions)
|
77
|
+
# end
|
78
|
+
# MyModel::MyReducer.call(params)
|
46
79
|
def reduces(dataset, filters:)
|
80
|
+
warn "#{caller(1..1).first}}\n#{Warnings[:reduces]}"
|
81
|
+
reducer = Reduction.new(dataset, *filters)
|
47
82
|
define_singleton_method :reduce do |params|
|
48
|
-
|
49
|
-
params: params,
|
50
|
-
filters: filters,
|
51
|
-
dataset: dataset,
|
52
|
-
).reduce
|
83
|
+
reducer.apply(params)
|
53
84
|
end
|
54
85
|
end
|
55
86
|
end
|
data/spec/benchmarks.rb
CHANGED
@@ -1,40 +1,70 @@
|
|
1
|
-
|
2
|
-
require '
|
1
|
+
require 'rack'
|
2
|
+
require 'pry'
|
3
3
|
require 'json'
|
4
|
+
require_relative '../lib/rack/reducer'
|
4
5
|
require 'benchmark/ips'
|
6
|
+
require_relative 'fixtures'
|
7
|
+
require 'sequel'
|
5
8
|
|
6
|
-
|
9
|
+
DB = Sequel.sqlite.tap do |db|
|
10
|
+
db.create_table(:artists) do
|
11
|
+
primary_key :id
|
12
|
+
String :name
|
13
|
+
String :genre
|
14
|
+
Integer :release_count
|
15
|
+
end
|
16
|
+
Fixtures::DB[:artists].each { |row| db[:artists].insert(row) }
|
17
|
+
end
|
18
|
+
|
19
|
+
conditional_app = lambda do |env|
|
20
|
+
params = Rack::Request.new(env).params
|
7
21
|
@artists = DB[:artists]
|
8
|
-
if (genre = params[
|
9
|
-
@artists = @artists.
|
22
|
+
if (genre = params['genre'])
|
23
|
+
@artists = @artists.where(genre: genre.to_s)
|
10
24
|
end
|
11
|
-
if (name = params[
|
25
|
+
if (name = params['name'])
|
12
26
|
@artists = @artists.grep(:name, "%#{name}%", case_insensitive: true)
|
13
27
|
end
|
14
|
-
|
15
|
-
@artists.to_json
|
28
|
+
Rack::Response.new(@artists).finish
|
16
29
|
end
|
17
30
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
31
|
+
TestReducer = Rack::Reducer.create(
|
32
|
+
DB[:artists],
|
33
|
+
->(genre:) { where(genre: genre.to_s) },
|
34
|
+
->(name:) { grep(:name, "%#{name}%", case_insensitive: true) },
|
35
|
+
)
|
23
36
|
|
24
|
-
|
37
|
+
reducer_app = lambda do |env|
|
38
|
+
params = Rack::Request.new(env).params
|
39
|
+
@artists = TestReducer.apply(params)
|
40
|
+
Rack::Response.new(@artists).finish
|
25
41
|
end
|
26
42
|
|
27
|
-
Benchmark.ips
|
28
|
-
|
43
|
+
Benchmark.ips do |bm|
|
44
|
+
env = {
|
45
|
+
'REQUEST_METHOD' => 'GET',
|
46
|
+
'PATH_INFO' => '/',
|
47
|
+
'rack.input' => StringIO.new('')
|
48
|
+
}
|
29
49
|
|
30
|
-
|
50
|
+
query = {
|
51
|
+
'QUERY_STRING' => 'name=blake&genre=electronic',
|
52
|
+
}
|
53
|
+
|
54
|
+
bm.report('Conditionals (full)') do
|
55
|
+
conditional_app.call env.merge(query)
|
56
|
+
end
|
57
|
+
|
58
|
+
bm.report('Reducer (full)') do
|
59
|
+
reducer_app.call env.merge(query)
|
60
|
+
end
|
31
61
|
|
32
|
-
bm.report('
|
33
|
-
|
62
|
+
bm.report('Conditionals (empty)') do
|
63
|
+
conditional_app.call env.dup
|
34
64
|
end
|
35
65
|
|
36
|
-
bm.report('
|
37
|
-
|
66
|
+
bm.report('Reducer (empty)') do
|
67
|
+
reducer_app.call env.dup
|
38
68
|
end
|
39
69
|
|
40
70
|
bm.compare!
|
data/spec/fixtures.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Fixtures
|
2
|
+
DB = {
|
3
|
+
artists: [
|
4
|
+
{ name: 'Blake Mills', genre: 'alternative', release_count: 3 },
|
5
|
+
{ name: 'Björk', genre: 'electronic', release_count: 3 },
|
6
|
+
{ name: 'James Blake', genre: 'electronic', release_count: 3 },
|
7
|
+
{ name: 'Janelle Monae', genre: 'alt-soul', release_count: 3 },
|
8
|
+
{ name: 'SZA', genre: 'alt-soul', release_count: 3 },
|
9
|
+
{ name: 'Chris Frank', genre: 'alt-soul', release_count: nil },
|
10
|
+
]
|
11
|
+
}
|
12
|
+
|
13
|
+
FILTERS = [
|
14
|
+
->(genre:) {
|
15
|
+
select { |item| item[:genre].match(/#{genre}/i) }
|
16
|
+
},
|
17
|
+
->(name:) {
|
18
|
+
select { |item| item[:name].match(/#{name}/i) }
|
19
|
+
},
|
20
|
+
->(sort:) {
|
21
|
+
sort_by { |item| item[sort.to_sym] }
|
22
|
+
},
|
23
|
+
->(releases:) {
|
24
|
+
select { |item| item[:release_count].to_i == releases.to_i }
|
25
|
+
},
|
26
|
+
]
|
27
|
+
|
28
|
+
ArtistReducer = Rack::Reducer.create(DB[:artists], *FILTERS)
|
29
|
+
end
|
30
|
+
|
data/spec/middleware_spec.rb
CHANGED
@@ -1,31 +1,63 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
|
2
|
+
require_relative 'fixtures'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
module
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
}
|
4
|
+
RSpec.describe Rack::Reducer::Middleware do
|
5
|
+
using SpecRefinements
|
6
|
+
module AppFactory
|
7
|
+
def self.create(key: nil, middleware_class: Rack::Reducer::Middleware)
|
8
|
+
Rack::Builder.new do
|
9
|
+
use(
|
10
|
+
middleware_class,
|
11
|
+
dataset: Fixtures::DB[:artists],
|
12
|
+
filters: Fixtures::FILTERS,
|
13
|
+
key: key
|
14
|
+
)
|
15
|
+
run ->(env) { [200, {}, [env.to_json]] }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'without a key set' do
|
21
|
+
let(:app) { AppFactory.create }
|
22
|
+
it 'responds with unfiltered data when filter params are empty' do
|
23
|
+
get('/') do |res|
|
24
|
+
reduction = res.json['rack.reduction']
|
25
|
+
expect(reduction.count).to eq(Fixtures::DB[:artists].count)
|
26
|
+
end
|
27
|
+
end
|
16
28
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
29
|
+
it 'filters by a single param, e.g. name' do
|
30
|
+
get('/artists?name=Blake') do |res|
|
31
|
+
reduction = res.body
|
32
|
+
expect(reduction).to include('Blake Mills')
|
33
|
+
expect(reduction).to include('James Blake')
|
34
|
+
expect(reduction).not_to include('SZA')
|
35
|
+
end
|
21
36
|
end
|
22
37
|
end
|
23
|
-
end
|
24
38
|
|
25
|
-
describe
|
26
|
-
|
27
|
-
|
39
|
+
describe 'with a custom key' do
|
40
|
+
let(:app) { AppFactory.create(key: 'custom_key') }
|
41
|
+
it 'stores reducer data at env[custom_key]' do
|
42
|
+
get('/') do |res|
|
43
|
+
expect(res.json['custom_key'].class).to eq(Array)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'using Rack::Reducer instead of Rack::Reducer::Middleware' do
|
49
|
+
before do
|
50
|
+
@warnings = []
|
51
|
+
allow(Rack::Reducer).to receive(:warn) do |msg|
|
52
|
+
@warnings << msg
|
53
|
+
end
|
54
|
+
end
|
28
55
|
|
29
|
-
|
30
|
-
|
56
|
+
let(:app) { AppFactory.create(middleware_class: Rack::Reducer) }
|
57
|
+
|
58
|
+
it 'emits a deprecation warning' do
|
59
|
+
get('/')
|
60
|
+
expect(@warnings.last).to include('alias of ::create')
|
61
|
+
end
|
62
|
+
end
|
31
63
|
end
|
data/spec/rails_spec.rb
CHANGED
@@ -1,6 +1,36 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require_relative '
|
2
|
+
require_relative 'fixtures'
|
3
|
+
require 'action_controller/railtie'
|
4
|
+
require 'securerandom'
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
+
class RailsApp < Rails::Application
|
7
|
+
routes.append do
|
8
|
+
get "/", to: "artists#index"
|
9
|
+
get "/query", to: "artists#query"
|
10
|
+
end
|
11
|
+
|
12
|
+
config.api_only = true
|
13
|
+
config.eager_load = true
|
14
|
+
config.secret_key_base = SecureRandom.hex(64)
|
15
|
+
end
|
16
|
+
|
17
|
+
class ArtistsController < ActionController::API
|
18
|
+
def index
|
19
|
+
@artists = Fixtures::ArtistReducer.apply(params)
|
20
|
+
render json: @artists
|
21
|
+
end
|
22
|
+
|
23
|
+
def query
|
24
|
+
@artists = Fixtures::ArtistReducer.apply(request.query_parameters)
|
25
|
+
render json: @artists
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
RSpec.describe RailsApp do
|
30
|
+
let(:app) { RailsApp.initialize! }
|
31
|
+
|
32
|
+
it 'works with ActionController::Parameters and a plain hash' do
|
33
|
+
get('/') { |res| expect(res.status).to eq(200) }
|
34
|
+
get('/query') { |res| expect(res.status).to eq(200) }
|
35
|
+
end
|
6
36
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'fixtures'
|
3
|
+
|
4
|
+
RSpec.describe Rack::Reducer do
|
5
|
+
using SpecRefinements
|
6
|
+
|
7
|
+
let(:app) do
|
8
|
+
lambda do |env|
|
9
|
+
req = Rack::Request.new(env)
|
10
|
+
res = Fixtures::ArtistReducer.apply(req.params).to_json
|
11
|
+
[200, { 'Content-Type' => 'application/json' }, [res]]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'responds with unfiltered data when filter params are empty' do
|
16
|
+
get('/') do |res|
|
17
|
+
expect(res.json.count).to eq(Fixtures::DB[:artists].count)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'filters by a single param, e.g. name' do
|
22
|
+
get('/artists?name=Blake') do |response|
|
23
|
+
expect(response.body).to include('Blake Mills')
|
24
|
+
expect(response.body).to include('James Blake')
|
25
|
+
expect(response.body).not_to include('SZA')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'resets state between requests' do
|
30
|
+
get('/artists?name=Blake')
|
31
|
+
get('/artists') do |res|
|
32
|
+
expect(res.json.count).to eq(Fixtures::DB[:artists].count)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'filters by a single param, e.g. genre' do
|
37
|
+
get('/artists?genre=electronic') do |response|
|
38
|
+
expect(response.body).to include('Björk')
|
39
|
+
expect(response.body).to include('James Blake')
|
40
|
+
expect(response.body).not_to include('Blake Mills')
|
41
|
+
end
|
42
|
+
|
43
|
+
get '/artists?genre=soul' do |response|
|
44
|
+
expect(response.body).to include('Janelle Monae')
|
45
|
+
expect(response.body).not_to include('Björk')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'chains multiple filters' do
|
50
|
+
get('/artists?genre=electronic&name=blake') do |response|
|
51
|
+
expect(response.body).to include('James Blake')
|
52
|
+
expect(response.body).not_to include('Blake Mills')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'handles falsy values' do
|
57
|
+
get('/artists?releases=0') do |response|
|
58
|
+
expect(response.body).to include('Chris Frank')
|
59
|
+
expect(JSON.parse(response.body).length).to eq(1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'accepts nil as params' do
|
64
|
+
expect(Fixtures::ArtistReducer.apply(nil)).to be_truthy
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'can sort' do
|
68
|
+
get '/artists?order=genre' do |response|
|
69
|
+
genre = response.json[0]['genre']
|
70
|
+
expect(genre).to eq('alternative')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'ad-hoc style via ::call' do
|
75
|
+
let(:params) { { 'genre' => 'electronic', 'name' => 'blake' } }
|
76
|
+
it 'works just like the primary style, but slower' do
|
77
|
+
result = Rack::Reducer.call(
|
78
|
+
params,
|
79
|
+
dataset: Fixtures::DB[:artists],
|
80
|
+
filters: Fixtures::FILTERS,
|
81
|
+
)
|
82
|
+
expect(result.count).to eq(1)
|
83
|
+
expect(result[0][:name]).to eq('James Blake')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'mixin-style' do
|
88
|
+
before { @warnings = [] }
|
89
|
+
|
90
|
+
let(:model) do
|
91
|
+
dataset = Fixtures::DB[:artists].dup
|
92
|
+
allow(dataset).to(receive(:warn)) { |msg| @warnings << msg }
|
93
|
+
dataset.extend Rack::Reducer
|
94
|
+
dataset.reduces dataset, filters: Fixtures::FILTERS
|
95
|
+
dataset
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'is still supported, but with a deprecation warning' do
|
99
|
+
params = { 'genre' => 'electronic', 'name' => 'blake' }
|
100
|
+
expect(model.reduce(params).count).to eq(1)
|
101
|
+
expect(@warnings.first).to include('mixin-style is deprecated')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,26 +1,17 @@
|
|
1
1
|
require 'bundler/setup'
|
2
2
|
Bundler.setup
|
3
|
-
require 'rspec'
|
4
3
|
require 'pry'
|
5
4
|
require 'rack/test'
|
6
5
|
require 'rack/reducer'
|
7
|
-
require 'sequel'
|
8
|
-
require_relative 'behavior'
|
9
|
-
ENV['RACK_ENV'] = ENV['RAILS_ENV'] = 'test'
|
10
|
-
DB = Sequel.connect "sqlite://#{__dir__}/fixtures.sqlite"
|
11
|
-
|
12
|
-
SEQUEL_QUERY = {
|
13
|
-
dataset: DB[:artists],
|
14
|
-
filters: [
|
15
|
-
->(genre:) { grep(:genre, "%#{genre}%", case_insensitive: true) },
|
16
|
-
->(name:) { grep(:name, "%#{name}%", case_insensitive: true) },
|
17
|
-
->(order: 'genre') { order(order.to_sym) },
|
18
|
-
->(releases: ) { where(release_count: releases.to_i) },
|
19
|
-
]
|
20
|
-
}.freeze
|
21
6
|
|
22
7
|
RSpec.configure do |config|
|
23
8
|
config.color = true
|
24
9
|
config.order = :random
|
25
10
|
config.include Rack::Test::Methods
|
26
11
|
end
|
12
|
+
|
13
|
+
module SpecRefinements
|
14
|
+
refine Rack::MockResponse do
|
15
|
+
define_method(:json) { JSON.parse(body) }
|
16
|
+
end
|
17
|
+
end
|