eyeballs 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -26,7 +26,10 @@ o_O.routes = {
26
26
  {
27
27
  parsed_route = prefix + '/' + parsed_route
28
28
  }
29
- o_O.routes.rules[parsed_route] = {"action": o_O.routes.figure_action(options), "with": options.with};
29
+ o_O.routes.rules[parsed_route] = {
30
+ action: o_O.routes.figure_action(options),
31
+ with_args: options.with_args
32
+ };
30
33
  o_O.routes.urls.push(parsed_route);
31
34
  }
32
35
  }
@@ -48,7 +51,7 @@ o_O.routes = {
48
51
  var hash = location.hash.replace(/^(#)/, '').o_O_trim('/');
49
52
  if(o_O.routes.urls.indexOf(hash) >= 0)
50
53
  {
51
- o_O.routes.rules[hash].action(o_O.routes.rules[hash].with);
54
+ o_O.routes.rules[hash].action(o_O.routes.rules[hash].with_args);
52
55
  }
53
56
  });
54
57
 
@@ -61,10 +61,28 @@ o_O.get_template = function(template, data, callback){
61
61
  }
62
62
  }
63
63
 
64
- if(typeof String.prototype.capitalize == 'undefined')
64
+ if(typeof String.prototype.capitalize === 'undefined')
65
65
  {
66
66
  String.prototype.capitalize = function(){
67
- return this.charAt(0).toUpperCase() + this.slice(1);
67
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
68
+ }
69
+ }
70
+
71
+ if(typeof String.prototype.underscore === 'undefined')
72
+ {
73
+ String.prototype.underscore = function(){
74
+ return this.replace(/::/g, '/')
75
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
76
+ .replace(/([a-z\d])([A-Z])/g, '$1_$2')
77
+ .replace(/-/g, '_')
78
+ .toLowerCase();
79
+ }
80
+ }
81
+
82
+ if(typeof String.prototype.blank === 'undefined')
83
+ {
84
+ String.prototype.blank = function(){
85
+ return /^\s*$/.test(this);
68
86
  }
69
87
  }
70
88
 
