jim 0.1.2 → 0.2.0

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.
Files changed (72) hide show
  1. data/HISTORY +28 -0
  2. data/README.rdoc +1 -1
  3. data/Rakefile +2 -1
  4. data/jim.gemspec +67 -9
  5. data/lib/jim/bundler.rb +14 -11
  6. data/lib/jim/cli.rb +51 -9
  7. data/lib/jim/index.rb +3 -7
  8. data/lib/jim/installer.rb +140 -22
  9. data/lib/jim/templates/commands +21 -6
  10. data/lib/jim/version_parser.rb +2 -3
  11. data/lib/jim.rb +19 -2
  12. data/test/fixtures/jimfile +3 -3
  13. data/test/fixtures/sammy-0.5.0/HISTORY.md +135 -0
  14. data/test/fixtures/sammy-0.5.0/LICENSE +22 -0
  15. data/test/fixtures/sammy-0.5.0/README.md +81 -0
  16. data/test/fixtures/sammy-0.5.0/Rakefile +174 -0
  17. data/test/fixtures/sammy-0.5.0/examples/backend/README.md +23 -0
  18. data/test/fixtures/sammy-0.5.0/examples/backend/Rakefile +15 -0
  19. data/test/fixtures/sammy-0.5.0/examples/backend/app.rb +17 -0
  20. data/test/fixtures/sammy-0.5.0/examples/backend/app.ru +3 -0
  21. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/app.js +106 -0
  22. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/jquery.cloudkit.js +840 -0
  23. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/jquery.js +19 -0
  24. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/sammy.js +1013 -0
  25. data/test/fixtures/sammy-0.5.0/examples/backend/public/templates/index.html.erb +11 -0
  26. data/test/fixtures/sammy-0.5.0/examples/backend/public/templates/task.html.erb +4 -0
  27. data/test/fixtures/sammy-0.5.0/examples/backend/public/templates/task_details.html.erb +4 -0
  28. data/test/fixtures/sammy-0.5.0/examples/backend/views/app.sass +63 -0
  29. data/test/fixtures/sammy-0.5.0/examples/backend/views/index.haml +18 -0
  30. data/test/fixtures/sammy-0.5.0/examples/form_handling/files/form.html +12 -0
  31. data/test/fixtures/sammy-0.5.0/examples/form_handling/index.html +65 -0
  32. data/test/fixtures/sammy-0.5.0/examples/hello_world/index.html +50 -0
  33. data/test/fixtures/sammy-0.5.0/examples/location_override/README.md +15 -0
  34. data/test/fixtures/sammy-0.5.0/examples/location_override/data.html +110 -0
  35. data/test/fixtures/sammy-0.5.0/examples/location_override/index.html +79 -0
  36. data/test/fixtures/sammy-0.5.0/examples/location_override/test.html +121 -0
  37. data/test/fixtures/sammy-0.5.0/lib/min/sammy-0.5.0.min.js +5 -0
  38. data/test/fixtures/sammy-0.5.0/lib/min/sammy-lastest.min.js +5 -0
  39. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.cache.js +117 -0
  40. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.haml.js +539 -0
  41. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.json.js +362 -0
  42. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.mustache.js +415 -0
  43. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.nested_params.js +118 -0
  44. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.storage.js +515 -0
  45. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.template.js +117 -0
  46. data/test/fixtures/sammy-0.5.0/lib/sammy.js +1367 -0
  47. data/test/fixtures/sammy-0.5.0/test/fixtures/partial +1 -0
  48. data/test/fixtures/sammy-0.5.0/test/fixtures/partial.html +1 -0
  49. data/test/fixtures/sammy-0.5.0/test/fixtures/partial.noengine +1 -0
  50. data/test/fixtures/sammy-0.5.0/test/fixtures/partial.template +1 -0
  51. data/test/fixtures/sammy-0.5.0/test/index.html +84 -0
  52. data/test/fixtures/sammy-0.5.0/test/test_sammy_application.js +953 -0
  53. data/test/fixtures/sammy-0.5.0/test/test_sammy_event_context.js +252 -0
  54. data/test/fixtures/sammy-0.5.0/test/test_sammy_location_proxy.js +91 -0
  55. data/test/fixtures/sammy-0.5.0/test/test_sammy_plugins.js +296 -0
  56. data/test/fixtures/sammy-0.5.0/test/test_sammy_storage.js +175 -0
  57. data/test/fixtures/sammy-0.5.0/test/test_server +27 -0
  58. data/test/fixtures/sammy-0.5.0/vendor/jquery-1.4.1.js +6078 -0
  59. data/test/fixtures/sammy-0.5.0/vendor/jquery-1.4.1.min.js +152 -0
  60. data/test/fixtures/sammy-0.5.0/vendor/jsdoc/doc.haml +58 -0
  61. data/test/fixtures/sammy-0.5.0/vendor/jsdoc/jsdoc.rb +143 -0
  62. data/test/fixtures/sammy-0.5.0/vendor/jslitmus.js +670 -0
  63. data/test/fixtures/sammy-0.5.0/vendor/qunit/qunit.css +119 -0
  64. data/test/fixtures/sammy-0.5.0/vendor/qunit/qunit.js +1043 -0
  65. data/test/fixtures/sammy-0.5.0/vendor/qunit-spec.js +127 -0
  66. data/test/helper.rb +23 -3
  67. data/test/test_jim_bundler.rb +9 -8
  68. data/test/test_jim_cli.rb +21 -12
  69. data/test/test_jim_installer.rb +152 -35
  70. data/test/test_jim_version_parser.rb +4 -0
  71. metadata +117 -27
  72. data/.document +0 -5
