rhosync 2.1.16 → 2.1.17.beta1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ ## 2.1.17 (not yet released)
2
+ * Refactor server middleware. Remove explicit rack dependency. Fix broken rspec examples.
3
+ * Fixing error with recursive loading of application.rb in ruby-1.8.7 and ree
4
+ * #28330213 - Implementing fast_insert/update/delete API
5
+ * #27612327 - Bulk Sync Not Returning Errors from Zendesk ticket #2336
6
+ * #28219647 - Schema Change Error from Zendesk ticket #2353
7
+ * #28328057 - Feature Request: Adding a Bulk Data Job after_perform hook from Zendesk ticket #2367
8
+
1
9
  ## 2.1.16
2
10
  * revert rack support for v. 1.4.1 and lock to v. 1.3.6 due to problems with Heroku deployment (Rack 1.4.1 produces 502 error)
3
11
 
data/Rakefile CHANGED
@@ -66,7 +66,6 @@ begin
66
66
  gemspec.files = FileList["[A-Z]*", "{bench,bin,generators,lib,spec,tasks}/**/*"]
67
67
 
68
68
  # TODO: Due to https://www.pivotaltracker.com/story/show/3417862, we can't use JSON 1.4.3
69
- gemspec.add_dependency "rack", "~> 1.3.6"
70
69
  gemspec.add_dependency "sinatra", "~> 1.3.1"
71
70
  gemspec.add_dependency "json", "~>1.4.2"
72
71
  gemspec.add_dependency "sqlite3-ruby", "~>1.2.5"
@@ -137,7 +137,7 @@ module Rhosync
137
137
  if secret == '<changeme>'
138
138
  log "*"*60+"\n\n"
139
139
  log "WARNING: Change the session secret in config.ru from <changeme> to something secure."
140
- log " i.e. running `rake secret` in a rails app will generate a secret you could use.\n\n"
140
+ log " i.e. running `rake rhosync:secret` in your rhosync app directory will generate a secret you could use.\n\n"
141
141
  log "*"*60
142
142
  end
143
143
  end
@@ -0,0 +1,8 @@
1
+ Server.api :fast_delete do |params,user|
2
+ source = Source.load(params[:source_id],{:app_id=>APP_NAME,:user_id=>params[:user_id]})
3
+ source_sync = SourceSync.new(source)
4
+ timeout = params[:timeout] || 10
5
+ raise_on_expire = params[:raise_on_expire] || false
6
+ source_sync.fast_delete(params[:data], timeout,raise_on_expire)
7
+ 'done'
8
+ end
@@ -0,0 +1,8 @@
1
+ Server.api :fast_insert do |params,user|
2
+ source = Source.load(params[:source_id],{:app_id=>APP_NAME,:user_id=>params[:user_id]})
3
+ source_sync = SourceSync.new(source)
4
+ timeout = params[:timeout] || 10
5
+ raise_on_expire = params[:raise_on_expire] || false
6
+ source_sync.fast_insert(params[:data], timeout,raise_on_expire)
7
+ 'done'
8
+ end
@@ -0,0 +1,8 @@
1
+ Server.api :fast_update do |params,user|
2
+ source = Source.load(params[:source_id],{:app_id=>APP_NAME,:user_id=>params[:user_id]})
3
+ source_sync = SourceSync.new(source)
4
+ timeout = params[:timeout] || 10
5
+ raise_on_expire = params[:raise_on_expire] || false
6
+ source_sync.fast_update(params[:delete_data], params[:data],timeout,raise_on_expire)
7
+ 'done'
8
+ end
@@ -9,9 +9,6 @@ module Rhosync
9
9
  class << self
10
10
  def create(fields={})
11
11
  fields[:id] = fields[:name]
12
- begin
13
- require under_score(fields[:name])
14
- rescue Exception; end
15
12
  super(fields)
16
13
  end
17
14
  end
@@ -54,7 +54,11 @@ module Rhosync
54
54
  end
55
55
  true
56
56
  end
57
-
57
+
58
+ def delete_files
59
+ FileUtils.rm Dir.glob(File.join(Rhosync.base_directory, "#{self.url}*"))
60
+ end
61
+
58
62
  class << self
59
63
  def create(fields={})
60
64
  fields[:id] = fields[:name]
@@ -256,6 +256,21 @@ module Rhosync
256
256
 
257
257
  if data and data.completed? and data.dbfiles_exist?
258
258
  client.update_clientdoc(sources)
259
+ sources.each do |src|
260
+ s = Source.load(src, {:user_id => client.user_id, :app_id => client.app_id})
261
+ errordoc = s.docname(:errors)
262
+ errors = {}
263
+ Store.lock(errordoc) do
264
+ errors = Store.get_data(errordoc)
265
+ end
266
+ unless errors.empty?
267
+ # FIXME: :result => :bulk_sync_error, :errors => "#{errors}"
268
+ log "Bulk sync errors are found in #{src}: #{errors}"
269
+ # Delete all related bulk files
270
+ data.delete_files
271
+ return {:result => :url, :url => ''}
272
+ end
273
+ end
259
274
  {:result => :url, :url => data.url}
260
275
  elsif data
261
276
  {:result => :wait}
@@ -21,8 +21,8 @@ module Rhosync
21
21
  create_hsql_data_file(bulk_data,ts) if Rhosync.blackberry_bulk_sync
22
22
  lap_timer('create_hsql_data_file',timer)
23
23
  log "finished bulk data process"
24
- bulk_data.state = :completed
25
- bulk_data.refresh_time = Time.now.to_i + Rhosync.bulk_sync_poll_interval
24
+ #bulk_data.state = :completed
25
+ #bulk_data.refresh_time = Time.now.to_i + Rhosync.bulk_sync_poll_interval
26
26
  else
27
27
  raise Exception.new("No bulk data found for #{params["data_name"]}")
28
28
  end
@@ -33,6 +33,26 @@ module Rhosync
33
33
  raise e
34
34
  end
35
35
  end
36
+
37
+ def self.after_perform_x(*args)
38
+ log "BulkDataJob.after_perform_x hook called ..."
39
+ params = args[0] # 1st parameter is bulk data
40
+ begin
41
+ bulk_data = BulkData.load(params["data_name"]) if BulkData.is_exist?(params["data_name"])
42
+ if bulk_data
43
+ bulk_data.state = :completed
44
+ bulk_data.refresh_time = Time.now.to_i + Rhosync.bulk_sync_poll_interval
45
+ log "BulkDataJob.after_perform_x hook set data state to complete."
46
+ else
47
+ raise Exception.new("No bulk data found for #{params["data_name"]}")
48
+ end
49
+ rescue Exception => e
50
+ bulk_data.delete if bulk_data
51
+ log "Bulk data after_perform_x raised: #{e.message}"
52
+ log e.backtrace.join("\n")
53
+ raise e
54
+ end
55
+ end
36
56
 
37
57
  def self.import_data_to_object_values(db,source)
38
58
  data = source.get_data(:md)
@@ -28,13 +28,28 @@ module Rhosync
28
28
  set :stats, false
29
29
 
30
30
  # default secret
31
- @@secret = '<changeme>'
31
+ @secret = '<changeme>'
32
32
 
33
33
  # Setup route and mimetype for bulk data downloads
34
34
  # TODO: Figure out why "mime :data, 'application/octet-stream'" doesn't work
35
35
  Rack::Mime::MIME_TYPES['.data'] = 'application/octet-stream'
36
36
 
37
37
  include Rhosync
38
+
39
+ # Set rhosync middleware
40
+ set :use_middleware, Proc.new {
41
+ return false if @middleware_configured # Middleware might be configured only once!
42
+
43
+ use Rhosync::BodyContentTypeParser
44
+ use Rhosync::Stats::Middleware
45
+ Rhosync::Server.set :secret, @secret unless settings.respond_to?(:secret)
46
+ use Rack::Session::Cookie,
47
+ :key => 'rhosync_session',
48
+ :expire_after => 31536000,
49
+ :secret => Rhosync::Server.secret
50
+
51
+ @middleware_configured ||= true
52
+ }
38
53
 
39
54
  helpers do
40
55
  def request_action
@@ -145,19 +160,13 @@ module Rhosync
145
160
 
146
161
  # hook into new so we can enable middleware
147
162
  def self.new
148
- use Rhosync::BodyContentTypeParser
149
163
  if settings.respond_to?(:stats) and settings.send(:stats) == true
150
- use Rhosync::Stats::Middleware
151
- Rhosync.stats = true
164
+ Rhosync.stats = true
152
165
  else
153
- Rhosync::Server.disable :stats
154
- Rhosync.stats = false
166
+ Rhosync::Server.disable :stats
167
+ Rhosync.stats = false
155
168
  end
156
- Rhosync::Server.set :secret, @@secret unless settings.respond_to?(:secret)
157
- use Rack::Session::Cookie,
158
- :key => 'rhosync_session',
159
- :expire_after => 31536000,
160
- :secret => Rhosync::Server.secret
169
+ settings.use_middleware
161
170
  super
162
171
  end
163
172
 
@@ -81,6 +81,29 @@ module Rhosync
81
81
  @source.app_id,@source.user_id,client_id,params)
82
82
  end
83
83
 
84
+ def fast_insert(new_objs, timeout=10,raise_on_expire=false)
85
+ @source.lock(:md,timeout,raise_on_expire) do |s|
86
+ diff_count = new_objs.size
87
+ @source.put_data(:md, new_objs, true)
88
+ @source.update_count(:md_size,diff_count)
89
+ end
90
+ end
91
+
92
+ def fast_update(orig_hash, new_hash, timeout=10,raise_on_expire=false)
93
+ @source.lock(:md,timeout,raise_on_expire) do |s|
94
+ @source.delete_data(:md, orig_hash)
95
+ @source.put_data(:md, new_hash, true)
96
+ end
97
+ end
98
+
99
+ def fast_delete(delete_objs, timeout=10,raise_on_expire=false)
100
+ @source.lock(:md,timeout,raise_on_expire) do |s|
101
+ diff_count = -delete_objs.size
102
+ @source.delete_data(:md, delete_objs)
103
+ @source.update_count(:md_size,diff_count)
104
+ end
105
+ end
106
+
84
107
  def push_objects(objects,timeout=10,raise_on_expire=false,rebuild_md=true)
85
108
  @source.lock(:md,timeout,raise_on_expire) do |s|
86
109
  diff_count = 0
@@ -259,7 +282,14 @@ module Rhosync
259
282
  data = @adapter.send(method)
260
283
  if data
261
284
  @source.put_value(method,data)
262
- @source.put_value("#{method}_sha1",Digest::SHA1.hexdigest(data))
285
+ if method == :schema
286
+ parsed = JSON.parse(data)
287
+ schema_version = parsed['version']
288
+ raise "Mandatory version key is not defined in source adapter schema method" if schema_version.nil?
289
+ @source.put_value("#{method}_sha1",Digest::SHA1.hexdigest(schema_version))
290
+ else
291
+ @source.put_value("#{method}_sha1",Digest::SHA1.hexdigest(data))
292
+ end
263
293
  end
264
294
  end
265
295
  end
@@ -6,14 +6,19 @@ module Rhosync
6
6
  end
7
7
 
8
8
  def call(env)
9
- start = Time.now.to_f
10
- status, headers, body = @app.call(env)
11
- finish = Time.now.to_f
12
- metric = "http:#{env['REQUEST_METHOD']}:#{env['PATH_INFO']}"
13
- source_name = env['rack.request.query_hash']["source_name"] if env['rack.request.query_hash']
14
- metric << ":#{source_name}" if source_name
15
- Record.save_average(metric,finish - start)
16
- [status, headers, body]
9
+ if Rhosync.stats || Rhosync::Server.stats
10
+ start = Time.now.to_f
11
+ status, headers, body = @app.call(env)
12
+ finish = Time.now.to_f
13
+ metric = "http:#{env['REQUEST_METHOD']}:#{env['PATH_INFO']}"
14
+ source_name = env['rack.request.query_hash']["source_name"] if env['rack.request.query_hash']
15
+ metric << ":#{source_name}" if source_name
16
+ Record.save_average(metric,finish - start)
17
+ [status, headers, body]
18
+ else
19
+ status, headers, body = @app.call(env)
20
+ [status, headers, body]
21
+ end
17
22
  end
18
23
  end
19
24
  end
@@ -1,3 +1,3 @@
1
1
  module Rhosync
2
- VERSION = '2.1.16'
2
+ VERSION = '2.1.17.beta1'
3
3
  end
@@ -24,6 +24,8 @@ describe "ApiHelper", :shared => true do
24
24
  end
25
25
 
26
26
  def app
27
+ Rhosync::Server.set :stats, false
28
+ Rhosync.stats = false
27
29
  @app ||= Rhosync::Server.new
28
30
  end