@@ -4,7 +4,8 @@ o_O.model = {
4
4
  var callback = callback;
5
5
  var class_methods, instance_methods, initializer_methods;
6
6
  var validates_presence_of, validates_length_of;
7
- var table_name = model_name.toLowerCase() + 's';
7
+ var table_name = model_name.underscore() + 's';
8
+ var initial_class_methods = [];
8
9
 
9
10
  class_methods = {
10
11
  validations: {presence: [], lengthliness: [], custom: []},
@@ -20,16 +21,21 @@ o_O.model = {
20
21
  this.validations.custom.push(validation)
21
22
  }
22
23
  }
24
+ for(var method in class_methods)
25
+ {
26
+ initial_class_methods.push(method);
27
+ }
28
+
23
29
 
24
- var run_callback = function(callback, method, args){
30
+ var run_callback = function(callback, method, object, response){
25
31
  try{
26
32
  if(typeof callback === 'function' && method === 'success')
27
33
  {
28
- callback(args);
34
+ callback(object, response);
29
35
  }
30
36
  if(typeof callback === 'object' && typeof callback[method] === 'function')
31
37
  {
32
- callback[method](args);
38
+ callback[method](object, response);
33
39
  }
34
40
  }
35
41
  catch(e)
@@ -52,9 +58,9 @@ o_O.model = {
52
58
  run_callback(callback, 'loading', this)
53
59
  if(this.adapter)
54
60
  {
55
- this.adapter.destroy(this, function(returned_object){
56
- run_callback(callback, 'success', returned_object);
57
- });
61
+ this.adapter.destroy(this, function(returned_object, response){
62
+ run_callback(callback, 'success', returned_object, response);
63
+ }, callback);
58
64
  }
59
65
  else
60
66
  {
@@ -70,11 +76,11 @@ o_O.model = {
70
76
  if(this.adapter)
71
77
  {
72
78
  var model = this;
73
- this.adapter.save(this, function(returned_object){
79
+ this.adapter.save(this, function(returned_object, response){
74
80
  var initialized_object = o_O.models[model.model_name].initialize(returned_object);
75
81
  initialized_object.new_record = false;
76
- run_callback(callback, 'success', initialized_object);
77
- });
82
+ run_callback(callback, 'success', initialized_object, response);
83
+ }, callback);
78
84
  }
79
85
  else
80
86
  {
@@ -107,7 +113,7 @@ o_O.model = {
107
113
  this.save(callback);
108
114
  },
109
115
  valid: function(){
110
- this.errors = [];
116
+ this.errors.length = 0;
111
117
 
112
118
  o_O.validations.run(this);
113
119
 
@@ -123,6 +129,16 @@ o_O.model = {
123
129
  errors: [],
124
130
  validations: class_methods.validations
125
131
  }
132
+ instance_methods.errors.on = function(field){
133
+ for(var i=0; i<instance_methods.errors.length; i++)
134
+ {
135
+ if(field === instance_methods.errors[i].field)
136
+ {
137
+ return instance_methods.errors[i];
138
+ }
139
+ }
140
+ }
141
+
126
142
  for(method in class_methods.methods)
127
143
  {
128
144
  instance_methods[method] = class_methods.methods[method]
@@ -157,6 +173,9 @@ o_O.model = {
157
173
  }
158
174
  return attributes;
159
175
  },
176
+ create: function(attributes, callbacks){
177
+ this.initialize(attributes).save(callbacks);
178
+ },
160
179
  find: function(id, callback){
161
180
  if(this.adapter)
162
181
  {
@@ -182,6 +201,14 @@ o_O.model = {
182
201
  model_name: model_name,
183
202
  table_name: table_name
184
203
  }
204
+
205
+ for(var method in class_methods)
206
+ {
207
+ if(initial_class_methods.indexOf(method) === -1)
208
+ {
209
+ initializer_methods[method] = class_methods[method];
210
+ }
211
+ }
185
212
 
186
213
  return initializer_methods;
187
214
  }
@@ -20,12 +20,12 @@ o_O.validations = {
20
20
  {
21
21
  if(max && object[field].length > max)
22
22
  {
23
- var message = field + ' should be less than ' + max + ' characters';
23
+ var message = field.capitalize() + ' should be less than ' + max + ' characters';
24
24
  object.errors.push({field: field, type: 'length', message: message});
25
25
  }
26
26
  if(min && object[field].length < min)
27
27
  {
28
- var message = field + ' should be greater than ' + min + ' characters';
28
+ var message = field.capitalize() + ' should be greater than ' + min + ' characters';
29
29
  object.errors.push({field: field, type: 'length', message: message});
30
30
  }
31
31
  }
@@ -35,9 +35,9 @@ o_O.validations = {
35
35
  for(var i = 0; i < object.validations.presence.length; i++)
36
36
  {
37
37
  var field = object.validations.presence[i].field;
38
- if(object[field] == '' || object[field] == null)
38
+ if(object[field] == null || (typeof object[field] === 'string' && object[field].blank()) )
39
39
  {
40
- var message = field + ' should be present';
40
+ var message = field.capitalize() + ' should be present';
41
41
  object.errors.push({field: field, type: 'presence', message: message})
42
42
  }
43
43
  }
@@ -1,3 +1,4 @@
1
1
  // This is a very handy place to put any initial logic
2
2
  // For example, choosing your adapter
3
- // o_O.model.adapter = o_O.rest;
3
+ o_O.model.adapter = o_O.rest;
4
+ o_O.config.authenticity_token = $('meta[name=csrf-token]').attr('content')
@@ -3,7 +3,6 @@
3
3
  <html>
4
4
  <head>
5
5
  <script src="../../dist/jquery/jquery-1.4.2.min.js"></script>
6
- <script src="../../dist/jquery/jquery.livequery.js"></script>
7
6
  <script src="../../src/o_O.js"></script>
8
7
  <script src="../../src/drivers/jquery/modules/o_O.controller.js"></script>
9
8
  <link rel="stylesheet" href="qunit.css" type="text/css" media="screen" />
@@ -46,50 +45,39 @@
46
45
  $('form#myForm').trigger('submit');
47
46
  equals($('form#myForm').html(), 'created!', 'html should have been updated');
48
47
  });
49
-
50
- test('submitting an auto-bound form with data-bind shortcut', 1, function(){
51
- $('form#myShortcutForm').trigger('submit');
52
- equals($('form#myShortcutForm').html(), 'created!', 'html should have been updated');
53
- });
54
-
48
+
55
49
  test('clicking an auto-bound link', 1, function(){
56
50
  $('a#myLink').trigger('click');
57
51
  equals($('a#myLink').html(), 'shown!', 'html should have been updated');
58
52
  });
59
-
60
- test('clicking an auto-bound link with data-bind shortcut', 1, function(){
61
- $('a#myShortcutLink').trigger('click');
62
- equals($('a#myShortcutLink').html(), 'shown!', 'html should have been updated');
63
- });
64
-
53
+
54
+
65
55
  test('hovering a custom bound element', 1, function(){
66
56
  $('div#myDiv').trigger('mouseover');
67
57
  equals($('div#myDiv').html(), 'other!', 'html should have been updated');
68
58
  });
69
59
 
70
- new_element = $('<a id="anotherLink" data-controller="reviews" data-action="another">another link</a>')
60
+ new_element = $('<a id="anotherLink" data-bind="reviews#another">another link</a>')
71
61
  $('div#anotherDiv').append(new_element);
72
62
 
73
63
 
74
- setTimeout(function(){
75
- test("a new link", 1, function(){
64
+ asyncTest('new link', function(){
65
+ setTimeout(function(){
76
66
  $('a#anotherLink').trigger('click');
77
67
  equals($('a#anotherLink').html(), 'another!', 'it should have some dynamic behavior');
78
- })
79
- }, 100);
80
-
81
- test('should allow default behavior', 1, function(){
82
- $('input#myCheckbox').trigger('click');
83
- equals($('input#myCheckbox:checked').length, 1, "check box should be checked")
68
+ start();
69
+ }, 100);
84
70
  })
85
-
86
- test('should allow default behavior with data-bind', 2, function(){
87
- $('input#myOtherCheckbox').trigger('click');
88
- equals($('input#myOtherCheckbox:checked').val(), 'bacon', "check box value should change")
89
- equals($('input#myOtherCheckbox:checked').length, 1, "check box should be checked")
71
+
72
+ asyncTest('should allow default behavior', 1, function(){
73
+ setTimeout(function(){
74
+ $('input#myCheckbox').trigger('click');
75
+ equals($('input#myCheckbox:checked').length, 1, "check box should be checked")
76
+ start();
77
+ }, 100)
90
78
  })
91
79
 
92
- test('should bind on shortcuts',2, function(){
80
+ test('should bind multiple events',2, function(){
93
81
  $('div#shortcut').trigger('click');
94
82
  equals($("div#shortcut").html(), 'shortcut!', 'it should be bound with the data-bind keyword')
95
83
  $('div#shortcut').trigger('mouseover');
@@ -107,15 +95,11 @@
107
95
  <ol id="qunit-tests"></ol>
108
96
 
109
97
  <div data-id="paul">
110
- <form id="myForm" data-controller="reviews" data-action="create">
111
- test form
112
- </form>
113
-
114
- <form id="myShortcutForm" data-bind="reviews#create">
98
+ <form id="myForm" data-bind="reviews#create">
115
99
  test form
116
100
  </form>
117
-
118
- <a id="myLink" data-controller="reviews" data-action="show">
101
+
102
+ <a id="myLink" data-bind="reviews#show">
119
103
  test link
120
104
  </a>
121
105
 
@@ -123,15 +107,14 @@
123
107
  test link
124
108
  </a>
125
109
 
126
- <div id="myDiv" data-controller="reviews" data-action="other" data-event="mouseenter">stuff</div>
127
- <div id="shortcut" data-bind="click:reviews#shortcut; mouseenter:reviews#multi">
110
+ <div id="myDiv" data-bind="mouseover:reviews#other">stuff</div>
111
+ <div id="shortcut" data-bind="click:reviews#shortcut; mouseover:reviews#multi">
128
112
  I'm a shortcut
129
113
  </div>
130
114
  <div id="anotherDiv">
131
115
 
132
116
  </div>
133
- <input id="myCheckbox" data-controller="reviews" data-action="check" data-default="true" type="checkbox">
134
- <input id="myOtherCheckbox" data-bind="+click:reviews#check" type="checkbox" value="toast">
117
+ <input id="myCheckbox" data-bind="+click:reviews#check" data-default="true" type="checkbox">
135
118
  </div>
136
119
  </body>
137
120
  </html>
@@ -88,10 +88,12 @@
88
88
  equals(myReview.content, 'Dublin', 'should update the content');
89
89
  })
90
90
 
91
- test('updating a review invalid', 1, function(){
91
+ test('updating a review invalid', function(){
92
92
  myReview = Review.find('paul');
93
93
  myReview.update_attributes({title: '', content: ''});
94
94
  equals(myReview.errors.length, 2, "should have 2 errors");
95
+ equals(myReview.errors.on('title').field, 'title')
96
+ equals(myReview.errors.on('title').type, 'presence')
95
97
  })
96
98
 
97
99
  test('defining methods should work',1, function(){
@@ -105,11 +107,11 @@
105
107
  equals(myReview.errors.length, 1, "should have an error");
106
108
  equals(myReview.errors[0].field, 'title', 'should be on title');
107
109
  equals(myReview.errors[0].type, 'length', 'should be length')
108
- equals(myReview.errors[0].message, 'title should be less than 15 characters', 'should have an error message')
110
+ equals(myReview.errors[0].message, 'Title should be less than 15 characters', 'should have an error message')
109
111
  myReview.title = 'the'
110
112
  myReview.save();
111
113
  equals(myReview.errors.length, 1, "should still only have one error");
112
- equals(myReview.errors[0].message, 'title should be greater than 5 characters', 'should have an error message')
114
+ equals(myReview.errors[0].message, 'Title should be greater than 5 characters', 'should have an error message')
113
115
  });
114
116
 
115
117
  test('custom validation', 3, function(){
@@ -65,6 +65,18 @@
65
65
  });
66
66
  });
67
67
 
68
+ asyncTest('create', 2, function(){
69
+ Review.create({title: "Biscuit", content: "Some Content"},{
70
+ loading: function(){
71
+ ok('should call the loading callback before success')
72
+ },
73
+ success: function(){
74
+ ok('should call the success callback')
75
+ start();
76
+ }
77
+ });
78
+ });
79
+
68
80
  asyncTest('an invalid review',1, function(){
69
81
  var myReview = Review.initialize();
70
82
  myReview.save({
@@ -21,9 +21,12 @@
21
21
 
22
22
  o_O.model.adapter = o_O.rest;
23
23
 
24
- module("Rails Test (with jQuery)");
24
+ module("REST Interface Test (with jQuery)");
25
25
 
26
- o_O('Review', function(){})
26
+ o_O('Review', function(){});
27
+ o_O('FakeReview', function(fake_review){
28
+ fake_review.url = '/reviews'
29
+ });
27
30
 
28
31
  asyncTest('storing a basic basic thing', 3, function(){
29
32
  var review = Review.initialize({title: 'Magic!'})
@@ -35,6 +38,96 @@
35
38
  });
36
39
  });
37
40
 
41
+ asyncTest('storing a basic basic thing with custom URL set in the model', function(){
42
+ FakeReview.create({title: 'Magic!'}, function(saved_review){
43
+ equals(FakeReview.url, '/reviews', 'Should be set');
44
+ equals(saved_review.title, 'Magic!', 'title should match');
45
+ equals(saved_review.id, '1', "Rails should give back the ID");
46
+ start();
47
+ });
48
+ });
49
+
50
+ asyncTest('storing a basic basic thing with a custom URL set in the save', function(){
51
+ var review = Review.initialize({title: 'Magic!'})
52
+ review.save({
53
+ url: "/alternate_reviews",
54
+ success: function(saved_review){
55
+ equals(saved_review.title, review.title, 'title should match');
56
+ equals(saved_review.id, '2', "Rails should give back the ID");
57
+ start();
58
+ }
59
+ });
60
+ });
61
+
62
+ o_O('RailsReview', function(review){
63
+ review.include_json_root = true;
64
+ });
65
+ o_O('AlternateRailsReview', function(review){
66
+ review.include_json_root = true;
67
+ review.json_root_name = 'alternate_object'
68
+ });
69
+
70
+ asyncTest('storing a basic basic thing with include_root', function(){
71
+ RailsReview.create({title: "Awesome"},function(review, response){
72
+ equals(response, 'ok', 'should send things with a root');
73
+ start();
74
+ });
75
+ });
76
+
77
+ asyncTest('specifying a custom json_root_name', function(){
78
+ AlternateRailsReview.create({title: "Awesome"},function(review, response){
79
+ equals(response, 'ok', 'should send things to /alternate_rails_reviews');
80
+ start();
81
+ });
82
+ });
83
+
84
+ o_O.config.authenticity_token = 'a12345';
85
+ asyncTest('CSRF token', function(){
86
+ RailsReview.create({title: "Unnecessary"}, {
87
+ url: '/auth_token',
88
+ success: function(review, response){
89
+ equals(response, 'a12345', 'authenticity token should match')
90
+ start();
91
+ }
92
+ })
93
+ })
94
+
95
+ asyncTest('accepting a string back', function(){
96
+ var review = Review.initialize({title: 'Magic!'})
97
+ review.save({
98
+ url: "/string_back",
99
+ success: function(saved_review, response){
100
+ equals(response, 'some string', 'we should be able to accept a string back');
101
+ start();
102
+ }
103
+ });
104
+ });
105
+
106
+ asyncTest("ID shouldn't be sent", function(){
107
+ var string_back_id;
108
+ AlternateRailsReview.create({},{
109
+ url: "/test_id",
110
+ success: function(saved_review, response){
111
+ equals('ok', response, 'it should not sent an ID');
112
+ start();
113
+ }
114
+ });
115
+ });
116
+
117
+ asyncTest('consistent ID on string back', function(){
118
+ var string_back_id;
119
+ Review.create({},{
120
+ url: "/string_back",
121
+ loading: function(saved_review){
122
+ string_back_id = saved_review.id;
123
+ },
124
+ success: function(saved_review, response){
125
+ equals(saved_review.id, string_back_id, 'ids should be the same');
126
+ start();
127
+ }
128
+ });
129
+ });
130
+
38
131
  asyncTest('pulling something in', 2, function(){
39
132
  var review = Review.initialize({title: 'More Magic!'});
40
133
  review.save(function(){
@@ -77,28 +170,51 @@
77
170
  var review = Review.initialize({title: 'Doomed!'});
78
171
  review.save(function(saved_review){
79
172
  saved_review.update_attributes({title: 'Tennessee'}, function(saved_review){
80
- equals(saved_review.title, 'Tennessee', 'Title should have been updated');
173
+ equals(saved_review.title, 'Updated', 'Title should have been updated');
174
+ start();
175
+ });
176
+ });
177
+ });
178
+
179
+ asyncTest('updating with custom URL', 1, function(){
180
+ var review = FakeReview.initialize({title: 'Doomed!'});
181
+ review.save(function(saved_review){
182
+ saved_review.update_attributes({title: 'Tennessee'}, function(saved_review){
183
+ equals(saved_review.title, 'Updated', 'Title should have been updated');
81
184
  start();
82
185
  });
83
186
  });
84
187
  });
85
188
 
86
- asyncTest('deleting', 1, function(){
189
+ asyncTest('deleting', 2, function(){
87
190
  var review = Review.initialize({title: 'Doomed!'});
88
191
  review.save(function(saved_review){
89
- saved_review.destroy(function(destroyed_review){
192
+ saved_review.destroy(function(destroyed_review, response){
90
193
  equals(destroyed_review.destroyed, true, 'It should have deleted the doc')
194
+ equals(response, 'Deleted OK', 'It should have deleted the doc')
91
195
  start()
92
196
  });
93
197
  });
94
198
  })
199
+
200
+ asyncTest('deleting with custom URL', 2, function(){
201
+ var review = FakeReview.initialize({title: 'Doomed!'});
202
+ review.save(function(saved_review){
203
+ saved_review.destroy(function(destroyed_review, response){
204
+ equals(destroyed_review.destroyed, true, 'It should have deleted the doc')
205
+ equals(response, 'Deleted OK', 'It should have deleted the doc')
206
+ start()
207
+ });
208
+ });
209
+ })
210
+
95
211
 
96
212
  });
97
213
  </script>
98
214
 
99
215
  </head>
100
216
  <body>
101
- <h1 id="qunit-header">Rails Tests (with jQuery)</h1>
217
+ <h1 id="qunit-header">REST Interface Tests (with jQuery)</h1>
102
218
  <h2 id="qunit-banner"></h2>
103
219
  <h2 id="qunit-userAgent"></h2>
104
220
  <ol id="qunit-tests"></ol>