extjs_scaffold 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. data/.gitignore +9 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +80 -0
  6. data/Rakefile +28 -0
  7. data/extjs_scaffold.gemspec +27 -0
  8. data/features/scaffold_generator.feature +124 -0
  9. data/features/step_definitions/aruba_ext_steps.rb +6 -0
  10. data/features/support/setup.rb +5 -0
  11. data/lib/extjs_scaffold.rb +1 -0
  12. data/lib/extjs_scaffold/version.rb +3 -0
  13. data/lib/generators/extjs_scaffold.rb +15 -0
  14. data/lib/generators/extjs_scaffold/install/USAGE +28 -0
  15. data/lib/generators/extjs_scaffold/install/install_generator.rb +82 -0
  16. data/lib/generators/extjs_scaffold/install/templates/Actionable.js +27 -0
  17. data/lib/generators/extjs_scaffold/install/templates/App.js +11 -0
  18. data/lib/generators/extjs_scaffold/install/templates/EditWindow.js +135 -0
  19. data/lib/generators/extjs_scaffold/install/templates/FormPanel.js +27 -0
  20. data/lib/generators/extjs_scaffold/install/templates/Format.js +34 -0
  21. data/lib/generators/extjs_scaffold/install/templates/GridPanel.js +289 -0
  22. data/lib/generators/extjs_scaffold/install/templates/ParentComboField.js +39 -0
  23. data/lib/generators/extjs_scaffold/install/templates/Rails.js +162 -0
  24. data/lib/generators/extjs_scaffold/install/templates/ScrollingToolbar.js +170 -0
  25. data/lib/generators/extjs_scaffold/install/templates/SearchField.js +104 -0
  26. data/lib/generators/extjs_scaffold/install/templates/UpdateWindow.js +112 -0
  27. data/lib/generators/extjs_scaffold/install/templates/Updateable.js +31 -0
  28. data/lib/generators/extjs_scaffold/install/templates/ext_auth.html.erb +3 -0
  29. data/lib/generators/extjs_scaffold/install/templates/extjs_scaffold.css.scss +9 -0
  30. data/lib/generators/extjs_scaffold/install/templates/images/README.txt +1 -0
  31. data/lib/generators/extjs_scaffold/install/templates/images/add.gif +0 -0
  32. data/lib/generators/extjs_scaffold/install/templates/images/application_form_edit.png +0 -0
  33. data/lib/generators/extjs_scaffold/install/templates/images/delete.gif +0 -0
  34. data/lib/generators/extjs_scaffold/install/templates/images/tick.png +0 -0
  35. data/lib/generators/extjs_scaffold/scaffold/USAGE +24 -0
  36. data/lib/generators/extjs_scaffold/scaffold/scaffold_generator.rb +19 -0
  37. data/lib/generators/extjs_scaffold/scaffold_controller/USAGE +20 -0
  38. data/lib/generators/extjs_scaffold/scaffold_controller/scaffold_controller_generator.rb +296 -0
  39. data/lib/generators/extjs_scaffold/scaffold_controller/templates/controller.rb +191 -0
  40. data/lib/generators/extjs_scaffold/scaffold_controller/templates/js/Controller.js +53 -0
  41. data/lib/generators/extjs_scaffold/scaffold_controller/templates/js/EditForm.js +17 -0
  42. data/lib/generators/extjs_scaffold/scaffold_controller/templates/js/EditWindow.js +27 -0
  43. data/lib/generators/extjs_scaffold/scaffold_controller/templates/js/Grid.js +26 -0
  44. data/lib/generators/extjs_scaffold/scaffold_controller/templates/js/Model.js +43 -0
  45. data/lib/generators/extjs_scaffold/scaffold_controller/templates/js/ReferenceStore.js +27 -0
  46. data/lib/generators/extjs_scaffold/scaffold_controller/templates/js/Store.js +28 -0
  47. data/lib/generators/extjs_scaffold/scaffold_controller/templates/js/UpdateForm.js +17 -0
  48. data/lib/generators/extjs_scaffold/scaffold_controller/templates/js/UpdateWindow.js +21 -0
  49. data/lib/generators/extjs_scaffold/scaffold_controller/templates/model.rb +57 -0
  50. data/lib/generators/extjs_scaffold/scaffold_controller/templates/tests/controller_spec.rb +197 -0
  51. data/lib/generators/extjs_scaffold/scaffold_controller/templates/tests/controller_test.rb +85 -0
  52. data/lib/generators/extjs_scaffold/scaffold_controller/templates/views/erb/index.html.erb +3 -0
  53. data/lib/generators/extjs_scaffold/scaffold_controller/templates/views/haml/index.html.haml +3 -0
  54. data/spec/dummy/.gitignore +15 -0
  55. data/spec/dummy/README +261 -0
  56. data/spec/dummy/Rakefile +7 -0
  57. data/spec/dummy/app/assets/images/rails.png +0 -0
  58. data/spec/dummy/app/assets/javascripts/application.js +7 -0
  59. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  60. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  61. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  62. data/spec/dummy/app/mailers/.gitkeep +0 -0
  63. data/spec/dummy/app/models/.gitkeep +0 -0
  64. data/spec/dummy/app/views/layouts/application.html.erb +16 -0
  65. data/spec/dummy/config.ru +4 -0
  66. data/spec/dummy/config/application.rb +48 -0
  67. data/spec/dummy/config/boot.rb +7 -0
  68. data/spec/dummy/config/database.yml +25 -0
  69. data/spec/dummy/config/environment.rb +5 -0
  70. data/spec/dummy/config/environments/development.rb +30 -0
  71. data/spec/dummy/config/environments/production.rb +60 -0
  72. data/spec/dummy/config/environments/test.rb +39 -0
  73. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  74. data/spec/dummy/config/initializers/inflections.rb +10 -0
  75. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  76. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  77. data/spec/dummy/config/initializers/session_store.rb +8 -0
  78. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  79. data/spec/dummy/config/locales/en.yml +5 -0
  80. data/spec/dummy/config/routes.rb +58 -0
  81. data/spec/dummy/db/schema.rb +23 -0
  82. data/spec/dummy/db/seeds.rb +7 -0
  83. data/spec/dummy/doc/README_FOR_APP +2 -0
  84. data/spec/dummy/lib/assets/.gitkeep +0 -0
  85. data/spec/dummy/lib/tasks/.gitkeep +0 -0
  86. data/spec/dummy/log/.gitkeep +0 -0
  87. data/spec/dummy/public/404.html +26 -0
  88. data/spec/dummy/public/422.html +26 -0
  89. data/spec/dummy/public/500.html +26 -0
  90. data/spec/dummy/public/favicon.ico +0 -0
  91. data/spec/dummy/public/index.html +241 -0
  92. data/spec/dummy/public/robots.txt +5 -0
  93. data/spec/dummy/script/rails +6 -0
  94. data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
  95. data/spec/dummy/vendor/plugins/.gitkeep +0 -0
  96. data/spec/generators/install_spec.rb +140 -0
  97. data/spec/generators/scaffold_spec.rb +244 -0
  98. data/spec/spec_helper.rb +26 -0
  99. data/spec/support/App.js +12 -0
  100. data/spec/support/rails_routes.rb +4 -0
  101. metadata +290 -0
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @author Mark H Winkler
3
+ * @mixin App.ux.data.Actionable
4
+ * Required use: class using App.ux.data.proxy.Rails
5
+ *
6
+ * Adds doAction function to class - convenience method for rails proxy doAction
7
+ * allowing a non rest action to be passed
8
+ * to Rails (e.g. /teams/delete_all.json).
9
+ *
10
+ * The actionName must exist in the proxy under the 'addActions' config which stores
11
+ * the METHOD (GET, POST, PUT, DELETE) and Collection (true = collection or false = member)
12
+ *
13
+ * params:
14
+ * actionName (string) - required - name of action added to proxy
15
+ * records (array) - optional - array of Model Records
16
+ * options (object) - optional - options to associate with action including callback
17
+ *
18
+ */
19
+
20
+ Ext.define('<%= app_name %>.ux.data.Actionable', {
21
+ doAction: function(action, records, options) {
22
+ var me = this;
23
+ // call doAction in proxy
24
+ me.getProxy().doAction(action, records, options);
25
+ return me;
26
+ }
27
+ });
@@ -0,0 +1,11 @@
1
+ Ext.application({
2
+ name: '<%= app_name %>',
3
+ autoCreateViewport: false,
4
+ models: [],
5
+ stores: [],
6
+ controllers: [],
7
+
8
+ launch: function() {
9
+
10
+ }
11
+ });
@@ -0,0 +1,135 @@
1
+ /**
2
+ * @author Mark H Winkler
3
+ * @class App.ux.window.EditWindow
4
+ * @extends Ext.Window
5
+ * <p>Ext.Window modal window used for editing list detail.</p>
6
+ */
7
+ Ext.define('<%= app_name %>.ux.window.EditWindow', {
8
+ extend: 'Ext.Window',
9
+
10
+ /**
11
+ * Config defaults
12
+ */
13
+ width: 600,
14
+ modal: true,
15
+ constrainHeader: true,
16
+ resizable: false,
17
+ closable: false,
18
+
19
+ /**
20
+ * Custom Config Params
21
+ */
22
+ recordId: -3,
23
+ gridId: '',
24
+ formItemId: '',
25
+ model: '',
26
+ baseUrl: '',
27
+ submitDirtyOnly: true,
28
+ /**
29
+ * boolean trigger for grid load on cancel
30
+ * used in save and add another
31
+ */
32
+ submitDirtyOnly: true,
33
+
34
+ initComponent: function() {
35
+ var me = this;
36
+ me.buttons = this.buildButtons();
37
+ me.callParent(arguments);
38
+ me.addEvents(
39
+ /**
40
+ * @event afterloadrecord
41
+ * Fires after a blank record is loaded into the underlying form
42
+ * @param {Ext.window.Window} this
43
+ * @param {Ext.data.Model} record loaded
44
+ */
45
+ 'afterloadrecord'
46
+ );
47
+ me.on('afterrender', this.loadRecord, this);
48
+ },
49
+
50
+ /**
51
+ * set up bottom window buttons
52
+ */
53
+ buildButtons: function() {
54
+ var me = this;
55
+ items = [];
56
+ items.push({
57
+ text: 'Cancel',
58
+ scope: me,
59
+ handler: me.closeWindow
60
+ });
61
+
62
+ items.push({
63
+ text: 'Save and Close',
64
+ scope: me,
65
+ handler: me.saveRecord
66
+ });
67
+
68
+ return items;
69
+ },
70
+
71
+ /**
72
+ * New Record (recordID < 1) loads a new model
73
+ * Existing Records loaded via form.load
74
+ */
75
+ loadRecord: function() {
76
+ var me = this;
77
+ var formPanel = me.getComponent(me.formItemId);
78
+ if (me.recordId < 1) {
79
+ var newModel = Ext.ModelManager.create({}, me.model);
80
+ formPanel.getForm().loadRecord(newModel);
81
+ // fire event and pass model
82
+ me.fireEvent('afterloadrecord', me, newModel);
83
+ return;
84
+ }
85
+
86
+ formPanel.getForm().load({
87
+ waitMsg: 'Loading...',
88
+ // rails formatting for edit record
89
+ url: me.baseUrl+'/'+me.recordId+'/edit.json',
90
+ method: 'GET',
91
+ failure: function(form, action) {
92
+ Ext.Msg.alert("Load failed", action.result.errorMessage);
93
+ }
94
+ });
95
+
96
+ },
97
+
98
+ /**
99
+ * Close window called from Cancel button
100
+ */
101
+ closeWindow: function() {
102
+ var me = this;
103
+ me.close();
104
+ },
105
+
106
+ /**
107
+ * Calls the associated grid uses gridId config
108
+ * Note that ajax failure is now handled automatically using json errors object
109
+ */
110
+ saveRecord: function() {
111
+ var me = this;
112
+ var grid = Ext.getCmp(me.gridId);
113
+ var formPanel = me.getComponent(me.formItemId);
114
+ var form = formPanel.getForm();
115
+ var entity = 'data';
116
+
117
+ if ( me.submitDirtyOnly ) { //don't submit form if no changes were made
118
+ if (!form.isDirty()) {
119
+ me.closeWindow();
120
+ return;
121
+ }
122
+ }
123
+
124
+ if (form.isValid()) {
125
+ form.submit({
126
+ waitMsg: 'Saving...',
127
+ success: function(form, action) {
128
+ var updatedRecord = Ext.ModelManager.create(action.result[entity], me.model);
129
+ grid.updateRecord(me.recordId, updatedRecord, action.result);
130
+ me.closeWindow();
131
+ }
132
+ });
133
+ }
134
+ }
135
+ });
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @author Mark H Winkler
3
+ * @class App.ux.form.Panel
4
+ * @extends Ext.form.Panel
5
+ * <p>Extends form.Panel to establish form config defaults.</p>
6
+ */
7
+ Ext.define('<%= app_name %>.ux.form.Panel', {
8
+ extend: 'Ext.form.Panel',
9
+
10
+ bodyPadding: 5,
11
+
12
+ defaults: {
13
+ style: 'padding-bottom: 2px',
14
+ labelStyle: 'padding-right: 5px',
15
+ labelAlign: 'right',
16
+ labelWidth: 120,
17
+ width: 400
18
+ },
19
+
20
+ // The fields
21
+ defaultType: 'textfield',
22
+
23
+ initComponent: function() {
24
+ this.callParent(arguments);
25
+ this.getForm().trackResetOnLoad = true;
26
+ }
27
+ });
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @singleton
3
+ *
4
+ * application wide formatting
5
+ *
6
+ */
7
+ (function() {
8
+ Ext.ns('<%= app_name %>.util');
9
+
10
+ <%= app_name %>.util.Format = {};
11
+ var AppUtilFormat = <%= app_name %>.util.Format;
12
+
13
+ Ext.apply(AppUtilFormat, {
14
+ /**
15
+ * Returns a date as set format - used to control application wide grid dates
16
+ * @return {Function} The date formatting function
17
+ */
18
+ dateRenderer : function() {
19
+ return function(v) {
20
+ return Ext.util.Format.date(v, 'm/d/Y');
21
+ }
22
+ },
23
+
24
+ /**
25
+ * Returns an image or empty string - used to control application wide grid booleans
26
+ * @return {Function} The boolean formatting function
27
+ */
28
+ booleanRenderer: function() {
29
+ return function(v) {
30
+ return v ? '<img src="/images/extjs_scaffold/tick.png">' : '';
31
+ }
32
+ }
33
+ });
34
+ })();
@@ -0,0 +1,289 @@
1
+ /**
2
+ * @author Mark H Winkler
3
+ * @class App.ux.grid.Panel
4
+ * @extends Ext.grid.Panel
5
+ * <p>Extends grid.Panel to establish list grid config defaults.</p>
6
+ */
7
+ Ext.define('<%= app_name %>.ux.grid.Panel', {
8
+ extend: 'Ext.grid.Panel',
9
+
10
+ /**
11
+ * Config defaults
12
+ */
13
+ multiSelect: true,
14
+
15
+ // Use a PagingGridScroller
16
+ verticalScrollerType: 'paginggridscroller',
17
+ invalidateScrollerOnRefresh: false,
18
+ //disableSelection: true,
19
+
20
+ enableColumnHide: false,
21
+ enableColumnMove: false,
22
+ enableColumnResize: false,
23
+
24
+ height: 600,
25
+ width: 800,
26
+
27
+ preventHeader: true,
28
+
29
+ style: {
30
+ marginTop: '10px',
31
+ marginBottom: '10px'
32
+ },
33
+
34
+ /**
35
+ * Custom Config Params
36
+ */
37
+ // used in dialogs and detail form titles
38
+ // entitySingular also used in editWindow.saveRecord()
39
+ // for response root (action.result[entity])
40
+ entitySingular: 'Record',
41
+ entityPlural: 'Records',
42
+ // edit window class used in openEditWindow()
43
+ editWindow: 'App.ux.window.EditWindow',
44
+
45
+ // update window class used in openUpdateWindow()
46
+ updateWindow: 'App.ux.window.UpdateWindow',
47
+
48
+ // enabling top bar buttons by default
49
+ enableRemoteSearch: true,
50
+ enableAddRecord: true,
51
+ enableDeleteRecord: true,
52
+ enableUpdateAll: true,
53
+ enableStoreReload: true,
54
+
55
+ // hides the bottom bar
56
+ hideBottomToolbar: false,
57
+
58
+ initComponent: function() {
59
+ this.dockedItems = this.buildDockedItems();
60
+ this.callParent(arguments);
61
+ this.on('itemdblclick', this.onDblClick, this);
62
+ },
63
+
64
+ /**
65
+ * Setup for top and bottom toolbars
66
+ */
67
+ buildDockedItems: function() {
68
+ var me = this,
69
+ topitems = [],
70
+ toolbars = [];
71
+
72
+ if ( me.enableRemoteSearch ) {
73
+ topitems.push({ xtype: 'searchfield', store: me.store });
74
+ topitems.push( '-' );
75
+ }
76
+
77
+ if ( me.enableAddRecord ) {
78
+ topitems.push({
79
+ text: 'Add Record',
80
+ tooltip: 'Add new '+this.entitySingular,
81
+ iconCls: 'add',
82
+ scope: me,
83
+ handler: me.newRecord
84
+ });
85
+ topitems.push( '-' );
86
+ }
87
+
88
+ if ( me.enableDeleteRecord ) {
89
+ topitems.push({
90
+ text: 'Delete All',
91
+ tooltip: 'Delete selected records',
92
+ iconCls: 'remove',
93
+ scope: me,
94
+ handler: me.deleteRecords
95
+ });
96
+ topitems.push( '-' );
97
+ }
98
+
99
+ if ( me.enableUpdateAll ) {
100
+ topitems.push({
101
+ text: 'Update All',
102
+ tooltip: 'Update all selected records',
103
+ iconCls: 'updateall',
104
+ scope: me,
105
+ handler: me.updateAll
106
+ });
107
+ topitems.push( '-' );
108
+ }
109
+
110
+ toolbars.push({
111
+ xtype: 'toolbar',
112
+ itemId: 'toptoolbar',
113
+ dock: 'top',
114
+ items: topitems
115
+ });
116
+
117
+ if (!me.hideBottomToolbar) {
118
+ toolbars.push({
119
+ xtype: 'scrollingtoolbar',
120
+ itemId: 'bottomtoolbar',
121
+ store: me.store,
122
+ dock: 'bottom',
123
+ displayInfo: true,
124
+ displayReload: me.enableStoreReload
125
+ });
126
+ }
127
+
128
+ return toolbars;
129
+ },
130
+
131
+ /**
132
+ * Grid double click handler
133
+ */
134
+ onDblClick: function(view, record, item, index, event, options) {
135
+ this.editRecord(record);
136
+ },
137
+
138
+ /**
139
+ * Opens edit window with new record
140
+ * called from top toolbar Add button
141
+ */
142
+ newRecord: function() {
143
+ this.openEditWindow(-3);
144
+ },
145
+
146
+ /**
147
+ * Opens edit window with passed record
148
+ * called from onDblClick handler
149
+ */
150
+ editRecord: function(record) {
151
+ if (record) { // edit the record
152
+ var recordId = record.get('id');
153
+ this.openEditWindow(recordId);
154
+ }
155
+ },
156
+
157
+ /**
158
+ * Uses the App.ux.data.Actionable mixin to delete one or more records
159
+ * called from top toolbar Delete button
160
+ */
161
+ deleteRecords: function() {
162
+ var me = this;
163
+ var records = me.getSelectionModel().getSelection();
164
+ if (records.length > 0) {
165
+ // confirm that records should be deleted
166
+ var entity = records.length > 1 ? me.entityPlural : me.entitySingular;
167
+ Ext.Msg.confirm('Warning','Do you really want to delete '+records.length+' '+entity+'. This cannot be undone!', function(btn){
168
+ if(btn=='yes'){
169
+ me.setLoading('Deleting..');
170
+ // call the actionable mixin in the store
171
+ me.getStore().doAction('destroy_all', records,
172
+ {
173
+ success: function(operation){
174
+ me.setLoading(false);
175
+ me.getStore().load();
176
+ },
177
+ failure: function(operation){
178
+ me.setLoading(false);
179
+ alert('Could not delete, changes rolled back');
180
+ }
181
+ });
182
+ };
183
+ });
184
+ }
185
+ },
186
+
187
+ /**
188
+ * Opens update window if records are selected
189
+ * called from onDblClick handler
190
+ */
191
+ updateAll: function() {
192
+ var me = this;
193
+ var records = me.getSelectionModel().getSelection();
194
+ if (records.length > 0) {
195
+ me.openUpdateWindow(records);
196
+ } else {
197
+ alert('Please select records to update');
198
+ }
199
+ },
200
+
201
+ /**
202
+ * Uses the App.ux.data.Actionable mixin to update one or more records
203
+ * called from App.ux.window.UpdateWindow
204
+ */
205
+ updateAllRecords: function(records) {
206
+ var me = this;
207
+
208
+ me.setLoading('Updating..');
209
+ // call the actionable mixin in the store
210
+ me.getStore().doAction('update_all', records,
211
+ {
212
+ success: function(operation){
213
+ me.setLoading(false);
214
+ for(var i=0; i < records.length; i++) {
215
+ records[i].commit();
216
+ }
217
+ },
218
+ failure: function(operation){
219
+ me.setLoading(false);
220
+ for(var i=0; i < records.length; i++) {
221
+ records[i].reject();
222
+ }
223
+ alert('Could not update, changes rolled back');
224
+ }
225
+ });
226
+ },
227
+
228
+ /**
229
+ * Opens edit window with new or existing record
230
+ * called newRecord() and editRecord()
231
+ */
232
+ openEditWindow: function(recordId) {
233
+ var me = this;
234
+
235
+ if (me.editWindow != '') {
236
+ var title = recordId > 0 ? 'Update '+me.entitySingular : 'Add '+me.entitySingular;
237
+ var editWindow = Ext.create(me.editWindow, {
238
+ title: title,
239
+ gridId: me.id,
240
+ recordId: recordId
241
+ })
242
+
243
+ editWindow.show();
244
+ }
245
+ },
246
+
247
+ /**
248
+ * Opens update window to update all
249
+ */
250
+ openUpdateWindow: function(records) {
251
+ var me = this;
252
+
253
+ if (me.updateWindow != '') {
254
+ var updateWindow = Ext.create(me.updateWindow, {
255
+ gridId: me.id,
256
+ selectedRecords: records
257
+ })
258
+
259
+ updateWindow.show();
260
+ }
261
+ },
262
+
263
+ /**
264
+ * calls the App.ux.data.Updateable model mixin to update grid record with passed config
265
+ * called App.ux.window.EditWindow.saveRecord()
266
+ */
267
+ updateRecord: function(recordId, updateRecord, response) {
268
+ var me = this;
269
+
270
+ // add or update the edited record
271
+ if (recordId < 1) {
272
+ me.getStore().load();
273
+ } else {
274
+ var record = me.store.getById(recordId);
275
+ if (record) {
276
+ record.updateModel(updateRecord);
277
+ }
278
+ }
279
+ },
280
+
281
+ /**
282
+ * Loads the grid store and handles the initial section completed display
283
+ */
284
+ load: function() {
285
+ var me = this;
286
+
287
+ me.store.guaranteeRange(0, 199);
288
+ }
289
+ });