29
31
 
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhoconnectApiFastDelete" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should delete an object from rhosync's :md" do
7
+ data = {'1' => @product1, '2' => @product2, '3' => @product3}
8
+ @s = Source.load(@s_fields[:name],@s_params)
9
+ set_state(@s.docname(:md) => data,@s.docname(:md_size) => '3')
10
+ post "/api/fast_delete", :api_token => @api_token,
11
+ :user_id => @u.id, :source_id => @s_fields[:name], :data => {'3' => @product3}
12
+ last_response.should be_ok
13
+ data.delete('3')
14
+ verify_result(@s.docname(:md) => data,@s.docname(:md_size)=>'2')
15
+ end
16
+
17
+ it "should not properly delete the object if fast_delete is called without all the attributes (because fast_delete doesn't ensure any data integrity)" do
18
+ data = {'1' => @product1, '2' => @product2, '3' => @product3}
19
+ delete_data = {'3' => {'price' => '1.99'}}
20
+ @s = Source.load(@s_fields[:name],@s_params)
21
+ set_state(@s.docname(:md) => data,@s.docname(:md_size) => '3')
22
+ post "/api/fast_delete", :api_token => @api_token,
23
+ :user_id => @u.id, :source_id => @s_fields[:name], :data => delete_data
24
+ last_response.should be_ok
25
+ verify_result(@s.docname(:md) => data,@s.docname(:md_size)=>'2')
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhoconnectApiFastInsert" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should append new objects to rhosync's :md" do
7
+ data = {'1' => @product1, '2' => @product2}
8
+ @s = Source.load(@s_fields[:name],@s_params)
9
+ set_state(@s.docname(:md) => data,@s.docname(:md_size) => '2')
10
+ post "/api/fast_insert", :api_token => @api_token,
11
+ :user_id => @u.id, :source_id => @s_fields[:name], :data => {'3' => @product3}
12
+ last_response.should be_ok
13
+ data.merge!({'3' => @product3})
14
+ verify_result(@s.docname(:md) => data,@s.docname(:md_size)=>'3')
15
+ end
16
+
17
+ it "should incorrectly append data to existing object (because fast_insert doesn't ensure any data integrity)" do
18
+ data = {'1' => @product1, '2' => @product2, '3' => @product3}
19
+ incorrect_insert = {'3' => {'price' => '1.99', 'new_field' => 'value'}}
20
+ @s = Source.load(@s_fields[:name],@s_params)
21
+ set_state(@s.docname(:md) => data,@s.docname(:md_size) => '3')
22
+ post "/api/fast_insert", :api_token => @api_token,
23
+ :user_id => @u.id, :source_id => @s_fields[:name], :data => incorrect_insert
24
+ last_response.should be_ok
25
+ data['3'].merge!(incorrect_insert['3'])
26
+ verify_result(@s.docname(:md) => data,@s.docname(:md_size)=>'4')
27
+ end
28
+ end
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(__FILE__),'api_helper')
2
+
3
+ describe "RhoconnectApiFastUpdate" do
4
+ it_should_behave_like "ApiHelper"
5
+
6
+ it "should update an attribute and add new one for an object in rhosync's :md" do
7
+ data = {'1' => @product1, '2' => @product2, '3' => @product3}
8
+ @s = Source.load(@s_fields[:name],@s_params)
9
+ set_state(@s.docname(:md) => data,@s.docname(:md_size) => '3')
10
+
11
+ orig_obj_attrs = {'3' => {'price' => @product3['price']}}
12
+ new_obj_attrs = {'3' => {'price' => '0.99', 'new_attr' => 'new_value'}}
13
+
14
+ post "/api/fast_update", :api_token => @api_token,
15
+ :user_id => @u.id, :source_id => @s_fields[:name], :delete_data => orig_obj_attrs, :data => new_obj_attrs
16
+ last_response.should be_ok
17
+ data['3'].merge!(new_obj_attrs['3'])
18
+ verify_result(@s.docname(:md) => data,@s.docname(:md_size)=>'3')
19
+ end
20
+
21
+ it "should update one attr, add one attr, and remove one attr for an object in rhosync's :md" do
22
+ data = {'1' => @product1, '2' => @product2, '3' => @product3}
23
+ @s = Source.load(@s_fields[:name],@s_params)
24
+ set_state(@s.docname(:md) => data,@s.docname(:md_size) => '3')
25
+
26
+ orig_obj_attrs = {'3' => {'name' => @product3['name'], 'price' => @product3['price']}}
27
+ new_obj_attrs = {'3' => {'price' => '0.99', 'new_attr' => 'new_value'}}
28
+
29
+ post "/api/fast_update", :api_token => @api_token,
30
+ :user_id => @u.id, :source_id => @s_fields[:name], :delete_data => orig_obj_attrs, :data => new_obj_attrs
31
+ last_response.should be_ok
32
+ data['3'].delete('name')
33
+ data['3'].merge!(new_obj_attrs['3'])
34
+ verify_result(@s.docname(:md) => data,@s.docname(:md_size)=>'3')
35
+ end
36
+
37
+ it "should remove all attributes , but leave the count incorrect (because fast_update doesn't check if the whole object is deleted)" do
38
+ data = {'1' => @product1, '2' => @product2, '3' => @product3}
39
+ @s = Source.load(@s_fields[:name],@s_params)
40
+ set_state(@s.docname(:md) => data,@s.docname(:md_size) => '3')
41
+
42
+ orig_obj_attrs = {'3' => @product3}
43
+ new_obj_attrs = {}
44
+
45
+ post "/api/fast_update", :api_token => @api_token,
46
+ :user_id => @u.id, :source_id => @s_fields[:name], :delete_data => orig_obj_attrs, :data => new_obj_attrs
47
+ last_response.should be_ok
48
+ data.delete('3')
49
+ verify_result(@s.docname(:md) => data,@s.docname(:md_size)=>'3')
50
+ end
51
+ end
@@ -9,10 +9,12 @@ describe "RhosyncApiStats" do
9
9
 
10
10
  before(:each) do
11
11
  Rhosync::Server.set :stats, true
12
+ Rhosync.stats = true
12
13
  end
13
14
 
14
15
  after(:each) do
15
16
  Rhosync::Server.set :stats, false
17
+ Rhosync.stats = false
16
18
  end
17
19
 
18
20
  it "should retrieve metric names" do
@@ -9,6 +9,9 @@ describe "ClientSync" do
9
9
  lambda { ClientSync.new(nil,@c,2) }.should raise_error(ArgumentError,'Unknown source')
10
10
  end
11
11
 
12
+ let(:mock_schema) { {"property" => { "name" => "string", "brand" => "string" }, "version" => "1.0"} }
13
+ let(:sha1) { get_sha1(mock_schema['version']) }
14
+
12
15
  before(:each) do
13
16
  @s = Source.load(@s_fields[:name],@s_params)
14
17
  @cs = ClientSync.new(@s,@c,2)
