cardiac 0.2.0.pre2 → 0.2.0.pre3
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 +4 -4
- data/lib/cardiac.rb +7 -5
- data/lib/cardiac/client.rb +58 -0
- data/lib/cardiac/log_subscriber.rb +5 -5
- data/lib/cardiac/model/base.rb +3 -1
- data/lib/cardiac/model/dirty.rb +0 -5
- data/lib/cardiac/model/persistence.rb +2 -4
- data/lib/cardiac/operation_handler.rb +1 -44
- data/lib/cardiac/railtie.rb +70 -5
- data/lib/cardiac/resource.rb +2 -0
- data/lib/cardiac/resource/adapter.rb +38 -21
- data/lib/cardiac/resource/cache.rb +146 -0
- data/lib/cardiac/resource/request_methods.rb +12 -0
- data/lib/cardiac/util.rb +21 -0
- data/lib/cardiac/version.rb +1 -1
- data/spec/rails-3.2/Gemfile.lock +1 -1
- data/spec/rails-3.2/app_root/log/test.log +9788 -0
- data/spec/rails-4.0/Gemfile.lock +1 -1
- data/spec/rails-4.0/app_root/log/test.log +10387 -0
- data/spec/shared/support/client_execution.rb +1 -1
- metadata +4 -3
- data/cardiac-0.2.0.pre2.gem +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: faafad3cb935e5bb55d912bce4253d8a87167355
|
4
|
+
data.tar.gz: 176df035bb2ab9e4e84d78b85a7608f1d9e7f857
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 :
|
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
|
16
|
-
|
17
|
-
|
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 = "#{
|
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|
|
data/lib/cardiac/model/base.rb
CHANGED
@@ -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(:
|
160
|
+
ActiveSupport.run_load_hooks(:cardiac_model, Base)
|
159
161
|
end
|
160
162
|
|
161
163
|
end
|
data/lib/cardiac/model/dirty.rb
CHANGED
@@ -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
|
-
|
74
|
-
|
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
|
data/lib/cardiac/railtie.rb
CHANGED
@@ -1,14 +1,10 @@
|
|
1
1
|
require 'active_attr/railtie'
|
2
|
-
require
|
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
|
data/lib/cardiac/resource.rb
CHANGED
@@ -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
|
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
|
28
|
-
@reflection
|
29
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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:
|
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
|