rhosync 2.0.0.beta3 → 2.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
@@ -48,6 +48,50 @@ module Rhosync
48
48
  end
49
49
  counter
50
50
  end
51
+
52
+ # Loads data into fixed schema table based on source settings
53
+ def self.import_data_to_fixed_schema(db,source)
54
+ data = source.get_data(:md)
55
+ counter = {}
56
+ columns,qm = [],[]
57
+ create_table = ['object varchar']
58
+ schema = JSON.parse(source.schema)
59
+
60
+ db.transaction do |database|
61
+ # Create a table with columns specified by 'property' array in settings
62
+ schema['property'].each do |column|
63
+ create_table << "#{column.keys[0]} varchar default NULL"
64
+ columns << column.keys[0]
65
+ qm << '?'
66
+ end
67
+ database.execute("CREATE TABLE #{source.name}(
68
+ #{create_table.join(",")} );")
69
+
70
+ # Insert each object as single row in fixed schema table
71
+ database.prepare("insert into #{source.name}
72
+ (object,#{columns.join(',')}) values (?,#{qm.join(',')})") do |stmt|
73
+ data.each do |obj,row|
74
+ args = [obj]
75
+ columns.each do |col|
76
+ args << row[col]
77
+ end
78
+ stmt.execute(args)
79
+ end
80
+ end
81
+
82
+ # Create indexes for specified columns in settings 'index'
83
+ schema['index'].each do |index|
84
+ database.execute("CREATE INDEX #{index.keys[0]} on #{source.name} (#{index.values[0]});")
85
+ end if schema['index']
86
+
87
+ # Create unique indexes for specified columns in settings 'unique_index'
88
+ schema['unique_index'].each do |index|
89
+ database.execute("CREATE UNIQUE INDEX #{index.keys[0]} on #{source.name} (#{index.values[0]});")
90
+ end if schema['unique_index']
91
+ end
92
+
93
+ return {}
94
+ end
51
95
 
52
96
  def self.refs_to_s(refs)
53
97
  str = ''
@@ -84,7 +128,12 @@ module Rhosync
84
128
  :user_id => bulk_data.user_id})
85
129
  source.source_id = src_counter
86
130
  src_counter += 1
87
- source_attrib_refs = import_data_to_object_values(db,source)
131
+ source_attrib_refs = nil
132
+ if source.schema
133
+ source_attrib_refs = import_data_to_fixed_schema(db,source)
134
+ else
135
+ source_attrib_refs = import_data_to_object_values(db,source)
136
+ end
88
137
  sources_refs[source_name] =
89
138
  {:source => source, :refs => source_attrib_refs}
90
139
  lap_timer("finished importing sqlite data for #{source_name}",timer)
@@ -99,7 +148,7 @@ module Rhosync
99
148
  hsql_file = dbfile + ".hsqldb"
100
149
  raise Exception.new("Error running hsqldata") unless
101
150
  system('java','-cp', File.join(File.dirname(__FILE__),'..','..','..','vendor','hsqldata.jar'),
102
- 'com.rhomobile.hsqldata.HsqlData', dbfile, hsql_file, schema, index)
151
+ 'com.rhomobile.hsqldata.HsqlData', dbfile, hsql_file)
103
152
  end
104
153
 
105
154
  def self.get_file_args(bulk_data_name,ts)
@@ -112,10 +112,6 @@ module Rhosync
112
112
  params[:source_name] ? {:source_name => current_source.name} : {:source_name => '*'})
113
113
  end
114
114
  end
115
-
116
- def source_config
117
- { "sources" => Rhosync.get_config(Rhosync.base_directory)[:sources] }
118
- end
119
115
 
120
116
  def catch_all
121
117
  begin
@@ -136,15 +132,21 @@ module Rhosync
136
132
  Rhosync.log "Rhosync Server v#{Rhosync::VERSION} started..."
137
133
 
138
134
  before do
