rack-reducer 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96e9d87cd9c12373fb42f37eedb4246d6296009faab5bb5f1831236a4c200b0f
4
- data.tar.gz: cb1e793d45ba3e71f588b22df0469358a40f56ab3167d8dc734b105306a841f0
3
+ metadata.gz: 3658ebc181adebd904bdb3da749349c92686b173a168dc5f73a5ef1f968ff7bf
4
+ data.tar.gz: eef09ce59406c0341c95977d3967be1e402be4288cf63af7ed5e6604375bc438
5
5
  SHA512:
6
- metadata.gz: ebb52de791e396e475cbfb76040645120e4892f12d9fc9f3b7901d86a9b52adf2d7314b6795472640fb5a3eb2c1f44b49191a823457a9b94588fea129a530870
7
- data.tar.gz: c373da2d40a7e3da1b281408636c38107430a7583155f3923539e8db7ce85c09cffd2b1233fc867dc1f56dd94e86281dd7820a9131d5ed544f1dd3439def249b
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 zero dependencies beyond Rack itself.
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
- # sinatra_functional_style.rb
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. The code below will order by `params[:sort]` when
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
- return @dataset if !params || params.empty?
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
- symbolized_params = params.to_unsafe_h.symbolize_keys
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(
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rack
4
4
  module Reducer
5
- VERSION = '1.1.0'
5
+ VERSION = '1.1.1'
6
6
  end
7
7
  end
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
@@ -17,7 +17,7 @@ module Fixtures
17
17
  ->(name:) {
18
18
  select { |item| item[:name].match(/#{name}/i) }
19
19
  },
20
- ->(sort:) {
20
+ ->(sort: 'name') {
21
21
  sort_by { |item| item[sort.to_sym] }
22
22
  },
23
23
  ->(releases:) {
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
- class RailsApp < Rails::Application
7
- routes.append do
8
- get "/", to: "artists#index"
9
- get "/query", to: "artists#query"
10
- end
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
- config.api_only = true
13
- config.eager_load = true
14
- config.secret_key_base = SecureRandom.hex(64)
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
- class ArtistsController < ActionController::API
18
- def index
19
- @artists = Fixtures::ArtistReducer.apply(params)
20
- render json: @artists
31
+ app.initialize!
21
32
  end
22
33
 
23
- def query
24
- @artists = Fixtures::ArtistReducer.apply(request.query_parameters)
25
- render json: @artists
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
- RSpec.describe RailsApp do
30
- let(:app) { RailsApp.initialize! }
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 '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) }
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 'can sort' do
68
- get '/artists?order=genre' do |response|
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('alternative')
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.0
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-03-30 00:00:00.000000000 Z
11
+ date: 2019-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack