dry_crud 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/README.rdoc +24 -19
  2. data/Rakefile +12 -4
  3. data/VERSION +1 -1
  4. data/lib/generators/dry_crud/dry_crud_generator.rb +7 -7
  5. data/lib/generators/dry_crud/templates/app/assets/stylesheets/crud.scss +18 -7
  6. data/lib/generators/dry_crud/templates/app/assets/stylesheets/sample.scss +13 -18
  7. data/lib/generators/dry_crud/templates/app/controllers/crud_controller.rb +74 -87
  8. data/lib/generators/dry_crud/templates/app/controllers/list_controller.rb +95 -50
  9. data/lib/generators/dry_crud/templates/app/helpers/crud_helper.rb +11 -11
  10. data/lib/generators/dry_crud/templates/app/helpers/list_helper.rb +7 -7
  11. data/lib/generators/dry_crud/templates/app/helpers/standard_form_builder.rb +55 -27
  12. data/lib/generators/dry_crud/templates/app/helpers/standard_helper.rb +83 -30
  13. data/lib/generators/dry_crud/templates/app/helpers/standard_table_builder.rb +5 -13
  14. data/lib/generators/dry_crud/templates/app/views/layouts/_flash.html.erb +1 -4
  15. data/lib/generators/dry_crud/templates/app/views/layouts/_flash.html.haml +1 -3
  16. data/lib/generators/dry_crud/templates/app/views/layouts/_nav.html.erb +6 -6
  17. data/lib/generators/dry_crud/templates/app/views/layouts/_nav.html.haml +5 -3
  18. data/lib/generators/dry_crud/templates/app/views/layouts/crud.html.erb +13 -11
  19. data/lib/generators/dry_crud/templates/app/views/layouts/crud.html.haml +15 -15
  20. data/lib/generators/dry_crud/templates/app/views/shared/_error_messages.html.erb +10 -10
  21. data/lib/generators/dry_crud/templates/app/views/shared/_labeled.html.erb +2 -2
  22. data/lib/generators/dry_crud/templates/app/views/shared/_labeled.html.haml +2 -2
  23. data/lib/generators/dry_crud/templates/config/initializers/field_error_proc.rb +1 -0
  24. data/lib/generators/dry_crud/templates/config/locales/en_crud.yml +1 -0
  25. data/lib/generators/dry_crud/templates/test/crud_test_model.rb +72 -17
  26. data/lib/generators/dry_crud/templates/test/custom_assertions.rb +1 -1
  27. data/lib/generators/dry_crud/templates/test/functional/crud_controller_test_helper.rb +42 -26
  28. data/lib/generators/dry_crud/templates/test/functional/crud_test_models_controller_test.rb +135 -29
  29. data/lib/generators/dry_crud/templates/test/unit/custom_assertions_test.rb +4 -4
  30. data/lib/generators/dry_crud/templates/test/unit/helpers/crud_helper_test.rb +4 -2
  31. data/lib/generators/dry_crud/templates/test/unit/helpers/list_helper_test.rb +2 -0
  32. data/lib/generators/dry_crud/templates/test/unit/helpers/standard_form_builder_test.rb +94 -16
  33. data/lib/generators/dry_crud/templates/test/unit/helpers/standard_helper_test.rb +58 -18
  34. data/lib/generators/dry_crud/templates/test/unit/helpers/standard_table_builder_test.rb +4 -4
  35. data/test/templates/Gemfile +1 -0
  36. data/test/templates/app/controllers/admin/cities_controller.rb +0 -7
  37. data/test/templates/app/controllers/admin/countries_controller.rb +2 -3
  38. data/test/templates/app/controllers/ajax_controller.rb +2 -0
  39. data/test/templates/app/controllers/people_controller.rb +1 -1
  40. data/test/templates/app/models/city.rb +2 -0
  41. data/test/templates/app/models/country.rb +2 -0
  42. data/test/templates/app/models/person.rb +2 -0
  43. data/test/templates/app/views/admin/cities/_attrs.html.erb +1 -0
  44. data/test/templates/app/views/admin/cities/_attrs.html.haml +1 -0
  45. data/test/templates/app/views/admin/cities/_form.html.erb +7 -1
  46. data/test/templates/app/views/admin/cities/_form.html.haml +5 -1
  47. data/test/templates/app/views/admin/cities/_list.html.erb +1 -4
  48. data/test/templates/app/views/admin/cities/_list.html.haml +1 -3
  49. data/test/templates/app/views/ajax/_actions_show.html.erb +4 -0
  50. data/test/templates/app/views/ajax/_actions_show.html.haml +4 -0
  51. data/test/templates/app/views/ajax/_form.html.erb +2 -0
  52. data/test/templates/app/views/ajax/_form.html.haml +2 -0
  53. data/test/templates/app/views/ajax/edit.js.erb +1 -0
  54. data/test/templates/app/views/ajax/edit.js.haml +1 -0
  55. data/test/templates/app/views/ajax/show.js.erb +1 -0
  56. data/test/templates/app/views/ajax/show.js.haml +1 -0
  57. data/test/templates/app/views/ajax/update.js.erb +5 -0
  58. data/test/templates/app/views/ajax/update.js.haml +5 -0
  59. data/test/templates/app/views/layouts/_nav.html.erb +6 -0
  60. data/test/templates/app/views/layouts/_nav.html.haml +5 -0
  61. data/test/templates/app/views/layouts/bootstrap.html.erb +68 -0
  62. data/test/templates/app/views/layouts/bootstrap.html.haml +49 -0
  63. data/test/templates/app/views/people/_attrs.html.erb +2 -2
  64. data/test/templates/app/views/people/_attrs.html.haml +2 -2
  65. data/test/templates/config/routes.rb +5 -5
  66. data/test/templates/db/migrate/20100511174904_create_people_and_cities.rb +1 -1
  67. data/test/templates/db/seeds.rb +52 -52
  68. data/test/templates/test/functional/admin/cities_controller_test.rb +15 -15
  69. data/test/templates/test/functional/admin/countries_controller_test.rb +4 -5
  70. data/test/templates/test/functional/people_controller_test.rb +32 -4
  71. metadata +22 -9
  72. data/lib/generators/dry_crud/templates/app/views/layouts/_menu.html.erb +0 -3
  73. data/lib/generators/dry_crud/templates/app/views/layouts/_menu.html.haml +0 -3
  74. data/test/templates/app/views/layouts/_menu.html.erb +0 -3
  75. data/test/templates/app/views/layouts/_menu.html.haml +0 -3