@@ -372,7 +375,6 @@ describe "ClientSync" do
372
375
  token1.should be_nil
373
376
  Store.get_data(@c.docname(:search)).should == {}
374
377
  end
375
-
376
378
  end
377
379
 
378
380
  describe "page methods" do
@@ -380,10 +382,20 @@ describe "ClientSync" do
380
382
  Store.put_data(@s.docname(:md),@data).should == true
381
383
  Store.get_data(@s.docname(:md)).should == @data
382
384
  Store.put_value(@s.docname(:md_size),@data.size)
383
- @expected = {'1'=>@product1,'2'=>@product2}
384
- @cs.compute_page.should == [0,3,@expected]
385
+
386
+ progress_count, total_count, res = @cs.compute_page
387
+ progress_count.to_i.should == 0
388
+ total_count.to_i.should == 3
389
+ res.each do |key, value|
390
+ @data.has_key?(key).should == true
391
+ @data[key].should == value
392
+ end
393
+
385
394
  Store.get_value(@cs.client.docname(:cd_size)).to_i.should == 0
386
- Store.get_data(@cs.client.docname(:page)).should == @expected
395
+ Store.get_data(@cs.client.docname(:page)).each do |key, value|
396
+ @data.has_key?(key).should == true
397
+ @data[key].should == value
398
+ end
387
399
  end
388
400
 
389
401
  it "appends diff to the client document" do
@@ -483,7 +495,7 @@ describe "ClientSync" do
483
495
  token = @c.get_value(:page_token)
484
496
  result.should == [{"version"=>ClientSync::VERSION},{"token"=>token},
485
497
  {"count"=>1}, {"progress_count"=>0},{"total_count"=>1},{'insert'=>expected}]
486
- @c.get_value(:schema_sha1).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
498
+ @c.get_value(:schema_sha1).should == sha1
487
499
  end
488
500
  end
489
501
 
@@ -494,8 +506,8 @@ describe "ClientSync" do
494
506
  token = @c.get_value(:page_token)
495
507
  result.should == [{"version"=>ClientSync::VERSION},{"token"=>token},
496
508
  {"count"=>0}, {"progress_count"=>0},{"total_count"=>0},{'schema-changed'=>'true'}]
497
- @c.get_value(:schema_page).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
498
- @c.get_value(:schema_sha1).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
509
+ @c.get_value(:schema_page).should == sha1
510
+ @c.get_value(:schema_sha1).should == sha1
499
511
  end
500
512
  end
501
513
 
@@ -506,8 +518,8 @@ describe "ClientSync" do
506
518
  token = @c.get_value(:page_token)
507
519
  @cs.send_cud.should == [{"version"=>ClientSync::VERSION},{"token"=>token},
508
520
  {"count"=>0}, {"progress_count"=>0},{"total_count"=>0},{'schema-changed'=>'true'}]
509
- @c.get_value(:schema_page).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
510
- @c.get_value(:schema_sha1).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
521
+ @c.get_value(:schema_page).should == sha1
522
+ @c.get_value(:schema_sha1).should == sha1
511
523
  end
512
524
  end
513
525
 
@@ -519,7 +531,7 @@ describe "ClientSync" do
519
531
  @cs.send_cud(token).should == [{"version"=>ClientSync::VERSION},{"token"=>""},
520
532
  {"count"=>0}, {"progress_count"=>0},{"total_count"=>0},{}]
521
533
  @c.get_value(:schema_page).should be_nil
522
- @c.get_value(:schema_sha1).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
534
+ @c.get_value(:schema_sha1).should == sha1
523
535
  end
524
536
  end
525
537
 
@@ -538,7 +550,7 @@ describe "ClientSync" do
538
550
  @cs.send_cud(token).should == [{"version"=>ClientSync::VERSION},{"token"=>""},
539
551
  {"count"=>0}, {"progress_count"=>0},{"total_count"=>0},{}]
540
552
  @c.get_value(:schema_page).should be_nil
541
- @c.get_value(:schema_sha1).should == '8c148c8c1a66c7baf685c07d58bea360da87981b'
553
+ @c.get_value(:schema_sha1).should == sha1
542
554
  data = BulkData.load(docname)
543
555
  data.refresh_time.should <= Time.now.to_i
544
556
  end
@@ -574,6 +586,7 @@ describe "ClientSync" do
574
586
  set_state('test_db_storage' => @data)
575
587
  ClientSync.bulk_data(:user,@c)
576
588
  BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,@u.id))
589
+ BulkDataJob.after_perform_x("data_name" => bulk_data_docname(@a.id,@u.id))
577
590
  ClientSync.bulk_data(:user,@c).should == {:result => :url,
578
591
  :url => BulkData.load(bulk_data_docname(@a.id,@u.id)).url}
579
592
  verify_result(
@@ -581,6 +594,18 @@ describe "ClientSync" do
581
594
  "source:#{@a_fields[:name]}:#{@u_fields[:login]}:#{@s_fields[:name]}:md" => @data,
582
595
  "source:#{@a_fields[:name]}:#{@u_fields[:login]}:#{@s_fields[:name]}:md_copy" => @data)
583
596
  end
597
+
598
+ it "should return empty bulk data url if there are errors in query" do
599
+ ClientSync.bulk_data(:user,@c)
600
+ BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,@u.id))
601
+ BulkDataJob.after_perform_x("data_name" => bulk_data_docname(@a.id,@u.id))
602
+ errordoc = @s.docname(:errors) # source SampleAdapter
603
+ operation = 'query'
604
+ Store.lock(errordoc) do
605
+ Store.put_data(errordoc,{"#{operation}-error"=>{'message'=>"Some exception message"}}, true)
606
+ end
607
+ ClientSync.bulk_data(:user,@c).should == {:result => :url, :url => ''}
608
+ end
584
609
 
585
610
  it "should escape bulk data url" do
586
611
  name = 'a b'
@@ -590,6 +615,7 @@ describe "ClientSync" do
590
615
  :user_id => name,
591
616
  :sources => [@s_fields[:name]])
592
617
  BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,name))
618
+ BulkDataJob.after_perform_x("data_name" => bulk_data_docname(@a.id,name))
593
619
  data = BulkData.load(bulk_data_docname(@a.id,name))
594
620
  data.url.should match /a%20b/
595
621
  data.delete
@@ -600,6 +626,7 @@ describe "ClientSync" do
600
626
  @s.partition = :app
601
627
  ClientSync.bulk_data(:app,@c)
602
628
  BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,"*"))
629
+ BulkDataJob.after_perform_x("data_name" => bulk_data_docname(@a.id,"*"))
603
630
  ClientSync.bulk_data(:app,@c).should == {:result => :url,
604
631
  :url => BulkData.load(bulk_data_docname(@a.id,"*")).url}
