ngmoco-cache-money 0.2.10 → 0.2.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,4 +9,10 @@ ActiveRecord::Schema.define(:version => 2) do
9
9
  t.integer "story_id"
10
10
  t.string "name"
11
11
  end
12
+
13
+ create_table :sessions, :force => true do |t|
14
+ t.string :session_id
15
+ t.text :data
16
+ t.timestamps
17
+ end
12
18
  end
@@ -1,8 +1,5 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
-
3
- require 'rubygems'
4
- require 'activesupport'
5
- require 'activerecord'
1
+ require 'active_support'
2
+ require 'active_record'
6
3
 
7
4
  require 'cash/lock'
8
5
  require 'cash/transactional'
@@ -27,9 +24,21 @@ require 'cash/util/marshal'
27
24
 
28
25
  class ActiveRecord::Base
29
26
  def self.is_cached(options = {})
30
- options.assert_valid_keys(:ttl, :repository, :version)
31
- include Cash unless ancestors.include?(Cash)
32
- Cash::Config.create(self, options)
27
+ if options == false
28
+ include NoCash
29
+ else
30
+ options.assert_valid_keys(:ttl, :repository, :version)
31
+ include Cash unless ancestors.include?(Cash)
32
+ Cash::Config.create(self, options)
33
+ end
34
+ end
35
+
36
+ def <=>(other)
37
+ if self.id == other.id then
38
+ 0
39
+ else
40
+ self.id < other.id ? -1 : 1
41
+ end
33
42
  end
34
43
  end
35
44
 
@@ -49,7 +58,27 @@ module Cash
49
58
  end
50
59
 
51
60
  def transaction_with_cache_transaction(&block)
52
- repository.transaction { transaction_without_cache_transaction(&block) }
61
+ if cache_config
62
+ repository.transaction { transaction_without_cache_transaction(&block) }
63
+ else
64
+ transaction_without_cache_transaction(&block)
65
+ end
66
+ end
67
+
68
+ def cacheable?(*args)
69
+ true
53
70
  end
54
71
  end
55
72
  end
73
+ module NoCash
74
+ def self.included(active_record_class)
75
+ active_record_class.class_eval do
76
+ extend ClassMethods
77
+ end
78
+ end
79
+ module ClassMethods
80
+ def cachable?(*args)
81
+ false
82
+ end
83
+ end
84
+ end
@@ -80,15 +80,12 @@ module Cash
80
80
  end
81
81
  end
82
82
 
83
- def method_missing(method, *args, &block)
84
- @cache.send(method, *args, &block)
85
- end
86
-
87
83
  def respond_to?(method)
88
84
  @cache.respond_to?(method)
89
85
  end
90
86
 
91
87
  protected
88
+
92
89
  def perform_commands
93
90
  @commands.each do |command|
94
91
  command.call(@cache)
@@ -98,6 +95,12 @@ module Cash
98
95
  def buffer_command(command)
99
96
  @commands << command
100
97
  end
98
+
99
+ private
100
+
101
+ def method_missing(method, *args, &block)
102
+ @cache.send(method, *args, &block)
103
+ end
101
104
  end
102
105
 
103
106
  class NestedBuffered < Buffered
@@ -15,8 +15,11 @@ module Cash
15
15
  module ClassMethods
16
16
  def self.extended(a_class)
17
17
  class << a_class
18
- attr_reader :cache_config
19
- delegate :repository, :indices, :to => :@cache_config
18
+ def cache_config
19
+ @cache_config ? @cache_config : superclass.cache_config
20
+ end
21
+
22
+ delegate :repository, :indices, :to => :cache_config
20
23
  alias_method_chain :inherited, :cache_config
21
24
  end
22
25
  end
@@ -52,7 +55,7 @@ module Cash
52
55
  end
53
56
 
54
57
  def ttl
55
- @options[:ttl] || 0
58
+ @ttl ||= @options[:ttl] || repository.default_ttl || 1.day
56
59
  end
57
60
 
58
61
  def version
