cardiac 0.2.0.pre3 → 0.2.0.pre5

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: faafad3cb935e5bb55d912bce4253d8a87167355
4
- data.tar.gz: 176df035bb2ab9e4e84d78b85a7608f1d9e7f857
3
+ metadata.gz: 261e36951731f899ea83b30a9f35c0e69d651abe
4
+ data.tar.gz: 87e4b496c8d9eb31e1f2c9fb5e06df89a52e27db
5
5
  SHA512:
6
- metadata.gz: ef4123ec8f187b9bf88850845a01d0678aabd2d5780795948a3fb02c8c03012686f3c67d48ef791b4b3f14af371606a805a6e4de9d99e31b4196b00185965651
7
- data.tar.gz: 5ee48420e2b3053599dc737913f1c2aa43a2a2197c07e411ff884be131f4b290c379d1b7b5e1054a521d9954cf858eef49dcf42967d2d261bdef4ed41d9fc994
6
+ metadata.gz: 832b11c9e2bd6338857f28c5c9bd41d11e58d90aacbfa38b10eb438c165975a35374252c73f3a5038b9c7d2f1f15d44bdf308ef1728c70521606a84ab64be742
7
+ data.tar.gz: e9b1188718e09854fe9f7448d2c88859b31a5da351b618484acf744413fce97f660ce9464dcf7db3355e535f3fe552e4bdfd8dc6e158e8f501564628069ab0df
@@ -16,8 +16,8 @@ module Cardiac
16
16
  def call(env)
17
17
  @app.call(env)
18
18
  ensure
19
- if message = env['rack.errors'].to_s.presence
20
- Cardiac::Model::Base.logger.error message
19
+ if message = env['rack.errors'].string.presence
20
+ Cardiac::Model::Base.logger.error "cardiac: #{message}"
21
21
  end
22
22
  end
23
23
  end
@@ -1,6 +1,8 @@
1
1
  module Cardiac
2
2
 
3
3
  class LogSubscriber < ActiveSupport::LogSubscriber
4
+ class_attribute :verbose
5
+ self.verbose = false
4
6
 
5
7
  delegate :logger, to: '::Cardiac::Model::Base'
6
8
 
@@ -13,17 +15,28 @@ module Cardiac
13
15
  return unless logger.debug?
14
16
 
15
17
  payload = event.payload
16
- name, url = payload[:name], payload[:url]
18
+ name, url, verb, result = *payload.values_at(:name, :url, :verb, :result)
19
+
20
+ cache_trace = result.headers['X-Rack-Client-Cache'] if result
17
21
 
18
22
  stats = "#{event.duration.round(1)}ms"
19
- stats = "CACHED #{stats}" if name!='CACHE' && /fresh/ === payload[:response_headers].try(:[],'X-Rack-Client-Cache')
20
- name = "#{name} #{payload[:verb]} (#{stats})"
21
-
22
- if extra = payload.except(:name, :verb, :url, :response_headers).presence
23
- extra = " " + extra.map{|key,value|
23
+ stats = "CACHED #{stats}" if cache_trace && name!='CACHE' && /fresh/ === cache_trace
24
+ name = "#{name} #{verb} (#{stats})"
25
+
26
+ if extra = payload.except(:name, :verb, :url, :result).presence
27
+ extra = extra.map{|key,value|
24
28
  key = key.to_s.underscore.upcase
25
- "#{key}: #{key=='PAYLOAD' ? value : value.inspect}"
26
- }.join(",\n\t +")
29
+ "\n\t +#{key}: #{key=='PAYLOAD' ? value : value.inspect}"
30
+ }
31
+ end
32
+
33
+ if verbose && result
34
+ verbosity = Hash===verbose ? verbose : {status: true, cache: true, headers: false, body: false}
35
+ extra ||= []
36
+ extra.unshift "\n\t +BODY: #{body.inspect}" if verbosity[:body]
37
+ extra.unshift "\n\t +HEADERS: #{headers.inspect}" if verbosity[:headers]
38
+ extra.unshift "CACHE: #{cache_trace}" if verbosity[:cache]
39
+ extra.unshift result.status if verbosity[:status]
27
40
  end
