extjs-mvc 0.4.0.j → 0.4.0.k

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. data/Rakefile +1 -1
  2. data/VERSION +1 -1
  3. data/lib/extjs-mvc.rb +0 -14
  4. data/lib/extjs-mvc/api.rb +14 -27
  5. data/lib/extjs-mvc/src/App.js +219 -0
  6. data/lib/extjs-mvc/src/MVC.js +260 -0
  7. data/lib/extjs-mvc/src/Presenter.js +52 -0
  8. data/lib/extjs-mvc/src/README.rdoc +69 -0
  9. data/lib/extjs-mvc/src/controller/Controller.js +278 -0
  10. data/lib/extjs-mvc/src/controller/CrudController.js +460 -0
  11. data/lib/extjs-mvc/src/lib/Array.js +26 -0
  12. data/lib/extjs-mvc/src/lib/Booter.js +417 -0
  13. data/lib/extjs-mvc/src/lib/ClassManager.js +191 -0
  14. data/lib/extjs-mvc/src/lib/ControllerClassManager.js +95 -0
  15. data/lib/extjs-mvc/src/lib/Dependencies.js +44 -0
  16. data/lib/extjs-mvc/src/lib/DispatchMatcher.js +98 -0
  17. data/lib/extjs-mvc/src/lib/Dispatcher.js +129 -0
  18. data/lib/extjs-mvc/src/lib/Environment.js +43 -0
  19. data/lib/extjs-mvc/src/lib/Inflector.js +155 -0
  20. data/lib/extjs-mvc/src/lib/ModelClassManager.js +19 -0
  21. data/lib/extjs-mvc/src/lib/Route.js +139 -0
  22. data/lib/extjs-mvc/src/lib/Router.js +282 -0
  23. data/lib/extjs-mvc/src/lib/String.js +94 -0
  24. data/lib/extjs-mvc/src/lib/ViewClassManager.js +229 -0
  25. data/lib/extjs-mvc/src/lib/notes.txt +32 -0
  26. data/lib/extjs-mvc/src/model/AdapterManager.js +30 -0
  27. data/lib/extjs-mvc/src/model/Association.js +26 -0
  28. data/lib/extjs-mvc/src/model/Base.js +63 -0
  29. data/lib/extjs-mvc/src/model/BelongsToAssociation.js +116 -0
  30. data/lib/extjs-mvc/src/model/Cache.js +131 -0
  31. data/lib/extjs-mvc/src/model/HasManyAssociation.js +160 -0
  32. data/lib/extjs-mvc/src/model/Model.js +331 -0
  33. data/lib/extjs-mvc/src/model/UrlBuilder.js +106 -0
  34. data/lib/extjs-mvc/src/model/adapters/AbstractAdapter.js +296 -0
  35. data/lib/extjs-mvc/src/model/adapters/MemoryAdapter.js +103 -0
  36. data/lib/extjs-mvc/src/model/adapters/RESTAdapter.js +345 -0
  37. data/lib/extjs-mvc/src/model/adapters/RESTJSONAdapter.js +68 -0
  38. data/lib/extjs-mvc/src/model/adapters/notes.txt +42 -0
  39. data/lib/extjs-mvc/src/model/associations/Association.js +192 -0
  40. data/lib/extjs-mvc/src/model/associations/notes.txt +87 -0
  41. data/lib/extjs-mvc/src/model/validations/Errors.js +136 -0
  42. data/lib/extjs-mvc/src/model/validations/Plugin.js +139 -0
  43. data/lib/extjs-mvc/src/model/validations/Validations.js +276 -0
  44. data/lib/extjs-mvc/src/notes/Charts.graffle +0 -0
  45. data/lib/extjs-mvc/src/overrides/Ext.Component.js +21 -0
  46. data/lib/extjs-mvc/src/overrides/Ext.extend.js +142 -0
  47. data/lib/extjs-mvc/src/spec/Array.spec.js +15 -0
  48. data/lib/extjs-mvc/src/spec/ExtMVC.spec.js +65 -0
  49. data/lib/extjs-mvc/src/spec/Model.spec.js +370 -0
  50. data/lib/extjs-mvc/src/spec/OS.spec.js +83 -0
  51. data/lib/extjs-mvc/src/spec/Router.spec.js +99 -0
  52. data/lib/extjs-mvc/src/spec/SpecHelper.js +106 -0
  53. data/lib/extjs-mvc/src/spec/String.spec.js +83 -0
  54. data/lib/extjs-mvc/src/spec/model/AbstractAdapter.spec.js +49 -0
  55. data/lib/extjs-mvc/src/spec/model/Associations.spec.js +99 -0
  56. data/lib/extjs-mvc/src/spec/model/Cache.spec.js +5 -0
  57. data/lib/extjs-mvc/src/spec/model/RESTAdapter.spec.js +19 -0
  58. data/lib/extjs-mvc/src/spec/model/ValidationErrors.spec.js +64 -0
  59. data/lib/extjs-mvc/src/spec/model/Validations.spec.js +166 -0
  60. data/lib/extjs-mvc/src/spec/model/ValidationsPlugin.spec.js +108 -0
  61. data/lib/extjs-mvc/src/spec/suite.html +60 -0
  62. data/lib/extjs-mvc/src/specs-old/JSSpec.css +216 -0
  63. data/lib/extjs-mvc/src/specs-old/JSSpec.js +1512 -0
  64. data/lib/extjs-mvc/src/specs-old/all.html +66 -0
  65. data/lib/extjs-mvc/src/specs-old/base.js +14 -0
  66. data/lib/extjs-mvc/src/specs-old/controller.js +17 -0
  67. data/lib/extjs-mvc/src/specs-old/diff_match_patch.js +1 -0
  68. data/lib/extjs-mvc/src/specs-old/model.js +70 -0
  69. data/lib/extjs-mvc/src/specs-old/route.js +38 -0
  70. data/lib/extjs-mvc/src/specs-old/router.js +59 -0
  71. data/lib/extjs-mvc/src/specs-old/string.js +22 -0
  72. data/lib/extjs-mvc/src/testrunner/JSpecFormatter.js +111 -0
  73. data/lib/extjs-mvc/src/testrunner/TestClient.js +181 -0
  74. data/lib/extjs-mvc/src/testrunner/TestGrid.js +351 -0
  75. data/lib/extjs-mvc/src/testrunner/TestRunner.js +110 -0
  76. data/lib/extjs-mvc/src/testrunner/TestViewport.js +94 -0
  77. data/lib/extjs-mvc/src/vendor.yml +30 -0
  78. data/lib/extjs-mvc/src/vendor/ext-3.1.1/vendor.yml +16 -0
  79. data/lib/extjs-mvc/src/view/FormWindow.js +184 -0
  80. data/lib/extjs-mvc/src/view/HasManyEditorGridPanel.js +211 -0
  81. data/lib/extjs-mvc/src/view/scaffold/Edit.js +46 -0
  82. data/lib/extjs-mvc/src/view/scaffold/Index.js +561 -0
  83. data/lib/extjs-mvc/src/view/scaffold/New.js +20 -0
  84. data/lib/extjs-mvc/src/view/scaffold/ScaffoldFormPanel.js +255 -0
  85. data/lib/{vendor.yml → extjs-mvc/vendor.yml} +0 -0
  86. data/test/app/vendor/extjs-mvc/App.js +219 -0
  87. data/test/app/vendor/extjs-mvc/MVC.js +260 -0
  88. data/test/app/vendor/extjs-mvc/Presenter.js +52 -0
  89. data/test/app/vendor/extjs-mvc/README.rdoc +69 -0
  90. data/test/app/vendor/extjs-mvc/controller/Controller.js +278 -0
  91. data/test/app/vendor/extjs-mvc/controller/CrudController.js +460 -0
  92. data/test/app/vendor/extjs-mvc/lib/Array.js +26 -0
  93. data/test/app/vendor/extjs-mvc/lib/Booter.js +417 -0
  94. data/test/app/vendor/extjs-mvc/lib/ClassManager.js +191 -0
  95. data/test/app/vendor/extjs-mvc/lib/ControllerClassManager.js +95 -0
  96. data/test/app/vendor/extjs-mvc/lib/Dependencies.js +44 -0
  97. data/test/app/vendor/extjs-mvc/lib/DispatchMatcher.js +98 -0
  98. data/test/app/vendor/extjs-mvc/lib/Dispatcher.js +129 -0
  99. data/test/app/vendor/extjs-mvc/lib/Environment.js +43 -0
  100. data/test/app/vendor/extjs-mvc/lib/Inflector.js +155 -0
  101. data/test/app/vendor/extjs-mvc/lib/ModelClassManager.js +19 -0
  102. data/test/app/vendor/extjs-mvc/lib/Route.js +139 -0
  103. data/test/app/vendor/extjs-mvc/lib/Router.js +282 -0
  104. data/test/app/vendor/extjs-mvc/lib/String.js +94 -0
  105. data/test/app/vendor/extjs-mvc/lib/ViewClassManager.js +229 -0
  106. data/test/app/vendor/extjs-mvc/lib/notes.txt +32 -0
  107. data/test/app/vendor/extjs-mvc/model/AdapterManager.js +30 -0
  108. data/test/app/vendor/extjs-mvc/model/Association.js +26 -0
  109. data/test/app/vendor/extjs-mvc/model/Base.js +63 -0
  110. data/test/app/vendor/extjs-mvc/model/BelongsToAssociation.js +116 -0
  111. data/test/app/vendor/extjs-mvc/model/Cache.js +131 -0
  112. data/test/app/vendor/extjs-mvc/model/HasManyAssociation.js +160 -0
  113. data/test/app/vendor/extjs-mvc/model/Model.js +331 -0
  114. data/test/app/vendor/extjs-mvc/model/UrlBuilder.js +106 -0
  115. data/test/app/vendor/extjs-mvc/model/adapters/AbstractAdapter.js +296 -0
  116. data/test/app/vendor/extjs-mvc/model/adapters/MemoryAdapter.js +103 -0
  117. data/test/app/vendor/extjs-mvc/model/adapters/RESTAdapter.js +345 -0
  118. data/test/app/vendor/extjs-mvc/model/adapters/RESTJSONAdapter.js +68 -0
  119. data/test/app/vendor/extjs-mvc/model/adapters/notes.txt +42 -0
  120. data/test/app/vendor/extjs-mvc/model/associations/Association.js +192 -0
  121. data/test/app/vendor/extjs-mvc/model/associations/notes.txt +87 -0
  122. data/test/app/vendor/extjs-mvc/model/validations/Errors.js +136 -0
  123. data/test/app/vendor/extjs-mvc/model/validations/Plugin.js +139 -0
  124. data/test/app/vendor/extjs-mvc/model/validations/Validations.js +276 -0
  125. data/test/app/vendor/extjs-mvc/notes/Charts.graffle +0 -0
  126. data/test/app/vendor/extjs-mvc/overrides/Ext.Component.js +21 -0
  127. data/test/app/vendor/extjs-mvc/overrides/Ext.extend.js +142 -0
  128. data/test/app/vendor/extjs-mvc/spec/Array.spec.js +15 -0
  129. data/test/app/vendor/extjs-mvc/spec/ExtMVC.spec.js +65 -0
  130. data/test/app/vendor/extjs-mvc/spec/Model.spec.js +370 -0
  131. data/test/app/vendor/extjs-mvc/spec/OS.spec.js +83 -0
  132. data/test/app/vendor/extjs-mvc/spec/Router.spec.js +99 -0
  133. data/test/app/vendor/extjs-mvc/spec/SpecHelper.js +106 -0
  134. data/test/app/vendor/extjs-mvc/spec/String.spec.js +83 -0
  135. data/test/app/vendor/extjs-mvc/spec/model/AbstractAdapter.spec.js +49 -0
  136. data/test/app/vendor/extjs-mvc/spec/model/Associations.spec.js +99 -0
  137. data/test/app/vendor/extjs-mvc/spec/model/Cache.spec.js +5 -0
  138. data/test/app/vendor/extjs-mvc/spec/model/RESTAdapter.spec.js +19 -0
  139. data/test/app/vendor/extjs-mvc/spec/model/ValidationErrors.spec.js +64 -0
  140. data/test/app/vendor/extjs-mvc/spec/model/Validations.spec.js +166 -0
  141. data/test/app/vendor/extjs-mvc/spec/model/ValidationsPlugin.spec.js +108 -0
  142. data/test/app/vendor/extjs-mvc/spec/suite.html +60 -0
  143. data/test/app/vendor/extjs-mvc/specs-old/JSSpec.css +216 -0
  144. data/test/app/vendor/extjs-mvc/specs-old/JSSpec.js +1512 -0
  145. data/test/app/vendor/extjs-mvc/specs-old/all.html +66 -0
  146. data/test/app/vendor/extjs-mvc/specs-old/base.js +14 -0
  147. data/test/app/vendor/extjs-mvc/specs-old/controller.js +17 -0
  148. data/test/app/vendor/extjs-mvc/specs-old/diff_match_patch.js +1 -0
  149. data/test/app/vendor/extjs-mvc/specs-old/model.js +70 -0
  150. data/test/app/vendor/extjs-mvc/specs-old/route.js +38 -0
  151. data/test/app/vendor/extjs-mvc/specs-old/router.js +59 -0
  152. data/test/app/vendor/extjs-mvc/specs-old/string.js +22 -0
  153. data/test/app/vendor/extjs-mvc/testrunner/JSpecFormatter.js +111 -0
  154. data/test/app/vendor/extjs-mvc/testrunner/TestClient.js +181 -0
  155. data/test/app/vendor/extjs-mvc/testrunner/TestGrid.js +351 -0
  156. data/test/app/vendor/extjs-mvc/testrunner/TestRunner.js +110 -0
  157. data/test/app/vendor/extjs-mvc/testrunner/TestViewport.js +94 -0
  158. data/test/app/vendor/extjs-mvc/vendor.yml +30 -0
  159. data/test/app/vendor/extjs-mvc/vendor/ext-3.1.1/vendor.yml +16 -0
  160. data/test/app/vendor/extjs-mvc/view/FormWindow.js +184 -0
  161. data/test/app/vendor/extjs-mvc/view/HasManyEditorGridPanel.js +211 -0
  162. data/test/app/vendor/extjs-mvc/view/scaffold/Edit.js +46 -0
  163. data/test/app/vendor/extjs-mvc/view/scaffold/Index.js +561 -0
  164. data/test/app/vendor/extjs-mvc/view/scaffold/New.js +20 -0
  165. data/test/app/vendor/extjs-mvc/view/scaffold/ScaffoldFormPanel.js +255 -0
  166. data/test/helper.rb +7 -1
  167. data/test/test_extjs-mvc.rb +46 -0
  168. metadata +167 -7
  169. data/test/test_extjs-mvc-gem.rb +0 -7
@@ -0,0 +1,192 @@
1
+ /**
2
+ * @class ExtMVC.model.plugin.association
3
+ * @ignore
4
+ */
5
+ ExtMVC.model.plugin.association = {
6
+ /**
7
+ * This function is called every time a model is created via ExtMVC.model.create
8
+ * (*NOT* when a model instance is instantiated). Here we initialize associations
9
+ * on this model.
10
+ */
11
+ initialize: function(model) {
12
+ var proto = model.prototype,
13
+ assoc = ExtMVC.model.plugin.association;
14
+
15
+ this.resolveDependencies(model);
16
+
17
+ if (proto.hasMany) {
18
+ Ext.each(this.parseParams(proto.hasMany, 'HasMany'), function(params) {
19
+ this.define(assoc.HasMany, model, params);
20
+ }, this);
21
+ }
22
+
23
+ if (proto.belongsTo) {
24
+ Ext.each(this.parseParams(proto.belongsTo, 'BelongsTo'), function(params) {
25
+ this.define(assoc.BelongsTo, model, params);
26
+ }, this);
27
+ }
28
+ },
29
+
30
+ /**
31
+ * @property dependencies
32
+ * @type ExtMVC.lib.Dependencies
33
+ * Dependencies class to manage associations on currently undefined models
34
+ */
35
+ dependencies: new ExtMVC.lib.Dependencies(),
36
+
37
+ /**
38
+ * Defines a new association between two models. If both models have already been created,
39
+ * the association is created immediately, otherwise it is deferred until both models have been created
40
+ * You should never have to call this manually...
41
+ * @param {Function} constructor The Association constructor to use (one of BelongsTo or HasMany)
42
+ * @param {ExtMVC.model.Base} model The model which owns the association
43
+ * @param {Object} params Association params such as associationName and associatedClass
44
+ */
45
+ define: function(constructor, model, params) {
46
+ var modelNS = ExtMVC.model.modelNamespace,
47
+ associatedClass = params.associatedClass,
48
+ modelName = model.prototype.modelName;
49
+
50
+ if (typeof modelNS[associatedClass] == 'function') {
51
+ //create the model now
52
+ this.create.call(this, constructor, modelName, params);
53
+ } else {
54
+ //create the model later
55
+ params.associationConstructor = constructor;
56
+ this.dependencies.add(associatedClass, modelName, params);
57
+ }
58
+ },
59
+
60
+ /**
61
+ * Creates an association once both models in question have been created
62
+ * @param {Function} constructor The association constructor (should be HasMany or BelongsTo function)
63
+ * @param {String} modelName The name of the model on which the association is to be defined
64
+ * @param {Object} params Parameters for the association, containing at least the following properties:
65
+ */
66
+ create: function(constructor, modelName, params) {
67
+ var modelNS = ExtMVC.model.modelNamespace,
68
+ model = modelNS[modelName],
69
+ associatedModel = modelNS[params.associatedClass],
70
+ associationName = params.associationName;
71
+
72
+ model.prototype[associationName] = new constructor(model, associatedModel, params);
73
+ },
74
+
75
+ /**
76
+ * This is called immediately by initialize(). Associations are often specified on models that haven't
77
+ * been created yet, so we keep a list of dependent associations which are to be defined as soon as the
78
+ * model has been created. This method is called with the Model constructor function, looks up any associations
79
+ * that couldn't previously be defined (as this model did not yet exist), and creates them no
80
+ * @param {ExtMVC.model.Base} model The newly created model
81
+ */
82
+ resolveDependencies: function(model) {
83
+ var dependents = this.dependencies.get(model.prototype.modelName);
84
+
85
+ Ext.each(dependents || [], function(dependent) {
86
+ var constructor = dependent.config.associationConstructor;
87
+ delete dependent.config.associationConstructor;
88
+
89
+ this.create(constructor, dependent.name, dependent.config);
90
+ }, this);
91
+ },
92
+
93
+ /**
94
+ * Parses belongsTo and hasMany params into a unified format
95
+ * @param {Mixed} params String, Object or Array
96
+ * @param {String} associationType BelongsTo or HasMany - decides how to generate the default association name
97
+ * @return {Array} An array of normalized params objects
98
+ */
99
+ parseParams: function(params, associationType) {
100
+ var results = [],
101
+ associationType = associationType || 'BelongsTo',
102
+ inflectMethod = associationType == 'BelongsTo' ? 'singularize' : 'pluralize';
103
+
104
+ /**
105
+ * We're either passed a string, an object, or an array containing one or more
106
+ * of each...
107
+ */
108
+ if (Ext.isArray(params)) {
109
+ Ext.each(params, function(association) {
110
+ results.concat(this.parseParams(association));
111
+ }, this);
112
+ return results;
113
+
114
+ } else {
115
+ if (typeof params == 'string') {
116
+ params = {associatedClass: params};
117
+ }
118
+
119
+ var assocClass = params.associatedClass,
120
+ assocName = typeof assocClass == 'function'
121
+ ? ExtMVC.Inflector[inflectMethod](assocClass.prototype.modelName)
122
+ : ExtMVC.Inflector[inflectMethod](assocClass);
123
+
124
+ Ext.applyIf(params, {
125
+ extend: {},
126
+ associationName: assocName
127
+ });
128
+
129
+ results.push(params);
130
+ }
131
+
132
+ return results;
133
+ }
134
+ };
135
+
136
+ /**
137
+ * @class ExtMVC.model.plugin.association.Base
138
+ * Association Base class which provides basic functionality for other Association classes to build upon
139
+ * Don't use directly - instead use the HasMany or BelongsTo classes.
140
+ */
141
+ ExtMVC.model.plugin.association.Base = function(ownerClass, associatedClass, config) {
142
+ config = config || {};
143
+
144
+ this.ownerClass = ownerClass;
145
+ this.associatedClass = associatedClass;
146
+
147
+ Ext.apply(this, config.extend || {});
148
+ this.initialConfig = config;
149
+
150
+ this.initialize();
151
+ };
152
+
153
+ ExtMVC.model.plugin.association.Base.prototype = {
154
+ /**
155
+ * Sets up default values for foreignKey
156
+ */
157
+ initialize: Ext.emptyFn
158
+ };
159
+
160
+ /**
161
+ * @class ExtMVC.model.plugin.association.BelongsTo
162
+ * @extends ExtMVC.model.plugin.association.Base
163
+ * A belongsTo association
164
+ */
165
+ ExtMVC.model.plugin.association.BelongsTo = Ext.extend(ExtMVC.model.plugin.association.Base, {
166
+ initialize: function() {
167
+ Ext.apply(this, {
168
+ name: ExtMVC.Inflector.singularize(this.associatedClass.prototype.tableName),
169
+ foreignKey: this.associatedClass.prototype.foreignKeyName
170
+ });
171
+ }
172
+ });
173
+
174
+ /**
175
+ * @class ExtMVC.model.plugin.association.HasMany
176
+ * @extends ExtMVC.model.plugin.association.Base
177
+ * A hasMany association
178
+ */
179
+ ExtMVC.model.plugin.association.HasMany = Ext.extend(ExtMVC.model.plugin.association.Base, {
180
+
181
+ /**
182
+ * Set up default values for name etc
183
+ */
184
+ initialize: function() {
185
+ Ext.apply(this, {
186
+ name: this.associatedClass.prototype.tableName,
187
+ foreignKey: this.ownerClass.prototype.foreignKeyName
188
+ });
189
+ }
190
+ });
191
+
192
+ ExtMVC.model.addPlugin(ExtMVC.model.plugin.association);
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Method Collection Individual
3
+ * create yes yes (but different)
4
+ * build yes yes
5
+ * find yes no
6
+ * loaded yes yes (but different)
7
+ * count yes no
8
+ * destroy yes yes (but different)
9
+ */
10
+
11
+ /**
12
+ * Method HasMany BelongsTo
13
+ * create yes no
14
+ * build yes no
15
+ * destroy yes yes
16
+ * find yes yes
17
+ */
18
+
19
+ /**
20
+ * User.find(1, {
21
+ * success: function(user) {
22
+ * //on belongs to associations
23
+ * user.group.destroy();
24
+ * user.group.find({success: function(group) {}});
25
+ * user.group.set(someGroupInstance); //someGroupInstance must be a saved record (e.g. have an ID)
26
+ *
27
+ * //on has many associations
28
+ * user.posts.destroy(1);
29
+ * user.posts.find({id: 1, conditions: [{field: 'title', comparator: '=', value: 'some title'}]}, options);
30
+ * user.posts.create(data, options)
31
+ * user.posts.build(data)
32
+ * }
33
+ * };
34
+ */
35
+
36
+ // ExtMVC.Model.define('User', {
37
+ // fields: [],
38
+ // belongsTo: "Group",
39
+ // hasMany: [{
40
+ // name: 'posts',
41
+ // className: 'Post',
42
+ // foreignKey: 'user_id',
43
+ //
44
+ // extend: {
45
+ // //some functions
46
+ // }
47
+ // }]
48
+ // });
49
+ //
50
+ // user.posts.find(1, {
51
+ // success: function() {},
52
+ // failure: function() {}
53
+ // });
54
+ //
55
+ // user.posts.create({}, {
56
+ // success: function() {},
57
+ // failure: function() {}
58
+ // });
59
+ //
60
+ // user.posts.build({});
61
+ //
62
+ // user.posts.loaded();
63
+ // user.posts.count();
64
+ // user.posts.destroy(1);
65
+ //
66
+ // ExtMVC.Model.define('Post', {
67
+ // fields: [],
68
+ // belongsTo: [{
69
+ // name: 'user',
70
+ // className: 'User',
71
+ // foreignKey: 'user_id',
72
+ //
73
+ // extend: {
74
+ // //some functions
75
+ // }
76
+ // }],
77
+ // hasMany: 'Comment'
78
+ // });
79
+ //
80
+ // post.user.find();
81
+ // post.user.loaded();
82
+ // post.user.destroy();
83
+ //
84
+ // ExtMVC.Model.define('Comment', {
85
+ // fields: [],
86
+ // belongsTo: "Post"
87
+ // });
@@ -0,0 +1,136 @@
1
+ /**
2
+ * @class ExtMVC.model.plugin.validation
3
+ * @ignore
4
+ */
5
+
6
+ /**
7
+ * @class ExtMVC.model.plugin.validation.Errors
8
+ * Simple class to collect validation errors on a model and return them in various formats. Usage:
9
+ <pre><code>
10
+ ExtMVC.model.define("SomeModel", {
11
+ fields: [
12
+ {name: 'title', type: 'string'},
13
+ {name: 'price', type: 'int'},
14
+ {name: 'stock', type: 'int'},
15
+ {name: 'colour', type: 'string'}
16
+ ],
17
+
18
+ validatesPresenceOf : ["title", "price"],
19
+ validatesLengthOf : {field: 'title', minimum: 3, maximum: 12}
20
+ });
21
+
22
+ var s = new SomeModel({title: 'Some really long title'});
23
+ s.errors; //returns this Errors object
24
+ s.isValid(); //returns false, same as calling s.errors.isValid()
25
+
26
+ //manually add a validation error on the title field
27
+ s.errors.add('title', 'has an problem of some kind');
28
+
29
+ this.errors.forField('title'); //returns an array of problems with the title field
30
+
31
+ this.errors.forForm(); //returns an object suitable for marking fields invalid on a form
32
+ </code></pre>
33
+ */
34
+ ExtMVC.model.plugin.validation.Errors = function() {
35
+ /**
36
+ * @property errors
37
+ * @type Object
38
+ * Object containing all errors attached to this model. This is READ ONLY - do not interact with directly
39
+ */
40
+ this.all = {};
41
+ };
42
+
43
+ ExtMVC.model.plugin.validation.Errors.prototype = {
44
+
45
+ /**
46
+ * Returns an errors object suitable for applying to a form via BasicForm's markInvalid() method
47
+ * @return {Object} An object with field IDs as keys and formatted error strings as values
48
+ */
49
+ forForm: function() {
50
+ var formErrors = {};
51
+ for (key in this.all) {
52
+ formErrors[key] = this.forField(key, true);
53
+ }
54
+
55
+ return formErrors;
56
+ },
57
+
58
+ /**
59
+ * @property multipleErrorConnector
60
+ * @type String
61
+ * The string to use when connecting more than one error (defaults to 'and')
62
+ */
63
+ multipleErrorConnector: 'and',
64
+
65
+ /**
66
+ * Clears out all errors
67
+ */
68
+ clear: function() {
69
+ this.all = {};
70
+ },
71
+
72
+ /**
73
+ * Adds an error to a particular field
74
+ * @param {String} field The field to add an error onto
75
+ * @param {String} error The error message
76
+ */
77
+ add: function(field, error) {
78
+ this.all[field] = this.all[field] || [];
79
+ this.all[field].push(error);
80
+ },
81
+
82
+ /**
83
+ * Returns an array of all errors for the given field. Pass true as a second argument to
84
+ * return a human-readable string, e.g.:
85
+ <pre><code>
86
+ forField('title'); // ['must be present', 'is too short']
87
+ forField('title', true); // 'must be present and is too short'
88
+ </code></pre>
89
+ * @param {String} field The field to find errors for
90
+ * @param {Boolean} humanize True to turn the errors array into a human-readable string (defaults to false)
91
+ * @return {Array|String} An array of errors for this field, or a string
92
+ */
93
+ forField: function(field, humanize) {
94
+ humanize = humanize || false;
95
+ var errs = this.all[field] || [];
96
+
97
+ return humanize ? errs.toSentence(this.multipleErrorConnector) : errs;
98
+ },
99
+
100
+ /**
101
+ * Returns true if this model currently has no validation errors
102
+ * @return {Boolean} True if this model is currently valid
103
+ */
104
+ isValid: function(paramName) {
105
+ for (key in this.all) {return false;}
106
+
107
+ return true;
108
+ },
109
+
110
+ /**
111
+ * Parses server response to a failed save, adding each error message to the appropriate field. Override to provide
112
+ * an implementation for your own server responses. The default implementation accepts a response like this:
113
+ * {errors: [['some_field', 'some error regarding some_field'], ['another_field', 'another error']]}
114
+ * @param {Object/String} serverErrors A errors object returned by server-side validations. If this is a string it will
115
+ * @param {Boolean} preserveErrors False to clear all errors before adding errors from server (defaults to false)
116
+ * automatically be turned into an object via Ext.decode
117
+ */
118
+ readServerErrors: function(serverErrors, preserveErrors) {
119
+ var serverErrors = serverErrors || {};
120
+
121
+ //remove any existing errors unless instructed to preserve them
122
+ if (preserveErrors !== true) {this.clearErrors();}
123
+
124
+ //make sure we're dealing with an object
125
+ if (typeof(serverErrors) == 'string') {
126
+ serverErrors = Ext.decode(serverErrors);
127
+ };
128
+
129
+ var rawErrors = serverErrors.errors;
130
+ if (rawErrors) {
131
+ for (var i=0; i < rawErrors.length; i++) {
132
+ this.all.push(rawErrors[i]);
133
+ };
134
+ };
135
+ }
136
+ };
@@ -0,0 +1,139 @@
1
+ /**
2
+ * This is the Validation plugin definition, which mixes in validation.Errors
3
+ * and some other functions into a model prototype
4
+ * @ignore
5
+ */
6
+
7
+ /**
8
+ * Overrides Ext.data.Record's isValid() function.
9
+ * We apply this to Record's prototype as there is no need to define it per model or instance
10
+ * @ignore
11
+ */
12
+ Ext.apply(Ext.data.Record.prototype, {
13
+ isValid: function() {
14
+ if (this.validations) {
15
+ if (!this.errors) this.errors = new ExtMVC.model.plugin.validations.Errors();
16
+
17
+ this.errors.clear();
18
+
19
+ //test each validation, add to errors if any fail
20
+ Ext.each(this.validations, function(validation) {
21
+ if (!validation.isValid(this)) {
22
+ this.errors.add(validation.field, validation.message);
23
+ };
24
+ }, this);
25
+ };
26
+
27
+ return this.errors.isValid();
28
+ }
29
+ });
30
+
31
+ /**
32
+ * @ignore
33
+ * FIXME: This is possibly the most horrendous hack ever. I'm so sorry :(
34
+ *
35
+ * The basic problem is that we need to add an errors object to every Record instance,
36
+ * which means we need to hook into the constructor somehow. Sadly everything I tried
37
+ * to overload the constructor directly failed, so this horrific hack has been done instead
38
+ */
39
+ (function() {
40
+ var oldPrototype = Ext.data.Record.prototype,
41
+ oldConstructor = Ext.data.Record,
42
+ oldFunctionMethods = {};
43
+
44
+ for (var method in Ext.data.Record) {
45
+ oldFunctionMethods[method] = Ext.data.Record[method];
46
+ }
47
+
48
+ Ext.data.Record = function(data, id) {
49
+ oldConstructor.apply(this, arguments);
50
+
51
+ this.errors = new ExtMVC.model.plugin.validation.Errors();
52
+ };
53
+
54
+ for (var method in oldFunctionMethods) {
55
+ Ext.data.Record[method] = oldFunctionMethods[method];
56
+ }
57
+ })();
58
+ /**
59
+ * Again, I'm really sorry :(
60
+ * @ignore
61
+ */
62
+
63
+ /**
64
+ * @class ExtMVC.model.plugin.validation.Plugin
65
+ */
66
+ ExtMVC.model.plugin.validation.Plugin = {
67
+ /**
68
+ * Initializes this plugin for a given model. This is called every time a model is *created*
69
+ * via ExtMVC.model.create, not when a model object is *instantiated*
70
+ * @param {ExtMVC.model} model The model to initialize the plugin for
71
+ */
72
+ initialize: function(model) {
73
+ this.model = model;
74
+
75
+ Ext.apply(model.prototype, {
76
+ /**
77
+ * @property validations
78
+ * @type Array
79
+ * An array of all validations performed on this model
80
+ */
81
+ validations: this.parseValidations()
82
+ });
83
+ },
84
+
85
+ /**
86
+ * Parses a defined model's prototype for validation declarations and creates validation instances
87
+ * @return {Array} An Array of validation objects
88
+ */
89
+ parseValidations: function() {
90
+ var validations = [];
91
+
92
+ for (var validation in ExtMVC.model.plugin.validation) {
93
+ if (/^validate/.test(validation.toLowerCase())) {
94
+
95
+ //for each validation type defined on ExtMVC.model.plugin.validation, check to see if we are using
96
+ //it in on our model
97
+ for (var modelKey in this.model.prototype) {
98
+ if (modelKey.toLowerCase() == validation.toLowerCase()) {
99
+ //this validation is being used by the model, so add it now
100
+ var validationConstructor = ExtMVC.model.plugin.validation[validation],
101
+ validationOptions = this.model.prototype[modelKey];
102
+
103
+ if (!Ext.isArray(validationOptions)) {
104
+ validationOptions = [validationOptions];
105
+ };
106
+
107
+ Ext.each(validationOptions, function(options) {
108
+ validations.push(this.buildValidation(validationConstructor, options));
109
+ }, this);
110
+ };
111
+ }
112
+ };
113
+ }
114
+
115
+ return validations;
116
+ },
117
+
118
+ /**
119
+ * Creates a new Validation object based on the passed constructor and options
120
+ * @param {Function} validation The validation constructor function
121
+ * @param {Object|String} options A fieldname string, or config object
122
+ * @return {ExtMVC.model.plugin.validation.AbstractValidation} The validation instance
123
+ */
124
+ buildValidation: function(validation, options) {
125
+ var field, config = {};
126
+
127
+ if (typeof options == 'string') {
128
+ field = options;
129
+ } else {
130
+ field = options.field;
131
+ delete options.field;
132
+ config = options;
133
+ }
134
+
135
+ return new validation(this.model, field, config);
136
+ }
137
+ };
138
+
139
+ ExtMVC.model.addPlugin(ExtMVC.model.plugin.validation.Plugin);