139
- if params["cud"]
140
- cud = JSON.parse(params["cud"])
141
- params.delete("cud")
142
- params.merge!(cud)
143
- end
144
- if request.env['CONTENT_TYPE'] == 'application/json'
145
- params.merge!(JSON.parse(request.body.read))
146
- request.body.rewind
147
- end
135
+ begin
136
+ if params["cud"]
137
+ cud = JSON.parse(params["cud"])
138
+ params.delete("cud")
139
+ params.merge!(cud)
140
+ end
141
+ if request.env['CONTENT_TYPE'] == 'application/json'
142
+ params.merge!(JSON.parse(request.body.read))
143
+ request.body.rewind
144
+ end
145
+ rescue JSON::ParserError => jpe
146
+ throw :halt, [500, "Server error while processing client data"]
147
+ rescue Exception => e
148
+ throw :halt, [500, "Internal server error"]
149
+ end
148
150
  if params[:version] and params[:version].to_i < 3
149
151
  throw :halt, [404, "Server supports version 3 or higher of the protocol."]
150
152
  end
@@ -13,6 +13,7 @@ module Rhosync
13
13
  field :queue,:string
14
14
  field :query_queue,:string
15
15
  field :cud_queue,:string
16
+ field :schema, :string
16
17
  attr_accessor :app_id, :user_id
17
18
  validates_presence_of :name #, :source_id
18
19
 
@@ -27,6 +28,7 @@ module Rhosync
27
28
  fields[:partition_type] ||= :user
28
29
  fields[:poll_interval] ||= 300
29
30
  fields[:sync_type] ||= :incremental
31
+ fields[:schema] = fields[:schema].to_json if fields[:schema]
30
32
  end
31
33
 
32
34
  def self.create(fields,params)
@@ -43,6 +45,7 @@ module Rhosync
43
45
  end
44
46
 
45
47
  def update(fields)
48
+ fields = fields.with_indifferent_access # so we can access hash keys as symbols
46
49
  self.class.set_defaults(fields)
47
50
  super(fields)
48
51
  end
@@ -4,6 +4,8 @@ module Rhosync
4
4
  end
5
5
 
6
6
  module TestMethods
7
+ # Initializes the source adapter under test for a given user, typically in a before(:each) block
8
+ # setup_test_for(Product,'testuser') #=> 'testuser' will be used by rest of the specs
7
9
  def setup_test_for(adapter,user_id)
8
10
  app_id = 'application'
