dry_crud 1.2.7 → 1.3.0

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 (54) hide show
  1. data/README.rdoc +60 -27
  2. data/Rakefile +3 -1
  3. data/VERSION +1 -1
  4. data/lib/generators/dry_crud/dry_crud_generator.rb +3 -3
  5. data/lib/generators/dry_crud/templates/INSTALL +3 -1
  6. data/lib/generators/dry_crud/templates/app/controllers/crud_controller.rb +106 -90
  7. data/lib/generators/dry_crud/templates/app/controllers/list_controller.rb +90 -74
  8. data/lib/generators/dry_crud/templates/app/controllers/render_inheritable.rb +34 -33
  9. data/lib/generators/dry_crud/templates/app/helpers/crud_helper.rb +39 -23
  10. data/lib/generators/dry_crud/templates/app/helpers/list_helper.rb +11 -9
  11. data/lib/generators/dry_crud/templates/app/helpers/standard_form_builder.rb +55 -47
  12. data/lib/generators/dry_crud/templates/app/helpers/standard_helper.rb +134 -86
  13. data/lib/generators/dry_crud/templates/app/helpers/standard_table_builder.rb +41 -35
  14. data/lib/generators/dry_crud/templates/app/views/crud/_actions_edit.html.erb +1 -0
  15. data/lib/generators/dry_crud/templates/app/views/crud/edit.html.erb +3 -3
  16. data/lib/generators/dry_crud/templates/app/views/crud/new.html.erb +2 -2
  17. data/lib/generators/dry_crud/templates/app/views/crud/show.html.erb +3 -3
  18. data/lib/generators/dry_crud/templates/app/views/layouts/crud.html.erb +9 -7
  19. data/lib/generators/dry_crud/templates/app/views/list/_search.html.erb +1 -1
  20. data/lib/generators/dry_crud/templates/app/views/list/index.html.erb +4 -4
  21. data/lib/generators/dry_crud/templates/app/views/shared/_error_messages.html.erb +3 -1
  22. data/lib/generators/dry_crud/templates/config/locales/en_crud.yml +63 -0
  23. data/lib/generators/dry_crud/templates/test/crud_test_model.rb +93 -58
  24. data/lib/generators/dry_crud/templates/test/custom_assertions.rb +24 -13
  25. data/lib/generators/dry_crud/templates/test/functional/crud_controller_test_helper.rb +26 -56
  26. data/lib/generators/dry_crud/templates/test/functional/crud_test_models_controller_test.rb +47 -41
  27. data/lib/generators/dry_crud/templates/test/unit/custom_assertions_test.rb +28 -24
  28. data/lib/generators/dry_crud/templates/test/unit/helpers/crud_helper_test.rb +20 -34
  29. data/lib/generators/dry_crud/templates/test/unit/helpers/list_helper_test.rb +39 -53
  30. data/lib/generators/dry_crud/templates/test/unit/helpers/render_inheritable_test.rb +33 -33
  31. data/lib/generators/dry_crud/templates/test/unit/helpers/standard_form_builder_test.rb +27 -27
  32. data/lib/generators/dry_crud/templates/test/unit/helpers/standard_helper_test.rb +103 -50
  33. data/lib/generators/dry_crud/templates/test/unit/helpers/standard_table_builder_test.rb +52 -24
  34. data/test/templates/Gemfile +34 -0
  35. data/test/templates/app/controllers/ajax_controller.rb +3 -3
  36. data/test/templates/app/controllers/application_controller.rb +1 -1
  37. data/test/templates/app/controllers/cities_controller.rb +2 -5
  38. data/test/templates/app/controllers/people_controller.rb +5 -5
  39. data/test/templates/app/controllers/vips_controller.rb +6 -11
  40. data/test/templates/app/helpers/people_helper.rb +2 -2
  41. data/test/templates/app/models/city.rb +9 -9
  42. data/test/templates/app/models/person.rb +5 -4
  43. data/test/templates/app/views/ajax/_actions_index.html.erb +2 -2
  44. data/test/templates/app/views/cities/_form.html.erb +5 -1
  45. data/test/templates/app/views/layouts/_menu.html.erb +3 -3
  46. data/test/templates/app/views/people/_attrs.html.erb +3 -3
  47. data/test/templates/config/database.yml +22 -0
  48. data/test/templates/config/locales/en_cities.yml +56 -0
  49. data/test/templates/config/routes.rb +5 -5
  50. data/test/templates/db/migrate/20100511174904_create_people_and_cities.rb +5 -2
  51. data/test/templates/db/seeds.rb +38 -29
  52. data/test/templates/test/functional/cities_controller_test.rb +12 -12
  53. data/test/templates/test/functional/people_controller_test.rb +10 -10
  54. metadata +11 -7