28
41
 
29
42
  if odd?
@@ -33,7 +46,7 @@ module Cardiac
33
46
  name = color(name, MAGENTA, true)
34
47
  end
35
48
 
36
- debug " #{name} #{url}#{extra}"
49
+ debug " #{name} #{url}#{' ['+extra.join('; ')+']' if extra}"
37
50
  end
38
51
 
39
52
  def identity(event)
@@ -15,6 +15,7 @@ module Cardiac
15
15
  include Cardiac::Model::Callbacks
16
16
  include Cardiac::Model::Declarations
17
17
  include Cardiac::Model::Operations
18
+ include Cardiac::Model::CacheDecoration
18
19
  include ActiveSupport::Configurable
19
20
 
20
21
  # Instances may not explicitly define their own base resource.
@@ -0,0 +1,73 @@
1
+ module Cardiac
2
+ module Model
3
+
4
+ # Cardiac::Model cache decoration methods.
5
+ module CacheDecoration
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+
10
+ # The callback to be installed on the adapter.
11
+ MODEL_CACHE_CONTROL_PROC = Proc.new{|adapter| adapter.klass._model_cache_control(adapter.response) }
12
+
13
+ # Causes all find(..) methods to go through an object-level cache,
14
+ # which is populated on demand whenever it is needed.
15
+ #
16
+ # Pass +false+ to uninstall the model cache.
17
+ def cache_all! options={}
18
+ if options
19
+ @_model_cache_control = (Hash===options ? options : {}).update(expires_at: Time.now)
20
+ __operation_proxy__.__adapter__.after_execute MODEL_CACHE_CONTROL_PROC
21
+ else
22
+ @model_cache = @_model_cache_control = nil
23
+ __operation_proxy__.__adapter__.skip_callback :execute, :after, MODEL_CACHE_CONTROL_PROC
24
+ end
25
+ end
26
+
27
+ # Internal method that controls the model cache, using the given response object.
28
+ def _model_cache_control(response)
29
+ response = Rack::Cache::Response.new(*response.to_a)
30
+ if response.fresh?
31
+ @_model_cache_control[:expires_at] = response.expires || (response.date + response.ttl)
32
+ else
33
+ @_model_cache_control[:expires_at] = Time.now
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ # Override this internal method to use a local model cache for retrieving all instances.
40
+ def find_all(*args, &evaluator)
41
+ unless @_model_cache_control
42
+ super
43
+ else
44
+ if @model_cache.nil? || @_model_cache_control[:expires_at].past?
45
+ result = super(*args)
46
+ sort_by = @_model_cache_control[:sort_by] and result.sort_by!(&sort_by)
47
+ @model_cache = Hash[result.map{|record| record.readonly! ; record.freeze ; [record.id.to_s, record] }]
48
+ end
49
+ evaluator ? @model_cache.values.each(&evaluator) : @model_cache.values
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # Override this internal method utilize a market cache for retrieving single instances.
56
+ # TODO: This should not require knowledge of Model::Base internals.
57
+ #
58
+ # @see Cardiac::Model::Querying
59
+ def find_by_identity(id, &evaluator)
60
+ unless @_model_cache_control
61
+ super
62
+ else
63
+ find_all if @model_cache.nil? || @_model_cache_control[:expires_at].past?
64
+ record = @model_cache[id.to_s] unless id.nil?
65
+ evaluator.call(record) if evaluator
66
+ record
67
+ end
68
+ end
69
+ end
70
+
71
+ end
72
+ end
73
+ end
@@ -4,28 +4,17 @@ module Cardiac
4
4
  module Operations
5
5
  extend ActiveSupport::Concern
6
6
 
