rack-reducer 0.1.2 → 1.0.0

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
- SHA256:
3
- metadata.gz: a41029921e31a738165238e25af3300edc47c9a131b537ce597a313ba2060b4d
4
- data.tar.gz: 2b1c09a05bb3f5af59344d9e65bbdb166a54c1d8d8bffbe34ba3bb8e768d6c4e
2
+ SHA1:
3
+ metadata.gz: 0be5dfa4acf3c9a2c7755c67f2bbc15a2126b5d3
4
+ data.tar.gz: 6bd09c026cd5e5b9fb1d8ac0425d2e525afb3a36
5
5
  SHA512:
6
- metadata.gz: 7c6c6a069e7a9b6b9cdb1cf423143175cfa257215f69a2c7aaa7e027ac935d14073ed8192030af702f263441d38046671f26f635bd559d3cfe3c0743a1cb12a8
7
- data.tar.gz: 9839e1d5a9cd4f4557ad0a87c24c1cee4a4ec5357f085baa894c81c7e8981eadc01f65de84656818ce13357ea7a6ef9d7cabef22c0a7b2d96e65cd1d32a455b3
6
+ metadata.gz: e03f7c4766706254c287c7dbb0b664cf29d8c3b7d1e210189d4c17a9cd3a013c579d4e5ee2d0cda56cbf146e639d7515aff3092cb6832621e058375316a2d692
7
+ data.tar.gz: 15c82a76f5a7218393656ddb4abfaa5584bc11b483326452d08ff16a4a9c3efdecbd07ed810af51401a03b07a95dadd89b76199dfc4564afb04c1f4db9a7b2ec
data/lib/rack/reducer.rb CHANGED
@@ -1,6 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rack/request'
4
1
  require_relative 'reducer/reduction'
5
2
  require_relative 'reducer/middleware'
6
3
 
@@ -1,5 +1,4 @@
1
- # frozen_string_literal: true
2
-
1
+ require 'rack/request'
3
2
  require_relative 'reduction'
4
3
 
5
4
  module Rack
@@ -1,11 +1,15 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Rack
4
2
  module Reducer
5
3
  # convert params from Sinatra, Rails, Roda, etc into a symbol hash
6
4
  module Parser
7
5
  def self.call(data)
8
- data.is_a?(Hash) ? data : hashify(data)
6
+ data.is_a?(Hash) ? symbolize(data) : hashify(data)
7
+ end
8
+
9
+ def self.symbolize(data)
10
+ data.each_with_object({}) do |(key, val), hash|
11
+ hash[key.to_sym] = val.is_a?(Hash) ? symbolize(val) : val
12
+ end
9
13
  end
10
14
 
11
15
  # turns out a Rails params hash is not really a hash
@@ -13,7 +17,7 @@ module Rack
13
17
  # are automatically sanitized by the lambda keywords
14
18
  def self.hashify(data)
15
19
  fn = %i[to_unsafe_h to_h].find { |name| data.respond_to?(name) }
16
- data.send(fn)
20
+ symbolize(data.send(fn))
17
21
  end
18
22
  end
19
23
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require_relative 'refinements'
4
2
  require_relative 'parser'
5
3
 
@@ -8,7 +6,7 @@ module Rack
8
6
  # call `reduce` on a params hash, filtering data via lambdas with
9
7
  # matching keyword arguments
10
8
  class Reduction
11
- using Refinements # augment Hash & Proc inside this scope
9
+ using Refinements # define Proc#required_argument_names, #satisfies?, etc
12
10
 
