dry_crud 1.2.0 → 1.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +28 -11
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/lib/generators/dry_crud/templates/INSTALL +0 -1
- data/lib/generators/dry_crud/templates/app/controllers/crud_controller.rb +11 -183
- data/lib/generators/dry_crud/templates/app/controllers/list_controller.rb +212 -0
- data/lib/generators/dry_crud/templates/app/helpers/crud_helper.rb +10 -20
- data/lib/generators/dry_crud/templates/app/helpers/list_helper.rb +25 -0
- data/lib/generators/dry_crud/templates/app/helpers/standard_helper.rb +5 -4
- data/lib/generators/dry_crud/templates/app/helpers/standard_table_builder.rb +2 -2
- data/lib/generators/dry_crud/templates/app/views/crud/edit.html.erb +1 -1
- data/lib/generators/dry_crud/templates/app/views/crud/new.html.erb +1 -1
- data/lib/generators/dry_crud/templates/app/views/crud/show.html.erb +1 -1
- data/lib/generators/dry_crud/templates/app/views/layouts/crud.html.erb +5 -1
- data/lib/generators/dry_crud/templates/app/views/list/_actions_index.html.erb +0 -0
- data/lib/generators/dry_crud/templates/app/views/list/_list.html.erb +1 -0
- data/lib/generators/dry_crud/templates/app/views/list/_search.html.erb +7 -0
- data/lib/generators/dry_crud/templates/app/views/{crud → list}/index.html.erb +1 -1
- data/lib/generators/dry_crud/templates/public/stylesheets/crud.css +13 -10
- data/lib/generators/dry_crud/templates/test/crud_test_model.rb +50 -25
- data/lib/generators/dry_crud/templates/test/custom_assertions.rb +8 -0
- data/lib/generators/dry_crud/templates/test/functional/crud_test_models_controller_test.rb +23 -3
- data/lib/generators/dry_crud/templates/test/unit/custom_assertions_test.rb +18 -0
- data/lib/generators/dry_crud/templates/test/unit/helpers/crud_helper_test.rb +22 -75
- data/lib/generators/dry_crud/templates/test/unit/helpers/list_helper_test.rb +139 -0
- data/lib/generators/dry_crud/templates/test/unit/helpers/standard_form_builder_test.rb +4 -7
- data/lib/generators/dry_crud/templates/test/unit/helpers/standard_helper_test.rb +6 -10
- data/test/templates/app/controllers/people_controller.rb +1 -0
- data/test/templates/app/controllers/vips_controller.rb +24 -0
- data/test/templates/app/views/cities/_list.html.erb +3 -3
- data/test/templates/app/views/layouts/application.html.erb +33 -0
- data/test/templates/app/views/people/_list.html.erb +1 -0
- data/test/templates/config/routes.rb +2 -0
- data/test/templates/db/seeds.rb +1 -1
- data/test/templates/public/stylesheets/demo.css +113 -0
- metadata +15 -7
- data/lib/generators/dry_crud/templates/app/views/crud/_search.html.erb +0 -8
- data/test/templates/app/views/layouts/crud.html.erb +0 -26
data/README.rdoc
CHANGED
@@ -73,18 +73,18 @@ This only displays these three attributes in the table. All other templates, as
|
|
73
73
|
|
74
74
|
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
75
|
|
76
|
-
In <tt>app/controllers/
|
76
|
+
In <tt>app/controllers/list_controller.rb</tt>, change the index action to
|
77
77
|
|
78
78
|
def index
|
79
79
|
@entries = list_entries.paginate(:page => params[:page])
|
80
80
|
respond_with @entries
|
81
81
|
end
|
82
82
|
|
83
|
-
In <tt>app/views/
|
83
|
+
In <tt>app/views/list/index.html.erb</tt>, add the following line for the pagination links:
|
84
84
|
<%= will_paginate @entries %>
|
85
85
|
|
86
|
-
And we are done again. All our controllers inheriting from +
|
87
|
-
If the current page should be remembered while viewing or editing an entry, just add :page to the remembered_params in <tt>
|
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.
|
87
|
+
If the current page should be remembered while viewing or editing an entry, just add :page to the remembered_params in <tt>ListController::Memory</tt>:
|
88
88
|
|
89
89
|
controller.remember_params = [:q, :sort, :sort_dir, :page]
|
90
90
|
|
@@ -180,7 +180,9 @@ All generated files are supposed to provide a reasonable foundation for the CRUD
|
|
180
180
|
|
181
181
|
=== Controller:
|
182
182
|
|
183
|
-
{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.
|
183
|
+
{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.
|
184
|
+
|
185
|
+
{controller/list_controller.rb}[http://codez.ch/dry_crud/?q=ListController]:: Abstract controller providing a basic list action. 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.
|
184
186
|
|
185
187
|
{controller/render_inheritable.rb}[http://codez.ch/dry_crud/?q=RenderInheritable]:: A controller enhancement that allows one to render inheritable views and partials. If no view file is found for the current controller, the corresponding file is looked up in its superclass hierarchy. Thus, only views or partials that look differently have to be overwritten.
|
186
188
|
|
@@ -191,6 +193,8 @@ All generated files are supposed to provide a reasonable foundation for the CRUD
|
|
191
193
|
|
192
194
|
{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.
|
193
195
|
|
196
|
+
{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.
|
197
|
+
|
194
198
|
{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.
|
195
199
|
|
196
200
|
{helpers/standard_form_builder.rb}[http://codez.ch/dry_crud/?q=StandardFormBuilder]:: A form builder that automatically selects the corresponding input element for ActiveRecord columns. Input elements are rendered with a corresponding label by default.
|
@@ -198,25 +202,38 @@ All generated files are supposed to provide a reasonable foundation for the CRUD
|
|
198
202
|
|
199
203
|
=== Views:
|
200
204
|
|
201
|
-
|
202
|
-
|
203
|
-
views/crud/_list.html.erb:: A partial defining the table in the index view. To change the displayed attributes in your CRUD model, just create an own _list.html.erb in your models view directory.
|
205
|
+
All templates in the +crud+ folder may be 'overriden' individually in a respective view folder. Define the basic structure of your CRUD views here and adapt it as required for each single model.
|
204
206
|
|
205
|
-
views/crud/_search.html.erb:: A partial defining a simple search form that is displayed when +search_columns+ are defined in a subclassing controller.
|
206
207
|
|
207
208
|
views/crud/show.html.erb:: The show view displaying all the attributes of one entry and the various actions to perform on it.
|
208
209
|
|
209
210
|
views/crud/_attrs.html.erb:: A partial defining the attributes to be displayed in the show view.
|
210
211
|
|
212
|
+
views/crud/_list.html.erb:: A partial defining the table in the index view. To change the displayed attributes for your list model, just create an own _list.html.erb in your controller's view directory.
|
213
|
+
|
211
214
|
views/crud/new.html.erb:: The view to create a new entry.
|
212
215
|
|
213
216
|
views/crud/edit.html.erb:: The view to edit an existing entry.
|
214
217
|
|
215
|
-
views/crud/_form.html.erb:: The form used to create and edit entries. If you would like to customize this form for various models, just create an own _form.html.erb in your
|
218
|
+
views/crud/_form.html.erb:: The form used to create and edit entries. If you would like to customize this form for various models, just create an own _form.html.erb in your controller's view directory.
|
219
|
+
|
220
|
+
views/crud/_actions_index.html.erb:: The action links available in the index view.
|
221
|
+
|
222
|
+
views/crud/_actions_show.html.erb:: The action links available in the show view.
|
223
|
+
|
224
|
+
views/crud/_actions_edit.html.erb:: The action links available in the edit view.
|
225
|
+
|
226
|
+
views/list/index.html.erb:: The index view displaying a sortable table with all entries. If you have +search_columns+ defined for your controller, then a search box is rendered as well.
|
227
|
+
|
228
|
+
views/list/_list.html.erb:: A partial defining the table in the index view. To change the displayed attributes for your list model, just create an own _list.html.erb in your controller's view directory.
|
229
|
+
|
230
|
+
views/list/_search.html.erb:: A partial defining a simple search form that is displayed when +search_columns+ are defined in a subclassing controller.
|
231
|
+
|
232
|
+
views/list/_actions_index.html.erb:: The action links available in the index view. None by default.
|
216
233
|
|
217
234
|
views/shared/_labeled.html.erb:: Partial to define the layout for an arbitrary content with a label.
|
218
235
|
|
219
|
-
views/layouts/crud.html.erb:: An example layout showing how to use the @title and +flash+. Most probably you want to
|
236
|
+
views/layouts/crud.html.erb:: An example layout showing how to use the @title and +flash+. Most probably you want to merge this with your application.html.erb or adapt the main CRUD templates, so you wont need this file.
|
220
237
|
|
221
238
|
public/stylesheets/crud.css:: A simple CSS with all the classes and ids used in the CRUD code.
|
222
239
|
|
data/Rakefile
CHANGED
@@ -51,8 +51,9 @@ namespace :test do
|
|
51
51
|
desc "Initializes the test application with a couple of classes"
|
52
52
|
task :init => :generate_crud do
|
53
53
|
FileUtils.cp_r(File.join(File.dirname(__FILE__), 'test', 'templates', '.'), TEST_APP_ROOT)
|
54
|
+
FileUtils.rm_f(File.join(TEST_APP_ROOT, 'app', 'views', 'layouts', 'crud.html.erb'))
|
54
55
|
FileUtils.cd(TEST_APP_ROOT) do
|
55
|
-
sh "rake db:migrate db:test:prepare"
|
56
|
+
sh "rake db:migrate db:seed db:test:prepare"
|
56
57
|
end
|
57
58
|
end
|
58
59
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.2.
|
1
|
+
1.2.5
|
@@ -4,31 +4,24 @@
|
|
4
4
|
# Several protected helper methods are there to be (optionally) overriden by subclasses.
|
5
5
|
# With the help of additional callbacks, it is possible to hook into the action procedures without
|
6
6
|
# overriding the entire method.
|
7
|
-
class CrudController <
|
8
|
-
|
9
|
-
include RenderInheritable
|
10
|
-
|
7
|
+
class CrudController < ListController
|
8
|
+
|
11
9
|
# Set up entry object to use in the various actions.
|
12
10
|
before_filter :build_entry, :only => [:new, :create]
|
13
11
|
before_filter :set_entry, :only => [:show, :edit, :update, :destroy]
|
14
12
|
|
15
|
-
|
16
|
-
helper_method :model_class, :models_label, :full_entry_label
|
13
|
+
helper_method :full_entry_label
|
17
14
|
|
18
|
-
delegate :
|
15
|
+
delegate :model_identifier, :to => 'self.class'
|
19
16
|
|
20
|
-
hide_action :
|
21
|
-
|
22
|
-
|
23
|
-
# Callbacks
|
24
|
-
extend ActiveModel::Callbacks
|
17
|
+
hide_action :model_identifier, :run_callbacks
|
18
|
+
|
25
19
|
|
26
20
|
# Defines before and after callback hooks for create, update, save and destroy.
|
27
21
|
define_model_callbacks :create, :update, :save, :destroy
|
28
22
|
|
29
23
|
# Defines before callbacks for the render actions.
|
30
|
-
define_model_callbacks :
|
31
|
-
:render_show,
|
24
|
+
define_model_callbacks :render_show,
|
32
25
|
:render_new,
|
33
26
|
:render_edit,
|
34
27
|
:only => :before,
|
@@ -43,14 +36,7 @@ class CrudController < ApplicationController
|
|
43
36
|
|
44
37
|
|
45
38
|
############## ACTIONS ############################################
|
46
|
-
|
47
|
-
# List all entries of this model.
|
48
|
-
# GET /entries
|
49
|
-
# GET /entries.xml
|
50
|
-
def index
|
51
|
-
@entries = list_entries
|
52
|
-
respond_with @entries
|
53
|
-
end
|
39
|
+
|
54
40
|
|
55
41
|
# Show one entry of this model.
|
56
42
|
# GET /entries/1
|
@@ -112,14 +98,7 @@ class CrudController < ApplicationController
|
|
112
98
|
protected
|
113
99
|
|
114
100
|
############# CUSTOMIZABLE HELPER METHODS ##############################
|
115
|
-
|
116
|
-
# Convenience method to respond to various formats with the given object.
|
117
|
-
def respond_with(object)
|
118
|
-
respond_to do |format|
|
119
|
-
format.html { render_with_callback action_name }
|
120
|
-
format.xml { render :xml => object }
|
121
|
-
end
|
122
|
-
end
|
101
|
+
|
123
102
|
|
124
103
|
# Convenience method to respond to various formats if the performed
|
125
104
|
# action may succeed or fail. In case of failure, a standard response
|
@@ -151,12 +130,7 @@ class CrudController < ApplicationController
|
|
151
130
|
def full_entry_label
|
152
131
|
"#{models_label.singularize} '#{@entry.label}'"
|
153
132
|
end
|
154
|
-
|
155
|
-
# The entries to be displayed in the current index page.
|
156
|
-
def list_entries
|
157
|
-
model_class.scoped
|
158
|
-
end
|
159
|
-
|
133
|
+
|
160
134
|
# Redirects to the show action of a single entry.
|
161
135
|
def redirect_to_show
|
162
136
|
redirect_to @entry
|
@@ -166,13 +140,6 @@ class CrudController < ApplicationController
|
|
166
140
|
def redirect_to_index
|
167
141
|
redirect_to polymorphic_path(model_class, :returning => true)
|
168
142
|
end
|
169
|
-
|
170
|
-
# Helper method to run before_render callbacks and render the action.
|
171
|
-
# If a callback renders or redirects, the action is not rendered.
|
172
|
-
def render_with_callback(action)
|
173
|
-
run_callbacks(:"render_#{action}")
|
174
|
-
render :action => action unless performed?
|
175
|
-
end
|
176
143
|
|
177
144
|
# Saves the current entry with callbacks.
|
178
145
|
def save_entry
|
@@ -186,151 +153,12 @@ class CrudController < ApplicationController
|
|
186
153
|
end
|
187
154
|
|
188
155
|
class << self
|
189
|
-
# The ActiveRecord class of the model.
|
190
|
-
def model_class
|
191
|
-
@model_class ||= controller_name.classify.constantize
|
192
|
-
end
|
193
|
-
|
194
156
|
# The identifier of the model used for form parameters.
|
195
157
|
# I.e., the symbol of the underscored model name.
|
196
158
|
def model_identifier
|
197
159
|
@model_identifier ||= model_class.name.underscore.to_sym
|
198
160
|
end
|
199
|
-
|
200
|
-
# A human readable plural name of the model.
|
201
|
-
def models_label
|
202
|
-
@models_label ||= model_class.model_name.human.pluralize
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
# The search functionality for the index table.
|
207
|
-
# Extracted into an own module for convenience.
|
208
|
-
module Search
|
209
|
-
# Adds a :search_columns class attribute.
|
210
|
-
def self.included(controller)
|
211
|
-
# Define an array of searchable columns in your subclassing controllers.
|
212
|
-
controller.class_attribute :search_columns
|
213
|
-
controller.search_columns = []
|
214
|
-
|
215
|
-
controller.helper_method :search_support?
|
216
|
-
|
217
|
-
controller.alias_method_chain :list_entries, :search
|
218
|
-
end
|
219
|
-
|
220
|
-
protected
|
221
|
-
|
222
|
-
# Enhance the list entries with an optional search criteria
|
223
|
-
def list_entries_with_search
|
224
|
-
list_entries_without_search.where(search_condition)
|
225
|
-
end
|
226
|
-
|
227
|
-
# Compose the search condition with a basic SQL OR query.
|
228
|
-
def search_condition
|
229
|
-
if search_support? && params[:q].present?
|
230
|
-
clause = search_columns.collect {|f| "#{model_class.table_name}.#{f} LIKE ?" }.
|
231
|
-
join(" OR ")
|
232
|
-
param = "%#{params[:q]}%"
|
233
|
-
["(#{clause})"] + [param] * search_columns.size
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
# Returns true if this controller has searchable columns.
|
238
|
-
def search_support?
|
239
|
-
search_columns.present?
|
240
|
-
end
|
241
|
-
|
242
|
-
end
|
243
|
-
|
244
|
-
include Search
|
245
|
-
|
246
|
-
# Sort functionality for the index table.
|
247
|
-
# Extracted into an own module for convenience.
|
248
|
-
module Sorting
|
249
|
-
# Adds a :sort_mappings class attribute.
|
250
|
-
def self.included(controller)
|
251
|
-
# Define a map of (virtual) attributes to SQL order expressions.
|
252
|
-
# May be used for sorting table columns that do not appear directly
|
253
|
-
# in the database table. E.g., map :city_id => 'cities.name' to
|
254
|
-
# sort the displayed city names.
|
255
|
-
controller.class_attribute :sort_mappings
|
256
|
-
controller.sort_mappings = {}
|
257
|
-
|
258
|
-
controller.helper_method :sortable?
|
259
|
-
|
260
|
-
controller.alias_method_chain :list_entries, :sort
|
261
|
-
end
|
262
|
-
|
263
|
-
protected
|
264
|
-
|
265
|
-
# Enhance the list entries with an optional sort order.
|
266
|
-
def list_entries_with_sort
|
267
|
-
if params[:sort].present? && sortable?(params[:sort])
|
268
|
-
list_entries_without_sort.except(:order).order(sort_expression)
|
269
|
-
else
|
270
|
-
list_entries_without_sort
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
# Return the sort expression to be used in the list query.
|
275
|
-
def sort_expression
|
276
|
-
col = sort_mappings[params[:sort].to_sym] ||
|
277
|
-
"#{model_class.table_name}.#{params[:sort]}"
|
278
|
-
"#{col} #{sort_dir}"
|
279
|
-
end
|
280
|
-
|
281
|
-
# The sort direction, either 'asc' or 'desc'.
|
282
|
-
def sort_dir
|
283
|
-
params[:sort_dir] == 'desc' ? 'desc' : 'asc'
|
284
|
-
end
|
285
|
-
|
286
|
-
# Returns true if the passed attribute is sortable.
|
287
|
-
def sortable?(attr)
|
288
|
-
model_class.column_names.include?(attr.to_s) ||
|
289
|
-
sort_mappings.include?(attr.to_sym)
|
290
|
-
end
|
161
|
+
|
291
162
|
end
|
292
163
|
|
293
|
-
include Sorting
|
294
|
-
|
295
|
-
# Remembers certain params of the index action in order to return
|
296
|
-
# to the same list after an entry was viewed or edited.
|
297
|
-
# If the index is called with a param :returning, the remembered params
|
298
|
-
# will be re-used.
|
299
|
-
# Extracted into an own module for convenience.
|
300
|
-
module Memory
|
301
|
-
|
302
|
-
# Adds the :remember_paramas class attribute and a before filter to the index action.
|
303
|
-
def self.included(controller)
|
304
|
-
# Define a list of param keys that should be remembered for the list action.
|
305
|
-
controller.class_attribute :remember_params
|
306
|
-
controller.remember_params = [:q, :sort, :sort_dir]
|
307
|
-
|
308
|
-
controller.before_filter :handle_remember_params, :only => [:index]
|
309
|
-
end
|
310
|
-
|
311
|
-
private
|
312
|
-
|
313
|
-
# Store and restore the corresponding params.
|
314
|
-
def handle_remember_params
|
315
|
-
remembered = remembered_params
|
316
|
-
if params[:returning]
|
317
|
-
# restore params
|
318
|
-
remember_params.each {|p| params[p] ||= remembered[p] }
|
319
|
-
end
|
320
|
-
|
321
|
-
# store current params
|
322
|
-
remember_params.each {|p| remembered[p] = params[p].presence }
|
323
|
-
end
|
324
|
-
|
325
|
-
# Get the params stored in the session.
|
326
|
-
# Params are stored by request path to play nice when a controller
|
327
|
-
# is used in different routes.
|
328
|
-
def remembered_params
|
329
|
-
session[:list_params] ||= {}
|
330
|
-
session[:list_params][request.path] ||= {}
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
include Memory
|
335
|
-
|
336
164
|
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
# Abstract controller providing a basic list action.
|
2
|
+
# This action lists all entries of a certain model and provides functionality to
|
3
|
+
# search and sort this list.
|
4
|
+
# Furthermore, it remembers the last search and sort parameters. When the action
|
5
|
+
# is called with a param returning=true, these parameters are reused to present
|
6
|
+
# the user the same list as he left it.
|
7
|
+
class ListController < ApplicationController
|
8
|
+
|
9
|
+
include RenderInheritable
|
10
|
+
|
11
|
+
# Move this declaration to the application controller.
|
12
|
+
helper :standard
|
13
|
+
|
14
|
+
helper_method :model_class, :models_label
|
15
|
+
|
16
|
+
delegate :model_class, :models_label, :to => 'self.class'
|
17
|
+
|
18
|
+
hide_action :model_class, :models_label, :inheritable_root_controller
|
19
|
+
|
20
|
+
|
21
|
+
# Callbacks
|
22
|
+
extend ActiveModel::Callbacks
|
23
|
+
|
24
|
+
# Defines before callbacks for the render actions.
|
25
|
+
define_model_callbacks :render_index,
|
26
|
+
:only => :before,
|
27
|
+
:terminator => "result == false || performed?"
|
28
|
+
|
29
|
+
|
30
|
+
############## ACTIONS ############################################
|
31
|
+
|
32
|
+
# List all entries of this model.
|
33
|
+
# GET /entries
|
34
|
+
# GET /entries.xml
|
35
|
+
def index
|
36
|
+
@entries = list_entries
|
37
|
+
respond_with @entries
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
# The entries to be displayed in the current index page.
|
44
|
+
def list_entries
|
45
|
+
model_class.scoped
|
46
|
+
end
|
47
|
+
|
48
|
+
# Convenience method to respond to various formats with the given object.
|
49
|
+
def respond_with(object)
|
50
|
+
respond_to do |format|
|
51
|
+
format.html { render_with_callback action_name }
|
52
|
+
format.xml { render :xml => object }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# Helper method to run before_render callbacks and render the action.
|
58
|
+
# If a callback renders or redirects, the action is not rendered.
|
59
|
+
def render_with_callback(action)
|
60
|
+
run_callbacks(:"render_#{action}")
|
61
|
+
render :action => action unless performed?
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
class << self
|
66
|
+
# The ActiveRecord class of the model.
|
67
|
+
def model_class
|
68
|
+
@model_class ||= controller_name.classify.constantize
|
69
|
+
end
|
70
|
+
|
71
|
+
# A human readable plural name of the model.
|
72
|
+
def models_label
|
73
|
+
@models_label ||= model_class.model_name.human.pluralize
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# The search functionality for the index table.
|
78
|
+
# Extracted into an own module for convenience.
|
79
|
+
module Search
|
80
|
+
def self.included(controller)
|
81
|
+
# Define an array of searchable columns in your subclassing controllers.
|
82
|
+
controller.class_attribute :search_columns
|
83
|
+
controller.search_columns = []
|
84
|
+
|
85
|
+
controller.helper_method :search_support?
|
86
|
+
|
87
|
+
controller.alias_method_chain :list_entries, :search
|
88
|
+
end
|
89
|
+
|
90
|
+
protected
|
91
|
+
|
92
|
+
# Enhance the list entries with an optional search criteria
|
93
|
+
def list_entries_with_search
|
94
|
+
list_entries_without_search.where(search_condition)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Compose the search condition with a basic SQL OR query.
|
98
|
+
def search_condition
|
99
|
+
if search_support? && params[:q].present?
|
100
|
+
clause = search_columns.collect {|f| "#{model_class.table_name}.#{f} LIKE ?" }.
|
101
|
+
join(" OR ")
|
102
|
+
param = "%#{params[:q]}%"
|
103
|
+
["(#{clause})"] + [param] * search_columns.size
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns true if this controller has searchable columns.
|
108
|
+
def search_support?
|
109
|
+
search_columns.present?
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
include Search
|
115
|
+
|
116
|
+
# Sort functionality for the index table.
|
117
|
+
# Extracted into an own module for convenience.
|
118
|
+
module Sorting
|
119
|
+
# Adds a :sort_mappings class attribute.
|
120
|
+
def self.included(controller)
|
121
|
+
# Define a map of (virtual) attributes to SQL order expressions.
|
122
|
+
# May be used for sorting table columns that do not appear directly
|
123
|
+
# in the database table. E.g., map :city_id => 'cities.name' to
|
124
|
+
# sort the displayed city names.
|
125
|
+
controller.class_attribute :sort_mappings
|
126
|
+
controller.sort_mappings = {}
|
127
|
+
|
128
|
+
controller.helper_method :sortable?
|
129
|
+
|
130
|
+
controller.alias_method_chain :list_entries, :sort
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
|
135
|
+
# Enhance the list entries with an optional sort order.
|
136
|
+
def list_entries_with_sort
|
137
|
+
if params[:sort].present? && sortable?(params[:sort])
|
138
|
+
list_entries_without_sort.except(:order).order(sort_expression)
|
139
|
+
else
|
140
|
+
list_entries_without_sort
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Return the sort expression to be used in the list query.
|
145
|
+
def sort_expression
|
146
|
+
col = sort_mappings[params[:sort].to_sym] ||
|
147
|
+
"#{model_class.table_name}.#{params[:sort]}"
|
148
|
+
"#{col} #{sort_dir}"
|
149
|
+
end
|
150
|
+
|
151
|
+
# The sort direction, either 'asc' or 'desc'.
|
152
|
+
def sort_dir
|
153
|
+
params[:sort_dir] == 'desc' ? 'desc' : 'asc'
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns true if the passed attribute is sortable.
|
157
|
+
def sortable?(attr)
|
158
|
+
model_class.column_names.include?(attr.to_s) ||
|
159
|
+
sort_mappings.include?(attr.to_sym)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
include Sorting
|
164
|
+
|
165
|
+
# Remembers certain params of the index action in order to return
|
166
|
+
# to the same list after an entry was viewed or edited.
|
167
|
+
# If the index is called with a param :returning, the remembered params
|
168
|
+
# will be re-used.
|
169
|
+
# Extracted into an own module for convenience.
|
170
|
+
module Memory
|
171
|
+
|
172
|
+
# Adds the :remember_params class attribute and a before filter to the index action.
|
173
|
+
def self.included(controller)
|
174
|
+
# Define a list of param keys that should be remembered for the list action.
|
175
|
+
controller.class_attribute :remember_params
|
176
|
+
controller.remember_params = [:q, :sort, :sort_dir]
|
177
|
+
|
178
|
+
controller.before_filter :handle_remember_params, :only => [:index]
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
# Store and restore the corresponding params.
|
184
|
+
def handle_remember_params
|
185
|
+
remembered = remembered_params
|
186
|
+
if params[:returning]
|
187
|
+
# restore params
|
188
|
+
remember_params.each {|p| params[p] ||= remembered[p] }
|
189
|
+
end
|
190
|
+
|
191
|
+
# store current params
|
192
|
+
remember_params.each do |p|
|
193
|
+
remembered[p] = params[p].presence
|
194
|
+
remembered.delete(p) if remembered[p].nil?
|
195
|
+
end
|
196
|
+
|
197
|
+
# clear void params
|
198
|
+
session[:list_params].delete(request.path) if remembered.blank?
|
199
|
+
end
|
200
|
+
|
201
|
+
# Get the params stored in the session.
|
202
|
+
# Params are stored by request path to play nice when a controller
|
203
|
+
# is used in different routes.
|
204
|
+
def remembered_params
|
205
|
+
session[:list_params] ||= {}
|
206
|
+
session[:list_params][request.path] ||= {}
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
include Memory
|
211
|
+
|
212
|
+
end
|
@@ -6,23 +6,21 @@ module CrudHelper
|
|
6
6
|
# Create a table of the @entries variable with the default or
|
7
7
|
# the passed attributes in its columns.
|
8
8
|
def crud_table(*attrs, &block)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
add_list_actions(t)
|
17
|
-
end
|
9
|
+
if block_given?
|
10
|
+
list_table(*attrs, &block)
|
11
|
+
else
|
12
|
+
attrs = default_attrs if attrs.blank?
|
13
|
+
list_table(*attrs) do |t|
|
14
|
+
add_list_actions(t)
|
15
|
+
end
|
18
16
|
end
|
19
17
|
end
|
20
18
|
|
21
19
|
# Adds a set of standard action link column (show, edit, destroy) to the given table.
|
22
20
|
def add_list_actions(table)
|
23
|
-
table.col { |e| link_action_show(e) }
|
24
|
-
table.col { |e| link_action_edit(e) }
|
25
|
-
table.col { |e| link_action_destroy(e) }
|
21
|
+
table.col('', :class => 'center') { |e| link_action_show(e) }
|
22
|
+
table.col('', :class => 'center') { |e| link_action_edit(e) }
|
23
|
+
table.col('', :class => 'center') { |e| link_action_destroy(e) }
|
26
24
|
end
|
27
25
|
|
28
26
|
# Renders a generic form for the current entry with :default_attrs or the
|
@@ -36,12 +34,4 @@ module CrudHelper
|
|
36
34
|
standard_form(@entry, attrs, &block)
|
37
35
|
end
|
38
36
|
|
39
|
-
# The default attributes to use in attrs, list and form partials.
|
40
|
-
# These are all defined attributes except certain special ones like 'id' or 'position'.
|
41
|
-
def default_attrs
|
42
|
-
attrs = model_class.column_names.collect(&:to_sym)
|
43
|
-
[:id, :position].each {|a| attrs.delete(a) }
|
44
|
-
attrs
|
45
|
-
end
|
46
|
-
|
47
37
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Extension of StandardHelper functionality to provide a set of default
|
2
|
+
# attributes for the current model to be used in tables and forms. This helper
|
3
|
+
# is included in CrudController.
|
4
|
+
module ListHelper
|
5
|
+
|
6
|
+
# Create a table of the @entries variable with the default or
|
7
|
+
# the passed attributes in its columns.
|
8
|
+
def list_table(*attrs, &block)
|
9
|
+
# only use default attrs if no attrs and no block are given
|
10
|
+
attributes = (block_given? || attrs.present?) ? attrs : default_attrs
|
11
|
+
table(@entries) do |t|
|
12
|
+
t.sortable_attrs(*attributes)
|
13
|
+
yield t if block_given?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# The default attributes to use in attrs, list and form partials.
|
18
|
+
# These are all defined attributes except certain special ones like 'id' or 'position'.
|
19
|
+
def default_attrs
|
20
|
+
attrs = model_class.column_names.collect(&:to_sym)
|
21
|
+
[:id, :position].each {|a| attrs.delete(a) }
|
22
|
+
attrs
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -178,11 +178,12 @@ module StandardHelper
|
|
178
178
|
val = obj.send(attr)
|
179
179
|
return EMPTY_STRING if val.nil?
|
180
180
|
case column_type(obj, attr)
|
181
|
-
when :time then val.
|
182
|
-
when :date then val.to_date
|
183
|
-
when :
|
181
|
+
when :time then f(val.to_time)
|
182
|
+
when :date then f(val.to_date)
|
183
|
+
when :datetime, :timestamp then "#{f(val.to_date)} #{f(val.to_time)}"
|
184
|
+
when :text then val.present? ? simple_format(h(val)) : EMPTY_STRING
|
184
185
|
when :decimal then f(val.to_s.to_f)
|
185
|
-
|
186
|
+
else f(val)
|
186
187
|
end
|
187
188
|
end
|
188
189
|
|
@@ -65,9 +65,9 @@ class StandardTableBuilder
|
|
65
65
|
entry = entries.first
|
66
66
|
case column_type(entry, attr)
|
67
67
|
when :integer, :float, :decimal
|
68
|
-
'
|
68
|
+
'right' unless association(entry, attr, :belongs_to)
|
69
69
|
when :boolean
|
70
|
-
'
|
70
|
+
'center'
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|