extjs-mvc 0.3.10 → 0.4.0.a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (204) hide show
  1. data/LICENSE +1 -1
  2. data/README.rdoc +5 -173
  3. data/Rakefile +18 -20
  4. data/VERSION +1 -1
  5. data/bin/xmvc +13 -0
  6. data/lib/extjs-mvc/README +20 -0
  7. data/lib/extjs-mvc/builder.rb +83 -0
  8. data/lib/extjs-mvc/builders/all_builder.rb +15 -0
  9. data/lib/extjs-mvc/builders/app_builder.rb +36 -0
  10. data/lib/extjs-mvc/builders/base.rb +138 -0
  11. data/lib/extjs-mvc/builders/css_builder.rb +36 -0
  12. data/lib/extjs-mvc/builders/docs_builder.rb +20 -0
  13. data/lib/extjs-mvc/builders/mvc_builder.rb +33 -0
  14. data/lib/extjs-mvc/builders/plugin_builder.rb +53 -0
  15. data/lib/extjs-mvc/cli.rb +52 -0
  16. data/lib/extjs-mvc/generator.rb +79 -0
  17. data/lib/extjs-mvc/generators/app.rb +57 -0
  18. data/lib/extjs-mvc/generators/base.rb +53 -0
  19. data/lib/extjs-mvc/generators/controller.rb +41 -0
  20. data/lib/extjs-mvc/generators/model.rb +41 -0
  21. data/lib/extjs-mvc/generators/scaffold.rb +17 -0
  22. data/lib/extjs-mvc/generators/templates/Controller.js +17 -0
  23. data/lib/extjs-mvc/generators/templates/Model.js +9 -0
  24. data/lib/extjs-mvc/generators/templates/ModelSpec.js +12 -0
  25. data/lib/extjs-mvc/generators/templates/View.js +16 -0
  26. data/lib/extjs-mvc/generators/templates/_Action.js +7 -0
  27. data/lib/extjs-mvc/generators/templates/app/README.rdoc +152 -0
  28. data/lib/extjs-mvc/generators/templates/app/app/App.js +64 -0
  29. data/lib/extjs-mvc/generators/templates/app/app/controllers/ApplicationController.js +10 -0
  30. data/lib/extjs-mvc/generators/templates/app/app/controllers/HomeController.js +10 -0
  31. data/lib/extjs-mvc/generators/templates/app/app/views/home/Index.js +17 -0
  32. data/lib/extjs-mvc/generators/templates/app/app/views/layout/Menu.js +23 -0
  33. data/lib/extjs-mvc/generators/templates/app/config/application.js +1 -0
  34. data/lib/extjs-mvc/generators/templates/app/config/boot.js +67 -0
  35. data/lib/extjs-mvc/generators/templates/app/config/build.xml +77 -0
  36. data/lib/extjs-mvc/generators/templates/app/config/database.js +4 -0
  37. data/lib/extjs-mvc/generators/templates/app/config/environment.json +58 -0
  38. data/lib/extjs-mvc/generators/templates/app/config/environments/development.json +1 -0
  39. data/lib/extjs-mvc/generators/templates/app/config/environments/production.json +4 -0
  40. data/lib/extjs-mvc/generators/templates/app/config/routes.js +27 -0
  41. data/lib/extjs-mvc/generators/templates/app/config/settings.yml +3 -0
  42. data/lib/extjs-mvc/generators/templates/app/public/index.html +19 -0
  43. data/lib/extjs-mvc/generators/templates/app/public/stylesheets/ext-mvc-all.css +49 -0
  44. data/{test/active_record_test.rb → lib/extjs-mvc/generators/templates/app/spec/SpecHelper.js} +0 -0
  45. data/lib/extjs-mvc/generators/templates/app/spec/index.html +66 -0
  46. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/EXAMPLE.html +68 -0
  47. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/LICENSE +22 -0
  48. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/README.markdown +307 -0
  49. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/lib/jquery-1.2.3.js +3408 -0
  50. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/lib/jquery.fn.js +29 -0
  51. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/lib/jquery.print.js +108 -0
  52. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/lib/screw.assets.js +36 -0
  53. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/lib/screw.behaviors.js +91 -0
  54. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/lib/screw.builder.js +80 -0
  55. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/lib/screw.css +90 -0
  56. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/lib/screw.events.js +42 -0
  57. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/lib/screw.matchers.js +145 -0
  58. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/lib/screw.server.js +21 -0
  59. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/spec/behaviors_spec.js +178 -0
  60. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/spec/matchers_spec.js +237 -0
  61. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/spec/print_spec.js +119 -0
  62. data/{test/controller_test.rb → lib/extjs-mvc/generators/templates/app/vendor/screw-unit/spec/spec_helper.js} +0 -0
  63. data/lib/extjs-mvc/generators/templates/app/vendor/screw-unit/spec/suite.html +18 -0
  64. data/lib/extjs-mvc/generators/templates/scaffold/ScaffoldController.js +18 -0
  65. data/lib/extjs-mvc/generators/view.rb +29 -0
  66. data/lib/extjs-mvc/plugin.rb +105 -0
  67. data/lib/extjs-mvc/scripts.rb +15 -0
  68. data/lib/extjs-mvc/settings.rb +75 -0
  69. data/lib/extjs-mvc/setup.rb +5 -0
  70. data/lib/extjs-mvc/stats.rb +241 -0
  71. data/lib/extjs-mvc/test.rb +11 -0
  72. data/lib/extjs-mvc/testserver.ru +95 -0
  73. data/lib/extjs-mvc/ui.rb +58 -0
  74. data/lib/extjs-mvc/update.rb +73 -0
  75. data/lib/extjs-mvc.rb +70 -32
  76. data/lib/js/App.js +219 -0
  77. data/lib/js/MVC.js +260 -0
  78. data/lib/js/Presenter.js +52 -0
  79. data/lib/js/README.rdoc +69 -0
  80. data/lib/js/build +21 -0
  81. data/lib/js/controller/Controller.js +278 -0
  82. data/lib/js/controller/CrudController.js +460 -0
  83. data/lib/js/lib/Array.js +26 -0
  84. data/lib/js/lib/Booter.js +415 -0
  85. data/lib/js/lib/ClassManager.js +191 -0
  86. data/lib/js/lib/ControllerClassManager.js +95 -0
  87. data/lib/js/lib/Dependencies.js +44 -0
  88. data/lib/js/lib/DispatchMatcher.js +98 -0
  89. data/lib/js/lib/Dispatcher.js +129 -0
  90. data/lib/js/lib/Environment.js +43 -0
  91. data/lib/js/lib/Inflector.js +138 -0
  92. data/lib/js/lib/ModelClassManager.js +19 -0
  93. data/lib/js/lib/Route.js +139 -0
  94. data/lib/js/lib/Router.js +282 -0
  95. data/lib/js/lib/String.js +94 -0
  96. data/lib/js/lib/ViewClassManager.js +229 -0
  97. data/lib/js/lib/notes.txt +32 -0
  98. data/lib/js/model/AdapterManager.js +30 -0
  99. data/lib/js/model/Association.js +26 -0
  100. data/lib/js/model/Base.js +63 -0
  101. data/lib/js/model/BelongsToAssociation.js +116 -0
  102. data/lib/js/model/Cache.js +131 -0
  103. data/lib/js/model/HasManyAssociation.js +160 -0
  104. data/lib/js/model/Model.js +331 -0
  105. data/lib/js/model/UrlBuilder.js +106 -0
  106. data/lib/js/model/adapters/AbstractAdapter.js +296 -0
  107. data/lib/js/model/adapters/MemoryAdapter.js +103 -0
  108. data/lib/js/model/adapters/RESTAdapter.js +345 -0
  109. data/lib/js/model/adapters/RESTJSONAdapter.js +68 -0
  110. data/lib/js/model/adapters/notes.txt +42 -0
  111. data/lib/js/model/associations/Association.js +192 -0
  112. data/lib/js/model/associations/notes.txt +87 -0
  113. data/lib/js/model/validations/Errors.js +136 -0
  114. data/lib/js/model/validations/Plugin.js +139 -0
  115. data/lib/js/model/validations/Validations.js +276 -0
  116. data/lib/js/notes/Charts.graffle +0 -0
  117. data/lib/js/overrides/Ext.Component.js +21 -0
  118. data/lib/js/overrides/Ext.extend.js +142 -0
  119. data/lib/js/spec/Array.spec.js +15 -0
  120. data/lib/js/spec/ExtMVC.spec.js +65 -0
  121. data/lib/js/spec/Model.spec.js +370 -0
  122. data/lib/js/spec/OS.spec.js +83 -0
  123. data/lib/js/spec/Router.spec.js +99 -0
  124. data/lib/js/spec/SpecHelper.js +106 -0
  125. data/lib/js/spec/String.spec.js +83 -0
  126. data/lib/js/spec/model/AbstractAdapter.spec.js +49 -0
  127. data/lib/js/spec/model/Associations.spec.js +99 -0
  128. data/lib/js/spec/model/Cache.spec.js +5 -0
  129. data/lib/js/spec/model/RESTAdapter.spec.js +19 -0
  130. data/lib/js/spec/model/ValidationErrors.spec.js +64 -0
  131. data/lib/js/spec/model/Validations.spec.js +166 -0
  132. data/lib/js/spec/model/ValidationsPlugin.spec.js +108 -0
  133. data/lib/js/spec/suite.html +60 -0
  134. data/lib/js/specs-old/JSSpec.css +216 -0
  135. data/lib/js/specs-old/JSSpec.js +1512 -0
  136. data/lib/js/specs-old/all.html +66 -0
  137. data/lib/js/specs-old/base.js +14 -0
  138. data/lib/js/specs-old/controller.js +17 -0
  139. data/lib/js/specs-old/diff_match_patch.js +1 -0
  140. data/lib/js/specs-old/model.js +70 -0
  141. data/lib/js/specs-old/route.js +38 -0
  142. data/lib/js/specs-old/router.js +59 -0
  143. data/lib/js/specs-old/string.js +22 -0
  144. data/lib/js/testrunner/JSpecFormatter.js +111 -0
  145. data/lib/js/testrunner/TestClient.js +181 -0
  146. data/lib/js/testrunner/TestGrid.js +351 -0
  147. data/lib/js/testrunner/TestRunner.js +110 -0
  148. data/lib/js/testrunner/TestViewport.js +94 -0
  149. data/lib/js/vendor/screw-unit/EXAMPLE.html +68 -0
  150. data/lib/js/vendor/screw-unit/LICENSE +22 -0
  151. data/lib/js/vendor/screw-unit/README.markdown +307 -0
  152. data/lib/js/vendor/screw-unit/lib/jquery-1.2.3.js +3408 -0
  153. data/lib/js/vendor/screw-unit/lib/jquery.fn.js +29 -0
  154. data/lib/js/vendor/screw-unit/lib/jquery.print.js +108 -0
  155. data/lib/js/vendor/screw-unit/lib/screw.assets.js +36 -0
  156. data/lib/js/vendor/screw-unit/lib/screw.behaviors.js +91 -0
  157. data/lib/js/vendor/screw-unit/lib/screw.builder.js +80 -0
  158. data/lib/js/vendor/screw-unit/lib/screw.css +91 -0
  159. data/lib/js/vendor/screw-unit/lib/screw.events.js +42 -0
  160. data/lib/js/vendor/screw-unit/lib/screw.matchers.js +145 -0
  161. data/lib/js/vendor/screw-unit/lib/screw.server.js +21 -0
  162. data/lib/js/vendor/screw-unit/spec/behaviors_spec.js +178 -0
  163. data/lib/js/vendor/screw-unit/spec/matchers_spec.js +237 -0
  164. data/lib/js/vendor/screw-unit/spec/print_spec.js +119 -0
  165. data/{test/data_mapper_test.rb → lib/js/vendor/screw-unit/spec/spec_helper.js} +0 -0
  166. data/lib/js/vendor/screw-unit/spec/suite.html +18 -0
  167. data/lib/js/view/FormWindow.js +184 -0
  168. data/lib/js/view/HasManyEditorGridPanel.js +211 -0
  169. data/lib/js/view/scaffold/Edit.js +46 -0
  170. data/lib/js/view/scaffold/Index.js +561 -0
  171. data/lib/js/view/scaffold/New.js +20 -0
  172. data/lib/js/view/scaffold/ScaffoldFormPanel.js +255 -0
  173. data/test/helper.rb +10 -0
  174. data/test/test_extjs-mvc-gem.rb +7 -0
  175. metadata +232 -56
  176. data/README +0 -12
  177. data/lib/controller/controller.rb +0 -28
  178. data/lib/core_ext/array/extract_options.rb +0 -15
  179. data/lib/extjs/component.rb +0 -71
  180. data/lib/extjs/data/store.rb +0 -131
  181. data/lib/helpers/component.rb +0 -37
  182. data/lib/helpers/store.rb +0 -7
  183. data/lib/model/active_record.rb +0 -89
  184. data/lib/model/base.rb +0 -370
  185. data/lib/model/data_mapper.rb +0 -66
  186. data/lib/model/mongo_mapper.rb +0 -64
  187. data/lib/test/macros.rb +0 -20
  188. data/test/app/config/application.rb +0 -70
  189. data/test/app/config/database.yml +0 -3
  190. data/test/app/db/schema.rb +0 -75
  191. data/test/app/models/active_record/address.rb +0 -4
  192. data/test/app/models/active_record/data_type.rb +0 -3
  193. data/test/app/models/active_record/group.rb +0 -4
  194. data/test/app/models/active_record/house.rb +0 -4
  195. data/test/app/models/active_record/location.rb +0 -5
  196. data/test/app/models/active_record/person.rb +0 -4
  197. data/test/app/models/active_record/user.rb +0 -6
  198. data/test/app/models/active_record/user_group.rb +0 -4
  199. data/test/component_test.rb +0 -15
  200. data/test/debug.log +0 -2969
  201. data/test/model_test.rb +0 -526
  202. data/test/mongo_mapper_test.rb +0 -0
  203. data/test/store_test.rb +0 -0
  204. data/test/test_helper.rb +0 -32