data/README.rdoc CHANGED
@@ -72,17 +72,16 @@ This only displays these three attributes in the table. All other templates, as
72
72
 
73
73
  ==== Adapt general behavior
74
74
 
75
- Next, let's adapt a part of the general behavior used in all CRUD controllers. As an example, we include pagination with will_paginate[http://wiki.github.com/mislav/will_paginate/] in all our overview tables:
75
+ Next, let's adapt a part of the general behavior used in all CRUD controllers. As an example, we include pagination with kaminari[https://github.com/amatsuda/kaminari] in all our overview tables:
76
76
 
77
- In <tt>app/controllers/list_controller.rb</tt>, change the index action to
77
+ In <tt>app/controllers/list_controller.rb</tt>, change the list_entries method to
78
78
 
79
- def index
80
- @entries = list_entries.paginate(:page => params[:page])
81
- respond_with @entries
79
+ def list_entries
80
+ model_scope.page(params[:page])
82
81
  end
83
82
 
84
83
  In <tt>app/views/list/index.html.erb</tt>, add the following line for the pagination links:
85
- <%= will_paginate @entries %>
84
+ <%= paginate entries %>
86
85
 
87
86
  And we are done again. All our controllers inheriting from +ListController+, including above +PeopleController+, now have paginated index views. Because our customization for the people table is in the separate <tt>_list</tt> partial, no further modifications are required.
88
87
 
@@ -123,7 +122,7 @@ In <tt>app/controllers/people_controller.rb</tt>:
123
122
  end
124
123
 
125
124
  def delete_picture
126
- if !perform_delete_picture(@entry.picture)
125
+ if !perform_delete_picture(entry.picture)
127
126
  flash.alert = 'Could not delete picture'
128
127
  false
129
128
  end
@@ -135,7 +134,7 @@ In <tt>app/controllers/people_controller.rb</tt>:
135
134
  before_render_form :set_hometowns
136
135
 