@@ -1,34 +1,25 @@
1
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.
2
+ # This action lists all entries of a certain model and provides functionality to
3
+ # search and sort this list.
4
4
  # Furthermore, it remembers the last search and sort parameters. When the action
5
5
  # is called with a param returning=true, these parameters are reused to present
6
- # the user the same list as he left it.
6
+ # the user the same list as he left it.
7
7
  class ListController < ApplicationController
8
-
9
- include RenderInheritable
10
-
8
+
9
+ include RenderInheritable
10
+
11
11
  # Move this declaration to the application controller.
12
12
  helper :standard
13
-
13
+
14
14
  helper_method :model_class, :models_label
15
-
16
- delegate :model_class, :models_label, :to => 'self.class'
17
-
15
+
16
+ delegate :model_class, :models_label, :to => 'self.class'
17
+
18
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
-
19
+
20
+
30
21
  ############## ACTIONS ############################################
31
-
22
+
32
23
  # List all entries of this model.
33
24
  # GET /entries
34
25
  # GET /entries.xml
@@ -36,15 +27,14 @@ class ListController < ApplicationController
36
27
  @entries = list_entries
37
28
  respond_with @entries
38
29
  end
39
-
40
-
30
+
41
31
  protected
42
-
32
+
43
33
  # The entries to be displayed in the current index page.
44
34
  def list_entries
45
35
  model_class.scoped
46
- end
47
-
36
+ end
37
+
48
38
  # Convenience method to respond to various formats with the given object.
49
39
  def respond_with(object)
50
40
  respond_to do |format|
@@ -52,47 +42,61 @@ class ListController < ApplicationController
52
42
  format.xml { render :xml => object }
53
43
  end
54
44
  end
55
-
45
+
56
46
  # Helper method to run before_render callbacks and render the action.
57
47
  # If a callback renders or redirects, the action is not rendered.
58
48
  def render_with_callback(action)
59
49
  run_callbacks(:"render_#{action}")
60
- render :action => action unless performed?
50
+ render action unless performed?
61
51
  end
62
-
63
-
52
+
64
53
  class << self
54
+ # Callbacks
55
+ include ActiveModel::Callbacks
56
+
65
57
  # The ActiveRecord class of the model.
66
58
  def model_class
67
59
  @model_class ||= controller_name.classify.constantize
68
60
  end
69
-
61
+
70
62
  # A human readable plural name of the model.
71
- def models_label
72
- @models_label ||= model_class.model_name.human.pluralize
73
- end
63
+ def models_label(plural = true)
64
+ opts = {:count => (plural ? 3 : 1)}
65
+ opts[:default] = model_class.model_name.human.pluralize if plural
66
+ model_class.model_name.human(opts)
67
+ end
68
+
69
+ # Defines before callbacks for the render actions.
70
+ def define_render_callbacks(*actions)
71
+ args = actions.collect {|a| :"render_#{a}" }
72
+ args << {:only => :before,
73
+ :terminator => "result == false || performed?"}
74
+ define_model_callbacks *args
75
+ end
74
76
  end
75
77
 
78
+ define_render_callbacks :index
79
+
76
80
  # The search functionality for the index table.
77
81
  # Extracted into an own module for convenience.
78
82
  module Search
79
- def self.included(controller)
83
+ def self.included(controller)
80
84
  # Define an array of searchable columns in your subclassing controllers.
81
85
  controller.class_attribute :search_columns
82
86
  controller.search_columns = []
83
-
87
+
84
88
  controller.helper_method :search_support?
