rhodes 2.4.0 → 2.4.1.beta.1

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,8 @@
1
+ ## 2.4.1
2
+ * iPhone: fix production build
3
+ * Android: fix 2.1 version
4
+ * Sync: do not stop sync on source with pending create objects
5
+
1
6
  ## 2.4
2
7
  * Android: support old sdk(s)
3
8
  * Blackberry: encryption for hsql database
@@ -1,6 +1,6 @@
1
1
  module Rhodes
2
2
  unless defined? Rhodes::VERSION
3
- VERSION = '2.4.0'
3
+ VERSION = '2.4.1'
4
4
  end
5
5
  unless defined? Rhodes::DBVERSION
6
6
  DBVERSION = '2.2.0'
@@ -1,6 +1,6 @@
1
1
  module RhodesFramework
2
2
  unless defined? RhodesFramework::VERSION
3
- VERSION = '2.4.0'
3
+ VERSION = '2.4.1'
4
4
  end
5
5
  unless defined? RhodesFramework::DBVERSION
6
6
  DBVERSION = '2.2.2'
data/lib/rhodes.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Rhodes
2
2
  unless defined? Rhodes::VERSION
3
- VERSION = '2.4.0'
3
+ VERSION = '2.4.1'
4
4
  end
5
5
  unless defined? Rhodes::DBVERSION
6
6
  DBVERSION = '2.2.0'
@@ -2,8 +2,8 @@
2
2
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
3
  package="com.rhomobile.rhodes"
4
4
  android:installLocation="auto"
5
- android:versionCode="30"
6
- android:versionName="2.4.0">
5
+ android:versionCode="31"
6
+ android:versionName="2.4.1">
7
7
 
8
8
  <uses-sdk android:minSdkVersion="4" />
9
9
 
@@ -564,9 +564,9 @@ RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_RhodesService_doSyncSource
564
564
  }
565
565
 
566
566
  RHO_GLOBAL void JNICALL Java_com_rhomobile_rhodes_RhodesApplication_setStartParameters
567
- (JNIEnv *, jclass, jstring strUrl)
567
+ (JNIEnv *env, jclass, jstring strUrl)
568
568
  {
569
- std::string const &url = rho_cast<std::string>(strUrl);
569
+ std::string url = rho_cast<std::string>(env, strUrl);
570
570
  RHODESAPP().setStartParameters(url.c_str());
571
571
  }
572
572
 