@@ -0,0 +1,26 @@
1
+ /**
2
+ * class ExtMVC.model.Association
3
+ * @ignore
4
+ */
5
+ ExtMVC.model.Association = {
6
+
7
+ /**
8
+ * @ignore
9
+ * Returns the default association name for a given class (e.g. "Post" becomes "posts", "SecretAgent" becomes "secretAgents" etc)
10
+ * @param {String} className The string name of the class which this belongs to
11
+ * @return {String} The association name for this class
12
+ */
13
+ hasManyAssociationName: function(className) {
14
+ return className.toLowerCase() + 's';
15
+ },
16
+
17
+ /**
18
+ * @ignore
19
+ * Returns the default association name for a given class (e.g. "Post" becomes "post", "SecretAgent" becomes "secretAgent")
20
+ * @param {String} className The string name of the class to calculate a belongsTo name for
21
+ * @return {String} The association name for this class
22
+ */
23
+ belongsToAssociationName: function(className) {
24
+ return className.toLowerCase();
25
+ }
26
+ };
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @class ExtMVC.model.Base
3
+ * A set of properties and functions which are applied to all ExtMVC.models when they are defined
4
+ */
5
+ ExtMVC.model.Base = function() {};
6
+
7
+ ExtMVC.model.Base.prototype = {
8
+
9
+ /**
10
+ * @property primaryKey
11
+ * @type String
12
+ * The name of the field assumed to be the primary key (defaults to 'id')
13
+ */
14
+ primaryKey: 'id',
15
+
16
+ /**
17
+ * Returns true if this model's primaryKey has not yet been set (i.e. it has not been saved yet)
18
+ * @return {Boolean} True if this model's primaryKey has not yet been set
19
+ */
20
+ newRecord: function() {
21
+ var id = this.get(this.primaryKey);
22
+ return typeof id == 'undefined' || id == '';
23
+ },
24
+
25
+ /**
26
+ * Returns a unique string for a model instance, suitable for use as a key in a cache (e.g. ExtMVC.model.Cache).
27
+ * new User({id: 123}).MVCModelId(); //'user-123'
28
+ * @return {String} The unique key for this model object
29
+ */
30
+ MVCModelId: function() {
31
+ return String.format("{0}-{1}", this.tableName, this.get(this.primaryKey));
32
+ },
33
+
34
+ /**
35
+ * Returns a JsonReader suitable for use decoding generic JSON data from a server response
36
+ * Override this to provide your own Reader
37
+ */
38
+ getReader: function() {
39
+ if (!this.reader) {
40
+ this.reader = new Ext.data.JsonReader({
41
+ totalProperty: "results",
42
+ root: this.tableName
43
+ }, this.constructor);
44
+ }
45
+
46
+ return this.reader;
47
+ },
48
+
49
+ /**
50
+ * @property initialize
51
+ * @type Function
52
+ * Function which is called whenever a model object is instantiated. Override this with your own callback if needed
53
+ */
54
+ initialize: Ext.emptyFn
55
+ };
56
+
57
+
58
+ /**
59
+ * @ignore
60
+ * Add the above Base methods and properties to the Ext.data.Record prototype. This means all Record instances
61
+ * will have MVC models methods, even if not instantiated by an MVC-defined model constructor
62
+ */
63
+ Ext.apply(Ext.data.Record.prototype, new ExtMVC.model.Base());
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @class ExtMVC.model.BelongsToAssociation
3
+ * @extends ExtMVC.model.Association
4
+ */
5
+ ExtMVC.model.BelongsToAssociation = function(ownerObject, config) {
6
+ var config = config || {};
7
+
8
+ Ext.applyIf(config, {
9
+ associationName: ExtMVC.model.Association.belongsToAssociationName(config.name)
10
+ });
11
+
12
+ //TODO: these should be abstracted to a parent object (as should private vars and funcs below)
13
+ Ext.applyIf(config, {
14
+ primaryKey: 'id',
15
+ foreignKey: ownerObject.foreignKeyName,
16
+ extend: {},
17
+
18
+ className: (ownerObject.constructor.namespace ? ownerObject.constructor.namespace + '.' + config.name : config.name)
19
+ });
20
+
21
+ //get a reference to the class definition function of the associated object
22
+ //(e.g. a hasMany: ['Post'] association will return a reference to Post)
23
+ var associatedObjectClass = eval(config.className);
24
+
25
+ /**
26
+ * Private, calls the ownerObject's class method with the supplied args
27
+ */
28
+ function callOwnerObjectClassMethod(method, args, scope) {
29
+ return ownerObject.constructor[method].apply(scope || ownerObject.constructor, args || []);
30
+ };
31
+
32
+ /**
33
+ * Private, calls the associated object's class method with the supplied args
34
+ */
35
+ function callAssociatedObjectClassMethod (method, args, scope) {
36
+ return associatedObjectClass[method].apply(scope || associatedObjectClass, args || []);
37
+ };
38
+
39
+ return {
40
+ /**
41
+ * @property associationName
42
+ * @type String
43
+ * Returns the name of this association so that the model can add it to its definition
44
+ */
45
+ associationName: config.associationName,
46
+
47
+ /**
48
+ * @property associationClass
49
+ * @type ExtMVC.model
50
+ * A reference to the association's class (e.g. belongsTo: "Post" would have associationClass of Post)
51
+ */
52
+ associationClass: associatedObjectClass,
53
+
54
+ /**
55
+ * @property associationType
56
+ * @type String
57
+ * The type of association (hasMany or belongsTo)
58
+ */
59
+ associationType: 'belongsTo',
60
+
61
+ /**
62
+ * @property lastFetched
63
+ * @type Date
64
+ * Date object representing the last time the associated object was successfully fetched
65
+ */
66
+ lastFetched: null,
67
+
68
+ /**
69
+ * Sets the associated model for this association to the specified model object
70
+ * @param {ExtMVC.model} modelObject The associated model to set this belongsTo association to
71
+ */
72
+ set: function(modelObject) {
73
+ this.lastFetched = new Date();
74
+ this.cachedObject = modelObject;
75
+
76
+ //add the foreign key automatically
77
+ ownerObject.data[modelObject.foreignKeyName] = modelObject.data[config.primaryKey];
78
+ },
79
+
80
+ /**
81
+ * Gets the associated model for this association
82
+ * @param {Object} options Options to pass through to the Ajax load request
83
+ * @param {Number} cacheFor If the object has been retrieved less than this number of seconds ago, use the cached object
84
+ */
85
+ get: function(options, cacheFor) {
86
+ var options = options || {};
87
+ var cacheFor = cacheFor || 0;
88
+
89
+ Ext.applyIf(options, {
90
+ loadSuccess: Ext.emptyFn,
91
+ loadFailure: Ext.emptyFn
92
+ });
93
+
94
+ var cacheIsCurrent = (((new Date() - this.lastFetched) / 1000) < cacheFor) || (cacheFor == -1);
95
+
96
+ if (this.lastFetched && this.cachedObject && cacheIsCurrent) {
97
+ //return the cached object via a callback
98
+ options.loadSuccess.call(options.scope || this, this.cachedObject);
99
+
100
+ //also return via normal return if this is a cached object. This allows some functions to use the cached object
101
+ //without the overhead of setting up a callback, so long as they first check that the object has been fetched
102
+ return this.cachedObject;
103
+ } else {
104
+ //inject caching code before loadSuccess - caches the object into this.cachedObject and sets this.lastFetched to now
105
+ Ext.apply(options, {
106
+ loadSuccess: options.loadSuccess.createInterceptor(function(obj) {
107
+ this.cachedObject = obj;
108
+ this.lastFetched = new Date();
109
+ }, this)
110
+ });
111
+
112
+ return callAssociatedObjectClassMethod('findById', [1, options]);
113
+ };
114
+ }
115
+ };
116
+ };
@@ -0,0 +1,131 @@
1
+ /**
2
+ * @class ExtMVC.model.Cache
3
+ * @extends Ext.util.Observable
4
+ * Provides an interface for caching model objects which have been fetched from some database/backend
5
+ */
6
+ ExtMVC.model.Cache = function(config) {
7
+ var config = config || {};
8
+
9
+ ExtMVC.model.Cache.superclass.constructor.call(this, config);
10
+
11
+ this.addEvents(
12
+ /**
13
+ * @event beforeadd
14
+ * Fires before an item is added to the cache
15
+ * @param {ExtMVC.model} modelObject The model which is about to be added
16
+ */
17
+ 'beforeadd',
18
+
19
+ /**
20
+ * @event add
21
+ * Fires after an item is added to the cache
22
+ * @param {ExtMVC.model} modelObject The model which was just added
23
+ */
24
+ 'add',
25
+
26
+ /**
27
+ * @event beforeclear
28
+ * Fires before the cache is cleared
29
+ * @param {Number} seconds The number of seconds worth of caches which will be saved
30
+ */
31
+ 'beforeclear',
32
+
33
+ /**
34
+ * @event clear
35
+ * Fires after the cache has been cleared
36
+ * @param {Number} seconds The number of seconds worth of caches which were saved
37
+ */
38
+ 'clear'
39
+ );
40
+ };
41
+
42
+ Ext.extend(ExtMVC.model.Cache, Ext.util.Observable, {
43
+
44
+ /**
45
+ * @property caches
46
+ * @type Object
47
+ * Maintains all cached objects
48
+ */
49
+ caches: {},
50
+
51
+ /**
52
+ * Adds the given model object to the cache. Automatically stores the datetime of the add
53
+ * @param {ExtMVC.model} modelObject The model you want to store in the cache
54
+ */
55
+ add: function(modelObject) {
56
+ if (this.fireEvent('beforeadd', modelObject)) {
57
+ var modelName = modelObject.className;
58
+ var modelId = modelObject.data.id;
59
+
60
+ if (modelName && modelId) {
61
+ modelObject.cachedAt = new Date();
62
+
63
+ this.caches[modelName] = this.caches[modelName] || {};
64
+ this.caches[modelName][modelId] = modelObject;
65
+
66
+ this.fireEvent('add', modelObject);
67
+ return true;
68
+ } else {
69
+
70
+ return false;
71
+ };
72
+ }
73
+ },
74
+
75
+ /**
76
+ * Fetches an object from the cache
77
+ * @param {Object} params params object which must contain at least modelName and id. Optionally
78
+ * supply staleTime, which is the number of seconds old the cached object is allowed to be to get a hit,
79
+ * or a Date which will restrict hits to anything cached after that date
80
+ * @return {ExtMVC.model/null} The model if found, or null
81
+ */
82
+ fetch: function(params) {
83
+ this.caches[params['modelName']] = this.caches[params['modelName']] || {};
84
+
85
+ var params = params || {};
86
+ var hit = this.caches[params['modelName']][params['id']];
87
+
88
+ if (hit) {
89
+ if (params.staleTime) {
90
+ if (typeof params.staleTime == 'number') {
91
+ var date = new Date();
92
+ date.setTime(date.getTime() - (1000 * params.staleTime));
93
+ params.staleTime = date;
94
+ };
95
+
96
+ //make sure we have a date object
97
+ if (params.staleTime.getTime && hit.cachedAt > params.staleTime) {
98
+ return hit;
99
+ }
100
+ } else {
101
+ return hit;
102
+ };
103
+ };
104
+ },
105
+
106
+ /**
107
+ * Clears all objects more than the given number of seconds old (defaults to clearing all objects)
108
+ * @param {Number} seconds The number of seconds to keep cached objects for (e.g. setting this to 10 would delete anything cached more than 10 seconds ago)
109
+ */
110
+ clear: function(seconds) {
111
+ var seconds = seconds || 0;
112
+ var date = new Date();
113
+ date.setTime(date.getTime() - (1000 * seconds));
114
+
115
+ if (this.fireEvent('beforeclear', seconds)) {
116
+ if (seconds == 0) {
117
+ this.caches = {};
118
+ } else {
119
+ for (var i=0; i < this.caches.length; i++) {
120
+ for (var j=0; j < this.caches[i].length; j++) {
121
+ if (this.caches[i][j].cachedAt < date) {
122
+ delete this.caches[i][j];
123
+ };
124
+ };
125
+ };
126
+ };
127
+
128
+ this.fireEvent('clear', seconds);
129
+ }
130
+ }
131
+ });
@@ -0,0 +1,160 @@
1
+ /**
2
+ * @class ExtMVC.model.HasManyAssociation
3
+ * @extends ExtMVC.model.Association
4
+ */
5
+ ExtMVC.model.HasManyAssociation = function(ownerObject, config) {
6
+ var config = config || {};
7
+
8
+ Ext.applyIf(config, {
9
+ offset: 0,
10
+ limit: 25,
11
+ associationName: ExtMVC.model.Association.hasManyAssociationName(config.name)
12
+ });
13
+
14
+ //TODO: these should be abstracted to a parent object (as should private vars and funcs below)
15
+ Ext.applyIf(config, {
16
+ primaryKey: 'id',
17
+ foreignKey: ownerObject.foreignKeyName,
18
+ extend: {},
19
+
20
+ className: (ownerObject.constructor.namespace ? ownerObject.constructor.namespace + '.' + config.name : config.name)
21
+ });
22
+
23
+ //get a reference to the class definition function of the associated object
24
+ //(e.g. a hasMany: ['Post'] association will return a reference to Post)
25
+ var associatedObjectClass = eval(config.className);
26
+
27
+ /**
28
+ * Private, calls the ownerObject's class method with the supplied args
29
+ */
30
+ function callOwnerObjectClassMethod(method, args, scope) {
31
+ return ownerObject.constructor[method].apply(scope || ownerObject.constructor, args || []);
32
+ };
33
+
34
+ /**
35
+ * Private, calls the associated object's class method with the supplied args
36
+ */
37
+ function callAssociatedObjectClassMethod (method, args, scope) {
38
+ return associatedObjectClass[method].apply(scope || associatedObjectClass, args || []);
39
+ }
40
+
41
+ return Ext.applyIf(config.extend, {
42
+
43
+ /**
44
+ * @property associationName
45
+ * @type String
46
+ * Returns the name of this association so that the model can add it to its definition
47
+ */
48
+ associationName: config.associationName,
49
+
50
+ /**
51
+ * @property associationType
52
+ * @type String
53
+ * The type of association (hasMany or belongsTo)
54
+ */
55
+ associationType: 'hasMany',
56
+
57
+ /**
58
+ * Returns the URL for this association - e.g. if model User hasMany Posts, user.posts.url() will
59
+ * return something like /users/1/posts.
60
+ * Pass additional parameter options as arguments to pass straight through to the URL builder - e.g.:
61
+ * user.posts.url('published'); // = /users/1/posts/published
62
+ * @return {String} The URL for the has many resource
63
+ */
64
+ url: function() {
65
+ var args = [ownerObject, associatedObjectClass];
66
+ for (var i=0; i < arguments.length; i++) {
67
+ args.push(arguments[i]);
68
+ };
69
+
70
+ return ExtMVC.UrlBuilder.urlFor.apply(ExtMVC.UrlBuilder, args);
71
+ },
72
+
73
+ /**
74
+ * Passes through to the owner class's findById class method, adding a foreign key constraint first
75
+ * @param {String} id The ID of the associated object to retrieve
76
+ * @param {Object} options Options passed along to the associated model's Class's findById method. Pass in loadSuccess, loadFailure, conditions, order etc here
77
+ */
78
+ findById: function(id, options) {
79
+ var options = options || {};
80
+
81
+ //add a condition to constrain to the owner object's id
82
+ if (!options.conditions) { options.conditions = []; }
83
+ options.conditions.push({
84
+ key: config.foreignKey,
85
+ value: ownerObject.data[config.primaryKey]
86
+ });
87
+
88
+ return callAssociatedObjectClassMethod('findById', [id, options]);
89
+ },
90
+
91
+ findAll: function(storeOptions) {
92
+ var storeOptions = storeOptions || {};
93
+ Ext.applyIf(storeOptions, {
94
+ url: this.url(),
95
+
96
+ listeners: {
97
+
98
+ //Once we have fetched all hasMany records, make sure newRecord is false, and set the parent
99
+ //relationship to point to this ownerObject (The object which hasMany of these records)
100
+ 'load': {
101
+ scope: this,
102
+ fn: function(store, records, options) {
103
+ Ext.each(records, function(record) {
104
+ record.newRecord = false;
105
+ if (record.parent && record.parent.set) {
106
+ record.parent.set(ownerObject);
107
+ }
108
+ }, this);
109
+ }
110
+ }
111
+ }
112
+ });
113
+
114
+ return callAssociatedObjectClassMethod('findAll', [storeOptions]);
115
+ },
116
+
117
+ /**
118
+ * Creates (builds and attempts to save) this associated model
119
+ * @param {Object} fields Object with keys and values to initialise this object
120
+ * @param {Object} saveConfig Passed to the Ext.Ajax request, supply success and failure options here
121
+ */
122
+ create: function(fields, saveConfig) {
123
+ return this.build(fields).save(saveConfig);
124
+ },
125
+
126
+ /**
127
+ * Builds an instantiation of the associated model with the supplied data.
128
+ * Automatically links in the correct foreign key
129
+ * @param {Object} fields The data to initialize this object with
130
+ */
131
+ build: function(fields) {
132
+ var fields = fields || {};
133
+
134
+ //instantiate the new object with the augmented fields
135
+ var obj = new associatedObjectClass(fields);
136
+
137
+ //set up the object's belongsTo association. This also sets up the foreign key
138
+ var assocName = ExtMVC.model.Association.belongsToAssociationName(ownerObject.className);
139
+ obj[assocName].set(ownerObject);
140
+
141
+ return obj;
142
+ },
143
+
144
+ /**
145
+ * Adds an existing (saved) instantiation of the associated model to this model's hasMany collection
146
+ * @param {ExtMVC.model} modelObject The existing, saved model
147
+ */
148
+ add: function(modelObject) {
149
+ //TODO: implement this
150
+
151
+ },
152
+
153
+ destroy: function(id) {
154
+ //TODO: implement this
155
+
156
+ }
157
+ });
158
+ };
159
+
160
+ // Ext.extend(ExtMVC.model.HasManyAssociation, ExtMVC.model.Association);