cache2base 0.0.5
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.
- data/Gemfile +6 -0
- data/History.md +0 -0
- data/LICENSE +20 -0
- data/README.md +57 -0
- data/Rakefile +30 -0
- data/cache2base.gemspec +26 -0
- data/lib/cache2base/core.rb +253 -0
- data/lib/cache2base/version.rb +3 -0
- data/lib/cache2base.rb +3 -0
- metadata +88 -0
data/Gemfile
ADDED
data/History.md
ADDED
File without changes
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Jason Pearlman
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
cache2base
|
2
|
+
==========
|
3
|
+
|
4
|
+
cache2base is a high performance Ruby orm for memcache and membase. Just set your models up and stop worrying about keys!
|
5
|
+
Also supports maintaining collections of keys for you.
|
6
|
+
|
7
|
+
Installation and Usage
|
8
|
+
------------------------
|
9
|
+
|
10
|
+
gem install cache2base
|
11
|
+
require 'cache2base'
|
12
|
+
Cache2base.init!(:server => Dalli::Client.new('localhost:11211'))
|
13
|
+
|
14
|
+
And create a model:
|
15
|
+
|
16
|
+
class MyModel4
|
17
|
+
include Cache2base
|
18
|
+
set_basename 'mm4'
|
19
|
+
set_ttl 300 # 300 seconds
|
20
|
+
set_fields :user_id, :first_name, :last_name
|
21
|
+
|
22
|
+
set_primary_key :user_id
|
23
|
+
member_of_collection :first_name, :hash_key => true # People with the same first name!
|
24
|
+
end
|
25
|
+
|
26
|
+
And use that model:
|
27
|
+
|
28
|
+
m = MyModel4.new(:last_name => "lname", :user_id => 5) # creates an in-memory instance
|
29
|
+
m.first_name = 'fname' # all set_fields are given accessors
|
30
|
+
m.save # saves to memcache/base
|
31
|
+
|
32
|
+
m2 = MyModel4.create(:last_name => "lname2", :first_name => 'fname', :user_id => 6) # auto create (.new, .save shortcut)
|
33
|
+
|
34
|
+
fnames = MyModel4.all(:first_name => "fname") # returns array of model instances that share the same first name
|
35
|
+
#=> [#<MyModel4:0x10133cd98 @last_name="lname", @new_instance=false, @first_name="fname", @user_id=5>,
|
36
|
+
#=> #<MyModel4:0x10133c4d8 @last_name="lname2", @new_instance=false, @first_name="fname", @user_id=6>]
|
37
|
+
|
38
|
+
m.delete # delete the first one
|
39
|
+
|
40
|
+
MyModel4.all(:first_name => "fname") #=> [#<MyModel4:0x101264560 @last_name="lname2", @new_instance=false, @first_name="fname", @user_id=6>]
|
41
|
+
|
42
|
+
Thanks
|
43
|
+
------------
|
44
|
+
|
45
|
+
Mike Perham and the dalli project for making the best ruby memcached/membase library - [Dalli](https://github.com/mperham/dalli)
|
46
|
+
|
47
|
+
OMGPOP for providing a great environment for interesting ruby development - [OMGPOP](http://www.omgpop.com)
|
48
|
+
|
49
|
+
Author
|
50
|
+
------------
|
51
|
+
|
52
|
+
Jason Pearlman, jason@omgpop.com / crash2burn@gmail.com
|
53
|
+
|
54
|
+
Copyright
|
55
|
+
-----------
|
56
|
+
|
57
|
+
Copyright (c) 2011 Jason Pearlman. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
CLEAN.include "**/*.rbc"
|
3
|
+
CLEAN.include "**/.DS_Store"
|
4
|
+
CLEAN.include "cache2base-*.gem"
|
5
|
+
|
6
|
+
require File.expand_path("../lib/cache2base/version", __FILE__)
|
7
|
+
|
8
|
+
NAME = 'cache2base'
|
9
|
+
|
10
|
+
# Gem Packaging and Release
|
11
|
+
|
12
|
+
desc "Packages cache2base"
|
13
|
+
task :package=>[:clean] do |p|
|
14
|
+
sh %{gem build cache2base.gemspec}
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Install cache2base gem"
|
18
|
+
task :install=>[:package] do
|
19
|
+
sh %{sudo gem install ./#{NAME}-#{Cache2base::VERSION} --local}
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Uninstall cache2base gem"
|
23
|
+
task :uninstall=>[:clean] do
|
24
|
+
sh %{sudo gem uninstall #{NAME}}
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Upload cache2base gem to gemcutter"
|
28
|
+
task :release=>[:package] do
|
29
|
+
sh %{gem push ./#{NAME}-#{Cache2base::VERSION}.gem}
|
30
|
+
end
|
data/cache2base.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require './lib/cache2base/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{cache2base}
|
5
|
+
s.version = Cache2base::VERSION
|
6
|
+
|
7
|
+
s.authors = ["Jason Pearlman"]
|
8
|
+
s.date = Time.now.utc.strftime("%Y-%m-%d")
|
9
|
+
s.description = %q{A ruby orm for memcache and membase}
|
10
|
+
s.email = %q{crash2burn@gmail.com}
|
11
|
+
s.files = Dir.glob("lib/**/*") + [
|
12
|
+
"LICENSE",
|
13
|
+
"README.md",
|
14
|
+
"History.md",
|
15
|
+
"Rakefile",
|
16
|
+
"Gemfile",
|
17
|
+
"cache2base.gemspec"
|
18
|
+
]
|
19
|
+
s.homepage = %q{http://github.com/OMGPOP/cache2base}
|
20
|
+
s.has_rdoc = false
|
21
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
s.summary = %q{A ruby orm for memcache and membase}
|
24
|
+
s.test_files = Dir.glob("test/**/*")
|
25
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
26
|
+
end
|
@@ -0,0 +1,253 @@
|
|
1
|
+
module Cache2base
|
2
|
+
def self.included(klass) # :nodoc:
|
3
|
+
klass.class_eval "@basename ||= self.to_s"
|
4
|
+
klass.class_eval "@ttl ||= 0"
|
5
|
+
klass.class_eval "@collections ||= []"
|
6
|
+
klass.class_eval "@server ||= Cache2base.server"
|
7
|
+
klass.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.init!(params = {})
|
11
|
+
@server = params[:server]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.server
|
15
|
+
@server||MEMBASE
|
16
|
+
end
|
17
|
+
|
18
|
+
def server
|
19
|
+
self.class.server
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid_primary_key?
|
23
|
+
self.class.primary_key.each { |f| return false if self.send(f).nil? }
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(hsh = {}, params = {})
|
28
|
+
@new_instance = params[:new_instance].nil? ? true : params[:new_instance]
|
29
|
+
hsh.each_pair do |k,v|
|
30
|
+
self.send(:"#{k}=", v)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def new?
|
35
|
+
@new_instance
|
36
|
+
end
|
37
|
+
|
38
|
+
def save
|
39
|
+
raise "Invalid Primary Key" unless valid_primary_key?
|
40
|
+
add_to_collections
|
41
|
+
result = @new_instance ? server.add(self.key, self.marshal, self.class.ttl) : server.set(self.key, self.marshal, self.class.ttl)
|
42
|
+
raise 'Duplicate Primary Key' unless result
|
43
|
+
@new_instance = false
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete
|
48
|
+
remove_from_collections
|
49
|
+
server.delete(self.key)
|
50
|
+
end
|
51
|
+
|
52
|
+
def marshal
|
53
|
+
Marshal.dump(self.field_hash)
|
54
|
+
end
|
55
|
+
|
56
|
+
def field_hash
|
57
|
+
o = {}
|
58
|
+
self.class.fields.each do |field|
|
59
|
+
o[field] = self.send(field) if !self.send(field).nil?
|
60
|
+
end
|
61
|
+
o
|
62
|
+
end
|
63
|
+
|
64
|
+
def collection_key(field)
|
65
|
+
self.class.collection_key(Hash[Array(field).collect {|f| [f, self.send(f)]}])
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_to_collections
|
69
|
+
self.class.collections.each do |field|
|
70
|
+
raise "Could not add field #{field} collection" unless add_to_collection(field)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def add_to_collection(field, loops = 0)
|
75
|
+
Array(field).each { |f| return 'could_not_add' if self.send(f).nil? } # still evaluates to true, so add_to_collections does not fail
|
76
|
+
success = server.cas(collection_key(field), self.class.ttl) do |value|
|
77
|
+
value << self.key unless value.include?(self.key)
|
78
|
+
value
|
79
|
+
end
|
80
|
+
|
81
|
+
unless success
|
82
|
+
if success.nil?
|
83
|
+
success = server.add(collection_key(field), [self.key], self.class.ttl)
|
84
|
+
if success
|
85
|
+
return true
|
86
|
+
else
|
87
|
+
return loops < 5 ? add_to_collection(field, loops+1) : false
|
88
|
+
end
|
89
|
+
else
|
90
|
+
return loops < 5 ? add_to_collection(field, loops+1) : false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
success
|
95
|
+
end
|
96
|
+
|
97
|
+
def remove_from_collections
|
98
|
+
self.class.collections.each do |field|
|
99
|
+
raise "Could not remove field #{field} collection" unless remove_from_collection(field)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def remove_from_collection(field, loops=0)
|
104
|
+
Array(field).each { |f| return 'could_not_add' if self.send(f).nil? }
|
105
|
+
#return 'could_not_remove' if self.send(field).nil? # still evaluates to true, so remove_from_collections does not fail
|
106
|
+
success = server.cas(collection_key(field), self.class.ttl) do |value|
|
107
|
+
value.delete(self.key)
|
108
|
+
value
|
109
|
+
end
|
110
|
+
|
111
|
+
unless success
|
112
|
+
if success.nil?
|
113
|
+
return true # return true because theres no collection to remove from
|
114
|
+
else
|
115
|
+
return loops < 5 ? remove_from_collection(field, loops+1) : false # race conditions
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
success
|
120
|
+
end
|
121
|
+
|
122
|
+
module ClassMethods
|
123
|
+
def ttl
|
124
|
+
@ttl
|
125
|
+
end
|
126
|
+
|
127
|
+
def set_ttl(i)
|
128
|
+
@ttl = i.to_i
|
129
|
+
end
|
130
|
+
|
131
|
+
def set_basename(name)
|
132
|
+
@basename = name.to_s
|
133
|
+
end
|
134
|
+
|
135
|
+
def primary_key
|
136
|
+
@primary_key
|
137
|
+
end
|
138
|
+
|
139
|
+
def server
|
140
|
+
@server
|
141
|
+
end
|
142
|
+
|
143
|
+
def set_primary_key(mk, params = {})
|
144
|
+
@primary_key = Array(mk)
|
145
|
+
#o = '#{self.class}'
|
146
|
+
#c = "#{self}"
|
147
|
+
#h = "#{self}"
|
148
|
+
o = []
|
149
|
+
c = []
|
150
|
+
h = []
|
151
|
+
Array(mk).each_with_index do |v, i|
|
152
|
+
o << '#{self.send(:'+v.to_s+').to_s.gsub(\'_\',\'-\')}'
|
153
|
+
c << '#{Array(pk)['+i.to_s+'].to_s.gsub(\'_\',\'-\')}'
|
154
|
+
h << '#{pk[0][:'+v.to_s+'].to_s.gsub(\'_\',\'-\')}'
|
155
|
+
end
|
156
|
+
|
157
|
+
o = "#{@basename}_\#{#{params[:hash_key] ? "self.class.hash_key(\"#{o.join("_")}\")" : "\"#{o.join("_")}\""}}"
|
158
|
+
c = "#{@basename}_\#{#{params[:hash_key] ? "hash_key(\"#{c.join("_")}\")" : "\"#{c.join("_")}\""}}"
|
159
|
+
h = "#{@basename}_\#{#{params[:hash_key] ? "hash_key(\"#{h.join("_")}\")" : "\"#{h.join("_")}\""}}"
|
160
|
+
|
161
|
+
class_eval "def key; \"#{o}\"; end"
|
162
|
+
class_eval "def self.key(*pk); pk.first.is_a?(Hash) ? \"#{h}\" : \"#{c}\"; end"
|
163
|
+
end
|
164
|
+
|
165
|
+
def basename
|
166
|
+
@basename
|
167
|
+
end
|
168
|
+
|
169
|
+
def hash_key(k)
|
170
|
+
Digest::SHA1.hexdigest(k.to_s)
|
171
|
+
end
|
172
|
+
|
173
|
+
def set_fields(*fields)
|
174
|
+
@fields = @fields ? (@fields + (fields)) : (fields)
|
175
|
+
fields.each do |field|
|
176
|
+
class_eval "attr_accessor :#{field}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def set_field(field, params)
|
181
|
+
@fields ||= []
|
182
|
+
@fields << field
|
183
|
+
@field_meta ||= {}
|
184
|
+
if params[:hash]
|
185
|
+
@field_meta[field] ||= {}
|
186
|
+
@field_meta[field][:hash] = true
|
187
|
+
end
|
188
|
+
class_eval "attr_accessor :#{field}"
|
189
|
+
end
|
190
|
+
|
191
|
+
def uses_hash?(field)
|
192
|
+
@field_meta[field] && @field_meta[field][:hash]
|
193
|
+
end
|
194
|
+
|
195
|
+
def member_of_collection(fields, params = {})
|
196
|
+
fields = Array(fields).sort { |a,b| a.to_s <=> b.to_s }
|
197
|
+
@collections ||= []
|
198
|
+
@collections << fields
|
199
|
+
@hashed_collections ||= {}
|
200
|
+
@hashed_collections[fields.join(",").to_s] = true if params[:hash_key]
|
201
|
+
end
|
202
|
+
|
203
|
+
def collections
|
204
|
+
@collections
|
205
|
+
end
|
206
|
+
|
207
|
+
def collection_key(vhsh)
|
208
|
+
keys = vhsh.keys.sort {|a,b| a.to_s <=> b.to_s}
|
209
|
+
"#{@basename}_c_#{hash_collection?(keys) ? hash_key(keys.collect {|field| vhsh[field].to_s.gsub('_','-') }.join("_")) : keys.collect {|field| vhsh[field].to_s.gsub('_','-') }.join("_")}"
|
210
|
+
end
|
211
|
+
|
212
|
+
def fields
|
213
|
+
@fields
|
214
|
+
end
|
215
|
+
|
216
|
+
def hash_collection?(field)
|
217
|
+
@hashed_collections[Array(field).join(',').to_s]
|
218
|
+
end
|
219
|
+
|
220
|
+
def find(fields, params = {})
|
221
|
+
o = server.get(key(fields))
|
222
|
+
return nil unless o
|
223
|
+
self.from_hash(Marshal.load(o))
|
224
|
+
end
|
225
|
+
|
226
|
+
def find_by_key(key)
|
227
|
+
o = server.get(key)
|
228
|
+
return nil unless o
|
229
|
+
self.from_hash(Marshal.load(o))
|
230
|
+
end
|
231
|
+
|
232
|
+
def find_by_keys(keys)
|
233
|
+
hsh = server.get_multi(keys)
|
234
|
+
keys.collect do |key| # to get it back in order since get_multi results in a hash
|
235
|
+
self.from_hash(Marshal.load(hsh[key]))
|
236
|
+
end.compact
|
237
|
+
end
|
238
|
+
|
239
|
+
def from_hash(hsh)
|
240
|
+
self.new(hsh, :new_instance => false)
|
241
|
+
end
|
242
|
+
|
243
|
+
def create(params)
|
244
|
+
o = self.new(params)
|
245
|
+
o.save
|
246
|
+
end
|
247
|
+
|
248
|
+
def all(fields, params = {})
|
249
|
+
arr = server.get(collection_key(fields))
|
250
|
+
find_by_keys(Array(arr)).compact
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
data/lib/cache2base.rb
ADDED
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cache2base
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jason Pearlman
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-05-23 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
description: A ruby orm for memcache and membase
|
36
|
+
email: crash2burn@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- lib/cache2base/core.rb
|
45
|
+
- lib/cache2base/version.rb
|
46
|
+
- lib/cache2base.rb
|
47
|
+
- LICENSE
|
48
|
+
- README.md
|
49
|
+
- History.md
|
50
|
+
- Rakefile
|
51
|
+
- Gemfile
|
52
|
+
- cache2base.gemspec
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://github.com/OMGPOP/cache2base
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options:
|
59
|
+
- --charset=UTF-8
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
hash: 3
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
requirements: []
|
81
|
+
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.3.7
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: A ruby orm for memcache and membase
|
87
|
+
test_files: []
|
88
|
+
|