qo 0.1.7 → 0.1.8

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
  SHA1:
3
- metadata.gz: c98fa2c3b82ac4167eba17b183fe1cea46a0e36f
4
- data.tar.gz: ab2527c365781b54fac1290ac64e8a17acdcd07c
3
+ metadata.gz: 594012163edefd5fedd3266382aca2efddd10432
4
+ data.tar.gz: aa376733835f2fa1ce1abbe16018e2b2344f96ad
5
5
  SHA512:
6
- metadata.gz: f1e9b886e694b8c08f212209db40bc8ca1276b738d1d30c81040e0b651f264bcfc9c83a21d82d96f2ff418d05def3e5abae82a29394f184266fab424f5dad4a6
7
- data.tar.gz: ab226ea4d673ea462b2e41084d25c8a61b2c04e37e8aac9354745b1b17c4ae54b5efa6184ae28e0201fa453b525938f4d8f260912300a0f145cc0992607424e1
6
+ metadata.gz: 6db60b6de5c5ab900e6b144ddc3c9ed4eee275781017cc3f36580f1f904b2287ce734378d9d3dc6e0412ac4f62f37166c001c07235ae519373efbb99282c75f6
7
+ data.tar.gz: 93404e15e4db2dee338f64769e035d2c33f5945e6f1a2c2d53240d5019c2f11b9eceb08af13dcfdb9a540fbe39aa935fb201f08ef8638c7c5f447ea722456923
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Short for Query Object, my play at Ruby pattern matching and fluent querying
4
4
 
5
+ ![Qo Lemur logo](img/qo_logo.png)
6
+
5
7
  ## How does it work?
6
8
 
7
9
  Triple equals black magic, mostly.
@@ -505,7 +507,7 @@ To be fair that means anything that can respond to `===`, including classes and
505
507
  This ends up coming up a lot, especially around querying, so let's get a way to count by!
506
508
 
