lookup_by 0.10.0 → 0.10.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
  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