@@ -76,19 +76,6 @@ public class RhodesActivity extends BaseActivity {
76
76
  Thread ct = Thread.currentThread();
77
77
  //ct.setPriority(Thread.MAX_PRIORITY);
78
78
  uiThreadId = ct.getId();
79
-
80
- Intent intent = getIntent();
81
- //intent.putExtra(RHO_URL_START_KEY, "/app/BrowserStart");
82
- //intent.putExtra(RHO_URL_PARAMS_KEY, "param1=value1&param2=value2");
83
- //Log.d(TAG, "MY URI: " + intent.toUri(Intent.URI_INTENT_SCHEME));
84
-
85
- // Check if we really started through URI
86
- if(intent.getData() != null)
87
- {
88
- //Workaround to get URI string from intent since intent.getData() badly initialized
89
- Log.d(TAG, "Application URI: " + intent.toUri(0));
90
- RhodesApplication.setStartParameters(intent.toUri(0).toString());
91
- }
92
79
 
93
80
  if (!RhodesService.isEnableTitle()) {
94
81
  requestWindowFeature(Window.FEATURE_NO_TITLE);
@@ -356,30 +343,34 @@ public class RhodesActivity extends BaseActivity {
356
343
  @Override
357
344
  public void onServiceConnected(ComponentName name, IBinder service) {
358
345
  super.onServiceConnected(name, service);
359
-
360
- Bundle startParams = null;
361
- Object params = getIntent().getExtras();
362
- if (params != null && params instanceof Bundle)
363
- startParams = (Bundle)params;
364
-
365
- String rhoStartParams = null;
366
- if (startParams != null)
367
- rhoStartParams = startParams.getString(RHO_START_PARAMS_KEY);
368
- if (rhoStartParams == null)
369
- rhoStartParams = "";
370
-
371
- if (!RhodesService.canStartApp(rhoStartParams, ", ")) {
372
- Logger.E(TAG, "This is hidden app and can be started only with security key.");
373
- getRhodesApplication().exit();
374
- return;
375
- }
376
-
377
- String urlStart = startParams == null ? null : startParams.getString(RHO_URL_START_KEY);
378
- if (urlStart != null) {
379
- Logger.D(TAG, "PROCESS URL START: " + urlStart);
380
- RhoConf.setString("start_path", Uri.decode(urlStart));
381
- }
382
-
346
+
347
+ Logger.D(TAG, "onServiceConnected: " + name.toShortString());
348
+
349
+ Intent intent = getIntent();
350
+ String startParams = (intent.getData() != null) ? intent.toUri(0) : "";
351
+ Uri uri = Uri.parse(startParams);
352
+ String scheme = uri.getScheme();
353
+ if(startParams.compareTo("") != 0)
354
+ {
355
+ startParams = startParams.substring(scheme.length() + 1);
356
+ if(startParams.startsWith("//"))
357
+ startParams = startParams.substring(2);
358
+ }
359
+
360
+ if(!RhodesService.canStartApp(startParams, "&#"))
361
+ {
362
+ Logger.E(TAG, "This is hidden app and can be started only with security key.");
363
+ getRhodesApplication().exit();
364
+ return;
365
+ }
366
+
367
+ String urlStart = uri.getPath();
368
+ if (urlStart.compareTo("") != 0)
369
+ {
370
+ Logger.D(TAG, "PROCESS URL START: " + urlStart);
371
+ RhoConf.setString("start_path", Uri.decode(urlStart));
372
+ }
373
+
383
374
  ENABLE_LOADING_INDICATION = !RhoConf.getBool("disable_loading_indication");
384
375
  }
385
376
 
@@ -39,7 +39,7 @@ def setup_ndk(ndkpath,apilevel)
39
39
  $ndktools = nil
40
40
  $ndkabi = "unknown"
41
41
  $ndkgccver = "unknown"
42
- ["arm-linux-androideabi-4.4.3", "arm-eabi-4.4.0", "arm-eabi-4.2.1"].each do |abi|
42
+ ["arm-eabi-4.4.0", "arm-eabi-4.2.1"].each do |abi|
43
43
  variants = []
44
44
  variants << File.join(ndkpath, "toolchains", abi, "prebuilt", $ndkhost)
45
45
  variants << File.join(ndkpath, "build/prebuilt", $ndkhost, abi)
@@ -578,7 +578,7 @@ public class SyncEngine implements NetRequest.IRhoSession
578
578
  {
579
579
  Hashtable/*<String, int>*/ hashPassed = new Hashtable();
580
580
 
581
- for( int nCurSrc = m_sources.size()-1; nCurSrc > 0 ; )
581
+ for( int nCurSrc = m_sources.size()-1; nCurSrc >= 0 ; )
582
582
  {
583
583
  SyncSource oCurSrc = (SyncSource)m_sources.elementAt(nCurSrc);
584
584
  if ( oCurSrc.getAssociations().size() == 0 || hashPassed.containsKey(oCurSrc.getName()) )
@@ -590,6 +590,9 @@ public class SyncEngine implements NetRequest.IRhoSession
590
590
  {
591
591
  SyncSource.CAssociation oAssoc = (SyncSource.CAssociation)oCurSrc.getAssociations().elementAt(i);
592
592
  int nAssocSrcIndex = findSrcIndex( m_sources, oAssoc.m_strSrcName);
593
+ if ( nAssocSrcIndex >= 0 )
594
+ ((SyncSource)m_sources.elementAt(nAssocSrcIndex)).addBelongsTo( oAssoc.m_strAttrib, oCurSrc.getID() );
595
+
593
596
  if ( nAssocSrcIndex >=0 && nAssocSrcIndex < nSrc )
594
597
  {
595
598
  m_sources.removeElementAt( nSrc );
@@ -91,6 +91,8 @@ public class SyncSource
91
91
  Vector/*<CAssociation>*/ m_arAssociations = new Vector();
92
92
  Vector/*Ptr<net::CMultipartItem*>*/ m_arMultipartItems = new Vector();
93
93
  Vector/*<String>*/ m_arBlobAttrs = new Vector();
94
+ Hashtable/*<String,int>*/ m_hashIgnorePushObjects = new Hashtable();
95
+ Hashtable/*<String,int>*/ m_hashBelongsTo = new Hashtable();
94
96
 
95
97
  Integer getID() { return m_nID; }
96
98
  String getName() { return m_strName; }
@@ -215,10 +217,14 @@ public class SyncSource
215
217
  {
216
218
  if ( isEmptyToken() )
217
219
  processToken(1);
218
-
220
+
221
+ syncClientChanges();
222
+ syncServerChanges();
223
+ /*
219
224
  boolean bSyncedServer = syncClientChanges();
220
225
  if ( !bSyncedServer )
221
226
  syncServerChanges();
227
+ */
222
228
  }
223
229
  }catch(Exception exc)
224
230
  {
@@ -233,7 +239,23 @@ public class SyncSource
233
239
  new Integer(m_bGetAtLeastOnePage?1:0), new Integer(m_nRefreshTime), getID() );
234
240
  }
235
241
  }
242
+
243
+ void syncClientChanges()throws Exception
244
+ {
245
+ PROF.START("Pull");
246
+
247
+ boolean bSyncClient = false;
248
+ {
249
+ IDBResult res = getDB().executeSQL("SELECT object FROM changed_values WHERE source_id=? and sent<=1 LIMIT 1 OFFSET 0", getID());
250
+ bSyncClient = !res.isEnd();
251
+ }
252
+ if ( bSyncClient )
253
+ doSyncClientChanges();
236
254
 
255
+ PROF.STOP("Pull");
256
+ }
257
+
258
+ /*
237
259
  boolean syncClientChanges()throws Exception
238
260
  {
239
261
  boolean bSyncedServer = false;
@@ -274,8 +296,69 @@ public class SyncSource
274
296
  {
275
297
  IDBResult res = getDB().executeSQL("SELECT object FROM changed_values WHERE source_id=? and update_type='create' and sent>1 LIMIT 1 OFFSET 0", getID());
276
298
  return !res.isEnd();
299
+ }*/
300
+
301
+ void addBelongsTo(String strAttrib, Integer nSrcID)
302
+ {
303
+ m_hashBelongsTo.put(strAttrib, nSrcID);
304
+ }
305
+
306
+ Integer getBelongsToSrcID(String strAttrib)
307
+ {
308
+ if ( m_hashBelongsTo.containsKey(strAttrib) )
309
+ return (Integer)m_hashBelongsTo.get(strAttrib);
310
+
311
+ return new Integer(-1);
277
312
  }
278
313
 
314
+ void checkIgnorePushObjects()throws Exception
315
+ {
316
+ // ignore changes in pending creates
317
+ {
318
+ IDBResult res = getDB().executeSQL("SELECT distinct(object) FROM changed_values where source_id=? and sent>=2", getID() );
319
+ for( ; !res.isEnd(); res.next() )
320
+ {
321
+ String strObject = res.getStringByIdx(0);
322
+ m_hashIgnorePushObjects.put(strObject, new Integer(1));
323
+ }
324
+ }
325
+
326
+ //check for belongs_to
327
+ String strAttribQuests = "";
328
+ Vector/*<String>*/ arValues = new Vector();
329
+ arValues.addElement(getID());
330
+ Enumeration keys = m_hashBelongsTo.keys();
331
+ while (keys.hasMoreElements())
332
+ {
333
+ if ( strAttribQuests.length() > 0 )
334
+ strAttribQuests += ",";
335
+
336
+ strAttribQuests += "?";
337
+ arValues.addElement(keys.nextElement());
338
+ }
339
+
340
+ if ( strAttribQuests.length() > 0 )
341
+ {
342
+ IDBResult res = getDB().executeSQLEx( "SELECT object, attrib, value FROM changed_values where source_id=? and sent<=1 and attrib IN ( " + strAttribQuests + " )",
343
+ arValues );
344
+
345
+ for( ; !res.isEnd(); res.next() )
346
+ {
347
+ String strObject = res.getStringByIdx(0);
348
+ String strAttrib = res.getStringByIdx(1);
349
+ String strValue = res.getStringByIdx(2);
350
+
351
+ IDBResult res2 = getDB().executeSQL(
352
+ "SELECT object FROM changed_values where source_id=? and sent>=2 and object=? LIMIT 1 OFFSET 0",
353
+ getBelongsToSrcID(strAttrib), strValue );
354
+
355
+ if (!res2.isEnd())
356
+ m_hashIgnorePushObjects.put(strObject, new Integer(1) );
357
+
358
+ }
359
+ }
360
+ }
361
+
279
362
  void doSyncClientChanges()throws Exception
280
363
  {
281
364
  String arUpdateTypes[] = {"create", "update", "delete"};
@@ -286,32 +369,41 @@ public class SyncSource
286
369
  String strBody = "{\"source_name\":" + JSONEntry.quoteValue(getName()) + ",\"client_id\":" + JSONEntry.quoteValue(getSync().getClientID());
287
370
  boolean bSend = false;
288
371
  int i = 0;
289
- for( i = 0; i < 3 && getSync().isContinueSync(); i++ )
372
+
373
+ getDB().Lock();
374
+ try{
375
+ checkIgnorePushObjects();
376
+
377
+ for( i = 0; i < 3 && getSync().isContinueSync(); i++ )
378
+ {
379
+ String strBody1;
380
+ strBody1 = makePushBody_Ver3(arUpdateTypes[i], true);
381
+ if (strBody1.length() > 0)
382
+ {
383
+ strBody += "," + strBody1;
384
+
385
+ String strBlobAttrs = "";
386
+ for ( int j = 0; j < (int)m_arBlobAttrs.size(); j++)
387
+ {
388
+ if ( strBlobAttrs.length() > 0 )
389
+ strBlobAttrs += ",";
390
+
391
+ strBlobAttrs += JSONEntry.quoteValue((String)m_arBlobAttrs.elementAt(j));
392
+ }
393
+
394
+ if ( strBlobAttrs.length() > 0 )
395
+ strBody += ",\"blob_fields\":[" + strBlobAttrs + "]";
396
+
397
+ arUpdateSent[i] = true;
398
+ bSend = true;
399
+ }
400
+ }
401
+ strBody += "}";
402
+ }finally
290
403
  {
291
- String strBody1;
292
- strBody1 = makePushBody_Ver3(arUpdateTypes[i], true);
293
- if (strBody1.length() > 0)
294
- {
295
- strBody += "," + strBody1;
296
-
297
- String strBlobAttrs = "";
298
- for ( int j = 0; j < (int)m_arBlobAttrs.size(); j++)
299
- {
300
- if ( strBlobAttrs.length() > 0 )
301
- strBlobAttrs += ",";
302
-
303
- strBlobAttrs += JSONEntry.quoteValue((String)m_arBlobAttrs.elementAt(j));
304
- }
305
-
306
- if ( strBlobAttrs.length() > 0 )
307
- strBody += ",\"blob_fields\":[" + strBlobAttrs + "]";
308
-
309
- arUpdateSent[i] = true;
310
- bSend = true;
311
- }
404
+ getDB().Unlock();
312
405
  }
313
- strBody += "}";
314
-
406
+
315
407
  if ( bSend )
316
408
  {
317
409
  LOG.INFO( "Push client changes to server. Source: " + getName() + "Size :" + strBody.length() );
@@ -399,6 +491,9 @@ public class SyncSource
399
491
  String value = res.getStringByIdx(2);
400
492
  String attribType = res.getStringByIdx(3);
401
493
 
494
+ if ( m_hashIgnorePushObjects.containsKey(strObject) )
495
+ continue;
496
+
402
497
  if ( attribType.compareTo("blob.file") == 0 )
403
498
  {
404
499
  MultipartItem oItem = new MultipartItem();
@@ -38,7 +38,7 @@
38
38
  <key>CFBundleSignature</key>
39
39
  <string>????</string>
40
40
  <key>CFBundleVersion</key>
41
- <string>2.4.0</string>
41
+ <string>2.4.1</string>
42
42
  <key>LSRequiresIPhoneOS</key>
43
43
  <true/>
44
44
  <key>UILaunchImageFile</key>
@@ -471,6 +471,7 @@
471
471
  );
472
472
  PRODUCT_NAME = rholib;
473
473
  SDKROOT = iphoneos;
474
+ SKIP_INSTALL = YES;
474
475
  SYMROOT = ../build;
475
476
  USER_HEADER_SEARCH_PATHS = "../../shared/curl/include ../../shared";
476
477
  };
@@ -491,6 +492,7 @@
491
492
  );
492
493
  PRODUCT_NAME = rholib;
493
494
  SDKROOT = iphoneos;
495
+ SKIP_INSTALL = YES;
494
496
  SYMROOT = ../build;
495
497
  USER_HEADER_SEARCH_PATHS = "../../shared/curl/include ../../shared";
496
498
  };
@@ -552,6 +554,7 @@
552
554
  );
553
555
  PRODUCT_NAME = rholib;
554
556
  SDKROOT = iphoneos;
557
+ SKIP_INSTALL = YES;
555
558
  SYMROOT = ../build;
556
559
  USER_HEADER_SEARCH_PATHS = "../../shared/curl/include ../../shared";
557
560
  };
@@ -643,7 +643,11 @@
643
643
  isa = PBXProject;
644
644
  buildConfigurationList = 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "curl" */;
645
645
  compatibilityVersion = "Xcode 3.1";
646
+ developmentRegion = English;
646
647
  hasScannedForEncodings = 1;
648
+ knownRegions = (
649
+ en,
650
+ );
647
651
  mainGroup = 08FB7794FE84155DC02AAC07 /* curl */;
648
652
  projectDirPath = "";
649
653
  projectRoot = "";
@@ -757,7 +761,9 @@
757
761
  );
