rack-reducer 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|