605
632
  verify_result(
@@ -613,6 +640,7 @@ describe "ClientSync" do
613
640
  @s.sync_type = :bulk_sync_only
614
641
  ClientSync.bulk_data(:user,@c)
615
642
  BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,@u.id))
643
+ BulkDataJob.after_perform_x("data_name" => bulk_data_docname(@a.id,@u.id))
616
644
  ClientSync.bulk_data(:user,@c).should == {:result => :url,
617
645
  :url => BulkData.load(bulk_data_docname(@a.id,@u.id)).url}
618
646
  verify_result(
@@ -626,6 +654,7 @@ describe "ClientSync" do
626
654
  Rhosync.blackberry_bulk_sync = true
627
655
  ClientSync.bulk_data(:user,@c)
628
656
  BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,@u.id))
657
+ BulkDataJob.after_perform_x("data_name" => bulk_data_docname(@a.id,@u.id))
629
658
  data = BulkData.load(bulk_data_docname(@a.id,@u.id))
630
659
  ClientSync.bulk_data(:user,@c).should == {:result => :url, :url => data.url}
631
660
  File.delete(data.dbfile)
@@ -12,6 +12,8 @@ describe "BulkDataJob" do
12
12
  delete_data_directory
13
13
  end
14
14
 
15
+ let(:mock_schema) { {"property" => { "name" => "string", "brand" => "string" }, "version" => "1.0"} }
16
+
15
17
  it "should create bulk data files from master document" do
16
18
  set_state('test_db_storage' => @data)
17
19
  docname = bulk_data_docname(@a.id,@u.id)
@@ -24,6 +26,7 @@ describe "BulkDataJob" do
24
26
  :user_id => @u.id,
25
27
  :sources => [@s_fields[:name], 'FixedSchemaAdapter'])
26
28
  BulkDataJob.perform("data_name" => data.name)
29
+ BulkDataJob.after_perform_x("data_name" => data.name)
27
30
  data = BulkData.load(docname)
28
31
  data.completed?.should == true
29
32
  verify_result(@s.docname(:md) => @data,@s.docname(:md_copy) => @data)
@@ -51,6 +54,7 @@ describe "BulkDataJob" do
51
54
  :user_id => @u.id,
52
55
  :sources => [@s_fields[:name]])
53
56
  BulkDataJob.perform("data_name" => data.name)
57
+ BulkDataJob.after_perform_x("data_name" => data.name)
54
58
  data = BulkData.load(docname)
55
59
  data.completed?.should == true
56
60
  verify_result(@s.docname(:md) => @data,@s.docname(:md_copy) => @data)
@@ -69,6 +73,7 @@ describe "BulkDataJob" do
69
73
  :user_id => @u.id,
70
74
  :sources => [@s_fields[:name]])
71
75
  BulkDataJob.perform("data_name" => data.name)
76
+ BulkDataJob.after_perform_x("data_name" => data.name)
72
77
  data = BulkData.load(docname)
73
78
  data.completed?.should == true
74
79
  verify_result(@s.docname(:md) => @data,
@@ -88,11 +93,11 @@ describe "BulkDataJob" do
88
93
  :user_id => @u.id,
89
94
  :sources => [@s_fields[:name]])
90
95
  BulkDataJob.perform("data_name" => data.name)
96
+ BulkDataJob.after_perform_x("data_name" => data.name)
91
97
  data = BulkData.load(docname)
92
98
  data.completed?.should == true
93
- verify_result(@s.docname(:md) => @data,
94
- @s.docname(:schema) => "{\"property\":{\"brand\":\"string\",\"name\":\"string\"},\"version\":\"1.0\"}",
95
- @s.docname(:md_copy) => @data)
99
+ verify_result(@s.docname(:md) => @data, @s.docname(:md_copy) => @data)
100
+ JSON.parse(Store.get_value(@s.docname(:schema))).should == mock_schema
96
101
  validate_db(data,@s.name => @data).should == true
97
102
  end
98
103
  end
@@ -108,7 +113,9 @@ describe "BulkDataJob" do
108
113
  end
109
114
 
110
115
  it "should delete bulk data if exception is raised" do
111
- lambda { BulkDataJob.perform("data_name" => 'broken') }.should raise_error(Exception)
116
+ lambda {
117
+ BulkDataJob.perform("data_name" => 'broken')
118
+ BulkDataJob.after_perform_x("data_name" => data.name) }.should raise_error(Exception)
112
119
  Store.db.keys('bulk_data*').should == []
113
120
  end
114
121
 
@@ -118,7 +125,9 @@ describe "BulkDataJob" do
118
125
  :app_id => 'broken',
119
126
  :user_id => @u.id,
120
127
  :sources => [@s_fields[:name]])
121
- lambda { BulkDataJob.perform("data_name" => data.name) }.should raise_error(Exception)
128
+ lambda {
129
+ BulkDataJob.perform("data_name" => data.name)
130
+ BulkDataJob.after_perform_x("data_name" => data.name) }.should raise_error(Exception)
122
131
  Store.db.keys('bulk_data*').should == []
123
132
  end
124
133
  end
@@ -27,6 +27,7 @@ describe "BulkData Performance" do
27
27
  :user_id => @u.id,
28
28
  :sources => [@s_fields[:name]])
29
29
  BulkDataJob.perform("data_name" => data.name)
30
+ BulkDataJob.after_perform_x("data_name" => data.name)
30
31
  lap_timer('BulkDataJob.perform duration',start)
31
32
  end
32
33
  end
@@ -41,18 +41,24 @@ describe "Ping Apple" do
41
41
  end
42
42
 
43
43
  it "should compute apn_message" do
44
- expected = <<-eos
45
- \000\000 \253\315\000g{"aps":{"vibrate":"5","badge":5,"sound":"hello.mp3","alert":"hello world"},"do_sync":["SampleAdapter"]}
46
- eos
47
- Apple.apn_message(@params).should == expected.strip!
44
+ expected_hash = {
45
+ "aps"=>{"vibrate"=>"5", "badge"=>5, "alert"=>"hello world", "sound"=>"hello.mp3"},
46
+ "do_sync"=>["SampleAdapter"]
47
+ }
48
+ apn_message = Apple.apn_message(@params)
49
+ apn_message.start_with?("\000\000 \253\315\000g").should be_true
50
+ JSON.parse(apn_message.sub("\000\000 \253\315\000g","")).should == expected_hash
48
51
  end
49
52
 
50
53
  it "should compute apn_message with source array" do
51
54
  @params['sources'] << 'SimpleAdapter'
