logical_model 0.5.12 → 0.5.13

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.12
1
+ 0.5.13
@@ -12,6 +12,7 @@ require 'logical_model/safe_log'
12
12
  require 'logical_model/associations'
13
13
  require 'logical_model/api_key'
14
14
  require 'logical_model/attributes'
15
+ require 'logical_model/cache'
15
16
 
16
17
  # Logical Model, not persistant on DB, works through API. (replaces ActiveResource)
17
18
  #
@@ -50,6 +51,8 @@ require 'logical_model/attributes'
50
51
  # RemoteResource.delete(params[:id])
51
52
  # RemoteResource#destroy
52
53
  class LogicalModel
54
+ extend ActiveModel::Callbacks
55
+ define_model_callbacks :create, :save, :update, :destroy, :initialize
53
56
 
54
57
  include LogicalModel::Hydra
55
58
  include LogicalModel::ResponsesConfiguration
@@ -59,6 +62,7 @@ class LogicalModel
59
62
  include LogicalModel::ApiKey
60
63
  include LogicalModel::SafeLog
61
64
  include LogicalModel::Associations
65
+ include LogicalModel::Cache
62
66
 
63
67
  # include ActiveModel Modules that are usefull
64
68
  extend ActiveModel::Naming
@@ -67,8 +71,6 @@ class LogicalModel
67
71
  include ActiveModel::Validations
68
72
  include ActiveModel::MassAssignmentSecurity
69
73
 
70
- extend ActiveModel::Callbacks
71
- define_model_callbacks :create, :save, :update, :destroy
72
74
 
73
75
  self.include_root_in_json = false
74
76
 
@@ -81,6 +83,13 @@ class LogicalModel
81
83
  self.attributes = attributes
82
84
  end
83
85
 
86
+ def initialize_with_callback(attributes = {})
87
+ run_callbacks :initialize do
88
+ initialize_without_callback(attributes)
89
+ end
90
+ end
91
+ alias_method_chain :initialize, :callback
92
+
84
93
  class << self
85
94
  attr_accessor :timeout, :retries,
86
95
  :json_root