9
11
  s_fields = {
@@ -23,15 +25,51 @@ module Rhosync
23
25
  @ss = SourceSync.new(@s)
24
26
  end
25
27
 
28
+ # Executes the adapter's query method and returns
29
+ # the master document (:md) stored in redis
30
+ # For example, if your source adapter query method was:
31
+ # def query(params=nil)
32
+ # @result = {
33
+ # "1"=>{"name"=>"Acme", "industry"=>"Electronics"},
34
+ # "2"=>{"name"=>"Best", "industry"=>"Software"}
35
+ # }
36
+ # end
37
+ #
38
+ # test_query would return:
39
+ # {
40
+ # "1"=>{"name"=>"Acme", "industry"=>"Electronics"},
41
+ # "2"=>{"name"=>"Best", "industry"=>"Software"}
42
+ # }
26
43
  def test_query
27
44
  @ss.process_query
28
45
  return md
29
46
  end
30
47
 
48
+ # Returns any errors stored in redis for the previous source adapter query
49
+ # For example: {"query-error"=>{"message"=>"error connecting to web service!"}}
31
50
  def query_errors
32
51
  @s.get_data(:errors)
33
52
  end
34
53
 
54
+ # Execute's the adapter's create method with a provided record and
55
+ # returns the object string from the create method. If the create method
56
+ # returns a string, then a link will be saved for the device next time
57
+ # it synchronizes. This link can be tested here.
58
+ #
59
+ # For example, in your spec:
60
+ # @product = {
61
+ # 'name' => 'iPhone',
62
+ # 'brand' => 'Apple',
63
+ # 'price' => '$299.99',
64
+ # 'quantity' => '5',
65
+ # 'sku' => '1234'
66
+ # }
67
+ # new_product_id = test_create(@product)
68
+ # create_errors.should == {}
69
+ # md[new_product_id].should == @product
70
+ #
71
+ # This will return the result of the adapter's create method. The master
72
+ # document (:md) should also contain the new record.
35
73
  def test_create(record)
36
74
  @c.put_data(:create,{'temp-id' => record})
37
75
  @ss.create(@c.id)
@@ -39,32 +77,72 @@ module Rhosync
39
77
  links ? links['l'] : nil
40
78
  end
41
79
 
80
+ # Returns any errors stored in redis from the previous source adapter create
81
+ # (same structure as query errors)
42
82
  def create_errors
43
83
  @c.get_data(:create_errors)
44
84
  end
45
85
 
86
+ # Execute the source adapter's update method.
87
+ # Takes a record as hash of hashes (object_id => object)
88
+ #
89
+ # For example:
90
+ # test_update({'4' => {'price' => '$199.99'}})
91
+ # update_errors.should == {}
92
+ # test_query
93
+ # md[product_id]['price'].should == '$199.99'
94
+ #
95
+ # This will call the adapter's update method for object_id '4'
96
+ # NOTE: To test the master document, you will need to run def test_query
97
+ # as shown above
46
98
  def test_update(record)
47
99
  @c.put_data(:update,record)
48
100
  @ss.update(@c.id)
49
101
  end
50
102
 
103
+ # Returns any errors stored in redis from the previous source adapter update
104
+ # (same structure as query errors)
51
105
  def update_errors
52
106
  @c.get_data(:update_errors)
53
107
  end
54
108
 
109
+ # Execute the source adapter's delete method.
110
+ # Takes a record as hash of hashes (object_id => object)
111
+ #
112
+ # For example:
113
+ # @product = {
114
+ # 'name' => 'iPhone',
115
+ # 'brand' => 'Apple',
116
+ # 'price' => '$299.99',
117
+ # 'quantity' => '5',
118
+ # 'sku' => '1234'
119
+ # }
120
+ # test_delete('4' => @product)
121
+ # delete_errors.should == {}
122
+ # md.should == {}
123
+ #
124
+ # This will call the adapter's delete method for product '4'
125
+ # NOTE: The master document (:md) will be updated and can be
126
+ # verified as shown above.
55
127
  def test_delete(record)
56
128
  @c.put_data(:delete,record)
57
129
  @ss.delete(@c.id)
58
130
  end
59
131
 
132
+ # Returns any errors stored in redis from the previous source adapter delete
133
+ # (same structure as query errors)
60
134
  def delete_errors
61
135
  @c.get_data(:delete_errors)
62
136
  end
63
137
 
138
+ # Returns the master document (:md) for the source adapter stored in redis.
139
+ # This is equivalent to the @result hash of hashes structure.
64
140
  def md
65
141
  @s.get_data(:md)
66
142
  end
67
143
 
144
+ # Returns the client document (:cd) for the source adapter + client under test.
145
+ # The master document (:md) and client document (:cd) should be equal
68
146
  def cd
69
147
  @c.get_data(:cd)
70
148
  end
@@ -1,3 +1,3 @@
1
1
  module Rhosync
2
- VERSION = '2.0.0.beta3'
2
+ VERSION = '2.0.0.beta4'
3
3
  end
data/lib/rhosync.rb CHANGED
@@ -122,6 +122,10 @@ module Rhosync
122
122
  settings_file = File.join(basedir,'settings','settings.yml') if basedir
123
123
  YAML.load_file(settings_file) if settings_file and File.exist?(settings_file)
124
124
  end
125
+
126
+ def source_config
127
+ { "sources" => Rhosync.get_config(Rhosync.base_directory)[:sources] }
128
+ end
125
129
  ### End Rhosync setup methods
126
130
 
127
131
 
@@ -19,7 +19,8 @@ describe "RhosyncApiGetSourceParams" do
19
19
  {"name"=>"sync_type", "value"=>"incremental", "type"=>"string"},
20
20
  {"name"=>"queue", "value"=>nil, "type"=>"string"},
21
21
  {"name"=>"query_queue", "value"=>nil, "type"=>"string"},
22
- {"name"=>"cud_queue", "value"=>nil, "type"=>"string"}]
22
+ {"name"=>"cud_queue", "value"=>nil, "type"=>"string"},
23
+ {"name"=>"schema", "value"=>nil, "type"=>"string"}]
23
24
  end
