copycopter_client 1.0.0.beta1 → 1.0.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.
@@ -35,10 +35,13 @@ module CopycopterClient
35
35
  if spawner?
36
36
  register_spawn_hooks
37
37
  else
38
+ register_job_hooks
38
39
  logger.info("Starting poller")
39
40
  @pending = true
40
41
  at_exit { sync }
41
- Thread.new { poll }
42
+ unless Thread.new { poll }
43
+ logger.error("Couldn't start poller thread")
44
+ end
42
45
  end
43
46
  end
44
47
 
@@ -78,6 +81,12 @@ module CopycopterClient
78
81
  end
79
82
  end
80
83
 
84
+ def flush
85
+ with_queued_changes do |queued|
86
+ client.upload(queued)
87
+ end
88
+ end
89
+
81
90
  private
82
91
 
83
92
  attr_reader :client, :polling_delay, :logger
@@ -95,9 +104,7 @@ module CopycopterClient
95
104
  begin
96
105
  downloaded_blurbs = client.download
97
106
  lock { @blurbs = downloaded_blurbs }
98
- with_queued_changes do |queued|
99
- client.upload(queued)
100
- end
107
+ flush
101
108
  rescue ConnectionError => error
102
109
  logger.error(error.message)
103
110
  end
@@ -124,6 +131,20 @@ module CopycopterClient
124
131
  $0.include?("ApplicationSpawner") || $0 =~ /unicorn.*master/
125
132
  end
126
133
 
134
+ def register_job_hooks
135
+ if defined?(Resque)
136
+ logger.info("Registered Resque after_perform hook")
137
+ Resque::Job.class_eval do
138
+ alias_method :perform_without_copycopter, :perform
139
+ def perform
140
+ job_was_performed = perform_without_copycopter
141
+ CopycopterClient.flush
142
+ job_was_performed
143
+ end
144
+ end
145
+ end
146
+ end
147
+
127
148
  def register_spawn_hooks
128
149
  if defined?(PhusionPassenger)
129
150
  logger.info("Registered Phusion Passenger fork hook")
@@ -1,6 +1,6 @@
1
1
  module CopycopterClient
2
2
  # Client version
3
- VERSION = "1.0.0.beta1"
3
+ VERSION = "1.0.0.beta2"
4
4
 
5
5
  # API version being used to communicate with the server
6
6
  API_VERSION = "2.0"
@@ -34,6 +34,12 @@ module CopycopterClient
34
34
  sync.start
35
35
  end
36
36
 
37
+ # Flush queued changed synchronously
38
+ # This is called from the Resque after perform "hook"
39
+ def self.flush
40
+ sync.flush
41
+ end
42
+
37
43
  # Call this method to modify defaults in your initializers.
38
44
  #
39
45
  # @example
@@ -95,6 +95,17 @@ describe CopycopterClient::Sync do
95
95
  client.uploaded.should == { 'test.key' => 'test value' }
96
96
  end
97
97
 
98
+ it "uploads changes when flushed" do
99
+ sync = build_sync(:polling_delay => 86400)
100
+ sync.start
101
+ sleep 2
102
+ sync['test.key'] = 'test value'
103
+ sync.flush
104
+ sleep(2)
105
+
106
+ client.uploaded.should == { 'test.key' => 'test value' }
107
+ end
108
+
98
109
  it "handles connection errors when polling" do
99
110
  failure = "server is napping"
100
111
  logger = FakeLogger.new
@@ -153,6 +164,39 @@ describe CopycopterClient::Sync do
153
164
  "Got entries: #{logger.entries.inspect}"
154
165
  end
155
166
 