52
- expected = <<-eos
53
- \000\000 \253\315\000w{"aps":{"vibrate":"5","badge":5,"sound":"hello.mp3","alert":"hello world"},"do_sync":["SampleAdapter","SimpleAdapter"]}
54
- eos
55
- Apple.apn_message(@params).should == expected.strip!
55
+ expected_hash = {
56
+ "aps"=>{"vibrate"=>"5", "badge"=>5, "alert"=>"hello world", "sound"=>"hello.mp3"},
57
+ "do_sync"=>["SampleAdapter", "SimpleAdapter"]
58
+ }
59
+ apn_message = Apple.apn_message(@params)
60
+ apn_message.start_with?("\000\000 \253\315\000w").should be_true
61
+ JSON.parse(apn_message.sub("\000\000 \253\315\000w","")).should == expected_hash
56
62
  end
57
63
 
58
64
  it "should raise SocketError if socket fails" do
@@ -70,14 +70,6 @@ describe "Server" do
70
70
  Rhosync::Server.secret.should == "secure!"
71
71
  end
72
72
 
73
- it "should use Stats::Middleware if stats enabled" do
74
- Rhosync::Server.enable :stats
75
- Rhosync::Server.new
76
- Rhosync.stats.should == true
77
- Rhosync.stats = nil
78
- Rhosync::Server.disable :stats
79
- end
80
-
81
73
  it "should update session secret to default" do
82
74
  Rhosync::Server.set :secret, "<changeme>"
83
75
  Rhosync::Server.secret.should == "<changeme>"
@@ -380,6 +372,7 @@ describe "Server" do
380
372
  set_state('test_db_storage' => @data)
381
373
  get "/application/bulk_data", :partition => :user, :client_id => @c.id
382
374
  BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,@u.id))
375
+ BulkDataJob.after_perform_x("data_name" => bulk_data_docname(@a.id,@u.id))
383
376
  get "/application/bulk_data", :partition => :user, :client_id => @c.id
384
377
  last_response.should be_ok
385
378
  data = BulkData.load(bulk_data_docname(@a.id,@u.id))
@@ -392,6 +385,7 @@ describe "Server" do
392
385
  set_state('test_db_storage' => @data)
393
386
  get "/application/bulk_data", :partition => :user, :client_id => @c.id
394
387
  BulkDataJob.perform("data_name" => bulk_data_docname(@a.id,@u.id))
388
+ BulkDataJob.after_perform_x("data_name" => bulk_data_docname(@a.id,@u.id))
395
389
  get "/application/bulk_data", :partition => :user, :client_id => @c.id
396
390
  get JSON.parse(last_response.body)["url"]
397
391
  last_response.should be_ok
@@ -9,6 +9,8 @@ describe "SourceSync" do
9
9
  @ss = SourceSync.new(@s)
10
10
  end
11
11
 
12
+ let(:mock_schema) { {"property" => { "name" => "string", "brand" => "string" }, "version" => "1.0"} }
13
+
12
14
  it "should create SourceSync" do
13
15
  @ss.adapter.is_a?(SampleAdapter).should == true
14
16
  end
@@ -78,12 +80,25 @@ describe "SourceSync" do
78
80
  expected = {'1'=>@product1,'2'=>@product2}
79
81
  set_state('test_db_storage' => expected)
80
82
  @ss.process_query
81
- verify_result(@s.docname(:md) => expected,
82
- @s.docname(:schema) => "{\"property\":{\"brand\":\"string\",\"name\":\"string\"},\"version\":\"1.0\"}",
83
- @s.docname(:schema_sha1) => "8c148c8c1a66c7baf685c07d58bea360da87981b")
83
+ verify_result(@s.docname(:md) => expected)
84
+ JSON.parse(Store.get_value(@s.docname(:schema))).should == mock_schema
85
+ Store.get_value(@s.docname(:schema_sha1)).should == get_sha1(mock_schema['version'])
84
86
  end
85
87
  end
86
-
88
+
89
+ it "should raise exception if source adapter schema has no version key/value pair" do
90
+ mock_schema_no_version_method([SampleAdapter]) do
91
+ expected = {'1'=>@product1,'2'=>@product2}
92
+ set_state('test_db_storage' => expected)
93
+ @ss.process_query
94
+ errordoc = @s.docname(:errors)
95
+ errors = {}
96
+ Store.lock(errordoc) { errors = Store.get_data(errordoc) }
97
+ errors.empty?().should == false
98
+ errors["query-error"]["message"].should == "Mandatory version key is not defined in source adapter schema method"
99
+ end
100
+ end
101
+
87
102
  it "should process source adapter with stash" do
88
103
  expected = {'1'=>@product1,'2'=>@product2}
89
104
  set_state('test_db_storage' => expected)