758
762
  PRODUCT_NAME = curl;
759
763
  SDKROOT = iphoneos;
764
+ SKIP_INSTALL = YES;
760
765
  SYMROOT = ../build;
766
+ TARGETED_DEVICE_FAMILY = "1,2";
761
767
  };
762
768
  name = Debug;
763
769
  };
@@ -775,7 +781,9 @@
775
781
  );
776
782
  PRODUCT_NAME = curl;
777
783
  SDKROOT = iphoneos;
784
+ SKIP_INSTALL = YES;
778
785
  SYMROOT = ../build;
786
+ TARGETED_DEVICE_FAMILY = "1,2";
779
787
  };
780
788
  name = Release;
781
789
  };
@@ -834,7 +842,9 @@
834
842
  );
835
843
  PRODUCT_NAME = curl;
836
844
  SDKROOT = iphoneos;
845
+ SKIP_INSTALL = YES;
837
846
  SYMROOT = ../build;
847
+ TARGETED_DEVICE_FAMILY = "1,2";
838
848
  };
839
849
  name = Distribution;
840
850
  };
@@ -119,7 +119,11 @@
119
119
  isa = PBXProject;
120
120
  buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "rhoextlib" */;
121
121
  compatibilityVersion = "Xcode 3.1";
122
+ developmentRegion = English;
122
123
  hasScannedForEncodings = 1;
