remotable 0.3.0 → 0.4.0.beta2
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/.gitignore +1 -0
- data/Gemfile.lock +4 -1
- data/README.mdown +6 -6
- data/lib/remotable.rb +33 -32
- data/lib/remotable/active_record_extender.rb +186 -166
- data/lib/remotable/active_resource_fixes.rb +16 -16
- data/lib/remotable/adapters/active_resource.rb +45 -33
- data/lib/remotable/core_ext/enumerable.rb +2 -2
- data/lib/remotable/core_ext/object.rb +6 -6
- data/lib/remotable/core_ext/uri.rb +4 -4
- data/lib/remotable/errors.rb +2 -0
- data/lib/remotable/logger_wrapper.rb +10 -10
- data/lib/remotable/nosync.rb +8 -8
- data/lib/remotable/null_remote.rb +13 -13
- data/lib/remotable/validate_models.rb +6 -6
- data/lib/remotable/version.rb +1 -1
- data/lib/remotable/with_remote_model_proxy.rb +4 -4
- data/test/active_resource_test.rb +176 -126
- data/test/bespoke_test.rb +50 -50
- data/test/nosync_test.rb +16 -16
- data/test/null_remote_test.rb +22 -22
- data/test/remotable_test.rb +35 -35
- data/test/support/active_resource.rb +5 -5
- data/test/support/bespoke.rb +13 -13
- data/test/support/concurrently.rb +62 -0
- data/test/support/null.rb +1 -1
- data/test/support/schema.rb +3 -0
- data/test/test_helper.rb +1 -1
- data/test/understanding_test.rb +2 -2
- metadata +37 -34
@@ -4,20 +4,20 @@ require "active_support/concern"
|
|
4
4
|
|
5
5
|
module ActiveResourceFixes
|
6
6
|
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
|
7
|
+
|
8
|
+
|
9
9
|
# ! ActiveModel::AttributeMethods assumes that :attribute is the target
|
10
10
|
# for attribute lookup. ActiveResource doesn't define that method.
|
11
11
|
def attribute(method)
|
12
12
|
attributes[method]
|
13
13
|
end
|
14
|
-
|
15
|
-
|
14
|
+
|
15
|
+
|
16
16
|
included do
|
17
17
|
alias_method_chain :destroy, :validation
|
18
18
|
end
|
19
|
-
|
20
|
-
|
19
|
+
|
20
|
+
|
21
21
|
# ActiveResource::Validations overrides ActiveResource::Base#save
|
22
22
|
# to rescue from ActiveResource::ResourceInvalid and record the
|
23
23
|
# resource's errors. Do the same for `destroy`.
|
@@ -31,7 +31,7 @@ module ActiveResourceFixes
|
|
31
31
|
load_remote_errors(@remote_errors, true)
|
32
32
|
false
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
end
|
36
36
|
|
37
37
|
|
@@ -50,19 +50,19 @@ module ActiveResourceFixes30
|
|
50
50
|
super(method_symbol, include_private)
|
51
51
|
end
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
# ! in this method, don't check the Content-Type header: rack doesn't always return it
|
55
55
|
def load_attributes_from_response(response)
|
56
56
|
if !response.body.nil? && response.body.strip.size > 0
|
57
57
|
load(self.class.format.decode(response.body))
|
58
58
|
end
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
end
|
62
62
|
|
63
63
|
|
64
64
|
module ActiveResourceFixes31
|
65
|
-
|
65
|
+
|
66
66
|
# ActiveResource hacks method_missing without hacking respond_to?
|
67
67
|
# In fact, it responds to any method that ends in an equals sign.
|
68
68
|
# It also responds to any method that matches an attribute name.
|
@@ -76,7 +76,7 @@ module ActiveResourceFixes31
|
|
76
76
|
super(method_symbol, include_private)
|
77
77
|
end
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
# ! in this method, don't check the Content-Type header: rack doesn't always return it
|
81
81
|
def load_attributes_from_response(response)
|
82
82
|
if !response.body.nil? && response.body.strip.size > 0
|
@@ -84,7 +84,7 @@ module ActiveResourceFixes31
|
|
84
84
|
@persisted = true
|
85
85
|
end
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
end
|
89
89
|
|
90
90
|
if Rails.version >= '3.2'
|
@@ -101,12 +101,12 @@ ActiveResource::Base.send(:include, ActiveResourceFixes)
|
|
101
101
|
|
102
102
|
|
103
103
|
module ActiveResourceJsonFormatFixes
|
104
|
-
|
104
|
+
|
105
105
|
def decode(json)
|
106
106
|
return {} if json.blank? # <-- insert this line. json will be nil if response is 304
|
107
107
|
super
|
108
108
|
end
|
109
|
-
|
109
|
+
|
110
110
|
end
|
111
111
|
|
112
112
|
ActiveResource::Formats::JsonFormat.extend ActiveResourceJsonFormatFixes
|
@@ -117,7 +117,7 @@ ActiveResource::Formats::JsonFormat.extend ActiveResourceJsonFormatFixes
|
|
117
117
|
# However, this is not what Rails Responders are inclined to return.
|
118
118
|
|
119
119
|
class ActiveResource::Errors
|
120
|
-
|
120
|
+
|
121
121
|
# Grabs errors from an array of messages (like ActiveRecord::Validations).
|
122
122
|
# The second parameter directs the errors cache to be cleared (default)
|
123
123
|
# or not (by passing true).
|
@@ -130,5 +130,5 @@ class ActiveResource::Errors
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
end
|
133
|
-
|
133
|
+
|
134
134
|
end
|
@@ -7,51 +7,60 @@ module Remotable
|
|
7
7
|
module Adapters
|
8
8
|
module ActiveResource
|
9
9
|
extend ActiveSupport::Concern
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
|
11
|
+
|
12
|
+
|
13
13
|
def key?(attribute)
|
14
14
|
attributes.key?(attribute.to_s)
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def [](attribute)
|
18
18
|
attributes[attribute.to_s]
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def []=(attribute, value)
|
22
22
|
attributes[attribute.to_s] = value
|
23
23
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
|
25
|
+
|
26
|
+
|
27
27
|
# If we use `remote_key` to explicitly set the path where
|
28
28
|
# this resource ought to be found, then we should use the
|
29
29
|
# same path when updating or destroying this resource.
|
30
|
-
#
|
30
|
+
#
|
31
31
|
# To accomplish this, we need to override ActiveResource's
|
32
32
|
# element_path to return the canonical path for this resource.
|
33
|
-
|
33
|
+
|
34
34
|
attr_accessor :remote_key_path
|
35
|
-
|
35
|
+
|
36
36
|
def element_path(*args)
|
37
37
|
return remote_key_path if remote_key_path
|
38
38
|
super
|
39
39
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
def destroy
|
44
|
+
super
|
45
|
+
rescue ::ActiveResource::ResourceNotFound
|
46
|
+
$!.extend Remotable::NotFound
|
47
|
+
raise
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
43
52
|
module ClassMethods
|
44
|
-
|
53
|
+
|
45
54
|
IF_MODIFIED_SINCE = "If-Modified-Since".freeze
|
46
|
-
|
47
|
-
|
48
|
-
|
55
|
+
|
56
|
+
|
57
|
+
|
49
58
|
def new_resource
|
50
59
|
new
|
51
60
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
61
|
+
|
62
|
+
|
63
|
+
|
55
64
|
# This is always invoked by instance#fetch_remote_resource.
|
56
65
|
# It expects to find a remote counterpart for a local resource.
|
57
66
|
# It should always return a NullRemote object that doesn't
|
@@ -59,8 +68,8 @@ module Remotable
|
|
59
68
|
def find_by_for_local(local_record, path)
|
60
69
|
had_previous_value = headers.key?(IF_MODIFIED_SINCE)
|
61
70
|
previous_value = headers[IF_MODIFIED_SINCE]
|
62
|
-
|
63
|
-
headers[IF_MODIFIED_SINCE] = Remotable.http_format_time(local_record.
|
71
|
+
|
72
|
+
headers[IF_MODIFIED_SINCE] = Remotable.http_format_time(local_record.remote_updated_at) if local_record.accepts_not_modified?
|
64
73
|
find_by(path)
|
65
74
|
ensure
|
66
75
|
if had_previous_value
|
@@ -69,19 +78,22 @@ module Remotable
|
|
69
78
|
headers.delete(IF_MODIFIED_SINCE)
|
70
79
|
end
|
71
80
|
end
|
72
|
-
|
81
|
+
|
73
82
|
def find_by(path)
|
74
83
|
find_by!(path)
|
75
84
|
rescue ::ActiveResource::ResourceNotFound
|
76
85
|
nil
|
77
86
|
end
|
78
|
-
|
87
|
+
|
79
88
|
def find_by!(path)
|
80
89
|
expanded_path = expanded_path_for(path)
|
81
90
|
Remotable.logger.info "[remotable:#{name.underscore}] GET #{expanded_path} (timeout: #{timeout})"
|
82
91
|
find(:one, :from => expanded_path).tap do |resource|
|
83
92
|
resource.remote_key_path = expanded_path if resource
|
84
93
|
end
|
94
|
+
rescue SocketError, EOFError
|
95
|
+
$!.extend Remotable::NetworkError
|
96
|
+
raise
|
85
97
|
rescue ::ActiveResource::TimeoutError
|
86
98
|
$!.extend Remotable::TimeoutError
|
87
99
|
raise
|
@@ -90,9 +102,9 @@ module Remotable
|
|
90
102
|
$!.extend Remotable::TimeoutError if $!.response.code == 504
|
91
103
|
raise
|
92
104
|
end
|
93
|
-
|
94
|
-
|
95
|
-
|
105
|
+
|
106
|
+
|
107
|
+
|
96
108
|
def expanded_path_for(path)
|
97
109
|
if relative_path?(path)
|
98
110
|
URI.join_url_segments(prefix, collection_name, "#{path}.#{format.extension}")
|
@@ -100,15 +112,15 @@ module Remotable
|
|
100
112
|
path
|
101
113
|
end
|
102
114
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
115
|
+
|
116
|
+
|
117
|
+
|
106
118
|
private
|
107
|
-
|
119
|
+
|
108
120
|
def relative_path?(path)
|
109
121
|
!(path.start_with?("/") || path["://"])
|
110
122
|
end
|
111
|
-
|
123
|
+
|
112
124
|
end
|
113
125
|
end
|
114
126
|
end
|
@@ -1,23 +1,23 @@
|
|
1
1
|
module Remotable
|
2
2
|
module CoreExt
|
3
3
|
module Object
|
4
|
-
|
5
|
-
|
4
|
+
|
5
|
+
|
6
6
|
def respond_to_all?(*methods)
|
7
7
|
respond_to = method(:respond_to?)
|
8
8
|
methods.flatten.all?(&respond_to)
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def responds_to(*methods)
|
12
12
|
respond_to = method(:respond_to?)
|
13
13
|
methods.flatten.select(&respond_to)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def does_not_respond_to(*methods)
|
17
17
|
methods.flatten - self.responds_to(methods)
|
18
18
|
end
|
19
|
-
|
20
|
-
|
19
|
+
|
20
|
+
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -4,16 +4,16 @@ require "uri"
|
|
4
4
|
module Remotable
|
5
5
|
module CoreExt
|
6
6
|
module URI
|
7
|
-
|
8
|
-
|
7
|
+
|
8
|
+
|
9
9
|
def join_url_segments(*segments)
|
10
10
|
segments = segments.dup.flatten.map(&:to_s)
|
11
11
|
first_segment = segments.shift.gsub(/\/$/, "")
|
12
12
|
segments.map! { |seg| seg.gsub(/(^\/)|(\/$)/, "") }
|
13
13
|
[first_segment, *segments].join("/")
|
14
14
|
end
|
15
|
-
|
16
|
-
|
15
|
+
|
16
|
+
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
data/lib/remotable/errors.rb
CHANGED
@@ -1,37 +1,37 @@
|
|
1
1
|
module Remotable
|
2
2
|
class LoggerWrapper
|
3
|
-
|
3
|
+
|
4
4
|
def initialize(logger)
|
5
5
|
@logger = logger
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
attr_reader :logger
|
9
|
-
|
9
|
+
|
10
10
|
def debug(*args)
|
11
11
|
logger.debug(*args) if log? :debug
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def info(*args)
|
15
15
|
logger.info(*args) if log? :info
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def warn(*args)
|
19
19
|
logger.warn(*args) if log? :warn
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def error(*args)
|
23
23
|
logger.error(*args) if log? :error
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
private
|
27
|
-
|
27
|
+
|
28
28
|
LEVELS = [:debug, :info, :warn, :error].freeze
|
29
|
-
|
29
|
+
|
30
30
|
def log?(value)
|
31
31
|
level = LEVELS.index(Remotable.log_level)
|
32
32
|
value = LEVELS.index(value)
|
33
33
|
value >= level
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
end
|
37
37
|
end
|
data/lib/remotable/nosync.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Remotable
|
2
2
|
module Nosync
|
3
|
-
|
4
|
-
|
3
|
+
|
4
|
+
|
5
5
|
def nosync!
|
6
6
|
self.nosync = true
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def nosync(new_value=true)
|
10
10
|
old_value = @nosync
|
11
11
|
self.nosync = new_value
|
@@ -13,19 +13,19 @@ module Remotable
|
|
13
13
|
ensure
|
14
14
|
@nosync = old_value
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def nosync=(val)
|
18
18
|
@nosync = val
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def nosync_value?
|
22
22
|
!@nosync.nil?
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def nosync?
|
26
26
|
@nosync == true
|
27
27
|
end
|
28
|
-
|
29
|
-
|
28
|
+
|
29
|
+
|
30
30
|
end
|
31
31
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Remotable
|
2
2
|
class NullRemote
|
3
|
-
|
3
|
+
|
4
4
|
class << self
|
5
|
-
|
5
|
+
|
6
6
|
# This is always invoked by instance#fetch_remote_resource.
|
7
7
|
# It expects to find a remote counterpart for a local resource.
|
8
8
|
# It should always return a NullRemote object that doesn't
|
@@ -10,7 +10,7 @@ module Remotable
|
|
10
10
|
def find_by_for_local(local_record, remote_key, fetch_value)
|
11
11
|
new
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
# This is always invoked via class#find_remote_resource_by
|
15
15
|
# by class#fetch_by. It gives the remote model an opportunity
|
16
16
|
# to discover a remote object that doesn't have a local
|
@@ -19,36 +19,36 @@ module Remotable
|
|
19
19
|
def find_by(remote_attr, value)
|
20
20
|
nil
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def new_resource
|
24
24
|
new
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
|
29
|
+
|
30
|
+
|
31
31
|
# NullRemote needs to receive setter messages and
|
32
32
|
# swallow them. It doesn't need to respond to getter
|
33
33
|
# messages since it has nothing to say.
|
34
34
|
def []=(attribute, value)
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def key?(attribute)
|
38
38
|
false
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
def save
|
42
42
|
true
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
def errors
|
46
46
|
{}
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
def destroy
|
50
50
|
true
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
end
|
54
54
|
end
|