13
11
  DEFAULTS = {
14
12
  dataset: [],
@@ -18,7 +16,7 @@ module Rack
18
16
 
19
17
  def initialize(options)
20
18
  @props = DEFAULTS.merge(options)
21
- @params = Parser.call(@props[:params]).symbolize_keys
19
+ @params = Parser.call(@props[:params])
22
20
  end
23
21
 
24
22
  def reduce
@@ -27,10 +25,10 @@ module Rack
27
25
 
28
26
  private
29
27
 
30
- def apply_filter(data, fn)
31
- requirements = fn.required_argument_names.to_set
32
- return data unless @params.satisfies?(requirements)
33
- data.instance_exec(@params.slice(*fn.all_argument_names), &fn)
28
+ def apply_filter(data, filter)
29
+ return data unless filter.satisfies?(@params)
30
+
31
+ data.instance_exec(@params.slice(*filter.all_argument_names), &filter)
34
32
  end
35
33
  end
36
34
  end
@@ -1,29 +1,29 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Rack
4
2
  module Reducer
5
- # refine a few core classes in Rack::Reducer's scope only
3
+ # refine Proc and hash in this scope only
6
4
  module Refinements
7
- refine Hash do
8
- def symbolize_keys
9
- each_with_object({}) do |(key, val), hash|
10
- hash[key.to_sym] = val.is_a?(Hash) ? val.symbolize_keys : val
11
- end
12
- end
13
-
14
- def satisfies?(requirements)
15
- slice(*requirements).keys.to_set == requirements
16
- end
17
- end
18
-
19
5
  refine Proc do
20
6
  def required_argument_names
21
- parameters.select { |arg| arg[0] == :keyreq }.map(&:last)
7
+ parameters.select { |type, _| type == :keyreq }.map(&:last)
22
8
  end
23
9
 
24
10
  def all_argument_names
25
11
  parameters.map(&:last)
26
12
  end
13
+
14
+ def satisfies?(params)
15
+ keywords = required_argument_names
16
+ params.slice(*keywords).keys.to_set == keywords.to_set
17
+ end
18
+ end
19
+
20
+ # backport Hash#slice for older rubies
21
+ unless {}.respond_to?(:slice)
22
+ refine Hash do
23
+ def slice(*keys)
24
+ [keys, values_at(*keys)].transpose.select { |_k, val| val }.to_h
25
+ end
26
+ end
27
27
  end
28
28
  end
29
29
  end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ module Reducer
3
+ VERSION = '1.0.0'.freeze
4
+ end
5
+ end
data/spec/behavior.rb CHANGED
@@ -1,4 +1,4 @@
1
- shared_examples_for Rack::Reducer do
1
+ RSpec.shared_examples_for Rack::Reducer do
2
2
  let(:app) { described_class }
3
3
 
4
4
  it 'responds with unfiltered data when filter params are empty' do
data/spec/benchmarks.rb CHANGED
@@ -1,56 +1,41 @@
1
- require 'spec_helper'
2
- require_relative 'fixtures'
1
+ require_relative 'spec_helper'
3
2
  require 'sinatra/base'
4
3
  require 'json'
5
4
  require 'benchmark/ips'
6
5
 
7
- class App < Sinatra::Base
8
- get '/conditionals' do
9
- @artists = DB[:artists]
10
- if (genre = params[:genre])
11
- @artists = @artists.grep(:genre, "%#{genre}%", case_insensitive: true)
12
- end
13
- if (name = params[:name])
14
- @artists = @artists.grep(:name, "%#{name}%", case_insensitive: true)
15
- end
16
-
17
- @artists.to_json
6
+ Conditionals = lambda do |params = {}|
7
+ @artists = DB[:artists]
8
+ if (genre = params[:genre])
9
+ @artists = @artists.grep(:genre, "%#{genre}%", case_insensitive: true)
18
10
  end
11
+ if (name = params[:name])
12
+ @artists = @artists.grep(:name, "%#{name}%", case_insensitive: true)
13
+ end
14
+
15
+ @artists.to_json
16
+ end
19
17
 
20
- get '/reduction' do
21
- @artists = Rack::Reducer.call(params, dataset: DB[:artists], filters: [
22
- ->(genre:) { grep(:genre, "%#{genre}%", case_insensitive: true) },
23
- ->(name:) { grep(:name, "%#{name}%", case_insensitive: true) },
24
- ])
18
+ Reduction = lambda do |params = {}|
19
+ @artists = Rack::Reducer.call(params, dataset: DB[:artists], filters: [
20
+ ->(genre:) { grep(:genre, "%#{genre}%", case_insensitive: true) },
21
+ ->(name:) { grep(:name, "%#{name}%", case_insensitive: true) },
22
+ ])
25
23
 
26
- @artists.to_json
27
- end
24
+ @artists.to_json
28
25
  end
29
26
 
30
- describe 'Performance' do
31
- let(:app) { App }
32
-
33
- it 'compares favorably to spaghetti code when params are empty' do
34
- Benchmark.ips(3) do |bm|
35
- bm.report('conditionals, empty params') do
36
- get '/conditionals'
37
- end
38
- bm.report('reduction, empty params') do
39
- get '/reduction'
40
- end
41
- bm.compare!
42
- end
27
+ Benchmark.ips(3) do |bm|
28
+ bm.report('conditionals, empty params') { Conditionals.call }
29
+
30
+ bm.report('reduction, empty params') { Reduction.call }
31
+
32
+ bm.report('conditionals, full params') do
33
+ Conditionals.call({ name: 'blake', genre: 'electric' })
43
34
  end
44
35
 
45
- it 'compares favorably to spaghetti code when params are full' do
46
- Benchmark.ips(3) do |bm|
47
- bm.report('conditionals, full params') do
48
- get '/conditionals?name=blake&genre=electronic'
49
- end
50
- bm.report('reduction, full params') do
51
- get '/reduction?name=blake&genre=electronic'
52
- end
53
- bm.compare!
54
- end
36
+ bm.report('reduction, full params') do
37
+ Reduction.call({ name: 'blake', genre: 'electric' })
55
38
  end
39
+
40
+ bm.compare!
56
41
  end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'bundler/setup'
2
2
  Bundler.setup
3
+ require 'rspec'
3
4
  require 'pry'
4
5
  require 'rack/test'
5
6
  require 'rack/reducer'
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: 0.1.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Frank
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-05 00:00:00.000000000 Z
11
+ date: 2018-12-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1'
19
+ version: '1.16'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1'
26
+ version: '1.16'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: benchmark-ips
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2'
33
+ version: '2.7'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2'
40
+ version: '2.7'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: pry
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.11'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '0.11'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: hanami
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
152
  version: '3'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.61'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.61'
153
167
  - !ruby/object:Gem::Dependency
154
168
  name: sequel
155
169
  requirement: !ruby/object:Gem::Requirement
@@ -192,6 +206,20 @@ dependencies:
192
206
  - - "~>"
193
207
  - !ruby/object:Gem::Version
194
208
  version: '1'
209
+ - !ruby/object:Gem::Dependency
210
+ name: yard
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: '0.9'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: '0.9'
195
223
  - !ruby/object:Gem::Dependency
196
224
  name: rack
197
225
  requirement: !ruby/object:Gem::Requirement
@@ -215,7 +243,7 @@ dependencies:
215
243
  description: Dynamically filter, sort, and paginate data via URL params, in any Rack
216
244
  app.
217
245
  email:
218
- - chris.frank@thefutureproject.org
246
+ - chris.frank@future.com
219
247
  executables: []
220
248
  extensions: []
221
249
  extra_rdoc_files: []
@@ -226,6 +254,7 @@ files:
226
254
  - lib/rack/reducer/parser.rb
227
255
  - lib/rack/reducer/reduction.rb
228
256
  - lib/rack/reducer/refinements.rb
257
+ - lib/rack/reducer/version.rb
229
258
  - spec/_hanami_example/apps/web/application.rb
230
259
  - spec/_hanami_example/apps/web/config/routes.rb
231
260
  - spec/_hanami_example/apps/web/controllers/artists/index.rb
@@ -288,7 +317,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
288
317
  version: '0'
289
318
  requirements: []
290
319
  rubyforge_project:
291
- rubygems_version: 2.7.6
320
+ rubygems_version: 2.4.5.5
292
321
  signing_key:
293
322
  specification_version: 4
294
323
  summary: Dynamically filter data via URL params, in any Rack app.