cardiac 0.2.0.pre2 → 0.2.0.pre3

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: ffcee4b9c9da2df77d5d3530d5562848b497fdab
4
- data.tar.gz: d0dc3f4297ef67ddc38f139be6b64b00b7ff4dc9
3
+ metadata.gz: faafad3cb935e5bb55d912bce4253d8a87167355
4
+ data.tar.gz: 176df035bb2ab9e4e84d78b85a7608f1d9e7f857
5
5
  SHA512:
6
- metadata.gz: d01919339fec4e2519bc8b92479383d7c259247fcae5055e1a2722baceca9f91e070398660c04103a6fb0ffdb70c55faf23a9d04973e0faf24f8fdbf45cc9715
7
- data.tar.gz: 55c38c5f11b07b8d92fc6d314d6c3a674bf8bfed80a936ab54b755042d185745d6fa76d217551d851772a00a418b664f24f24293f8df95fc0cb048df1b77f767
6
+ metadata.gz: ef4123ec8f187b9bf88850845a01d0678aabd2d5780795948a3fb02c8c03012686f3c67d48ef791b4b3f14af371606a805a6e4de9d99e31b4196b00185965651
7
+ data.tar.gz: 5ee48420e2b3053599dc737913f1c2aa43a2a2197c07e411ff884be131f4b290c379d1b7b5e1054a521d9954cf858eef49dcf42967d2d261bdef4ed41d9fc994
data/lib/cardiac.rb CHANGED
@@ -21,14 +21,15 @@ module Cardiac
21
21
  # Resources and builder DSL.
22
22
  autoload :Resource
23
23
  autoload_under 'resource' do
24
- autoload :Subresource
25
- autoload :UriMethods
26
- autoload :RequestMethods
27
24
  autoload :CodecMethods
28
- autoload :ExtensionMethods
29
25
  autoload :ConfigMethods
30
- autoload :ResourceBuilder, 'cardiac/resource/builder'
26
+ autoload :ExtensionMethods
27
+ autoload :RequestMethods
31
28
  autoload :ResourceAdapter, 'cardiac/resource/adapter'
29
+ autoload :ResourceBuilder, 'cardiac/resource/builder'
30
+ autoload :ResourceCache, 'cardiac/resource/cache'
31
+ autoload :Subresource
32
+ autoload :UriMethods
32
33
  end
33
34
  autoload :Representation
34
35
  autoload_at 'cardiac/declarations' do
@@ -38,6 +39,7 @@ module Cardiac
38
39
  end
39
40
 
40
41
  # Operations and execution.
42
+ autoload :Client
41
43
  autoload :OperationHandler
42
44
  autoload :OperationResult, 'cardiac/operation_handler'
43
45
  autoload :OperationBuilder