124
+ knownRegions = (
125
+ en,
126
+ );
123
127
  mainGroup = 0867D691FE84028FC02AAC07 /* rhoextlib */;
124
128
  productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
125
129
  projectDirPath = "";
@@ -161,6 +165,7 @@
161
165
  ONLY_ACTIVE_ARCH = YES;
162
166
  PRODUCT_NAME = rhoextlib;
163
167
  SDKROOT = iphoneos;
168
+ SKIP_INSTALL = YES;
164
169
  TARGETED_DEVICE_FAMILY = "1,2";
165
170
  };
166
171
  name = Debug;
@@ -179,6 +184,7 @@
179
184
  ONLY_ACTIVE_ARCH = YES;
180
185
  PRODUCT_NAME = rhoextlib;
181
186
  SDKROOT = iphoneos;
187
+ SKIP_INSTALL = YES;
182
188
  TARGETED_DEVICE_FAMILY = "1,2";
183
189
  };
184
190
  name = Release;
@@ -249,6 +255,7 @@
249
255
  ONLY_ACTIVE_ARCH = YES;
250
256
  PRODUCT_NAME = rhoextlib;
251
257
  SDKROOT = iphoneos;
258
+ SKIP_INSTALL = YES;
252
259
  TARGETED_DEVICE_FAMILY = "1,2";
253
260
  };
254
261
  name = Distribution;
@@ -834,6 +834,7 @@
834
834
  ONLY_ACTIVE_ARCH = YES;
835
835
  PRODUCT_NAME = rhorubylib;
836
836
  SDKROOT = iphoneos;
837
+ SKIP_INSTALL = YES;
837
838
  SYMROOT = ../build;
838
839
  };
839
840
  name = Debug;
@@ -855,6 +856,7 @@
855
856
  ONLY_ACTIVE_ARCH = YES;
856
857
  PRODUCT_NAME = rhorubylib;
857
858
  SDKROOT = iphoneos;
859
+ SKIP_INSTALL = YES;
858
860
  SYMROOT = ../build;
859
861
  };
860
862
  name = Release;
@@ -917,6 +919,7 @@
917
919
  ONLY_ACTIVE_ARCH = YES;
918
920
  PRODUCT_NAME = rhorubylib;
919
921
  SDKROOT = iphoneos;
922
+ SKIP_INSTALL = YES;
920
923
  SYMROOT = ../build;
921
924
  };
922
925
  name = Distribution;
@@ -255,7 +255,11 @@
255
255
  isa = PBXProject;
256
256
  buildConfigurationList = 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "rhosynclib" */;
257
257
  compatibilityVersion = "Xcode 3.1";
258
+ developmentRegion = English;
258
259
  hasScannedForEncodings = 1;
260
+ knownRegions = (
261
+ en,
262
+ );
259
263
  mainGroup = 08FB7794FE84155DC02AAC07 /* rhosynclib */;
260
264
  projectDirPath = "";
261
265
  projectRoot = "";
@@ -311,6 +315,7 @@
311
315
  );
312
316
  PRODUCT_NAME = rhosynclib;
313
317
  SDKROOT = iphoneos;
318
+ SKIP_INSTALL = YES;
314
319
  };
315
320
  name = Debug;
316
321
  };
@@ -329,6 +334,7 @@
329
334
  );
330
335
  PRODUCT_NAME = rhosynclib;
331
336
  SDKROOT = iphoneos;
337
+ SKIP_INSTALL = YES;
332
338
  };
333
339
  name = Release;
334
340
  };
@@ -393,6 +399,7 @@
393
399
  );
394
400
  PRODUCT_NAME = rhosynclib;
395
401
  SDKROOT = iphoneos;
402
+ SKIP_INSTALL = YES;
396
403
  };
397
404
  name = Distribution;
398
405
  };