24
25
 
25
26
  end
@@ -5,13 +5,13 @@ describe "RhosyncApiListSources" do
5
5
 
6
6
  it "should list all application sources" do
7
7
  post "/api/list_sources", {:api_token => @api_token}
8
- JSON.parse(last_response.body).should == ["SimpleAdapter", "SampleAdapter"]
8
+ JSON.parse(last_response.body).sort.should == ["SimpleAdapter", "SampleAdapter", "FixedSchemaAdapter"].sort
9
9
  end
10
10
 
11
11
  it "should list all application sources using partition_type param" do
12
12
  post "/api/list_sources",
13
13
  {:api_token => @api_token, :partition_type => 'all'}
14
- JSON.parse(last_response.body).should == ["SimpleAdapter", "SampleAdapter"]
14
+ JSON.parse(last_response.body).sort.should == ["SimpleAdapter", "SampleAdapter", "FixedSchemaAdapter"].sort
15
15
  end
16
16
 
17
17
  it "should list app partition sources" do
@@ -21,7 +21,7 @@ describe "RhosyncApiListSources" do
21
21
 
22
22
  it "should list user partition sources" do
23
23
  post "/api/list_sources", {:api_token => @api_token, :partition_type => :user}
24
- JSON.parse(last_response.body).should == ["SampleAdapter"]
24
+ JSON.parse(last_response.body).sort.should == ["SampleAdapter", "FixedSchemaAdapter"].sort
25
25
  end
26
26
 
27
27
  end
@@ -4,6 +4,19 @@
4
4
  SimpleAdapter:
5
5
  poll_interval: 600
6
6
  partition_type: app
7
+ FixedSchemaAdapter:
8
+ poll_interval: 300
9
+ schema:
10
+ version: '1.0'
11
+ property:
12
+ - name: string
13
+ - brand: string
14
+ - price: string
15
+ - image_url: blob
16
+ index:
17
+ - by_name_brand: 'name,brand'
18
+ unique_index:
19
+ - by_price: 'price'
7
20
 
8
21
  :development:
9
22
  :licensefile: settings/license.key
@@ -0,0 +1,9 @@
1
+ class FixedSchemaAdapter < SourceAdapter
2
+ def initialize(source,credential)
3
+ super(source,credential)
4
+ end
5
+
6
+ def query(params=nil)
7
+ @result = Store.get_data('test_db_storage')
8
+ end
9
+ end
data/spec/doc/doc_spec.rb CHANGED
@@ -28,8 +28,6 @@ describe "Protocol" do
28
28
  :run => false,
29
29
  :secret => "secure!"
30
30
  )
31
- Server.use Rack::Static, :urls => ["/data"],
32
- :root => File.join(File.dirname(__FILE__),'..','apps',@test_app_name)
33
31
  end
34
32
 
35
33
  before(:each) do
@@ -16,16 +16,19 @@ describe "BulkDataJob" do
16
16
  it "should create sqlite data file from master document" do
17
17
  set_state('test_db_storage' => @data)
18
18
  docname = bulk_data_docname(@a.id,@u.id)
19
+ expected = { @s_fields[:name] => @data,
20
+ 'FixedSchemaAdapter' => @data
21
+ }
19
22
  data = BulkData.create(:name => docname,
20
23
  :state => :inprogress,
21
24
  :app_id => @a.id,
22
25
  :user_id => @u.id,
23
- :sources => [@s_fields[:name]])
26
+ :sources => [@s_fields[:name], 'FixedSchemaAdapter'])
24
27
  BulkDataJob.perform("data_name" => data.name)
25
28
  data = BulkData.load(docname)
26
29
  data.completed?.should == true
27
30
  verify_result(@s.docname(:md) => @data,@s.docname(:md_copy) => @data)
