cardiac 0.2.0.pre3 → 0.2.0.pre5

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.
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)