@@ -152,7 +167,6 @@ describe "SourceSync" do
152
167
  verify_result(
153
168
  @c.docname(:update_errors) =>
154
169
  {"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]},
155
- @c.docname(:update) => {'4'=> { 'price' => '199.99'}},
156
170
  @c.docname(:update_rollback) => {ERROR=>{"price"=>"99.99"}})
157
171
  end
158
172
  end
@@ -174,9 +188,7 @@ describe "SourceSync" do
174
188
  data = add_error_object({'2'=>@product2},msg)
175
189
  set_state(@c.docname(:delete) => data)
176
190
  @ss.delete(@c.id)
177
- verify_result(@c.docname(:delete_errors) =>
178
- {"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]},
179
- @c.docname(:delete) => {'2'=>@product2})
191
+ verify_result(@c.docname(:delete_errors) => {"#{ERROR}-error"=>{"message"=>msg}, ERROR=>data[ERROR]})
180
192
  end
181
193
  end
182
194
 
@@ -194,6 +194,23 @@ module TestHelpers
194
194
  end
195
195
  end
196
196
 
197
+ def mock_schema_no_version_method(adapters, &block)
198
+ adapters.each do |klass|
199
+ klass.class_eval 'def schema
200
+ {
201
+ "property" => {
202
+ "name" => "string",
203
+ "brand" => "string"
204
+ }
205
+ }.to_json
206
+ end'
207
+ end
208
+ yield
209
+ adapters.each do |klass|
210
+ klass.class_eval "def schema; end"
211
+ end
212
+ end
213
+
197
214
  def unzip_file(file,file_dir)
198
215
  Zip::ZipFile.open(file) do |zip_file|
199
216
  zip_file.each do |f|
@@ -203,6 +220,11 @@ module TestHelpers
203
220
  end
204
221
  end
205
222
  end
223
+
224
+ def get_sha1(str)
225
+ Digest::SHA1.hexdigest(str)
226
+ end
227
+
206
228
  end #TestHelpers
207
229
 
208
230
  describe "RhosyncHelper", :shared => true do
@@ -1,4 +1,7 @@
1
+ require File.join(File.dirname(__FILE__),'..','spec_helper')
2
+ require File.join(File.dirname(__FILE__),'..','..','lib','rhosync','server.rb')
1
3
  require 'rhosync'
4
+
2
5
  STATS_RECORD_RESOLUTION = 2 unless defined? STATS_RECORD_RESOLUTION
3
6
  STATS_RECORD_SIZE = 8 unless defined? STATS_RECORD_SIZE
4
7
 
@@ -12,10 +15,17 @@ describe "Middleware" do
12
15
  Store.db.flushdb
13
16
  app = mock('app')
14
17
  app.stub!(:call)
18
+ Rhosync.stats = true
19
+ Rhosync::Server.enable :stats
15
20
  @middleware = Middleware.new(app)
16
21
  Store.stub!(:lock).and_yield
17
22
  end
18
-
23
+
24
+ after(:each) do
25
+ Rhosync.stats = false
26
+ Rhosync::Server.disable :stats
27
+ end
28
+
19
29
  it "should compute http average" do
20
30
  Time.stub!(:now).and_return { @now += 0.3; @now }
21
31
  env = {
metadata CHANGED
@@ -1,13 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rhosync
3
3
  version: !ruby/object:Gem::Version
4
- hash: 43
5
- prerelease:
4
+ hash: 391570199
5
+ prerelease: 7
6
6
  segments:
7
7
  - 2
8
8
  - 1
9
- - 16
10
- version: 2.1.16
9
+ - 17
10
+ - beta
11
+ - 1
12
+ version: 2.1.17.beta1
11
13
  platform: ruby
12
14
  authors:
13
15
  - Rhomobile
@@ -15,28 +17,12 @@ autorequire:
15
17
  bindir: bin
16
18
  cert_chain: []
17
19
 
18
- date: 2012-02-07 00:00:00 Z
20
+ date: 2012-04-24 00:00:00 Z
19
21
  dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: rack
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- none: false
25
- requirements:
26
- - - ~>
27
- - !ruby/object:Gem::Version
28
- hash: 23
29
- segments:
30
- - 1
31
- - 3
32
- - 6
33
- version: 1.3.6
34
- type: :runtime
35
- version_requirements: *id001
36
22
  - !ruby/object:Gem::Dependency
37
23
  name: sinatra
38
24
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ requirement: &id001 !ruby/object:Gem::Requirement
40
26
  none: false
41
27
  requirements:
42
28
  - - ~>
@@ -48,11 +34,11 @@ dependencies:
48
34
  - 1
49
35
  version: 1.3.1
50
36
  type: :runtime
51
- version_requirements: *id002
37
+ version_requirements: *id001
52
38
  - !ruby/object:Gem::Dependency
53
39
  name: json
54
40
  prerelease: false
55
- requirement: &id003 !ruby/object:Gem::Requirement
41
+ requirement: &id002 !ruby/object:Gem::Requirement
56
42
  none: false
57
43
  requirements:
58
44
  - - ~>
@@ -64,11 +50,11 @@ dependencies:
64
50
  - 2
65
51
  version: 1.4.2
66
52
  type: :runtime
67
- version_requirements: *id003
53
+ version_requirements: *id002
68
54
  - !ruby/object:Gem::Dependency
69
55
  name: sqlite3-ruby
70
56
  prerelease: false
71
- requirement: &id004 !ruby/object:Gem::Requirement
57
+ requirement: &id003 !ruby/object:Gem::Requirement
72
58
  none: false
73
59
  requirements:
74
60
  - - ~>
@@ -80,11 +66,11 @@ dependencies:
80
66
  - 5
81
67
  version: 1.2.5
82
68
  type: :runtime
83
- version_requirements: *id004
69
+ version_requirements: *id003
84
70
  - !ruby/object:Gem::Dependency
85
71
  name: rubyzip
86
72
  prerelease: false
87
- requirement: &id005 !ruby/object:Gem::Requirement
73
+ requirement: &id004 !ruby/object:Gem::Requirement
88
74
  none: false
89
75
  requirements:
90
76
  - - ~>
@@ -96,11 +82,11 @@ dependencies:
96
82
  - 4
97
83
  version: 0.9.4
98
84
  type: :runtime
99
- version_requirements: *id005
85
+ version_requirements: *id004
100
86
  - !ruby/object:Gem::Dependency
101
87
  name: uuidtools
102
88
  prerelease: false
103
- requirement: &id006 !ruby/object:Gem::Requirement
89
+ requirement: &id005 !ruby/object:Gem::Requirement
104
90
  none: false
105
91
  requirements:
106
92
  - - ">="
@@ -112,11 +98,11 @@ dependencies:
112
98
  - 1
113
99
  version: 2.1.1
114
100
  type: :runtime
115
- version_requirements: *id006
101
+ version_requirements: *id005
116
102
  - !ruby/object:Gem::Dependency
117
103
  name: redis
118
104
  prerelease: false
119
- requirement: &id007 !ruby/object:Gem::Requirement
105
+ requirement: &id006 !ruby/object:Gem::Requirement
120
106
  none: false
121
107
  requirements:
122
108
  - - ~>
@@ -128,11 +114,11 @@ dependencies:
128
114
  - 1
129
115
  version: 2.1.1
130
116
  type: :runtime
131
- version_requirements: *id007
117
+ version_requirements: *id006
132
118
  - !ruby/object:Gem::Dependency
133
119
  name: resque
134
120
  prerelease: false
135
- requirement: &id008 !ruby/object:Gem::Requirement
121
+ requirement: &id007 !ruby/object:Gem::Requirement
136
122
  none: false
137
123
  requirements:
138
124
  - - ~>
@@ -144,11 +130,11 @@ dependencies:
144
130
  - 0
145
131
  version: 1.14.0
146
132
  type: :runtime
147
- version_requirements: *id008
133
+ version_requirements: *id007
148
134
  - !ruby/object:Gem::Dependency
149
135
  name: rest-client
150
136
  prerelease: false
151
- requirement: &id009 !ruby/object:Gem::Requirement
137
+ requirement: &id008 !ruby/object:Gem::Requirement
152
138
  none: false
153
139
  requirements:
154
140
  - - ~>
@@ -160,11 +146,11 @@ dependencies:
160
146
  - 1
161
147
  version: 1.6.1
162
148
  type: :runtime
163
- version_requirements: *id009
149
+ version_requirements: *id008
164
150
  - !ruby/object:Gem::Dependency
165
151
  name: templater
166
152
  prerelease: false
167
- requirement: &id010 !ruby/object:Gem::Requirement
153
+ requirement: &id009 !ruby/object:Gem::Requirement
168
154
  none: false
169
155
  requirements:
170
156
  - - ~>
@@ -176,11 +162,11 @@ dependencies:
176
162
  - 0
177
163
  version: 1.0.0
178
164
  type: :runtime
179
- version_requirements: *id010
165
+ version_requirements: *id009
180
166
  - !ruby/object:Gem::Dependency
181
167
  name: rake
182
168
  prerelease: false
183
- requirement: &id011 !ruby/object:Gem::Requirement
169
+ requirement: &id010 !ruby/object:Gem::Requirement
184
170
  none: false
185
171
  requirements:
186
172
  - - ~>
@@ -193,11 +179,11 @@ dependencies:
193
179
  - 2
194
180
  version: 0.9.2.2
195
181
  type: :runtime
196
- version_requirements: *id011
182
+ version_requirements: *id010
197
183
  - !ruby/object:Gem::Dependency
198
184
  name: log4r
199
185
  prerelease: false
200
- requirement: &id012 !ruby/object:Gem::Requirement
186
+ requirement: &id011 !ruby/object:Gem::Requirement
201
187
  none: false
202
188
  requirements:
203
189
  - - ~>
@@ -209,11 +195,11 @@ dependencies:
209
195
  - 7
210
196
  version: 1.1.7
211
197
  type: :development
212
- version_requirements: *id012
198
+ version_requirements: *id011
213
199
  - !ruby/object:Gem::Dependency
214
200
  name: jeweler
215
201
  prerelease: false
216
- requirement: &id013 !ruby/object:Gem::Requirement
202
+ requirement: &id012 !ruby/object:Gem::Requirement
217
203
  none: false
218
204
  requirements:
219
205
  - - ">="
@@ -225,11 +211,11 @@ dependencies:
225
211
  - 0
226
212
  version: 1.4.0
227
213
  type: :development
228
- version_requirements: *id013
214
+ version_requirements: *id012
229
215
  - !ruby/object:Gem::Dependency
230
216
  name: rspec
231
217
  prerelease: false
232
- requirement: &id014 !ruby/object:Gem::Requirement
218
+ requirement: &id013 !ruby/object:Gem::Requirement
233
219
  none: false
234
220
  requirements:
235
221
  - - ">="
@@ -241,11 +227,11 @@ dependencies:
241
227
  - 0
242
228
  version: 1.3.0
243
229
  type: :development
244
- version_requirements: *id014
230
+ version_requirements: *id013
245
231
  - !ruby/object:Gem::Dependency
246
232
  name: rcov
247
233
  prerelease: false
248
- requirement: &id015 !ruby/object:Gem::Requirement
234
+ requirement: &id014 !ruby/object:Gem::Requirement
249
235
  none: false
250
236
  requirements:
251
237
  - - ">="
@@ -257,11 +243,11 @@ dependencies:
257
243
  - 8
258
244
  version: 0.9.8
259
245
  type: :development
260
- version_requirements: *id015
246
+ version_requirements: *id014
261
247
  - !ruby/object:Gem::Dependency
262
248
  name: faker
263
249
  prerelease: false
264
- requirement: &id016 !ruby/object:Gem::Requirement
250
+ requirement: &id015 !ruby/object:Gem::Requirement
265
251
  none: false
266
252
  requirements:
267
253
  - - ">="
@@ -273,11 +259,11 @@ dependencies:
273
259
  - 1
274
260
  version: 0.3.1
275
261
  type: :development
276
- version_requirements: *id016
262
+ version_requirements: *id015
277
263
  - !ruby/object:Gem::Dependency
278
264
  name: rack-test
279
265
  prerelease: false
280
- requirement: &id017 !ruby/object:Gem::Requirement
266
+ requirement: &id016 !ruby/object:Gem::Requirement
281
267
  none: false
282
268
  requirements:
283
269
  - - ">="
@@ -289,11 +275,11 @@ dependencies:
289
275
  - 3
290
276
  version: 0.5.3
291
277
  type: :development
292
- version_requirements: *id017
278
+ version_requirements: *id016
293
279
  - !ruby/object:Gem::Dependency
294
280
  name: thor
295
281
  prerelease: false
296
- requirement: &id018 !ruby/object:Gem::Requirement
282
+ requirement: &id017 !ruby/object:Gem::Requirement
297
283
  none: false
298
284
  requirements:
299
285
  - - ">="
@@ -305,7 +291,7 @@ dependencies:
305
291
  - 6
306
292
  version: 0.13.6
307
293
  type: :development
308
- version_requirements: *id018
294
+ version_requirements: *id017
309
295
  description: RhoSync Synchronization Framework and related command-line utilities
310
296
  email: dev@rhomobile.com
311
297
  executables:
@@ -368,6 +354,9 @@ files:
368
354
  - lib/rhosync/api/create_user.rb
369
355
  - lib/rhosync/api/delete_client.rb
370
356
  - lib/rhosync/api/delete_user.rb
357
+ - lib/rhosync/api/fast_delete.rb
358
+ - lib/rhosync/api/fast_insert.rb
359
+ - lib/rhosync/api/fast_update.rb
371
360
  - lib/rhosync/api/get_api_token.rb
372
361
  - lib/rhosync/api/get_client_params.rb
373
362
  - lib/rhosync/api/get_db_doc.rb
@@ -484,6 +473,9 @@ files:
484
473
  - spec/api/create_user_spec.rb
485
474
  - spec/api/delete_client_spec.rb
486
475
  - spec/api/delete_user_spec.rb
476
+ - spec/api/fast_delete_spec.rb
477
+ - spec/api/fast_insert_spec.rb
478
+ - spec/api/fast_update_spec.rb
487
479
  - spec/api/get_api_token_spec.rb
488
480
  - spec/api/get_client_params_spec.rb
489
481
  - spec/api/get_db_doc_spec.rb
@@ -579,16 +571,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
579
571
  required_rubygems_version: !ruby/object:Gem::Requirement
580
572
  none: false
581
573
  requirements:
582
- - - ">="
574
+ - - ">"
583
575
  - !ruby/object:Gem::Version
584
- hash: 3
576
+ hash: 25
585
577
  segments:
586
- - 0
587
- version: "0"
578
+ - 1
579
+ - 3
580
+ - 1
581
+ version: 1.3.1
588
582
  requirements: []
589
583
 
590
584
  rubyforge_project:
591
- rubygems_version: 1.8.10
585
+ rubygems_version: 1.8.20
592
586
  signing_key:
593
587
  specification_version: 3
594
588
  summary: RhoSync Synchronization Framework