28
- validate_db(data,@data).should == true
31
+ validate_db(data,expected).should == true
29
32
  File.exists?(data.dbfile+'.rzip').should == true
30
33
  File.exists?(data.dbfile+'.hsqldb.data').should == true
31
34
  File.exists?(data.dbfile+'.hsqldb.script').should == true
@@ -33,7 +36,8 @@ describe "BulkDataJob" do
33
36
  path = File.join(File.dirname(data.dbfile),'tmp')
34
37
  FileUtils.mkdir_p path
35
38
  unzip_file("#{data.dbfile}.rzip",path)
36
- validate_db_by_name(File.join(path,File.basename(data.dbfile)),@data)
39
+ data.dbfile = File.join(path,File.basename(data.dbfile))
40
+ validate_db(data,expected).should == true
37
41
  end
38
42
 
39
43
  it "should not create hsql db files if blackberry_bulk_sync is disabled" do
@@ -49,11 +53,11 @@ describe "BulkDataJob" do
49
53
  data = BulkData.load(docname)
50
54
  data.completed?.should == true
51
55
  verify_result(@s.docname(:md) => @data,@s.docname(:md_copy) => @data)
52
- validate_db(data,@data).should == true
56
+ validate_db(data,@s.name => @data).should == true
53
57
  File.exists?(data.dbfile+'.hsqldb.script').should == false
54
58
  File.exists?(data.dbfile+'.hsqldb.properties').should == false
55
59
  end
56
-
60
+
57
61
  it "should create sqlite data with source metadata" do
58
62
  set_state('test_db_storage' => @data)
59
63
  mock_metadata_method([SampleAdapter]) do
@@ -69,7 +73,7 @@ describe "BulkDataJob" do
69
73
  verify_result(@s.docname(:md) => @data,
70
74
  @s.docname(:metadata) => {'foo'=>'bar'}.to_json,
71
75
  @s.docname(:md_copy) => @data)
72
- validate_db(data,@data).should == true
76
+ validate_db(data,@s.name => @data).should == true
73
77
  end
74
78
  end
75
79
 
@@ -13,6 +13,8 @@ describe "Server" do
13
13
  include Rack::Test::Methods
14
14
  include Rhosync
15
15
 
16
+ it_should_behave_like "DBObjectsHelper"
17
+
16
18
  before(:each) do
17
19
  require File.join(get_testapp_path,@test_app_name)
18
20
  Rhosync.bootstrap(get_testapp_path) do |rhosync|
@@ -24,14 +26,12 @@ describe "Server" do
24
26
  :secret => "secure!"
25
27
  )
26
28
  Server.use Rack::Static, :urls => ["/data"],
27
- :root => File.join(File.dirname(__FILE__),'..','apps','rhotestapp')
29
+ :root => File.expand_path(File.join(File.dirname(__FILE__),'..','apps','rhotestapp'))
28
30
  end
29
31
 
30
32
  def app
31
33
  @app ||= Server.new
32
34
  end
33
-
34
- it_should_behave_like "DBObjectsHelper"
35
35
 
36
36
  it "should show status page" do
37
37
  get '/'
@@ -116,8 +116,19 @@ describe "Server" do
116
116
  describe "client management routes" do
117
117
  before(:each) do
118
118
  do_post "/application/clientlogin", "login" => @u.login, "password" => 'testpass'
119
- @source_config = {"sources"=>{"SampleAdapter"=>{"poll_interval"=>300},
120
- "SimpleAdapter"=>{"partition_type"=>"app","poll_interval"=>600}}}
119
+ @source_config = {
120
+ "sources"=> {
121
+ "FixedSchemaAdapter"=>
122
+ {"schema"=>{"property"=>[{"name"=>"string"},
123
+ {"brand"=>"string"}, {"price"=>"string"},
124
+ {"image_url"=>"blob"}], "version"=>"1.0",
125
+ "unique_index"=>[{"by_price"=>"price"}],
126
+ "index"=>[{"by_name_brand"=>"name,brand"}]},
127
+ "poll_interval"=>300},
128
+ "SampleAdapter"=>{"poll_interval"=>300},
129
+ "SimpleAdapter"=>{"partition_type"=>"app",
130
+ "poll_interval"=>600}}
131
+ }
121
132
  end
