extjs_scaffold 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ });