my-simon 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/.document +5 -0
  2. data/.gitignore.orig +55 -0
  3. data/DOC/Launch Check List.docx +0 -0
  4. data/DOC/Launch Check List.pdf +0 -0
  5. data/Gemfile +14 -0
  6. data/Gemfile.lock +44 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.md +21 -0
  9. data/README.rdoc +19 -0
  10. data/Rakefile +46 -0
  11. data/VERSION +1 -0
  12. data/build/rakefile.rb +12 -0
  13. data/config.rb +24 -0
  14. data/lib/simon.rb +0 -5
  15. data/my-simon.gemspec +141 -0
  16. data/scaffolding/simon/controller.tpl +9 -0
  17. data/scaffolding/simon/model.tpl +10 -0
  18. data/scaffolding/simon/view.tpl +5 -0
  19. data/scaffolding/standards/html_template.html +44 -0
  20. data/scaffolding/standards/html_template.php +44 -0
  21. data/scaffolding/standards/jquery_plugin_template.js +45 -0
  22. data/scaffolding/standards/js_template.js +43 -0
  23. data/simon +282 -0
  24. data/test/helper.rb +18 -0
  25. data/test/test_simon.rb +7 -0
  26. data/www/.htaccess +488 -0
  27. data/www/404.html +32 -0
  28. data/www/crossdomain.xml +25 -0
  29. data/www/favicon.ico +0 -0
  30. data/www/index.php +17 -0
  31. data/www/lib/js/homePage.js +43 -0
  32. data/www/lib/js/jquery/JQbook.js +809 -0
  33. data/www/lib/js/jquery/jquery-1.8.0.min.js +27 -0
  34. data/www/lib/js/jquery/jquery.alphanumeric.js +82 -0
  35. data/www/lib/js/jquery/jquery.cookie.js +96 -0
  36. data/www/lib/js/jquery/jquery.easing.1.3.js +207 -0
  37. data/www/lib/js/main.js +117 -0
  38. data/www/lib/js/master.js +6 -0
  39. data/www/lib/js/plugins/handlebars-1.0.rc.1.js +1920 -0
  40. data/www/lib/js/plugins/modernizr-1.7.min.js +2 -0
  41. data/www/lib/js/plugins/swfobject.js +777 -0
  42. data/www/lib/js/plugins_mod/README.txt +3 -0
  43. data/www/lib/php/app.php +53 -0
  44. data/www/lib/php/controller/index.php +10 -0
  45. data/www/lib/php/controller/regex.php +23 -0
  46. data/www/lib/php/model/Book.php +8 -0
  47. data/www/lib/php/model/Model.php +8 -0
  48. data/www/lib/php/plugins/php-activerecord/ActiveRecord.php +44 -0
  49. data/www/lib/php/plugins/php-activerecord/lib/CallBack.php +226 -0
  50. data/www/lib/php/plugins/php-activerecord/lib/Column.php +155 -0
  51. data/www/lib/php/plugins/php-activerecord/lib/Config.php +288 -0
  52. data/www/lib/php/plugins/php-activerecord/lib/Connection.php +456 -0
  53. data/www/lib/php/plugins/php-activerecord/lib/ConnectionManager.php +38 -0
  54. data/www/lib/php/plugins/php-activerecord/lib/DateTime.php +45 -0
  55. data/www/lib/php/plugins/php-activerecord/lib/Exceptions.php +137 -0
  56. data/www/lib/php/plugins/php-activerecord/lib/Expressions.php +183 -0
  57. data/www/lib/php/plugins/php-activerecord/lib/Inflector.php +115 -0
  58. data/www/lib/php/plugins/php-activerecord/lib/Model.php +1673 -0
  59. data/www/lib/php/plugins/php-activerecord/lib/Reflections.php +86 -0
  60. data/www/lib/php/plugins/php-activerecord/lib/Relationship.php +637 -0
  61. data/www/lib/php/plugins/php-activerecord/lib/SQLBuilder.php +396 -0
  62. data/www/lib/php/plugins/php-activerecord/lib/Serialization.php +302 -0
  63. data/www/lib/php/plugins/php-activerecord/lib/Singleton.php +57 -0
  64. data/www/lib/php/plugins/php-activerecord/lib/Table.php +547 -0
  65. data/www/lib/php/plugins/php-activerecord/lib/Utils.php +351 -0
  66. data/www/lib/php/plugins/php-activerecord/lib/Validations.php +833 -0
  67. data/www/lib/php/plugins/php-activerecord/lib/adapters/MysqlAdapter.php +73 -0
  68. data/www/lib/php/plugins/php-activerecord/lib/adapters/OciAdapter.php +121 -0
  69. data/www/lib/php/plugins/php-activerecord/lib/adapters/PgsqlAdapter.php +104 -0
  70. data/www/lib/php/plugins/php-activerecord/lib/adapters/SqliteAdapter.php +81 -0
  71. data/www/lib/php/system/Config.php +174 -0
  72. data/www/lib/php/system/config.routes.php +29 -0
  73. data/www/lib/php/system/router.php +220 -0
  74. data/www/lib/php/template/footer.php +59 -0
  75. data/www/lib/php/template/header.php +74 -0
  76. data/www/lib/php/view/index.php +5 -0
  77. data/www/media/images/facebook_share.jpg +0 -0
  78. data/www/robots.txt +5 -0
  79. data/www/sandbox/readme.txt +3 -0
  80. data/www/sass/javascript.scss +1 -0
  81. data/www/sass/layout.scss +128 -0
  82. data/www/sass/master.scss +4 -0
  83. data/www/sass/reset.scss +47 -0
  84. data/www/sass/typography.scss +24 -0
  85. data/www/styles/javascript.css +1 -0
  86. data/www/styles/layout.css +186 -0
  87. data/www/styles/master.css +4 -0
  88. data/www/styles/reset.css +60 -0
  89. data/www/styles/typography.css +24 -0
  90. metadata +179 -7
