rack-reducer 1.1.0 → 1.1.1
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 +8 -6
- data/lib/rack/reducer/reduction.rb +13 -3
- data/lib/rack/reducer/version.rb +1 -1
- data/spec/benchmarks.rb +20 -2
- data/spec/fixtures.rb +1 -1
- data/spec/rails_spec.rb +41 -24
- data/spec/reducer_spec.rb +10 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3658ebc181adebd904bdb3da749349c92686b173a168dc5f73a5ef1f968ff7bf
|
4
|
+
data.tar.gz: eef09ce59406c0341c95977d3967be1e402be4288cf63af7ed5e6604375bc438
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ccd88dd529cfcbc6c71fff712f509509030d1ba74a705ae6e41b74b9e93146198cfa3ec5820407bec3221c8256146630221ae040e80e64aad89467625c25e1f
|
7
|
+
data.tar.gz: c170a6b2a71b8df5f46e9faf57bd74074e79c1189a18ea0fd07404dc1a97dd206b4a7f855f715ec4fc9d30f7ff1607bd3f5816d0b72603d1c376efd030ea90b6
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ Add `rack-reducer` to your Gemfile:
|
|
13
13
|
gem 'rack-reducer', require: 'rack/reducer'
|
14
14
|
```
|
15
15
|
|
16
|
-
Rack::Reducer has
|
16
|
+
Rack::Reducer has no dependencies beyond Rack itself.
|
17
17
|
|
18
18
|
Use
|
19
19
|
------------------------------------------
|
@@ -33,6 +33,7 @@ data. Here’s how you might use it in a Rails controller:
|
|
33
33
|
```ruby
|
34
34
|
# app/controllers/artists_controller.rb
|
35
35
|
class ArtistsController < ApplicationController
|
36
|
+
|
36
37
|
# Step 1: Create a reducer
|
37
38
|
ArtistReducer = Rack::Reducer.create(
|
38
39
|
Artist.all,
|
@@ -70,6 +71,10 @@ This example app would handle requests as follows:
|
|
70
71
|
[{ "name": "James Blake", "genre": "electronic" }]
|
71
72
|
```
|
72
73
|
|
74
|
+
API Documentation
|
75
|
+
---------------------------
|
76
|
+
https://www.rubydoc.info/gems/rack-reducer
|
77
|
+
|
73
78
|
Framework-specific Examples
|
74
79
|
---------------------------
|
75
80
|
These examples apply Rack::Reducer in different frameworks and ORMs. The
|
@@ -81,7 +86,7 @@ This example uses [Sinatra][sinatra] to handle requests, and [Sequel][sequel]
|
|
81
86
|
as an ORM.
|
82
87
|
|
83
88
|
```ruby
|
84
|
-
#
|
89
|
+
# config.ru
|
85
90
|
class SinatraExample < Sinatra::Base
|
86
91
|
DB = Sequel.connect ENV['DATABASE_URL']
|
87
92
|
|
@@ -135,7 +140,6 @@ env['rack.reduction'], then calls the next app in the middleware stack. You can
|
|
135
140
|
change the `env` key by passing a new name as option to `use`:
|
136
141
|
|
137
142
|
```ruby
|
138
|
-
# config.ru
|
139
143
|
use Rack::Reducer::Midleware, key: 'custom.key', dataset: ARTISTS, filters: [
|
140
144
|
# an array of lambdas
|
141
145
|
]
|
@@ -179,11 +183,10 @@ filter, and skip running the filter altogether when the keyword argments aren't
|
|
179
183
|
present.
|
180
184
|
|
181
185
|
But sometimes you'll want to run a filter with a default value, even when the
|
182
|
-
required params are missing.
|
186
|
+
required params are missing. The code below will order by `params[:sort]` when
|
183
187
|
it exists, and by name otherwise.
|
184
188
|
|
185
189
|
```ruby
|
186
|
-
# app/controllers/artists_controller.rb
|
187
190
|
class ArtistsController < ApplicationController
|
188
191
|
ArtistReducer = Rack::Reducer.create(
|
189
192
|
Artist.all,
|
@@ -218,7 +221,6 @@ class ArtistsController < ApplicationController
|
|
218
221
|
end
|
219
222
|
```
|
220
223
|
|
221
|
-
|
222
224
|
How Rack::Reducer Works
|
223
225
|
--------------------------------------
|
224
226
|
Rack::Reducer takes a dataset, an array of lambdas, and a params hash.
|
@@ -12,6 +12,9 @@ module Rack
|
|
12
12
|
def initialize(dataset, *filters)
|
13
13
|
@dataset = dataset
|
14
14
|
@filters = filters
|
15
|
+
@default_filters = filters.select do |filter|
|
16
|
+
filter.required_argument_names.empty?
|
17
|
+
end
|
15
18
|
end
|
16
19
|
|
17
20
|
# Run +@filters+ against the params argument
|
@@ -19,10 +22,17 @@ module Rack
|
|
19
22
|
# a Rack-compatible params hash
|
20
23
|
# @return +@dataset+ with the matching filters applied
|
21
24
|
def apply(params)
|
22
|
-
|
25
|
+
if !params || params.empty?
|
26
|
+
return @dataset if @default_filters.empty?
|
27
|
+
|
28
|
+
filters = @default_filters
|
29
|
+
symbolized_params = {}
|
30
|
+
else
|
31
|
+
filters = @filters
|
32
|
+
symbolized_params = params.to_unsafe_h.symbolize_keys
|
33
|
+
end
|
23
34
|
|
24
|
-
|
25
|
-
@filters.reduce(@dataset) do |data, filter|
|
35
|
+
filters.reduce(@dataset) do |data, filter|
|
26
36
|
next data unless filter.satisfies?(symbolized_params)
|
27
37
|
|
28
38
|
data.instance_exec(
|
data/lib/rack/reducer/version.rb
CHANGED
data/spec/benchmarks.rb
CHANGED
@@ -25,18 +25,27 @@ conditional_app = lambda do |env|
|
|
25
25
|
if (name = params['name'])
|
26
26
|
@artists = @artists.grep(:name, "%#{name}%", case_insensitive: true)
|
27
27
|
end
|
28
|
+
if env['DEFAULTS']
|
29
|
+
@artists = @artists.order(:name)
|
30
|
+
end
|
28
31
|
Rack::Response.new(@artists).finish
|
29
32
|
end
|
30
33
|
|
31
34
|
TestReducer = Rack::Reducer.create(
|
35
|
+
DB[:artists],
|
36
|
+
->(genre:) { where(genre: genre.to_s) },
|
37
|
+
->(name:) { grep(:name, "%#{name}%", case_insensitive: true) }
|
38
|
+
)
|
39
|
+
TestReducerWithDefaults = Rack::Reducer.create(
|
32
40
|
DB[:artists],
|
33
41
|
->(genre:) { where(genre: genre.to_s) },
|
34
42
|
->(name:) { grep(:name, "%#{name}%", case_insensitive: true) },
|
43
|
+
->(sort: 'name') { order(sort.to_sym) }
|
35
44
|
)
|
36
45
|
|
37
46
|
reducer_app = lambda do |env|
|
38
47
|
params = Rack::Request.new(env).params
|
39
|
-
@artists = TestReducer.apply(params)
|
48
|
+
@artists = env['DEFAULTS'] ? TestReducerWithDefaults.apply(params) : TestReducer.apply(params)
|
40
49
|
Rack::Response.new(@artists).finish
|
41
50
|
end
|
42
51
|
|
@@ -44,7 +53,8 @@ Benchmark.ips do |bm|
|
|
44
53
|
env = {
|
45
54
|
'REQUEST_METHOD' => 'GET',
|
46
55
|
'PATH_INFO' => '/',
|
47
|
-
'rack.input' => StringIO.new('')
|
56
|
+
'rack.input' => StringIO.new(''),
|
57
|
+
'DEFAULTS' => false
|
48
58
|
}
|
49
59
|
|
50
60
|
query = {
|
@@ -67,5 +77,13 @@ Benchmark.ips do |bm|
|
|
67
77
|
reducer_app.call env.dup
|
68
78
|
end
|
69
79
|
|
80
|
+
bm.report('Conditionals (defaults)') do
|
81
|
+
conditional_app.call env.merge('DEFAULTS' => true)
|
82
|
+
end
|
83
|
+
|
84
|
+
bm.report('Reducer (defaults)') do
|
85
|
+
reducer_app.call env.merge('DEFAULTS' => true)
|
86
|
+
end
|
87
|
+
|
70
88
|
bm.compare!
|
71
89
|
end
|
data/spec/fixtures.rb
CHANGED
data/spec/rails_spec.rb
CHANGED
@@ -1,36 +1,53 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require_relative 'fixtures'
|
3
|
-
require 'action_controller/railtie'
|
4
|
-
require 'securerandom'
|
5
3
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
Process.respond_to?(:fork) && RSpec.describe('in a Rails app') do
|
5
|
+
BuildRailsApp = lambda do
|
6
|
+
require 'action_controller/railtie'
|
7
|
+
require 'securerandom'
|
8
|
+
app = Class.new(Rails::Application) do
|
9
|
+
routes.append do
|
10
|
+
get "/", to: "artists#index"
|
11
|
+
get "/query", to: "artists#query"
|
12
|
+
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
14
|
+
config.api_only = true
|
15
|
+
config.eager_load = true
|
16
|
+
config.secret_key_base = SecureRandom.hex(64)
|
17
|
+
end
|
18
|
+
|
19
|
+
ArtistsController = Class.new(ActionController::API) do
|
20
|
+
def index
|
21
|
+
@artists = Fixtures::ArtistReducer.apply(params)
|
22
|
+
render json: @artists
|
23
|
+
end
|
24
|
+
|
25
|
+
def query
|
26
|
+
@artists = Fixtures::ArtistReducer.apply(request.query_parameters)
|
27
|
+
render json: @artists
|
28
|
+
end
|
29
|
+
end
|
16
30
|
|
17
|
-
|
18
|
-
def index
|
19
|
-
@artists = Fixtures::ArtistReducer.apply(params)
|
20
|
-
render json: @artists
|
31
|
+
app.initialize!
|
21
32
|
end
|
22
33
|
|
23
|
-
|
24
|
-
|
25
|
-
|
34
|
+
let(:app) { BuildRailsApp.call }
|
35
|
+
|
36
|
+
it 'works with ActionController::Parameters' do
|
37
|
+
pid = Process.fork do
|
38
|
+
get('/') { |res| expect(res.status).to eq(200) }
|
39
|
+
end
|
40
|
+
Process.wait pid
|
26
41
|
end
|
27
|
-
end
|
28
42
|
|
29
|
-
|
30
|
-
|
43
|
+
it 'works with request.query_parameters' do
|
44
|
+
pid = Process.fork do
|
45
|
+
get('/query') { |res| expect(res.status).to eq(200) }
|
46
|
+
end
|
47
|
+
Process.wait(pid)
|
48
|
+
end
|
31
49
|
|
32
|
-
it '
|
33
|
-
|
34
|
-
get('/query') { |res| expect(res.status).to eq(200) }
|
50
|
+
it 'does not load ActiveSupport into global scope b/c of this spec' do
|
51
|
+
expect { ''.blank? }.to raise_error(NoMethodError)
|
35
52
|
end
|
36
53
|
end
|
data/spec/reducer_spec.rb
CHANGED
@@ -64,10 +64,17 @@ RSpec.describe Rack::Reducer do
|
|
64
64
|
expect(Fixtures::ArtistReducer.apply(nil)).to be_truthy
|
65
65
|
end
|
66
66
|
|
67
|
-
it '
|
68
|
-
get '/artists
|
67
|
+
it 'applies default filters' do
|
68
|
+
get '/artists' do |response|
|
69
|
+
name = response.json[0]['name']
|
70
|
+
expect(name).to eq('Björk')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'can override default params' do
|
75
|
+
get '/artists?sort=genre' do |response|
|
69
76
|
genre = response.json[0]['genre']
|
70
|
-
expect(genre).to eq('
|
77
|
+
expect(genre).to eq('alt-soul')
|
71
78
|
end
|
72
79
|
end
|
73
80
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-reducer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Frank
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|