dry_crud 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/MIT-LICENSE +19 -0
  2. data/README.rdoc +208 -0
  3. data/Rakefile +101 -0
  4. data/VERSION +1 -0
  5. data/rails_generators/dry_crud/USAGE +1 -0
  6. data/rails_generators/dry_crud/dry_crud_generator.rb +22 -0
  7. data/rails_generators/dry_crud/templates/INSTALL +13 -0
  8. data/rails_generators/dry_crud/templates/app/controllers/crud_controller.rb +182 -0
  9. data/rails_generators/dry_crud/templates/app/helpers/crud_helper.rb +45 -0
  10. data/rails_generators/dry_crud/templates/app/helpers/standard_helper.rb +209 -0
  11. data/rails_generators/dry_crud/templates/app/views/crud/_attrs.html.erb +1 -0
  12. data/rails_generators/dry_crud/templates/app/views/crud/_form.html.erb +1 -0
  13. data/rails_generators/dry_crud/templates/app/views/crud/_list.html.erb +1 -0
  14. data/rails_generators/dry_crud/templates/app/views/crud/edit.html.erb +8 -0
  15. data/rails_generators/dry_crud/templates/app/views/crud/index.html.erb +14 -0
  16. data/rails_generators/dry_crud/templates/app/views/crud/new.html.erb +7 -0
  17. data/rails_generators/dry_crud/templates/app/views/crud/show.html.erb +9 -0
  18. data/rails_generators/dry_crud/templates/app/views/layouts/crud.html.erb +20 -0
  19. data/rails_generators/dry_crud/templates/app/views/shared/_labeled.html.erb +5 -0
  20. data/rails_generators/dry_crud/templates/lib/crud_callbacks.rb +55 -0
  21. data/rails_generators/dry_crud/templates/lib/render_inheritable.rb +118 -0
  22. data/rails_generators/dry_crud/templates/lib/standard_form_builder.rb +161 -0
  23. data/rails_generators/dry_crud/templates/lib/standard_table_builder.rb +104 -0
  24. data/rails_generators/dry_crud/templates/public/stylesheets/crud.css +91 -0
  25. data/rails_generators/dry_crud/templates/test/crud_test_model.rb +124 -0
  26. data/rails_generators/dry_crud/templates/test/functional/crud_controller_test_helper.rb +172 -0
  27. data/rails_generators/dry_crud/templates/test/functional/crud_test_models_controller_test.rb +96 -0
  28. data/rails_generators/dry_crud/templates/test/unit/crud_helper_test.rb +57 -0
  29. data/rails_generators/dry_crud/templates/test/unit/render_inheritable_test.rb +161 -0
  30. data/rails_generators/dry_crud/templates/test/unit/standard_form_builder_test.rb +83 -0
  31. data/rails_generators/dry_crud/templates/test/unit/standard_helper_test.rb +183 -0
  32. data/rails_generators/dry_crud/templates/test/unit/standard_table_builder_test.rb +99 -0
  33. data/test/templates/app/controllers/ajax_controller.rb +7 -0
  34. data/test/templates/app/controllers/application_controller.rb +11 -0
  35. data/test/templates/app/controllers/cities_controller.rb +13 -0
  36. data/test/templates/app/controllers/people_controller.rb +7 -0
  37. data/test/templates/app/helpers/people_helper.rb +7 -0
  38. data/test/templates/app/models/city.rb +13 -0
  39. data/test/templates/app/models/person.rb +9 -0
  40. data/test/templates/app/views/ajax/_hello.html.erb +1 -0
  41. data/test/templates/app/views/ajax/ajax.js.rjs +1 -0
  42. data/test/templates/app/views/ajax/index.html.erb +9 -0
  43. data/test/templates/app/views/cities/_attrs.html.erb +1 -0
  44. data/test/templates/app/views/cities/_form.html.erb +4 -0
  45. data/test/templates/app/views/cities/_hello.html.erb +1 -0
  46. data/test/templates/app/views/cities/_list.html.erb +5 -0
  47. data/test/templates/config/routes.rb +11 -0
  48. data/test/templates/db/migrate/20100511174904_create_people_and_cities.rb +23 -0
  49. data/test/templates/test/fixtures/cities.yml +11 -0
  50. data/test/templates/test/fixtures/people.yml +14 -0
  51. data/test/templates/test/functional/cities_controller_test.rb +35 -0
  52. data/test/templates/test/functional/people_controller_test.rb +30 -0
  53. metadata +127 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Pascal Zumkehr
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,208 @@
1
+ = DRY CRUD
2
+
3
+ Generates simple and extendable controller, views and helpers that support you to DRY up the CRUD code in your Rails project. Start with these elements and build a clean base to efficiently develop your application upon. First, you need to install the gem with
4
+
5
+ gem install dry_crud
6
+
7
+ Then simply run the following generator in your Rails app to get the goodies:
8
+
9
+ script/generate dry_crud
10
+
11
+ To integrate dry_crud into your code, only a few additions are required:
12
+
13
+ * To use a standard formatting, tables and forms throughout your application, add 'helper :standard' to your ApplicationController.
14
+ * To get uniform CRUD functionallity, just subclass your controllers from CrudController.
15
+ * Add a :label method to your models for a human-friendly representation.
16
+
17
+ == Overview
18
+
19
+ In most Rails applications, you have some models that require basic CRUD (create, read, update, delete) functionality. There are various possibilities like Rails scaffolding, {Active Scaffold}[http://activescaffold.com/] or {Dry Scalffold}[http://github.com/grimen/dry_scaffold]. Still, various parts in your application remain duplicated. While you might pull up common methods into a common superclass controller, most views still contain very similar code.
20
+
21
+ Enter DRY CRUD.
22
+
23
+ <b>
24
+ The main idea of DRY CRUD is to concentrate basic functionality of your application, like CRUD actions, uniform formatting, forms and tables into specifically extendable units. DRY CRUD generates various foundation classes that you may freely adapt to your application's needs. For each model, you may transparently customize arbitrary parts or just fallback to the general behaviour. This applies not only for controllers, but also for view templates and helpers.
25
+ </b>
26
+
27
+ A core element of DRY CRUD is the RenderInheritable module. This gives you inheritable views and partials. In the default case, a template is searched in the current controller's view folder. If it is not found there, the template with the same name in the view folder of the superclass controller is used. This lookup path might be customized as well.
28
+
29
+ DRY CRUD is a Rails generator. All code resides in your application and is open for you to inspect and to extend. You may pick whatever you consider useful or adapt what is not sufficient. Even if you do not require any CRUD functionality, you might find some helpers simplifying your work. There are no runtime dependencies to the dry_crud gem. Having said this, DRY CRUD does not want to provide a maximum of functionality that requires a lot of configuration, but rather a clean and lightweight foundation to build your application's requirements upon. This is why DRY CRUD comes as a generator and not as a Rails plugin.
30
+
31
+ DRY CRUD does not depend on any other plugins, but easily allows you to integrate them in order to unify the behaviour of your CRUD controllers. You might even use the plugins mentioned above to adapt your generated CrudController base class. All classes come with thorough tests that provide you with a solid foundation for implementing your own adaptions.
32
+
33
+ The gem was developped with Rails 2.3.8, we plan to reach a fully compatible version 1.0 when Rails 3 has come out.
34
+
35
+ See the Examples section for some use cases and the Generated Files section below for details on the single classes and templates.
36
+
37
+ == Examples
38
+
39
+ === Controller with CRUD functionality
40
+
41
+ Say you want to manage a Person model. Create the following controller and add a :label method to your model for a human-friendly representation used in page titles.
42
+
43
+ <tt>app/controllers/people_controller.rb</tt>:
44
+ class PeopleController < CrudController
45
+ end
46
+
47
+ <tt>app/models/person.rb</tt>:
48
+ class Person
49
+ def label
50
+ "#{lastname} #{firstname}"
51
+ end
52
+ end
53
+
54
+ That's it. You have an overview of all people, detail pages and forms to edit and create persons. Oh, and of course, you may delete persons as well. By default, all attributes are displayed and formatted according to their column type whereever they appear. This holds for the input fields as well.
55
+
56
+
57
+ ==== Customize single views
58
+
59
+ Well, maybe there are certain attributes you do not want to display in the people list, or others that are not editable. No problem, simply create a <tt>list</tt> partial in <tt>app/views/people/_list.html.erb</tt> to customize this:
60
+
61
+ <%= crud_table [:lastname, :firstname, :city, :sex] %>
62
+
63
+ This only displays these three attributes in the table. All other templates, as well as the main index view, fallback to the ones in <tt>app/views/crud</tt>.
64
+
65
+
66
+ ==== Adapt general behaviour
67
+
68
+ Next, let's adapt a part of the general behaviour 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:
69
+
70
+ In <tt>app/controllers/crud_controller.rb</tt>, change the index action to
71
+
72
+ def index
73
+ @entries = model_class.paginate({:page => params[:page]}.merge(find_all_options))
74
+ respond_with @entries
75
+ end
76
+
77
+ In <tt>app/views/crud/index.html.erb</tt>, add the following line for the pagination links:
78
+ <%= will_paginate @entries %>
79
+
80
+ And we are done again. All our controllers inheriting from CrudController, including above PeopleController, now have paginated index views. Because our customization for the people table is in the seperate <tt>_list</tt> partial, no further modifications are required.
81
+
82
+
83
+ ==== Special formatting for selected attributes
84
+
85
+ Sometimes, the default formatting provided by :format_attr will not be sufficient. We have a boolean column <tt>sex</tt> in our model, but would like to display 'male' or 'female' for it (instead of 'no' or 'yes', which is a bit cryptic). Just define a method in your view helper starting with <tt>format_</tt>, followed by the attribute name:
86
+
87
+ In <tt>app/helpers/people.rb</tt>:
88
+ def format_sex(person)
89
+ person.sex ? 'female' : 'male'
90
+ end
91
+
92
+ By the way: The method :f in StandardHelper uniformly formats arbitrary values according to their class.
93
+
94
+
95
+ ==== CRUD controller callbacks
96
+
97
+ 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:
98
+
99
+ In <tt>app/controllers/people_controller.rb</tt>:
100
+ after_save :upload_picture
101
+ before_destroy :delete_picture
102
+
103
+ def upload_picture
104
+ store_file(params[:person][:picture]) if params[:person][:picture]
105
+ end
106
+
107
+ def delete_picture
108
+ if !perform_delete_picture(@entry.picture)
109
+ flash[:error] = 'Could not delete picture'
110
+ false
111
+ end
112
+ end
113
+
114
+
115
+ === Standard Tables and Forms
116
+
117
+ DRY CRUD also provides two builder classes for update/create forms and tables for displaying entries of one model. They may be used allover your application to DRY up the form and table code. Normally, they are used with the corresponding methods from StandardHelper.
118
+
119
+ ==== Tables
120
+
121
+ This is the code to define a table with some attribue columns for a list of same-type entries. Columns get a header corresponding to the attribute name:
122
+ <%= table(@people) do |t|
123
+ t.attrs :lastname, :firstname
124
+ end %>
125
+
126
+ If entries is empty, a basic 'No entries available' message is rendered instead of the table.
127
+
128
+ To render custom columns, use the :col method:
129
+ <%= table(@people) do |t|
130
+ t.attrs :lastname, :firstname
131
+ t.col('', :class => 'center') {|entry| image_tag(entry.picture) }
132
+ t.attrs :street
133
+ t.col('Map') {|entry| link_to(entry.city, "http://maps.google.com/?q=#{entry.city}" }
134
+ end %>
135
+
136
+ ==== Forms
137
+
138
+ 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:
139
+
140
+ <% form(@person, [:firstname, :lastname, :age, :city] -%>
141
+
142
+ Of course, custom input fields may be defined as well:
143
+ <% form(@person, [], :url => {:action => 'custom_update', :id => @person.id}) do |f| -%>
144
+ <%= f.labeled_input_fields :firstname, :lastname %>
145
+ <% f.labeled(:sex) do %>
146
+ <%= f.radio_button :sex, true %> female
147
+ <%= f.radio_button :sex, false %> male
148
+ <% end -%>
149
+ <%= f.labeled_integer_field :age %>
150
+ <%= f.labeled_file_field :picture %>
151
+ <% end -%>
152
+
153
+ == Generated Files
154
+
155
+ All generated files are supposed to provide a reasonable foundation for the CRUD functionality. You are encouraged to adapt them to fit the needs of your application. They're yours!
156
+
157
+ === Controller:
158
+
159
+ {controller/crud_controller.rb}[http://codez.ch/dry_crud/?q=CrudController]:: Abstract controller providing basic CRUD actions in a RESTful way. This implementation mainly follows the one of the Rails scaffolding controller. Some enhancements were made to ease extendability. Several protected helper methods are there to be (optionally) overriden by subclasses.
160
+
161
+ === Helpers:
162
+
163
+ {helpers/standard_helper.rb}[http://codez.ch/dry_crud/?q=StandardHelper]:: A view helper to standartize often used functions like formatting, tables, forms or action links. This helper is ideally defined in the ApplicationController. It is required to use the StandardTableBuilder and the StandardFormBuilder.
164
+
165
+ {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.
166
+
167
+ === Views:
168
+
169
+ views/crud/index.html.erb:: The index view displaying a table with all entries and an action link to add new ones.
170
+
171
+ 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.
172
+
173
+ views/crud/show.html.erb:: The show view displaying all the attributes of one entry and the various actions to perform on it.
174
+
175
+ views/crud/_attrs.html.erb:: A partial defining the attributes to be displayed in the show view.
176
+
177
+ views/crud/new.html.erb:: The view to create a new entry.
178
+
179
+ views/crud/edit.html.erb:: The view to edit an existing entry.
180
+
181
+ 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 models view directory.
182
+
183
+ views/shared/_labeled.html.erb:: Partial to define the layout for an arbitrary content with a label.
184
+
185
+ views/layouts/crud.html.erb:: An example layout showing how to use the @title and +flash+. Most probably you want to include this in your application.html.erb or adapt this main crud templates, so you wont need this file.
186
+
187
+ public/stylesheets/crud.css:: A simple CSS with all the classes and ids used in the crud code.
188
+
189
+ === Lib:
190
+
191
+ {lib/standard_table_builder.rb}[http://codez.ch/dry_crud/?q=StandardTableBuilder]:: A simple helper to easily define tables listing several rows of the same data type.
192
+
193
+ {lib/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.
194
+
195
+ {lib/render_inheritable.rb}[http://codez.ch/dry_crud/?q=RenderInheritable]:: 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.
196
+
197
+ {lib/crud_callbacks.rb}[http://codez.ch/dry_crud/?q=CrudCallbacks]:: Defines before and after callback hooks for create, update, save and destroy. When to execute the callbacks is in the responsibility of the clients of this module. This module is used in the CrudController to provide callbacks.
198
+
199
+ === Tests:
200
+
201
+ test/crud_test_model.rb:: A dummy model to run CRUD tests against.
202
+
203
+ {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 handfull of CRUD functionality tests for the provided implementation. So for each new crud controller, you get 20 tests for free.
204
+
205
+ test/several other tests:: Testing the provided implementation and a great base to test your adaptions of the CRUD code.
206
+
207
+
208
+
data/Rakefile ADDED
@@ -0,0 +1,101 @@
1
+ require 'rubygems'
2
+ require "rake/testtask"
3
+ require 'rake/gempackagetask'
4
+ require 'rake/rdoctask'
5
+ sdoc = (require 'sdoc' || true) rescue false
6
+
7
+ load 'dry_crud.gemspec'
8
+
9
+ TEST_APP_ROOT = File.join(File.dirname(__FILE__), 'test', 'test_app')
10
+ GENERATOR_ROOT = File.join(File.dirname(__FILE__), 'rails_generators', 'dry_crud')
11
+
12
+ task :default => :test
13
+
14
+ desc "Run all tests"
15
+ task :test => ['test:app:init'] do
16
+ Rake::TestTask.new do |test|
17
+ test.libs << "test/test_app/test"
18
+ test.test_files = Dir[ "test/test_app/test/**/*_test.rb" ]
19
+ test.verbose = true
20
+ end
21
+ end
22
+
23
+ namespace :test do
24
+ namespace :app do
25
+ task :environment do
26
+ ::RAILS_ROOT = TEST_APP_ROOT
27
+ ::RAILS_ENV = 'test'
28
+
29
+ require(File.join(TEST_APP_ROOT, 'config', 'environment'))
30
+ end
31
+
32
+ desc "Create a rails test application"
33
+ task :create do
34
+ unless File.exist?(TEST_APP_ROOT)
35
+ sh "rails #{TEST_APP_ROOT}"
36
+ end
37
+ end
38
+
39
+ desc "Run the dry_crud generator for the test application"
40
+ task :generate_crud => [:create, :environment] do
41
+ require 'rails_generator'
42
+ require File.join(GENERATOR_ROOT, 'dry_crud_generator')
43
+
44
+ Rails::Generator::Spec.new('dry_crud', GENERATOR_ROOT, :RubyGems).klass.new([], :collision => :force).command(:create).invoke!
45
+ end
46
+
47
+ desc "Initializes the test application with a couple of classes"
48
+ task :init => :generate_crud do
49
+ FileUtils.cp_r(File.join(File.dirname(__FILE__), 'test', 'templates', '.'), TEST_APP_ROOT)
50
+ FileUtils.cd(TEST_APP_ROOT) do
51
+ sh "rake db:migrate db:test:prepare"
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ desc "Clean up all generated resources"
58
+ task :clobber do
59
+ FileUtils.rm_rf(TEST_APP_ROOT)
60
+ end
61
+
62
+ desc "Install dry_crud as a local gem."
63
+ task :install => [:package] do
64
+ sudo = RUBY_PLATFORM =~ /win32/ ? '' : 'sudo'
65
+ gem = RUBY_PLATFORM =~ /java/ ? 'jgem' : 'gem'
66
+ sh %{#{sudo} #{gem} install --no-ri pkg/dry_crud-#{File.read('VERSION').strip}}
67
+ end
68
+
69
+ desc "Deploy rdoc to website"
70
+ task :site => :rdoc do
71
+ sh "rsync -rzv rdoc/ #{ENV['DEST']}"
72
+ end
73
+
74
+ # :package task
75
+ Rake::GemPackageTask.new(DRY_CRUD_GEMSPEC) do |pkg|
76
+ if Rake.application.top_level_tasks.include?('release')
77
+ pkg.need_tar_gz = true
78
+ pkg.need_tar_bz2 = true
79
+ pkg.need_zip = true
80
+ end
81
+ end
82
+
83
+ # :rdoc task
84
+ Rake::RDocTask.new do |rdoc|
85
+ rdoc.title = 'Dry Crud'
86
+ rdoc.options << '--line-numbers' << '--inline-source'
87
+ if sdoc
88
+ rdoc.options << '--fmt' << 'shtml'
89
+ rdoc.template = 'direct'
90
+ end
91
+ rdoc.rdoc_files.include(*FileList.new('*') do |list|
92
+ list.exclude(/(^|[^.a-z])[a-z]+/)
93
+ list.exclude('TODO')
94
+ end.to_a)
95
+ rdoc.rdoc_files.include('rails_generators/dry_crud/templates/**/*.rb')
96
+ rdoc.rdoc_files.exclude('rails_generators/dry_crud/templates/**/*_test.rb')
97
+ rdoc.rdoc_files.exclude('TODO')
98
+
99
+ rdoc.rdoc_dir = 'rdoc'
100
+ rdoc.main = 'README.rdoc'
101
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.6.0
@@ -0,0 +1 @@
1
+ script/generate dry_crud
@@ -0,0 +1,22 @@
1
+ class DryCrudGenerator < Rails::Generator::Base
2
+
3
+ def manifest
4
+ record do |m|
5
+ # copy everything in template subfolders
6
+ Dir.chdir(File.join(File.dirname(__FILE__), 'templates')) do
7
+ Dir.glob("*/**/*").each do |f|
8
+ if File.directory?(f)
9
+ m.directory f
10
+ else
11
+ m.file f, f
12
+ end
13
+ end
14
+ end
15
+
16
+ #m.template src, dst
17
+
18
+ m.readme "INSTALL"
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,13 @@
1
+
2
+ Thank you for using dry_crud. Feel free to adapt all generated classes to
3
+ your needs. To integrate dry_crud into your code, only a few additions are
4
+ required:
5
+
6
+ * To use a standard formatting, tables and forms throught your application,
7
+ add 'helper :standard' to your ApplicationController.
8
+ * To get uniform CRUD functionallity, just subclass your controllers from
9
+ CrudController.
10
+ * Add a :label method to your models for a human-friendly representation.
11
+
12
+ Enjoy dry_crud and stay DRY!
13
+
@@ -0,0 +1,182 @@
1
+ # Abstract controller providing basic CRUD actions.
2
+ # This implementation mainly follows the one of the Rails scaffolding
3
+ # controller. Some enhancements were made to ease extendability.
4
+ # Several protected helper methods are there to be (optionally) overriden by subclasses.
5
+ class CrudController < ApplicationController
6
+
7
+ include CrudCallbacks
8
+ include RenderInheritable
9
+
10
+ delegate :model_class, :model_identifier, :models_label, :to => 'self.class'
11
+
12
+ # Verify that required :id param is present and only allow good http methods.
13
+ verify :params => :id, :only => :show, :redirect_to => { :action => 'index' }
14
+ verify :method => :post, :only => :create, :redirect_to => { :action => 'index' }
15
+ verify :method => [:put, :post], :params => :id, :only => :update, :redirect_to => { :action => 'index' }
16
+ verify :method => [:delete, :post], :params => :id, :only => :destroy, :redirect_to => { :action => 'index' }
17
+
18
+ # Set up entry object to use in the various actions.
19
+ before_filter :build_entry, :only => [:new, :create]
20
+ before_filter :set_entry, :only => [:show, :edit, :update, :destroy]
21
+
22
+ helper_method :model_class, :models_label, :full_entry_label
23
+
24
+ hide_action :model_class, :models_label, :model_identifier, :run_callbacks, :inheritable_root_controller
25
+
26
+
27
+ ############## ACTIONS ############################################
28
+
29
+ # List all entries of this model.
30
+ # GET /entries
31
+ # GET /entries.xml
32
+ def index
33
+ @entries = model_class.all find_all_options
34
+ respond_with @entries
35
+ end
36
+
37
+ # Show one entry of this model.
38
+ # GET /entries/1
39
+ # GET /entries/1.xml
40
+ def show
41
+ respond_with @entry
42
+ end
43
+
44
+ # Display a form to create a new entry of this model.
45
+ # GET /entries/new
46
+ # GET /entries/new.xml
47
+ def new
48
+ respond_with @entry
49
+ end
50
+
51
+ # Display a form to edit an exisiting entry of this model.
52
+ # GET /entries/1/edit
53
+ def edit
54
+ render_with_callback :edit
55
+ end
56
+
57
+ # Create a new entry of this model from the passed params.
58
+ # POST /entries
59
+ # POST /entries.xml
60
+ def create
61
+ created = with_callbacks(:create) { save_entry }
62
+
63
+ respond_processed(created, 'created', 'new') do |format|
64
+ format.html { redirect_to_show }
65
+ format.xml { render :xml => @entry, :status => :created, :location => @entry }
66
+ end
67
+ end
68
+
69
+ # Update an existing entry of this model from the passed params.
70
+ # PUT /entries/1
71
+ # PUT /entries/1.xml
72
+ def update
73
+ @entry.attributes = params[model_identifier]
74
+ updated = with_callbacks(:update) { save_entry }
75
+
76
+ respond_processed(updated, 'updated', 'edit') do |format|
77
+ format.html { redirect_to_show }
78
+ format.xml { head :ok }
79
+ end
80
+ end
81
+
82
+ # Destroy an existing entry of this model.
83
+ # DELETE /entries/1
84
+ # DELETE /entries/1.xml
85
+ def destroy
86
+ destroyed = with_callbacks(:destroy) { @entry.destroy }
87
+
88
+ respond_processed(destroyed, 'destroyed', 'show') do |format|
89
+ format.html { redirect_to_index }
90
+ format.xml { head :ok }
91
+ end
92
+ end
93
+
94
+ protected
95
+
96
+ ############# CUSTOMIZABLE HELPER METHODS ##############################
97
+
98
+ # Convenience method to respond to various formats with the given object.
99
+ def respond_with(object)
100
+ respond_to do |format|
101
+ format.html { render_with_callback action_name }
102
+ format.xml { render :xml => object }
103
+ end
104
+ end
105
+
106
+ # Convenience method to respond to various formats if the performed
107
+ # action may succeed or fail. In case of failure, a standard response
108
+ # is given and the failed_action template is rendered. In case of success,
109
+ # the flash[:notice] is set and control is passed to the given block.
110
+ def respond_processed(success, operation, failed_action)
111
+ respond_to do |format|
112
+ if success
113
+ flash[:notice] = "#{full_entry_label} was successfully #{operation}."
114
+ yield format
115
+ else
116
+ format.html { render_with_callback failed_action }
117
+ format.xml { render :xml => @entry.errors, :status => :unprocessable_entity }
118
+ end
119
+ end
120
+ end
121
+
122
+ # Creates a new model entry from the given params.
123
+ def build_entry
124
+ @entry = model_class.new(params[model_identifier])
125
+ end
126
+
127
+ # Sets an existing model entry from the given id.
128
+ def set_entry
129
+ @entry = model_class.find(params[:id])
130
+ end
131
+
132
+ # A label for the current entry, including the model name.
133
+ def full_entry_label
134
+ "#{models_label.singularize} '#{@entry.label}'"
135
+ end
136
+
137
+ # Find options used in the index action.
138
+ def find_all_options
139
+ {}
140
+ end
141
+
142
+ # Redirects to the show action of a single entry.
143
+ def redirect_to_show
144
+ redirect_to @entry
145
+ end
146
+
147
+ # Redirects to the main action of this controller.
148
+ def redirect_to_index
149
+ redirect_to :action => 'index'
150
+ end
151
+
152
+ # Helper method to run before_render callbacks and render the action.
153
+ # If a callback renders or redirects, the action is not rendered.
154
+ def render_with_callback(action)
155
+ render_callbacks(action)
156
+ render_inheritable :action => action unless performed?
157
+ end
158
+
159
+ # Saves the current entry with callbacks.
160
+ def save_entry
161
+ with_callbacks(:save) { @entry.save }
162
+ end
163
+
164
+ class << self
165
+ # The ActiveRecord class of the model.
166
+ def model_class
167
+ @model_class ||= controller_name.classify.constantize
168
+ end
169
+
170
+ # The identifier of the model used for form parameters.
171
+ # I.e., the symbol of the underscored model name.
172
+ def model_identifier
173
+ @model_identifier ||= model_class.name.underscore.to_sym
174
+ end
175
+
176
+ # A human readable plural name of the model.
177
+ def models_label
178
+ @models_label ||= model_class.human_name.pluralize
179
+ end
180
+ end
181
+
182
+ end
@@ -0,0 +1,45 @@
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 CrudHelper
5
+
6
+ # Create a table of the @entries variable with the default or
7
+ # the passed attributes in its columns.
8
+ def crud_table(attrs = nil, &block)
9
+ if block_given?
10
+ table(@entries, &block)
11
+ else
12
+ table(@entries) do |t|
13
+ t.attrs(*(attrs || default_attrs))
14
+ add_list_actions(t)
15
+ end
16
+ end
17
+ end
18
+
19
+ # Adds a set of standard action link column (show, edit, destroy) to the given table.
20
+ def add_list_actions(table)
21
+ table.col { |e| link_action_show(e) }
22
+ table.col { |e| link_action_edit(e) }
23
+ table.col { |e| link_action_destroy(e) }
24
+ end
25
+
26
+ # Renders a generic form for the current entry with :default_attrs or the
27
+ # given attribute array, using the StandardFormBuilder.
28
+ # If a block is given, a custom form may be rendered and attrs is ignored.
29
+ def crud_form(attrs = nil, options = {}, &block)
30
+ unless attrs
31
+ attrs = default_attrs
32
+ [:created_at, :updated_at].each {|a| attrs.delete(a) }
33
+ end
34
+ standard_form(@entry, attrs, &block)
35
+ end
36
+
37
+ # The default attributes to use in attrs, list and form partials.
38
+ # These are all defined attributes except certain special ones like 'id' or 'position'.
39
+ def default_attrs
40
+ attrs = model_class.column_names.collect(&:to_sym)
41
+ [:id, :position].each {|a| attrs.delete(a) }
42
+ attrs
43
+ end
44
+
45
+ end