active_cash 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dfe51a8a1ee9db01686a85f20fe6645be5a7197f
4
- data.tar.gz: 9b496a458a78e3bb25e6e19dd9a317306cce4fd2
3
+ metadata.gz: 2ce7a48da4376ed48bcaf90d9b56d270cad0d646
4
+ data.tar.gz: 689beea4558ef5b39f18d3cf0c1d12585085028d
5
5
  SHA512:
6
- metadata.gz: f920abb9e8575cda8f24ac9b1cda4a158e724d9f095409c5c720f60ed297ff409047bdbd55ccf663678c74ef910d5558eee1ce750005f4be07de0b1f4fd199ad
7
- data.tar.gz: bc10988ccc150437efa698fc1f79b717acbed4bf10946e1072452cdbbb1ca170d2aadcf1aae21d06982cb8784fd5f4321948f56ee2c998cb9b23ea84a1f6b890
6
+ metadata.gz: 2fc6f6e25239b5facaafc71341cda2e89a0a3e94b29adff8b3119b5835d09ea28412e9e00e27e2334570e2d1ec05fc6c7bbfa925af969da53c0722579383d835
7
+ data.tar.gz: 049e2153e2fbd3ed201924af45d0bf85794abcfd01f0458204ba5c273936e26ad4110a4958a7f7dac604cce306052fa60652522004925bc48537c3cef9937193
data/README.md CHANGED
@@ -30,7 +30,7 @@ class Like < ActiveRecord::Base
30
30
 
31
31
  # type # matched conditions # cache name (default is strategy name)
32
32
  caches :existence, find_by: [:user_id, :video_id]
33
- caches :existence, find_by: [:user_id, :video_id, :hidden], name: :hidden
33
+ caches :existence, find_by: [:user_id, :video_id, :hidden], as: :hidden #also name: :hidden is supported
34
34
  end
35
35
 
36
36
  # Check if Like exists for specific :user_id, :video_id.
@@ -45,7 +45,11 @@ end
45
45
  I am open for better naming conventions :)
46
46
 
47
47
  Cache will be created and updated on every object creation, update and deletion