@@ -52,17 +52,11 @@ String CRhodesAppBase::canonicalizeRhoUrl(const String& strUrl)
52
52
  if (strUrl.length() == 0 )
53
53
  return m_strHomeUrl;
54
54
 
55
- if ( strncmp("http://", strUrl.c_str(), 7 ) == 0 ||
56
- strncmp("https://", strUrl.c_str(), 8 ) == 0 ||
57
- strncmp("javascript:", strUrl.c_str(), 11 ) == 0 ||
58
- strncmp("mailto:", strUrl.c_str(), 7) == 0 ||
59
- strncmp("tel:", strUrl.c_str(), 4) == 0 ||
60
- strncmp("wtai:", strUrl.c_str(), 5) == 0 ||
61
- strncmp("sms:", strUrl.c_str(), 4) == 0
62
- )
63
- return strUrl;
64
-
65
- return CFilePath::join(m_strHomeUrl,strUrl);
55
+ size_t pos = strUrl.find_first_of(":#");
56
+ if((pos == String::npos) || (strUrl.at(pos) == '#'))
57
+ return CFilePath::join(m_strHomeUrl,strUrl);
58
+
59
+ return strUrl;
66
60
  }
67
61
 
68
62
  } //namespace common
@@ -446,7 +446,7 @@ void CSyncEngine::checkSourceAssociations()
446
446
  {
447
447
  Hashtable<String, int> hashPassed;
448
448
 
449
- for( int nCurSrc = m_sources.size()-1; nCurSrc > 0 ; )
449
+ for( int nCurSrc = m_sources.size()-1; nCurSrc >= 0 ; )
450
450
  {
451
451
  CSyncSource& oCurSrc = *(m_sources.elementAt(nCurSrc));
452
452
  if ( oCurSrc.getAssociations().size() == 0 || hashPassed.containsKey(oCurSrc.getName()) )
@@ -458,6 +458,9 @@ void CSyncEngine::checkSourceAssociations()
458
458
  {
459
459
  const CSyncSource::CAssociation& oAssoc = oCurSrc.getAssociations().elementAt(i);
460
460
  int nAssocSrcIndex = findSrcIndex( m_sources, oAssoc.m_strSrcName);
461
+ if ( nAssocSrcIndex >= 0 )
462
+ m_sources.elementAt(nAssocSrcIndex)->addBelongsTo( oAssoc.m_strAttrib, oCurSrc.getID() );
463
+
461
464
  if ( nAssocSrcIndex >=0 && nAssocSrcIndex < nSrc )
462
465
  {
463
466
  m_sources.removeElementAt( nSrc, false );
@@ -123,9 +123,13 @@ void CSyncSource::sync()
123
123
  if ( isEmptyToken() )
124
124
  processToken(1);
125
125
 
126
+ syncClientChanges();
127
+ syncServerChanges();
128
+ /*
126
129
  boolean bSyncedServer = syncClientChanges();
127
130
  if ( !bSyncedServer )
128
131
  syncServerChanges();
132
+ */
129
133
  }
130
134
 
131
135
  CTimeInterval endTime = CTimeInterval::getCurrentTime();
@@ -137,6 +141,22 @@ void CSyncSource::sync()
137
141
  getID() );
138
142
  }
139
143
 
144
+ void CSyncSource::syncClientChanges()
145
+ {
146
+ PROF_START("Pull");
147
+
148
+ boolean bSyncClient = true;
149
+ {
150
+ IDBResult res = getDB().executeSQL("SELECT object FROM changed_values WHERE source_id=? and sent<=1 LIMIT 1 OFFSET 0", getID());
151
+ bSyncClient = !res.isEnd();
152
+ }
153
+ if ( bSyncClient )
154
+ doSyncClientChanges();
155
+
156
+ PROF_STOP("Pull");
157
+ }
158
+
159
+ /*
140
160
  boolean CSyncSource::syncClientChanges()
141
161
  {
142
162
  boolean bSyncedServer = false;
@@ -177,6 +197,66 @@ boolean CSyncSource::isPendingClientChanges()
177
197
  {
178
198
  IDBResult res = getDB().executeSQL("SELECT object FROM changed_values WHERE source_id=? and update_type='create' and sent>1 LIMIT 1 OFFSET 0", getID());
179
199
  return !res.isEnd();
200
+ }*/
201
+
202
+ void CSyncSource::addBelongsTo(const String& strAttrib, int nSrcID)
203
+ {
204
+ m_hashBelongsTo.put(strAttrib, nSrcID);
205
+ }
206
+
207
+ int CSyncSource::getBelongsToSrcID(const String& strAttrib)
208
+ {
209
+ if ( m_hashBelongsTo.containsKey(strAttrib) )
210
+ return m_hashBelongsTo.get(strAttrib);
211
+
212
+ return -1;
213
+ }
214
+
215
+ void CSyncSource::checkIgnorePushObjects()
216
+ {
217
+ // ignore changes in pending creates
218
+ {
219
+ IDBResult res = getDB().executeSQL("SELECT distinct(object) FROM changed_values where source_id=? and sent>=2", getID() );
220
+ for( ; !res.isEnd(); res.next() )
221
+ {
222
+ String strObject = res.getStringByIdx(0);
223
+ m_hashIgnorePushObjects.put(strObject, 1);
224
+ }
225
+ }
226
+
227
+ //check for belongs_to
228
+ String strAttribQuests = "";
229
+ Vector<String> arValues;
230
+ arValues.addElement(convertToStringA(getID()));
231
+ for ( Hashtable<String,int>::iterator it = m_hashBelongsTo.begin(); it != m_hashBelongsTo.end(); ++it )
232
+ {
233
+ if ( strAttribQuests.length() > 0 )
234
+ strAttribQuests += ",";
235
+
236
+ strAttribQuests += "?";
237
+ arValues.addElement(it->first);
238
+ }
239
+
240
+ if ( strAttribQuests.length() > 0 )
241
+ {
242
+ IDBResult res = getDB().executeSQLEx( (String("SELECT object, attrib, value FROM changed_values where source_id=? and sent<=1 and attrib IN ( ") + strAttribQuests + " )").c_str(),
243
+ arValues );
244
+
245
+ for( ; !res.isEnd(); res.next() )
246
+ {
247
+ String strObject = res.getStringByIdx(0);
248
+ String strAttrib = res.getStringByIdx(1);
249
+ String strValue = res.getStringByIdx(2);
250
+
251
+ IDBResult res2 = getDB().executeSQL(
252
+ "SELECT object FROM changed_values where source_id=? and sent>=2 and object=? LIMIT 1 OFFSET 0",
253
+ getBelongsToSrcID(strAttrib), strValue );
254
+
255
+ if (!res2.isEnd())
256
+ m_hashIgnorePushObjects.put(strObject, 1);
257
+
258
+ }
259
+ }
180
260
  }
181
261
 
182
262
  void CSyncSource::doSyncClientChanges()
@@ -189,6 +269,10 @@ void CSyncSource::doSyncClientChanges()
189
269
  String strBody = "{\"source_name\":" + CJSONEntry::quoteValue(getName()) + ",\"client_id\":" + CJSONEntry::quoteValue(getSync().getClientID());
190
270
  boolean bSend = false;
191
271
  int i = 0;
272
+
273
+ getDB().Lock();
274
+ checkIgnorePushObjects();
275
+
192
276
  for( i = 0; i < 3 && getSync().isContinueSync(); i++ )
193
277
  {
194
278
  String strBody1;
@@ -215,6 +299,8 @@ void CSyncSource::doSyncClientChanges()
215
299
  }
216
300
  strBody += "}";
217
301
 
302
+ getDB().Unlock();
303
+
218
304
  if ( bSend )
219
305
  {
220
306
  LOG(INFO) + "Push client changes to server. Source: " + getName() + "Size :" + strBody.length();
@@ -308,6 +394,9 @@ void CSyncSource::makePushBody_Ver3(String& strBody, const String& strUpdateType
308
394
  String value = res.getStringByIdx(2);
309
395
  String attribType = res.getStringByIdx(3);
310
396
 
397
+ if ( m_hashIgnorePushObjects.containsKey(strObject) )
398
+ continue;
399
+
311
400
  if ( attribType.compare("blob.file") == 0 )
312
401
  {
313
402
  CMultipartItem* pItem = new CMultipartItem();
@@ -62,6 +62,8 @@ private:
62
62
  Vector<CAssociation> m_arAssociations;
63
63
  VectorPtr<net::CMultipartItem*> m_arMultipartItems;
64
64
  Vector<String> m_arBlobAttrs;
65
+ Hashtable<String,int> m_hashIgnorePushObjects;
66
+ Hashtable<String,int> m_hashBelongsTo;
65
67
 
66
68
  public:
67
69
  int m_nErrCode;
@@ -70,7 +72,7 @@ public:
70
72
  public:
71
73
  CSyncSource(int id, const String& strName, const String& strSyncType, db::CDBAdapter& db, CSyncEngine& syncEngine );
72
74
  virtual void sync();
73
- virtual boolean syncClientChanges();
75
+ virtual void syncClientChanges();
74
76
 
75
77
  int getID()const { return m_nID; }
76
78
  String getName() { return m_strName; }
@@ -100,7 +102,11 @@ public:
100
102
  CSyncSource(CSyncEngine& syncEngine, db::CDBAdapter& db );
101
103
 
102
104
  void doSyncClientChanges();
103
- boolean isPendingClientChanges();
105
+ void checkIgnorePushObjects();
106
+ int getBelongsToSrcID(const String& strAttrib);
107
+ void addBelongsTo(const String& strAttrib, int nSrcID);
108
+
109
+ //boolean isPendingClientChanges();
104
110
 
105
111
  void syncServerChanges();
106
112
  void makePushBody_Ver3(String& strBody, const String& strUpdateType, boolean isSync);
@@ -30,12 +30,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "syncengine", "syncengine\sy
30
30
  Release.AspNetCompiler.Debug = "False"
31
31
  EndProjectSection
32
32
  EndProject
33
- Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tcmalloc", "tcmalloc\tcmalloc.vcproj", "{DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}"
34
- ProjectSection(WebsiteProperties) = preProject
35
- Debug.AspNetCompiler.Debug = "True"
36
- Release.AspNetCompiler.Debug = "False"
37
- EndProjectSection
38
- EndProject
39
33
  Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RhoLib", "RhoLib\RhoLib.vcproj", "{F196A418-11F1-4067-9F4F-BCC7D70E6EC5}"
40
34
  ProjectSection(WebsiteProperties) = preProject
41
35
  Debug.AspNetCompiler.Debug = "True"
@@ -210,34 +204,6 @@ Global
210
204
  {6F57C60E-C083-4D46-A3B9-E17948A33518}.Release|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
211
205
  {6F57C60E-C083-4D46-A3B9-E17948A33518}.Release|Windows Mobile 6 Professional SDK (ARMV4I).Build.0 = Release|Windows Mobile 6 Professional SDK (ARMV4I)
212
206
  {6F57C60E-C083-4D46-A3B9-E17948A33518}.Release|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
213
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Mixed Platforms.ActiveCfg = Debug|RhodesCE6 (ARMV4I)
214
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Mixed Platforms.Build.0 = Debug|RhodesCE6 (ARMV4I)
215
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Mixed Platforms.Deploy.0 = Debug|RhodesCE6 (ARMV4I)
216
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Pocket PC 2003 (ARMV4).ActiveCfg = Debug|RhodesCE6 (ARMV4I)
217
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|RhodesCE6 (ARMV4I).ActiveCfg = Debug|RhodesCE6 (ARMV4I)
218
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|RhodesCE6 (ARMV4I).Build.0 = Debug|RhodesCE6 (ARMV4I)
219
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|RhodesCE6 (ARMV4I).Deploy.0 = Debug|RhodesCE6 (ARMV4I)
220
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Smartphone 2003 (ARMV4).ActiveCfg = Debug|RhodesCE6 (ARMV4I)
221
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Win32.ActiveCfg = Debug|Win32
222
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Win32.Build.0 = Debug|Win32
223
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
224
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
225
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
226
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Debug|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Debug|Windows Mobile 6 Professional SDK (ARMV4I)
227
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Mixed Platforms.ActiveCfg = Release|RhodesCE6 (ARMV4I)
228
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Mixed Platforms.Build.0 = Release|RhodesCE6 (ARMV4I)
229
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Mixed Platforms.Deploy.0 = Release|RhodesCE6 (ARMV4I)
230
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Pocket PC 2003 (ARMV4).ActiveCfg = Release|RhodesCE6 (ARMV4I)
231
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|RhodesCE6 (ARMV4I).ActiveCfg = Release|RhodesCE6 (ARMV4I)
232
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|RhodesCE6 (ARMV4I).Build.0 = Release|RhodesCE6 (ARMV4I)
233
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|RhodesCE6 (ARMV4I).Deploy.0 = Release|RhodesCE6 (ARMV4I)
234
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Smartphone 2003 (ARMV4).ActiveCfg = Release|RhodesCE6 (ARMV4I)
235
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Win32.ActiveCfg = Release|Win32
236
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Win32.Build.0 = Release|Win32
237
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
238
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
239
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Windows Mobile 6 Professional SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
240
- {DCD9FEEA-AEB5-4787-AF75-B8D66C1CDC3B}.Release|Windows Mobile 6 Standard SDK (ARMV4I).ActiveCfg = Release|Windows Mobile 6 Professional SDK (ARMV4I)
241
207
  {F196A418-11F1-4067-9F4F-BCC7D70E6EC5}.Debug|Mixed Platforms.ActiveCfg = Debug|RhodesCE6 (ARMV4I)
242
208
  {F196A418-11F1-4067-9F4F-BCC7D70E6EC5}.Debug|Mixed Platforms.Build.0 = Debug|RhodesCE6 (ARMV4I)
243
209
  {F196A418-11F1-4067-9F4F-BCC7D70E6EC5}.Debug|Mixed Platforms.Deploy.0 = Debug|RhodesCE6 (ARMV4I)
data/rhodes.gemspec CHANGED
@@ -3,7 +3,7 @@ require "lib/rhodes.rb"
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = %q{rhodes}
6
- s.version = Rhodes::VERSION
6
+ s.version = "2.4.1.beta.1"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
9
  s.authors = ["Rhomobile"]
@@ -100,6 +100,7 @@ if !defined?(RHO_WP7)
100
100
  Rho::RhoConfig.syncserver.should == saveSrv
101
101
  end
102
102
  end
103
+
103
104
  it "should not sync without login" do
104
105
  SyncEngine.logged_in.should == 0
105
106
 
@@ -107,6 +108,7 @@ end
107
108
  res['error_code'].to_i.should == ::Rho::RhoError::ERR_CLIENTISNOTLOGGEDIN
108
109
 
109
110
  end
111
+
110
112
  =begin
111
113
  it "should update sources from database" do
112
114
  uniq_sources = Rho::RhoConfig::sources.values
@@ -636,6 +638,102 @@ end
636
638
 
637
639
  end
638
640
 
641
+ it "should NOT push pending created objects" do
642
+ item = getProduct.create({:name => 'Test', :brand => "Rho"})
643
+ records = getTestDB().select_from_table('changed_values','*', 'update_type' => 'create')
644
+ records.length.should == 1
645
+ records[0]['attrib'].should == 'object'
646
+
647
+ err_resp = "[{\"version\":3},{\"token\":\"\"},{\"count\":0},{\"progress_count\":0},{\"total_count\":0},{\"create-error\":{\"" + item.object + "\":{\"name\":\"wrongname\",\"an_attribute\":\"error create\"},\"" + item.object + "-error\":{\"message\":\"error create\"}}}]"
648
+
649
+ SyncEngine.set_source_property(getProduct().get_source_id.to_i(), "rho_server_response", err_resp )
650
+ res = ::Rho::RhoSupport::parse_query_parameters getProduct.sync( "/app/Settings/sync_notify")
651
+
652
+ item.update_attributes({:price => "123"})
653
+
654
+ records2 = getTestDB().select_from_table('changed_values','*', 'update_type' => 'create')
655
+ records2.length.should == ($spec_settings[:schema_model] ? 7 : 2)
656
+
657
+ records2 = getTestDB().select_from_table('changed_values','*', {'update_type' => 'create', "sent"=>0})
658
+ records2.length.should == 0
659
+
660
+ records3 = getTestDB().select_from_table('changed_values','*', {'update_type' => 'update', "sent"=>0})
661
+ records3.length.should == 1
662
+
663
+ SyncEngine.set_source_property(getProduct().get_source_id.to_i(), "rho_server_response", err_resp )
664
+ res = ::Rho::RhoSupport::parse_query_parameters getProduct.sync( "/app/Settings/sync_notify")
665
+
666
+ records3 = getTestDB().select_from_table('changed_values','*', {'update_type' => 'update', "sent"=>1})
667
+ records3.length.should == 1
668
+
669
+ end
670
+
671
+ it "should push when pending created objects" do
672
+ item = getProduct.create({:name => 'Test', :brand => "Rho"})
673
+ records = getTestDB().select_from_table('changed_values','*', 'update_type' => 'create')
674
+ records.length.should == 1
675
+ records[0]['attrib'].should == 'object'
676
+
677
+ err_resp = "[{\"version\":3},{\"token\":\"\"},{\"count\":0},{\"progress_count\":0},{\"total_count\":0},{\"create-error\":{\"" + item.object + "\":{\"name\":\"wrongname\",\"an_attribute\":\"error create\"},\"" + item.object + "-error\":{\"message\":\"error create\"}}}]"
678
+
679
+ SyncEngine.set_source_property(getProduct().get_source_id.to_i(), "rho_server_response", err_resp )
680
+ res = ::Rho::RhoSupport::parse_query_parameters getProduct.sync( "/app/Settings/sync_notify")
681
+
682
+ item2 = getProduct.create({:name => 'Test2', :brand => "Rho2"})
683
+ records2 = getTestDB().select_from_table('changed_values','*', {'update_type' => 'create', "sent"=>0} )
684
+ records2.length.should == 1
685
+ records2[0]['attrib'].should == 'object'
686
+
687
+ SyncEngine.set_source_property(getProduct().get_source_id.to_i(), "rho_server_response", "" )
688
+ res = ::Rho::RhoSupport::parse_query_parameters getProduct.sync( "/app/Settings/sync_notify")
689
+ res['status'].should == 'ok'
690
+ res['error_code'].to_i.should == ::Rho::RhoError::ERR_NONE
691
+
692
+ records2 = getTestDB().select_from_table('changed_values','*')
693
+ records2.length.should == 0
694
+
695
+ item3 = getProduct.find(item2.object)
696
+ item3.should be_nil
697
+
698
+ end
699
+
700
+ it "should NOT push when children pending created objects" do
701
+ cust1 = getCustomer.create( {:first => "CustTest1"})
702
+ cust2 = getCustomer.create( {:first => "CustTest2"})
703
+
704
+ @product_test_name = Rho::RhoConfig.generate_id().to_s
705
+ item = getProduct.create({:name => @product_test_name, :quantity => cust1.object, :sku => cust2.object})
706
+ item2 = getProduct.find(item.object)
707
+ item2.vars.should == item.vars
708
+
709
+ err_resp = "[{\"version\":3},{\"token\":\"\"},{\"count\":0},{\"progress_count\":0},{\"total_count\":0},{\"create-error\":{\"" + cust1.object + "\":{\"name\":\"wrongname\",\"an_attribute\":\"error create\"},\"" + cust1.object + "-error\":{\"message\":\"error create\"}}}]"
710
+ SyncEngine.set_source_property(getCustomer().get_source_id.to_i(), "rho_server_response", err_resp )
711
+ res = ::Rho::RhoSupport::parse_query_parameters getCustomer.sync( "/app/Settings/sync_notify")
712
+
713
+ records2 = getTestDB().select_from_table('changed_values','*', {'update_type' => 'create',
714
+ 'source_id'=>getCustomer().get_source_id.to_i(), 'sent'=>2})
715
+ records2.length.should > 0
716
+ records2 = getTestDB().select_from_table('changed_values','*', {'update_type' => 'create',
717
+ 'source_id'=>getProduct().get_source_id.to_i(), 'sent'=>0})
718
+ records2.length.should > 0
719
+
720
+ SyncEngine.set_source_property(getCustomer().get_source_id.to_i(), "rho_server_response", "" )
721
+ res = ::Rho::RhoSupport::parse_query_parameters getProduct.sync( "/app/Settings/sync_notify")
722
+ res['status'].should == 'ok'
723
+ res['error_code'].to_i.should == ::Rho::RhoError::ERR_NONE
724
+
725
+ getTestDB().select_from_table('changed_values','*')
726
+ records2 = getTestDB().select_from_table('changed_values','*', {'update_type' => 'create',
727
+ 'source_id'=>getCustomer().get_source_id.to_i(), 'sent'=>2})
728
+ records2.length.should > 0
729
+ records2 = getTestDB().select_from_table('changed_values','*', {'update_type' => 'create',
730
+ 'source_id'=>getProduct().get_source_id.to_i(), 'sent'=>1})
731
+ records2.length.should > 0
732
+
733
+ item2 = getProduct.find(item.object)
734
+ item2.vars.should_not be_nil
735
+ end
736
+
639
737
  it "should logout" do
640
738
  SyncEngine.logout()
641
739
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: phone_spec
3
- bbver: 5.0
3
+ bbver: 6.0
4
4
  sdk: /Users/crystax/work/rhomobile/rhodes
5
5
  #applog: rholog.txt
6
6
  version: 1.0.0
metadata CHANGED
@@ -1,13 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rhodes
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
5
- prerelease: false
4
+ hash: 62196393
5
+ prerelease: 6
6
6
  segments:
7
7
  - 2
8
8
  - 4
9
- - 0
10
- version: 2.4.0
9
+ - 1
10
+ - beta
11
+ - 1
12
+ version: 2.4.1.beta.1
11
13
  platform: ruby
12
14
  authors:
13
15
  - Rhomobile
@@ -15,8 +17,7 @@ autorequire:
15
17
  bindir: bin
16
18
  cert_chain: []
17
19
 
18
- date: 2011-04-19 00:00:00 -07:00
19
- default_executable:
20
+ date: 2011-04-20 00:00:00 Z
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
22
23
  name: templater
@@ -6268,7 +6269,6 @@ files:
6268
6269
  - spec/phone_spec/Rakefile
6269
6270
  - spec/phone_spec/rhoconfig.txt
6270
6271
  - spec/phone_spec/server.rb
6271
- has_rdoc: true
6272
6272
  homepage: http://www.rhomobile.com
6273
6273
  licenses: []
6274
6274
 
@@ -6299,7 +6299,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
6299
6299
  requirements: []
6300
6300
 
6301
6301
  rubyforge_project: rhodes
6302
- rubygems_version: 1.3.7
6302
+ rubygems_version: 1.7.2
6303
6303
  signing_key:
6304
6304
  specification_version: 2
6305
6305
  summary: The Rhodes framework is the easiest way to develop NATIVE apps with full device capabilities (GPS, PIM, camera, etc.) for any smartphone.