@@ -0,0 +1,840 @@
1
+ /*
2
+ json2.js
3
+ 2008-03-14
4
+
5
+ Public Domain
6
+
7
+ No warranty expressed or implied. Use at your own risk.
8
+
9
+ See http://www.JSON.org/js.html
10
+
11
+ This is a reference implementation. You are free to copy, modify, or
12
+ redistribute.
13
+
14
+ Use your own copy. It is extremely unwise to load third party
15
+ code into your pages.
16
+ */
17
+
18
+ if (!this.JSON) {
19
+
20
+ JSON = function () {
21
+
22
+ function f(n) { // Format integers to have at least two digits.
23
+ return n < 10 ? '0' + n : n;
24
+ }
25
+
26
+ Date.prototype.toJSON = function () {
27
+ return this.getUTCFullYear() + '-' +
28
+ f(this.getUTCMonth() + 1) + '-' +
29
+ f(this.getUTCDate()) + 'T' +
30
+ f(this.getUTCHours()) + ':' +
31
+ f(this.getUTCMinutes()) + ':' +
32
+ f(this.getUTCSeconds()) + 'Z';
33
+ };
34
+
35
+
36
+ var m = { // table of character substitutions
37
+ '\b': '\\b',
38
+ '\t': '\\t',
39
+ '\n': '\\n',
40
+ '\f': '\\f',
41
+ '\r': '\\r',
42
+ '"' : '\\"',
43
+ '\\': '\\\\'
44
+ };
45
+
46
+ function stringify(value, whitelist) {
47
+ var a, // The array holding the partial texts.
48
+ i, // The loop counter.
49
+ k, // The member key.
50
+ l, // Length.
51
+ r = /["\\\x00-\x1f\x7f-\x9f]/g,
52
+ v; // The member value.
53
+
54
+ switch (typeof value) {
55
+ case 'string':
56
+
57
+ return r.test(value) ?
58
+ '"' + value.replace(r, function (a) {
59
+ var c = m[a];
60
+ if (c) {
61
+ return c;
62
+ }
63
+ c = a.charCodeAt();
64
+ return '\\u00' + Math.floor(c / 16).toString(16) +
65
+ (c % 16).toString(16);
66
+ }) + '"' :
67
+ '"' + value + '"';
68
+
69
+ case 'number':
70
+
71
+ return isFinite(value) ? String(value) : 'null';
72
+
73
+ case 'boolean':
74
+ case 'null':
75
+ return String(value);
76
+
77
+ case 'object':
78
+
79
+ if (!value) {
80
+ return 'null';
81
+ }
82
+
83
+ if (typeof value.toJSON === 'function') {
84
+ return stringify(value.toJSON());
85
+ }
86
+ a = [];
87
+ if (typeof value.length === 'number' &&
88
+ !(value.propertyIsEnumerable('length'))) {
89
+
90
+ l = value.length;
91
+ for (i = 0; i < l; i += 1) {
92
+ a.push(stringify(value[i], whitelist) || 'null');
93
+ }
94
+
95
+ return '[' + a.join(',') + ']';
96
+ }
97
+ if (whitelist) {
98
+ l = whitelist.length;
99
+ for (i = 0; i < l; i += 1) {
100
+ k = whitelist[i];
101
+ if (typeof k === 'string') {
102
+ v = stringify(value[k], whitelist);
103
+ if (v) {
104
+ a.push(stringify(k) + ':' + v);
105
+ }
106
+ }
107
+ }
108
+ } else {
109
+
110
+ for (k in value) {
111
+ if (typeof k === 'string') {
112
+ v = stringify(value[k], whitelist);
113
+ if (v) {
114
+ a.push(stringify(k) + ':' + v);
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ return '{' + a.join(',') + '}';
121
+ }
122
+ }
123
+
124
+ return {
125
+ stringify: stringify,
126
+ parse: function (text, filter) {
127
+ var j;
128
+
129
+ function walk(k, v) {
130
+ var i, n;
131
+ if (v && typeof v === 'object') {
132
+ for (i in v) {
133
+ if (Object.prototype.hasOwnProperty.apply(v, [i])) {
134
+ n = walk(i, v[i]);
135
+ if (n !== undefined) {
136
+ v[i] = n;
137
+ } else {
138
+ delete v[i];
139
+ }
140
+ }
141
+ }
142
+ }
143
+ return filter(k, v);
144
+ }
145
+
146
+ if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
147
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
148
+ replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
149
+
150
+ j = eval('(' + text + ')');
151
+
152
+ return typeof filter === 'function' ? walk('', j) : j;
153
+ }
154
+
155
+ throw new SyntaxError('parseJSON');
156
+ }
157
+ };
158
+ }();
159
+ }
160
+
161
+ /*
162
+ Copyright Jason E. Smith 2008 Licensed under the Apache License, Version 2.0 (the "License");
163
+ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
164
+ */
165
+
166
+ /*
167
+ * CREDITS:
168
+ * Thanks to Kris Zyp from SitePen for contributing his source for
169
+ * a standalone port of JSONQuery (from the dojox.json.query module).
170
+ *
171
+ * OVERVIEW:
172
+ * JSONQuery.js is a standalone port of the dojox.json.query module. It is intended as
173
+ * a dropin solution with zero dependencies. JSONQuery is intended to succeed and improve upon
174
+ * the JSONPath api (http://goessner.net/articles/JsonPath/) which offers rich powerful
175
+ * querying capabilities similar to those of XQuery.
176
+ *
177
+ * EXAMPLES / USAGE:
178
+ * see http://www.sitepen.com/blog/2008/07/16/jsonquery-data-querying-beyond-jsonpath/
179
+ *
180
+ * *Ripped from original source.
181
+ * JSONQuery(queryString,object)
182
+ and
183
+ JSONQuery(queryString)(object)
184
+ always return identical results. The first one immediately evaluates, the second one returns a
185
+ function that then evaluates the object.
186
+
187
+ example:
188
+ JSONQuery("foo",{foo:"bar"})
189
+ This will return "bar".
190
+
191
+ example:
192
+ evaluator = JSONQuery("?foo='bar'&rating>3");
193
+ This creates a function that finds all the objects in an array with a property
194
+ foo that is equals to "bar" and with a rating property with a value greater
195
+ than 3.
196
+ evaluator([{foo:"bar",rating:4},{foo:"baz",rating:2}])
197
+ This returns:
198
+ {foo:"bar",rating:4}
199
+
200
+ example:
201
+ evaluator = JSONQuery("$[?price<15.00][\rating][0:10]");
202
+ This finds objects in array with a price less than 15.00 and sorts then
203
+ by rating, highest rated first, and returns the first ten items in from this
204
+ filtered and sorted list.
205
+
206
+ example:
207
+ var data = {customers:[
208
+ {name:"Susan", purchases:29},
209
+ {name:"Kim", purchases:150},
210
+ {name:"Jake", purchases:27}
211
+ ]};
212
+
213
+ var results = json.JSONQuery("$.customers[?purchases > 21 & name='Jake'][\\purchases]",data);
214
+
215
+ returns customers sorted by higest number of purchases to lowest.
216
+
217
+ */
218
+
219
+ (function(){
220
+ function map(arr, fun /*, thisp*/){
221
+ var len = arr.length;
222
+ if (typeof fun != "function")
223
+ throw new TypeError();
224
+
225
+ var res = new Array(len);
226
+ var thisp = arguments[2];
227
+ for (var i = 0; i < len; i++) {
228
+ if (i in arr)
229
+ res[i] = fun.call(thisp, arr[i], i, arr);
230
+ }
231
+
232
+ return res;
233
+ }
234
+
235
+ function filter(arr, fun /*, thisp*/){
236
+ var len = arr.length;
237
+ if (typeof fun != "function")
238
+ throw new TypeError();
239
+
240
+ var res = new Array();
241
+ var thisp = arguments[2];
242
+ for (var i = 0; i < len; i++) {
243
+ if (i in arr) {
244
+ var val = arr[i]; // in case fun mutates this
245
+ if (fun.call(thisp, val, i, arr))
246
+ res.push(val);
247
+ }
248
+ }
249
+
250
+ return res;
251
+ };
252
+
253
+ function slice(obj,start,end,step){
254
+ // handles slice operations: [3:6:2]
255
+ var len=obj.length,results = [];
256
+ end = end || len;
257
+ start = (start < 0) ? Math.max(0,start+len) : Math.min(len,start);
258
+ end = (end < 0) ? Math.max(0,end+len) : Math.min(len,end);
259
+ for(var i=start; i<end; i+=step){
260
+ results.push(obj[i]);
261
+ }
262
+ return results;
263
+ }
264
+ function expand(obj,name){
265
+ // handles ..name, .*, [*], [val1,val2], [val]
266
+ // name can be a property to search for, undefined for full recursive, or an array for picking by index
267
+ var results = [];
268
+ function walk(obj){
269
+ if(name){
270
+ if(name===true && !(obj instanceof Array)){
271
+ //recursive object search
272
+ results.push(obj);
273
+ }else if(obj[name]){
274
+ // found the name, add to our results
275
+ results.push(obj[name]);
276
+ }
277
+ }
278
+ for(var i in obj){
279
+ var val = obj[i];
280
+ if(!name){
281
+ // if we don't have a name we are just getting all the properties values (.* or [*])
282
+ results.push(val);
283
+ }else if(val && typeof val == 'object'){
284
+
285
+ walk(val);
286
+ }
287
+ }
288
+ }
289
+ if(name instanceof Array){
290
+ // this is called when multiple items are in the brackets: [3,4,5]
291
+ if(name.length==1){
292
+ // this can happen as a result of the parser becoming confused about commas
293
+ // in the brackets like [@.func(4,2)]. Fixing the parser would require recursive
294
+ // analsys, very expensive, but this fixes the problem nicely.
295
+ return obj[name[0]];
296
+ }
297
+ for(var i = 0; i < name.length; i++){
298
+ results.push(obj[name[i]]);
299
+ }
300
+ }else{
301
+ // otherwise we expanding
302
+ walk(obj);
303
+ }
304
+ return results;
305
+ }
306
+
307
+ function distinctFilter(array, callback){
308
+ // does the filter with removal of duplicates in O(n)
309
+ var outArr = [];
310
+ var primitives = {};
311
+ for(var i=0,l=array.length; i<l; ++i){
312
+ var value = array[i];
313
+ if(callback(value, i, array)){
314
+ if((typeof value == 'object') && value){
315
+ // with objects we prevent duplicates with a marker property
316
+ if(!value.__included){
317
+ value.__included = true;
318
+ outArr.push(value);
319
+ }
320
+ }else if(!primitives[value + typeof value]){
321
+ // with primitives we prevent duplicates by putting it in a map
322
+ primitives[value + typeof value] = true;
323
+ outArr.push(value);
324
+ }
325
+ }
326
+ }
327
+ for(i=0,l=outArr.length; i<l; ++i){
328
+ // cleanup the marker properties
329
+ if(outArr[i]){
330
+ delete outArr[i].__included;
331
+ }
332
+ }
333
+ return outArr;
334
+ }
335
+
336
+ var JSONQuery = function(/*String*/query,/*Object?*/obj){
337
+ // summary:
338
+ // Performs a JSONQuery on the provided object and returns the results.
339
+ // If no object is provided (just a query), it returns a "compiled" function that evaluates objects
340
+ // according to the provided query.
341
+ // query:
342
+ // Query string
343
+ // obj:
344
+ // Target of the JSONQuery
345
+ //
346
+ // description:
347
+ // JSONQuery provides a comprehensive set of data querying tools including filtering,
348
+ // recursive search, sorting, mapping, range selection, and powerful expressions with
349
+ // wildcard string comparisons and various operators. JSONQuery generally supersets
350
+ // JSONPath and provides syntax that matches and behaves like JavaScript where
351
+ // possible.
352
+ //
353
+ // JSONQuery evaluations begin with the provided object, which can referenced with
354
+ // $. From
355
+ // the starting object, various operators can be successively applied, each operating
356
+ // on the result of the last operation.
357
+ //
358
+ // Supported Operators:
359
+ // --------------------
360
+ // * .property - This will return the provided property of the object, behaving exactly
361
+ // like JavaScript.
362
+ // * [expression] - This returns the property name/index defined by the evaluation of
363
+ // the provided expression, behaving exactly like JavaScript.
364
+ // * [?expression] - This will perform a filter operation on an array, returning all the
365
+ // items in an array that match the provided expression. This operator does not
366
+ // need to be in brackets, you can simply use ?expression, but since it does not
367
+ // have any containment, no operators can be used afterwards when used
368
+ // without brackets.
369
+ // * [^?expression] - This will perform a distinct filter operation on an array. This behaves
370
+ // as [?expression] except that it will remove any duplicate values/objects from the
371
+ // result set.
372
+ // * [/expression], [\expression], [/expression, /expression] - This performs a sort
373
+ // operation on an array, with sort based on the provide expression. Multiple comma delimited sort
374
+ // expressions can be provided for multiple sort orders (first being highest priority). /
375
+ // indicates ascending order and \ indicates descending order
376
+ // * [=expression] - This performs a map operation on an array, creating a new array
377
+ // with each item being the evaluation of the expression for each item in the source array.
378
+ // * [start:end:step] - This performs an array slice/range operation, returning the elements
379
+ // from the optional start index to the optional end index, stepping by the optional step number.
380
+ // * [expr,expr] - This a union operator, returning an array of all the property/index values from
381
+ // the evaluation of the comma delimited expressions.
382
+ // * .* or [*] - This returns the values of all the properties of the current object.
383
+ // * $ - This is the root object, If a JSONQuery expression does not being with a $,
384
+ // it will be auto-inserted at the beginning.
385
+ // * @ - This is the current object in filter, sort, and map expressions. This is generally
386
+ // not necessary, names are auto-converted to property references of the current object
387
+ // in expressions.
388
+ // * ..property - Performs a recursive search for the given property name, returning
389
+ // an array of all values with such a property name in the current object and any subobjects
390
+ // * expr = expr - Performs a comparison (like JS's ==). When comparing to
391
+ // a string, the comparison string may contain wildcards * (matches any number of
392
+ // characters) and ? (matches any single character).
393
+ // * expr ~ expr - Performs a string comparison with case insensitivity.
394
+ // * ..[?expression] - This will perform a deep search filter operation on all the objects and
395
+ // subobjects of the current data. Rather than only searching an array, this will search
396
+ // property values, arrays, and their children.
397
+ // * $1,$2,$3, etc. - These are references to extra parameters passed to the query
398
+ // function or the evaluator function.
399
+ // * +, -, /, *, &, |, %, (, ), <, >, <=, >=, != - These operators behave just as they do
400
+ // in JavaScript.
401
+ //
402
+ //
403
+ //
404
+ // | dojox.json.query(queryString,object)
405
+ // and
406
+ // | dojox.json.query(queryString)(object)
407
+ // always return identical results. The first one immediately evaluates, the second one returns a
408
+ // function that then evaluates the object.
409
+ //
410
+ // example:
411
+ // | dojox.json.query("foo",{foo:"bar"})
412
+ // This will return "bar".
413
+ //
414
+ // example:
415
+ // | evaluator = dojox.json.query("?foo='bar'&rating>3");
416
+ // This creates a function that finds all the objects in an array with a property
417
+ // foo that is equals to "bar" and with a rating property with a value greater
418
+ // than 3.
419
+ // | evaluator([{foo:"bar",rating:4},{foo:"baz",rating:2}])
420
+ // This returns:
421
+ // | {foo:"bar",rating:4}
422
+ //
423
+ // example:
424
+ // | evaluator = dojox.json.query("$[?price<15.00][\rating][0:10]");
425
+ // This finds objects in array with a price less than 15.00 and sorts then
426
+ // by rating, highest rated first, and returns the first ten items in from this
427
+ // filtered and sorted list.
428
+ tokens = [];
429
+ var depth = 0;
430
+ var str = [];
431
+ query = query.replace(/"(\\.|[^"\\])*"|'(\\.|[^'\\])*'|[\[\]]/g,function(t){
432
+ depth += t == '[' ? 1 : t == ']' ? -1 : 0; // keep track of bracket depth
433
+ return (t == ']' && depth > 0) ? '`]' : // we mark all the inner brackets as skippable
434
+ (t.charAt(0) == '"' || t.charAt(0) == "'") ? "`" + (str.push(t) - 1) :// and replace all the strings
435
+ t;
436
+ });
437
+ var prefix = '';
438
+ function call(name){
439
+ // creates a function call and puts the expression so far in a parameter for a call
440
+ prefix = name + "(" + prefix;
441
+ }
442
+ function makeRegex(t,a,b,c,d){
443
+ // creates a regular expression matcher for when wildcards and ignore case is used
444
+ return str[d].match(/[\*\?]/) || c == '~' ?
445
+ "/^" + str[d].substring(1,str[d].length-1).replace(/\\([btnfr\\"'])|([^\w\*\?])/g,"\\$1$2").replace(/([\*\?])/g,".$1") + (c == '~' ? '$/i' : '$/') + ".test(" + a + ")" :
446
+ t;
447
+ }
448
+ query.replace(/(\]|\)|push|pop|shift|splice|sort|reverse)\s*\(/,function(){
449
+ throw new Error("Unsafe function call");
450
+ });
451
+
452
+ query = query.replace(/([^<>=]=)([^=])/g,"$1=$2"). // change the equals to comparisons
453
+ replace(/@|(\.\s*)?[a-zA-Z\$_]+(\s*:)?/g,function(t){
454
+ return t.charAt(0) == '.' ? t : // leave .prop alone
455
+ t == '@' ? "$obj" :// the reference to the current object
456
+ (t.match(/:|^(\$|Math|true|false|null)$/) ? "" : "$obj.") + t; // plain names should be properties of root... unless they are a label in object initializer
457
+ }).
458
+ replace(/\.?\.?\[(`\]|[^\]])*\]|\?.*|\.\.([\w\$_]+)|\.\*/g,function(t,a,b){
459
+ var oper = t.match(/^\.?\.?(\[\s*\^?\?|\^?\?|\[\s*==)(.*?)\]?$/); // [?expr] and ?expr and [=expr and =expr
460
+ if(oper){
461
+ var prefix = '';
462
+ if(t.match(/^\./)){
463
+ // recursive object search
464
+ call("expand");
465
+ prefix = ",true)";
466
+ }
467
+ call(oper[1].match(/\=/) ? "map" : oper[1].match(/\^/) ? "distinctFilter" : "filter");
468
+ return prefix + ",function($obj){return " + oper[2] + "})";
469
+ }
470
+ oper = t.match(/^\[\s*([\/\\].*)\]/); // [/sortexpr,\sortexpr]
471
+ if(oper){
472
+ // make a copy of the array and then sort it using the sorting expression
473
+ return ".concat().sort(function(a,b){" + oper[1].replace(/\s*,?\s*([\/\\])\s*([^,\\\/]+)/g,function(t,a,b){
474
+ return "var av= " + b.replace(/\$obj/,"a") + ",bv= " + b.replace(/\$obj/,"b") + // FIXME: Should check to make sure the $obj token isn't followed by characters
475
+ ";if(av>bv||bv==null){return " + (a== "/" ? 1 : -1) +";}\n" +
476
+ "if(bv>av||av==null){return " + (a== "/" ? -1 : 1) +";}\n";
477
+ }) + "})";
478
+ }
479
+ oper = t.match(/^\[(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)\]/); // slice [0:3]
480
+ if(oper){
481
+ call("slice");
482
+ return "," + (oper[1] || 0) + "," + (oper[2] || 0) + "," + (oper[3] || 1) + ")";
483
+ }
484
+ if(t.match(/^\.\.|\.\*|\[\s*\*\s*\]|,/)){ // ..prop and [*]
485
+ call("expand");
486
+ return (t.charAt(1) == '.' ?
487
+ ",'" + b + "'" : // ..prop
488
+ t.match(/,/) ?
489
+ "," + t : // [prop1,prop2]
490
+ "") + ")"; // [*]
491
+ }
492
+ return t;
493
+ }).
494
+ replace(/(\$obj\s*(\.\s*[\w_$]+\s*)*)(==|~)\s*`([0-9]+)/g,makeRegex). // create regex matching
495
+ replace(/`([0-9]+)\s*(==|~)\s*(\$obj(\s*\.\s*[\w_$]+)*)/g,function(t,a,b,c,d){ // and do it for reverse =
496
+ return makeRegex(t,c,d,b,a);
497
+ });
498
+ query = prefix + (query.charAt(0) == '$' ? "" : "$") + query.replace(/`([0-9]+|\])/g,function(t,a){
499
+ //restore the strings
500
+ return a == ']' ? ']' : str[a];
501
+ });
502
+ // create a function within this scope (so it can use expand and slice)
503
+
504
+ var executor = eval("1&&function($,$1,$2,$3,$4,$5,$6,$7,$8,$9){var $obj=$;return " + query + "}");
505
+ for(var i = 0;i<arguments.length-1;i++){
506
+ arguments[i] = arguments[i+1];
507
+ }
508
+ return obj ? executor.apply(this,arguments) : executor;
509
+ };
510
+
511
+ if(typeof namespace == "function"){
512
+ namespace("json::JSONQuery", JSONQuery);
513
+ }
514
+ else {
515
+ window["JSONQuery"] = JSONQuery;
516
+ }
517
+ })();
518
+
519
+
520
+ //------------------------------------------------------------------------------
521
+ //
522
+ // jquery.cloudkit.js source
523
+ //
524
+ // Copyright (c) 2008, 2009 Jon Crosby http://joncrosby.me
525
+ //
526
+ // For the complete source with the bundled dependencies,
527
+ // run 'rake dist' and use the contents of the dist directory.
528
+ //
529
+ //------------------------------------------------------------------------------
530
+
531
+ (function($) {
532
+
533
+ $.cloudkit = $.cloudkit || {};
534
+
535
+ //----------------------------------------------------------------------------
536
+ // Resource Model
537
+ //----------------------------------------------------------------------------
538
+ var buildResource = function(collection, spec, metadata) {
539
+ var that = {};
540
+ var meta = {};
541
+ var json = spec;
542
+
543
+ // return a key that is unique across all local items
544
+ var generateId = function() {
545
+ return (new Date).getTime() + '-' + Math.floor(Math.random()*10000);
546
+ };
547
+
548
+ var saveFromRemote = function() {
549
+ meta = metadata;
550
+ meta.id = generateId();
551
+ };
552
+
553
+ that.save = function(callbacks) {
554
+ if (!(typeof metadata === 'undefined')) {
555
+ return saveFromRemote();
556
+ }
557
+ $.ajax({
558
+ type: 'POST',
559
+ url: collection,
560
+ data: JSON.stringify(spec),
561
+ contentType: 'application/json',
562
+ dataType: 'json',
563
+ processData: false,
564
+ complete: function(response, statusText) {
565
+ if (response.status == 201) {
566
+ meta = JSON.parse(response.responseText);
567
+ meta.id = generateId();
568
+ callbacks.success();
569
+ } else {
570
+ callbacks.error(response.status);
571
+ }
572
+ }
573
+ });
574
+ };
575
+
576
+ that.update = function(spec, callbacks) {
577
+ var id = meta.id;
578
+ $.ajax({
579
+ type: 'PUT',
580
+ url: meta.uri,
581
+ data: JSON.stringify($.extend(json,spec)),
582
+ contentType: 'application/json',
583
+ dataType: 'json',
584
+ beforeSend: function(xhr) {
585
+ xhr.setRequestHeader('If-Match', meta.etag);
586
+ },
587
+ processData: false,
588
+ complete: function(response, statusText) {
589
+ if (response.status == 200) {
590
+ meta = JSON.parse(response.responseText);
591
+ meta.id = id;
592
+ json = $.extend(json,spec);
593
+ callbacks.success();
594
+ } else {
595
+ // TODO implement default 412 strategy as progressive diff/merge
596
+ callbacks.error(response.status);
597
+ }
598
+ }
599
+ });
600
+ };
601
+
602
+ that.destroy = function(callbacks) {
603
+ var id = meta.id
604
+ $.ajax({
605
+ type: 'DELETE',
606
+ url: meta.uri,
607
+ dataType: 'json',
608
+ beforeSend: function(xhr) {
609
+ xhr.setRequestHeader('If-Match', meta.etag);
610
+ },
611
+ processData: false,
612
+ complete: function(response, statusText) {
613
+ meta = JSON.parse(response.responseText);
614
+ meta.id = id;
615
+ if (response.status == 200) {
616
+ meta.deleted = true;
617
+ callbacks.success();
618
+ } else {
619
+ callbacks.error(response.status);
620
+ }
621
+ }
622
+ });
623
+ };
624
+
625
+ that.json = function() {
626
+ return json;
627
+ };
628
+
629
+ that.id = function() {
630
+ return meta.id;
631
+ };
632
+
633
+ that.uri = function() {
634
+ return meta.uri;
635
+ };
636
+
637
+ that.isDeleted = function() {
638
+ return (meta.deleted == true);
639
+ };
640
+
641
+ that.attr = function(name, value) {
642
+ if (typeof json[name] != 'undefined') {
643
+ switch(typeof value) {
644
+ case 'undefined':
645
+ return json[name]; break;
646
+ case 'function':
647
+ return json[name] = value.apply(json[name]); break;
648
+ default:
649
+ return json[name] = value;
650
+ }
651
+ } else if (typeof meta[name] != 'undefined') {
652
+ return meta[name];
653
+ }
654
+ };
655
+
656
+ return that;
657
+ };
658
+
659
+ //----------------------------------------------------------------------------
660
+ // Internal Data Store
661
+ //----------------------------------------------------------------------------
662
+ var buildStore = function(collection) {
663
+ var that = {};
664
+
665
+ var key = function(resource) {
666
+ return collection+resource.id();
667
+ };
668
+
669
+ var persist = function(resource) {
670
+ var k = key(resource);
671
+ $.data(window, k, resource);
672
+ var index = $.data(window, collection+'index') || [];
673
+ index.push(k);
674
+ $.data(window, collection+'index', index);
675
+ };
676
+
677
+ that.create = function(spec, callbacks) {
678
+ resource = buildResource(collection, spec);
679
+ resource.save({
680
+ success: function() {
681
+ persist(resource);
682
+ callbacks.success(resource);
683
+ },
684
+ error: function(status) {
685
+ callbacks.error(status);
686
+ }
687
+ });
688
+ };
689
+
690
+ that.createFromRemote = function(spec, metadata) {
691
+ resource = buildResource(collection, spec, metadata);
692
+ resource.save();
693
+ persist(resource);
694
+ return resource;
695
+ };
696
+
697
+ that.all = function(spec) {
698
+ // TODO - don't ignore spec
699
+ var result = [];
700
+ var index = $.data(window, collection+'index');
701
+ $(index).each(function(count, id) {
702
+ var item = $.data(window, id);
703
+ if (!item.isDeleted()) {
704
+ result.push(item);
705
+ }
706
+ });
707
+ return result;
708
+ };
709
+
710
+ that.get = function(id) {
711
+ return $.data(window, collection+id);
712
+ };
713
+
714
+ that.query = function(spec) {
715
+ var jsonObjects = [];
716
+ var self = this;
717
+ $(this.all()).each(function(index, item) {
718
+ json = $.extend(item.json(), {'___id___':item.id()});
719
+ jsonObjects.push(json);
720
+ });
721
+ var query_result = JSONQuery(spec, jsonObjects);
722
+ var resources = []
723
+ $(query_result).each(function(index, item) {
724
+ resources.push(self.get(item['___id___']));
725
+ });
726
+ return resources;
727
+ }
728
+
729
+ return that;
730
+ };
731
+
732
+ //----------------------------------------------------------------------------
733
+ // Private API
734
+ //----------------------------------------------------------------------------
735
+
736
+ var collectionURIs = []; // collection URIs found during boot via discovery
737
+ var collections = {}; // local stores, one per remote resource collection
738
+
739
+ // load remote collection URIs
740
+ var loadMeta = function(callbacks) {
741
+ $.ajax({
742
+ type: 'GET',
743
+ url: '/cloudkit-meta',
744
+ complete: function(response, statusText) {
745
+ data = JSON.parse(response.responseText);
746
+ if (response.status == 200) {
747
+ collectionURIs = data.uris;
748
+ callbacks.success();
749
+ } else if (response.status >= 400) {
750
+ callbacks.error(response.status);
751
+ } else {
752
+ callbacks.error('unexpected error');
753
+ }
754
+ }
755
+ });
756
+ };
757
+
758
+ // configure a local collection
759
+ var configureCollection = function(collection) {
760
+ $.data(window, collection+'index', []);
761
+ var name = collection.replace(/^\//, '');
762
+ collections[name] = buildStore(collection);
763
+ };
764
+
765
+ // load remote data into local store
766
+ var populateCollectionsFromRemote = function(index, callbacks) {
767
+ if (index == collectionURIs.length) {
768
+ callbacks.success();
769
+ return;
770
+ }
771
+ $.ajax({
772
+ type: 'GET',
773
+ url: collectionURIs[index]+"/_resolved",
774
+ dataType: 'json',
775
+ processData: false,
776
+ complete: function(response, statusText) {
777
+ if (response.status == 200) {
778
+ var resources = JSON.parse(response.responseText).documents;
779
+ var name = collectionURIs[index].replace(/^\//, '');
780
+ for (var i = 0; i < resources.length; i++) {
781
+ var resource = resources[i];
782
+ collections[name].createFromRemote(
783
+ JSON.parse(resource.document),
784
+ {
785
+ uri: resource.uri,
786
+ etag: resource.etag,
787
+ last_modified: resource.last_modified
788
+ }
789
+ );
790
+ }
791
+ populateCollectionsFromRemote(index+1, callbacks);
792
+ } else {
793
+ callbacks.error(response.status);
794
+ }
795
+ }
796
+ });
797
+ };
798
+
799
+ // extend jquery
800
+ $.fn.extend($.cloudkit, {
801
+
802
+ //--------------------------------------------------------------------------
803
+ // Public API
804
+ //--------------------------------------------------------------------------
805
+
806
+ // setup the local store
807
+ boot: function(callbacks) {
808
+ collectionURIs = [];
809
+ collections = [];
810
+ loadMeta({
811
+ success: function() {
812
+ $(collectionURIs).each(function(index, collection) {
813
+ configureCollection(collection);
814
+ });
815
+ populateCollectionsFromRemote(0, {
816
+ success: function() {
817
+ callbacks.success();
818
+ },
819
+ error: function(status) {
820
+ callbacks.error(status);
821
+ }
822
+ });
823
+ },
824
+ error: function(status) {
825
+ callbacks.error(status);
826
+ }
827
+ });
828
+ },
829
+
830
+ // return all collections
831
+ collections: function() {
832
+ return collections;
833
+ },
834
+
835
+ // return a specific collection
836
+ collection: function(name) {
837
+ return this.collections()[name];
838
+ }
839
+ });
840
+ })(jQuery);