@@ -0,0 +1,132 @@
1
+ class LogicalModel
2
+ module Cache
3
+
4
+ def self.included(base)
5
+ base.send(:extend, ClassMethods)
6
+ base.send(:include, InstanceMethods)
7
+ base.send(:after_initialize, :initialize_loaded_at)
8
+ end
9
+
10
+ module InstanceMethods
11
+ attr_accessor :loaded_at
12
+
13
+ def initialize_loaded_at
14
+ self.loaded_at = Time.now
15
+ end
16
+
17
+ def _save
18
+ super
19
+ end
20
+
21
+ def _save_with_cache
22
+ model_name = self.class.to_s.pluralize.underscore
23
+ self.class.logger.debug "LogicalModel Log CACHE: Delete cache for #{model_name}\/#{self.id}-.*"
24
+ self.class.cache_store.delete_matched(/#{model_name}\/#{self.id}-.*/)
25
+ _save_without_cache
26
+ end
27
+ alias_method_chain :_save, :cache
28
+
29
+ def _update(params={})
30
+ super
31
+ end
32
+
33
+ def _update_with_cache(params={})
34
+ model_name = self.class.to_s.pluralize.underscore
35
+ self.class.logger.debug "LogicalModel Log CACHE: Delete cache for #{model_name}\/#{self.id}-.*"
36
+ self.class.cache_store.delete_matched(/#{model_name}\/#{self.id}-.*/)
37
+ _update_without_cache params
38
+ end
39
+ alias_method_chain :_update, :cache
40
+
41
+ def _destroy(params={})
42
+ super
43
+ end
44
+
45
+ def _destroy_with_cache(params={})
46
+ model_name = self.class.to_s.pluralize.underscore
47
+ self.class.logger.debug "LogicalModel Log CACHE: Delete cache for #{model_name}\/#{self.id}-.*"
48
+ self.class.cache_store.delete_matched(/#{model_name}\/#{self.id}-.*/)
49
+ _destroy_without_cache
50
+ end
51
+ alias_method_chain :_destroy, :cache
52
+ end
53
+
54
+ module ClassMethods
55
+ attr_accessor :expires_in
56
+
57
+ def cache_store
58
+ @cache_store ||= Rails.cache
59
+ end
60
+
61
+ # Will return key for cache
62
+ # @param id [String] (nil)
63
+ # @param params [Hash]
64
+ def cache_key(id, params = {})
65
+ model_name = self.to_s.pluralize.underscore
66
+ params_hash = Digest::MD5.hexdigest(params.to_s)
67
+
68
+ cache_key = "#{model_name}/#{id}-#{params_hash}"
69
+ end
70
+
71
+ def async_find(id, params={})
72
+ super(id, params)
73
+ end
74
+
75
+ def async_find_with_cache(id, params = {}, &block)
76
+ # Generate key based on params
77
+ cache_key = self.cache_key(id, params)
78
+ # If there is a cached value return it
79
+ self.logger.debug "LogicalModel Log CACHE: Reading cache key=#{cache_key}"
80
+ cached_result = self.cache_store.read(cache_key)
81
+ if cached_result
82
+ yield cached_result
83
+ else
84
+ self.logger.debug 'LogicalModel Log CACHE: Cache not present. Calling find_async without cache'
85
+ async_find_without_cache(id, params, &block)
86
+ end
87
+ end
88
+ alias_method_chain :async_find, :cache
89
+
90
+ def async_find_response(id, params={}, body)
91
+ super(id, params, body)
92
+ end
93
+
94
+ def async_find_response_with_cache(id, params={}, body)
95
+ # remove params not used in cache_key
96
+ %w(app_key token).each {|k| params.delete(k) }
97
+ cache_value = async_find_response_without_cache(id, params, body)
98
+ # Generate key based on params
99
+ cache_key = self.cache_key(id, params)
100
+ self.logger.debug "LogicalModel Log CACHE: Writing cache key=#{cache_key}"
101
+ self.cache_store.write(cache_key, cache_value, :expires_in => self.expires_in || 10.minutes)
102
+ cache_value
103
+ end
104
+ alias_method_chain :async_find_response, :cache
105
+
106
+ def delete(id, params={})
107
+ super(id, params)
108
+ end
109
+
110
+ def delete_with_cache(id, params = {})
111
+ model_name = self.to_s.pluralize.underscore
112
+ self.logger.debug "LogicalModel Log CACHE: Delete cache for #{model_name}\/#{id}-.*"
113
+ self.cache_store.delete_matched(/#{model_name}\/#{id}-.*/)
114
+ delete_without_cache(id, params)
115
+ end
116
+ alias_method_chain :delete, :cache
117
+
118
+ def delete_multiple(ids, params={})
119
+ super(ids, params)
120
+ end
121
+
122
+ def delete_multiple_with_cache(ids, params = {})
123
+ model_name = self.to_s.pluralize.underscore
124
+ self.logger.debug "LogicalModel Log CACHE: Delete cache for #{model_name}\/(#{ids.join('|')})-.*"
125
+ self.cache_store.delete_matched(/#{model_name}\/(#{ids.join('|')})-.*/)
126
+ delete_multiple_without_cache(ids, params)
127
+ end
128
+ alias_method_chain :delete_multiple, :cache
129
+ end
130
+
131
+ end
132
+ end
@@ -352,7 +352,7 @@ class LogicalModel
352
352
  request.on_complete do |response|
353
353
  if response.code >= 200 && response.code < 400
354
354
  log_ok(response)
355
- yield self.new.from_json(response.body) # this from_json is defined in ActiveModel::Serializers::JSON
355
+ yield async_find_response(id, params, response.body)
356
356
  else
357
357
  log_failed(response)
358
358
  end
@@ -361,6 +361,10 @@ class LogicalModel
361
361
  self.hydra.queue(request)
362
362
  end
363
363
 
364
+ def async_find_response(id, params, body)
365
+ self.new.from_json(body) # this from_json is defined in ActiveModel::Serializers::JSON
366
+ end
367
+
364
368
  # synchronic find
365
369
  def find(id, params = {})
366
370
  result = nil
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "logical_model"
8
- s.version = "0.5.12"
8
+ s.version = "0.5.13"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Dwayne Macgowan"]
12
- s.date = "2014-04-20"
12
+ s.date = "2014-06-13"
13
13
  s.description = "LogicalModel allows to use a resource as a model. It is based on web presentation http://www.slideshare.net/ihower/serviceoriented-design-and-implement-with-rails3"
14
14
  s.email = "dwaynemac@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -38,6 +38,7 @@ Gem::Specification.new do |s|
38
38
  "lib/logical_model/associations/belongs_to.rb",
39
39
  "lib/logical_model/associations/has_many_keys.rb",
40
40
  "lib/logical_model/attributes.rb",
41
+ "lib/logical_model/cache.rb",
41
42
  "lib/logical_model/hydra.rb",
42
43
  "lib/logical_model/responses_configuration.rb",
43
44
  "lib/logical_model/rest_actions.rb",
@@ -50,6 +51,7 @@ Gem::Specification.new do |s|
50
51
  "models/user.rb",
51
52
  "spec/client_spec.rb",
52
53
  "spec/lib/logical_model/associations/has_many_keys_spec.rb",
54
+ "spec/lib/logical_model/cache_spec.rb",
53
55
  "test/helper.rb",
54
56
  "test/test_logical_model.rb",
55
57
  "test/typhoeus_mocks.rb"
@@ -0,0 +1,136 @@
1
+ require './lib/logical_model'
2
+
3
+ describe LogicalModel::Cache do
4
+ describe "when included" do
5
+ before do
6
+ class Example < LogicalModel
7
+
8
+ attribute :id
9
+ attribute :name
10
+
11
+ self.hydra = Typhoeus::Hydra.new
12
+
13
+ self.enable_delete_multiple = true
14
+ end
15
+ end
16
+
17
+ it "adds expires_in class method" do
18
+ Example.should respond_to :expires_in
19
+ end
20
+
21
+ it "adds loaded_at as instance method" do
22
+ Example.new.should respond_to :loaded_at
23
+ end
24
+
25
+ it "initializes loaded_at" do
26
+ model = Example.new
27
+ model.run_callbacks :initialize
28
+ model.loaded_at.should_not be_nil
29
+ end
30
+
31
+ it "should use params to build cache_key" do
32
+ cache_key = Example.cache_key('id', {})
33
+ cache_key2 = Example.cache_key('id', {:param => true})
34
+ cache_key.should_not == cache_key2
35
+ end
36
+
37
+ it "should chain find_async" do
38
+ Example.should respond_to :async_find_with_cache
39
+ Example.should respond_to :async_find_without_cache
40
+ end
41
+
42
+ describe "cached value present" do
43
+ before do
44
+ Example.stub_chain(:cache_store, :read).and_return("test")
45
+ end
46
+
47
+ it "#async_find should return the cached value" do
48
+ Example.async_find("id") { |r| @result = r}
49
+ @result.should == "test"
50
+ end
51
+ end
52
+
53
+ describe "cached value not present" do
54
+ before do
55
+ Example.stub_chain(:cache_store, :read).and_return(nil)
56
+ Example.stub_chain(:cache_store, :write).and_return(nil)
57
+ end
58
+
59
+ it "#async_find should look for the value" do
60
+ Example.should_receive(:async_find_without_cache)
61
+ Example.async_find("id") {|r| @result = r}
62
+ end
63
+
64
+ it "#async_find_response should store the value in the cache" do
65
+ Example.should_receive(:async_find_response_without_cache)
66
+ Example.async_find_response("id", {}, "test")
67
+ end
68
+ end
69
+
70
+ describe "save" do
71
+ before do
72
+ Example.stub_chain(:cache_store, :read).and_return(Example.new)
73
+ Example.stub_chain(:cache_store, :delete_matched).and_return(nil)
74
+ Example.async_find("id") {|r| @result = r}
75
+ end
76
+
77
+ it "should clear cache" do
78
+ Example.cache_store.should_receive(:delete_matched)
79
+ @result.save
80
+ end
81
+ end
82
+
83
+ describe "update" do
84
+ before do
85
+ Example.stub_chain(:cache_store, :read).and_return(Example.new)
86
+ Example.stub_chain(:cache_store, :delete_matched).and_return(nil)
87
+ Example.any_instance.stub(:_update_without_cache).and_return(true)
88
+ Example.async_find("id") {|r| @result = r}
89
+ end
90
+
91
+ it "should clear cache" do
92
+ @result.should_receive(:_update_without_cache)
93
+ @result.update({:name => "test"})
94
+ end
95
+ end
96
+
97
+ describe "destroy" do
98
+ before do
99
+ Example.stub_chain(:cache_store, :read).and_return(Example.new)
100
+ Example.stub_chain(:cache_store, :delete_matched).and_return(nil)
101
+ Example.async_find("id") {|r| @result = r}
102
+ end
103
+
104
+ it "should clear cache" do
105
+ Example.cache_store.should_receive(:delete_matched)
106
+ @result.destroy
107
+ end
108
+ end
109
+
110
+ describe "delete" do
111
+ before do
112
+ Example.stub_chain(:cache_store, :read).and_return(Example.new)
113
+ Example.stub_chain(:cache_store, :delete_matched).and_return(nil)
114
+ Example.async_find("id") {|r| @result = r}
115
+ end
116
+
117
+ it "should clear cache" do
118
+ Example.cache_store.should_receive(:delete_matched)
119
+ Example.delete("id")
120
+ end
121
+ end
122
+
123
+ describe "delete_multiple" do
124
+ before do
125
+ Example.stub_chain(:cache_store, :read).and_return(Example.new)
126
+ Example.stub_chain(:cache_store, :delete_matched).and_return(nil)
127
+ Example.async_find("id") {|r| @result = r}
128
+ end
129
+
130
+ it "should clear cache" do
131
+ Example.cache_store.should_receive(:delete_matched)
132
+ Example.delete_multiple(["id1","id2"])
133
+ end
134
+ end
135
+ end
136
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logical_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.12
4
+ version: 0.5.13
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-04-20 00:00:00.000000000 Z
12
+ date: 2014-06-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -361,6 +361,7 @@ files:
361
361
  - lib/logical_model/associations/belongs_to.rb
362
362
  - lib/logical_model/associations/has_many_keys.rb
363
363
  - lib/logical_model/attributes.rb
364
+ - lib/logical_model/cache.rb
364
365
  - lib/logical_model/hydra.rb
365
366
  - lib/logical_model/responses_configuration.rb
366
367
  - lib/logical_model/rest_actions.rb
@@ -373,6 +374,7 @@ files:
373
374
  - models/user.rb
374
375
  - spec/client_spec.rb
375
376
  - spec/lib/logical_model/associations/has_many_keys_spec.rb
377
+ - spec/lib/logical_model/cache_spec.rb
376
378
  - test/helper.rb
377
379
  - test/test_logical_model.rb
378
380
  - test/typhoeus_mocks.rb
@@ -391,7 +393,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
391
393
  version: '0'
392
394
  segments:
393
395
  - 0
394
- hash: 474828961
396
+ hash: -822604947
395
397
  required_rubygems_version: !ruby/object:Gem::Requirement
396
398
  none: false
397
399
  requirements: