lookup_by 0.10.0 → 0.10.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
  SHA1:
3
- metadata.gz: b7b482a9755de8d24837b3555d53cc416b77ac87
4
- data.tar.gz: 087b776e7ad8c89b4fcb90a63769820c6279d81e
3
+ metadata.gz: c81a1e0b0e5640594ff736ea71404e85a8bd107c
4
+ data.tar.gz: bd457c39c27f0549c0d5dc136e65e5685cbad2a7
5
5
  SHA512:
6
- metadata.gz: 2d728f55c1da30eb53de8d33397bb4b6fbe1c1d1b9003d03f11db296d34455c65d5a0f16058f904b298b9050cc5b86ee69d88223cb942c6ff9419dadf34e2501
7
- data.tar.gz: 62479abc739d5125a2feffa8589236f4e1f83fd9a391b343bb5f49ac3510dd05293c221cb4b3521362a659804a021ce7a114556d045fc2644a223faa88a7afd9
6
+ metadata.gz: 3d55c28857d8e2f6556e1090435fadaf392d82f5f2d7b9d27b19acb620d1a520dea2a2009db2c8966dcdf3ab2681f6e5a94cee6a3641de0fa1e4da027bd4c29b
7
+ data.tar.gz: f3e0bd3c59d65ed5860d3b92220f7a4804b6229ef26b8539b844a4545709c0e9a380986e58412556a0da87a43a20d975456a85432c4e5053062bd01d58f68686
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.2.0
1
+ ruby-2.2.1
data/.travis.yml CHANGED
@@ -1,9 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.9.3-p551
4
- - 2.0.0-p598
4
+ - 2.0.0-p643
5
5
  - 2.1.5
6
- - 2.2.0
6
+ - 2.2.1
7
7
  - rbx-2.5.2
8
8
  services:
9
9
  - postgresql
@@ -202,16 +202,14 @@ module LookupBy
202
202
  return unless object
203
203
 
204
204
  @mutex.synchronize do
205
- @cache[object.id] = object
206
- @reverse[object.send(@field)] = object
205
+ @reverse[object.send(@field)] = @cache[object.id] = object
207
206
  end
208
207
  end
209
208
  else
210
209
  def cache_write(object)
211
210
  return unless object
212
211
 
213
- @cache[object.id] = object
214
- @reverse[object.send(@field)] = object
212
+ @reverse[object.send(@field)] = @cache[object.id] = object
215
213
  end
216
214
  end
217
215
 
@@ -1,56 +1,111 @@
1
1
  module LookupBy
2
2
  module Caching
3
3
  class LRU
4
- attr_reader :lru
4
+ # In Ruby 1.9+, Hash is ordered.
5
+ #
6
+ # http://bugs.ruby-lang.org/issues/8312
7
+ # In Ruby 2.0, Hash#shift is bugged and horribly slow.
8
+ #
9
+ # require 'benchmark/ips'
10
+ # hash, i, N = {}, 0, 20_000_000; while i < N; hash[i] = true; i += 1; end
11
+ #
12
+ # Benchmark.ips do |x|
13
+ # x.report("shift") { hash.shift }
14
+ # x.report("first") { hash.delete hash.first[0] }
15
+ # end
16
+ #
17
+ # Ruby | shift | first
18
+ # -----------+-------+------
19
+ # 1.9.3-p484 | 264k | 89k
20
+ # 1.9.3-p551 | 945k | 370k
21
+ # 2.0.0-p0 | 0k | 70k
22
+ # 2.0.0-p643 | 0k | 272k
23
+ # 2.1.5 | 4947k | 3557k
24
+ # 2.2.1 | 6801k | 4457k
25
+ # rbx-2.5.2 | 609k | 409k
5
26
 
6
- def initialize(maxsize)
7
- @maxsize = maxsize
8
- @lru = []
9
- @hash = {}
27
+ def initialize(max_size)
28
+ @data = {}
29
+ self.max_size = max_size
30
+ end
31
+
32
+ def max_size=(size)
33
+ raise ArgumentError.new(:maxsize) if size < 1
34
+
35
+ @max_size = size
36
+
37
+ @data.shift while @data.length > @max_size
10
38
  end
11
39
 
12
40
  def [](key)
13
- return nil unless @hash.key?(key)
14
- touch(key)
15
- @hash[key]
41
+ found = true
42
+ value = @data.delete(key) { found = false }
43
+
44
+ @data[key] = value if found
16
45
  end
