copycopter_client 1.0.0.beta8 → 1.0.0.beta9
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.
- data/features/rails.feature +1 -4
- data/lib/copycopter_client/client.rb +21 -9
- data/lib/copycopter_client/configuration.rb +16 -4
- data/lib/copycopter_client/rails.rb +1 -0
- data/lib/copycopter_client/request_sync.rb +23 -0
- data/lib/copycopter_client/sync.rb +13 -12
- data/lib/copycopter_client/version.rb +1 -1
- data/spec/copycopter_client/client_spec.rb +34 -11
- data/spec/copycopter_client/configuration_spec.rb +46 -1
- data/spec/copycopter_client/request_sync_spec.rb +27 -0
- data/spec/copycopter_client/sync_spec.rb +34 -4
- data/spec/support/fake_client.rb +2 -1
- data/spec/support/fake_copycopter_app.rb +15 -2
- data/spec/support/middleware_stack.rb +13 -0
- metadata +6 -3
data/features/rails.feature
CHANGED
|
@@ -71,13 +71,11 @@ Feature: Using copycopter in a rails app
|
|
|
71
71
|
<%= @text %>
|
|
72
72
|
"""
|
|
73
73
|
When I start the application
|
|
74
|
-
And I wait for changes to be synchronized
|
|
75
74
|
And I visit /users/
|
|
76
75
|
Then the response should contain "Old content"
|
|
77
76
|
When the the following blurbs are updated in the "abc123" project:
|
|
78
77
|
| key | draft content |
|
|
79
78
|
| en.users.index.controller-test | New content |
|
|
80
|
-
And I wait for changes to be synchronized
|
|
81
79
|
And I visit /users/
|
|
82
80
|
Then the response should contain "New content"
|
|
83
81
|
|
|
@@ -98,8 +96,7 @@ Feature: Using copycopter in a rails app
|
|
|
98
96
|
When I start the application
|
|
99
97
|
And I visit /users/
|
|
100
98
|
Then the response should contain "not found"
|
|
101
|
-
|
|
102
|
-
Then the "abc123" project should have the following blurbs:
|
|
99
|
+
And the "abc123" project should have the following blurbs:
|
|
103
100
|
| key | draft content |
|
|
104
101
|
| en.users.index.404 | not found |
|
|
105
102
|
And the log should contain "Uploaded missing translations"
|
|
@@ -35,14 +35,23 @@ module CopycopterClient
|
|
|
35
35
|
# If the +public+ option was set to +true+, this will use published blurbs.
|
|
36
36
|
# Otherwise, draft content is fetched.
|
|
37
37
|
#
|
|
38
|
-
#
|
|
38
|
+
# The client tracks ETags between download requests, and will return
|
|
39
|
+
# without yielding anything if the server returns a not modified response.
|
|
40
|
+
#
|
|
41
|
+
# @yield [Hash] downloaded blurbs
|
|
39
42
|
# @raise [ConnectionError] if the connection fails
|
|
40
43
|
def download
|
|
41
44
|
connect do |http|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
request = Net::HTTP::Get.new(uri(download_resource))
|
|
46
|
+
request['If-None-Match'] = @etag
|
|
47
|
+
response = http.request(request)
|
|
48
|
+
if check(response)
|
|
49
|
+
log("Downloaded translations")
|
|
50
|
+
yield JSON.parse(response.body)
|
|
51
|
+
else
|
|
52
|
+
log("No new translations")
|
|
53
|
+
end
|
|
54
|
+
@etag = response['ETag']
|
|
46
55
|
end
|
|
47
56
|
end
|
|
48
57
|
|
|
@@ -101,11 +110,14 @@ module CopycopterClient
|
|
|
101
110
|
end
|
|
102
111
|
|
|
103
112
|
def check(response)
|
|
104
|
-
|
|
113
|
+
case response
|
|
114
|
+
when Net::HTTPNotFound
|
|
105
115
|
raise InvalidApiKey, "Invalid API key: #{api_key}"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
when Net::HTTPNotModified
|
|
117
|
+
false
|
|
118
|
+
when Net::HTTPSuccess
|
|
119
|
+
true
|
|
120
|
+
else
|
|
109
121
|
raise ConnectionError, "#{response.code}: #{response.body}"
|
|
110
122
|
end
|
|
111
123
|
end
|
|
@@ -3,6 +3,7 @@ require 'copycopter_client/i18n_backend'
|
|
|
3
3
|
require 'copycopter_client/client'
|
|
4
4
|
require 'copycopter_client/sync'
|
|
5
5
|
require 'copycopter_client/prefixed_logger'
|
|
6
|
+
require 'copycopter_client/request_sync'
|
|
6
7
|
|
|
7
8
|
module CopycopterClient
|
|
8
9
|
# Used to set up and modify settings for the client.
|
|
@@ -13,7 +14,7 @@ module CopycopterClient
|
|
|
13
14
|
:http_open_timeout, :http_read_timeout, :client_name, :client_url,
|
|
14
15
|
:client_version, :port, :protocol, :proxy_host, :proxy_pass,
|
|
15
16
|
:proxy_port, :proxy_user, :secure, :polling_delay, :logger,
|
|
16
|
-
:framework].freeze
|
|
17
|
+
:framework, :middleware].freeze
|
|
17
18
|
|
|
18
19
|
# @return [String] The API key for your project, found on the project edit form.
|
|
19
20
|
attr_accessor :api_key
|
|
@@ -72,6 +73,9 @@ module CopycopterClient
|
|
|
72
73
|
# @return [Logger] Where to log messages. Must respond to same interface as Logger.
|
|
73
74
|
attr_reader :logger
|
|
74
75
|
|
|
76
|
+
# @return the middleware stack, if any, which should respond to +use+
|
|
77
|
+
attr_accessor :middleware
|
|
78
|
+
|
|
75
79
|
alias_method :secure?, :secure
|
|
76
80
|
|
|
77
81
|
# Instantiated from {CopycopterClient.configure}. Sets defaults.
|
|
@@ -116,12 +120,19 @@ module CopycopterClient
|
|
|
116
120
|
to_hash.merge(hash)
|
|
117
121
|
end
|
|
118
122
|
|
|
119
|
-
# Determines if the content will be
|
|
120
|
-
# @return [Boolean] Returns +false+ if in a development
|
|
123
|
+
# Determines if the published or draft content will be used
|
|
124
|
+
# @return [Boolean] Returns +false+ if in a development or test
|
|
125
|
+
# environment, +true+ otherwise.
|
|
121
126
|
def public?
|
|
122
127
|
!(development_environments + test_environments).include?(environment_name)
|
|
123
128
|
end
|
|
124
129
|
|
|
130
|
+
# Determines if the content will be editable
|
|
131
|
+
# @return [Boolean] Returns +true+ if in a development environment, +false+ otherwise.
|
|
132
|
+
def development?
|
|
133
|
+
development_environments.include?(environment_name)
|
|
134
|
+
end
|
|
135
|
+
|
|
125
136
|
# Determines if the content will fetched from the server
|
|
126
137
|
# @return [Boolean] Returns +true+ if in a test environment, +false+ otherwise.
|
|
127
138
|
def test?
|
|
@@ -134,7 +145,7 @@ module CopycopterClient
|
|
|
134
145
|
@applied
|
|
135
146
|
end
|
|
136
147
|
|
|
137
|
-
|
|
148
|
+
# Applies the configuration (internal).
|
|
138
149
|
#
|
|
139
150
|
# Called automatically when {CopycopterClient.configure} is called in the application.
|
|
140
151
|
#
|
|
@@ -147,6 +158,7 @@ module CopycopterClient
|
|
|
147
158
|
I18n.backend = I18nBackend.new(sync)
|
|
148
159
|
CopycopterClient.client = client
|
|
149
160
|
CopycopterClient.sync = sync
|
|
161
|
+
middleware.use(RequestSync, :sync => sync) if middleware && development?
|
|
150
162
|
@applied = true
|
|
151
163
|
logger.info("Client #{VERSION} ready")
|
|
152
164
|
logger.info("Environment Info: #{environment_info}")
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module CopycopterClient
|
|
2
|
+
# Rack middleware that synchronizes with Copycopter during each request.
|
|
3
|
+
#
|
|
4
|
+
# This is injected into the Rails middleware stack in development environments.
|
|
5
|
+
class RequestSync
|
|
6
|
+
# @param app [Rack] the upstream app into whose responses to inject the editor
|
|
7
|
+
# @param options [Hash]
|
|
8
|
+
# @option options [Sync] :sync agent that should be flushed after each request
|
|
9
|
+
def initialize(app, options)
|
|
10
|
+
@app = app
|
|
11
|
+
@sync = options[:sync]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Invokes the upstream Rack application and flushes the sync after each
|
|
15
|
+
# request.
|
|
16
|
+
def call(env)
|
|
17
|
+
@sync.download
|
|
18
|
+
response = @app.call(env)
|
|
19
|
+
@sync.flush
|
|
20
|
+
response
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -86,6 +86,17 @@ module CopycopterClient
|
|
|
86
86
|
with_queued_changes do |queued|
|
|
87
87
|
client.upload(queued)
|
|
88
88
|
end
|
|
89
|
+
rescue ConnectionError => error
|
|
90
|
+
logger.error(error.message)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def download
|
|
94
|
+
client.download do |downloaded_blurbs|
|
|
95
|
+
downloaded_blurbs.reject! { |key, value| value == "" }
|
|
96
|
+
lock { @blurbs = downloaded_blurbs }
|
|
97
|
+
end
|
|
98
|
+
rescue ConnectionError => error
|
|
99
|
+
logger.error(error.message)
|
|
89
100
|
end
|
|
90
101
|
|
|
91
102
|
private
|
|
@@ -103,12 +114,8 @@ module CopycopterClient
|
|
|
103
114
|
end
|
|
104
115
|
|
|
105
116
|
def sync
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
flush
|
|
109
|
-
rescue ConnectionError => error
|
|
110
|
-
logger.error(error.message)
|
|
111
|
-
end
|
|
117
|
+
download
|
|
118
|
+
flush
|
|
112
119
|
ensure
|
|
113
120
|
@pending = false
|
|
114
121
|
end
|
|
@@ -124,12 +131,6 @@ module CopycopterClient
|
|
|
124
131
|
yield(changes_to_push) if changes_to_push
|
|
125
132
|
end
|
|
126
133
|
|
|
127
|
-
def download
|
|
128
|
-
downloaded_blurbs = client.download
|
|
129
|
-
downloaded_blurbs.reject! { |key, value| value == "" }
|
|
130
|
-
lock { @blurbs = downloaded_blurbs }
|
|
131
|
-
end
|
|
132
|
-
|
|
133
134
|
def lock(&block)
|
|
134
135
|
@mutex.synchronize(&block)
|
|
135
136
|
end
|
|
@@ -29,28 +29,28 @@ describe CopycopterClient do
|
|
|
29
29
|
it "should timeout when connecting" do
|
|
30
30
|
project = add_project
|
|
31
31
|
client = build_client(:api_key => project.api_key, :http_open_timeout => 4)
|
|
32
|
-
client.download
|
|
32
|
+
client.download { |ignore| }
|
|
33
33
|
http.open_timeout.should == 4
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
it "should timeout when reading" do
|
|
37
37
|
project = add_project
|
|
38
38
|
client = build_client(:api_key => project.api_key, :http_read_timeout => 4)
|
|
39
|
-
client.download
|
|
39
|
+
client.download { |ignore| }
|
|
40
40
|
http.read_timeout.should == 4
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
it "uses ssl when secure" do
|
|
44
44
|
project = add_project
|
|
45
45
|
client = build_client(:api_key => project.api_key, :secure => true)
|
|
46
|
-
client.download
|
|
46
|
+
client.download { |ignore| }
|
|
47
47
|
http.use_ssl.should == true
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
it "doesn't use ssl when insecure" do
|
|
51
51
|
project = add_project
|
|
52
52
|
client = build_client(:api_key => project.api_key, :secure => false)
|
|
53
|
-
client.download
|
|
53
|
+
client.download { |ignore| }
|
|
54
54
|
http.use_ssl.should == false
|
|
55
55
|
end
|
|
56
56
|
|
|
@@ -66,9 +66,9 @@ describe CopycopterClient do
|
|
|
66
66
|
]
|
|
67
67
|
|
|
68
68
|
errors.each do |original_error|
|
|
69
|
-
http.stubs(:
|
|
69
|
+
http.stubs(:request).raises(original_error)
|
|
70
70
|
client = build_client_with_project
|
|
71
|
-
expect { client.download }.
|
|
71
|
+
expect { client.download { |ignore| } }.
|
|
72
72
|
to raise_error(CopycopterClient::ConnectionError) { |error|
|
|
73
73
|
error.message.
|
|
74
74
|
should == "#{original_error.class.name}: #{original_error.message}"
|
|
@@ -78,7 +78,8 @@ describe CopycopterClient do
|
|
|
78
78
|
|
|
79
79
|
it "handles 500 errors from downloads with ConnectionError" do
|
|
80
80
|
client = build_client(:api_key => 'raise_error')
|
|
81
|
-
expect { client.download }.
|
|
81
|
+
expect { client.download { |ignore| } }.
|
|
82
|
+
to raise_error(CopycopterClient::ConnectionError)
|
|
82
83
|
end
|
|
83
84
|
|
|
84
85
|
it "handles 500 errors from uploads with ConnectionError" do
|
|
@@ -88,7 +89,8 @@ describe CopycopterClient do
|
|
|
88
89
|
|
|
89
90
|
it "handles 404 errors from downloads with ConnectionError" do
|
|
90
91
|
client = build_client(:api_key => 'bogus')
|
|
91
|
-
expect { client.download }.
|
|
92
|
+
expect { client.download { |ignore| } }.
|
|
93
|
+
to raise_error(CopycopterClient::InvalidApiKey)
|
|
92
94
|
end
|
|
93
95
|
|
|
94
96
|
it "handles 404 errors from uploads with ConnectionError" do
|
|
@@ -109,8 +111,10 @@ describe CopycopterClient do
|
|
|
109
111
|
'key.two' => "expected two"
|
|
110
112
|
}
|
|
111
113
|
})
|
|
114
|
+
client = build_client(:api_key => project.api_key, :public => true)
|
|
115
|
+
blurbs = nil
|
|
112
116
|
|
|
113
|
-
|
|
117
|
+
client.download { |yielded| blurbs = yielded }
|
|
114
118
|
|
|
115
119
|
blurbs.should == {
|
|
116
120
|
'key.one' => 'expected one',
|
|
@@ -121,7 +125,7 @@ describe CopycopterClient do
|
|
|
121
125
|
it "logs that it performed a download" do
|
|
122
126
|
logger = FakeLogger.new
|
|
123
127
|
client = build_client_with_project(:logger => logger)
|
|
124
|
-
client.download
|
|
128
|
+
client.download { |ignore| }
|
|
125
129
|
logger.should have_entry(:info, "Downloaded translations")
|
|
126
130
|
end
|
|
127
131
|
|
|
@@ -137,8 +141,10 @@ describe CopycopterClient do
|
|
|
137
141
|
'key.three' => "unexpected three"
|
|
138
142
|
}
|
|
139
143
|
})
|
|
144
|
+
client = build_client(:api_key => project.api_key, :public => false)
|
|
145
|
+
blurbs = nil
|
|
140
146
|
|
|
141
|
-
|
|
147
|
+
client.download { |yielded| blurbs = yielded }
|
|
142
148
|
|
|
143
149
|
blurbs.should == {
|
|
144
150
|
'key.one' => 'expected one',
|
|
@@ -146,6 +152,23 @@ describe CopycopterClient do
|
|
|
146
152
|
}
|
|
147
153
|
end
|
|
148
154
|
|
|
155
|
+
it "handles a 304 response when downloading" do
|
|
156
|
+
project = add_project
|
|
157
|
+
project.update('draft' => { 'key.one' => "expected one" })
|
|
158
|
+
logger = FakeLogger.new
|
|
159
|
+
client = build_client(:api_key => project.api_key,
|
|
160
|
+
:public => false,
|
|
161
|
+
:logger => logger)
|
|
162
|
+
yields = 0
|
|
163
|
+
|
|
164
|
+
2.times do
|
|
165
|
+
client.download { |ignore| yields += 1 }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
yields.should == 1
|
|
169
|
+
logger.should have_entry(:info, "No new translations")
|
|
170
|
+
end
|
|
171
|
+
|
|
149
172
|
it "uploads defaults for missing blurbs in an existing project" do
|
|
150
173
|
project = add_project
|
|
151
174
|
|
|
@@ -42,6 +42,7 @@ describe CopycopterClient::Configuration do
|
|
|
42
42
|
it { should have_config_option(:api_key). overridable }
|
|
43
43
|
it { should have_config_option(:polling_delay). overridable.default(300) }
|
|
44
44
|
it { should have_config_option(:framework). overridable }
|
|
45
|
+
it { should have_config_option(:middleware). overridable }
|
|
45
46
|
|
|
46
47
|
it "should provide default values for secure connections" do
|
|
47
48
|
config = CopycopterClient::Configuration.new
|
|
@@ -105,12 +106,14 @@ describe CopycopterClient::Configuration do
|
|
|
105
106
|
config.development_environments = %w(development)
|
|
106
107
|
config.environment_name = 'production'
|
|
107
108
|
config.should be_public
|
|
109
|
+
config.should_not be_development
|
|
108
110
|
end
|
|
109
111
|
|
|
110
|
-
it "should
|
|
112
|
+
it "should be development in a development environment" do
|
|
111
113
|
config = CopycopterClient::Configuration.new
|
|
112
114
|
config.development_environments = %w(staging)
|
|
113
115
|
config.environment_name = 'staging'
|
|
116
|
+
config.should be_development
|
|
114
117
|
config.should_not be_public
|
|
115
118
|
end
|
|
116
119
|
|
|
@@ -249,3 +252,45 @@ describe CopycopterClient::Configuration, "applied when not testing" do
|
|
|
249
252
|
end
|
|
250
253
|
end
|
|
251
254
|
|
|
255
|
+
describe CopycopterClient::Configuration, "applied when developing with middleware" do
|
|
256
|
+
it_should_behave_like "applied configuration"
|
|
257
|
+
|
|
258
|
+
let(:middleware) { MiddlewareStack.new }
|
|
259
|
+
|
|
260
|
+
before do
|
|
261
|
+
subject.middleware = middleware
|
|
262
|
+
subject.environment_name = 'development'
|
|
263
|
+
subject.apply
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it "adds the sync middleware" do
|
|
267
|
+
middleware.should include(CopycopterClient::RequestSync)
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
describe CopycopterClient::Configuration, "applied when developing without middleware" do
|
|
272
|
+
it_should_behave_like "applied configuration"
|
|
273
|
+
|
|
274
|
+
before do
|
|
275
|
+
subject.middleware = nil
|
|
276
|
+
subject.environment_name = 'development'
|
|
277
|
+
subject.apply
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
describe CopycopterClient::Configuration, "applied with middleware when not developing" do
|
|
282
|
+
it_should_behave_like "applied configuration"
|
|
283
|
+
|
|
284
|
+
let(:middleware) { MiddlewareStack.new }
|
|
285
|
+
|
|
286
|
+
before do
|
|
287
|
+
subject.middleware = middleware
|
|
288
|
+
subject.environment_name = 'test'
|
|
289
|
+
subject.apply
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
it "doesn't add the sync middleware" do
|
|
293
|
+
middleware.should_not include(CopycopterClient::RequestSync)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe CopycopterClient::RequestSync do
|
|
4
|
+
|
|
5
|
+
let(:sync) { {} }
|
|
6
|
+
let(:response) { 'response' }
|
|
7
|
+
let(:env) { 'env' }
|
|
8
|
+
let(:app) { stub('app', :call => response) }
|
|
9
|
+
before { sync.stubs(:flush => nil, :download => nil) }
|
|
10
|
+
subject { CopycopterClient::RequestSync.new(app, :sync => sync) }
|
|
11
|
+
|
|
12
|
+
it "invokes the upstream app" do
|
|
13
|
+
result = subject.call(env)
|
|
14
|
+
app.should have_received(:call).with(env)
|
|
15
|
+
result.should == response
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "flushes defaults" do
|
|
19
|
+
subject.call(env)
|
|
20
|
+
sync.should have_received(:flush)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "downloads new copy" do
|
|
24
|
+
subject.call(env)
|
|
25
|
+
sync.should have_received(:download)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -100,16 +100,46 @@ describe CopycopterClient::Sync do
|
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
it "uploads changes when flushed" do
|
|
103
|
-
sync = build_sync
|
|
104
|
-
sync.start
|
|
105
|
-
sleep 2
|
|
103
|
+
sync = build_sync
|
|
106
104
|
sync['test.key'] = 'test value'
|
|
105
|
+
|
|
107
106
|
sync.flush
|
|
108
|
-
sleep(2)
|
|
109
107
|
|
|
110
108
|
client.uploaded.should == { 'test.key' => 'test value' }
|
|
111
109
|
end
|
|
112
110
|
|
|
111
|
+
it "downloads changes" do
|
|
112
|
+
client['test.key'] = 'test value'
|
|
113
|
+
sync = build_sync
|
|
114
|
+
|
|
115
|
+
sync.download
|
|
116
|
+
|
|
117
|
+
sync['test.key'].should == 'test value'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "handles connection errors when flushing" do
|
|
121
|
+
failure = "server is napping"
|
|
122
|
+
logger = FakeLogger.new
|
|
123
|
+
client.stubs(:upload).raises(CopycopterClient::ConnectionError.new(failure))
|
|
124
|
+
sync = build_sync(:logger => logger)
|
|
125
|
+
sync['upload.key'] = 'upload'
|
|
126
|
+
|
|
127
|
+
sync.flush
|
|
128
|
+
|
|
129
|
+
logger.should have_entry(:error, failure)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
it "handles connection errors when downloading" do
|
|
133
|
+
failure = "server is napping"
|
|
134
|
+
logger = FakeLogger.new
|
|
135
|
+
client.stubs(:download).raises(CopycopterClient::ConnectionError.new(failure))
|
|
136
|
+
sync = build_sync(:logger => logger)
|
|
137
|
+
|
|
138
|
+
sync.download
|
|
139
|
+
|
|
140
|
+
logger.should have_entry(:error, failure)
|
|
141
|
+
end
|
|
142
|
+
|
|
113
143
|
it "handles connection errors when polling" do
|
|
114
144
|
failure = "server is napping"
|
|
115
145
|
logger = FakeLogger.new
|
data/spec/support/fake_client.rb
CHANGED
|
@@ -37,11 +37,17 @@ class FakeCopycopterApp < Sinatra::Base
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
get "/api/v2/projects/:api_key/published_blurbs" do |api_key|
|
|
40
|
-
with_project(api_key)
|
|
40
|
+
with_project(api_key) do |project|
|
|
41
|
+
etag project.etag
|
|
42
|
+
project.published.to_json
|
|
43
|
+
end
|
|
41
44
|
end
|
|
42
45
|
|
|
43
46
|
get "/api/v2/projects/:api_key/draft_blurbs" do |api_key|
|
|
44
|
-
with_project(api_key)
|
|
47
|
+
with_project(api_key) do |project|
|
|
48
|
+
etag project.etag
|
|
49
|
+
project.draft.to_json
|
|
50
|
+
end
|
|
45
51
|
end
|
|
46
52
|
|
|
47
53
|
post "/api/v2/projects/:api_key/draft_blurbs" do |api_key|
|
|
@@ -66,10 +72,12 @@ class FakeCopycopterApp < Sinatra::Base
|
|
|
66
72
|
@api_key = attrs['api_key']
|
|
67
73
|
@draft = attrs['draft'] || {}
|
|
68
74
|
@published = attrs['published'] || {}
|
|
75
|
+
@etag = attrs['etag'] || 1
|
|
69
76
|
end
|
|
70
77
|
|
|
71
78
|
def to_hash
|
|
72
79
|
{ 'api_key' => @api_key,
|
|
80
|
+
'etag' => @etag,
|
|
73
81
|
'draft' => @draft,
|
|
74
82
|
'published' => @published }
|
|
75
83
|
end
|
|
@@ -77,6 +85,7 @@ class FakeCopycopterApp < Sinatra::Base
|
|
|
77
85
|
def update(attrs)
|
|
78
86
|
@draft. update(attrs['draft']) if attrs['draft']
|
|
79
87
|
@published.update(attrs['published']) if attrs['published']
|
|
88
|
+
@etag += 1
|
|
80
89
|
self.class.save(self)
|
|
81
90
|
end
|
|
82
91
|
|
|
@@ -89,6 +98,10 @@ class FakeCopycopterApp < Sinatra::Base
|
|
|
89
98
|
self.class.save(self)
|
|
90
99
|
end
|
|
91
100
|
|
|
101
|
+
def etag
|
|
102
|
+
@etag.to_s
|
|
103
|
+
end
|
|
104
|
+
|
|
92
105
|
def self.create(api_key)
|
|
93
106
|
project = Project.new('api_key' => api_key)
|
|
94
107
|
save(project)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: copycopter_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash:
|
|
4
|
+
hash: 299253589
|
|
5
5
|
prerelease: true
|
|
6
6
|
segments:
|
|
7
7
|
- 1
|
|
8
8
|
- 0
|
|
9
9
|
- 0
|
|
10
|
-
-
|
|
11
|
-
version: 1.0.0.
|
|
10
|
+
- beta9
|
|
11
|
+
version: 1.0.0.beta9
|
|
12
12
|
platform: ruby
|
|
13
13
|
authors:
|
|
14
14
|
- thoughtbot
|
|
@@ -69,6 +69,7 @@ files:
|
|
|
69
69
|
- lib/copycopter_client/prefixed_logger.rb
|
|
70
70
|
- lib/copycopter_client/rails.rb
|
|
71
71
|
- lib/copycopter_client/railtie.rb
|
|
72
|
+
- lib/copycopter_client/request_sync.rb
|
|
72
73
|
- lib/copycopter_client/sync.rb
|
|
73
74
|
- lib/copycopter_client/version.rb
|
|
74
75
|
- lib/copycopter_client.rb
|
|
@@ -78,6 +79,7 @@ files:
|
|
|
78
79
|
- spec/copycopter_client/helper_spec.rb
|
|
79
80
|
- spec/copycopter_client/i18n_backend_spec.rb
|
|
80
81
|
- spec/copycopter_client/prefixed_logger_spec.rb
|
|
82
|
+
- spec/copycopter_client/request_sync_spec.rb
|
|
81
83
|
- spec/copycopter_client/sync_spec.rb
|
|
82
84
|
- spec/spec.opts
|
|
83
85
|
- spec/spec_helper.rb
|
|
@@ -90,6 +92,7 @@ files:
|
|
|
90
92
|
- spec/support/fake_passenger.rb
|
|
91
93
|
- spec/support/fake_resque_job.rb
|
|
92
94
|
- spec/support/fake_unicorn.rb
|
|
95
|
+
- spec/support/middleware_stack.rb
|
|
93
96
|
- features/rails.feature
|
|
94
97
|
- features/step_definitions/copycopter_server_steps.rb
|
|
95
98
|
- features/step_definitions/rails_steps.rb
|