85
-
89
+
86
90
  controller.alias_method_chain :list_entries, :search
87
91
  end
88
-
92
+
89
93
  protected
90
-
94
+
91
95
  # Enhance the list entries with an optional search criteria
92
96
  def list_entries_with_search
93
97
  list_entries_without_search.where(search_condition)
94
98
  end
95
-
99
+
96
100
  # Compose the search condition with a basic SQL OR query.
97
101
  def search_condition
98
102
  if search_support? && params[:q].present?
@@ -102,16 +106,16 @@ class ListController < ApplicationController
102
106
  ["(#{clause})"] + [param] * search_columns.size
103
107
  end
104
108
  end
105
-
109
+
106
110
  # Returns true if this controller has searchable columns.
107
111
  def search_support?
108
112
  search_columns.present?
109
113
  end
110
-
114
+
111
115
  end
112
-
116
+
113
117
  include Search
114
-
118
+
115
119
  # Sort functionality for the index table.
116
120
  # Extracted into an own module for convenience.
117
121
  module Sort
@@ -123,14 +127,14 @@ class ListController < ApplicationController
123
127
  # sort the displayed city names.
124
128
  controller.class_attribute :sort_mappings
125
129
  controller.sort_mappings = {}
126
-
130
+
127
131
  controller.helper_method :sortable?
128
-
132
+
129
133
  controller.alias_method_chain :list_entries, :sort
130
134
  end
131
-
135
+
132
136
  protected
133
-
137
+
134
138
  # Enhance the list entries with an optional sort order.
135
139
  def list_entries_with_sort
136
140
  if params[:sort].present? && sortable?(params[:sort])
@@ -139,73 +143,85 @@ class ListController < ApplicationController
139
143
  list_entries_without_sort
140
144
  end
141
145
  end
142
-
146
+
143
147
  # Return the sort expression to be used in the list query.
144
148
  def sort_expression
145
- col = sort_mappings[params[:sort].to_sym] ||
149
+ col = sort_mappings[params[:sort].to_sym] ||
146
150
  "#{model_class.table_name}.#{params[:sort]}"
147
151
  "#{col} #{sort_dir}"
148
152
  end
149
-
153
+
150
154
  # The sort direction, either 'asc' or 'desc'.
151
155
  def sort_dir
152
156
  params[:sort_dir] == 'desc' ? 'desc' : 'asc'
153
157
  end
154
-
158
+
155
159
  # Returns true if the passed attribute is sortable.
156
160
  def sortable?(attr)
157
- model_class.column_names.include?(attr.to_s) ||
161
+ model_class.column_names.include?(attr.to_s) ||
158
162
  sort_mappings.include?(attr.to_sym)
159
163
  end
160
164
  end
161
-
165
+
162
166
  include Sort
163
-
167
+
164
168
  # Remembers certain params of the index action in order to return
165
169
  # to the same list after an entry was viewed or edited.
166
170
  # If the index is called with a param :returning, the remembered params
167
171
  # will be re-used.
168
172
  # Extracted into an own module for convenience.
169
173
  module Memory
170
-
174
+
171
175
  # Adds the :remember_params class attribute and a before filter to the index action.
172
- def self.included(controller)
176
+ def self.included(controller)
173
177
  # Define a list of param keys that should be remembered for the list action.
174
178
  controller.class_attribute :remember_params
175
179
  controller.remember_params = [:q, :sort, :sort_dir]
176
-
180
+
177
181
  controller.before_filter :handle_remember_params, :only => [:index]
178
182
  end
179
-
183
+
180
184
  private
181
-
185
+
182
186
  # Store and restore the corresponding params.
183
187
  def handle_remember_params
184
188
  remembered = remembered_params
189
+
190
+ restore_params_on_return(remembered)
191
+ store_current_params(remembered)
192
+ clear_void_params(remembered)
193
+ end
194
+
195
+ def restore_params_on_return(remembered)
185
196
  if params[:returning]
186
- # restore params
187
197
  remember_params.each {|p| params[p] ||= remembered[p] }
188
198
  end
189
-
190
- # store current params
191
- remember_params.each do |p|
199
+ end
200
+
201
+ def store_current_params(remembered)
202
+ remember_params.each do |p|
192
203
  remembered[p] = params[p].presence
193
204
  remembered.delete(p) if remembered[p].nil?
194
205
  end
195
-
196
- # clear void params
197
- session[:list_params].delete(request.path) if remembered.blank?
198
206
  end
199
-
207
+
208
+ def clear_void_params(remembered)
209
+ session[:list_params].delete(remember_key) if remembered.blank?
210
+ end
211
+
200
212
  # Get the params stored in the session.
201
- # Params are stored by request path to play nice when a controller
202
- # is used in different routes.
203
213
  def remembered_params
204
214
  session[:list_params] ||= {}
205
- session[:list_params][request.path] ||= {}
215
+ session[:list_params][remember_key] ||= {}
216
+ end
217
+
218
+ # Params are stored by request path to play nice when a controller
219
+ # is used in different routes.
220
+ def remember_key
221
+ request.path
206
222
  end
207
223
  end
208
-
224
+
209
225
  include Memory
210
-
226
+
211
227
  end
@@ -2,55 +2,56 @@
2
2
  # If no view file is found for the current controller, the corresponding file
3
3
  # is looked up in its superclass hierarchy. This module must only be
4
4
  # included in the root controller of the desired lookup hierarchy.
5
- #
5
+ #
6
6
  # By default, this module only supports direct inheritance over one level. By overriding
7
7
  # the method lookup_path, you may define a custom lookup path. By providing an object
8
8
  # for the 'with' parameter, this path may even be dynamic.
9
9
  module RenderInheritable
10
-
10
+
11
11
  # Add inheritable_root_path method to including controller.
12
12
  def self.included(controller_class)
13
13
  controller_class.send(:extend, ClassMethods)
14
-
14
+
15
15
  controller_class.send(:class_variable_set, :@@inheritable_root_controller, controller_class)
16
16
  controller_class.cattr_reader :inheritable_root_controller
17
- end
18
-
17
+ end
18
+
19
19
  # Performs a lookup for the given filename and returns the most specific
20
20
  # folder that contains the file.
21
21
  def find_inheritable_template_folder(name, partial = false)
22
22
  self.class.find_inheritable_template_folder(view_context, name, partial, formats, template_lookup_param)
23
23
  end
24
-
24
+
25
25
  # Override this method to specify a dynamic parameter used in the lookup path.
26
26
  # For the default inheritance lookup, this parameter is not needed.
27
27
  def template_lookup_param
28
28
  nil
29
29
  end
30
-
30
+
31
31
  module ClassMethods
32
+
32
33
  # Performs a lookup for the given filename and returns the most specific
33
34
  # folder that contains the file.
34
- def find_inheritable_template_folder(view_context, name, partial, formats, param = nil)
35
+ def find_inheritable_template_folder(view_context, name, partial, formats, param = nil)
35
36
  find_inheritable_template_folder_cached(view_context, name, partial, formats, param) do
36
37
  find_inheritable_artifact(param) do |folder|
37
38
  view_context.template_exists?(name, folder, partial)
38
39
  end
39
40
  end
40
41
  end
41
-
42
+
42
43
  # Performs a lookup for a controller and returns the name of the most specific one found.
43
44
  # This method is primarly usefull when given a 'param' argument that is used
44
- # in a custom #template_lookup_path. In this case, no controller class would need to
45
+ # in a custom #template_lookup_path. In this case, no controller class would need to
45
46
  # exist to render templates from corresponding view folders.
46
47
  def inheritable_controller(param = nil)
47
48
  descendants = inheritable_root_controller.descendants
48
49
  c = find_inheritable_artifact(param) do |folder|
49
- descendants.any? { |s| s.controller_path == folder }
50
+ descendants.any? { |s| s.controller_path == folder }
50
51
  end
51
52
  c || inheritable_root_controller.controller_path
52
53
  end
53
-
54
+
54
55
  # Runs through the lookup path and yields each folder to the passed block.
55
56
  # If the block returns true, this folder is returned and no further lookup
56
57
  # happens. If no folder is found, the nil is returned.
@@ -58,14 +59,14 @@ module RenderInheritable
58
59
  template_lookup_path(param).each { |folder| return folder if yield(folder) }
59
60
  nil
60
61
  end
61
-
62
+
62
63
  # An array of controller names / folders, ordered from most specific to most general.
63
- # May be dynamic dependening on the passed 'param' argument.
64
+ # May be dynamic dependening on the passed 'param' argument.
64
65
  # You may override this method in an own controller to customize the lookup path.
65
66
  def template_lookup_path(param = nil)
66
67
  inheritance_lookup_path
67
68
  end
68
-
69
+
69
70
  # The inheritance path of controllers that is used as default lookup path.
70
71
  def inheritance_lookup_path
71
72
  path = [self]
@@ -74,8 +75,8 @@ module RenderInheritable
74
75
  end
75
76
  path.collect(&:controller_path)
76
77
  end
77
-
78
- # Override view context class to includes the render inheritable modules.
78
+
79
+ # Override view context class to include the render inheritable modules.
79
80
  def view_context_class
80
81
  @view_context_class ||= begin
81
82
  Class.new(super) do
@@ -83,46 +84,46 @@ module RenderInheritable
83
84
  end
84
85
  end
85
86
  end
86
-
87
+
87
88
  private
88
-
89
+
89
90
  # Performs a lookup for a template folder using the cache.
90
91
  def find_inheritable_template_folder_cached(view_context, name, partial, formats, param = nil)
91
92
  prefix = inheritable_cache_get(formats, name, partial, param)
92
93
  return prefix if prefix
93
-
94
+
94
95
  prefix = yield
95
-
96
+
96
97
  if prefix
97
98
  template = view_context.find_template_without_lookup(name, prefix, partial)
98
99
  inheritable_cache_set(template.formats, name, partial, param, prefix)
99
100
  end
100
101
  prefix
101
102
  end
102
-
103
+
103
104
  # A simple template lookup cache for each controller.
104
105
  def inheritable_cache #:nodoc:
105
106
  # do not store keys on each access, only return default structure
106
- @inheritable_cache ||= Hash.new do |h1, k1|
107
- Hash.new do |h2, k2|
108
- Hash.new do |h3, k3|
109
- Hash.new
107
+ @inheritable_cache ||= Hash.new do |h1, k1|
108
+ Hash.new do |h2, k2|
109
+ Hash.new do |h3, k3|
110
+ Hash.new
110
111
  end
111
112
  end
112
113
  end
113
114
  end
114
-
115
+
115
116
  # Gets the prefix from the cache. Returns nil if it's not there yet.
116
117
  def inheritable_cache_get(formats, name, partial, param)
117
118
  prefixes = formats.collect { |format| inheritable_cache[format.to_sym][partial][name][param] }
118
119
  prefixes.compact!
119
120
  prefixes.empty? ? nil : prefixes.first
120
121
  end
121
-
122
+
122
123
  # Stores the found prefix in the cache.
123
124
  def inheritable_cache_set(formats, name, partial, param, prefix)
124
125
  formats.each do |format|
125
- # assign hash default values to respective key
126
+ # assign hash default values to respective key
126
127
  inheritable_cache[format.to_sym] = hf = inheritable_cache[format.to_sym]
127
128
  hf[partial] = hp = hf[partial]
128
129
  hp[name] = hn = hp[name]
@@ -130,15 +131,15 @@ module RenderInheritable
130
131
  hn[param] = prefix
131
132
  end
132
133
  end
133
-
134
+
134
135
  end
135
-
136
+
136
137
  # Extend ActionView so templates are looked up on a find_template call.
137
138
  module View
138
139
  def self.included(base)
139
140
  base.send :alias_method_chain, :find_template, :lookup
140
141
  end
141
-
142
+
142
143
  # Perform a template lookup if the prefix corresponds to the current controller's path.
143
144
  def find_template_with_lookup(name, prefix = nil, partial = false)
144
145
  if prefix == controller_path
@@ -148,5 +149,5 @@ module RenderInheritable
148
149
  find_template_without_lookup(name, prefix, partial)
149
150
  end
150
151
  end
151
-
152
+
152
153
  end