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 ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ group :spec do
4
+ gem 'dalli'
5
+ gem 'rspec'
6
+ end
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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Cache2base
2
+ VERSION = '0.0.5'
3
+ end
data/lib/cache2base.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'digest/sha1'
2
+ require 'cache2base/core.rb'
3
+ require 'cache2base/version.rb'
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
+