large_object_store 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - ree
3
+ - 1.9.2
4
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+ gemspec
3
+
4
+ gem "bump"
5
+ gem "rake"
6
+ gem "rspec", "~>2"
data/Gemfile.lock ADDED
@@ -0,0 +1,28 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ large_object_store (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ bump (0.3.9)
10
+ diff-lcs (1.1.3)
11
+ rake (10.0.3)
12
+ rspec (2.12.0)
13
+ rspec-core (~> 2.12.0)
14
+ rspec-expectations (~> 2.12.0)
15
+ rspec-mocks (~> 2.12.0)
16
+ rspec-core (2.12.2)
17
+ rspec-expectations (2.12.1)
18
+ diff-lcs (~> 1.1.3)
19
+ rspec-mocks (2.12.2)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ bump
26
+ large_object_store!
27
+ rake
28
+ rspec (~> 2)
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "bump/tasks"
3
+
4
+ task :default do
5
+ sh "rspec spec/"
6
+ end
data/Readme.md ADDED
@@ -0,0 +1,32 @@
1
+ Store large objects in memcache or others by slicing them.
2
+ - uses read_multi for fast access
3
+ - returns nil if one slice is missing
4
+
5
+ Install
6
+ =======
7
+
8
+ ```Bash
9
+ gem install large_object_store
10
+ ```
11
+
12
+ Usage
13
+ =====
14
+
15
+ ```Ruby
16
+ Rails.cache.write("a", "a"*10_000_000) # => false -> oops too large
17
+
18
+ store = LargeObjectStore.wrap(Rails.cache)
19
+ store.write("a", "a"*10_000_000) # => true -> always!
20
+ store.read("a").size # => 10_000_000 using multi_get
21
+ store.read("b") # => nil
22
+ store.fetch("a"){ "something" } # => "something" executes block on miss
23
+ ```
24
+
25
+ Author
26
+ ======
27
+ [Ana Martinez](https://github.com/anamartinez)<br/>
28
+ acemacu@gmail.com<br/>
29
+ [Michael Grosser](https://github.com/grosser)<br/>
30
+ michael@grosser.it<br/>
31
+ License: MIT<br/>
32
+ [![Build Status](https://travis-ci.org/anamartinez/large_object_store.png)](https://travis-ci.org/anamartinez/large_object_store)
@@ -0,0 +1,20 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDMjCCAhqgAwIBAgIBADANBgkqhkiG9w0BAQUFADA/MRAwDgYDVQQDDAdtaWNo
3
+ YWVsMRcwFQYKCZImiZPyLGQBGRYHZ3Jvc3NlcjESMBAGCgmSJomT8ixkARkWAml0
4
+ MB4XDTEzMDIwMzE4MTMxMVoXDTE0MDIwMzE4MTMxMVowPzEQMA4GA1UEAwwHbWlj
5
+ aGFlbDEXMBUGCgmSJomT8ixkARkWB2dyb3NzZXIxEjAQBgoJkiaJk/IsZAEZFgJp
6
+ dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMorXo/hgbUq97+kII9H
7
+ MsQcLdC/7wQ1ZP2OshVHPkeP0qH8MBHGg6eYisOX2ubNagF9YTCZWnhrdKrwpLOO
8
+ cPLaZbjUjljJ3cQR3B8Yn1veV5IhG86QseTBjymzJWsLpqJ1UZGpfB9tXcsFtuxO
9
+ 6vHvcIHdzvc/OUkICttLbH+1qb6rsHUceqh+JrH4GrsJ5H4hAfIdyS2XMK7YRKbh
10
+ h+IBu6dFWJJByzFsYmV1PDXln3UBmgAt65cmCu4qPfThioCGDzbSJrGDGLmw/pFX
11
+ FPpVCm1zgYSb1v6Qnf3cgXa2f2wYGm17+zAVyIDpwryFru9yF/jJxE38z/DRsd9R
12
+ /88CAwEAAaM5MDcwCQYDVR0TBAIwADAdBgNVHQ4EFgQUsiNnXHtKeMYYcr4yJVmQ
13
+ WONL+IwwCwYDVR0PBAQDAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQAlyN7kKo/NQCQ0
14
+ AOzZLZ3WAePvStkCFIJ53tsv5Kyo4pMAllv+BgPzzBt7qi605mFSL6zBd9uLou+W
15
+ Co3s48p1dy7CjjAfVQdmVNHF3MwXtfC2OEyvSQPi4xKR8iba8wa3xp9LVo1PuLpw
16
+ /6DsrChWw74HfsJN6qJOK684hJeT8lBYAUfiC3wD0owoPSg+XtyAAddisR+KV5Y1
17
+ NmVHuLtQcNTZy+gRht3ahJRMuC6QyLmkTsf+6MaenwAMkAgHdswGsJztOnNnBa3F
18
+ y0kCSWmK6D+x/SbfS6r7Ke07MRqziJdB9GuE1+0cIRuFh8EQ+LN6HXCKM5pon/GU
19
+ ycwMXfl0
20
+ -----END CERTIFICATE-----
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+ name = "large_object_store"
3
+ require "#{name.gsub("-","/")}/version"
4
+
5
+ Gem::Specification.new name, LargeObjectStore::VERSION do |s|
6
+ s.summary = "Store large objects in memcache or others"
7
+ s.authors = ["Ana Martinez"]
8
+ s.email = "acemacu@gmail.com"
9
+ s.homepage = "http://github.com/anamartinez/#{name}"
10
+ s.files = `git ls-files`.split("\n")
11
+ s.license = "MIT"
12
+ cert = File.expand_path("~/.ssh/gem-private_key.pem")
13
+ if File.exist?(cert)
14
+ s.signing_key = cert
15
+ s.cert_chain = ["gem-public_cert.pem"]
16
+ end
17
+ end
@@ -0,0 +1,58 @@
1
+ require "large_object_store/version"
2
+
3
+ module LargeObjectStore
4
+
5
+ def self.wrap(store)
6
+ RailsWrapper.new(store)
7
+ end
8
+
9
+ class RailsWrapper
10
+ attr_reader :store
11
+
12
+ LIMIT = 1024**2 - 100
13
+
14
+ def initialize(store)
15
+ @store = store
16
+ end
17
+
18
+ def write(key, value, options = {})
19
+ value = Marshal.dump(value)
20
+
21
+ # store number of pages
22
+ pages = (value.size / LIMIT.to_f).ceil
23
+ @store.write("#{key}_0", pages, options)
24
+
25
+ # store object
26
+ page = 1
27
+ loop do
28
+ slice = value.slice!(0, LIMIT)
29
+ break if slice.size == 0
30
+
31
+ @store.write("#{key}_#{page}", slice, options)
32
+ page += 1
33
+ end
34
+
35
+ true
36
+ end
37
+
38
+ def read(key)
39
+ # read pages
40
+ pages = @store.read("#{key}_0")
41
+ return if pages.nil?
42
+
43
+ # read sliced data
44
+ keys = Array.new(pages).each_with_index.map{|_,i| "#{key}_#{i+1}" }
45
+ slices = @store.read_multi(*keys).values
46
+ return nil if slices.compact.size < pages
47
+ Marshal.load(slices.join(""))
48
+ end
49
+
50
+ def fetch(key, options={})
51
+ value = read(key)
52
+ return value unless value.nil?
53
+ value = yield
54
+ write(key, value, options)
55
+ value
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module LargeObjectStore
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ class TestCache
5
+ def initialize
6
+ @data = {}
7
+ end
8
+
9
+ def write(k,v, options={})
10
+ v = Marshal.dump(v)
11
+ return false if v.bytesize > 1024**2
12
+ @data[k] = v
13
+ true
14
+ end
15
+
16
+ def read(k)
17
+ real_read(k)
18
+ end
19
+
20
+ def real_read(k)
21
+ v = @data[k]
22
+ v.nil? ? nil : Marshal.load(v)
23
+ end
24
+
25
+ def read_multi(*keys)
26
+ Hash[keys.map{|k| [k, real_read(k)] }]
27
+ end
28
+
29
+ def keys
30
+ @data.keys
31
+ end
32
+ end
33
+
34
+ describe LargeObjectStore do
35
+ let(:store) { LargeObjectStore.wrap(TestCache.new) }
36
+
37
+ it "has a VERSION" do
38
+ LargeObjectStore::VERSION.should =~ /^[\.\da-z]+$/
39
+ end
40
+
41
+ it "wraps and returns a wrapper" do
42
+ store.class.should == LargeObjectStore::RailsWrapper
43
+ end
44
+
45
+ it "can write/read big objects" do
46
+ store.write("a", "a"*10_000_000).should == true
47
+ store.read("a").size.should == 10_000_000
48
+ end
49
+
50
+ it "passes options" do
51
+ store.store.should_receive(:write).with(anything, anything, :expires_in => 111).twice
52
+ store.write("a", "a", :expires_in => 111)
53
+ end
54
+
55
+ it "cannot read corrupted objects" do
56
+ store.write("a", ["a"*10_000_000]).should == true
57
+ store.store.write("a_4", nil)
58
+ store.read("a").should == nil
59
+ end
60
+
61
+ it "can write/read big non-string objects" do
62
+ store.write("a", ["a"*10_000_000]).should == true
63
+ store.read("a").first.size.should == 10_000_000
64
+ end
65
+
66
+ it "can read/write objects with encoding" do
67
+ store.write("a", "ß"*10_000_000).should == true
68
+ store.read("a").size.should == 10_000_000
69
+ end
70
+
71
+ it "can write/read giant objects" do
72
+ store.write("a", "a"*100_000_000).should == true
73
+ store.read("a").size.should == 100_000_000
74
+ end
75
+
76
+ it "uses necessary keys" do
77
+ store.write("a", "a"*5_000_000)
78
+ store.store.keys.should == ["a_0", "a_1", "a_2", "a_3", "a_4", "a_5"]
79
+ end
80
+
81
+ it "uses read_multi" do
82
+ store.write("a", "a"*5_000_000)
83
+ store.store.should_receive(:read).with("a_0").and_return 5
84
+ store.read("a").size.should == 5_000_000
85
+ end
86
+
87
+ describe "#fetch" do
88
+ it "executes the block on miss" do
89
+ store.fetch("a"){ 1 }.should == 1
90
+ end
91
+
92
+ it "does not execute the block on hit" do
93
+ store.fetch("a"){ 1 }
94
+ store.fetch("a"){ 2 }.should == 1
95
+ end
96
+
97
+ it "passes the options" do
98
+ store.should_receive(:write).with(anything, anything, :expires_in => 111)
99
+ store.fetch("a", :expires_in => 111){ 2 }
100
+ end
101
+
102
+ it "can fetch false" do
103
+ store.fetch("a"){ false }.should == false
104
+ store.read("a").should == false
105
+ end
106
+ end
107
+ end
@@ -0,0 +1 @@
1
+ require "large_object_store"
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: large_object_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ana Martinez
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-17 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email: acemacu@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - .travis.yml
21
+ - Gemfile
22
+ - Gemfile.lock
23
+ - Rakefile
24
+ - Readme.md
25
+ - gem-public_cert.pem
26
+ - large_object_store.gemspec
27
+ - lib/large_object_store.rb
28
+ - lib/large_object_store/version.rb
29
+ - spec/large_object_store_spec.rb
30
+ - spec/spec_helper.rb
31
+ homepage: http://github.com/anamartinez/large_object_store
32
+ licenses:
33
+ - MIT
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 1.8.25
53
+ signing_key:
54
+ specification_version: 3
55
+ summary: Store large objects in memcache or others
56
+ test_files: []