122
133
 
123
134
  it "should respond to clientcreate" do
@@ -190,6 +201,20 @@ describe "Server" do
190
201
  verify_result("test_delete_storage" => {'1'=>@product1})
191
202
  end
192
203
 
204
+ it "should handle client posting broken json" do
205
+ broken_json = "{\"foo\":\"bar\"\"}"
206
+ post "/application", broken_json, {'CONTENT_TYPE'=>'application/json'}
207
+ last_response.status.should == 500
208
+ last_response.body.should == "Server error while processing client data"
209
+ end
210
+
211
+ it "should handle client posting broken body" do
212
+ broken_json = ['foo']
213
+ post "/application", broken_json, {'CONTENT_TYPE'=>'application/json'}
214
+ last_response.status.should == 500
215
+ last_response.body.should == "Internal server error"
216
+ end
217
+
193
218
  it "should get inserts json" do
194
219
  cs = ClientSync.new(@s,@c,1)
195
220
  data = {'1'=>@product1,'2'=>@product2}
@@ -307,7 +332,7 @@ describe "Server" do
307
332
  data = BulkData.load(bulk_data_docname(@a.id,@u.id))
308
333
  last_response.body.should == {:result => :url,
309
334
  :url => data.url}.to_json
310
- validate_db_by_name(data.dbfile,@data)
335
+ validate_db(data,{@s.name => @data, 'FixedSchemaAdapter' => @data})
311
336
  end
312
337
 
313
338
  it "should download bulk data file" do
@@ -318,7 +343,7 @@ describe "Server" do
318
343
  get JSON.parse(last_response.body)["url"]
319
344
  last_response.should be_ok
320
345
  File.open('test.data','wb') {|f| f.puts last_response.body}
321
- validate_db_by_name('test.data',@data)
346
+ validate_db_file('test.data',[@s.name,'FixedSchemaAdapter'],{@s.name => @data, 'FixedSchemaAdapter' => @data})
322
347
  File.delete('test.data')
323
348
  end
324
349
 
data/spec/spec_helper.rb CHANGED
@@ -58,6 +58,10 @@ module TestHelpers
58
58
  def delete_data_directory
59
59
  FileUtils.rm_rf(Rhosync.data_directory)
60
60
  end
61
+
62
+ def json_clone(data)
63
+ JSON.parse(data.to_json)
64
+ end
61
65
 
62
66
  def set_state(state)
63
67
  state.each do |dockey,data|
@@ -97,26 +101,52 @@ module TestHelpers
97
101
  end
98
102
 
99
103
  def validate_db(bulk_data,data)
100
- validate_db_by_name(bulk_data.dbfile,data)
104
+ validate_db_file(bulk_data.dbfile,bulk_data.sources.members,data)
105
+ end
106
+
107
+ def validate_db_file(dbfile,sources,data)
108
+ db = SQLite3::Database.new(dbfile)
109
+ sources.each do |source_name|
110
+ s = Source.load(source_name,{:app_id => APP_NAME,:user_id => @u.login})
111
+ return false unless validate_db_by_name(db,s,data[s.name])
112
+ end
113
+ true
101
114
  end
102
115
 