@@ -0,0 +1,58 @@
1
+ require 'rack'
2
+ require 'rack/client'
3
+ require 'rack/cache'
4
+ require 'stringio'
5
+
6
+ module Cardiac
7
+
8
+ class Client < Rack::Client::Simple
9
+
10
+ # :nodoc:
11
+ class ErrorLogger
12
+ def initialize(app)
13
+ @app = app
14
+ end
15
+
16
+ def call(env)
17
+ @app.call(env)
18
+ ensure
19
+ if message = env['rack.errors'].to_s.presence
20
+ Cardiac::Model::Base.logger.error message
21
+ end
22
+ end
23
+ end
24
+
25
+ # :nodoc:
26
+ class SwitchHeaders
27
+ def initialize(app,match,switch)
28
+ @app, @match, @switch = app, match, switch
29
+ end
30
+
31
+ def call(env)
32
+ status, headers, body = @app.call(env)
33
+ [status, Hash[ headers.to_a.map{|k,v| [@match===k ? @switch+$' : k, v] }], body]
34
+ end
35
+ end
36
+
37
+ def build_env(*)
38
+ env = super
39
+ end
40
+
41
+ def http_user_agent
42
+ "cardiac #{Cardiac::VERSION} (rack-client #{Rack::Client::VERSION})"
43
+ end
44
+
45
+ def self.new
46
+ super Rack::Client::Handler::NetHTTP
47
+ end
48
+
49
+ def self.request(*args)
50
+ @instance ||= new
51
+ @instance.request(*args)
52
+ end
53
+
54
+ def self.build_mock_response body, code, headers={}
55
+ Rack::Client::Simple::CollapsedResponse.new code, headers, StringIO.new(body)
56
+ end
57
+ end
58
+ end
@@ -12,12 +12,12 @@ module Cardiac
12
12
  def operation(event)
13
13
  return unless logger.debug?
14
14
 
15
- payload = event.payload
16
-
17
- url = payload[:url]
15
+ payload = event.payload
16
+ name, url = payload[:name], payload[:url]
17
+
18
18
  stats = "#{event.duration.round(1)}ms"
19
- stats = "CACHED #{stats}" if /fresh/ === payload[:response_headers].try(:[],'X-Rack-Client-Cache')
20
- name = "#{payload[:name]} #{payload[:verb]} (#{stats})"
19
+ stats = "CACHED #{stats}" if name!='CACHE' && /fresh/ === payload[:response_headers].try(:[],'X-Rack-Client-Cache')
20
+ name = "#{name} #{payload[:verb]} (#{stats})"
21
21
 
22
22
  if extra = payload.except(:name, :verb, :url, :response_headers).presence
23
23
  extra = " " + extra.map{|key,value|
@@ -4,6 +4,8 @@ require 'active_attr'
4
4
  module Cardiac
5
5
  module Model
6
6
  class Base
7
+ extend Cardiac::ResourceCache::ClassMethods
8
+
7
9
  include ActiveAttr::Model
8
10
  include Cardiac::Model::Attributes
9
11
  include Cardiac::Model::Querying
@@ -155,7 +157,7 @@ module Cardiac
155
157
  end
156
158
  end
157
159
 
158
- ActiveSupport.run_load_hooks(:cardiac, Base)
160
+ ActiveSupport.run_load_hooks(:cardiac_model, Base)
159
161
  end
160
162
 
161
163
  end
@@ -19,8 +19,6 @@ module Cardiac
19
19
  if status = super
20
20
  @previously_changed = changes
21
21
  @changed_attributes.clear
22
- #elsif IdentityMap.enabled?
23
- # IdentityMap.remove(self)
24
22
  end
25
23
  status
26
24
  end
@@ -33,9 +31,6 @@ module Cardiac
33
31
  @previously_changed = changes
34
32
  @changed_attributes.clear
35
33
  end
36
- #rescue
37
- # IdentityMap.remove(self) if IdentityMap.enabled?
38
- # raise
39
34
  end
40
35
 
41
36
  # <tt>reload</tt> the record and clears changed attributes.
@@ -70,10 +70,8 @@ module Cardiac
70
70
  # Reloads the attributes of this object from the remote.
71
71
  # Any (optional) arguments are passed to find when reloading.
72
72
  def reload(*args)
73
- #IdentityMap.without do
74
- fresh_object = self.class.find(self.id, *args)
75
- @attributes.update(fresh_object.instance_variable_get('@attributes'))
76
- #end
73
+ fresh_object = self.class.find(self.id, *args)
74
+ @attributes.update(fresh_object.instance_variable_get('@attributes'))
77
75
  self
78
76
  end
79
77
 
@@ -1,10 +1,6 @@
1
1
  require 'active_support/configurable'
2
2
  require 'active_support/rescuable'
3
3
  require 'active_support/callbacks'
4
- require 'rack'
5
- require 'rack/client'
6
- require 'rack/cache'
7
- require 'stringio'
8
4
 
9
5
  module Cardiac
10
6
 
@@ -44,40 +40,6 @@ module Cardiac
44
40
  response
45
41
  end
46
42
 
47
- class Client < Rack::Client::Simple
48
- class SwitchHeaders
49
- def initialize(app,match,switch)
50
- @app, @match, @switch = app, match, switch
51
- end
52
- def call(env)
53
- status, headers, body = @app.call(env)
54
- [status, Hash[ headers.to_a.map{|k,v| [@match===k ? @switch+$' : k, v] }], body]
55
- end
56
- end
57
-
58
- use SwitchHeaders, /^X-HideRack-/, 'X-Rack-'
59
- use SwitchHeaders, /^X-Rack-/, 'X-Rack-Client-'
60
- use Rack::Cache,
61
- 'rack-cache.ignore_headers' => ['Set-Cookie','X-Content-Digest']
62
- use Rack::Head
63
- use Rack::ConditionalGet
64
- use Rack::ETag
65
- use SwitchHeaders, /^X-Rack-/, 'X-HideRack-'
66
-
67
- def self.new
68
- super Rack::Client::Handler::NetHTTP
69
- end
70
-
71
- def self.request(*args)
72
- @instance ||= new
73
- @instance.request(*args)
74
- end
75
-
76
- def http_user_agent
77
- "cardiac #{Cardiac::VERSION} (rack-client #{Rack::Client::VERSION})"
78
- end
79
- end
80
-
81
43
  attr_accessor :verb, :url, :headers, :payload, :options, :response_handler, :result
82
44
 
83
45
  def initialize client_options, payload=nil, &response_handler
@@ -204,12 +166,7 @@ module Cardiac
204
166
  # Optionally, these conditions could also be made to provide a mock response on a connection error.
205
167
  # This would prevent the operation from aborting but still show that the response was not transmitted.
206
168
  def service_unavailable e
207
- self.result = build_mock_response e, '503' if mock_response_on_connection_error
208
- end
209
-
210
- # Internal method.
211
- def build_mock_response body, code, version='1.0', headers={}
212
- Rack::Client::Simple::CollapsedResponse.new code, headers, StringIO.new(body)
169
+ self.result = Client.build_mock_response e, '503' if mock_response_on_connection_error
213
170
  end
214
171
  end
215
172
  end
@@ -1,14 +1,10 @@
1
1
  require 'active_attr/railtie'
2
- require "cardiac/model/base"
2
+ require 'cardiac/model'
3
3
  require 'cardiac/log_subscriber'
4
4
 
5
5
  module Cardiac
6
6
  class Railtie < Rails::Railtie
7
7
 
8
- initializer "cardiac.logger" do
9
- ActiveSupport.on_load(:cardiac) { self.logger ||= ::Rails.logger }
10
- end
11
-
12
8
  # Make the console output logging to STDERR (unless AR already did it).
13
9
  console do |app|
14
10
  unless defined? ::ActiveRecord::Base
@@ -16,5 +12,74 @@ module Cardiac
16
12
  Rails.logger.extend ActiveSupport::Logger.broadcast(console)
17
13
  end
18
14
  end
15
+
16
+ # Rails 4.0+
17
+ if config.respond_to? :eager_load_namespaces
18
+ config.eager_load_namespaces << Cardiac << Cardiac::Model
19
+ end
20
+
21
+ # Cardiac
22
+ # ---------------------------------------------------------------------------
23
+
24
+ config.cardiac = ActiveSupport::OrderedOptions.new
25
+ config.cardiac.client_cache = {}
26
+ config.cardiac.verbose = false
27
+
28
+ config.app_middleware.insert_after "::ActionDispatch::Callbacks",
29
+ "Cardiac::ResourceCache::Middleware"
30
+
31
+ initializer 'cardiac.build_client_middleware' do |app|
32
+ ActiveSupport.on_load(:cardiac) do
33
+ Cardiac::Client.tap do |client|
34
+
35
+ # Optionally print out the rack errors after the request
36
+ client.use Cardiac::Client::ErrorLogger if app.config.cardiac.verbose
37
+
38
+ # Restore any headers set by the remote server's Rack.
39
+ client.use Cardiac::Client::SwitchHeaders, /^X-HideRack-/, 'X-Rack-'
40
+
41
+ # Rename any headers set by the local client's Rack.
42
+ client.use Cardiac::Client::SwitchHeaders, /^X-Rack-/, 'X-Rack-Client-'
43
+
44
+ # Unless disabled, configure the client's Rack cache.
45
+ # NOTE: Certain headers should ALWAYS be ignored by the client.
46
+ if client_cache = app.config.cardiac.client_cache
47
+ client_cache = {} unless Hash===client_cache
48
+
49
+ client_cache[:verbose] = app.config.cardiac.verbose unless client_cache.key? :verbose
50
+
51
+ client_cache[:ignore_headers] = Array(client_cache[:ignore_headers]) + ['Set-Cookie','X-Content-Digest']
52
+
53
+ client.use Rack::Cache, Hash[ client_cache.map{|k,v| ["rack-cache.#{k}", v] } ]
54
+ end
55
+
56
+ # This is the "meat" of our basic middleware.
57
+ client.use Rack::Head
58
+ client.use Rack::ConditionalGet
59
+ client.use Rack::ETag
60
+
61
+ # Hide any headers set by the remote server's Rack.
62
+ client.use Cardiac::Client::SwitchHeaders, /^X-Rack-/, 'X-HideRack-'
63
+
64
+ end
65
+ end
66
+ end
67
+
68
+
69
+ # Cardiac::Model
70
+ # ---------------------------------------------------------------------------
71
+
72
+ config.cardiac_model = ActiveSupport::OrderedOptions.new
73
+
74
+ initializer "cardiac_model.logger" do
75
+ ActiveSupport.on_load(:cardiac_model) do
76
+ self.logger ||= ::Rails.logger
77
+ end
78
+ end
79
+
80
+ # Make sure that the model layer is already available when running a script.
81
+ runner do
82
+ require 'cardiac/model/base'
83
+ end
19
84
  end
20
85
  end
@@ -74,4 +74,6 @@ module Cardiac
74
74
  base.userinfo = base.query = nil
75
75
  end
76
76
  end
77
+
78
+ ActiveSupport.run_load_hooks(:cardiac, Resource)
77
79
  end
@@ -8,13 +8,14 @@ module Cardiac
8
8
  # An adapter for performing operations on a resource.
9
9
  class ResourceAdapter
10
10
  include ::ActiveSupport::Callbacks
11
- include ::Cardiac::Representation::LookupMethods
11
+ include Representation::LookupMethods
12
+ include ResourceCache::InstanceMethods
12
13
 
13
14
  define_callbacks :resolve, :prepare, :encode, :execute, :decode
14
15
 
15
16
  attr_accessor :klass, :resource, :payload, :result
16
17
 
17
- delegate :request_has_body?, :response_has_body?, to: :resource
18
+ delegate :request_has_body?, :response_has_body?, :request_is_safe?, :request_is_idempotent?, to: :resource
18
19
  delegate :encoder_reflection, :decoder_reflections, to: '@reflection'
19
20
  delegate :transmitted?, :aborted?, :completed?, :response, to: :result, allow_nil: true
20
21
 
@@ -24,12 +25,9 @@ module Cardiac
24
25
  delegate :logger, to: '::Cardiac::Model::Base'
25
26
 
26
27
  def initialize(klass,base,payload=nil)
27
- @klass = klass
28
- @reflection = base.to_reflection if base.respond_to?(:to_reflection)
29
- run_callbacks :resolve do
30
- @resource = base.to_resource if base.respond_to?(:to_resource)
31
- end
32
- @reflection ||= @resource.to_reflection if resolved?
28
+ @klass = klass
29
+ @reflection = base.to_reflection if base.respond_to? :to_reflection
30
+ resolve! base
33
31
  end
34
32
 
35
33
  def __client_options__
@@ -58,9 +56,7 @@ module Cardiac
58
56
 
59
57
  # Convenience method to return the current HTTP verb
60
58
  def http_verb
61
- if defined? @__client_options__
62
- @__client_options__[:method].to_s.upcase
63
- end
59
+ @http_verb ||= (defined? @__client_options__ and @__client_options__[:method].to_s.upcase)
64
60
  end
65
61
 
66
62
  # Performs a remote call by performing the remaining phases in the lifecycle of this adapter.
@@ -80,15 +76,26 @@ module Cardiac
80
76
  end
81
77
 
82
78
  def prepared?
83
- __client_options__.present?
79
+ @__client_options__.present?
84
80
  end
85
81
 
86
82
  protected
87
83
 
84
+ def resolve! base
85
+ run_callbacks :resolve do
86
+ @resource = base.to_resource if base.respond_to?(:to_resource)
87
+ end
88
+ @reflection ||= @resource.to_reflection if resolved?
89
+ end
90
+
88
91
  def prepare! verb=nil
89
92
  run_callbacks :prepare do
90
- self.resource = resource.http_method(verb) if verb
93
+ if verb
94
+ self.resource = resource.http_method(verb)
95
+ @__client_options__ = nil
96
+ end
91
97
  end
98
+
92
99
  __client_options__.symbolize_keys!
93
100
  prepared?
94
101
  end
@@ -114,14 +121,20 @@ module Cardiac
114
121
  end
115
122
 
116
123
  def execute!
117
- event = event_attributes
118
- instrumenter.instrument "operation.cardiac", event do
119
- run_callbacks :execute do
120
- handler = __handler__.new(__client_options__, payload, &__client_handler__)
121
- self.result = handler.transmit!
124
+ run_callbacks :execute do
125
+ clear_resource_cache unless request_is_safe?
126
+
127
+ instrumenter.instrument "operation.cardiac", event=event_attributes do
128
+ if resource_cache_enabled? http_verb
129
+ url, headers = __client_options__.slice(:url, :headers)
130
+ self.result = cache_resource(url.to_s, headers, event) { transmit! }
131
+ else
132
+ self.result = transmit!
133
+ end
122
134
  event[:response_headers] = result.response.headers if result && result.response
123
- completed?
124
135
  end
136
+
137
+ completed?
125
138
  end
126
139
  rescue => e
127
140
  message = "#{e.class.name}: #{e.message}: #{resource.to_url}"
@@ -147,12 +160,16 @@ module Cardiac
147
160
 
148
161
  private
149
162
 
163
+ def transmit!
164
+ __handler__.new(__client_options__, payload, &__client_handler__).transmit!
165
+ end
166
+
150
167
  def model_name
151
168
  __klass_get(:model_name).try(:to_s)
152
169
  end
153
170
 
154
- def event_attributes
155
- h = { name: model_name, verb: http_verb, url: resource.to_url, payload: payload }
171
+ def event_attributes(name=model_name)
172
+ h = { name: name, verb: http_verb, url: resource.to_url, payload: payload }
156
173
  ctx = __klass_get :operation_context
157
174
  ctx = { context: ctx } unless ctx.present? && Hash===ctx
158
175
  h.reverse_merge! ctx if ctx