17
46
 
18
47
  def []=(key, value)
19
- touch(key)
20
- @hash[key] = value
21
- @hash.delete(@lru.shift) while @hash.size > @maxsize
48
+ @data.delete(key)
49
+ @data[key] = value
50
+ @data.shift if @data.length > @max_size
51
+ end
52
+
53
+ def clear
54
+ @data.clear
55
+ end
56
+
57
+ def count
58
+ @data.length
22
59
  end
23
60
 
24
61
  def delete(key)
25
- @lru.delete(key)
62
+ @data.delete(key)
63
+ end
26
64
 
27
- @hash.delete(key)
65
+ def each
66
+ @data.to_a.each do |pair|
67
+ yield pair
68
+ end
28
69
  end
29
70
 
30
- def clear
31
- @lru.clear
32
- @hash.clear
33
- self
71
+ def fetch(key)
72
+ found = true
73
+ value = @data.delete(key) { found = false }
74
+
75
+ if found
76
+ @data[key] = value
77
+ elsif block_given?
78
+ value = @data[key] = yield key
79
+ @data.shift if @data.length > @max_size
80
+ value
81
+ else
82
+ raise KeyError, "key not found: %p" % [key]
83
+ end
34
84
  end
35
85
 
36
- def values
37
- @hash.values
86
+ def key?(key)
87
+ @data.key?(key)
38
88
  end
39
89
 
40
90
  def size
41
- @hash.size
91
+ @data.size
92
+ end
93
+
94
+ def to_a
95
+ @data.to_a
42
96
  end
43
97
 
44
98
  def to_h
45
- @hash.dup
99
+ @data.dup
46
100
  end
47
101
 
48
- private
49
- def touch(key)
50
- # TODO: LRU deletes are O(N)
51
- @lru.delete(key)
52
- @lru << key
102
+ def values
103
+ @data.values
53
104
  end
54
105
  end
55
106
  end
56
107
  end
108
+
109
+ if RUBY_ENGINE == "ruby" && RUBY_VERSION == "2.0.0"
110
+ require "lookup_by/caching/lru_legacy"
111
+ end
@@ -0,0 +1,40 @@
1
+ module LookupBy
2
+ module Caching
3
+ class LRU
4
+ def max_size=(size)
5
+ raise ArgumentError.new(:max_size) if size < 1
6
+
7
+ @max_size = size
8
+
9
+ if @max_size < @data.length
10
+ @data.keys[0..@max_size-@data.size].each do |key|
11
+ @data.delete(key)
12
+ end
13
+ end
14
+ end
15
+
16
+ def []=(key, value)
17
+ @data.delete(key)
18
+ @data[key] = value
19
+
20
+ # See: http://bugs.ruby-lang.org/issues/8312
21
+ @data.delete(@data.first[0]) if @data.length > @max_size
22
+ end
23
+
24
+ def fetch(key)
25
+ found = true
26
+ value = @data.delete(key) { found = false }
27
+
28
+ if found
29
+ @data[key] = value
30
+ elsif block_given?
31
+ value = @data[key] = yield key
32
+ @data.delete(@data.first[0]) if @data.length > @max_size
33
+ value
34
+ else
35
+ raise KeyError, "key not found: %p" % [key]
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -7,7 +7,7 @@ module LookupBy
7
7
  class SafeLRU < LRU
8
8
  include Mutex_m
9
9
 
10
- def initialize(maxsize = nil)
10
+ def initialize(max_size)
11
11
  super
12
12
  end
13
13
 
@@ -19,15 +19,27 @@ module LookupBy
19
19
  synchronize { super }
20
20
  end
21
21
 
22
+ def clear
23
+ synchronize { super }
24
+ end
25
+
26
+ def count
27
+ synchronize { super }
28
+ end
29
+
22
30
  def delete(key)
23
31
  synchronize { super }
24
32
  end
25
33
 
26
- def clear
34
+ def each
27
35
  synchronize { super }
28
36
  end
29
37
 
30
- def values
38
+ def fetch(key)
39
+ synchronize { super }
40
+ end
41
+
42
+ def key?(key)
31
43
  synchronize { super }
32
44
  end
33
45
 
@@ -35,9 +47,17 @@ module LookupBy
35
47
  synchronize { super }
36
48
  end
37
49
 
50
+ def to_a
51
+ synchronize { super }
52
+ end
53
+
38
54
  def to_h
39
55
  synchronize { super }
40
56
  end
57
+
58
+ def values
59
+ synchronize { super }
60
+ end
41
61
  end
42
62
  end
43
63
  end
@@ -1,3 +1,3 @@
1
1
  module LookupBy
2
- VERSION = "0.10.0"
2
+ VERSION = "0.10.1"
3
3
  end
@@ -41,18 +41,52 @@ describe LookupBy::Caching::LRU do
41
41
  end
42
42
 
43
43
  describe "#clear" do
44
- specify { expect(cache.clear.size).to eq 0 }
44
+ specify { expect(cache.clear).to be_empty }
45
+ end
46
+
47
+ describe "#count" do
48
+ specify { expect(cache.count).to eq 2 }
49
+ end
50
+
51
+ describe "#delete" do
52
+ specify { expect(cache.delete(1)).to eq "one" }
53
+ specify { expect(cache.delete(3)).to be_nil }
54
+ end
55
+
56
+ describe "#fetch" do
57
+ specify { expect(cache.fetch(1)).to eq "one" }
58
+ specify { expect(cache.fetch(3) { |key| key }).to eq 3 }
59
+ specify { expect { cache.fetch(3) }.to raise_error }
60
+
61
+ it "writes missing values" do
62
+ expect(cache.fetch(3) { "missing" }).to eq "missing"
63
+ expect(cache.fetch(3)).to eq "missing"
64
+ expect(cache[1]).to be_nil
65
+ end
66
+ end
67
+
68
+ describe "#key?" do
69
+ specify { expect(cache.key?(1)).to eq true }
70
+ specify { expect(cache.key?(3)).to eq false }
71
+ end
72
+
73
+ describe "#max_size=" do
74
+ specify { expect { cache.max_size = -1 }.to raise_error }
45
75
  end
46
76
 
47
77
  describe "#size" do
48
78
  specify { expect(cache.size).to eq 2 }
49
79
  end
50
80
 
51
- describe "#values" do
52
- specify { expect(cache.values).to eq ["one", "two"] }
81
+ describe "#to_a" do
82
+ specify { expect(cache.to_a).to eq [[1, "one"], [2, "two"]] }
53
83
  end
54
84
 
55
85
  describe "#to_h" do
56
86
  specify { expect(cache.to_h).to eq(1 => "one", 2 => "two") }
57
87
  end
88
+
89
+ describe "#values" do
90
+ specify { expect(cache.values).to eq ["one", "two"] }
91
+ end
58
92
  end
data/spec/spec_helper.rb CHANGED
@@ -82,4 +82,26 @@ RSpec.configure do |config|
82
82
  config.after(:each) do
83
83
  LookupBy.clear
84
84
  end
85
+
86
+ if RUBY_ENGINE == 'rbx' && ENV['PROFILE']
87
+ require 'rubinius/profiler'
88
+
89
+ profiler = nil
90
+
91
+ config.before(:suite) do
92
+ profiler = Rubinius::Profiler::Instrumenter.new
93
+ end
94
+
95
+ config.before(:each) do
96
+ profiler.start
97
+ end
98
+
99
+ config.after(:each) do
100
+ profiler.stop
101
+ end
102
+
103
+ config.after(:suite) do
104
+ profiler.show
105
+ end
106
+ end
85
107
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lookup_by
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Peterson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-19 00:00:00.000000000 Z
11
+ date: 2015-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -76,6 +76,7 @@ files:
76
76
  - lib/lookup_by/association.rb
77
77
  - lib/lookup_by/cache.rb
78
78
  - lib/lookup_by/caching/lru.rb
79
+ - lib/lookup_by/caching/lru_legacy.rb
79
80
  - lib/lookup_by/caching/safe_lru.rb
80
81
  - lib/lookup_by/cucumber.rb
81
82
  - lib/lookup_by/hooks/cucumber.rb
@@ -152,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
153
  version: '0'
153
154
  requirements: []
154
155
  rubyforge_project:
155
- rubygems_version: 2.4.5
156
+ rubygems_version: 2.4.6
156
157
  signing_key:
157
158
  specification_version: 4
158
159
  summary: A thread-safe lookup table cache for ActiveRecord