137
136
  def set_hometowns
138
- @hometowns = City.where(:country => @entry.country)
137
+ @hometowns = City.where(:country => entry.country)
139
138
  end
140
139
 
141
140
  === Standard Tables and Forms
@@ -181,6 +180,12 @@ Even +belongs_to+ associations are automatically rendered with a select field. B
181
180
 
182
181
  Yes, it's bad practice to use finder logic in your views! Define the variable <tt>@hometowns</tt> in your controller instead (as shown in the example above), and you do not even have to specify the <tt>:list</tt> option.
183
182
 
183
+ Optionally, +has_and_belongs_to_many+ and +has_many+ associations can be rendered with a multi-select field. Similar to a +belongs_to+ association, all entries from the associated model are used, but can be overwritten using the <tt>:list</tt> option:
184
+ <%= f.has_many_field :visited_cities, :list => City.where(:is_touristic => true) %>
185
+
186
+ And yes again, the same advice for where to put finder logic applies here as well.
187
+
188
+ <b>Note:</b> +has_and_belongs_to_many+ and +has_many+ associations are not automatically rendered in a form, you have to explicitly include these attributes. You might also want to stylize the multi-select widget, for example with a {jQuery UI Multiselect}[http://www.quasipartikel.at/multiselect/].
184
189
 
185
190
  === Nested Resources
186
191
 
@@ -188,7 +193,7 @@ In case you define nested resources, your +CrudController+ subclass should know.
188
193
 
189
194
  self.nesting = :my_namspace, ParentResource
190
195
 
191
- This declaration is for a controller nested in +parent_resources+ within a +:my_namespace+ scope. +ParentResource+ is the corresponding +ActiveRecord+ model. The request param +:parent_resource_id+ is used to load the parent entry, which in turn is used to filter the entries listed and created in your controller.
196
+ This declaration is for a controller nested in +parent_resources+ within a +:my_namespace+ scope. +ParentResource+ is the corresponding +ActiveRecord+ model. The request param +:parent_resource_id+ is used to load the parent entry, which in turn is used to filter the entries listed and created in your controller. For all parent resources, a corresponding instance variable is created.
192
197
 
193
198
  The <tt>ListController::Nesting</tt> module defines this basic behaviour. For more complex setups, have a look there and adjust it to your needs.
194
199
 
@@ -221,22 +226,22 @@ All generated files are supposed to provide a reasonable foundation for the CRUD
221
226
 
222
227
  === Controller:
223
228
 
224
- {controller/crud_controller.rb}[http://codez.ch/dry_crud/?q=CrudController]:: Abstract controller providing basic CRUD actions. This implementation mainly follows the one of the Rails scaffolding controller and responses to HTML and XML requests. Some enhancements were made to ease extendability. Several protected helper methods are there to be (optionally) overriden by subclasses. With the help of additional callbacks, it is possible to hook into the action procedures without overriding the entire method. This class is based on +ListController+.
229
+ {controller/crud_controller.rb}[http://codez.ch/dry_crud/CrudController.html]:: Abstract controller providing basic CRUD actions. This implementation mainly follows the one of the Rails scaffolding controller and responses to HTML and XML requests. Some enhancements were made to ease extendability. Several protected helper methods are there to be (optionally) overriden by subclasses. With the help of additional callbacks, it is possible to hook into the action procedures without overriding the entire method. This class is based on +ListController+.
225
230
 
226
- {controller/list_controller.rb}[http://codez.ch/dry_crud/?q=ListController]:: Abstract controller providing a basic list action. Use this controller if you require read-only functionality. There are two sub-modules that provide search and sort functionality for the table displayed in the list action. A third sub-module remembers the list parameters in order to return to an identical list.
231
+ {controller/list_controller.rb}[http://codez.ch/dry_crud/ListController.html]:: Abstract controller providing a basic list action. Use this controller if you require read-only functionality. There are two sub-modules that provide search and sort functionality for the table displayed in the list action. A third sub-module remembers the list parameters in order to return to an identical list.
227
232
 
228
233
 
229
234
  === Helpers:
230
235
 
231
- {helpers/standard_helper.rb}[http://codez.ch/dry_crud/?q=StandardHelper]:: A view helper to standardize often used functions like formatting, tables, forms or action links. This helper is ideally defined in the +ApplicationController+. It is required to use the +StandardTableBuilder+ and the +StandardFormBuilder+.
236
+ {helpers/standard_helper.rb}[http://codez.ch/dry_crud/StandardHelper.html]:: A view helper to standardize often used functions like formatting, tables, forms or action links. This helper is ideally defined in the +ApplicationController+. It is required to use the +StandardTableBuilder+ and the +StandardFormBuilder+.
232
237
 
233
- {helpers/crud_helper.rb}[http://codez.ch/dry_crud/?q=CrudHelper]:: A small helper for +CrudController+ to render tables and forms with a default set of attributes.
238
+ {helpers/crud_helper.rb}[http://codez.ch/dry_crud/CrudHelper.html]:: A small helper for +CrudController+ to render tables and forms with a default set of attributes.
234
239
 
235
- {helpers/list_helper.rb}[http://codez.ch/dry_crud/?q=ListHelper]:: A small helper for +ListController+ to render the list table with a default set of attributes.
240
+ {helpers/list_helper.rb}[http://codez.ch/dry_crud/ListHelper.html]:: A small helper for +ListController+ to render the list table with a default set of attributes.
236
241
 
237
- {helpers/standard_table_builder.rb}[http://codez.ch/dry_crud/?q=StandardTableBuilder]:: A simple helper object to easily define tables listing several rows of the same data type.
242
+ {helpers/standard_table_builder.rb}[http://codez.ch/dry_crud/StandardTableBuilder.html]:: A simple helper object to easily define tables listing several rows of the same data type.
238
243
 
239
- {helpers/standard_form_builder.rb}[http://codez.ch/dry_crud/?q=StandardFormBuilder]:: A form builder that automatically selects the corresponding input type for ActiveRecord columns. Input elements are rendered together with a label by default.
244
+ {helpers/standard_form_builder.rb}[http://codez.ch/dry_crud/StandardFormBuilder.html]:: A form builder that automatically selects the corresponding input type for ActiveRecord columns. Input elements are rendered together with a label by default.
240
245
 
241
246
 
242
247
  === Views:
@@ -290,11 +295,11 @@ app/assets/images/action/*.png:: Some sample action icons from the {Open Icon Li
290
295
 
291
296
  === Tests:
292
297
 
293
- {test/crud_test_model.rb}[http://codez.ch/dry_crud/?q=CrudTestHelper]:: A dummy model to run CRUD tests against.
298
+ {test/crud_test_model.rb}[http://codez.ch/dry_crud/CrudTestHelper.html]:: A dummy model to run CRUD tests against.
294
299
 
295
- {test/custom_assertions.rb}[http://codez.ch/dry_crud/?q=CustomAssertions]:: A handful of convenient assertions. Include this module into your <tt>test_helper.rb</tt> file.
300
+ {test/custom_assertions.rb}[http://codez.ch/dry_crud/CustomAssertions.html]:: A handful of convenient assertions. Include this module into your <tt>test_helper.rb</tt> file.
296
301
 
297
- {test/functionals/crud_controller_test_helper.rb}[http://codez.ch/dry_crud/?q=CrudControllerTestHelper]:: A module to include into the functional tests for your +CrudController+ subclasses. Contains a handful of CRUD functionality tests for the provided implementation. So for each new CRUD controller, you get 20 tests for free.
302
+ {test/functionals/crud_controller_test_helper.rb}[http://codez.ch/dry_crud/CrudControllerTestHelper.html]:: A module to include into the functional tests for your +CrudController+ subclasses. Contains a handful of CRUD functionality tests for the provided implementation. So for each new CRUD controller, you get 20 tests for free.
298
303
 
299
304
  test/several other tests:: Testing the provided implementation and a great base to test your adaptions of the CRUD code.
300
305
 
data/Rakefile CHANGED
@@ -85,12 +85,12 @@ namespace :test do
85
85
  desc "Adds pagination to the test app"
86
86
  task :add_pagination => :generate_crud do
87
87
  list_ctrl = File.join(TEST_APP_ROOT, 'app', 'controllers', 'list_controller.rb')
88
- file_replace(list_ctrl, "@entries = list_entries",
89
- "@entries = list_entries.page(params[:page]).per(10)")
88
+ file_replace(list_ctrl, /def list_entries\n\s+model_scope\s*\n/,
89
+ "def list_entries\n model_scope.page(params[:page]).per(10)\n")
90
90
  file_replace(File.join(TEST_APP_ROOT, 'app', 'views', 'list', 'index.html.erb'),
91
- "<%= render 'list' %>", "<%= paginate @entries %>\n\n<%= render 'list' %>")
91
+ "<%= render 'list' %>", "<%= paginate entries %>\n\n<%= render 'list' %>")
92
92
  file_replace(File.join(TEST_APP_ROOT, 'app', 'views', 'list', 'index.html.haml'),
93
- "= render 'list'", "= paginate @entries\n\n= render 'list'")
93
+ "= render 'list'", "= paginate entries\n\n= render 'list'")
94
94
  end
95
95
 
96
96
  desc "Use Boostrap in the test app"
@@ -98,6 +98,14 @@ namespace :test do
98
98
  file_replace(File.join(TEST_APP_ROOT, 'app', 'assets', 'stylesheets', 'application.css'), " *= require_self", "*= require twitter/bootstrap\n *= require_self")
99
99
  file_replace(File.join(TEST_APP_ROOT, 'app', 'assets', 'javascripts', 'application.js'), "//= require_tree .", "//= require twitter/bootstrap\n//= require_tree .")
100
100
  FileUtils.rm(File.join(TEST_APP_ROOT, 'app', 'assets', 'stylesheets', 'sample.scss'))
101
+
102
+ layouts = File.join(TEST_APP_ROOT, 'app', 'views', 'layouts')
103
+ FileUtils.mv(File.join(layouts, 'bootstrap.html.erb'),
104
+ File.join(layouts, 'application.html.erb'),
105
+ :force => true) if File.exists?(File.join(layouts, 'bootstrap.html.erb'))
106
+ FileUtils.mv(File.join(layouts, 'bootstrap.html.haml'),
107
+ File.join(layouts, 'application.html.haml'),
108
+ :force => true) if File.exists?(File.join(layouts, 'bootstrap.html.haml'))
101
109
  end
102
110
 
103
111
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.5.0
1
+ 1.6.0
@@ -3,8 +3,8 @@ require 'rails/generators'
3
3
  class DryCrudGenerator < Rails::Generators::Base
4
4
 
5
5
  class_options %w(templates -t) => 'erb'
6
-
7
-
6
+
7
+
8
8
  def self.source_root
9
9
  File.join(File.dirname(__FILE__), 'templates')
10
10
  end
@@ -12,13 +12,13 @@ class DryCrudGenerator < Rails::Generators::Base
12
12
  def install_dry_crud
13
13
  # copy everything in template subfolders
14
14
  exclude = options[:templates].downcase == 'haml' ? '.erb' : '.haml'
15
-
15
+
16
16
  Dir.chdir(self.class.source_root) do
17
17
  Dir.glob(File.join('**', '**')).sort.each do |file_source|
18
- if !File.directory?(file_source) &&
19
- !file_source.ends_with?(exclude) &&
20
- file_source != 'INSTALL'
21
- copy_file(file_source)
18
+ if !File.directory?(file_source) &&
19
+ !file_source.end_with?(exclude) &&
20
+ file_source != 'INSTALL'
21
+ copy_file(file_source)
22
22
  end
23
23
  end
24
24
  end
@@ -9,7 +9,7 @@
9
9
 
10
10
  .labeled {
11
11
  vertical-align: top;
12
- padding-bottom: 5px;
12
+ padding: 2px 0 2px;
13
13
  clear: both;
14
14
  }
15
15
 
@@ -27,14 +27,25 @@
27
27
  margin: 0;
28
28
  }
29
29
 
30
+ #content {
31
+ clear: both;
32
+ padding-top: 5px;
33
+ width: 100%;
34
+ }
35
+
36
+ table.table td.action {
37
+ width: 20px;
38
+ text-align: center;
39
+ }
40
+
30
41
  .control-group {
31
42
  clear: both;
43
+ padding: 2px 0 2px;
32
44
  }
33
45
 
34
46
  .control-group label {
35
47
  float: left;
36
48
  width: 120px;
37
- padding-top: 5px;
38
49
  padding-right: 5px;
39
50
  }
40
51
 
@@ -42,6 +53,11 @@
42
53
  margin-left: 130px;
43
54
  }
44
55
 
56
+ .cancel {
57
+ font-size: 80%;
58
+ margin-left: 7px;
59
+ }
60
+
45
61
  .required {
46
62
  font-size: 80%;
47
63
  vertical-align: top;
@@ -57,8 +73,3 @@
57
73
  margin-bottom: 5px;
58
74
  }
59
75
 
60
- .controls div.field_with_errors {
61
- background-color: #da9;
62
- display: inline-block;
63
- padding: 1px 1px;
64
- }
@@ -25,11 +25,6 @@ body {
25
25
  box-shadow: 0px 0px 5px $theme_color;
26
26
  }
27
27
 
28
- #content {
29
- clear: both;
30
- padding-top: 5px;
31
- }
32
-
33
28
  h1 {
34
29
  font-size: 150%;
35
30
  margin: 30px 0 15px 0;
@@ -102,13 +97,9 @@ table.table td {
102
97
  padding: 2px 4px;
103
98
  }
104
99
 
105
- table.table td.action {
106
- width: 20px;
107
- text-align: center;
108
- }
109
-
110
100
  label {
111
101
  font-style: italic;
102
+ text-align: right;
112
103
  }
113
104
 
114
105
  .form-actions {
@@ -155,16 +146,11 @@ input[type=number] {
155
146
  width: 80px;
156
147
  }
157
148
 
158
- textarea {
149
+ textarea, select[multiple] {
159
150
  width: 180px;
160
151
  height: 80px;
161
152
  }
162
153
 
163
- .cancel {
164
- font-size: 80%;
165
- margin-left: 7px;
166
- }
167
-
168
154
  .alert {
169
155
  margin: 15px;
170
156
  padding: 5px 10px;
@@ -176,7 +162,7 @@ textarea {
176
162
  }
177
163
 
178
164
  .alert-error {
179
- border: solid 2px #da9;
165
+ border: solid 2px #D88;
180
166
  background-color: #fec;
181
167
  }
182
168
 
@@ -237,8 +223,9 @@ a.brand:hover {
237
223
  display: block;
238
224
  float: left;
239
225
  margin: 0;
240
- padding: 8px 12px 8px;
226
+ padding: 7px 12px 7px;
241
227
  text-decoration: none;
228
+ height: 19px;
242
229
  }
243
230
 
244
231
  .nav a:hover {
@@ -255,4 +242,12 @@ footer {
255
242
 
256
243
  footer * {
257
244
  font-size: 80%;
245
+ }
246
+
247
+ .error .control-label {
248
+ color: #D88;
249
+ }
250
+
251
+ .error .controls input, .error .controls textarea {
252
+ border-color: #D88;
258
253
  }
@@ -6,15 +6,8 @@
6
6
  # overriding the entire method.
7
7
  class CrudController < ListController
8
8
 
9
- include ERB::Util
10
-
11
- helper_method :entry, :full_entry_label
12
-
13
9
  delegate :model_identifier, :to => 'self.class'
14
10
 
15
- hide_action :model_identifier, :run_callbacks
16
-
17
-
18
11
  # Defines before and after callback hooks for create, update, save and destroy actions.
19
12
  define_model_callbacks :create, :update, :save, :destroy
20
13
 
@@ -22,70 +15,63 @@ class CrudController < ListController
22
15
  # unifiying render_new and render_edit, called render_form, is defined further down.
23
16
  define_render_callbacks :show, :new, :edit
24
17
 
18
+ after_save :set_success_notice
19
+ after_destroy :set_success_notice
20
+
21
+ helper_method :entry, :full_entry_label
22
+
23
+ hide_action :model_identifier, :run_callbacks
24
+
25
+ # Simple helper object to give access to required view helper methods.
26
+ @@helper = Object.new.extend(ActionView::Helpers::TranslationHelper).
27
+ extend(ActionView::Helpers::OutputSafetyHelper)
28
+
25
29
 
26
30
  ############## ACTIONS ############################################
27
31
 
28
32
  # Show one entry of this model.
29
33
  # GET /entries/1
30
34
  # GET /entries/1.json
31
- def show
32
- respond_with entry
35
+ def show(&block)
36
+ respond_with(entry, &block)
33
37
  end
34
38
 
35
39
  # Display a form to create a new entry of this model.
36
40
  # GET /entries/new
37
41
  # GET /entries/new.json
38
- def new
42
+ def new(&block)
39
43
  assign_attributes
40
- respond_with entry
44
+ respond_with(entry, &block)
41
45
  end
42
46
 
43
47
  # Create a new entry of this model from the passed params.
44
48
  # There are before and after create callbacks to hook into the action.
45
49
  # To customize the response, you may overwrite this action and call
46
- # super with a block that gets success and format parameters.
50
+ # super with a block that gets the format parameter.
47
51
  # POST /entries
48
52
  # POST /entries.json
49
53
  def create(&block)
50
54
  assign_attributes
51
55
  created = with_callbacks(:create, :save) { entry.save }
52
-
53
- customizable_respond_to(created, block) do |format|
54
- if created
55
- format.html { redirect_to_show success_notice }
56
- format.json { render :json => entry, :status => :created, :location => path_args(entry) }
57
- else
58
- format.html { render_with_callback 'new' }
59
- format.json { render :json => entry.errors, :status => :unprocessable_entity }
60
- end
61
- end
56
+ respond_with(entry, :success => created, &block)
62
57
  end
63
58
 
64
59
  # Display a form to edit an exisiting entry of this model.
65
60
  # GET /entries/1/edit
66
- def edit
67
- render_with_callback 'edit'
61
+ def edit(&block)
62
+ respond_with(entry, &block)
68
63
  end
69
64
 
70
65
  # Update an existing entry of this model from the passed params.
71
66
  # There are before and after update callbacks to hook into the action.
72
67
  # To customize the response, you may overwrite this action and call
73
- # super with a block that gets success and format parameters.
68
+ # super with a block that gets the format parameter.
74
69
  # PUT /entries/1
75
70
  # PUT /entries/1.json
76
71
  def update(&block)
77
72
  assign_attributes
78
73
  updated = with_callbacks(:update, :save) { entry.save }
79
-
80
- customizable_respond_to(updated, block) do |format|
81
- if updated
82
- format.html { redirect_to_show success_notice }
83
- format.json { head :ok }
84
- else
85
- format.html { render_with_callback 'edit' }
86
- format.json { render :json => entry.errors, :status => :unprocessable_entity }
87
- end
88
- end
74
+ respond_with(entry, :success => updated, &block)
89
75
  end
90
76
 
91
77
  # Destroy an existing entry of this model.
@@ -96,19 +82,9 @@ class CrudController < ListController
96
82
  # DELETE /entries/1.json
97
83
  def destroy(&block)
98
84
  destroyed = run_callbacks(:destroy) { entry.destroy }
99
-
100
- customizable_respond_to(destroyed, block) do |format|
101
- if destroyed
102
- format.html { redirect_to_index success_notice }
103
- format.json { head :ok }
104
- else
105
- format.html {
106
- flash.alert = entry.errors.full_messages.join('<br/>')
107
- request.env["HTTP_REFERER"].present? ? redirect_to(:back) : redirect_to_show
108
- }
109
- format.json { render :json => entry.errors, :status => :unprocessable_entity }
110
- end
111
- end
85
+ flash[:alert] ||= error_messages.presence || flash_message(:failure) if !destroyed && request.format == :html
86
+ location = !destroyed && request.env["HTTP_REFERER"].presence || index_url
87
+ respond_with(entry, :success => destroyed, :location => location, &block)
112
88
  end
113
89
 
114
90
  protected
@@ -117,9 +93,9 @@ class CrudController < ListController
117
93
 
118
94
  # Main accessor method for the handled model entry.
119
95
  def entry
120
- @entry ||= params[:id] ? find_entry : build_entry
96
+ get_model_ivar || set_model_ivar(params[:id] ? find_entry : build_entry)
121
97
  end
122
-
98
+
123
99
  # Creates a new model entry.
124
100
  def build_entry
125
101
  model_scope.new
@@ -129,7 +105,7 @@ class CrudController < ListController
129
105
  def find_entry
130
106
  model_scope.find(params[:id])
131
107
  end
132
-
108
+
133
109
  # Assigns the attributes from the params to the model entry.
134
110
  def assign_attributes
135
111
  entry.attributes = params[model_identifier]
@@ -137,52 +113,39 @@ class CrudController < ListController
137
113
 
138
114
  # A label for the current entry, including the model name.
139
115
  def full_entry_label
140
- "#{models_label(false)} <i>#{h(entry)}</i>".html_safe
116
+ "#{models_label(false)} <i>#{ERB::Util.h(entry)}</i>".html_safe
141
117
  end
142
118
 
143
- # Redirects to the show action of a single entry.
144
- def redirect_to_show(options = {})
145
- redirect_to path_args(entry), options
119
+ # Url of the index page to return to
120
+ def index_url
121
+ polymorphic_url(path_args(model_class), :returning => true)
146
122
  end
147
123
 
148
- # Redirects to the main action of this controller.
149
- def redirect_to_index(options = {})
150
- redirect_to polymorphic_path(path_args(model_class), :returning => true), options
151
- end
124
+ private
152
125
 
153
- # Helper method the run the given block in between the before and after
154
- # callbacks of the given kinds.
155
- def with_callbacks(*kinds, &block)
156
- kinds.reverse.inject(block) do |b, kind|
157
- lambda { run_callbacks(kind, &b) }
158
- end.call
126
+ # Set a success flash notice when we got a HTML request.
127
+ def set_success_notice
128
+ flash[:notice] ||= flash_message(:success) if request.format == :html
159
129
  end
160
-
161
- private
162
-
163
- # Convenience method to respond to various formats if the performed
164
- # action may succeed or fail. It is possible to pass a custom_block and respond
165
- # in custom ways for certain cases. If no response is performed in the
166
- # given block, the default responses in the main block are executed.
167
- def customizable_respond_to(success, custom_block = nil)
168
- respond_to do |format|
169
- custom_block.call(success, format) if custom_block
170
- return if performed?
171
-
172
- yield format
173
- end
130
+
131
+ # Get an I18n flash message, considering _html keys as well.
132
+ # Uses the key {controller_name}.{action_name}.flash.{state}
133
+ # or crud.{action_name}.flash.{state} as fallback.
134
+ def flash_message(state)
135
+ scope = "#{action_name}.flash.#{state}"
136
+ keys = [:"#{controller_name}.#{scope}_html",
137
+ :"#{controller_name}.#{scope}",
138
+ :"crud.#{scope}_html",
139
+ :"crud.#{scope}"]
140
+ @@helper.t(keys.shift, :model => full_entry_label, :default => keys)
174
141
  end
175
142
 
176
- # Create an I18n flash notice if the action was successfull.
177
- # Uses the key {controller_name}.{action_name}.flash.success
178
- # or crud.{action_name}.flash.success as fallback.
179
- def success_notice
180
- key = "#{action_name}.flash.success"
181
- {:notice => t(:"#{controller_name}.#{key}",
182
- :model => full_entry_label,
183
- :default => :"crud.#{key}")}
143
+ # Html safe error messages of the current entry.
144
+ def error_messages
145
+ @@helper.safe_join(entry.errors.full_messages, '<br/>'.html_safe)
184
146
  end
185
147
 
148
+
186
149
  class << self
187
150
  # The identifier of the model used for form parameters.
188
151
  # I.e., the symbol of the underscored model name.
@@ -197,4 +160,28 @@ class CrudController < ListController
197
160
  end
198
161
  end
199
162
 
163
+ # Custom Responder that handles the controller's path_args.
164
+ # An additional :success option is used to handle action callback chain halts.
165
+ class Responder < ActionController::Responder
166
+
167
+ def initialize(controller, resources, options = {})
168
+ super(controller, with_path_args(resources, controller), options)
169
+ end
170
+
171
+ protected
172
+
173
+ # Check whether the resource has errors. Additionally checks the :success option.
174
+ def has_errors?
175
+ options[:success] == false || super
176
+ end
177
+
178
+ # Wraps the resources with the path_args for correct nesting.
179
+ def with_path_args(resources, controller)
180
+ resources.size == 1 ? Array(controller.send(:path_args, resources.first)) : resources
181
+ end
182
+
183
+ end
184
+
185
+ self.responder = Responder
186
+
200
187
  end