cachet 0.0.0

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/README ADDED
@@ -0,0 +1,50 @@
1
+ Just initialize the cache, by configuring your store.
2
+ Just add the configuration to your config initializers
3
+
4
+ require 'cachet'
5
+
6
+ Cachet.setup do |config|
7
+ config.logger = Rails.logger
8
+ config.storage = Cachet::FileStore.new("/pal_storage/path")
9
+
10
+ # The followings are the configurations that you can do on file store
11
+ # The given values are the default ones, if you are OK with them you don't need
12
+ # to configure at all.
13
+ # You can set whether you want directory optimization or not
14
+ # file_store.optimize = TRUE
15
+ # The depth of directories you want
16
+ # file_store.dir_levels = 3
17
+ # Number of directories within a directory, it is better if you use a prime number
18
+ # but something other than 31, that is the one we use for hashing :)
19
+ # file_store.dir_count = 19
20
+ end
21
+
22
+ And from this point forward , if you want return values of your methods to be cached;
23
+
24
+ #include cacheable module , which will add two class macros to mark a method as cacheable and also as cache invalidator.
25
+ You should pass blocks to these marcos which will return cache keys. Use exact signature of the method that you are referring in the block that returns the key.
26
+
27
+
28
+ class Sample
29
+ include Cachet::Cacheable
30
+
31
+ def first (param1, param2)
32
+ puts "First is running"
33
+ return "First"
34
+ end
35
+
36
+
37
+ def second (param1, param2)
38
+ puts "Second is running and invalidating for #{param1}"
39
+ return "First"
40
+ end
41
+
42
+ cacheable :first, :car do |param1, param2|
43
+ param1
44
+ end
45
+
46
+ cache_invalidator :second, :car do |param1, param2|
47
+ param1
48
+ end
49
+
50
+ end
data/lib/cachet.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'rails'
2
+
3
+ module Cachet
4
+ mattr_accessor :logger
5
+ mattr_accessor :storage
6
+
7
+ class << self
8
+ def setup
9
+ yield self
10
+ end
11
+
12
+ def cache (entity, key)
13
+ cached_item = storage.read(entity, key)
14
+ if not cached_item
15
+ cached_item = yield
16
+ storage.write(entity, key, cached_item)
17
+ Cachet.logger.info "#{entity} with key:#{key} could not found in cache. Evaluated and written to cache"
18
+ else
19
+ Cachet.logger.info "#{entity} with key:#{key} found in cache."
20
+ end
21
+ cached_item
22
+ end
23
+
24
+ def invalidate (entity, key)
25
+ storage.purge(entity, key)
26
+ Cachet.logger.info "#{entity} with key:#{key} invalidated."
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ require 'cachet/file_store'
33
+ require 'cachet/cacheable'
34
+
35
+
@@ -0,0 +1,31 @@
1
+ module Cachet
2
+ module Cacheable
3
+ def self.included(base)
4
+ Cachet.logger.debug "Including Cacheable in #{base}"
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def cacheable (cached_method, entity)
10
+ define_method("#{cached_method}_with_cache") { |*args|
11
+ cache_key = block_given? ? yield(*args) : cached_method.to_s
12
+ Cachet.cache(entity, cache_key) do
13
+ method("#{cached_method}_without_cache".to_sym).call(*args)
14
+ end
15
+ }
16
+ alias_method_chain "#{cached_method}".to_sym, :cache
17
+ Cachet.logger.debug "Aliasing method : #{cached_method} to cache the result as #{entity} entities"
18
+ end
19
+
20
+ def cache_invalidator (cached_method, entity)
21
+ define_method("#{cached_method}_with_cache_invalidation") { |*args|
22
+ cache_key = block_given? ? yield(*args) : cached_method.to_s
23
+ method("#{cached_method}_without_cache_invalidation".to_sym).call(*args)
24
+ Cachet.invalidate(entity, cache_key)
25
+ }
26
+ alias_method_chain "#{cached_method}".to_sym, :cache_invalidation
27
+ Cachet.logger.debug "Aliasing method : #{cached_method} to invalidate the #{entity} entities"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ require 'digest/md5'
2
+
3
+ module Cachet
4
+ class FileStore
5
+ attr_reader :root
6
+ attr_accessor :optimize
7
+ attr_accessor :dir_level
8
+ attr_accessor :dir_count
9
+
10
+ def initialize(root)
11
+ @root = root
12
+ FileUtils.mkdir_p root, :mode=>0755
13
+ @optimize = TRUE
14
+ @dir_level = 3
15
+ @dir_count = 19
16
+ end
17
+
18
+ def read(entity, key)
19
+ file_name = storage_path(entity, key)
20
+ File.open(file_name, 'rb') { |f| Marshal.load f.read } if File.exist?(file_name)
21
+ end
22
+
23
+ def write(entity, key, data_to_be_stored)
24
+ file_name = storage_path(entity, key)
25
+ temp_file_name = file_name + '.' + Thread.current.object_id.to_s
26
+ FileUtils.mkdir_p File.dirname(file_name), :mode => 755
27
+ File.open(temp_file_name, 'wb') { |f| f.write(Marshal.dump(data_to_be_stored)) }
28
+ if File.exist?(file_name)
29
+ File.unlink temp_file_name
30
+ else
31
+ FileUtils.mv temp_file_name, file_name
32
+ end
33
+ Cachet.logger.info "A new #{entity} entity stored in the cache with key:#{key} to path #{file_name}."
34
+ end
35
+
36
+ def purge(entity, key)
37
+ file_name = storage_path(entity, key)
38
+ begin
39
+ File.unlink file_name if File.exist?(file_name)
40
+ rescue
41
+ Cachet.logger.warn "An element of #{entity} in the cache with key:#{key} to path #{file_name}entity has could not be deleted."
42
+ end
43
+ Cachet.logger.info "An element of #{entity} in the cache with key:#{key} to path #{file_name}entity has been removed from the cache."
44
+ end
45
+
46
+ def storage_path(entity, key)
47
+ file_name = Digest::MD5.hexdigest(key)
48
+ if optimize
49
+ dirs = (1..dir_level).inject({:hash=>key.sum, :dirs=>[]}) do |result, level|
50
+ result[:dirs] << (result[:hash] % dir_count).abs.to_s
51
+ result[:hash] += (result[:hash] * 31)
52
+ result
53
+ end
54
+ file_name = File.join dirs[:dirs], file_name
55
+ end
56
+ File.join root, entity.to_s, file_name
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,59 @@
1
+ require 'test_helper'
2
+
3
+ class FileStoreTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ FakeFS.activate!
7
+ FakeFS::FileSystem.clear
8
+ @test_store = Cachet::FileStore.new("~/testroot/")
9
+ end
10
+
11
+ def teardown
12
+ FakeFS.deactivate!
13
+ end
14
+
15
+ def test_storage_path_creation
16
+ assert_equal "~/testroot/test/2/7/15/25f9e794323b453885f5181f1b624d0b", @test_store.storage_path(:test, "123456789"), "Check storage path, hashing seems to be not working."
17
+ end
18
+
19
+ def test_idempotent_storage_paths
20
+ assert_equal @test_store.storage_path(:test, "123456789"), @test_store.storage_path(:test, "123456789"), "Should always create same path for same entry."
21
+ end
22
+
23
+ def test_storage_options
24
+ @test_store.dir_level = 10
25
+ assert_equal "~/testroot/test/2/7/15/5/8/9/3/1/13/17/25f9e794323b453885f5181f1b624d0b", @test_store.storage_path(:test, "123456789"), "Should nest resourced under 10 recursive directories."
26
+ @test_store.dir_count = 100
27
+ assert_equal "~/testroot/test/77/64/48/36/52/64/48/36/52/64/25f9e794323b453885f5181f1b624d0b", @test_store.storage_path(:test, "123456789"), "Should allow 100 hundred directories under any directory."
28
+ @test_store.optimize = FALSE
29
+ assert_equal "~/testroot/test/25f9e794323b453885f5181f1b624d0b", @test_store.storage_path(:test, "123456789"), "Should not optimize paths since optimize is disabled."
30
+ end
31
+
32
+ def test_purging_entries
33
+ file_name = @test_store.storage_path(:test, 'test_item_1')
34
+ FileUtils.mkdir_p File.dirname(file_name), :mode => 755
35
+ File.open(file_name, 'wb') { |f| f.write("Data") }
36
+ assert_equal(true, File.exist?(file_name))
37
+ @test_store.purge(:test, 'test_item_1')
38
+ assert_equal(false, File.exist?(file_name))
39
+ end
40
+
41
+ def test_purging_nonexisting_entries
42
+ file_name = @test_store.storage_path(:test, 'test_item_2')
43
+ assert_equal(false, File.exist?(file_name))
44
+ assert_nothing_thrown("FileStore should be slince while purging nonexisting entities") {
45
+ @test_store.purge(:test, 'test_item_2')
46
+ }
47
+ assert_equal(false, File.exist?(file_name))
48
+ end
49
+
50
+ def test_reading_existing_entries
51
+ @test_store.write(:test, 'test_item_3', "Some test data")
52
+ assert_equal("Some test data", @test_store.read(:test, 'test_item_3'))
53
+ end
54
+
55
+ def test_reading_nonexisting_entries
56
+ @test_store.purge(:test, 'test_item_4')
57
+ assert_nil @test_store.read(:test, 'test_item_4')
58
+ end
59
+ end
@@ -0,0 +1,7 @@
1
+ require 'test/unit'
2
+ require 'fakefs/safe'
3
+ require 'cachet'
4
+
5
+ Cachet.setup do |config|
6
+ config.logger = Logger.new(STDOUT)
7
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cachet
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.0
6
+ platform: ruby
7
+ authors:
8
+ - Meltem Atesalp
9
+ - Umut Utkan
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2012-02-07 00:00:00 +02:00
15
+ default_executable:
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: rails
19
+ prerelease: false
20
+ requirement: &id001 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 3.0.0
26
+ type: :runtime
27
+ version_requirements: *id001
28
+ - !ruby/object:Gem::Dependency
29
+ name: fakefs
30
+ prerelease: false
31
+ requirement: &id002 !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: "0"
37
+ type: :development
38
+ version_requirements: *id002
39
+ description: Provides a way to cache and invalidate return values of your time consuming methods.
40
+ email: meltem.atesalp@gmail.com
41
+ executables: []
42
+
43
+ extensions: []
44
+
45
+ extra_rdoc_files: []
46
+
47
+ files:
48
+ - lib/cachet.rb
49
+ - lib/cachet/cacheable.rb
50
+ - lib/cachet/file_store.rb
51
+ - README
52
+ - test/test_file_store.rb
53
+ - test/test_helper.rb
54
+ has_rdoc: true
55
+ homepage: https://github.com/meltem/cachet
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options: []
60
+
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.6.2
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Caches method responses to have a better performance!
82
+ test_files:
83
+ - test/test_file_store.rb
84
+ - test/test_helper.rb