@@ -0,0 +1,833 @@
1
+ <?php
2
+ /**
3
+ * These two classes have been <i>heavily borrowed</i> from Ruby on Rails' ActiveRecord so much that
4
+ * this piece can be considered a straight port. The reason for this is that the vaildation process is
5
+ * tricky due to order of operations/events. The former combined with PHP's odd typecasting means
6
+ * that it was easier to formulate this piece base on the rails code.
7
+ *
8
+ * @package ActiveRecord
9
+ */
10
+
11
+ namespace ActiveRecord;
12
+ use ActiveRecord\Model;
13
+ use IteratorAggregate;
14
+ use ArrayIterator;
15
+
16
+ /**
17
+ * Manages validations for a {@link Model}.
18
+ *
19
+ * This class isn't meant to be directly used. Instead you define
20
+ * validators thru static variables in your {@link Model}. Example:
21
+ *
22
+ * <code>
23
+ * class Person extends ActiveRecord\Model {
24
+ * static $validates_length_of = array(
25
+ * array('name', 'within' => array(30,100),
26
+ * array('state', 'is' => 2)
27
+ * );
28
+ * }
29
+ *
30
+ * $person = new Person();
31
+ * $person->name = 'Tito';
32
+ * $person->state = 'this is not two characters';
33
+ *
34
+ * if (!$person->is_valid())
35
+ * print_r($person->errors);
36
+ * </code>
37
+ *
38
+ * @package ActiveRecord
39
+ * @see Errors
40
+ * @link http://www.phpactiverecord.org/guides/validations
41
+ */
42
+ class Validations
43
+ {
44
+ private $model;
45
+ private $options = array();
46
+ private $validators = array();
47
+ private $record;
48
+
49
+ private static $VALIDATION_FUNCTIONS = array(
50
+ 'validates_presence_of',
51
+ 'validates_size_of',
52
+ 'validates_length_of',
53
+ 'validates_inclusion_of',
54
+ 'validates_exclusion_of',
55
+ 'validates_format_of',
56
+ 'validates_numericality_of',
57
+ 'validates_uniqueness_of'
58
+ );
59
+
60
+ private static $DEFAULT_VALIDATION_OPTIONS = array(
61
+ 'on' => 'save',
62
+ 'allow_null' => false,
63
+ 'allow_blank' => false,
64
+ 'message' => null,
65
+ );
66
+
67
+ private static $ALL_RANGE_OPTIONS = array(
68
+ 'is' => null,
69
+ 'within' => null,
70
+ 'in' => null,
71
+ 'minimum' => null,
72
+ 'maximum' => null,
73
+ );
74
+
75
+ private static $ALL_NUMERICALITY_CHECKS = array(
76
+ 'greater_than' => null,
77
+ 'greater_than_or_equal_to' => null,
78
+ 'equal_to' => null,
79
+ 'less_than' => null,
80
+ 'less_than_or_equal_to' => null,
81
+ 'odd' => null,
82
+ 'even' => null
83
+ );
84
+
85
+ /**
86
+ * Constructs a {@link Validations} object.
87
+ *
88
+ * @param Model $model The model to validate
89
+ * @return Validations
90
+ */
91
+ public function __construct(Model $model)
92
+ {
93
+ $this->model = $model;
94
+ $this->record = new Errors($this->model);
95
+ $this->validators = array_intersect(array_keys(Reflections::instance()->get(get_class($this->model))->getStaticProperties()), self::$VALIDATION_FUNCTIONS);
96
+ }
97
+
98
+ /**
99
+ * Returns validator data.
100
+ *
101
+ * @return array
102
+ */
103
+ public function rules()
104
+ {
105
+ $data = array();
106
+ $reflection = Reflections::instance()->get(get_class($this->model));
107
+
108
+ foreach ($this->validators as $validate)
109
+ {
110
+ $attrs = $reflection->getStaticPropertyValue($validate);
111
+
112
+ foreach ($attrs as $attr)
113
+ {
114
+ $field = $attr[0];
115
+
116
+ if (!isset($data[$field]) || !is_array($data[$field]))
117
+ $data[$field] = array();
118
+
119
+ $attr['validator'] = $validate;
120
+ unset($attr[0]);
121
+ array_push($data[$field],$attr);
122
+ }
123
+ }
124
+ return $data;
125
+ }
126
+
127
+ /**
128
+ * Runs the validators.
129
+ *
130
+ * @return Errors the validation errors if any
131
+ */
132
+ public function validate()
133
+ {
134
+ $reflection = Reflections::instance()->get(get_class($this->model));
135
+
136
+ foreach ($this->validators as $validate)
137
+ $this->$validate($reflection->getStaticPropertyValue($validate));
138
+
139
+ $this->record->clear_model();
140
+ return $this->record;
141
+ }
142
+
143
+ /**
144
+ * Validates a field is not null and not blank.
145
+ *
146
+ * <code>
147
+ * class Person extends ActiveRecord\Model {
148
+ * static $validates_presence_of = array(
149
+ * array('first_name'),
150
+ * array('last_name')
151
+ * );
152
+ * }
153
+ * </code>
154
+ *
155
+ * Available options:
156
+ *
157
+ * <ul>
158
+ * <li><b>message:</b> custom error message</li>
159
+ * </ul>
160
+ *
161
+ * @param array $attrs Validation definition
162
+ */
163
+ public function validates_presence_of($attrs)
164
+ {
165
+ $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES['blank'], 'on' => 'save'));
166
+
167
+ foreach ($attrs as $attr)
168
+ {
169
+ $options = array_merge($configuration, $attr);
170
+ $this->record->add_on_blank($options[0], $options['message']);
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Validates that a value is included the specified array.
176
+ *
177
+ * <code>
178
+ * class Car extends ActiveRecord\Model {
179
+ * static $validates_inclusion_of = array(
180
+ * array('fuel_type', 'in' => array('hyrdogen', 'petroleum', 'electric')),
181
+ * );
182
+ * }
183
+ * </code>
184
+ *
185
+ * Available options:
186
+ *
187
+ * <ul>
188
+ * <li><b>in/within:</b> attribute should/shouldn't be a value within an array</li>
189
+ * <li><b>message:</b> custome error message</li>
190
+ * </ul>
191
+ *
192
+ * @param array $attrs Validation definition
193
+ */
194
+ public function validates_inclusion_of($attrs)
195
+ {
196
+ $this->validates_inclusion_or_exclusion_of('inclusion', $attrs);
197
+ }
198
+
199
+ /**
200
+ * This is the opposite of {@link validates_include_of}.
201
+ *
202
+ * @param array $attrs Validation definition
203
+ * @see validates_inclusion_of
204
+ */
205
+ public function validates_exclusion_of($attrs)
206
+ {
207
+ $this->validates_inclusion_or_exclusion_of('exclusion', $attrs);
208
+ }
209
+
210
+ /**
211
+ * Validates that a value is in or out of a specified list of values.
212
+ *
213
+ * @see validates_inclusion_of
214
+ * @see validates_exclusion_of
215
+ * @param string $type Either inclusion or exclusion
216
+ * @param $attrs Validation definition
217
+ */
218
+ public function validates_inclusion_or_exclusion_of($type, $attrs)
219
+ {
220
+ $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES[$type], 'on' => 'save'));
221
+
222
+ foreach ($attrs as $attr)
223
+ {
224
+ $options = array_merge($configuration, $attr);
225
+ $attribute = $options[0];
226
+ $var = $this->model->$attribute;
227
+
228
+ if (isset($options['in']))
229
+ $enum = $options['in'];
230
+ elseif (isset($options['within']))
231
+ $enum = $options['within'];
232
+
233
+ if (!is_array($enum))
234
+ array($enum);
235
+
236
+ $message = str_replace('%s', $var, $options['message']);
237
+
238
+ if ($this->is_null_with_option($var, $options) || $this->is_blank_with_option($var, $options))
239
+ continue;
240
+
241
+ if (('inclusion' == $type && !in_array($var, $enum)) || ('exclusion' == $type && in_array($var, $enum)))
242
+ $this->record->add($attribute, $message);
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Validates that a value is numeric.
248
+ *
249
+ * <code>
250
+ * class Person extends ActiveRecord\Model {
251
+ * static $validates_numericality_of = array(
252
+ * array('salary', 'greater_than' => 19.99, 'less_than' => 99.99)
253
+ * );
254
+ * }
255
+ * </code>
256
+ *
257
+ * Available options:
258
+ *
259
+ * <ul>
260
+ * <li><b>only_integer:</b> value must be an integer (e.g. not a float)</li>
261
+ * <li><b>even:</b> must be even</li>
262
+ * <li><b>odd:</b> must be odd"</li>
263
+ * <li><b>greater_than:</b> must be greater than specified number</li>
264
+ * <li><b>greater_than_or_equal_to:</b> must be greater than or equal to specified number</li>
265
+ * <li><b>equal_to:</b> ...</li>
266
+ * <li><b>less_than:</b> ...</li>
267
+ * <li><b>less_than_or_equal_to:</b> ...</li>
268
+ * </ul>
269
+ *
270
+ * @param array $attrs Validation definition
271
+ */
272
+ public function validates_numericality_of($attrs)
273
+ {
274
+ $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('only_integer' => false));
275
+
276
+ // Notice that for fixnum and float columns empty strings are converted to nil.
277
+ // Validates whether the value of the specified attribute is numeric by trying to convert it to a float with Kernel.Float
278
+ // (if only_integer is false) or applying it to the regular expression /\A[+\-]?\d+\Z/ (if only_integer is set to true).
279
+ foreach ($attrs as $attr)
280
+ {
281
+ $options = array_merge($configuration, $attr);
282
+ $attribute = $options[0];
283
+ $var = $this->model->$attribute;
284
+
285
+ $numericalityOptions = array_intersect_key(self::$ALL_NUMERICALITY_CHECKS, $options);
286
+
287
+ if ($this->is_null_with_option($var, $options))
288
+ continue;
289
+
290
+ if (true === $options['only_integer'] && !is_integer($var))
291
+ {
292
+ if (!preg_match('/\A[+-]?\d+\Z/', (string)($var)))
293
+ {
294
+ if (isset($options['message']))
295
+ $message = $options['message'];
296
+ else
297
+ $message = Errors::$DEFAULT_ERROR_MESSAGES['not_a_number'];
298
+
299
+ $this->record->add($attribute, $message);
300
+ continue;
301
+ }
302
+ }
303
+ else
304
+ {
305
+ if (!is_numeric($var))
306
+ {
307
+ $this->record->add($attribute, Errors::$DEFAULT_ERROR_MESSAGES['not_a_number']);
308
+ continue;
309
+ }
310
+
311
+ $var = (float)$var;
312
+ }
313
+
314
+ foreach ($numericalityOptions as $option => $check)
315
+ {
316
+ $option_value = $options[$option];
317
+
318
+ if ('odd' != $option && 'even' != $option)
319
+ {
320
+ $option_value = (float)$options[$option];
321
+
322
+ if (!is_numeric($option_value))
323
+ throw new ValidationsArgumentError("$option must be a number");
324
+
325
+ if (isset($options['message']))
326
+ $message = $options['message'];
327
+ else
328
+ $message = Errors::$DEFAULT_ERROR_MESSAGES[$option];
329
+
330
+ $message = str_replace('%d', $option_value, $message);
331
+
332
+ if ('greater_than' == $option && !($var > $option_value))
333
+ $this->record->add($attribute, $message);
334
+
335
+ elseif ('greater_than_or_equal_to' == $option && !($var >= $option_value))
336
+ $this->record->add($attribute, $message);
337
+
338
+ elseif ('equal_to' == $option && !($var == $option_value))
339
+ $this->record->add($attribute, $message);
340
+
341
+ elseif ('less_than' == $option && !($var < $option_value))
342
+ $this->record->add($attribute, $message);
343
+
344
+ elseif ('less_than_or_equal_to' == $option && !($var <= $option_value))
345
+ $this->record->add($attribute, $message);
346
+ }
347
+ else
348
+ {
349
+ if (isset($options['message']))
350
+ $message = $options['message'];
351
+ else
352
+ $message = Errors::$DEFAULT_ERROR_MESSAGES[$option];
353
+
354
+ if ( ('odd' == $option && !( Utils::is_odd($var))) || ('even' == $option && ( Utils::is_odd($var))))
355
+ $this->record->add($attribute, $message);
356
+ }
357
+ }
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Alias of {@link validates_length_of}
363
+ *
364
+ * @param array $attrs Validation definition
365
+ */
366
+ public function validates_size_of($attrs)
367
+ {
368
+ $this->validates_length_of($attrs);
369
+ }
370
+
371
+ /**
372
+ * Validates that a value is matches a regex.
373
+ *
374
+ * <code>
375
+ * class Person extends ActiveRecord\Model {
376
+ * static $validates_format_of = array(
377
+ * array('email', 'with' => '/^.*?@.*$/')
378
+ * );
379
+ * }
380
+ * </code>
381
+ *
382
+ * Available options:
383
+ *
384
+ * <ul>
385
+ * <li><b>with:</b> a regular expression</li>
386
+ * <li><b>message:</b> custom error message</li>
387
+ * </ul>
388
+ *
389
+ * @param array $attrs Validation definition
390
+ */
391
+ public function validates_format_of($attrs)
392
+ {
393
+ $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES['invalid'], 'on' => 'save', 'with' => null));
394
+
395
+ foreach ($attrs as $attr)
396
+ {
397
+ $options = array_merge($configuration, $attr);
398
+ $attribute = $options[0];
399
+ $var = $this->model->$attribute;
400
+
401
+ if (is_null($options['with']) || !is_string($options['with']) || !is_string($options['with']))
402
+ throw new ValidationsArgumentError('A regular expression must be supplied as the [with] option of the configuration array.');
403
+ else
404
+ $expression = $options['with'];
405
+
406
+ if ($this->is_null_with_option($var, $options) || $this->is_blank_with_option($var, $options))
407
+ continue;
408
+
409
+ if (!@preg_match($expression, $var))
410
+ $this->record->add($attribute, $options['message']);
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Validates the length of a value.
416
+ *
417
+ * <code>
418
+ * class Person extends ActiveRecord\Model {
419
+ * static $validates_length_of = array(
420
+ * array('name', 'within' => array(1,50))
421
+ * );
422
+ * }
423
+ * </code>
424
+ *
425
+ * Available options:
426
+ *
427
+ * <ul>
428
+ * <li><b>is:</b> attribute should be exactly n characters long</li>
429
+ * <li><b>in/within:</b> attribute should be within an range array(min,max)</li>
430
+ * <li><b>maximum/minimum:</b> attribute should not be above/below respectively</li>
431
+ * </ul>
432
+ *
433
+ * @param array $attrs Validation definition
434
+ */
435
+ public function validates_length_of($attrs)
436
+ {
437
+ $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array(
438
+ 'too_long' => Errors::$DEFAULT_ERROR_MESSAGES['too_long'],
439
+ 'too_short' => Errors::$DEFAULT_ERROR_MESSAGES['too_short'],
440
+ 'wrong_length' => Errors::$DEFAULT_ERROR_MESSAGES['wrong_length']
441
+ ));
442
+
443
+ foreach ($attrs as $attr)
444
+ {
445
+ $options = array_merge($configuration, $attr);
446
+ $range_options = array_intersect(array_keys(self::$ALL_RANGE_OPTIONS), array_keys($attr));
447
+ sort($range_options);
448
+
449
+ switch (sizeof($range_options))
450
+ {
451
+ case 0:
452
+ throw new ValidationsArgumentError('Range unspecified. Specify the [within], [maximum], or [is] option.');
453
+
454
+ case 1:
455
+ break;
456
+
457
+ default:
458
+ throw new ValidationsArgumentError('Too many range options specified. Choose only one.');
459
+ }
460
+
461
+ $attribute = $options[0];
462
+ $var = $this->model->$attribute;
463
+ $range_option = $range_options[0];
464
+
465
+ if ($this->is_null_with_option($var, $options) || $this->is_blank_with_option($var, $options))
466
+ continue;
467
+
468
+ if ('within' == $range_option || 'in' == $range_option)
469
+ {
470
+ $range = $options[$range_options[0]];
471
+
472
+ if (!(Utils::is_a('range', $range)))
473
+ throw new ValidationsArgumentError("$range_option must be an array composing a range of numbers with key [0] being less than key [1]");
474
+
475
+ if (is_float($range[0]) || is_float($range[1]))
476
+ throw new ValidationsArgumentError("Range values cannot use floats for length.");
477
+
478
+ if ((int)$range[0] <= 0 || (int)$range[1] <= 0)
479
+ throw new ValidationsArgumentError("Range values cannot use signed integers.");
480
+
481
+ $too_short = isset($options['message']) ? $options['message'] : $options['too_short'];
482
+ $too_long = isset($options['message']) ? $options['message'] : $options['too_long'];
483
+
484
+ $too_short = str_replace('%d', $range[0], $too_short);
485
+ $too_long = str_replace('%d', $range[1], $too_long);
486
+
487
+ if (strlen($this->model->$attribute) < (int)$range[0])
488
+ $this->record->add($attribute, $too_short);
489
+ elseif (strlen($this->model->$attribute) > (int)$range[1])
490
+ $this->record->add($attribute, $too_long);
491
+ }
492
+
493
+ elseif ('is' == $range_option || 'minimum' == $range_option || 'maximum' == $range_option)
494
+ {
495
+ $option = $options[$range_option];
496
+
497
+ if ((int)$option <= 0)
498
+ throw new ValidationsArgumentError("$range_option value cannot use a signed integer.");
499
+
500
+ if (is_float($option))
501
+ throw new ValidationsArgumentError("$range_option value cannot use a float for length.");
502
+
503
+ if (!is_null($this->model->$attribute))
504
+ {
505
+ $messageOptions = array('is' => 'wrong_length', 'minimum' => 'too_short', 'maximum' => 'too_long');
506
+
507
+ if (isset($options[$messageOptions[$range_option]]))
508
+ $message = $options[$messageOptions[$range_option]];
509
+ else
510
+ $message = $options['message'];
511
+
512
+ $message = str_replace('%d', $option, $message);
513
+ $attribute_value = $this->model->$attribute;
514
+ $len = strlen($attribute_value);
515
+ $value = (int)$attr[$range_option];
516
+
517
+ if ('maximum' == $range_option && $len > $value)
518
+ $this->record->add($attribute, $message);
519
+
520
+ if ('minimum' == $range_option && $len < $value)
521
+ $this->record->add($attribute, $message);
522
+
523
+ if ('is' == $range_option && $len !== $value)
524
+ $this->record->add($attribute, $message);
525
+ }
526
+ }
527
+ }
528
+ }
529
+
530
+ /**
531
+ * Validates the uniqueness of a value.
532
+ *
533
+ * <code>
534
+ * class Person extends ActiveRecord\Model {
535
+ * static $validates_uniqueness_of = array(
536
+ * array('name'),
537
+ * array(array('blah','bleh'), 'message' => 'blech')
538
+ * );
539
+ * }
540
+ * </code>
541
+ *
542
+ * @param array $attrs Validation definition
543
+ */
544
+ public function validates_uniqueness_of($attrs)
545
+ {
546
+ $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array(
547
+ 'message' => Errors::$DEFAULT_ERROR_MESSAGES['unique']
548
+ ));
549
+
550
+ foreach ($attrs as $attr)
551
+ {
552
+ $options = array_merge($configuration, $attr);
553
+ $pk = $this->model->get_primary_key();
554
+ $pk_value = $this->model->$pk[0];
555
+
556
+ if (is_array($options[0]))
557
+ {
558
+ $add_record = join("_and_", $options[0]);
559
+ $fields = $options[0];
560
+ }
561
+ else
562
+ {
563
+ $add_record = $options[0];
564
+ $fields = array($options[0]);
565
+ }
566
+
567
+ $sql = "";
568
+ $conditions = array("");
569
+
570
+ if ($pk_value === null)
571
+ $sql = "{$pk[0]} is not null";
572
+ else
573
+ {
574
+ $sql = "{$pk[0]}!=?";
575
+ array_push($conditions,$pk_value);
576
+ }
577
+
578
+ foreach ($fields as $field)
579
+ {
580
+ $field = $this->model->get_real_attribute_name($field);
581
+ $sql .= " and {$field}=?";
582
+ array_push($conditions,$this->model->$field);
583
+ }
584
+
585
+ $conditions[0] = $sql;
586
+
587
+ if ($this->model->exists(array('conditions' => $conditions)))
588
+ $this->record->add($add_record, $options['message']);
589
+ }
590
+ }
591
+
592
+ private function is_null_with_option($var, &$options)
593
+ {
594
+ return (is_null($var) && (isset($options['allow_null']) && $options['allow_null']));
595
+ }
596
+
597
+ private function is_blank_with_option($var, &$options)
598
+ {
599
+ return (Utils::is_blank($var) && (isset($options['allow_blank']) && $options['allow_blank']));
600
+ }
601
+ }
602
+
603
+ /**
604
+ * Class that holds {@link Validations} errors.
605
+ *
606
+ * @package ActiveRecord
607
+ */
608
+ class Errors implements IteratorAggregate
609
+ {
610
+ private $model;
611
+ private $errors;
612
+
613
+ public static $DEFAULT_ERROR_MESSAGES = array(
614
+ 'inclusion' => "is not included in the list",
615
+ 'exclusion' => "is reserved",
616
+ 'invalid' => "is invalid",
617
+ 'confirmation' => "doesn't match confirmation",
618
+ 'accepted' => "must be accepted",
619
+ 'empty' => "can't be empty",
620
+ 'blank' => "can't be blank",
621
+ 'too_long' => "is too long (maximum is %d characters)",
622
+ 'too_short' => "is too short (minimum is %d characters)",
623
+ 'wrong_length' => "is the wrong length (should be %d characters)",
624
+ 'taken' => "has already been taken",
625
+ 'not_a_number' => "is not a number",
626
+ 'greater_than' => "must be greater than %d",
627
+ 'equal_to' => "must be equal to %d",
628
+ 'less_than' => "must be less than %d",
629
+ 'odd' => "must be odd",
630
+ 'even' => "must be even",
631
+ 'unique' => "must be unique",
632
+ 'less_than_or_equal_to' => "must be less than or equal to %d",
633
+ 'greater_than_or_equal_to' => "must be greater than or equal to %d"
634
+ );
635
+
636
+ /**
637
+ * Constructs an {@link Errors} object.
638
+ *
639
+ * @param Model $model The model the error is for
640
+ * @return Errors
641
+ */
642
+ public function __construct(Model $model)
643
+ {
644
+ $this->model = $model;
645
+ }
646
+
647
+ /**
648
+ * Nulls $model so we don't get pesky circular references. $model is only needed during the
649
+ * validation process and so can be safely cleared once that is done.
650
+ */
651
+ public function clear_model()
652
+ {
653
+ $this->model = null;
654
+ }
655
+
656
+ /**
657
+ * Add an error message.
658
+ *
659
+ * @param string $attribute Name of an attribute on the model
660
+ * @param string $msg The error message
661
+ */
662
+ public function add($attribute, $msg)
663
+ {
664
+ if (is_null($msg))
665
+ $msg = self :: $DEFAULT_ERROR_MESSAGES['invalid'];
666
+
667
+ if (!isset($this->errors[$attribute]))
668
+ $this->errors[$attribute] = array($msg);
669
+ else
670
+ $this->errors[$attribute][] = $msg;
671
+ }
672
+
673
+ /**
674
+ * Adds an error message only if the attribute value is {@link http://www.php.net/empty empty}.
675
+ *
676
+ * @param string $attribute Name of an attribute on the model
677
+ * @param string $msg The error message
678
+ */
679
+ public function add_on_empty($attribute, $msg)
680
+ {
681
+ if (empty($msg))
682
+ $msg = self::$DEFAULT_ERROR_MESSAGES['empty'];
683
+
684
+ if (empty($this->model->$attribute))
685
+ $this->add($attribute, $msg);
686
+ }
687
+
688
+ /**
689
+ * Retrieve error message for an attribute.
690
+ *
691
+ * @param string $attribute Name of an attribute on the model
692
+ * @return string
693
+ */
694
+ public function __get($attribute)
695
+ {
696
+ if (!isset($this->errors[$attribute]))
697
+ return null;
698
+
699
+ return $this->errors[$attribute];
700
+ }
701
+
702
+ /**
703
+ * Adds the error message only if the attribute value was null or an empty string.
704
+ *
705
+ * @param string $attribute Name of an attribute on the model
706
+ * @param string $msg The error message
707
+ */
708
+ public function add_on_blank($attribute, $msg)
709
+ {
710
+ if (!$msg)
711
+ $msg = self::$DEFAULT_ERROR_MESSAGES['blank'];
712
+
713
+ if (($value = $this->model->$attribute) === '' || $value === null)
714
+ $this->add($attribute, $msg);
715
+ }
716
+
717
+ /**
718
+ * Returns true if the specified attribute had any error messages.
719
+ *
720
+ * @param string $attribute Name of an attribute on the model
721
+ * @return boolean
722
+ */
723
+ public function is_invalid($attribute)
724
+ {
725
+ return isset($this->errors[$attribute]);
726
+ }
727
+
728
+ /**
729
+ * Returns the error message for the specified attribute or null if none.
730
+ *
731
+ * @param string $attribute Name of an attribute on the model
732
+ * @return string
733
+ */
734
+ public function on($attribute)
735
+ {
736
+ if (!isset($this->errors[$attribute]))
737
+ return null;
738
+
739
+ $errors = $this->errors[$attribute];
740
+
741
+ if (null === $errors)
742
+ return null;
743
+ else
744
+ return count($errors) == 1 ? $errors[0] : $errors;
745
+ }
746
+
747
+ /**
748
+ * Returns all the error messages as an array.
749
+ *
750
+ * <code>
751
+ * $model->errors->full_messages();
752
+ *
753
+ * # array(
754
+ * # "Name can't be blank",
755
+ * # "State is the wrong length (should be 2 chars)"
756
+ * # )
757
+ * </code>
758
+ *
759
+ * @param array $options Options for messages
760
+ * @return array
761
+ */
762
+ public function full_messages()
763
+ {
764
+ $full_messages = array();
765
+
766
+ if ($this->errors)
767
+ {
768
+ foreach ($this->errors as $attribute => $messages)
769
+ {
770
+ foreach ($messages as $msg)
771
+ {
772
+ if (is_null($msg))
773
+ continue;
774
+
775
+ $full_messages[] = Utils::human_attribute($attribute) . ' ' . $msg;
776
+ }
777
+ }
778
+ }
779
+ return $full_messages;
780
+ }
781
+
782
+ /**
783
+ * Returns true if there are no error messages.
784
+ * @return boolean
785
+ */
786
+ public function is_empty()
787
+ {
788
+ return empty($this->errors);
789
+ }
790
+
791
+ /**
792
+ * Clears out all error messages.
793
+ */
794
+ public function clear()
795
+ {
796
+ $this->errors = array();
797
+ }
798
+
799
+ /**
800
+ * Returns the number of error messages there are.
801
+ * @return int
802
+ */
803
+ public function size()
804
+ {
805
+ if ($this->is_empty())
806
+ return 0;
807
+
808
+ $count = 0;
809
+
810
+ foreach ($this->errors as $attribute => $error)
811
+ $count += count($error);
812
+
813
+ return $count;
814
+ }
815
+
816
+ /**
817
+ * Returns an iterator to the error messages.
818
+ *
819
+ * This will allow you to iterate over the {@link Errors} object using foreach.
820
+ *
821
+ * <code>
822
+ * foreach ($model->errors as $msg)
823
+ * echo "$msg\n";
824
+ * </code>
825
+ *
826
+ * @return ArrayIterator
827
+ */
828
+ public function getIterator()
829
+ {
830
+ return new ArrayIterator($this->full_messages());
831
+ }
832
+ };
833
+ ?>