103
- def validate_db_by_name(name,data)
104
- db = SQLite3::Database.new(name)
105
- db.execute("select source_id,name,sync_priority,partition,sync_type,source_attribs,metadata from sources").each do |row|
106
- return false if row[0] != @s.source_id.to_s
107
- return false if row[1] != @s.name
108
- return false if row[2] != @s.priority.to_s
109
- return false if row[3] != @s.partition_type.to_s
110
- return false if row[4] != @s.sync_type.to_s
111
- return false if row[5] != get_attrib_counter(data)
112
- return false if row[6] != @s.get_value(:metadata)
116
+ def validate_db_by_name(db,s,data)
117
+ db.execute("select source_id,name,sync_priority,partition,
118
+ sync_type,source_attribs,metadata from sources where name='#{s.name}'").each do |row|
119
+ return false if row[0] != s.source_id.to_s
120
+ return false if row[1] != s.name
121
+ return false if row[2] != s.priority.to_s
122
+ return false if row[3] != s.partition_type.to_s
123
+ return false if row[4] != s.sync_type.to_s
124
+ return false if row[5] != (s.schema ? "" : get_attrib_counter(data))
125
+ return false if row[6] != s.get_value(:metadata)
126
+ end
127
+ data = json_clone(data)
128
+ if s.schema
129
+ schema = JSON.parse(s.schema)
130
+ columns = ['object']
131
+ schema['property'].each do |column|
132
+ columns << column.keys[0]
133
+ end
134
+ db.execute("select #{columns.join(',')} from #{s.name}") do |row|
135
+ obj = data[row[0]]
136
+ columns.each_index do |i|
137
+ next if i == 0
138
+ return false if row[i] != obj[columns[i]]
139
+ end
140
+ data.delete(row[0])
141
+ end
142
+ else
143
+ db.execute("select * from object_values where source_id=#{s.source_id}").each do |row|
144
+ object = data[row[2]]
145
+ return false if object.nil? or object[row[1]] != row[3] or row[0] != s.source_id.to_s
146
+ object.delete(row[1])
147
+ data.delete(row[2]) if object.empty?
148
+ end
113
149
  end
114
- db.execute("select * from object_values").each do |row|
115
- object = data[row[2]]
116
- return false if object.nil? or object[row[1]] != row[3] or row[0] != @s.source_id.to_s
117
- object.delete(row[1])
118
- data.delete(row[2]) if object.empty?
119
- end
120
150
  data.empty?
121
151
  end
122
152
 
@@ -232,6 +262,10 @@ describe "DBObjectsHelper", :shared => true do
232
262
  @c = Client.create(@c_fields,{:source_name => @s_fields[:name]})
233
263
  @s = Source.load(@s_fields[:name],@s_params)
234
264
  @s = Source.create(@s_fields,@s_params) if @s.nil?
265
+ @s1 = Source.load('FixedSchemaAdapter',@s_params)
266
+ @s1 = Source.create({:name => 'FixedSchemaAdapter'},@s_params) if @s1.nil?
267
+ config = Rhosync.source_config["sources"]['FixedSchemaAdapter']
268
+ @s1.update(config)
235
269
  @r = @s.read_state
236
270
  @a.sources << @s.id
237
271
  @a.users << @u.id
metadata CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  - 2
7
7
  - 0
8
8
  - 0
9
- - beta3
10
- version: 2.0.0.beta3
9
+ - beta4
10
+ version: 2.0.0.beta4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Rhomobile
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-05-18 00:00:00 -07:00
18
+ date: 2010-05-20 00:00:00 -07:00
19
19
  default_executable: rhosync
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -412,6 +412,7 @@ files:
412
412
  - spec/apps/rhotestapp/settings/license.key
413
413
  - spec/apps/rhotestapp/settings/settings.yml
414
414
  - spec/apps/rhotestapp/sources/base_adapter.rb
415
+ - spec/apps/rhotestapp/sources/fixed_schema_adapter.rb
415
416
  - spec/apps/rhotestapp/sources/sample_adapter.rb
416
417
  - spec/apps/rhotestapp/sources/simple_adapter.rb
417
418
  - spec/apps/rhotestapp/sources/sub_adapter.rb
@@ -513,6 +514,7 @@ test_files:
513
514
  - spec/app_spec.rb
514
515
  - spec/apps/rhotestapp/application.rb
515
516
  - spec/apps/rhotestapp/sources/base_adapter.rb
517
+ - spec/apps/rhotestapp/sources/fixed_schema_adapter.rb
516
518
  - spec/apps/rhotestapp/sources/sample_adapter.rb
517
519
  - spec/apps/rhotestapp/sources/simple_adapter.rb
518
520
  - spec/apps/rhotestapp/sources/sub_adapter.rb