@@ -7,6 +7,7 @@ module Cash
7
7
  DEFAULT_OPTIONS = { :ttl => 1.day }
8
8
 
9
9
  def initialize(config, active_record, attributes, options = {})
10
+ DEFAULT_OPTIONS[:ttl] = config.ttl || DEFAULT_OPTIONS[:ttl]
10
11
  @config, @active_record, @attributes, @options = config, active_record, Array(attributes).collect(&:to_s).sort, DEFAULT_OPTIONS.merge(options)
11
12
  end
12
13
 
@@ -87,7 +88,11 @@ module Cash
87
88
  new_attribute_value_pairs = []
88
89
  @attributes.each do |name|
89
90
  new_value = object.attributes[name]
90
- original_value = object.send("#{name}_was")
91
+ if object.changed.include? name
92
+ original_value = object.send("#{name}_was")
93
+ else
94
+ original_value = new_value
95
+ end
91
96
  old_attribute_value_pairs << [name, original_value]
92
97
  new_attribute_value_pairs << [name, new_value]
93
98
  end
@@ -12,12 +12,6 @@ module Cash
12
12
  ensure
13
13
  @remote_cache = original_cache
14
14
  end
15
-
16
- def method_missing(method, *args, &block)
17
- autoload_missing_constants do
18
- @remote_cache.send(method, *args, &block)
19
- end
20
- end
21
15
 
22
16
  def autoload_missing_constants
23
17
  yield if block_given?
@@ -29,6 +23,14 @@ module Cash
29
23
  raise error
30
24
  end
31
25
  end
26
+
27
+ private
28
+
29
+ def method_missing(method, *args, &block)
30
+ autoload_missing_constants do
31
+ @remote_cache.send(method, *args, &block)
32
+ end
33
+ end
32
34
  end
33
35
 
34
36
  class LocalBuffer
@@ -65,6 +67,8 @@ module Cash
65
67
  @local_cache.delete(key)
66
68
  end
67
69
 
70
+ private
71
+
68
72
  def method_missing(method, *args, &block)
69
73
  @remote_cache.send(method, *args, &block)
70
74
  end
@@ -1,3 +1,5 @@
1
+ require 'socket'
2
+
1
3
  module Cash
2
4
  class Lock
3
5
  class Error < RuntimeError; end
@@ -25,12 +27,13 @@ module Cash
25
27
 
26
28
  def acquire_lock(key, lock_expiry = DEFAULT_EXPIRY, retries = DEFAULT_RETRY, initial_wait = INITIAL_WAIT)
27
29
  retries.times do |count|
28
- response = @cache.add("lock/#{key}", Process.pid, lock_expiry)
30
+ response = @cache.add("lock/#{key}", host_pid, lock_expiry)
29
31
  return if response == "STORED\r\n"
32
+ return if recursive_lock?(key)
30
33
  exponential_sleep(count, initial_wait) unless count == retries - 1
31
34
  end
32
35
  debug_lock(key)
33
- raise Error, "Couldn't acquire memcache lock for: #{key} server: #{@cache.get_server_for_key(key)}"
36
+ raise Error, "Couldn't acquire memcache lock on #{@cache.get_server_for_key("lock/#{key}")}"
34
37
  end
35
38
 
36
39
  def release_lock(key)
@@ -44,13 +47,17 @@ module Cash
44
47
  private
45
48
 
46
49
  def recursive_lock?(key)
47
- @cache.get("lock/#{key}") == Process.pid
50
+ @cache.get("lock/#{key}") == host_pid
48
51
  end
49
52
 
50
53
  def debug_lock(key)