507
509
  ```ruby
508
- Qo.count_by([1,2,3,2,2,2,1]
510
+ Qo.count_by([1,2,3,2,2,2,1])
509
511
 
510
512
  # => {
511
513
  # 1 => 2,
data/Rakefile CHANGED
@@ -1,6 +1,107 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require 'benchmark/ips'
4
+ require 'qo'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
8
  task :default => :spec
9
+
10
+ # Run a benchmark given a title and a set of benchmarks. Admittedly this
11
+ # is done because the Benchmark.ips code can get a tinge repetitive and this
12
+ # is easier to write out.
13
+ #
14
+ # @param title [String] Title of the benchmark
15
+ # @param **benchmarks [Hash[Symbol, Proc]] Name to Proc to run to benchmark it
16
+ #
17
+ # @note Notice I'm using `'String': -> {}` instead of hashrockets? Kwargs doesn't
18
+ # take string / hashrocket arguments, probably to prevent abuse of the
19
+ # "anything can be a key" bit of Ruby.
20
+ #
21
+ # @return [Unit] StdOut
22
+ def run_benchmark(title, **benchmarks)
23
+ puts '', title, '=' * title.size, ''
24
+
25
+ Benchmark.ips do |bm|
26
+ benchmarks.each do |benchmark_name, benchmark_fn|
27
+ bm.report(benchmark_name, &benchmark_fn)
28
+ end
29
+
30
+ bm.compare!
31
+ end
32
+ end
33
+
34
+ # Note that the current development of Qo is NOT to be performance first, it's to
35
+ # be readability first with performance coming later. That means that early iterations
36
+ # may well be slower, but the net expressiveness we get is worth it in the short run.
37
+ task :perf do
38
+ # Compare simple array equality. I almost think this isn't fair to Qo considering
39
+ # no sane dev should use it for literal 1 to 1 matches like this.
40
+
41
+ simple_array = [1, 1]
42
+ run_benchmark('Array * Array - Literal',
43
+ 'Vanilla': -> {
44
+ simple_array == simple_array
45
+ },
46
+
47
+ 'Qo.and': -> {
48
+ Qo.and(*simple_array).call(simple_array)
49
+ }
50
+ )
51
+
52
+ # Compare testing indexed array matches. This gets a bit more into what Qo does,
53
+ # though I feel like there are optimizations that could be had here as well.
54
+
55
+ range_match_set = [1..10, 1..10, 1..10, 1..10]
56
+ range_match_target = [1, 2, 3, 4]
57
+
58
+ run_benchmark('Array * Array - Index pattern match',
59
+ 'Vanilla': -> {
60
+ range_match_target.each_with_index.all? { |x, i| range_match_set[i] === x }
61
+ },
62
+
63
+ 'Qo.and': -> {
64
+ Qo.and(*range_match_set).call(range_match_target)
65
+ }
66
+ )
67
+
68
+ # Now we're getting into things Qo makes sense for. Comparing an entire list
69
+ # against a stream of predicates to check
70
+
71
+ numbers_array = [1, 2.0, 3, 4]
72
+
73
+ run_benchmark('Array * Object - Predicate match',
74
+ 'Vanilla': -> {
75
+ numbers_array.all? { |i| i.is_a?(Integer) && i.even? && (20..30).include?(i) }
76
+ },
77
+
78
+ 'Qo.and': -> {
79
+ numbers_array.all?(&Qo.and(Integer, :even?, 20..30))
80
+ }
81
+ )
82
+
83
+ # This one is a bit interesting. The vanilla version is written to reflect that
84
+ # it has NO idea what the length of either set is, which is exactly what Qo
85
+ # has to deal with as well.
86
+
87
+ people_array_target = [
88
+ ['Robert', 22],
89
+ ['Roberta', 22],
90
+ ['Foo', 42],
91
+ ['Bar', 18]
92
+ ]
93
+
94
+ people_array_query = [/Rob/, 15..25]
95
+
96
+ run_benchmark('Array * Array - Select index pattern match',
97
+ 'Vanilla': -> {
98
+ people_array_target.select { |person|
99
+ person.each_with_index.all? { |a, i| people_array_query[i] === a }
100
+ }
101
+ },
102
+
103
+ 'Qo.and': -> {
104
+ Qo.and(*people_array_query).call(people_array_target)
105
+ }
106
+ )
107
+ end
Binary file
@@ -43,6 +43,8 @@ module Qo
43
43
  # Object -> Bool # Boolean public send
44
44
  private def match_against_array(matchers)
45
45
  -> match_target {
46
+ return true if matchers == match_target
47
+
46
48
  match_target.is_a?(::Array) ?
47
49
  match_with(matchers.each_with_index, array_against_array_matcher(match_target)) :
48
50
  match_with(matchers, array_against_object_matcher(match_target))
@@ -60,6 +62,8 @@ module Qo
60
62
  # Object -> Bool # Uses keys as methods with public send to `===` match against the value
61
63
  private def match_against_hash(matchers)
62
64
  -> match_target {
65
+ return true if matchers == match_target
66
+
63
67
  match_target.is_a?(::Hash) ?
64
68
  match_with(matchers, hash_against_hash_matcher(match_target)) :
65
69
  match_with(matchers, hash_against_object_matcher(match_target))
@@ -1,3 +1,3 @@
1
1
  module Qo
2
- VERSION = '0.1.7'
2
+ VERSION = '0.1.8'
3
3
  end
@@ -0,0 +1,60 @@
1
+
2
+ Array * Array - Literal
3
+ =======================
4
+
5
+ Warming up --------------------------------------
6
+ Vanilla 289.298k i/100ms
7
+ Qo.and 39.896k i/100ms
8
+ Calculating -------------------------------------
9
+ Vanilla 9.620M (± 2.4%) i/s - 48.313M in 5.025188s
10
+ Qo.and 456.077k (± 2.3%) i/s - 2.314M in 5.076462s
11
+
12
+ Comparison:
13
+ Vanilla: 9620049.8 i/s
14
+ Qo.and: 456077.4 i/s - 21.09x slower
15
+
16
+
17
+ Array * Array - Index pattern match
18
+ ===================================
19
+
20
+ Warming up --------------------------------------
21
+ Vanilla 46.056k i/100ms
22
+ Qo.and 14.514k i/100ms
23
+ Calculating -------------------------------------
24
+ Vanilla 528.961k (± 3.0%) i/s - 2.671M in 5.054671s
25
+ Qo.and 154.299k (± 1.9%) i/s - 783.756k in 5.081304s
26
+
27
+ Comparison:
28
+ Vanilla: 528961.3 i/s
29
+ Qo.and: 154299.4 i/s - 3.43x slower
30
+
31
+
32
+ Array * Object - Predicate match
33
+ ================================
34
+
35
+ Warming up --------------------------------------
36
+ Vanilla 148.876k i/100ms
37
+ Qo.and 19.598k i/100ms
38
+ Calculating -------------------------------------
39
+ Vanilla 2.385M (± 1.7%) i/s - 12.059M in 5.058028s
40
+ Qo.and 211.780k (± 1.4%) i/s - 1.078M in 5.090600s
41
+
42
+ Comparison:
43
+ Vanilla: 2384846.4 i/s
44
+ Qo.and: 211780.0 i/s - 11.26x slower
45
+
46
+
47
+ Array * Array - Select index pattern match
48
+ ==========================================
49
+
50
+ Warming up --------------------------------------
51
+ Vanilla 12.366k i/100ms
52
+ Qo.and 20.112k i/100ms
53
+ Calculating -------------------------------------
54
+ Vanilla 128.645k (± 2.1%) i/s - 643.032k in 5.000638s
55
+ Qo.and 207.792k (± 7.2%) i/s - 1.046M in 5.061963s
56
+
57
+ Comparison:
58
+ Qo.and: 207792.4 i/s
59
+ Vanilla: 128645.0 i/s - 1.62x slower
60
+
data/qo.gemspec CHANGED
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "rspec", "~> 3.0"
26
26
  spec.add_development_dependency "guard"
27
27
  spec.add_development_dependency "guard-rspec"
28
+ spec.add_development_dependency "benchmark-ips"
28
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Weaver
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-13 00:00:00.000000000 Z
11
+ date: 2018-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: benchmark-ips
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  description:
84
98
  email:
85
99
  - keystonelemur@gmail.com
@@ -101,10 +115,12 @@ files:
101
115
  - bin/setup
102
116
  - docs/.gitkeep
103
117
  - docs/_config.yml
118
+ - img/qo_logo.png
104
119
  - lib/qo.rb
105
120
  - lib/qo/guard_block_matcher.rb
106
121
  - lib/qo/matcher.rb
107
122
  - lib/qo/version.rb
123
+ - performance_report.txt
108
124
  - qo.gemspec
109
125
  homepage: https://www.github.com/baweaver/q
110
126
  licenses: