dry_crud 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. data/README.rdoc +31 -12
  2. data/Rakefile +15 -5
  3. data/VERSION +1 -1
  4. data/lib/generators/dry_crud/templates/app/controllers/crud_controller.rb +37 -20
  5. data/lib/generators/dry_crud/templates/app/helpers/standard_form_builder.rb +17 -12
  6. data/lib/generators/dry_crud/templates/app/helpers/standard_helper.rb +23 -23
  7. data/lib/generators/dry_crud/templates/app/helpers/standard_table_builder.rb +11 -3
  8. data/lib/generators/dry_crud/templates/app/views/crud/_search.html.erb +6 -0
  9. data/lib/generators/dry_crud/templates/app/views/crud/index.html.erb +2 -0
  10. data/lib/generators/dry_crud/templates/test/crud_test_model.rb +10 -2
  11. data/lib/generators/dry_crud/templates/test/custom_assertions.rb +59 -0
  12. data/lib/generators/dry_crud/templates/test/functional/crud_controller_test_helper.rb +18 -7
  13. data/lib/generators/dry_crud/templates/test/functional/crud_test_models_controller_test.rb +6 -1
  14. data/lib/generators/dry_crud/templates/test/unit/custom_assertions_test.rb +95 -0
  15. data/lib/generators/dry_crud/templates/test/unit/{crud_helper_test.rb → helpers/crud_helper_test.rb} +0 -0
  16. data/lib/generators/dry_crud/templates/test/unit/{render_inheritable_test.rb → helpers/render_inheritable_test.rb} +0 -0
  17. data/lib/generators/dry_crud/templates/test/unit/{standard_form_builder_test.rb → helpers/standard_form_builder_test.rb} +31 -2
  18. data/lib/generators/dry_crud/templates/test/unit/{standard_helper_test.rb → helpers/standard_helper_test.rb} +25 -6
  19. data/lib/generators/dry_crud/templates/test/unit/{standard_table_builder_test.rb → helpers/standard_table_builder_test.rb} +0 -0
  20. data/test/templates/app/controllers/people_controller.rb +4 -0
  21. data/test/templates/app/helpers/people_helper.rb +2 -1
  22. data/test/templates/app/views/ajax/index.html.erb +1 -6
  23. data/test/templates/app/views/layouts/crud.html.erb +26 -0
  24. data/test/templates/app/views/people/_attrs.html.erb +7 -0
  25. metadata +14 -10
  26. data/test/templates/app/views/cities/_attrs.html.erb +0 -1
data/README.rdoc CHANGED
@@ -6,7 +6,7 @@ DRY CRUD generates simple and extendable controller, views and helpers that supp
6
6
 
7
7
  In order to use the generator, you have to register the gem in your Rails application's +Gemfile+. Add the following lines:
8
8
 
9
- group :development, :test do
9
+ group :development do
10
10
  gem 'dry_crud'
11
11
  end
12
12
 
@@ -98,6 +98,16 @@ In <tt>app/helpers/people.rb</tt>:
98
98
  By the way: The method +:f+ in +StandardHelper+ uniformly formats arbitrary values according to their class.
99
99
 
100
100
 
101
+ ==== Filtering the index list
102
+
103
+ There is a simple search functionality (based on SQL LIKE queries) implemented in the +CrudController+. Define an array of columns in your controller's +search_columns+ class variable to make the entries searchable by these fields:
104
+
105
+ In <tt>app/controllers/people_controller.rb</tt>:
106
+ self.search_columns = [:firstname, :lastname]
107
+
108
+ If you have columns defined, a search box will be displayed in the index view that let's you filter the displayed entries.
109
+
110
+
101
111
  ==== CRUD controller callbacks
102
112
 
103
113
  As a last example, let's say we have added a custom input field that must specially processed. Instead of overwriting the entire update action, it is possible to register callbacks for the +create+, +update+, +save+ (= +create+ and +update+) and +destroy+ actions. They work very similarliy like the callbacks on ActiveRecord. For each action, before and after callbacks are run. Before callbacks may also prevent the action from being executed when returning false. Here is some code:
@@ -112,7 +122,7 @@ In <tt>app/controllers/people_controller.rb</tt>:
112
122
 
113
123
  def delete_picture
114
124
  if !perform_delete_picture(@entry.picture)
115
- flash[:error] = 'Could not delete picture'
125
+ flash[:alert] = 'Could not delete picture'
116
126
  false
117
127
  end
118
128
  end
@@ -120,7 +130,7 @@ In <tt>app/controllers/people_controller.rb</tt>:
120
130
 
121
131
  === Standard Tables and Forms
122
132
 
123
- DRY CRUD also provides two builder classes for update/create forms and tables for displaying entries of one model. They may be used all over your application to DRY up the form and table code. Normally, they are used with the corresponding methods from +StandardHelper+.
133
+ DRY CRUD also provides two builder classes for update/create forms and tables for displaying entries of one model. They may be used all over your application to DRY up the form and table code. Normally, they are used with the corresponding methods from +StandardHelper+. When you define a view for a subclass of +CrudController+, you may also use the slightly enhanced +crud_table+ and +crud_form+ methods from +CrudHelper+.
124
134
 
125
135
  ==== Tables
126
136
 
@@ -129,13 +139,13 @@ This is the code to define a table with some attribute columns for a list of sam
129
139
  t.attrs :lastname, :firstname
130
140
  end %>
131
141
 
132
- If entries is empty, a basic 'No entries available' message is rendered instead of the table.
142
+ If entries is empty, a basic 'No entries found' message is rendered instead of the table.
133
143
 
134
144
  To render custom columns, use the :col method:
135
145
  <%= table(@people) do |t|
136
146
  t.attrs :lastname, :firstname
137
147
  t.col('', :class => 'center') {|entry| image_tag(entry.picture) }
138
- t.attrs :street
148
+ t.attrs :street
139
149
  t.col('Map') {|entry| link_to(entry.city, "http://maps.google.com/?q=#{entry.city}" }
140
150
  end %>
141
151
 
@@ -143,18 +153,23 @@ To render custom columns, use the :col method:
143
153
 
144
154
  Forms work very similar. In the most simple case, you just have to specify which attributes of a model to create input fields for, and you get a complete form with error messages, labeled input fields according the column types and a save button:
145
155
 
146
- <% form(@person, [:firstname, :lastname, :age, :city] -%>
156
+ <%= standard_form(@person, [:firstname, :lastname, :age, :city] -%>
147
157
 
148
158
  Of course, custom input fields may be defined as well:
149
- <% form(@person, [], :url => {:action => 'custom_update', :id => @person.id}) do |f| -%>
159
+ <%= standard_form(@person, [], :url => {:action => 'custom_update', :id => @person.id}) do |f| %>
150
160
  <%= f.labeled_input_fields :firstname, :lastname %>
151
- <% f.labeled(:sex) do %>
161
+ <%= f.labeled(:sex) do %>
152
162
  <%= f.radio_button :sex, true %> female
153
163
  <%= f.radio_button :sex, false %> male
154
- <% end -%>
164
+ <% end %>
155
165
  <%= f.labeled_integer_field :age %>
156
166
  <%= f.labeled_file_field :picture %>
157
- <% end -%>
167
+ <% end %>
168
+
169
+ Even +belongs_to+ associations are automatically rendered with a select field. By default, all entries from the associated model are used as options. To customize this, either define an instance variable with the same name as the association in your controller, or pass a <tt>:list</tt> option:
170
+ <%= f.belongs_to_field :hometown, :list => City.where('country = ?', @person.country) %>
171
+
172
+ Yes, it's bad practice to use finder logic in your views! Define the variable <tt>@hometowns</tt> in your controller instead, and you do not even have to specify the <tt>:list</tt> option.
158
173
 
159
174
  == Generated Files
160
175
 
@@ -180,10 +195,12 @@ All generated files are supposed to provide a reasonable foundation for the CRUD
180
195
 
181
196
  === Views:
182
197
 
183
- views/crud/index.html.erb:: The index view displaying a table with all entries and an action link to add new ones.
198
+ views/crud/index.html.erb:: The index view displaying a table with all entries and an action link to add new ones. If you have +search_columns+ defined for your controller, then a search box is rendered as well.
184
199
 
185
200
  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.
186
201
 
202
+ views/crud/_search.html.erb:: A partial defining a simple search form that is displayed when +search_columns+ are defined in a subclassing controller.
203
+
187
204
  views/crud/show.html.erb:: The show view displaying all the attributes of one entry and the various actions to perform on it.
188
205
 
189
206
  views/crud/_attrs.html.erb:: A partial defining the attributes to be displayed in the show view.
@@ -203,7 +220,9 @@ public/stylesheets/crud.css:: A simple CSS with all the classes and ids used in
203
220
 
204
221
  === Tests:
205
222
 
206
- test/crud_test_model.rb:: A dummy model to run CRUD tests against.
223
+ {test/crud_test_model.rb}[http://codez.ch/dry_crud/?q=CrudTestHelper]:: A dummy model to run CRUD tests against.
224
+
225
+ {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.
207
226
 
208
227
  {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.
209
228
 
data/Rakefile CHANGED
@@ -67,15 +67,15 @@ desc "Install dry_crud as a local gem."
67
67
  task :install => [:package] do
68
68
  sudo = RUBY_PLATFORM =~ /win32/ ? '' : 'sudo'
69
69
  gem = RUBY_PLATFORM =~ /java/ ? 'jgem' : 'gem'
70
- sh %{#{sudo} #{gem} install --no-ri pkg/dry_crud-#{File.read('VERSION').strip}}
70
+ sh %{#{sudo} #{gem} install --no-ri pkg/dry_crud-#{File.read('VERSION').strip}.gem}
71
71
  end
72
72
 
73
73
  desc "Deploy rdoc to website"
74
74
  task :site => :rdoc do
75
75
  if ENV['DEST']
76
- sh "rsync -rzv rdoc/ #{ENV['DEST']}"
76
+ sh "rsync -rzv rdoc/ #{ENV['DEST']}"
77
77
  else
78
- puts "Please specify a destination with DEST=user@server:/deploy/dir"
78
+ puts "Please specify a destination with DEST=user@server:/deploy/dir"
79
79
  end
80
80
  end
81
81
 
@@ -100,10 +100,20 @@ Rake::RDocTask.new do |rdoc|
100
100
  list.exclude(/(^|[^.a-z])[a-z]+/)
101
101
  list.exclude('TODO')
102
102
  end.to_a)
103
- rdoc.rdoc_files.include('generators/dry_crud/templates/**/*.rb')
104
- rdoc.rdoc_files.exclude('generators/dry_crud/templates/**/*_test.rb')
103
+ rdoc.rdoc_files.include('lib/generators/dry_crud/templates/**/*.rb')
104
+ rdoc.rdoc_files.exclude('lib/generators/dry_crud/templates/**/*_test.rb')
105
105
  rdoc.rdoc_files.exclude('TODO')
106
106
 
107
107
  rdoc.rdoc_dir = 'rdoc'
108
108
  rdoc.main = 'README.rdoc'
109
109
  end
110
+
111
+ desc "Outputs the commands required for a release. Does not perform any other actions"
112
+ task :release do
113
+ version = File.read('VERSION').strip
114
+ puts "Issue the following commands to perform a release:"
115
+ puts " $ git tag version-#{version}"
116
+ puts " $ git push --tags"
117
+ puts " $ rake repackage"
118
+ puts " $ gem push pkg/dry_crud-#{version}.gem"
119
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.1.0
@@ -8,6 +8,22 @@ class CrudController < ApplicationController
8
8
 
9
9
  include RenderInheritable
10
10
 
11
+ # Set up entry object to use in the various actions.
12
+ before_filter :build_entry, :only => [:new, :create]
13
+ before_filter :set_entry, :only => [:show, :edit, :update, :destroy]
14
+
15
+ helper :standard
16
+ helper_method :model_class, :models_label, :full_entry_label, :search_support?
17
+
18
+ delegate :model_class, :model_identifier, :models_label, :to => 'self.class'
19
+
20
+ hide_action :model_class, :models_label, :model_identifier, :run_callbacks, :inheritable_root_controller
21
+
22
+ # Define an array of searchable columns in your subclassing controllers.
23
+ class_attribute :search_columns
24
+ self.search_columns = []
25
+
26
+ # Callbacks
11
27
  extend ActiveModel::Callbacks
12
28
 
13
29
  # Defines before and after callback hooks for create, update, save and destroy.
@@ -20,9 +36,7 @@ class CrudController < ApplicationController
20
36
  :render_edit,
21
37
  :only => :before,
22
38
  :terminator => "result == false || performed?"
23
-
24
- delegate :model_class, :model_identifier, :models_label, :to => 'self.class'
25
-
39
+
26
40
  # Verify that required :id param is present and only allow good http methods.
27
41
  # Uncomment if you have the Rails verification plugin installed.
28
42
  #verify :params => :id, :only => :show, :redirect_to => { :action => 'index' }
@@ -30,15 +44,6 @@ class CrudController < ApplicationController
30
44
  #verify :method => [:put, :post], :params => :id, :only => :update, :redirect_to => { :action => 'index' }
31
45
  #verify :method => [:delete, :post], :params => :id, :only => :destroy, :redirect_to => { :action => 'index' }
32
46
 
33
- # Set up entry object to use in the various actions.
34
- before_filter :build_entry, :only => [:new, :create]
35
- before_filter :set_entry, :only => [:show, :edit, :update, :destroy]
36
-
37
- helper :standard
38
- helper_method :model_class, :models_label, :full_entry_label
39
-
40
- hide_action :model_class, :models_label, :model_identifier, :run_callbacks, :inheritable_root_controller
41
-
42
47
 
43
48
  ############## ACTIONS ############################################
44
49
 
@@ -143,16 +148,28 @@ class CrudController < ApplicationController
143
148
  # Sets an existing model entry from the given id.
144
149
  def set_entry
145
150
  @entry = model_class.find(params[:id])
146
- end
151
+ end
147
152
 
148
153
  # A label for the current entry, including the model name.
149
- def full_entry_label
150
- "#{models_label.singularize} '#{@entry.label}'"
151
- end
154
+ def full_entry_label
155
+ "#{models_label.singularize} '#{@entry.label}'"
156
+ end
152
157
 
153
158
  # Find options used in the index action.
154
159
  def find_all_options
155
- {}
160
+ { :conditions => search_condition }
161
+ end
162
+
163
+ def search_condition
164
+ if search_support? && params[:q].present?
165
+ clause = search_columns.collect {|f| "#{f} LIKE ?" }.join(" OR ")
166
+ param = "%#{params[:q]}%"
167
+ ["(#{clause})"] + [param] * search_columns.size
168
+ end
169
+ end
170
+
171
+ def search_support?
172
+ search_columns.present?
156
173
  end
157
174
 
158
175
  # Redirects to the show action of a single entry.
@@ -162,7 +179,7 @@ class CrudController < ApplicationController
162
179
 
163
180
  # Redirects to the main action of this controller.
164
181
  def redirect_to_index
165
- redirect_to :action => 'index'
182
+ redirect_to polymorphic_path(model_class)
166
183
  end
167
184
 
168
185
  # Helper method to run before_render callbacks and render the action.
@@ -173,9 +190,9 @@ class CrudController < ApplicationController
173
190
  end
174
191
 
175
192
  # Saves the current entry with callbacks.
176
- def save_entry
193
+ def save_entry
177
194
  with_callbacks(:save) { @entry.save }
178
- end
195
+ end
179
196
 
180
197
  # Helper method the run the given block in between the before and after
181
198
  # callbacks of the given kind.
@@ -9,12 +9,12 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
9
9
 
10
10
  attr_reader :template
11
11
 
12
- delegate :association, :belongs_to_association, :column_type, :column_property, :captionize,
12
+ delegate :association, :column_type, :column_property, :captionize,
13
13
  :to => :template
14
14
 
15
15
  # Render multiple input fields together with a label for the given attributes.
16
16
  def labeled_input_fields(*attrs)
17
- attrs.collect {|a| labeled_input_field(a) }.join("\n")
17
+ attrs.collect {|a| labeled_input_field(a) }.join("\n").html_safe
18
18
  end
19
19
 
20
20
  # Render a standartized label.
@@ -126,17 +126,11 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
126
126
 
127
127
  protected
128
128
 
129
- def labeled_field_method?(name)
130
- prefix = 'labeled_'
131
- if name.to_s.start_with?(prefix)
132
- field_method = name.to_s[prefix.size..-1]
133
- field_method if respond_to?(field_method)
134
- end
135
- end
136
-
129
+ # Returns true if attr is a non-polymorphic belongs_to association,
130
+ # for which an input field may be automatically rendered.
137
131
  def belongs_to_association?(attr, type)
138
132
  if type == :integer || type == nil
139
- assoc = belongs_to_association(@object, attr)
133
+ assoc = association(@object, attr, :belongs_to)
140
134
  assoc.present? && assoc.options[:polymorphic].nil?
141
135
  else
142
136
  false
@@ -152,10 +146,21 @@ class StandardFormBuilder < ActionView::Helpers::FormBuilder
152
146
  assoc = association(@object, attr)
153
147
  list = @template.send(:instance_variable_get, :"@#{assoc.name.to_s.pluralize}")
154
148
  unless list
155
- list = assoc.klass.find(:all, :conditions => assoc.options[:conditions],
149
+ list = assoc.klass.all(:conditions => assoc.options[:conditions],
156
150
  :order => assoc.options[:order])
157
151
  end
158
152
  end
159
153
  list
160
154
  end
155
+
156
+ private
157
+
158
+ def labeled_field_method?(name)
159
+ prefix = 'labeled_'
160
+ if name.to_s.start_with?(prefix)
161
+ field_method = name.to_s[prefix.size..-1]
162
+ field_method if respond_to?(field_method)
163
+ end
164
+ end
165
+
161
166
  end
@@ -3,12 +3,12 @@
3
3
  # ApplicationController.
4
4
  module StandardHelper
5
5
 
6
- NO_LIST_ENTRIES_MESSAGE = "No entries available"
6
+ NO_LIST_ENTRIES_MESSAGE = "No entries found"
7
7
  CONFIRM_DELETE_MESSAGE = 'Do you really want to delete this entry?'
8
8
 
9
9
  FLOAT_FORMAT = "%.2f"
10
10
  TIME_FORMAT = "%H:%M"
11
- EMPTY_STRING = "&nbsp;" # non-breaking space asserts better css styling.
11
+ EMPTY_STRING = "&nbsp;".html_safe # non-breaking space asserts better css styling.
12
12
 
13
13
  ################ FORMATTING HELPERS ##################################
14
14
 
@@ -20,13 +20,13 @@ module StandardHelper
20
20
  case value
21
21
  when Fixnum then number_with_delimiter(value)
22
22
  when Float then FLOAT_FORMAT % value
23
- when Date then value.to_s
23
+ when Date then value.to_s
24
24
  when Time then value.strftime(TIME_FORMAT)
25
25
  when true then 'yes'
26
26
  when false then 'no'
27
27
  when nil then EMPTY_STRING
28
28
  else
29
- value.respond_to?(:label) ? h(value.label) : h(value.to_s)
29
+ value.respond_to?(:label) ? value.label : value.to_s
30
30
  end
31
31
  end
32
32
 
@@ -38,7 +38,7 @@ module StandardHelper
38
38
  format_attr_method = :"format_#{attr.to_s}"
39
39
  if respond_to?(format_attr_method)
40
40
  send(format_attr_method, obj)
41
- elsif assoc = belongs_to_association(obj, attr)
41
+ elsif assoc = association(obj, attr, :belongs_to)
42
42
  format_assoc(obj, assoc)
43
43
  else
44
44
  format_type(obj, attr)
@@ -50,7 +50,6 @@ module StandardHelper
50
50
 
51
51
 
52
52
  # Renders an arbitrary content with the given label. Used for uniform presentation.
53
- # Without block, this may be used in the form <%= labeled(...) %>, with like <% labeled(..) do %>
54
53
  def labeled(label, content = nil, &block)
55
54
  content = capture(&block) if block_given?
56
55
  render(:partial => 'shared/labeled', :locals => { :label => label, :content => content})
@@ -69,12 +68,17 @@ module StandardHelper
69
68
  # Optionally surrounded with a div.
70
69
  def render_attrs(obj, attrs, div = true)
71
70
  html = attrs.collect do |a|
72
- labeled(captionize(a, obj.class), format_attr(obj, a))
73
- end.join.html_safe
71
+ labeled_attr(obj, a)
72
+ end.join("\n").html_safe
74
73
 
75
74
  div ? content_tag(:div, html, :class => 'attributes') : html
76
75
  end
77
76
 
77
+ # Renders the formatted content of the given attribute with a label.
78
+ def labeled_attr(obj, attr)
79
+ labeled(captionize(attr, obj.class), format_attr(obj, attr))
80
+ end
81
+
78
82
  # Renders a table for the given entries. One column is rendered for each attribute passed.
79
83
  # If a block is given, the columns defined therein are appended to the attribute columns.
80
84
  # If entries is empty, an appropriate message is rendered.
@@ -142,19 +146,19 @@ module StandardHelper
142
146
  end
143
147
 
144
148
  # A generic helper method to create action links.
145
- # These link may be styled to look like buttons, for example.
149
+ # These link could be styled to look like buttons, for example.
146
150
  def link_action(label, options = {}, html_options = {})
147
151
  link_to("[#{label}]", options, {:class => 'action'}.merge(html_options))
148
152
  end
149
153
 
150
154
  protected
151
155
 
152
- # Helper methods that are not necessarely called from templates.
156
+ # Helper methods that are not directly called from templates.
153
157
 
154
158
  # Formats an active record association
155
159
  def format_assoc(obj, assoc)
156
160
  if assoc_val = obj.send(assoc.name)
157
- link_to_unless(no_assoc_link?(assoc), h(assoc_val.label), assoc_val)
161
+ link_to_unless(no_assoc_link?(assoc), assoc_val.label, assoc_val)
158
162
  else
159
163
  '(none)'
160
164
  end
@@ -194,20 +198,16 @@ module StandardHelper
194
198
  end
195
199
  end
196
200
 
197
- # Returns the :belongs_to association for the given attribute or nil if there is none.
198
- def belongs_to_association(obj, attr)
199
- if assoc = association(obj, attr)
200
- assoc if assoc.macro == :belongs_to
201
- end
202
- end
203
-
204
201
  # Returns the association proxy for the given attribute. The attr parameter
205
- # may be the _id column or the association name. Returns nil if no association
206
- # was found.
207
- def association(obj, attr)
202
+ # may be the _id column or the association name. If a macro (e.g. :belongs_to)
203
+ # is given, the association must be of this type, otherwise, any association
204
+ # is returned. Returns nil if no association (or not of the given macro) was
205
+ # found.
206
+ def association(obj, attr, macro = nil)
208
207
  if obj.class.respond_to?(:reflect_on_association)
209
- assoc = attr.to_s =~ /_id$/ ? attr.to_s[0..-4].to_sym : attr
210
- obj.class.reflect_on_association(assoc)
208
+ name = attr.to_s =~ /_id$/ ? attr.to_s[0..-4].to_sym : attr
209
+ assoc = obj.class.reflect_on_association(name)
210
+ assoc if assoc && (macro.nil? || assoc.macro == macro)
211
211
  end
212
212
  end
213
213
 
@@ -10,7 +10,7 @@ class StandardTableBuilder
10
10
 
11
11
  # Delegate called methods to template.
12
12
  # including StandardHelper would lead to problems with indirectly called methods.
13
- delegate :content_tag, :format_attr, :column_type, :belongs_to_association,
13
+ delegate :content_tag, :format_attr, :column_type, :association,
14
14
  :captionize, :tr_alt, :to => :template
15
15
 
16
16
  def initialize(entries, template)
@@ -40,10 +40,18 @@ class StandardTableBuilder
40
40
  # the formatted attribute value for the current entry.
41
41
  def attrs(*attrs)
42
42
  attrs.each do |a|
43
- col(captionize(a, entry_class), :class => align_class(a)) { |e| format_attr(e, a) }
43
+ attr(a)
44
44
  end
45
45
  end
46
46
 
47
+ # Define a column for the given attribute and an optional header.
48
+ # If no header is given, the attribute name is used. The cell will
49
+ # contain the formatted attribute value for the current entry.
50
+ def attr(a, header = nil)
51
+ header ||= captionize(a, entry_class)
52
+ col(header, :class => align_class(a)) { |e| format_attr(e, a) }
53
+ end
54
+
47
55
  # Renders the table as HTML.
48
56
  def to_html
49
57
  content_tag :table, :class => 'list' do
@@ -57,7 +65,7 @@ class StandardTableBuilder
57
65
  entry = entries.first
58
66
  case column_type(entry, attr)
59
67
  when :integer, :float, :decimal
60
- 'right_align' unless belongs_to_association(entry, attr)
68
+ 'right_align' unless association(entry, attr, :belongs_to)
61
69
  when :boolean
62
70
  'center_align'
63
71
  end
@@ -0,0 +1,6 @@
1
+ <%= form_tag(polymorphic_path(model_class), {:method => :get}) do %>
2
+ <%= text_field_tag :q, params[:q] %>
3
+ <%= submit_tag 'Search' %>
4
+ <% end %>
5
+
6
+ <br/>
@@ -1,5 +1,7 @@
1
1
  <% @title = "Listing #{models_label}" -%>
2
2
 
3
+ <%= render :partial => 'search' if search_support? %>
4
+
3
5
  <%= render :partial => 'list' %>
4
6
 
5
7
  <br/>
@@ -1,7 +1,9 @@
1
1
  class CrudTestModel < ActiveRecord::Base #:nodoc:
2
- validates_presence_of :name
2
+
3
+ validates :name, :presence => true
4
+ validates :rating, :inclusion => { :in => 1..10 }
3
5
 
4
- default_scope :order => 'name'
6
+ default_scope order('name')
5
7
 
6
8
  belongs_to :companion, :class_name => 'CrudTestModel'
7
9
 
@@ -13,6 +15,8 @@ end
13
15
  class CrudTestModelsController < CrudController #:nodoc:
14
16
  HANDLE_PREFIX = 'handle_'
15
17
 
18
+ self.search_columns = [:name, :whatever, :remarks]
19
+
16
20
  before_create :possibly_redirect
17
21
  before_create :handle_name
18
22
 
@@ -24,6 +28,10 @@ class CrudTestModelsController < CrudController #:nodoc:
24
28
 
25
29
  hide_action :called_callbacks, :should_redirect, :should_redirect=
26
30
 
31
+ # don't use the standard layout as it may require different routes
32
+ # than just the test route for this controller
33
+ layout nil
34
+
27
35
  private
28
36
 
29
37
  # custom callback
@@ -0,0 +1,59 @@
1
+ # A handful of convenient assertions. The aim of custom assertions is to
2
+ # provide more specific error messages and to perform complex checks.
3
+ #
4
+ # Ideally, include this module into your test_helper.rb file:
5
+ # # at the beginning of the file:
6
+ # require 'custom_assertions'
7
+ #
8
+ # # inside the class definition:
9
+ # include CustomAssertions
10
+ module CustomAssertions
11
+
12
+ # Asserts that the element is included in the collection.
13
+ def assert_include(collection, element, message = "")
14
+ full_message = build_message(message, "<?> expected to be included in \n<?>.",
15
+ element, collection)
16
+ assert_block(full_message) { collection.include?(element) }
17
+ end
18
+
19
+ # Asserts that the element is not included in the collection.
20
+ def assert_not_include(collection, element, message = "")
21
+ full_message = build_message(message, "<?> expected not to be included in \n<?>.",
22
+ element, collection)
23
+ assert_block(full_message) { !collection.include?(element) }
24
+ end
25
+
26
+ # Asserts that the given active model record is valid.
27
+ # This method used to be part of Rails but was deprecated, no idea why.
28
+ def assert_valid(record, message = "")
29
+ record.valid?
30
+ full_message = build_message(message,
31
+ "? expected to be valid, but has the following errors: \n ?.",
32
+ record, record.errors.full_messages.join("\n"))
33
+ assert_block(full_message) { record.valid? }
34
+ end
35
+
36
+ # Asserts that the given active model record is not valid.
37
+ # If you provide a set of invalid attribute symbols, all of and only these
38
+ # attributes are expected to have errors. If no invalid attributes are
39
+ # specified, only the invalidity of the record is asserted.
40
+ def assert_not_valid(record, *invalid_attrs)
41
+ message = build_message("", "? expected to be invalid, but is valid.", record)
42
+ assert_block(message) { !record.valid? }
43
+
44
+ # assert that the given attributes have errors.
45
+ invalid_attrs.each do |a|
46
+ message = build_message("", "Attribute <?> expected to be invalid, but is valid.", a)
47
+ assert_block(message) { record.errors[a].present? }
48
+ end
49
+
50
+ if invalid_attrs.present?
51
+ # assert that no other than the invalid attributes have errors.
52
+ record.errors.each do |a, error|
53
+ message = build_message("", "Attribute <?> not declared as invalid attribute, but has the following error: \n?.", a, error)
54
+ assert_block(message) { invalid_attrs.include?(a) }
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -8,20 +8,31 @@ module CrudControllerTestHelper
8
8
  get :index
9
9
  assert_response :success
10
10
  assert_template 'index'
11
- assert_not_nil assigns(:entries)
11
+ assert_present assigns(:entries)
12
12
  end
13
13
 
14
14
  def test_index_xml
15
15
  get :index, :format => 'xml'
16
16
  assert_response :success
17
- assert_not_nil assigns(:entries)
17
+ assert_present assigns(:entries)
18
18
  assert @response.body.starts_with?("<?xml")
19
19
  end
20
20
 
21
+ def test_index_search
22
+ field = @controller.search_columns.first
23
+ val = field && test_entry[field].to_s
24
+ return if val.blank? # does not support search or no value in this field
25
+
26
+ get :index, :q => val[0..((val.size + 1)/ 2)]
27
+ assert_response :success
28
+ assert_present assigns(:entries)
29
+ assert assigns(:entries).include?(test_entry)
30
+ end
31
+
21
32
  def test_show
22
33
  get :show, :id => test_entry.id
23
34
  assert_response :success
24
- assert_template 'show'
35
+ assert_template 'show'
25
36
  assert_equal test_entry, assigns(:entry)
26
37
  end
27
38
 
@@ -37,13 +48,13 @@ module CrudControllerTestHelper
37
48
  get :show, :id => 9999
38
49
  end
39
50
  end
40
-
51
+
41
52
  def test_show_without_id_redirects_to_index
42
53
  assert_raise(ActionController::RoutingError) do
43
54
  get :show
44
55
  end
45
56
  end
46
-
57
+
47
58
  def test_new
48
59
  get :new
49
60
  assert_response :success
@@ -156,11 +167,11 @@ module CrudControllerTestHelper
156
167
  end
157
168
 
158
169
  def model_class
159
- @controller.controller_name.classify.constantize
170
+ @controller.model_class
160
171
  end
161
172
 
162
173
  def model_identifier
163
- @controller.controller_name.singularize.to_sym
174
+ @controller.model_identifier
164
175
  end
165
176
 
166
177
  # Test object used in several tests
@@ -29,6 +29,11 @@ class CrudTestModelsControllerTest < ActionController::TestCase
29
29
  assert_equal assigns(:entries).sort_by {|a| a.name }, assigns(:entries)
30
30
  end
31
31
 
32
+ def test_index_search
33
+ super
34
+ assert_equal 1, assigns(:entries).size
35
+ end
36
+
32
37
  def test_new
33
38
  super
34
39
  assert assigns(:companions)
@@ -99,7 +104,7 @@ class CrudTestModelsControllerTest < ActionController::TestCase
99
104
  {:name => 'foo',
100
105
  :children => 42,
101
106
  :companion_id => 3,
102
- :rating => 99.99,
107
+ :rating => 8.5,
103
108
  :income => 2.42,
104
109
  :birthdate => '31-12-1999'.to_date,
105
110
  :human => true,
@@ -0,0 +1,95 @@
1
+ require 'test_helper'
2
+ require 'custom_assertions'
3
+ require 'crud_test_model'
4
+
5
+ class CustomAssertionsTest < ActiveSupport::TestCase
6
+
7
+ include CustomAssertions
8
+
9
+ include CrudTestHelper
10
+
11
+ setup :reset_db, :setup_db, :create_test_data
12
+ teardown :reset_db
13
+
14
+ test "assert include succeeds if included" do
15
+ assert_nothing_raised do
16
+ assert_include [1,2,3], 2
17
+ end
18
+ end
19
+
20
+ test "assert include succeeds if record included" do
21
+ assert_nothing_raised do
22
+ assert_include CrudTestModel.all, crud_test_models("AAAAA")
23
+ end
24
+ end
25
+
26
+ test "assert include fails if not included" do
27
+ assert_raise(Test::Unit::AssertionFailedError) do
28
+ assert_include [1,2,3], 5
29
+ end
30
+ end
31
+
32
+ test "assert not include succeeds if not included" do
33
+ assert_nothing_raised do
34
+ assert_not_include [1,2,3], 5
35
+ end
36
+ end
37
+
38
+ test "assert not include fails if included" do
39
+ assert_raise(Test::Unit::AssertionFailedError) do
40
+ assert_not_include [1,2,3], 3
41
+ end
42
+ end
43
+
44
+ test "assert valid record succeeds" do
45
+ assert_nothing_raised do
46
+ assert_valid crud_test_models("AAAAA")
47
+ end
48
+ end
49
+
50
+ test "assert valid record fails for invalid" do
51
+ assert_raise(Test::Unit::AssertionFailedError) do
52
+ assert_valid invalid_record
53
+ end
54
+ end
55
+
56
+ test "assert not valid succeeds if record invalid" do
57
+ assert_nothing_raised do
58
+ assert_not_valid invalid_record
59
+ end
60
+ end
61
+
62
+ test "assert not valid succeds if record invalid and invalid attrs given" do
63
+ assert_nothing_raised do
64
+ assert_not_valid invalid_record, :name, :rating
65
+ end
66
+ end
67
+
68
+ test "assert not valid fails if record valid" do
69
+ assert_raise(Test::Unit::AssertionFailedError) do
70
+ assert_not_valid crud_test_models("AAAAA")
71
+ end
72
+ end
73
+
74
+ test "assert not valid fails if record invalid and valid attrs given" do
75
+ assert_raise(Test::Unit::AssertionFailedError) do
76
+ assert_not_valid invalid_record, :name, :rating, :children
77
+ end
78
+ end
79
+
80
+ test "assert not valid fails if record invalid and not all invalid attrs given" do
81
+ assert_raise(Test::Unit::AssertionFailedError) do
82
+ assert_not_valid invalid_record, :name
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def invalid_record
89
+ m = crud_test_models("AAAAA")
90
+ m.name = nil
91
+ m.rating = 42
92
+ m
93
+ end
94
+
95
+ end
@@ -20,26 +20,40 @@ class StandardFormBuilderTest < ActionView::TestCase
20
20
 
21
21
  test "input_field dispatches string attr to string_field" do
22
22
  assert_equal form.string_field(:name), form.input_field(:name)
23
+ assert form.string_field(:name).html_safe?
23
24
  end
24
25
 
25
26
  test "input_field dispatches text attr to text_area" do
26
27
  assert_equal form.text_area(:remarks), form.input_field(:remarks)
28
+ assert form.text_area(:remarks).html_safe?
27
29
  end
28
30
 
29
31
  test "input_field dispatches integer attr to integer_field" do
30
32
  assert_equal form.integer_field(:children), form.input_field(:children)
33
+ assert form.integer_field(:children).html_safe?
31
34
  end
32
35
 
33
36
  test "input_field dispatches boolean attr to boolean_field" do
34
37
  assert_equal form.boolean_field(:human), form.input_field(:human)
38
+ assert form.boolean_field(:human).html_safe?
35
39
  end
36
40
 
37
41
  test "input_field dispatches date attr to date_field" do
38
42
  assert_equal form.date_field(:birthdate), form.input_field(:birthdate)
43
+ assert form.date_field(:birthdate).html_safe?
39
44
  end
40
45
 
41
46
  test "input_field dispatches belongs_to attr to select field" do
42
47
  assert_equal form.belongs_to_field(:companion_id), form.input_field(:companion_id)
48
+ assert form.belongs_to_field(:companion_id).html_safe?
49
+ end
50
+
51
+ test "input_fields concats multiple fields" do
52
+ result = form.labeled_input_fields(:name, :remarks, :children)
53
+ assert result.html_safe?
54
+ assert result.include?(form.input_field(:name))
55
+ assert result.include?(form.input_field(:remarks))
56
+ assert result.include?(form.input_field(:children))
43
57
  end
44
58
 
45
59
  test "belongs_to_field has all options by default" do
@@ -70,14 +84,29 @@ class StandardFormBuilderTest < ActionView::TestCase
70
84
  assert_no_match /(\<option .*?)/m, f
71
85
  end
72
86
 
73
-
74
87
  test "string_field sets maxlength attribute if limit" do
75
88
  assert_match /maxlength="50"/, form.string_field(:name)
76
89
  end
77
90
 
78
- test "labeld_text_field create label" do
91
+ test "label creates captionized label" do
92
+ assert_match /label for.+Gugus dada/, form.label(:gugus_dada)
93
+ assert form.label(:gugus_dada).html_safe?
94
+ end
95
+
96
+ test "classic label still works" do
97
+ assert_match /label for.+hoho/, form.label(:gugus_dada, "hoho")
98
+ assert form.label(:gugus_dada, "hoho").html_safe?
99
+ end
100
+
101
+ test "labeled_text_field create label" do
79
102
  assert_match /label for.+input/m, form.labeled_string_field(:name)
103
+ assert form.labeled_string_field(:name).html_safe?
80
104
  end
81
105
 
106
+ test "labeled field creates label" do
107
+ result = form.labeled("gugus", "<input type='text' name='gugus' />")
108
+ assert result.html_safe?
109
+ assert_match /label for.+input/m, result
110
+ end
82
111
 
83
112
  end
@@ -16,25 +16,36 @@ class StandardHelperTest < ActionView::TestCase
16
16
  test "labeled text as block" do
17
17
  result = labeled("label") { "value" }
18
18
 
19
+ assert result.html_safe?
19
20
  assert_dom_equal "<div class='labeled'> <div class='caption'>label</div> <div class='value'>value</div> </div>", result.squish
20
21
  end
21
22
 
22
23
  test "labeled text empty" do
23
24
  result = labeled("label", "")
24
25
 
26
+ assert result.html_safe?
25
27
  assert_dom_equal "<div class='labeled'> <div class='caption'>label</div> <div class='value'>#{EMPTY_STRING}</div> </div>", result.squish
26
28
  end
27
29
 
28
30
  test "labeled text as content" do
29
- result = labeled("label", "value")
31
+ result = labeled("label", "value <unsafe>")
30
32
 
31
- assert_dom_equal "<div class='labeled'> <div class='caption'>label</div> <div class='value'>value</div> </div>", result.squish
33
+ assert result.html_safe?
34
+ assert_dom_equal "<div class='labeled'> <div class='caption'>label</div> <div class='value'>value &lt;unsafe&gt;</div> </div>", result.squish
35
+ end
36
+
37
+ test "labeled attr" do
38
+ result = labeled_attr('foo', :size)
39
+ assert result.html_safe?
40
+ assert_dom_equal "<div class='labeled'> <div class='caption'>Size</div> <div class='value'>3 chars</div> </div>", result.squish
32
41
  end
33
42
 
34
43
  test "alternate row" do
35
44
  result_1 = tr_alt { "(test row content)" }
36
45
  result_2 = tr_alt { "(test row content)" }
37
46
 
47
+ assert result_1.html_safe?
48
+ assert result_2.html_safe?
38
49
  assert_dom_equal "<tr class='even'>(test row content)</tr>", result_1
39
50
  assert_dom_equal "<tr class='odd'>(test row content)</tr>", result_2
40
51
  end
@@ -57,12 +68,14 @@ class StandardHelperTest < ActionView::TestCase
57
68
  end
58
69
 
59
70
  test "format nil" do
71
+ assert EMPTY_STRING.html_safe?
60
72
  assert_equal EMPTY_STRING, f(nil)
61
73
  end
62
74
 
63
75
  test "format Strings" do
64
76
  assert_equal "blah blah", f("blah blah")
65
- assert_equal "&lt;injection&gt;", f("<injection>")
77
+ assert_equal "<injection>", f("<injection>")
78
+ assert !f("<injection>").html_safe?
66
79
  end
67
80
 
68
81
  test "format attr with fallthrough to f" do
@@ -115,19 +128,25 @@ class StandardHelperTest < ActionView::TestCase
115
128
  test "format text column" do
116
129
  m = crud_test_models(:AAAAA)
117
130
  assert_equal "<p>AAAAA AAAAA AAAAA\n<br />AAAAA AAAAA AAAAA\n<br />AAAAA AAAAA AAAAA\n</p>", format_type(m, :remarks)
131
+ assert format_type(m, :remarks).html_safe?
118
132
  end
119
133
 
120
134
  test "empty table should render message" do
121
- assert_dom_equal "<div class='list'>#{NO_LIST_ENTRIES_MESSAGE}</div>", table([]) { }
135
+ result = table([]) { }
136
+ assert result.html_safe?
137
+ assert_dom_equal "<div class='list'>#{NO_LIST_ENTRIES_MESSAGE}</div>", result
122
138
  end
123
139
 
124
140
  test "non empty table should render table" do
125
- assert_match(/^\<table.*\<\/table\>$/, table(['foo', 'bar']) {|t| t.attrs :size, :upcase })
141
+ result = table(['foo', 'bar']) {|t| t.attrs :size, :upcase }
142
+ assert result.html_safe?
143
+ assert_match(/^\<table.*\<\/table\>$/, result)
126
144
  end
127
145
 
128
146
  test "table with attrs" do
129
147
  expected = StandardTableBuilder.table(['foo', 'bar'], self) { |t| t.attrs :size, :upcase }
130
148
  actual = table(['foo', 'bar'], :size, :upcase)
149
+ assert actual.html_safe?
131
150
  assert_equal expected, actual
132
151
  end
133
152
 
@@ -135,6 +154,7 @@ class StandardHelperTest < ActionView::TestCase
135
154
  assert_equal "Camel Case", captionize(:camel_case)
136
155
  assert_equal "All Upper Case", captionize("all upper case")
137
156
  assert_equal "With Object", captionize("With object", Object.new)
157
+ assert !captionize('bad <title>').html_safe?
138
158
  end
139
159
 
140
160
  test "standard form for existing entry" do
@@ -155,7 +175,6 @@ class StandardHelperTest < ActionView::TestCase
155
175
  assert_match /input .*?type="submit" .*?value="Save"/, f
156
176
  end
157
177
 
158
-
159
178
  test "standard form for new entry" do
160
179
  e = CrudTestModel.new
161
180
  f = with_test_routing do
@@ -1,5 +1,9 @@
1
1
  class PeopleController < AjaxController
2
2
 
3
+ self.search_columns = [:name, :children, :rating, :income, :birthdate, :remarks]
4
+
5
+ protected
6
+
3
7
  def fetch_all_options
4
8
  {:include => :city, :order => 'people.name, cities.country_code, cities.name'}
5
9
  end
@@ -1,7 +1,8 @@
1
1
  module PeopleHelper
2
2
 
3
3
  def format_income(person)
4
- "#{f(person.income)} $"
4
+ income = person.income
5
+ income.present? ? "#{f(income)} $" : StandardHelper::EMPTY_STRING
5
6
  end
6
7
 
7
8
  end
@@ -1,9 +1,4 @@
1
- <% @title = "Listing #{models_label}" -%>
1
+ <%= render :file => 'crud/index' %>
2
2
 
3
- <%= render :partial => 'list' %>
4
-
5
- <br/>
6
-
7
- <%= link_action_add %>
8
3
  <%= link_to 'Ajahx', {:action => 'ajax'}, :method => :get, :remote => true %>
9
4
  <div id="response"></div>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
7
+ <title><%= @title %></title>
8
+ <%= stylesheet_link_tag 'crud' %>
9
+ <%= javascript_include_tag :all %>
10
+ <%= csrf_meta_tag %>
11
+ </head>
12
+ <body>
13
+
14
+ <ul id="menu">
15
+ <li><%= link_to 'People', people_path %></li>
16
+ <li><%= link_to 'Cities', cities_path %></li>
17
+ </ul>
18
+
19
+ <h1><%= @title %></h1>
20
+
21
+ <p id="flash_notice"><%= flash[:notice] %></p>
22
+
23
+ <%= yield %>
24
+
25
+ </body>
26
+ </html>
@@ -0,0 +1,7 @@
1
+ <div class="attributes">
2
+ <%= render_attrs @entry, default_attrs, false %>
3
+ <%= labeled("I thinks its", "nice") %>
4
+ <%= labeled("Check google") do %>
5
+ <%= link_to "Maps", "http://map.google.com/?q=#{@entry.name}" %>
6
+ <% end %>
7
+ </div>
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry_crud
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
+ - 1
8
9
  - 0
9
- - 0
10
- version: 1.0.0
10
+ version: 1.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Pascal Zumkehr
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-31 00:00:00 +02:00
18
+ date: 2010-09-24 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -56,6 +56,7 @@ files:
56
56
  - lib/generators/dry_crud/templates/app/views/crud/_attrs.html.erb
57
57
  - lib/generators/dry_crud/templates/app/views/crud/_form.html.erb
58
58
  - lib/generators/dry_crud/templates/app/views/crud/_list.html.erb
59
+ - lib/generators/dry_crud/templates/app/views/crud/_search.html.erb
59
60
  - lib/generators/dry_crud/templates/app/views/crud/edit.html.erb
60
61
  - lib/generators/dry_crud/templates/app/views/crud/index.html.erb
61
62
  - lib/generators/dry_crud/templates/app/views/crud/new.html.erb
@@ -66,13 +67,15 @@ files:
66
67
  - lib/generators/dry_crud/templates/INSTALL
67
68
  - lib/generators/dry_crud/templates/public/stylesheets/crud.css
68
69
  - lib/generators/dry_crud/templates/test/crud_test_model.rb
70
+ - lib/generators/dry_crud/templates/test/custom_assertions.rb
69
71
  - lib/generators/dry_crud/templates/test/functional/crud_controller_test_helper.rb
70
72
  - lib/generators/dry_crud/templates/test/functional/crud_test_models_controller_test.rb
71
- - lib/generators/dry_crud/templates/test/unit/crud_helper_test.rb
72
- - lib/generators/dry_crud/templates/test/unit/render_inheritable_test.rb
73
- - lib/generators/dry_crud/templates/test/unit/standard_form_builder_test.rb
74
- - lib/generators/dry_crud/templates/test/unit/standard_helper_test.rb
75
- - lib/generators/dry_crud/templates/test/unit/standard_table_builder_test.rb
73
+ - lib/generators/dry_crud/templates/test/unit/custom_assertions_test.rb
74
+ - lib/generators/dry_crud/templates/test/unit/helpers/crud_helper_test.rb
75
+ - lib/generators/dry_crud/templates/test/unit/helpers/render_inheritable_test.rb
76
+ - lib/generators/dry_crud/templates/test/unit/helpers/standard_form_builder_test.rb
77
+ - lib/generators/dry_crud/templates/test/unit/helpers/standard_helper_test.rb
78
+ - lib/generators/dry_crud/templates/test/unit/helpers/standard_table_builder_test.rb
76
79
  - lib/generators/dry_crud/USAGE
77
80
  - test/templates/app/controllers/ajax_controller.rb
78
81
  - test/templates/app/controllers/application_controller.rb
@@ -84,10 +87,11 @@ files:
84
87
  - test/templates/app/views/ajax/_hello.html.erb
85
88
  - test/templates/app/views/ajax/ajax.js.rjs
86
89
  - test/templates/app/views/ajax/index.html.erb
87
- - test/templates/app/views/cities/_attrs.html.erb
88
90
  - test/templates/app/views/cities/_form.html.erb
89
91
  - test/templates/app/views/cities/_hello.html.erb
90
92
  - test/templates/app/views/cities/_list.html.erb
93
+ - test/templates/app/views/layouts/crud.html.erb
94
+ - test/templates/app/views/people/_attrs.html.erb
91
95
  - test/templates/config/routes.rb
92
96
  - test/templates/db/migrate/20100511174904_create_people_and_cities.rb
93
97
  - test/templates/test/fixtures/cities.yml
@@ -1 +0,0 @@
1
- <%= render_attrs @entry, default_attrs %>