7
- # Extensions that are applied to the OperationHandler.
8
- module HandlerExtensions
9
- def transmit!(*args)
10
- super
11
- end
12
- end
13
-
14
7
  # Extensions that are applied to the ResourceAdapter.
15
8
  module AdapterExtensions
16
9
  def __codecs__
17
10
  @__codecs__ ||= Module.new{ include ::Cardiac::Representation::Codecs }
18
11
  end
19
-
20
- def __handler__
21
- @__handler__ ||= Class.new(::Cardiac::OperationHandler){ include HandlerExtensions; self }
22
- end
23
12
  end
24
13
 
25
14
  # Extensions that are applied to the OperationProxy.
26
15
  module ProxyExtensions
27
16
  def __adapter__
28
- @__adapter__ ||= Class.new(::Cardiac::ResourceAdapter){ include AdapterExtensions ; self }
17
+ @__adapter__ ||= Class.new(::Cardiac::ResourceAdapter){ extend AdapterExtensions ; self }
29
18
  end
30
19
  end
31
20
 
@@ -34,6 +23,7 @@ module Cardiac
34
23
  private
35
24
 
36
25
  # All remote operations go through this method.
26
+ # The decoded payload is returned, after storing the maximum age as represented by the request.
37
27
  def perform_operation(name, *args, &block)
38
28
  proxy = __operation_proxy__.new(base_resource)
39
29
  proxy.klass = self
@@ -41,7 +31,7 @@ module Cardiac
41
31
  end
42
32
 
43
33
  def __operation_proxy__
44
- @__operation_proxy__ ||= Class.new(OperationProxy){ include ProxyExtensions ; self }
34
+ @__operation_proxy__ ||= Class.new(OperationProxy){ extend ProxyExtensions ; self }
45
35
  end
46
36
  end
47
37
  end
@@ -95,7 +95,9 @@ module Cardiac
95
95
 
96
96
  # @see ActiveRecord::Persistence#instantiate
97
97
  def instantiate(record, options = {})
98
- allocate.init_with options.merge('attributes'=>record).stringify_keys
98
+ record = allocate.init_with options.merge('attributes'=>record).stringify_keys
99
+ yield record if block_given?
100
+ record
99
101
  end
100
102
 
101
103
  # See ActiveRecord::Relation::FinderMethods#raise_record_not_found_exception
data/lib/cardiac/model.rb CHANGED
@@ -13,5 +13,6 @@ module Cardiac
13
13
  autoload :Persistence
14
14
  autoload :Querying
15
15
  autoload :Validations
16
+ autoload :CacheDecoration
16
17
  end
17
18
  end
@@ -67,9 +67,11 @@ module Cardiac
67
67
  resolved.result.payload
68
68
  end
69
69
 
70
- def __adapter__
70
+ def self.__adapter__
71
71
  @__adapter__ ||= ::Cardiac::ResourceAdapter
72
72
  end
73
73
 
74
+ delegate :__adapter__, to: 'self.class'
75
+
74
76
  end
75
77
  end
@@ -28,6 +28,12 @@ module Cardiac
28
28
  config.app_middleware.insert_after "::ActionDispatch::Callbacks",
29
29
  "Cardiac::ResourceCache::Middleware"
30
30
 
31
+ initializer 'cardiac.log_subscriber' do |app|
32
+ ActiveSupport.on_load(:cardiac) do
33
+ Cardiac::LogSubscriber.verbose = app.config.cardiac.verbose
34
+ end
35
+ end
36
+
31
37
  initializer 'cardiac.build_client_middleware' do |app|
32
38
  ActiveSupport.on_load(:cardiac) do
33
39
  Cardiac::Client.tap do |client|
@@ -45,9 +51,7 @@ module Cardiac
45
51
  # NOTE: Certain headers should ALWAYS be ignored by the client.
46
52
  if client_cache = app.config.cardiac.client_cache
47
53
  client_cache = {} unless Hash===client_cache
