rhosync 2.1.16 → 2.1.17.beta1

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/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