rhoconnect 3.3.6 → 3.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +40 -4
- data/Gemfile +2 -2
- data/Gemfile.lock +27 -25
- data/bench/benchapp/Gemfile +7 -27
- data/bench/benchapp/config.ru +9 -31
- data/bench/blobapp/Gemfile +7 -27
- data/bench/blobapp/config.ru +9 -29
- data/bench/lib/bench.rb +8 -1
- data/bench/lib/bench/test_data.rb +4 -1
- data/bench/scripts/blob_cud_script.rb +4 -0
- data/bench/scripts/cud_script.rb +7 -1
- data/bench/scripts/helpers.rb +1 -1
- data/bench/scripts/test_query_script.rb +20 -7
- data/bench/spec/bench_spec_helper.rb +3 -1
- data/bin/rhoconnect +22 -12
- data/commands/{commands/dtach_commands → dtach}/dtach_about.rb +0 -0
- data/commands/{commands/dtach_commands → dtach}/dtach_install.rb +0 -0
- data/commands/{commands/redis_commands → dtach}/redis_attach.rb +0 -0
- data/commands/execute.rb +14 -15
- data/commands/generators/update.rb +26 -0
- data/commands/{commands/redis_commands → redis}/redis_about.rb +0 -0
- data/commands/redis/redis_download.rb +13 -0
- data/commands/redis/redis_install.rb +26 -0
- data/commands/{commands/redis_commands → redis}/redis_make.rb +0 -0
- data/commands/{commands/redis_commands → redis}/redis_restart.rb +3 -2
- data/commands/{commands/redis_commands → redis}/redis_start.rb +0 -0
- data/commands/{commands/redis_commands → redis}/redis_startbg.rb +0 -0
- data/commands/{commands/redis_commands → redis}/redis_stop.rb +3 -2
- data/commands/{commands/rhoconnect → rhoconnect}/clean_start.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/config.rb +7 -2
- data/commands/{commands/rhoconnect → rhoconnect}/create_user.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/delete_device.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/delete_user.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/flushdb.rb +4 -4
- data/commands/{commands/rhoconnect → rhoconnect}/get_token.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/reset.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/reset_refresh.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/restart.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/secret.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/set_admin_password.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/spec.rb +0 -0
- data/commands/rhoconnect/start.rb +27 -0
- data/commands/{commands/rhoconnect → rhoconnect}/startbg.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/startdebug.rb +2 -2
- data/commands/{commands/rhoconnect → rhoconnect}/stop.rb +3 -4
- data/commands/{commands/rhoconnect → rhoconnect}/version.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect}/web.rb +0 -0
- data/commands/rhoconnect_attach/attach.rb +10 -0
- data/commands/rhoconnect_console/console.rb +16 -0
- data/commands/{commands/rhoconnect → rhoconnect_console}/console_helper.rb +0 -0
- data/commands/{commands/rhoconnect → rhoconnect_war}/war.rb +0 -0
- data/commands/{commands/redis_commands → utilities}/redis_runner.rb +22 -19
- data/commands/utilities/utilities.rb +6 -0
- data/doc/benchmarks.txt +2 -2
- data/doc/bulk-sync.txt +12 -1
- data/doc/client-java.txt +3 -3
- data/doc/client-objc.txt +1 -1
- data/doc/client.txt +5 -5
- data/doc/command-line.txt +80 -135
- data/doc/deploying.txt +119 -12
- data/doc/extending-rhoconnect-server.txt +1 -1
- data/doc/heroku-addon.txt +119 -23
- data/doc/install.txt +101 -39
- data/doc/java-plugin.txt +2 -2
- data/doc/licensing.txt +1 -1
- data/doc/plugin-intro.txt +3 -1
- data/doc/preparing-production.txt +4 -4
- data/doc/public/cli.txt +2 -2
- data/doc/push-backend-setup.txt +11 -1
- data/doc/push-client-setup.txt +72 -2
- data/doc/push-server-setup.txt +129 -8
- data/doc/rails-plugin.txt +245 -40
- data/doc/rest-api.txt +10 -6
- data/doc/rhoconnect-calculator.txt +237 -0
- data/doc/rhoconnect-redis-stack.txt +35 -0
- data/doc/session-and-configuration.txt +24 -0
- data/doc/settings.txt +51 -41
- data/doc/source-adapters.txt +45 -45
- data/doc/stats-middleware.txt +2 -2
- data/doc/supported-platforms.txt +6 -6
- data/doc/testing.txt +2 -2
- data/doc/tutorial.txt +63 -63
- data/examples/simple/Gemfile +7 -35
- data/examples/simple/config.ru +8 -26
- data/examples/simple/sources/product.rb +6 -6
- data/generators/rhoconnect.rb +5 -0
- data/generators/templates/application/Gemfile +7 -37
- data/generators/templates/application/Rakefile +8 -0
- data/generators/templates/application/config.ru +12 -31
- data/generators/templates/application/rcgemfile +44 -0
- data/generators/templates/application/settings/settings.yml +7 -5
- data/install.sh +4 -4
- data/installer/unix-like/create_texts.rb +7 -2
- data/installer/unix-like/rho_connect_install_constants.rb +2 -2
- data/installer/unix-like/rho_connect_install_installers.rb +1 -16
- data/lib/rhoconnect.rb +51 -38
- data/lib/rhoconnect/api/app/query.rb +4 -1
- data/lib/rhoconnect/api/app/search.rb +4 -1
- data/lib/rhoconnect/api/client/list_client_docs.rb +3 -1
- data/lib/rhoconnect/api/user/ping.rb +1 -5
- data/lib/rhoconnect/application/init.rb +43 -0
- data/lib/rhoconnect/async.rb +11 -6
- data/lib/rhoconnect/client_sync.rb +30 -37
- data/lib/rhoconnect/document.rb +4 -0
- data/lib/rhoconnect/graph_helper.rb +74 -56
- data/lib/rhoconnect/middleware/helpers.rb +4 -0
- data/lib/rhoconnect/ping.rb +1 -0
- data/lib/rhoconnect/ping/gcm.rb +58 -0
- data/lib/rhoconnect/predefined_adapters/bench_adapter.rb +7 -1
- data/lib/rhoconnect/source.rb +70 -56
- data/lib/rhoconnect/source_sync.rb +33 -5
- data/lib/rhoconnect/store.rb +358 -110
- data/lib/rhoconnect/user.rb +8 -0
- data/lib/rhoconnect/utilities.rb +16 -14
- data/lib/rhoconnect/version.rb +1 -1
- data/lib/rhoconnect/web-console/models/client.js +1 -1
- data/lib/rhoconnect/web-console/public/UNVR67bold.ttf +0 -0
- data/lib/rhoconnect/web-console/public/bootstrap.css +6 -0
- data/lib/rhoconnect/web-console/public/logo.png +0 -0
- data/lib/rhoconnect/web-console/server.rb +13 -11
- data/lib/rhoconnect/web-console/templates/index.erb +5 -5
- data/lib/rhoconnect/web-console/templates/jqplot.erb +1 -0
- data/lib/rhoconnect/web-console/views/doc.js +0 -4
- data/lib/rhoconnect/web-console/views/home.js +2 -1
- data/lib/rhoconnect/web-console/views/new_ping.js +11 -6
- data/lib/rhoconnect/web-console/views/stats.js +9 -5
- data/rhoconnect.gemspec +6 -4
- data/spec/api/app/fast_update_spec.rb +2 -2
- data/spec/api/source/get_source_params_spec.rb +1 -0
- data/spec/apps/rhotestapp/settings/settings.yml +5 -5
- data/spec/client_sync_spec.rb +3 -14
- data/spec/perf/perf_spec_helper.rb +11 -7
- data/spec/perf/store_perf_spec.rb +88 -11
- data/spec/ping/gcm_spec.rb +99 -0
- data/spec/server/server_spec.rb +7 -0
- data/spec/server/stats_spec.rb +9 -2
- data/spec/source_sync_spec.rb +29 -0
- data/spec/spec_helper.rb +40 -38
- data/spec/stats/record_spec.rb +18 -9
- data/spec/store_spec.rb +128 -19
- data/spec/testdata/10000-data.txt +0 -0
- data/spec/testdata/5-data.txt +0 -0
- data/spec/testdata/5000-data.txt +0 -0
- data/tasks/jasmine.rake +1 -0
- data/tasks/redis.rake +16 -13
- metadata +71 -39
- data/commands/commands/redis_commands/redis_download.rb +0 -33
- data/commands/commands/redis_commands/redis_install.rb +0 -26
- data/commands/commands/rhoconnect/attach.rb +0 -8
- data/commands/commands/rhoconnect/console.rb +0 -15
- data/commands/commands/rhoconnect/start.rb +0 -18
- data/commands/utilities/dtach_installed.rb +0 -10
data/doc/source-adapters.txt
CHANGED
@@ -62,44 +62,44 @@ Generating a source adapter named "product" will generate a new ruby class calle
|
|
62
62
|
def initialize(source)
|
63
63
|
super(source)
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
def login
|
67
67
|
# TODO: Login to your data source here if necessary
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
def query
|
71
|
-
# TODO: Query your backend data source and assign the records
|
71
|
+
# TODO: Query your backend data source and assign the records
|
72
72
|
# to a nested hash structure called @result. For example:
|
73
|
-
# @result = {
|
73
|
+
# @result = {
|
74
74
|
# "1"=>{"name"=>"Acme", "industry"=>"Electronics"},
|
75
75
|
# "2"=>{"name"=>"Best", "industry"=>"Software"}
|
76
76
|
# }
|
77
77
|
raise SourceAdapterException.new("Please provide some code to read records from the backend data source")
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
def sync
|
81
81
|
super
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
def create(create_hash)
|
85
85
|
# TODO: Create a new record in your backend data source
|
86
|
-
# If your rhodes rhom object contains image/binary data
|
86
|
+
# If your rhodes rhom object contains image/binary data
|
87
87
|
# (has the image_uri attribute), then a blob will be provided
|
88
88
|
raise "Please provide some code to create a single record in the backend data source using the create_hash"
|
89
89
|
end
|
90
|
-
|
90
|
+
|
91
91
|
def update(update_hash)
|
92
92
|
# TODO: Update an existing record in your backend data source
|
93
93
|
raise "Please provide some code to update a single record in the backend data source using the update_hash"
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
def delete(delete_hash)
|
97
97
|
# TODO: write some code here if applicable
|
98
98
|
# be sure to have a hash key and value for "object"
|
99
99
|
# for now, we'll say that its OK to not have a delete operation
|
100
100
|
# raise "Please provide some code to delete a single object in the backend application using the object_id"
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
def logoff
|
104
104
|
# TODO: Logout from the data source if necessary
|
105
105
|
end
|
@@ -108,8 +108,8 @@ Generating a source adapter named "product" will generate a new ruby class calle
|
|
108
108
|
It also adds a corresponding [rspec](http://rspec.info/) file `spec/sources/product_spec.rb` and updates `settings/settings.yml` with the product adapter to the sources section with some default options:
|
109
109
|
|
110
110
|
:::yaml
|
111
|
-
:sources:
|
112
|
-
Product:
|
111
|
+
:sources:
|
112
|
+
Product:
|
113
113
|
:poll_interval: 300
|
114
114
|
|
115
115
|
## Source Adapter API
|
@@ -162,7 +162,7 @@ Search your backend based on params and build a hash of hashes (optional). Simi
|
|
162
162
|
end
|
163
163
|
|
164
164
|
Next, you will need to add search to your Rhodes application. For details, see the [Rhodes search section](/rhodes/synchronization#filtering-datasets-with-search).
|
165
|
-
|
165
|
+
|
166
166
|
### `create(create_hash)`
|
167
167
|
Create a new record in the backend (optional).
|
168
168
|
|
@@ -190,7 +190,7 @@ Delete an existing record in the backend (optional).
|
|
190
190
|
def delete(delete_hash)
|
191
191
|
MyWebService.delete(delete_hash['id'])
|
192
192
|
end
|
193
|
-
|
193
|
+
|
194
194
|
### `current_user`
|
195
195
|
Returns the current user which called the adapter. For example, you could filter results for a specific user in your query method:
|
196
196
|
|
@@ -198,7 +198,7 @@ Returns the current user which called the adapter. For example, you could filte
|
|
198
198
|
def query
|
199
199
|
@result = MyWebService.get_records_for_user(current_user.login)
|
200
200
|
end
|
201
|
-
|
201
|
+
|
202
202
|
### `stash_result`
|
203
203
|
Saves the current state of `@result` to redis and assigns it to `nil`. Typically this is used when your adapter has to paginate through backend service data.
|
204
204
|
|
@@ -208,14 +208,14 @@ Saves the current state of `@result` to redis and assigns it to `nil`. Typicall
|
|
208
208
|
('a'..'z').each_with_index do |letter,i|
|
209
209
|
@result ||= {}
|
210
210
|
@result.merge!( DictionaryService.get_records_for(letter) )
|
211
|
-
stash_result if i % 2
|
211
|
+
stash_result if i % 2 == 0
|
212
212
|
end
|
213
213
|
end
|
214
214
|
|
215
215
|
## Data Partitioning
|
216
|
-
Data is stored in RhoConnect using [redis sets](http://redis.io/commands#set). The `@result` hash from the `query` method is stored in redis and referred to as the Master Document or MD.
|
216
|
+
Data is stored in RhoConnect using [redis sets](http://redis.io/commands#set). The `@result` hash from the `query` method is stored in redis and referred to as the Master Document or MD.
|
217
217
|
|
218
|
-
The MD is referenced in RhoConnect by a corresponding partition. Source adapters can partition data in two ways: user and app. As you might have guessed, user partitioning stores a copy of the source adapter MD for each user (one copy shared across all devices for a user).
|
218
|
+
The MD is referenced in RhoConnect by a corresponding partition. Source adapters can partition data in two ways: user and app. As you might have guessed, user partitioning stores a copy of the source adapter MD for each user (one copy shared across all devices for a user).
|
219
219
|
|
220
220
|
Likewise, app partitioning stores one copy of the source adapter MD for the entire application (all users and devices share the same data). App partitioning can be particularly useful if you have source adapters which retrieve large amounts of data that is fixed from user to user, for example a global product catalog. Using app partitioning wherever possible ***greatly reduces*** the amount of data in redis.
|
221
221
|
|
@@ -223,17 +223,17 @@ Likewise, app partitioning stores one copy of the source adapter MD for the enti
|
|
223
223
|
User partitioning is the default scheme for source adapters, however you can explicitly define it in `settings/settings.yml` with:
|
224
224
|
|
225
225
|
:::yaml
|
226
|
-
:sources:
|
227
|
-
Product:
|
226
|
+
:sources:
|
227
|
+
Product:
|
228
228
|
:poll_interval: 300
|
229
229
|
:partition_type: user
|
230
230
|
|
231
231
|
### App Partition
|
232
232
|
Enable app partitioning the same way:
|
233
|
-
|
233
|
+
|
234
234
|
:::yaml
|
235
|
-
:sources:
|
236
|
-
Product:
|
235
|
+
:sources:
|
236
|
+
Product:
|
237
237
|
:poll_interval: 300
|
238
238
|
:partition_type: app
|
239
239
|
|
@@ -241,10 +241,10 @@ Now you have a single copy of the `Product` source adapter dataset for all users
|
|
241
241
|
|
242
242
|
## Pass Through
|
243
243
|
RhoConnect provides a simple way to keep data out of redis. If you have sensitive data that you do not want saved in redis, add the pass_through option in settings/settings.yml for each source:
|
244
|
-
|
244
|
+
|
245
245
|
:::yaml
|
246
|
-
:sources:
|
247
|
-
Product:
|
246
|
+
:sources:
|
247
|
+
Product:
|
248
248
|
:pass_through: true
|
249
249
|
|
250
250
|
**NOTE: When running query or search the entire data set will be returned from your backend service. **
|
@@ -266,7 +266,7 @@ RhoConnect provides a simple redis interface for saving/retrieving arbitrary dat
|
|
266
266
|
)
|
267
267
|
|
268
268
|
Store.get_data('mydata') #=> { '1' => { 'hello' => 'world' } }
|
269
|
-
|
269
|
+
|
270
270
|
## Handling Exceptions
|
271
271
|
If your source adapter raises an instance of `SourceAdapterException`, the resulting message will be sent to the client's sync callback(in `@params['error_message']`). See the rhodes [sync exception handling docs](/rhodes/synchronization#handling-exceptions) for more details.
|
272
272
|
|
@@ -275,7 +275,7 @@ You can use `SourceAdapterException` as a convenient way to notify your applicat
|
|
275
275
|
For example, your delete method might check the web service HTTP response code was 200 to make sure the record was deleted:
|
276
276
|
|
277
277
|
:::ruby
|
278
|
-
def delete(delete_hash)
|
278
|
+
def delete(delete_hash)
|
279
279
|
rest_result = RestClient.delete("#{@base}/#{delete_hash['id']}")
|
280
280
|
if rest_result.code != 200
|
281
281
|
raise SourceAdapterException.new("Error deleting record.")
|
@@ -307,9 +307,9 @@ Handling conflicts in RhoConnect follows the same pattern as handling exceptions
|
|
307
307
|
Raise this if your adapter has detected a conflict.
|
308
308
|
|
309
309
|
:::ruby
|
310
|
-
def update(update_hash)
|
310
|
+
def update(update_hash)
|
311
311
|
obj_id = update_hash['id']
|
312
|
-
update_hash.delete('id')
|
312
|
+
update_hash.delete('id')
|
313
313
|
rest_result = RestClient.put("#{@base}/#{obj_id}",:product => update_hash)
|
314
314
|
if rest_result.code != 200
|
315
315
|
raise SourceAdapterObjectConflictError.new("Conflict detected updating the object.")
|
@@ -324,12 +324,12 @@ Here's a complete example of how the completed [product adapter might look](http
|
|
324
324
|
require 'rest_client'
|
325
325
|
|
326
326
|
class Product < SourceAdapter
|
327
|
-
|
327
|
+
|
328
328
|
def initialize(source)
|
329
|
-
@base = 'http://rhostore.
|
329
|
+
@base = 'http://rhostore.herokuapp.com/products'
|
330
330
|
super(source)
|
331
331
|
end
|
332
|
-
|
332
|
+
|
333
333
|
def query(params=nil)
|
334
334
|
rest_result = RestClient.get("#{@base}.json").body
|
335
335
|
|
@@ -341,29 +341,29 @@ Here's a complete example of how the completed [product adapter might look](http
|
|
341
341
|
@result={}
|
342
342
|
parsed.each do |item|
|
343
343
|
@result[item["product"]["id"].to_s] = item["product"]
|
344
|
-
end if parsed
|
344
|
+
end if parsed
|
345
345
|
end
|
346
|
-
|
346
|
+
|
347
347
|
def create(create_hash)
|
348
348
|
res = RestClient.post(@base,:product => create_hash)
|
349
|
-
|
350
|
-
# After create we are redirected to the new record.
|
351
|
-
# We need to get the id of that record and return
|
352
|
-
# it as part of create so rhoconnect can establish a link
|
353
|
-
# from its temporary object on the client to this newly
|
349
|
+
|
350
|
+
# After create we are redirected to the new record.
|
351
|
+
# We need to get the id of that record and return
|
352
|
+
# it as part of create so rhoconnect can establish a link
|
353
|
+
# from its temporary object on the client to this newly
|
354
354
|
# created object on the server
|
355
355
|
JSON.parse(
|
356
356
|
RestClient.get("#{res.headers[:location]}.json").body
|
357
357
|
)["product"]["id"]
|
358
358
|
end
|
359
|
-
|
360
|
-
def update(update_hash)
|
359
|
+
|
360
|
+
def update(update_hash)
|
361
361
|
obj_id = update_hash['id']
|
362
|
-
update_hash.delete('id')
|
362
|
+
update_hash.delete('id')
|
363
363
|
RestClient.put("#{@base}/#{obj_id}",:product => update_hash)
|
364
364
|
end
|
365
|
-
|
366
|
-
def delete(delete_hash)
|
365
|
+
|
366
|
+
def delete(delete_hash)
|
367
367
|
RestClient.delete("#{@base}/#{delete_hash['id']}")
|
368
368
|
end
|
369
369
|
end
|
data/doc/stats-middleware.txt
CHANGED
@@ -46,12 +46,12 @@ A complete config.ru might look like:
|
|
46
46
|
Now just restart your rhoconnect application:
|
47
47
|
|
48
48
|
:::term
|
49
|
-
$
|
49
|
+
$ rhoconnect restart
|
50
50
|
|
51
51
|
And navigate to the 'Statistics' tab in your RhoConnect console:
|
52
52
|
|
53
53
|
:::term
|
54
|
-
$
|
54
|
+
$ rhoconnect web
|
55
55
|
|
56
56
|
## Stats Console
|
57
57
|
You should see graphs, organized by categories:
|
data/doc/supported-platforms.txt
CHANGED
@@ -19,7 +19,7 @@ Supported Platforms
|
|
19
19
|
<td> ruby-1.8.7-p370 </td>
|
20
20
|
<td> ree-1.8.7-2012.02 </td>
|
21
21
|
<td> ruby-1.9.2-p320 </td>
|
22
|
-
<td> ruby-1.9.3-
|
22
|
+
<td> ruby-1.9.3-p286 </td>
|
23
23
|
<td> jruby-1.6.7 </td>
|
24
24
|
<td>Development &<br/>Production</td>
|
25
25
|
</tr>
|
@@ -29,7 +29,7 @@ Supported Platforms
|
|
29
29
|
<td> ruby-1.8.7-p370 </td>
|
30
30
|
<td> ree-1.8.7-2012.02 </td>
|
31
31
|
<td> ruby-1.9.2-p320 </td>
|
32
|
-
<td> ruby-1.9.3-
|
32
|
+
<td> ruby-1.9.3-p286 </td>
|
33
33
|
<td> jruby-1.6.7 </td>
|
34
34
|
<td>Development &<br/>Production</td>
|
35
35
|
</tr>
|
@@ -39,7 +39,7 @@ Supported Platforms
|
|
39
39
|
<td> ruby-1.8.7-p370 </td>
|
40
40
|
<td class='tbd'> TBD </td>
|
41
41
|
<td class='tbd'> TBD </td>
|
42
|
-
<td> ruby-1.9.3-
|
42
|
+
<td> ruby-1.9.3-p286 </td>
|
43
43
|
<td class='tbd'> TBD </td>
|
44
44
|
<td>Development</td>
|
45
45
|
</tr>
|
@@ -60,14 +60,14 @@ RhoConnect for Windows is supported only in development environments. The compon
|
|
60
60
|
|
61
61
|
RhoConnect for Linux and Mac OS X is supported for both development and production environments. The components you should have for this environment are:
|
62
62
|
|
63
|
-
* Ruby 1.9.3
|
64
|
-
* Nginx HTTP server (1.
|
63
|
+
* Ruby 1.9.3
|
64
|
+
* Nginx HTTP server (1.3.x)
|
65
65
|
* Redis data store (2.4.x)
|
66
66
|
* Thin application server
|
67
67
|
* RhoConnect gem with all required dependencies
|
68
68
|
|
69
|
+
|
69
70
|
## Known Issues
|
70
71
|
|
71
|
-
* Windows platform: only pre-release (~> 1.0.0.beta) of <b>eventmachine</b> is working on Windows
|
72
72
|
* If you deploy your RhoConnect application deploy on <b>Heroku</b>, then use new [Cedar](http://devcenter.heroku.com/articles/cedar) stack. The Bamboo stack is older than the widespread adoption of bundler,
|
73
73
|
so on Bamboo your application is not invoked in the context of your bundle and might not works properly.
|
data/doc/testing.txt
CHANGED
@@ -20,8 +20,8 @@ The full code listing for this product_spec is available [on github](http://gith
|
|
20
20
|
From your application's root folder:
|
21
21
|
|
22
22
|
:::term
|
23
|
-
$
|
24
|
-
[05:51:48 PM
|
23
|
+
$ rhoconnect spec
|
24
|
+
[05:51:48 PM 2012-04-19] Rhoconnect Server v3.2.0 started...
|
25
25
|
Product
|
26
26
|
it should behave like SpecHelper
|
27
27
|
should process Product query (PENDING: No reason given)
|
data/doc/tutorial.txt
CHANGED
@@ -10,7 +10,7 @@ Full documentation of all capabilities of RhoConnect is in the [RhoConnect Devel
|
|
10
10
|
|
11
11
|
## Installing the RhoConnect Dependencies
|
12
12
|
|
13
|
-
* Install RhoConnect
|
13
|
+
* Install RhoConnect [from the command line](/rhoconnect/install) or by installing [RhoMobile Suite](/rhomobile-install).
|
14
14
|
|
15
15
|
* If you have a RhoConnect license that has been sent to you, you can replace the settings/license.yml file as described [here](/rhoconnect/licensing).
|
16
16
|
|
@@ -33,7 +33,7 @@ Enter the name for your RhoConnect application in Project name; in this case, "s
|
|
33
33
|
:::term
|
34
34
|
$ rhoconnect app storeserver
|
35
35
|
$ cd storeserver
|
36
|
-
|
36
|
+
|
37
37
|
Next, install the application's dependencies. RhoConnect applications use [bundler](http://gembundler.com/) to manage dependencies, so you can install them with:
|
38
38
|
|
39
39
|
:::term
|
@@ -42,21 +42,21 @@ Next, install the application's dependencies. RhoConnect applications use [bundl
|
|
42
42
|
**If you are running this for the first time on Mac or Linux**, you will need to install [dtach](http://dtach.sourceforge.net/):
|
43
43
|
|
44
44
|
:::term
|
45
|
-
$ [sudo]
|
45
|
+
$ [sudo] rhoconnect dtach-install
|
46
46
|
|
47
47
|
Now you can run redis and your RhoConnect app:
|
48
48
|
|
49
49
|
:::term
|
50
|
-
$
|
51
|
-
$
|
50
|
+
$ rhoconnect redis-start
|
51
|
+
$ rhoconnect start
|
52
52
|
|
53
53
|
If everything went well you should see:
|
54
54
|
|
55
|
-
[
|
55
|
+
[01:48:15 PM 2012-08-04] Rhoconnect Server v3.3.4 started...
|
56
56
|
|
57
57
|
## Defining RhoConnect Source Adapters
|
58
58
|
|
59
|
-
Once RhoConnect is installed we're ready to build a RhoConnect source to integrate with our backend application. To define a RhoConnect source you just need to identify a handful of operations to interact with your backend data source: login, query, sync, create, update, delete and logoff. For more information please see the [RhoConnect source adapter](/rhoconnect/source-adapters) documentation.
|
59
|
+
Once RhoConnect is installed we're ready to build a RhoConnect source to integrate with our backend application. To define a RhoConnect source you just need to identify a handful of operations to interact with your backend data source: login, query, sync, create, update, delete and logoff. For more information please see the [RhoConnect source adapter](/rhoconnect/source-adapters) documentation.
|
60
60
|
|
61
61
|
### Generating the Source Adapter from RhoStudio
|
62
62
|
|
@@ -83,7 +83,7 @@ You can open these files for editing by clicking on them in the Project Explorer
|
|
83
83
|
|
84
84
|
### Generating the Source Adapter from the Command Line
|
85
85
|
|
86
|
-
From the command line, navigate to the main folder for your RhoConnect app: in this case, storeserver.
|
86
|
+
From the command line, navigate to the main folder for your RhoConnect app: in this case, storeserver.
|
87
87
|
|
88
88
|
Then run the command to generate a source adapter for the product model. The product model is used as an example for a Rhodes storemanager in the [RhoStudio tutorial](/rhostudio.tutorial) and the [documentation to generate a Rhodes application](/rhodes/generator).
|
89
89
|
|
@@ -107,47 +107,47 @@ The generated source adapter file (in this case, product.rb) is similar to the c
|
|
107
107
|
def initialize(source)
|
108
108
|
super(source)
|
109
109
|
end
|
110
|
-
|
110
|
+
|
111
111
|
def login
|
112
112
|
# TODO: Login to your data source here if necessary
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
def query
|
116
|
-
# TODO: Query your backend data source and assign the records
|
116
|
+
# TODO: Query your backend data source and assign the records
|
117
117
|
# to a nested hash structure called @result. For example:
|
118
|
-
# @result = {
|
118
|
+
# @result = {
|
119
119
|
# "1"=>{"name"=>"Acme", "industry"=>"Electronics"},
|
120
120
|
# "2"=>{"name"=>"Best", "industry"=>"Software"}
|
121
121
|
# }
|
122
122
|
raise SourceAdapterException.new("Please provide some code to read records from the backend data source")
|
123
123
|
end
|
124
|
-
|
124
|
+
|
125
125
|
def sync
|
126
|
-
# Manipulate @result before it is saved, or save it
|
126
|
+
# Manipulate @result before it is saved, or save it
|
127
127
|
# yourself using the Rhoconnect::Store interface.
|
128
128
|
# By default, super is called below which simply saves @result
|
129
129
|
super
|
130
130
|
end
|
131
|
-
|
131
|
+
|
132
132
|
def create(create_hash)
|
133
133
|
# TODO: Create a new record in your backend data source
|
134
|
-
# If your rhodes rhom object contains image/binary data
|
134
|
+
# If your rhodes rhom object contains image/binary data
|
135
135
|
# (has the image_uri attribute), then a blob will be provided
|
136
136
|
raise "Please provide some code to create a single record in the backend data source using the create_hash"
|
137
137
|
end
|
138
|
-
|
138
|
+
|
139
139
|
def update(update_hash)
|
140
140
|
# TODO: Update an existing record in your backend data source
|
141
141
|
raise "Please provide some code to update a single record in the backend data source using the update_hash"
|
142
142
|
end
|
143
|
-
|
143
|
+
|
144
144
|
def delete(delete_hash)
|
145
145
|
# TODO: write some code here if applicable
|
146
146
|
# be sure to have a hash key and value for "object"
|
147
147
|
# for now, we'll say that its OK to not have a delete operation
|
148
148
|
# raise "Please provide some code to delete a single object in the backend application using the object_id"
|
149
149
|
end
|
150
|
-
|
150
|
+
|
151
151
|
def logoff
|
152
152
|
# TODO: Logout from the data source if necessary
|
153
153
|
end
|
@@ -156,44 +156,44 @@ The generated source adapter file (in this case, product.rb) is similar to the c
|
|
156
156
|
The generator will also edit settings/settings.yml and add the product adapter to the sources section with some default options:
|
157
157
|
|
158
158
|
:::ruby
|
159
|
-
#Sources
|
160
|
-
:sources:
|
161
|
-
Product:
|
159
|
+
#Sources
|
160
|
+
:sources:
|
161
|
+
Product:
|
162
162
|
:poll_interval: 300
|
163
163
|
|
164
|
-
The next step is for you to fill in the login, query, create, update, delete and logoff methods with your own code to call a backend service.
|
164
|
+
The next step is for you to fill in the login, query, create, update, delete and logoff methods with your own code to call a backend service.
|
165
165
|
|
166
166
|
## A RhoConnect Query
|
167
167
|
|
168
|
-
If you're doing a readonly non-authenticated source adapter, you can just write one method, query, to retrieve records as we describe here. The following is a sample query method to interact with a simple product catalog (available at http://rhostore.
|
168
|
+
If you're doing a readonly non-authenticated source adapter, you can just write one method, query, to retrieve records as we describe here. The following is a sample query method to interact with a simple product catalog (available at http://rhostore.herokuapp.com) that exposes a REST interface. Note that RhoConnect can work with any protocol. This example shows JSON over HTTP with a REST interface, since that is common. The RhoConnect source adapter is Ruby code and there are ruby libraries (aka gems) that will make it easy to connect to and parse whatever you need -- the query code would just be slightly different.
|
169
169
|
|
170
170
|
For a more complete example of rewriting the source adapter methods (such as create, update, and delete), refer to the [source adapter example](/rhoconnect/source-adapters#sample-adapter) in the RhoConnect Developer Reference.
|
171
171
|
|
172
|
-
Our sample web service for returning all products in the catalog (http://rhostore.
|
173
|
-
|
172
|
+
Our sample web service for returning all products in the catalog (http://rhostore.herokuapp.com/products.json) returns data like this:
|
173
|
+
|
174
174
|
:::json
|
175
175
|
[
|
176
176
|
{
|
177
177
|
"product": {
|
178
|
-
"name": "iPhone",
|
179
|
-
"brand": "Apple",
|
180
|
-
"updated_at": "2010-05-11T02:04:57Z",
|
181
|
-
"price": "$299.99",
|
182
|
-
"quantity": "5",
|
183
|
-
"id": 649,
|
184
|
-
"sku": "1234",
|
178
|
+
"name": "iPhone",
|
179
|
+
"brand": "Apple",
|
180
|
+
"updated_at": "2010-05-11T02:04:57Z",
|
181
|
+
"price": "$299.99",
|
182
|
+
"quantity": "5",
|
183
|
+
"id": 649,
|
184
|
+
"sku": "1234",
|
185
185
|
"created_at": "2010-05-11T02:04:57Z"
|
186
186
|
}
|
187
|
-
},
|
187
|
+
},
|
188
188
|
{
|
189
189
|
"product": {
|
190
|
-
"name": "Accord",
|
191
|
-
"brand": "Honda",
|
192
|
-
"updated_at": "2010-05-13T22:24:48Z",
|
193
|
-
"price": "$6000",
|
194
|
-
"quantity": "",
|
195
|
-
"id": 648,
|
196
|
-
"sku": "123",
|
190
|
+
"name": "Accord",
|
191
|
+
"brand": "Honda",
|
192
|
+
"updated_at": "2010-05-13T22:24:48Z",
|
193
|
+
"price": "$6000",
|
194
|
+
"quantity": "",
|
195
|
+
"id": 648,
|
196
|
+
"sku": "123",
|
197
197
|
"created_at": "2010-05-11T02:04:53Z"
|
198
198
|
}
|
199
199
|
}
|
@@ -213,33 +213,33 @@ For convenience, we'll add an instance variable @base which contains the base UR
|
|
213
213
|
|
214
214
|
:::ruby
|
215
215
|
def initialize(source)
|
216
|
-
@base = 'http://rhostore.
|
216
|
+
@base = 'http://rhostore.herokuapp.com/products'
|
217
217
|
super(source)
|
218
218
|
end
|
219
219
|
|
220
220
|
Then fill in the query method:
|
221
|
-
|
221
|
+
|
222
222
|
:::ruby
|
223
223
|
def query
|
224
224
|
parsed=JSON.parse(RestClient.get("#{@base}.json").body)
|
225
225
|
|
226
226
|
@result={}
|
227
227
|
if parsed
|
228
|
-
parsed.each do |item|
|
228
|
+
parsed.each do |item|
|
229
229
|
key = item["product"]["id"].to_s
|
230
230
|
@result[key]=item["product"]
|
231
231
|
end
|
232
|
-
end
|
232
|
+
end
|
233
233
|
end
|
234
234
|
|
235
|
-
**NOTE: The code above could be much more concise, but it is written to be easily readable by programmers who are unfamiliar with the Ruby language. If you are new to Ruby, you can read [Ruby from other languages](http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/) for a good introduction.
|
235
|
+
**NOTE: The code above could be much more concise, but it is written to be easily readable by programmers who are unfamiliar with the Ruby language. If you are new to Ruby, you can read [Ruby from other languages](http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/) for a good introduction.
|
236
236
|
Each hash key in the inner hash represents an attribute of an individual object. All datatypes must be strings (so the hash values need to all be strings, not integers).**
|
237
237
|
|
238
238
|
For example:
|
239
|
-
|
239
|
+
|
240
240
|
:::ruby
|
241
241
|
@result = {
|
242
|
-
"1" => {
|
242
|
+
"1" => {
|
243
243
|
"name" => "inner tube",
|
244
244
|
"brand" => "Michelin"
|
245
245
|
},
|
@@ -247,19 +247,19 @@ For example:
|
|
247
247
|
"name" => "tire",
|
248
248
|
"brand" => "Michelin"
|
249
249
|
}
|
250
|
-
}
|
250
|
+
}
|
251
251
|
|
252
252
|
## Testing Sync from the Client
|
253
253
|
|
254
254
|
Make sure you are running redis. From the folder in which you app is located, run this command.
|
255
255
|
|
256
256
|
:::term
|
257
|
-
$
|
258
|
-
|
257
|
+
$ rhoconnect redis-start
|
258
|
+
|
259
259
|
Then start (or restart) your server:
|
260
|
-
|
260
|
+
|
261
261
|
:::term
|
262
|
-
$
|
262
|
+
$ rhoconnect start
|
263
263
|
|
264
264
|
**Note: The RhoConnect application must be run from the command line. It does not run from RhoStudio.**
|
265
265
|
|
@@ -280,7 +280,7 @@ Enable sync in your corresponding Rhodes app, storemanager/app/Product/product.r
|
|
280
280
|
enable :sync
|
281
281
|
end
|
282
282
|
|
283
|
-
To get a feel for what is happening, it is helpful to watch the server log (the output of
|
283
|
+
To get a feel for what is happening, it is helpful to watch the server log (the output of `rhoconnect start`) in one window, and tail the client log in another window. For example, on the iPhone, display the end of the client log with:
|
284
284
|
|
285
285
|
:::term
|
286
286
|
$ tail -f rholog-User.txt
|
@@ -293,7 +293,7 @@ To sync with the RhoConnect server, the Rhodes client must log in. The Rhodes ge
|
|
293
293
|
2. Login using any name & password. The generated code allows any login, but you can modify that in application.rb.
|
294
294
|
<img src="http://rhodocs.s3.amazonaws.com/rhoconnect-tutorial/rhosimulator-login.png"/>
|
295
295
|
|
296
|
-
3. If login is successful, you will see a Client ID in the settings screen. The ClientID is generated the first time you log in and is stored in the client database. It serves as a unique identifier which is required for rhoconnect. (Note: this value will persist across logins, but if you reset the client database or the user re-installs the app, a new ClientID will be generated.)
|
296
|
+
3. If login is successful, you will see a Client ID in the settings screen. The ClientID is generated the first time you log in and is stored in the client database. It serves as a unique identifier which is required for rhoconnect. (Note: this value will persist across logins, but if you reset the client database or the user re-installs the app, a new ClientID will be generated.)
|
297
297
|
4. Sync is triggered automatically. Click on the home icon and then select "Products" and you should see the list of product records from the server.
|
298
298
|
|
299
299
|
This example shows a couple of iPhone products.
|
@@ -303,19 +303,19 @@ This example shows a couple of iPhone products.
|
|
303
303
|
## Creating Objects with RhoConnect
|
304
304
|
|
305
305
|
For your create method, the RhoConnect server will pass you a hash containing the new record, called "create_hash". For example, it might be:
|
306
|
-
|
306
|
+
|
307
307
|
:::ruby
|
308
308
|
{
|
309
309
|
"name" => "Hovercraft",
|
310
310
|
"brand" => "Acme"
|
311
311
|
}
|
312
312
|
|
313
|
-
The RhoConnect sources/product.rb create method will be called once for every object that has been created on the client since the last sync. Your code for create (or edit or delete) needs to use this populated array to do its work. Below is an example of a create method against the [rhostore](http://rhostore.
|
313
|
+
The RhoConnect sources/product.rb create method will be called once for every object that has been created on the client since the last sync. Your code for create (or edit or delete) needs to use this populated array to do its work. Below is an example of a create method against the [rhostore](http://rhostore.herokuapp.com), which accepts an HTTP POST to perform a create action. The create method should return a unique id string for the object for it to be later modifiable by the client. If no id is returned, then you should treat the client object as read only, because it will not be able to be synchronized.
|
314
314
|
|
315
315
|
:::ruby
|
316
316
|
def create(create_hash)
|
317
317
|
result = RestClient.post(@base, :product => create_hash)
|
318
|
-
|
318
|
+
|
319
319
|
# after create we are redirected to the new record.
|
320
320
|
# The URL of the new record is given in the location header
|
321
321
|
location = "#{result.headers[:location]}.json"
|
@@ -323,7 +323,7 @@ The RhoConnect sources/product.rb create method will be called once for every ob
|
|
323
323
|
# We need to get the id of that record and return it as part of create
|
324
324
|
# so rhoconnect can establish a link from its temporary object on the
|
325
325
|
# client to this newly created object on the server
|
326
|
-
|
326
|
+
|
327
327
|
new_record = RestClient.get(location).body
|
328
328
|
JSON.parse(new_record)["product"]["id"].to_s
|
329
329
|
end
|
@@ -340,18 +340,18 @@ The generated RhoConnect application code includes a file at the root of the dir
|
|
340
340
|
def authenticate(username,password,session)
|
341
341
|
true # do some interesting authentication here...
|
342
342
|
end
|
343
|
-
|
343
|
+
|
344
344
|
# Add hooks for application startup here
|
345
345
|
# Don't forget to call super at the end!
|
346
346
|
def initializer(path)
|
347
347
|
super
|
348
348
|
end
|
349
|
-
|
349
|
+
|
350
350
|
# Calling super here returns rack tempfile path:
|
351
351
|
# i.e. /var/folders/J4/J4wGJ-r6H7S313GEZ-Xx5E+++TI
|
352
352
|
# Note: This tempfile is removed when server stops or crashes...
|
353
353
|
# See http://rack.rubyforge.org/doc/Multipart.html for more info
|
354
|
-
#
|
354
|
+
#
|
355
355
|
# Override this by creating a copy of the file somewhere
|
356
356
|
# and returning the path to that file (then don't call super!):
|
357
357
|
# i.e. /mnt/myimages/soccer.png
|
@@ -367,7 +367,7 @@ If your back end web service requires authentication, add code to the authentica
|
|
367
367
|
|
368
368
|
:::ruby
|
369
369
|
def authenticate(username, password, session)
|
370
|
-
# ... connect to backend using API and authenticate ...
|
370
|
+
# ... connect to backend using API and authenticate ...
|
371
371
|
if success
|
372
372
|
# save the data for later use in the source adapter
|
373
373
|
Store.put_value("username:#{username}:token",username)
|