48
-
49
- client_cache[:verbose] = app.config.cardiac.verbose unless client_cache.key? :verbose
50
-
54
+ client_cache[:verbose] = false unless client_cache.key? :verbose
51
55
  client_cache[:ignore_headers] = Array(client_cache[:ignore_headers]) + ['Set-Cookie','X-Content-Digest']
52
56
 
53
57
  client.use Rack::Cache, Hash[ client_cache.map{|k,v| ["rack-cache.#{k}", v] } ]
@@ -7,11 +7,11 @@ module Cardiac
7
7
 
8
8
  # An adapter for performing operations on a resource.
9
9
  class ResourceAdapter
10
- include ::ActiveSupport::Callbacks
11
10
  include Representation::LookupMethods
12
11
  include ResourceCache::InstanceMethods
13
12
 
14
- define_callbacks :resolve, :prepare, :encode, :execute, :decode
13
+ extend ActiveModel::Callbacks
14
+ define_model_callbacks :resolve, :prepare, :encode, :execute, :decode
15
15
 
16
16
  attr_accessor :klass, :resource, :payload, :result
17
17
 
@@ -66,7 +66,7 @@ module Cardiac
66
66
  resolved? or raise UnresolvableResourceError
67
67
  prepared? or prepare! or raise InvalidOperationError
68
68
  encode! *arguments
69
- execute!
69
+ execute! &block
70
70
  ensure
71
71
  decode! if completed?
72
72
  end
@@ -120,18 +120,18 @@ module Cardiac
120
120
  end
121
121
  end
122
122
 
123
- def execute!
123
+ def execute! &response_handler
124
124
  run_callbacks :execute do
125
125
  clear_resource_cache unless request_is_safe?
126
126
 
127
127
  instrumenter.instrument "operation.cardiac", event=event_attributes do
128
128
  if resource_cache_enabled? http_verb
129
129
  url, headers = __client_options__.slice(:url, :headers)
130
- self.result = cache_resource(url.to_s, headers, event) { transmit! }
130
+ self.result = cache_resource(url.to_s, headers, event) { transmit!(&response_handler) }
131
131
  else
132
- self.result = transmit!
132
+ self.result = transmit!(&response_handler)
133
133
  end
134
- event[:response_headers] = result.response.headers if result && result.response
134
+ event[:result] = response if response
135
135
  end
136
136
 
137
137
  completed?
@@ -160,8 +160,8 @@ module Cardiac
160
160
 
161
161
  private
162
162
 
163
- def transmit!
164
- __handler__.new(__client_options__, payload, &__client_handler__).transmit!
163
+ def transmit!(&response_handler)
164
+ __handler__.new(__client_options__, payload, &response_handler).transmit!
165
165
  end
166
166
 
167
167
  def model_name
@@ -176,20 +176,19 @@ module Cardiac
176
176
  h.keep_if{|key,value| key==:verb || key==:url || value.present? }
177
177
  end
178
178
 
179
- def __codecs__
179
+ def self.__codecs__
180
180
  @__codecs__ ||= ::Cardiac::Representation::Codecs
181
181
  end
182
182
 
183
- def __handler__
183
+ def self.__handler__
184
184
  @__handler__ ||= ::Cardiac::OperationHandler
185
185
  end
186
186
 
187
- def __client_handler__
188
- end
189
-
190
187
  def __klass_get method_name
191
188
  @klass.public_send(method_name) if @klass && @klass.respond_to?(method_name, false)
192
189
  end
190
+
191
+ delegate :__codecs__, :__handler__, to: 'self.class'
193
192
  end
194
193
 
195
194
  end
@@ -1,3 +1,3 @@
1
1
  module Cardiac
2
- VERSION = '0.2.0.pre3'
2
+ VERSION = '0.2.0.pre5'
3
3
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- cardiac (0.2.0.pre3)
4
+ cardiac (0.2.0.pre4)
5
5
  active_attr (>= 0.8.2)
6
6
  activemodel (>= 3.2, < 4.1)
7
7
  activesupport (>= 3.2, < 4.1)