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 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