48
- using an `after_commit` callback. If you want to have a read-driven cache (which could
48
+ using an `after_commit` callback. Only a string value is saved (true or false)
49
+ in Redis (using [redis-objects](https://github.com/nateware/redis-objects) underneath)
50
+ making it extremely efficient.
51
+
52
+ If you want to have a read-driven cache (which could
49
53
  enhance your hit ratio depending on your read/writes ratio) you can provide an
50
54
  empty `update_on` array (by default it includes `[:create, :update, :destroy]`):
51
55
 
@@ -68,13 +72,32 @@ end
68
72
 
69
73
  Now cache will be created and updated ONLY IF there is a reference to Redis.
70
74
 
75
+ You can also specify the return value (the symbol must be a method defined in the class instance):
76
+ ``` ruby
77
+ class Like < ActiveRecord::Base
78
+ include ActiveCash
79
+
80
+ caches :existence, find_by: [:user_id, :video_id, :state], as: :state, returns: :state
81
+ end
82
+
83
+ # Check if Like exists for specific :user_id, :video_id.
84
+ # returns the state
85
+ # returns false if state.nil?
86
+ @like = Like.cached_existence_by(user_id: 1, video_id: 2)
87
+ ```
88
+
89
+ Not though that cache will return false if state is nil.
90
+
91
+
71
92
  ## Todo
72
93
  Only existence strategy is supported at the moment but the code is designed to
73
94
  support other kind of caching strategies as well, like caching the whole object
74
95
  or some parts of it. Also:
75
96
 
97
+ * Refactor a bit :(
76
98
  * We should provide named callbacks
77
99
  * It's super easy to extend for Mongoid
100
+ * Add memcached adapter
78
101
 
79
102
  ## Relevant projects
80
103
  There is [identity_cache](https://github.com/Shopify/identity_cache) built by Shopify.
data/lib/active_cash.rb CHANGED
@@ -13,7 +13,7 @@ module ActiveCash
13
13
  end
14
14
  module ClassMethods
15
15
  def caches(type, opts = {})
16
- name = opts[:name] || type.to_sym
16
+ name = opts[:name] || opts[:as] || type.to_sym
17
17
  @cache_opts = Utils.build_cache_opts(type, opts, @cache_opts, self.to_s)
18
18
  Utils.set_callbacks(self, @cache_opts[name])
19
19
  Utils.create_methods(self, @cache_opts[name])
@@ -2,22 +2,38 @@ module ActiveCash::Adapter #fix boolean and make it generic
2
2
  extend self
3
3
 
4
4
  def get_value(key_name)
5
- boolean(Redis::Value.new(key_name).value)
5
+ value = Redis::Value.new(key_name).value
6
+ is_boolean?(value) ? boolean(value) : value
6
7
  end
7
8
 
8
9
  def set_value(key_name, value)
9
10
  Redis::Value.new(key_name).value = value
10
11
 
11
- boolean(value)
12
+ is_boolean?(value) ? boolean(value) : value
12
13
  end
13
14
 
14
15
  def delete_value(key_name)
15
16
  Redis::Value.new(key_name).delete
16
17
  end
17
18
 
19
+ def set_value_with_return(key_name, exists, returns)
20
+ if exists
21
+ returns.nil? ? set_value(key_name, true) : set_value(key_name, returns)
22
+ else
23
+ set_value(key_name, false)
24
+ end
25
+ end
26
+
18
27
  def boolean(value)
19
28
  return nil if value.nil?
20
29
 
21
30
  [true, 1, '1', 't', 'T', 'true', 'TRUE'].include? value
22
31
  end
32
+
33
+ def is_boolean?(value)
34
+ [
35
+ true, false, 1, 0, 'true', 'false', '1', '0', 't', 'f', 'TRUE', 'FALSE',
36
+ 'T', 'F'
37
+ ].include? value
38
+ end
23
39
  end
@@ -1,74 +1,86 @@
1
1
  module ActiveCash::Cache
2
2
  extend self
3
3
 
4
- def exists?(find_by:, method_name:, return: nil, klass:)
4
+ def exists?(find_by:, method_name:, returns:, klass:)
5
5
  key_name = get_key_name(klass, method_name, find_by)
6
6
  cached_value = adapter.get_value(key_name)
7
7
  return cached_value unless cached_value.nil?
8
8
 
9
- adapter.set_value(key_name, klass.to_s.constantize.exists?(find_by))
9
+ instance = klass.to_s.constantize.find_by(find_by)
10
+ adapter.set_value_with_return(
11
+ key_name, !instance.nil?, instance.try(returns)
12
+ )
10
13
  end
11
14
 
12
- def instance_exists?(find_by:, method_name:, instance:, return: nil)
15
+ def instance_exists?(find_by:, method_name:, instance:, returns:)
13
16
  key_name = get_key_name(instance.class, method_name, find_by)
14
17
  cached_value = adapter.get_value(key_name)
15
18
  return cached_value unless cached_value.nil?
16
19
 
17
- instance_update(find_by: find_by, method_name: method_name, instance: instance)
20
+ instance_update(
21
+ find_by: find_by, method_name: method_name, instance: instance,
22
+ returns: returns
23
+ )
18
24
  end
19
25
 
20
- def instance_update(find_by:, method_name:, return_value: nil, instance:)
26
+ def instance_update(find_by:, method_name:, returns:, instance:)
21
27
  key_name = get_key_name(instance.class, method_name, find_by)
22
28
 
23
29
  find_by.keys.each_with_index do |key, i|
24
30
  if instance.send(key) != find_by.values[i]
25
- return adapter.set_value(key_name, false)
31
+ return adapter.set_value_with_return(key_name, false, returns)
26
32
  end
27
33
  end
28
34
 
29
- adapter.set_value(key_name, true)
35
+ adapter.set_value_with_return(key_name, true, returns)#instance.try(returns))
30
36
  end
31
37
 
32
- def instance_updated?(find_by:, instance:)
38
+ def instance_updated?(find_by:, instance:, returns:)
39
+ changes = OpenStruct.new(instance.previous_changes)
40
+ return true if changes.send(returns) != nil
41
+
33
42
  find_by.keys.each_with_index do |key, i|
34
- if instance.send(key) != instance.send("#{key}_was")
43
+ if changes.send(key) != nil
35
44
  return true
36
45
  end
37
46
  end
38
47
  end
39
48
 
40
- def old_instance_update(find_by:, method_name:, return_value: nil, instance:)
49
+ def old_instance_update(find_by:, method_name:, returns:, instance:)
41
50
  #old values
42
51
  instance_update_if_exists(
43
52
  find_by: Hash[find_by.map {|k, v| ["#{k}_was", v] }],
44
53
  method_name: method_name,
45
- return: return_value,
54
+ returns: returns,
46
55
  instance: instance
47
56
  )
48
57
  end
49
58
 
50
- def instance_update_if_exists(find_by:, method_name:, return: nil, instance:)
59
+ def instance_update_if_exists(find_by:, method_name:, returns:, instance:)
51
60
  key_name = get_key_name(instance.class, method_name, find_by)
52
61
 
53
62
  cached_value = adapter.get_value(key_name)
54
63
  return true if cached_value.nil?
55
64
 
56
- instance_update(find_by: find_by, method_name: method_name, instance: instance)
65
+ instance_update(
66
+ find_by: find_by, method_name: method_name, instance: instance,
67
+ returns: returns
68
+ )
57
69
  end
58
70
 
59
- def delete(find_by:, method_name:, return: nil, klass:)
71
+ def delete(find_by:, method_name:, returns:, klass:)
60
72
  key_name = get_key_name(klass, method_name, find_by)
61
73
  adapter.delete_value(key_name)
62
74
  end
63
75
 
64
- def set_false(find_by:, method_name:, return: nil, klass:)
76
+ def set_false(find_by:, method_name:, returns:, klass:)
65
77
  key_name = get_key_name(klass, method_name, find_by)
66
- adapter.set_value(key_name, false)
78
+ adapter.set_value_with_return(key_name, false, returns)
67
79
  end
68
80
 
69
- def set_true(find_by:, method_name:, return: nil, klass:)
81
+ def set_true(find_by:, method_name:, returns:, klass:)
70
82
  key_name = get_key_name(klass, method_name, find_by)
71
- adapter.set_value(key_name, true)
83
+ adapter.set_value_with_return(key_name, true, returns)
72
84
  end
73
85
 
74
86
  def get_key_name(klass, method_name, hash_params)
@@ -6,3 +6,6 @@ end
6
6
 
7
7
  class UnknownCacheTypeError < ActiveCashError
8
8
  end
9
+
10
+ class FindByOptionsMismatchError < ActiveCashError
11
+ end
@@ -5,32 +5,52 @@ module ActiveCash::Utils
5
5
  if (opts[:update_on] & [:create]).blank?
6
6
  model.send(:after_commit, on: [:create]) do
7
7
  ActiveCash::Cache.instance_update_if_exists(
8
- ActiveCash::Utils.extract_instance_args(opts, self)
8
+ ActiveCash::Utils.extract_instance_args(opts, self).merge(
9
+ returns: self.try(opts[:returns])
10
+ )
9
11
  )
10
12
  end
11
13
  else
12
14
  model.send(:after_commit, on: [:create]) do
13
15
  ActiveCash::Cache.instance_update(
14
- ActiveCash::Utils.extract_instance_args(opts, self)
16
+ ActiveCash::Utils.extract_instance_args(opts, self).merge(
17
+ returns: self.try(opts[:returns])
18
+ )
15
19
  )
16
20
  end
17
21
  end
18
22
 
19
23
  if (opts[:update_on] & [:update]).blank?
20
24
  model.send(:after_commit, on: [:update]) do
21
- if instance_updated?(opts[:find_by], self)
22
- ActiveCash::Cache.delete(extract_old_instance_args(opts, self))
25
+ if ActiveCash::Cache.instance_updated?(
26
+ find_by: opts[:find_by], instance: self, returns: opts[:returns]
27
+ )
28
+ ActiveCash::Cache.delete(
29
+ ActiveCash::Utils.extract_instance_old_args(opts, self).merge(
30
+ returns: self.try(opts[:returns])
31
+ )
32
+ )
23
33
  ActiveCash::Cache.instance_update_if_exists(
24
- ActiveCash::Utils.extract_instance_args(opts, self)
34
+ ActiveCash::Utils.extract_instance_args(opts, self).merge(
35
+ returns: self.try(opts[:returns])
36
+ )
25
37
  )
26
38
  end
27
39
  end
28
40
  else
29
41
  model.send(:after_commit, on: [:update]) do
30
- if instance_updated?(opts[:find_by], self)
31
- ActiveCash::Cache.delete(extract_old_instance_args(opts, self))
42
+ if ActiveCash::Cache.instance_updated?(
43
+ find_by: opts[:find_by], instance: self, returns: opts[:returns]
44
+ )
45
+ ActiveCash::Cache.delete(
46
+ ActiveCash::Utils.extract_instance_old_args(opts, self).merge(
47
+ returns: self.try(opts[:returns])
48
+ )
49
+ )
32
50
  ActiveCash::Cache.instance_update(
33
- ActiveCash::Utils.extract_instance_args(opts, self)
51
+ ActiveCash::Utils.extract_instance_args(opts, self).merge(
52
+ returns: self.try(opts[:returns])
53
+ )
34
54
  )
35
55
  end
36
56
  end
@@ -39,13 +59,17 @@ module ActiveCash::Utils
39
59
  if (opts[:update_on] & [:destroy]).blank?
40
60
  model.send(:after_commit, on: [:destroy]) do
41
61
  ActiveCash::Cache.delete(
42
- ActiveCash::Utils.extract_args(opts, self)
62
+ ActiveCash::Utils.extract_args(opts, self).merge(
63
+ returns: self.try(opts[:returns])
64
+ )
43
65
  )
44
66
  end
45
67
  else
46
68
  model.send(:after_commit, on: [:destroy]) do
47
69
  ActiveCash::Cache.set_false(
48
- ActiveCash::Utils.extract_args(opts, self)
70
+ ActiveCash::Utils.extract_args(opts, self).merge(
71
+ returns: self.try(opts[:returns])
72
+ )
49
73
  )
50
74
  end
51
75
  end
@@ -53,7 +77,21 @@ module ActiveCash::Utils
53
77
 
54
78
  def create_methods(model, opts) #raise error when arg not given
55
79
  model.send(:define_singleton_method, "cached_#{opts[:name]}_by") do |args = {}|
56
- return ActiveCash::Cache.exists?(
80
+ xor_array = ((args.keys | opts[:find_by]) - (args.keys & opts[:find_by]))
81
+ if xor_array.empty?
82
+ return ActiveCash::Cache.exists?(
83
+ find_by: opts[:find_by].inject({}) {|h, arg| h[arg] = args[arg]; h},
84
+ method_name: opts[:name],
85
+ klass: opts[:klass],
86
+ returns: opts[:returns]
87
+ )
88
+ else
89
+ ActiveCash::Utils.raise_find_by_options_mismatch(xor_array, opts[:find_by])
90
+ end
91
+ end
92
+
93
+ model.send(:define_singleton_method, "delete_cached_#{opts[:name]}_by") do |args = {}|
94
+ return ActiveCash::Cache.delete(
57
95
  find_by: opts[:find_by].inject({}) {|h, arg| h[arg] = args[arg]; h},
58
96
  method_name: opts[:name],
59
97
  klass: opts[:klass]
@@ -61,6 +99,15 @@ module ActiveCash::Utils
61
99
  end
62
100
  end
63
101
 
102
+ def raise_find_by_options_mismatch(invalid, valid)
103
+ if (invalid & valid).any?
104
+ raise(FindByOptionsMismatchError, "Missing find_by options: #{invalid}")
105
+ else
106
+ raise(FindByOptionsMismatchError, "Not registerd find_by options: #{invalid}")
107
+ end
108
+ end
109
+
110
+
64
111
  def raise_unknown_cache_type(type)
65
112
  raise(
66
113
  UnknownCacheTypeError,
@@ -98,13 +145,20 @@ module ActiveCash::Utils
98
145
  end
99
146
 
100
147
  #and this
101
- def extract_instance_args(opts, instance)
148
+ def extract_instance_old_args(opts, instance)
149
+ changes = OpenStruct.new(instance.previous_changes)
150
+
102
151
  {
103
152
  find_by: opts[:find_by].inject({}){|h, arg|
104
- h[arg] = instance.send("#{arg}_was"); h
153
+ if changes.arg != nil
154
+ h[arg] = changes.send(arg).first
155
+ else
156
+ h[arg] = instance.send(arg)
157
+ end
158
+ h
105
159
  },
106
160
  method_name: opts[:name],
107
- instance: instance
161
+ klass: instance.class
108
162
  }
109
163
  end
110
164
 
@@ -112,13 +166,13 @@ module ActiveCash::Utils
112
166
  #add procs/lambdas for better finds
113
167
  raise_unknown_cache_type(type) unless type.to_sym == :existence
114
168
  cache_opts ||= {}
115
- name = opts[:name] || type.to_sym
169
+ name = opts[:name] || opts[:as] || type.to_sym
116
170
  raise_redefined_cache_error(name) unless cache_opts[name].nil?
117
171
  cache_opts[name] = {}
118
172
  cache_opts[name][:name] = name
119
173
  cache_opts[name][:type] = type.to_sym
120
174
  cache_opts[name][:find_by] = opts[:find_by]
121
- cache_opts[name][:return] = opts[:return]
175
+ cache_opts[name][:returns] = opts[:returns] || :nil
122
176
  cache_opts[name][:update_on] = opts[:update_on] || [:create, :update, :destroy]
123
177
  cache_opts[name][:klass] = klass
124
178
 
@@ -1,3 +1,3 @@
1
1
  module ActiveCash
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_cash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Filippos Vasilakis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-04 00:00:00.000000000 Z
11
+ date: 2016-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-objects