dry_crud 0.6.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +41 -36
- data/Rakefile +51 -43
- data/VERSION +1 -1
- data/lib/generators/dry_crud/USAGE +1 -0
- data/lib/generators/dry_crud/dry_crud_generator.rb +20 -0
- data/{rails_generators → lib/generators}/dry_crud/templates/INSTALL +4 -3
- data/{rails_generators → lib/generators}/dry_crud/templates/app/controllers/crud_controller.rb +31 -9
- data/lib/generators/dry_crud/templates/app/controllers/render_inheritable.rb +152 -0
- data/{rails_generators → lib/generators}/dry_crud/templates/app/helpers/crud_helper.rb +4 -4
- data/{rails_generators/dry_crud/templates/lib → lib/generators/dry_crud/templates/app/helpers}/standard_form_builder.rb +0 -0
- data/{rails_generators → lib/generators}/dry_crud/templates/app/helpers/standard_helper.rb +81 -76
- data/{rails_generators/dry_crud/templates/lib → lib/generators/dry_crud/templates/app/helpers}/standard_table_builder.rb +45 -46
- data/{rails_generators → lib/generators}/dry_crud/templates/app/views/crud/_attrs.html.erb +0 -0
- data/lib/generators/dry_crud/templates/app/views/crud/_form.html.erb +1 -0
- data/{rails_generators → lib/generators}/dry_crud/templates/app/views/crud/_list.html.erb +0 -0
- data/{rails_generators → lib/generators}/dry_crud/templates/app/views/crud/edit.html.erb +1 -1
- data/{rails_generators → lib/generators}/dry_crud/templates/app/views/crud/index.html.erb +1 -1
- data/{rails_generators → lib/generators}/dry_crud/templates/app/views/crud/new.html.erb +1 -1
- data/{rails_generators → lib/generators}/dry_crud/templates/app/views/crud/show.html.erb +1 -1
- data/{rails_generators → lib/generators}/dry_crud/templates/app/views/layouts/crud.html.erb +1 -0
- data/lib/generators/dry_crud/templates/app/views/shared/_error_messages.html.erb +10 -0
- data/lib/generators/dry_crud/templates/app/views/shared/_labeled.html.erb +4 -0
- data/{rails_generators → lib/generators}/dry_crud/templates/public/stylesheets/crud.css +5 -5
- data/{rails_generators → lib/generators}/dry_crud/templates/test/crud_test_model.rb +25 -17
- data/{rails_generators → lib/generators}/dry_crud/templates/test/functional/crud_controller_test_helper.rb +36 -32
- data/{rails_generators → lib/generators}/dry_crud/templates/test/functional/crud_test_models_controller_test.rb +17 -4
- data/{rails_generators → lib/generators}/dry_crud/templates/test/unit/crud_helper_test.rb +51 -6
- data/{rails_generators → lib/generators}/dry_crud/templates/test/unit/render_inheritable_test.rb +31 -39
- data/{rails_generators → lib/generators}/dry_crud/templates/test/unit/standard_form_builder_test.rb +0 -0
- data/{rails_generators → lib/generators}/dry_crud/templates/test/unit/standard_helper_test.rb +76 -54
- data/{rails_generators → lib/generators}/dry_crud/templates/test/unit/standard_table_builder_test.rb +42 -40
- data/test/templates/app/views/ajax/ajax.js.rjs +1 -1
- data/test/templates/app/views/ajax/index.html.erb +2 -2
- data/test/templates/app/views/cities/_form.html.erb +1 -1
- data/test/templates/app/views/cities/_list.html.erb +1 -2
- data/test/templates/config/routes.rb +14 -5
- metadata +51 -37
- data/rails_generators/dry_crud/USAGE +0 -1
- data/rails_generators/dry_crud/dry_crud_generator.rb +0 -22
- data/rails_generators/dry_crud/templates/app/views/crud/_form.html.erb +0 -1
- data/rails_generators/dry_crud/templates/app/views/shared/_labeled.html.erb +0 -5
- data/rails_generators/dry_crud/templates/lib/crud_callbacks.rb +0 -55
- data/rails_generators/dry_crud/templates/lib/render_inheritable.rb +0 -118
data/README.rdoc
CHANGED
@@ -1,36 +1,42 @@
|
|
1
1
|
= DRY CRUD
|
2
2
|
|
3
|
-
|
3
|
+
DRY CRUD 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
4
|
|
5
5
|
gem install dry_crud
|
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
|
-
|
9
|
+
group :development, :test do
|
10
|
+
gem 'dry_crud'
|
11
|
+
end
|
12
|
+
|
13
|
+
Don't worry, it's a simple development dependency. You may even savely remove it after you have run in once. So now, you should run the generator to get the goodies:
|
14
|
+
|
15
|
+
rails generate dry_crud
|
16
|
+
|
17
|
+
To integrate DRY CRUD into your code, only a few additions are required:
|
10
18
|
|
11
|
-
|
19
|
+
* For uniform CRUD functionality, just subclass your controllers from +CrudController+.
|
20
|
+
* To use standard formatting, tables and forms throughout your application, add <tt>helper :standard</tt> to your +ApplicationController+ and benefit everywhere from these little helper methods.
|
21
|
+
* Add a <tt>:label</tt> method to your models for a human-friendly representation.
|
12
22
|
|
13
|
-
|
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.
|
23
|
+
Version 1.0.0 is built for Rails 3. If you need a version for Rails 2.3, please get version 0.6.0 of the gem or go to the rails-2.3 branch on Github.
|
16
24
|
|
17
25
|
== Overview
|
18
26
|
|
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
|
27
|
+
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 Scaffold}[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
28
|
|
21
29
|
Enter DRY CRUD.
|
22
30
|
|
23
31
|
<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
|
32
|
+
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 behavior. This applies not only for controllers, but also for view templates and helpers.
|
25
33
|
</b>
|
26
34
|
|
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.
|
35
|
+
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. RenderInheritable is also available as a stand-alone gem at http://github.com/codez/render_inheritable.
|
28
36
|
|
29
37
|
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
38
|
|
31
|
-
DRY CRUD does not depend on any other plugins, but easily allows you to integrate them in order to unify the
|
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.
|
39
|
+
DRY CRUD does not depend on any other plugins, but easily allows you to integrate them in order to unify the behavior 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.
|
34
40
|
|
35
41
|
See the Examples section for some use cases and the Generated Files section below for details on the single classes and templates.
|
36
42
|
|
@@ -38,7 +44,7 @@ See the Examples section for some use cases and the Generated Files section belo
|
|
38
44
|
|
39
45
|
=== Controller with CRUD functionality
|
40
46
|
|
41
|
-
Say you want to manage a Person model. Create the following controller and add a
|
47
|
+
Say you want to manage a +Person+ model. Create the following controller and add a <tt>:label</tt> method to your model for a human-friendly representation used in page titles.
|
42
48
|
|
43
49
|
<tt>app/controllers/people_controller.rb</tt>:
|
44
50
|
class PeopleController < CrudController
|
@@ -51,21 +57,21 @@ Say you want to manage a Person model. Create the following controller and add a
|
|
51
57
|
end
|
52
58
|
end
|
53
59
|
|
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
|
60
|
+
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 wherever they appear. This holds for the input fields as well.
|
55
61
|
|
56
62
|
|
57
63
|
==== Customize single views
|
58
64
|
|
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>
|
65
|
+
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
66
|
|
61
67
|
<%= crud_table [:lastname, :firstname, :city, :sex] %>
|
62
68
|
|
63
69
|
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
70
|
|
65
71
|
|
66
|
-
==== Adapt general
|
72
|
+
==== Adapt general behavior
|
67
73
|
|
68
|
-
Next, let's adapt a part of the general
|
74
|
+
Next, let's adapt a part of the general behavior used in all CRUD controllers. As an example, we include pagination with will_paginate[http://wiki.github.com/mislav/will_paginate/] in all our overview tables:
|
69
75
|
|
70
76
|
In <tt>app/controllers/crud_controller.rb</tt>, change the index action to
|
71
77
|
|
@@ -77,19 +83,19 @@ In <tt>app/controllers/crud_controller.rb</tt>, change the index action to
|
|
77
83
|
In <tt>app/views/crud/index.html.erb</tt>, add the following line for the pagination links:
|
78
84
|
<%= will_paginate @entries %>
|
79
85
|
|
80
|
-
And we are done again. All our controllers inheriting from CrudController
|
86
|
+
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 separate <tt> _list</tt> partial, no further modifications are required.
|
81
87
|
|
82
88
|
|
83
89
|
==== Special formatting for selected attributes
|
84
90
|
|
85
|
-
Sometimes, the default formatting provided by
|
91
|
+
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
92
|
|
87
93
|
In <tt>app/helpers/people.rb</tt>:
|
88
94
|
def format_sex(person)
|
89
95
|
person.sex ? 'female' : 'male'
|
90
96
|
end
|
91
97
|
|
92
|
-
By the way: The method
|
98
|
+
By the way: The method +:f+ in +StandardHelper+ uniformly formats arbitrary values according to their class.
|
93
99
|
|
94
100
|
|
95
101
|
==== CRUD controller callbacks
|
@@ -114,11 +120,11 @@ In <tt>app/controllers/people_controller.rb</tt>:
|
|
114
120
|
|
115
121
|
=== Standard Tables and Forms
|
116
122
|
|
117
|
-
DRY CRUD also provides two builder classes for update/create forms and tables for displaying entries of one model. They may be used
|
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+.
|
118
124
|
|
119
125
|
==== Tables
|
120
126
|
|
121
|
-
This is the code to define a table with some
|
127
|
+
This is the code to define a table with some attribute columns for a list of same-type entries. Columns get a header corresponding to the attribute name:
|
122
128
|
<%= table(@people) do |t|
|
123
129
|
t.attrs :lastname, :firstname
|
124
130
|
end %>
|
@@ -156,14 +162,22 @@ All generated files are supposed to provide a reasonable foundation for the CRUD
|
|
156
162
|
|
157
163
|
=== Controller:
|
158
164
|
|
159
|
-
{controller/crud_controller.rb}[http://codez.ch/dry_crud/?q=CrudController]:: Abstract controller providing basic CRUD actions
|
165
|
+
{controller/crud_controller.rb}[http://codez.ch/dry_crud/?q=CrudController]:: Abstract controller providing basic CRUD actions. This implementation mainly follows the one of the Rails scaffolding controller and responses to HTML and XML requests. Some enhancements were made to ease extendability. Several protected helper methods are there to be (optionally) overriden by subclasses. With the help of additional callbacks, it is possible to hook into the action procedures without overriding the entire method.
|
166
|
+
|
167
|
+
{controller/render_inheritable.rb}[http://codez.ch/dry_crud/?q=RenderInheritable]:: A controller enhancement that allows one to render inheritable views and partials. If no view file is found for the current controller, the corresponding file is looked up in its superclass hierarchy. Thus, only views or partials that look differently have to be overwritten.
|
168
|
+
|
160
169
|
|
161
170
|
=== Helpers:
|
162
171
|
|
163
|
-
{helpers/standard_helper.rb}[http://codez.ch/dry_crud/?q=StandardHelper]:: A view helper to
|
172
|
+
{helpers/standard_helper.rb}[http://codez.ch/dry_crud/?q=StandardHelper]:: A view helper to standardize often used functions like formatting, tables, forms or action links. This helper is ideally defined in the ApplicationController. It is required to use the StandardTableBuilder and the StandardFormBuilder.
|
164
173
|
|
165
174
|
{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
175
|
|
176
|
+
{helpers/standard_table_builder.rb}[http://codez.ch/dry_crud/?q=StandardTableBuilder]:: A simple helper object to easily define tables listing several rows of the same data type.
|
177
|
+
|
178
|
+
{helpers/standard_form_builder.rb}[http://codez.ch/dry_crud/?q=StandardFormBuilder]:: A form builder that automatically selects the corresponding input element for ActiveRecord columns. Input elements are rendered with a corresponding label by default.
|
179
|
+
|
180
|
+
|
167
181
|
=== Views:
|
168
182
|
|
169
183
|
views/crud/index.html.erb:: The index view displaying a table with all entries and an action link to add new ones.
|
@@ -184,23 +198,14 @@ views/shared/_labeled.html.erb:: Partial to define the layout for an arbitrary c
|
|
184
198
|
|
185
199
|
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
200
|
|
187
|
-
public/stylesheets/crud.css:: A simple CSS with all the classes and ids used in the
|
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.
|
201
|
+
public/stylesheets/crud.css:: A simple CSS with all the classes and ids used in the CRUD code.
|
196
202
|
|
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
203
|
|
199
204
|
=== Tests:
|
200
205
|
|
201
206
|
test/crud_test_model.rb:: A dummy model to run CRUD tests against.
|
202
207
|
|
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
|
208
|
+
{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.
|
204
209
|
|
205
210
|
test/several other tests:: Testing the provided implementation and a great base to test your adaptions of the CRUD code.
|
206
211
|
|
data/Rakefile
CHANGED
@@ -2,46 +2,50 @@ require 'rubygems'
|
|
2
2
|
require "rake/testtask"
|
3
3
|
require 'rake/gempackagetask'
|
4
4
|
require 'rake/rdoctask'
|
5
|
-
sdoc =
|
5
|
+
sdoc = begin
|
6
|
+
require 'sdoc'
|
7
|
+
true
|
8
|
+
rescue Exception
|
9
|
+
false
|
10
|
+
end
|
6
11
|
|
7
|
-
load
|
12
|
+
load 'dry_crud.gemspec'
|
8
13
|
|
9
14
|
TEST_APP_ROOT = File.join(File.dirname(__FILE__), 'test', 'test_app')
|
10
|
-
GENERATOR_ROOT = File.join(File.dirname(__FILE__), '
|
15
|
+
GENERATOR_ROOT = File.join(File.dirname(__FILE__), 'lib', 'generators', 'dry_crud')
|
11
16
|
|
12
17
|
task :default => :test
|
13
18
|
|
14
19
|
desc "Run all tests"
|
15
20
|
task :test => ['test:app:init'] do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
Rake::TestTask.new do |test|
|
22
|
+
test.libs << "test/test_app/test"
|
23
|
+
test.test_files = Dir[ "test/test_app/test/**/*_test.rb" ]
|
24
|
+
test.verbose = true
|
25
|
+
end
|
21
26
|
end
|
22
27
|
|
23
28
|
namespace :test do
|
24
|
-
|
29
|
+
namespace :app do
|
25
30
|
task :environment do
|
26
|
-
|
27
|
-
|
31
|
+
ENV['RAILS_ROOT'] = TEST_APP_ROOT
|
32
|
+
ENV['RAILS_ENV'] = 'test'
|
28
33
|
|
29
34
|
require(File.join(TEST_APP_ROOT, 'config', 'environment'))
|
30
35
|
end
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
desc "Create a rails test application"
|
38
|
+
task :create do
|
39
|
+
unless File.exist?(TEST_APP_ROOT)
|
40
|
+
sh "rails new #{TEST_APP_ROOT}"
|
41
|
+
end
|
42
|
+
end
|
38
43
|
|
39
44
|
desc "Run the dry_crud generator for the test application"
|
40
45
|
task :generate_crud => [:create, :environment] do
|
41
|
-
require 'rails_generator'
|
42
46
|
require File.join(GENERATOR_ROOT, 'dry_crud_generator')
|
43
47
|
|
44
|
-
|
48
|
+
DryCrudGenerator.new('', {:force => true}, :destination_root => TEST_APP_ROOT).invoke_all
|
45
49
|
end
|
46
50
|
|
47
51
|
desc "Initializes the test application with a couple of classes"
|
@@ -51,51 +55,55 @@ namespace :test do
|
|
51
55
|
sh "rake db:migrate db:test:prepare"
|
52
56
|
end
|
53
57
|
end
|
54
|
-
|
58
|
+
end
|
55
59
|
end
|
56
60
|
|
57
61
|
desc "Clean up all generated resources"
|
58
62
|
task :clobber do
|
59
|
-
|
63
|
+
FileUtils.rm_rf(TEST_APP_ROOT)
|
60
64
|
end
|
61
65
|
|
62
66
|
desc "Install dry_crud as a local gem."
|
63
67
|
task :install => [:package] do
|
64
|
-
|
65
|
-
|
66
|
-
|
68
|
+
sudo = RUBY_PLATFORM =~ /win32/ ? '' : 'sudo'
|
69
|
+
gem = RUBY_PLATFORM =~ /java/ ? 'jgem' : 'gem'
|
70
|
+
sh %{#{sudo} #{gem} install --no-ri pkg/dry_crud-#{File.read('VERSION').strip}}
|
67
71
|
end
|
68
72
|
|
69
73
|
desc "Deploy rdoc to website"
|
70
74
|
task :site => :rdoc do
|
71
|
-
|
75
|
+
if ENV['DEST']
|
76
|
+
sh "rsync -rzv rdoc/ #{ENV['DEST']}"
|
77
|
+
else
|
78
|
+
puts "Please specify a destination with DEST=user@server:/deploy/dir"
|
79
|
+
end
|
72
80
|
end
|
73
81
|
|
74
82
|
# :package task
|
75
83
|
Rake::GemPackageTask.new(DRY_CRUD_GEMSPEC) do |pkg|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
84
|
+
if Rake.application.top_level_tasks.include?('release')
|
85
|
+
pkg.need_tar_gz = true
|
86
|
+
pkg.need_tar_bz2 = true
|
87
|
+
pkg.need_zip = true
|
88
|
+
end
|
81
89
|
end
|
82
90
|
|
83
91
|
# :rdoc task
|
84
92
|
Rake::RDocTask.new do |rdoc|
|
85
|
-
|
86
|
-
|
93
|
+
rdoc.title = 'Dry Crud'
|
94
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
87
95
|
if sdoc
|
88
96
|
rdoc.options << '--fmt' << 'shtml'
|
89
97
|
rdoc.template = 'direct'
|
90
98
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
rdoc.rdoc_files.exclude('
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end
|
99
|
+
rdoc.rdoc_files.include(*FileList.new('*') do |list|
|
100
|
+
list.exclude(/(^|[^.a-z])[a-z]+/)
|
101
|
+
list.exclude('TODO')
|
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')
|
105
|
+
rdoc.rdoc_files.exclude('TODO')
|
106
|
+
|
107
|
+
rdoc.rdoc_dir = 'rdoc'
|
108
|
+
rdoc.main = 'README.rdoc'
|
109
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
@@ -0,0 +1 @@
|
|
1
|
+
rails g dry_crud
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class DryCrudGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
def self.source_root
|
6
|
+
File.join(File.dirname(__FILE__), 'templates')
|
7
|
+
end
|
8
|
+
|
9
|
+
def install_dry_crud
|
10
|
+
# copy everything in template subfolders
|
11
|
+
Dir.chdir(self.class.source_root) do
|
12
|
+
Dir.glob("*").each do |f|
|
13
|
+
directory(f) if File.directory?(f)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
readme "INSTALL"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -3,10 +3,11 @@ Thank you for using dry_crud. Feel free to adapt all generated classes to
|
|
3
3
|
your needs. To integrate dry_crud into your code, only a few additions are
|
4
4
|
required:
|
5
5
|
|
6
|
-
*
|
7
|
-
add 'helper :standard' to your ApplicationController.
|
8
|
-
* To get uniform CRUD functionallity, just subclass your controllers from
|
6
|
+
* For uniform CRUD functionallity, just subclass your controllers from
|
9
7
|
CrudController.
|
8
|
+
* To use standard formatting, tables and forms throughout your
|
9
|
+
application, add 'helper :standard' to your ApplicationController and
|
10
|
+
benefit everywhere from these little helper methods.
|
10
11
|
* Add a :label method to your models for a human-friendly representation.
|
11
12
|
|
12
13
|
Enjoy dry_crud and stay DRY!
|
data/{rails_generators → lib/generators}/dry_crud/templates/app/controllers/crud_controller.rb
RENAMED
@@ -1,24 +1,40 @@
|
|
1
1
|
# Abstract controller providing basic CRUD actions.
|
2
2
|
# This implementation mainly follows the one of the Rails scaffolding
|
3
|
-
# controller. Some enhancements were made to ease extendability.
|
3
|
+
# controller and responses to HTML and XML requests. Some enhancements were made to ease extendability.
|
4
4
|
# Several protected helper methods are there to be (optionally) overriden by subclasses.
|
5
|
+
# With the help of additional callbacks, it is possible to hook into the action procedures without
|
6
|
+
# overriding the entire method.
|
5
7
|
class CrudController < ApplicationController
|
6
8
|
|
7
|
-
include CrudCallbacks
|
8
9
|
include RenderInheritable
|
9
10
|
|
11
|
+
extend ActiveModel::Callbacks
|
12
|
+
|
13
|
+
# Defines before and after callback hooks for create, update, save and destroy.
|
14
|
+
define_model_callbacks :create, :update, :save, :destroy
|
15
|
+
|
16
|
+
# Defines before callbacks for the render actions.
|
17
|
+
define_model_callbacks :render_index,
|
18
|
+
:render_show,
|
19
|
+
:render_new,
|
20
|
+
:render_edit,
|
21
|
+
:only => :before,
|
22
|
+
:terminator => "result == false || performed?"
|
23
|
+
|
10
24
|
delegate :model_class, :model_identifier, :models_label, :to => 'self.class'
|
11
25
|
|
12
26
|
# Verify that required :id param is present and only allow good http methods.
|
13
|
-
|
14
|
-
verify :
|
15
|
-
verify :method =>
|
16
|
-
verify :method => [:
|
27
|
+
# Uncomment if you have the Rails verification plugin installed.
|
28
|
+
#verify :params => :id, :only => :show, :redirect_to => { :action => 'index' }
|
29
|
+
#verify :method => :post, :only => :create, :redirect_to => { :action => 'index' }
|
30
|
+
#verify :method => [:put, :post], :params => :id, :only => :update, :redirect_to => { :action => 'index' }
|
31
|
+
#verify :method => [:delete, :post], :params => :id, :only => :destroy, :redirect_to => { :action => 'index' }
|
17
32
|
|
18
33
|
# Set up entry object to use in the various actions.
|
19
34
|
before_filter :build_entry, :only => [:new, :create]
|
20
35
|
before_filter :set_entry, :only => [:show, :edit, :update, :destroy]
|
21
36
|
|
37
|
+
helper :standard
|
22
38
|
helper_method :model_class, :models_label, :full_entry_label
|
23
39
|
|
24
40
|
hide_action :model_class, :models_label, :model_identifier, :run_callbacks, :inheritable_root_controller
|
@@ -152,8 +168,8 @@ class CrudController < ApplicationController
|
|
152
168
|
# Helper method to run before_render callbacks and render the action.
|
153
169
|
# If a callback renders or redirects, the action is not rendered.
|
154
170
|
def render_with_callback(action)
|
155
|
-
|
156
|
-
|
171
|
+
run_callbacks(:"render_#{action}")
|
172
|
+
render :action => action unless performed?
|
157
173
|
end
|
158
174
|
|
159
175
|
# Saves the current entry with callbacks.
|
@@ -161,6 +177,12 @@ class CrudController < ApplicationController
|
|
161
177
|
with_callbacks(:save) { @entry.save }
|
162
178
|
end
|
163
179
|
|
180
|
+
# Helper method the run the given block in between the before and after
|
181
|
+
# callbacks of the given kind.
|
182
|
+
def with_callbacks(kind, &block)
|
183
|
+
send(:"_run_#{kind}_callbacks", &block)
|
184
|
+
end
|
185
|
+
|
164
186
|
class << self
|
165
187
|
# The ActiveRecord class of the model.
|
166
188
|
def model_class
|
@@ -175,7 +197,7 @@ class CrudController < ApplicationController
|
|
175
197
|
|
176
198
|
# A human readable plural name of the model.
|
177
199
|
def models_label
|
178
|
-
@models_label ||= model_class.
|
200
|
+
@models_label ||= model_class.model_name.human.pluralize
|
179
201
|
end
|
180
202
|
end
|
181
203
|
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# Allows one to render inheritable views and partials.
|
2
|
+
# If no view file is found for the current controller, the corresponding file
|
3
|
+
# is looked up in its superclass hierarchy. This module must only be
|
4
|
+
# included in the root controller of the desired lookup hierarchy.
|
5
|
+
#
|
6
|
+
# By default, this module only supports direct inheritance over one level. By overriding
|
7
|
+
# the method lookup_path, you may define a custom lookup path. By providing an object
|
8
|
+
# for the 'with' parameter, this path may even be dynamic.
|
9
|
+
module RenderInheritable
|
10
|
+
|
11
|
+
# Add inheritable_root_path method to including controller.
|
12
|
+
def self.included(controller_class)
|
13
|
+
controller_class.send(:extend, ClassMethods)
|
14
|
+
|
15
|
+
controller_class.send(:class_variable_set, :@@inheritable_root_controller, controller_class)
|
16
|
+
controller_class.cattr_reader :inheritable_root_controller
|
17
|
+
end
|
18
|
+
|
19
|
+
# Performs a lookup for the given filename and returns the most specific
|
20
|
+
# folder that contains the file.
|
21
|
+
def find_inheritable_template_folder(name, partial = false)
|
22
|
+
self.class.find_inheritable_template_folder(view_context, name, partial, formats, template_lookup_param)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Override this method to specify a dynamic parameter used in the lookup path.
|
26
|
+
# For the default inheritance lookup, this parameter is not needed.
|
27
|
+
def template_lookup_param
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
# Performs a lookup for the given filename and returns the most specific
|
33
|
+
# folder that contains the file.
|
34
|
+
def find_inheritable_template_folder(view_context, name, partial, formats, param = nil)
|
35
|
+
find_inheritable_template_folder_cached(view_context, name, partial, formats, param) do
|
36
|
+
find_inheritable_artifact(param) do |folder|
|
37
|
+
view_context.template_exists?(name, folder, partial)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Performs a lookup for a controller and returns the name of the most specific one found.
|
43
|
+
# 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
|
+
# exist to render templates from corresponding view folders.
|
46
|
+
def inheritable_controller(param = nil)
|
47
|
+
descendants = inheritable_root_controller.descendants
|
48
|
+
c = find_inheritable_artifact(param) do |folder|
|
49
|
+
descendants.any? { |s| s.controller_path == folder }
|
50
|
+
end
|
51
|
+
c || inheritable_root_controller.controller_path
|
52
|
+
end
|
53
|
+
|
54
|
+
# Runs through the lookup path and yields each folder to the passed block.
|
55
|
+
# If the block returns true, this folder is returned and no further lookup
|
56
|
+
# happens. If no folder is found, the nil is returned.
|
57
|
+
def find_inheritable_artifact(param = nil)
|
58
|
+
template_lookup_path(param).each { |folder| return folder if yield(folder) }
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# An array of controller names / folders, ordered from most specific to most general.
|
63
|
+
# May be dynamic dependening on the passed 'param' argument.
|
64
|
+
# You may override this method in an own controller to customize the lookup path.
|
65
|
+
def template_lookup_path(param = nil)
|
66
|
+
inheritance_lookup_path
|
67
|
+
end
|
68
|
+
|
69
|
+
# The inheritance path of controllers that is used as default lookup path.
|
70
|
+
def inheritance_lookup_path
|
71
|
+
path = [self]
|
72
|
+
until path.last == inheritable_root_controller
|
73
|
+
path << path.last.superclass
|
74
|
+
end
|
75
|
+
path.collect(&:controller_path)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Override view context class to includes the render inheritable modules.
|
79
|
+
def view_context_class
|
80
|
+
@view_context_class ||= begin
|
81
|
+
Class.new(super) do
|
82
|
+
include RenderInheritable::View
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# Performs a lookup for a template folder using the cache.
|
90
|
+
def find_inheritable_template_folder_cached(view_context, name, partial, formats, param = nil)
|
91
|
+
prefix = inheritable_cache_get(formats, name, partial, param)
|
92
|
+
return prefix if prefix
|
93
|
+
|
94
|
+
prefix = yield
|
95
|
+
|
96
|
+
if prefix
|
97
|
+
template = view_context.find_template_without_lookup(name, prefix, partial)
|
98
|
+
inheritable_cache_set(template.formats, name, partial, param, prefix)
|
99
|
+
end
|
100
|
+
prefix
|
101
|
+
end
|
102
|
+
|
103
|
+
# A simple template lookup cache for each controller.
|
104
|
+
def inheritable_cache #:nodoc:
|
105
|
+
# 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
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Gets the prefix from the cache. Returns nil if it's not there yet.
|
116
|
+
def inheritable_cache_get(formats, name, partial, param)
|
117
|
+
prefixes = formats.collect { |format| inheritable_cache[format.to_sym][partial][name][param] }
|
118
|
+
prefixes.compact!
|
119
|
+
prefixes.empty? ? nil : prefixes.first
|
120
|
+
end
|
121
|
+
|
122
|
+
# Stores the found prefix in the cache.
|
123
|
+
def inheritable_cache_set(formats, name, partial, param, prefix)
|
124
|
+
formats.each do |format|
|
125
|
+
# assign hash default values to respective key
|
126
|
+
inheritable_cache[format.to_sym] = hf = inheritable_cache[format.to_sym]
|
127
|
+
hf[partial] = hp = hf[partial]
|
128
|
+
hp[name] = hn = hp[name]
|
129
|
+
# finally store prefix in the deepest hash
|
130
|
+
hn[param] = prefix
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
# Extend ActionView so templates are looked up on a find_template call.
|
137
|
+
module View
|
138
|
+
def self.included(base)
|
139
|
+
base.send :alias_method_chain, :find_template, :lookup
|
140
|
+
end
|
141
|
+
|
142
|
+
# Perform a template lookup if the prefix corresponds to the current controller's path.
|
143
|
+
def find_template_with_lookup(name, prefix = nil, partial = false)
|
144
|
+
if prefix == controller_path
|
145
|
+
folder = controller.find_inheritable_template_folder(name, partial)
|
146
|
+
prefix = folder if folder
|
147
|
+
end
|
148
|
+
find_template_without_lookup(name, prefix, partial)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
@@ -5,12 +5,12 @@ module CrudHelper
|
|
5
5
|
|
6
6
|
# Create a table of the @entries variable with the default or
|
7
7
|
# the passed attributes in its columns.
|
8
|
-
def crud_table(attrs
|
8
|
+
def crud_table(*attrs, &block)
|
9
9
|
if block_given?
|
10
|
-
table(@entries, &block)
|
10
|
+
table(@entries, *attrs, &block)
|
11
11
|
else
|
12
|
-
|
13
|
-
|
12
|
+
attributes = attrs.present? ? attrs : default_attrs
|
13
|
+
table(@entries, *attributes) do |t|
|
14
14
|
add_list_actions(t)
|
15
15
|
end
|
16
16
|
end
|