51
- @cache.logger.warn("#{@cache.get("lock/#{key}")}") if @cache.respond_to?(:logger) && @cache.logger.respond_to?(:warn)
54
+ @cache.logger.warn("Cash::Lock[#{key}]: #{@cache.get("lock/#{key}")}") if @cache.respond_to?(:logger) && @cache.logger.respond_to?(:warn)
52
55
  rescue
53
56
  @cache.logger.warn("#{$!}") if @cache.respond_to?(:logger) && @cache.logger.respond_to?(:warn)
54
57
  end
58
+
59
+ def host_pid
60
+ "#{Socket.gethostname} #{Process.pid}"
61
+ end
55
62
  end
56
63
  end
@@ -0,0 +1,154 @@
1
+ module Cash
2
+ class Mock < HashWithIndifferentAccess
3
+ attr_accessor :servers
4
+
5
+ class CacheEntry
6
+ attr_reader :value
7
+
8
+ def self.default_ttl
9
+ 1_000_000
10
+ end
11
+
12
+ def self.now
13
+ Time.now
14
+ end
15
+
16
+ def initialize(value, raw, ttl)
17
+ if raw
18
+ @value = value.to_s
19
+ else
20
+ @value = Marshal.dump(value)
21
+ end
22
+
23
+ if ttl.zero?
24
+ @ttl = self.class.default_ttl
25
+ else
26
+ @ttl = ttl
27
+ end
28
+
29
+ @expires_at = self.class.now + @ttl
30
+ end
31
+
32
+
33
+ def expired?
34
+ self.class.now > @expires_at
35
+ end
36
+
37
+ def increment(amount = 1)
38
+ @value = (@value.to_i + amount).to_s
39
+ end
40
+
41
+ def decrement(amount = 1)
42
+ @value = (@value.to_i - amount).to_s
43
+ end
44
+
45
+ def unmarshal
46
+ Marshal.load(@value)
47
+ end
48
+
49
+ def to_i
50
+ @value.to_i
51
+ end
52
+ end
53
+
54
+ attr_accessor :logging
55
+
56
+ def initialize
57
+ @logging = false
58
+ end
59
+
60
+ def get_multi(keys)
61
+ slice(*keys).collect { |k,v| [k, v.unmarshal] }.to_hash_without_nils
62
+ end
63
+
64
+ def set(key, value, ttl = CacheEntry.default_ttl, raw = false)
65
+ log "< set #{key} #{ttl}"
66
+ self[key] = CacheEntry.new(value, raw, ttl)
67
+ log('> STORED')
68
+ end
69
+
70
+ def get(key, raw = false)
71
+ log "< get #{key}"
72
+ unless self.has_unexpired_key?(key)
73
+ log('> END')
74
+ return nil
75
+ end
76
+
77
+ log("> sending key #{key}")
78
+ log('> END')
79
+ if raw
80
+ self[key].value
81
+ else
82
+ self[key].unmarshal
83
+ end
84
+ end
85
+
86
+ def delete(key, options = {})
87
+ log "< delete #{key}"
88
+ if self.has_unexpired_key?(key)
89
+ log "> DELETED"
90
+ super(key)
91
+ else
92
+ log "> NOT FOUND"
93
+ end
94
+ end
95
+
96
+ def incr(key, amount = 1)
97
+ if self.has_unexpired_key?(key)
98
+ self[key].increment(amount)
99
+ self[key].to_i
100
+ end
101
+ end
102
+
103
+ def decr(key, amount = 1)
104
+ if self.has_unexpired_key?(key)
105
+ self[key].decrement(amount)
106
+ self[key].to_i
107
+ end
108
+ end
109
+
110
+ def add(key, value, ttl = CacheEntry.default_ttl, raw = false)
111
+ if self.has_unexpired_key?(key)
112
+ "NOT_STORED\r\n"
113
+ else
114
+ set(key, value, ttl, raw)
115
+ "STORED\r\n"
116
+ end
117
+ end
118
+
119
+ def append(key, value)
120
+ set(key, get(key, true).to_s + value.to_s, nil, true)
121
+ end
122
+
123
+ def namespace
124
+ nil
125
+ end
126
+
127
+ def flush_all
128
+ log('< flush_all')
129
+ clear
130
+ end
131
+
132
+ def stats
133
+ {}
134
+ end
135
+
136
+ def reset_runtime
137
+ [0, Hash.new(0)]
138
+ end
139
+
140
+ def has_unexpired_key?(key)
141
+ self.has_key?(key) && !self[key].expired?
142
+ end
143
+
144
+ def log(message)
145
+ return unless logging
146
+ logger.debug(message)
147
+ end
148
+
149
+ def logger
150
+ @logger ||= ActiveSupport::BufferedLogger.new(Rails.root.join('log/cash_mock.log'))
151
+ end
152
+
153
+ end
154
+ end
@@ -27,7 +27,7 @@ module Cash
27
27
  misses, missed_keys, objects = hit_or_miss(cache_keys, index, get_options)
28
28
  format_results(cache_keys, choose_deserialized_objects_if_possible(missed_keys, cache_keys, misses, objects))
29
29
  else
30
- logger.debug("---- UNCACHEABLE #{table_name} - #{find_options.inspect} - #{get_options.inspect} - #{@options1.inspect} - #{@options2.inspect}") if logger
30
+ logger.debug(" \e[1;4;31mUNCACHEABLE\e[0m #{table_name} - #{find_options.inspect} - #{get_options.inspect} - #{@options1.inspect} - #{@options2.inspect}") if logger
31
31
  uncacheable
32
32
  end
33
33
  end
@@ -61,6 +61,7 @@ module Cash
61
61
 
62
62
  private
63
63
  def cacheable?(*optionss)
64
+ return false if @active_record.respond_to?(:cachable?) && ! @active_record.cachable?(*optionss)
64
65
  optionss.each { |options| return unless safe_options_for_cache?(options) }
65
66
  partial_indices = optionss.collect { |options| attribute_value_pairs_for_conditions(options[:conditions]) }
66
67
  return if partial_indices.include?(nil)
@@ -122,11 +123,15 @@ module Cash
122
123
  # value = sql_value == '?' ? values.shift : columns_hash[column_name].type_cast(sql_value)
123
124
  if sql_value == '?'
124
125
  value = values.shift
125
- elsif sql_value[0..0] == ':' && values && values.count > 0 && values[0].is_a?(Hash)
126
- symb = sql_value[1..-1].to_sym
127
- value = columns_hash[column_name].type_cast(values[0][symb])
128
126
  else
129
- value = columns_hash[column_name].type_cast(sql_value)
127
+ column = columns_hash[column_name]
128
+ raise "could not find column #{column_name} in columns #{columns_hash.keys.join(',')}" if column.nil?
129
+ if sql_value[0..0] == ':' && values && values.count > 0 && values[0].is_a?(Hash)
130
+ symb = sql_value[1..-1].to_sym
131
+ value = column.type_cast(values[0][symb])
132
+ else
133
+ value = column.type_cast(sql_value)
134
+ end
130
135
  end
131
136
  indices << [column_name, value]
132
137
  else
@@ -182,7 +187,10 @@ module Cash
182
187
 
183
188
  def find_from_keys(*missing_keys)
184
189
  missing_ids = Array(missing_keys).flatten.collect { |key| key.split('/')[2].to_i }
185
- find_from_ids_without_cache(missing_ids, @options1)
190
+ options = @options1.dup
191
+ options.delete(:conditions)
192
+ options.delete(:limit)
193
+ find_from_ids_without_cache(missing_ids, options)
186
194
  end
187
195
  end
188
196
  end
@@ -26,7 +26,6 @@ module Cash
26
26
  @ids.collect { |id| "id/#{id}" }
27
27
  end
28
28
 
29
-
30
29
  def miss(missing_keys, options)
31
30
  find_from_keys(*missing_keys)
32
31
  end
@@ -22,15 +22,16 @@ module Cash
22
22
  end
23
23
  end
24
24
 
25
- def method_missing(method, *args, &block)
26
- @cache.send(method, *args, &block)
27
- end
28
-
29
25
  def respond_to?(method)
30
26
  @cache.respond_to?(method)
31
27
  end
32
28
 
33
29
  private
30
+
31
+ def method_missing(method, *args, &block)
32
+ @cache.send(method, *args, &block)
33
+ end
34
+
34
35
  def begin_transaction
35
36
  @cache = Buffered.push(@cache, @lock)
36
37
  end