167
+ it "flushes after running a resque job" do
168
+ define_constant('Resque', Module.new)
169
+ job = define_constant('Resque::Job', FakeResqueJob).new(:key => 'test.key', :value => 'all your base')
170
+
171
+ api_key = "12345"
172
+ FakeCopycopterApp.add_project api_key
173
+ logger = FakeLogger.new
174
+
175
+ config = { :logger => logger, :polling_delay => 86400, :api_key => api_key }
176
+ default_config = CopycopterClient::Configuration.new.to_hash.update(config)
177
+ real_client = CopycopterClient::Client.new(default_config)
178
+ sync = CopycopterClient::Sync.new(real_client, default_config)
179
+ CopycopterClient.sync = sync
180
+ job.sync = sync
181
+
182
+ sync.start
183
+ sleep(2)
184
+
185
+ logger.should have_entry(:info, "Registered Resque after_perform hook")
186
+
187
+ if fork
188
+ Process.wait
189
+ else
190
+ job.perform
191
+ exit!
192
+ end
193
+ sleep(2)
194
+
195
+ project = FakeCopycopterApp.project(api_key)
196
+ project.draft['test.key'].should == 'all your base'
197
+
198
+ end
199
+
156
200
  it "starts after spawning when using unicorn" do
157
201
  logger = FakeLogger.new
158
202
  define_constant('Unicorn', Module.new)
@@ -232,6 +276,15 @@ describe CopycopterClient::Sync do
232
276
  logger.should_not have_entry(:info, "Waiting for first sync")
233
277
  end
234
278
 
279
+ it "logs an error if the background thread can't start" do
280
+ Thread.stubs(:new => nil)
281
+ logger = FakeLogger.new
282
+
283
+ build_sync(:logger => logger).start
284
+
285
+ logger.should have_entry(:error, "Couldn't start poller thread")
286
+ end
287
+
235
288
  describe "given locked mutex" do
236
289
  Spec::Matchers.define :finish_after_unlocking do |mutex|
237
290
  match do |thread|
@@ -291,5 +344,15 @@ describe CopycopterClient::Sync do
291
344
 
292
345
  sync.should have_received(:start)
293
346
  end
347
+
348
+ it "flushes from the top level" do
349
+ sync = build_sync
350
+ CopycopterClient.sync = sync
351
+ sync.stubs(:flush)
352
+
353
+ CopycopterClient.flush
354
+
355
+ sync.should have_received(:flush)
356
+ end
294
357
  end
295
358
 
@@ -0,0 +1,11 @@
1
+ class FakeResqueJob
2
+ attr_accessor :sync
3
+ def initialize(hash)
4
+ @key = hash[:key]
5
+ @value = hash[:value]
6
+ end
7
+ def perform
8
+ sync[@key] = @value
9
+ true
10
+ end
11
+ end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: copycopter_client
3
3
  version: !ruby/object:Gem::Version
4
- hash: 62196353
5
- prerelease: 6
4
+ hash: 299253594
5
+ prerelease: true
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
9
  - 0
10
- - beta
11
- - 1
12
- version: 1.0.0.beta1
10
+ - beta2
11
+ version: 1.0.0.beta2
13
12
  platform: ruby
14
13
  authors:
15
14
  - thoughtbot
@@ -21,9 +20,10 @@ date: 2010-06-04 00:00:00 -04:00
21
20
  default_executable:
22
21
  dependencies:
23
22
  - !ruby/object:Gem::Dependency
24
- name: i18n
23
+ type: :runtime
25
24
  prerelease: false
26
- requirement: &id001 !ruby/object:Gem::Requirement
25
+ name: i18n
26
+ version_requirements: &id001 !ruby/object:Gem::Requirement
27
27
  none: false
28
28
  requirements:
29
29
  - - ">="
@@ -32,8 +32,7 @@ dependencies:
32
32
  segments:
33
33
  - 0
34
34
  version: "0"
35
- type: :runtime
36
- version_requirements: *id001
35
+ requirement: *id001
37
36
  description:
38
37
  email: support@thoughtbot.com
39
38
  executables: []
@@ -73,6 +72,7 @@ files:
73
72
  - spec/support/fake_html_safe_string.rb
74
73
  - spec/support/fake_logger.rb
75
74
  - spec/support/fake_passenger.rb
75
+ - spec/support/fake_resque_job.rb
76
76
  - spec/support/fake_unicorn.rb
77
77
  - features/rails.feature
78
78
  - features/step_definitions/copycopter_server_steps.rb
@@ -113,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
113
  requirements: []
114
114
 
115
115
  rubyforge_project: copycopter_client
116
- rubygems_version: 1.4.1
116
+ rubygems_version: 1.3.7
117
117
  signing_key:
118
118
  specification_version: 3
119
119
  summary: Client for the Copycopter content management service