nested_restful_scaffold 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Manifest +19 -0
- data/README +92 -0
- data/Rakefile +14 -0
- data/generators/nested_restful_scaffold/USAGE +31 -0
- data/generators/nested_restful_scaffold/nested_restful_scaffold_generator.rb +285 -0
- data/generators/nested_restful_scaffold/templates/controller.rb +94 -0
- data/generators/nested_restful_scaffold/templates/fixtures.yml +19 -0
- data/generators/nested_restful_scaffold/templates/functional_test.rb +45 -0
- data/generators/nested_restful_scaffold/templates/helper.rb +2 -0
- data/generators/nested_restful_scaffold/templates/layout.html.erb +17 -0
- data/generators/nested_restful_scaffold/templates/migration.rb +16 -0
- data/generators/nested_restful_scaffold/templates/model.rb +5 -0
- data/generators/nested_restful_scaffold/templates/style.css +54 -0
- data/generators/nested_restful_scaffold/templates/unit_test.rb +8 -0
- data/generators/nested_restful_scaffold/templates/view_edit.html.erb +20 -0
- data/generators/nested_restful_scaffold/templates/view_index.html.erb +26 -0
- data/generators/nested_restful_scaffold/templates/view_new.html.erb +19 -0
- data/generators/nested_restful_scaffold/templates/view_show.html.erb +9 -0
- data/nested_restful_scaffold.gemspec +30 -0
- metadata +78 -0
data/Manifest
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
README
|
2
|
+
Rakefile
|
3
|
+
generators/nested_restful_scaffold/USAGE
|
4
|
+
generators/nested_restful_scaffold/nested_restful_scaffold_generator.rb
|
5
|
+
generators/nested_restful_scaffold/templates/controller.rb
|
6
|
+
generators/nested_restful_scaffold/templates/fixtures.yml
|
7
|
+
generators/nested_restful_scaffold/templates/functional_test.rb
|
8
|
+
generators/nested_restful_scaffold/templates/helper.rb
|
9
|
+
generators/nested_restful_scaffold/templates/layout.html.erb
|
10
|
+
generators/nested_restful_scaffold/templates/migration.rb
|
11
|
+
generators/nested_restful_scaffold/templates/model.rb
|
12
|
+
generators/nested_restful_scaffold/templates/style.css
|
13
|
+
generators/nested_restful_scaffold/templates/unit_test.rb
|
14
|
+
generators/nested_restful_scaffold/templates/view_edit.html.erb
|
15
|
+
generators/nested_restful_scaffold/templates/view_index.html.erb
|
16
|
+
generators/nested_restful_scaffold/templates/view_new.html.erb
|
17
|
+
generators/nested_restful_scaffold/templates/view_show.html.erb
|
18
|
+
nested_restful_scaffold.gemspec
|
19
|
+
Manifest
|
data/README
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
= Nested Restful Scaffold
|
2
|
+
|
3
|
+
== What is NestedRestfulScaffold
|
4
|
+
NestedRestfulScaffold is a gem built by BadrIT (http://www.badrit.com) for easily generating controller, views, model and routes of nested resources.
|
5
|
+
|
6
|
+
== Why NestedRestfulScaffold
|
7
|
+
The story begins when I was working on a project at BadrIT (http://www.badrit.com) using Ruby on Rails.
|
8
|
+
We needed to generate simple scaffold controller, views, models and routes for many nested resources.
|
9
|
+
There were a huge number of resources and nested resources and sometimes the length of nesting was more than two.
|
10
|
+
The problem I faced that I need to create them with rails scaffold generator then I need to update all generated controllers, views and models.
|
11
|
+
* I need to update routes.rb to handle nested paths like /libraries/1/books
|
12
|
+
* In the controller I need to be sure I am accessing the correct resource in the nested chain.
|
13
|
+
* All ActiveRecord calls in the controller must be scoped.
|
14
|
+
* Views will have a lot of work to support nested forms and links.
|
15
|
+
* Create the Active Record associations in my models.
|
16
|
+
This was a big overhead to do all of that with all resources, so I decided to create a generator to do all that work for me.
|
17
|
+
|
18
|
+
== How to install
|
19
|
+
To install QuickMagick just type at your command line:
|
20
|
+
gem install nested_restful_scaffold
|
21
|
+
... and it's done.
|
22
|
+
You don't have to install any libraries or compile code from source.
|
23
|
+
|
24
|
+
== How to use
|
25
|
+
Usage:
|
26
|
+
./script/generate nested_restful_scaffold ModelName [field:type, field:type, resource1,resource2,...:resources]
|
27
|
+
|
28
|
+
Lets start with an example of library resource and each library has books and each book has pages.
|
29
|
+
|
30
|
+
=== Library Resource
|
31
|
+
Use the following command to create library resource
|
32
|
+
./script/generate nested_restful_scaffold library name:string address:text
|
33
|
+
It will use rails scaffold generator because there isn't any nested resources included in the previous command.
|
34
|
+
|
35
|
+
=== Book & Page Resources
|
36
|
+
Use the following command to create library resource
|
37
|
+
./script/generate nested_restful_scaffold book name:string description:text library:references library:resources
|
38
|
+
./script/generate nested_restful_scaffold page contents:text book:references library,book:resources
|
39
|
+
|
40
|
+
As it is shown, we used <b>library:resources</b> for books resources and <b>library,book:resources</b> for pages resources.
|
41
|
+
They will do the same job of rails scaffold generator in addition to the following:
|
42
|
+
N.B. i will explain the result of the pages generation and it will be the same for book resources.
|
43
|
+
|
44
|
+
* It updated the routes.rb file with libraries, books and pages routing to be as the following:
|
45
|
+
map.resources :libraries do |library|
|
46
|
+
library.resources :books do |book|
|
47
|
+
book.resources :pages
|
48
|
+
end
|
49
|
+
end
|
50
|
+
If you run rake routes, we can see the pages routes as the following:
|
51
|
+
library_book_pages GET /libraries/:library_id/books/:book_id/pages {:controller=>"pages", :action=>"index"}
|
52
|
+
formatted_library_book_pages GET /libraries/:library_id/books/:book_id/pages.:format {:controller=>"pages", :action=>"index"}
|
53
|
+
POST /libraries/:library_id/books/:book_id/pages {:controller=>"pages", :action=>"create"}
|
54
|
+
POST /libraries/:library_id/books/:book_id/pages.:format {:controller=>"pages", :action=>"create"}
|
55
|
+
new_library_book_page GET /libraries/:library_id/books/:book_id/pages/new {:controller=>"pages", :action=>"new"}
|
56
|
+
formatted_new_library_book_page GET /libraries/:library_id/books/:book_id/pages/new.:format {:controller=>"pages", :action=>"new"}
|
57
|
+
edit_library_book_page GET /libraries/:library_id/books/:book_id/pages/:id/edit {:controller=>"pages", :action=>"edit"}
|
58
|
+
formatted_edit_library_book_page GET /libraries/:library_id/books/:book_id/pages/:id/edit.:format {:controller=>"pages", :action=>"edit"}
|
59
|
+
library_book_page GET /libraries/:library_id/books/:book_id/pages/:id {:controller=>"pages", :action=>"show"}
|
60
|
+
formatted_library_book_page GET /libraries/:library_id/books/:book_id/pages/:id.:format {:controller=>"pages", :action=>"show"}
|
61
|
+
PUT /libraries/:library_id/books/:book_id/pages/:id {:controller=>"pages", :action=>"update"}
|
62
|
+
PUT /libraries/:library_id/books/:book_id/pages/:id.:format {:controller=>"pages", :action=>"update"}
|
63
|
+
DELETE /libraries/:library_id/books/:book_id/pages/:id {:controller=>"pages", :action=>"destroy"}
|
64
|
+
DELETE /libraries/:library_id/books/:book_id/pages/:id.:format {:controller=>"pages", :action=>"destroy"}
|
65
|
+
|
66
|
+
* Pages controller has new method book to get its parent resource
|
67
|
+
protected
|
68
|
+
def book
|
69
|
+
@book ||= Library.find(params[:library_id]).books.find(params[:book_id])
|
70
|
+
end
|
71
|
+
* All ActiveRecord calls for page resource will be through its book to be well scoped.
|
72
|
+
@pages = book.pages
|
73
|
+
* The most bunch of work in in views files
|
74
|
+
<td><%= link_to 'Show', library_book_page_path(page.book.library,page.book,page) %></td>
|
75
|
+
<td><%= link_to 'Edit', edit_library_book_page_path(page.book.library,page.book,page) %></td>
|
76
|
+
<td><%= link_to 'Destroy', library_book_page_path(page.book.library,page.book,page), :confirm => 'Are you sure?', :method => :delete %></td>
|
77
|
+
and two links at the end of the html page to new page and to return to books page
|
78
|
+
<%= link_to 'New page', new_library_book_page_path %>
|
79
|
+
<%= link_to 'Return to books', library_books_path %>
|
80
|
+
* In book.rb model file, there will be new line added to define association
|
81
|
+
has_many :pages
|
82
|
+
and in page.rb model file, also there will be new line added
|
83
|
+
belongs_to :book
|
84
|
+
|
85
|
+
== Conclusion
|
86
|
+
NestedRestfulScaffold is very easy to install, very easy to use and allows you to generate all that work for you.
|
87
|
+
It will be very suitable for you if you are building a RESTful API or application with nested resources.
|
88
|
+
It will reduce the overhead of updating controller, views, models and routes.
|
89
|
+
|
90
|
+
For more information on nested resources check:
|
91
|
+
http://adam.blog.heroku.com/past/2007/12/20/nested_resources_in_rails_2
|
92
|
+
http://www.akitaonrails.com/2007/12/12/rolling-with-rails-2-0-the-first-full-tutorial
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('nested_restful_scaffold', '0.1.0') do |p|
|
6
|
+
p.description = "NestedRestfulScaffold allows you to generate controller, views, model and routes of nested resources."
|
7
|
+
p.url = "http://nestedrestfulscaffold.rubyforge.org/"
|
8
|
+
p.author = "Mahmoud Khaled"
|
9
|
+
p.email = "mahmoud.khaled@badrit.com"
|
10
|
+
p.project = "nestedrestscaff"
|
11
|
+
end
|
12
|
+
|
13
|
+
#Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
14
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
Description:
|
2
|
+
Scaffolds an entire resource, from model and migration to controller and
|
3
|
+
views, along with a full test suite. The resource is ready to use for your
|
4
|
+
restful, resource-oriented application.
|
5
|
+
|
6
|
+
Usage:
|
7
|
+
Pass the name of the model, either CamelCased or under_scored, as the first
|
8
|
+
argument, an optional list of attribute pairs, and an optional list of
|
9
|
+
resources tree comma separated.
|
10
|
+
|
11
|
+
Attribute pairs are column_name:sql_type arguments specifying the
|
12
|
+
model's attributes. Timestamps are added by default, so you don't have to
|
13
|
+
specify them by hand as 'created_at:datetime updated_at:datetime'.
|
14
|
+
|
15
|
+
You don't have to think up every attribute up front, but it helps to
|
16
|
+
sketch out a few so you can start working with the resource immediately.
|
17
|
+
|
18
|
+
Resources list is an ordered list of nested parent resources. They are comma
|
19
|
+
separated ordered from root to child. 'library,book,page:resources'
|
20
|
+
|
21
|
+
For example, `nested_restful_scaffold page page_number:integer contents:text
|
22
|
+
book:references library,book:resources`
|
23
|
+
gives you a model with attributes page_number, contents and book_id, a controller that handles
|
24
|
+
the create/show/update/destroy, forms to create and edit your posts, and
|
25
|
+
an index that lists them all, as well as a book.resources :pages
|
26
|
+
declaration in config/routes.rb.
|
27
|
+
|
28
|
+
Examples:
|
29
|
+
`script/generate nested_restful_scaffold library name:string address:text`
|
30
|
+
`script/generate nested_restful_scaffold book name:string description:text library:references library:resources `
|
31
|
+
`script/generate nested_restful_scaffold page page_number:integer contents:text book:references library,book:resources`
|
@@ -0,0 +1,285 @@
|
|
1
|
+
class NestedRestfulScaffoldGenerator < Rails::Generator::NamedBase
|
2
|
+
default_options :skip_timestamps => false, :skip_migration => false
|
3
|
+
|
4
|
+
attr_reader :controller_name,
|
5
|
+
:controller_class_path,
|
6
|
+
:controller_file_path,
|
7
|
+
:controller_class_nesting,
|
8
|
+
:controller_class_nesting_depth,
|
9
|
+
:controller_class_name,
|
10
|
+
:controller_underscore_name,
|
11
|
+
:controller_singular_name,
|
12
|
+
:controller_plural_name
|
13
|
+
alias_method :controller_file_name, :controller_underscore_name
|
14
|
+
alias_method :controller_table_name, :controller_plural_name
|
15
|
+
|
16
|
+
def initialize(runtime_args, runtime_options = {})
|
17
|
+
super
|
18
|
+
|
19
|
+
@controller_name = @name.pluralize
|
20
|
+
|
21
|
+
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
|
22
|
+
@controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name)
|
23
|
+
@controller_singular_name=base_name.singularize
|
24
|
+
if @controller_class_nesting.empty?
|
25
|
+
@controller_class_name = @controller_class_name_without_nesting
|
26
|
+
else
|
27
|
+
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Remove resources from attributes
|
31
|
+
# and push them in new instance varialbe @resources
|
32
|
+
# to separate between resources attributes and other attributes
|
33
|
+
@resources = []
|
34
|
+
attributes.delete_if{|a| a.type.to_s == 'resources' and @resources = a.name.split(',').collect{|e| e.strip} }
|
35
|
+
@resources = nil if @resources.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
def manifest
|
39
|
+
record do |m|
|
40
|
+
unless command_has_resources
|
41
|
+
m.dependency "scaffold", [name] + @args
|
42
|
+
else
|
43
|
+
# Check for class naming collisions.
|
44
|
+
m.class_collisions(controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}Helper")
|
45
|
+
m.class_collisions(class_path, "#{class_name}")
|
46
|
+
m.class_collisions class_path, class_name, "#{class_name}Test"
|
47
|
+
|
48
|
+
# Controller, helper, views, test and stylesheets directories.
|
49
|
+
m.directory(File.join('app/models', class_path))
|
50
|
+
m.directory(File.join('app/controllers', controller_class_path))
|
51
|
+
m.directory(File.join('app/helpers', controller_class_path))
|
52
|
+
m.directory(File.join('app/views', controller_class_path, controller_file_name))
|
53
|
+
m.directory(File.join('app/views/layouts', controller_class_path))
|
54
|
+
m.directory(File.join('test/functional', controller_class_path))
|
55
|
+
m.directory(File.join('test/unit', class_path))
|
56
|
+
m.directory(File.join('public/stylesheets', class_path))
|
57
|
+
|
58
|
+
# Model, test, and fixture directories.
|
59
|
+
m.directory File.join('app/models', class_path)
|
60
|
+
m.directory File.join('test/unit', class_path)
|
61
|
+
m.directory File.join('test/fixtures', class_path)
|
62
|
+
|
63
|
+
|
64
|
+
for action in scaffold_views
|
65
|
+
m.template(
|
66
|
+
"view_#{action}.html.erb",
|
67
|
+
File.join('app/views', controller_class_path, controller_file_name, "#{action}.html.erb")
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Layout and stylesheet.
|
72
|
+
m.template('layout.html.erb', File.join('app/views/layouts', controller_class_path, "#{controller_file_name}.html.erb"))
|
73
|
+
m.template('style.css', 'public/stylesheets/scaffold.css')
|
74
|
+
|
75
|
+
m.template(
|
76
|
+
"controller.rb", File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
|
77
|
+
)
|
78
|
+
|
79
|
+
m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
|
80
|
+
m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb"))
|
81
|
+
|
82
|
+
# add routes
|
83
|
+
generate_routes m
|
84
|
+
|
85
|
+
# genearte model, unit_test, fixtures and migration
|
86
|
+
generate_model m
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def resources
|
92
|
+
@resources
|
93
|
+
end
|
94
|
+
|
95
|
+
def command_has_resources
|
96
|
+
!resources.nil?
|
97
|
+
end
|
98
|
+
|
99
|
+
# Return parent resource name
|
100
|
+
def parent_resource_name
|
101
|
+
command_has_resources ? resources.last : ''
|
102
|
+
end
|
103
|
+
|
104
|
+
# Generate find statement to be used in the controller
|
105
|
+
def generate_find_statement
|
106
|
+
first_resource = resources.first
|
107
|
+
statement = "#{first_resource.classify}.find(params[:#{first_resource}_id])"
|
108
|
+
|
109
|
+
statement = resources[1..-1].inject(statement){|s, resource| s += ".#{resource.pluralize}.find(params[:#{resource}_id])"}
|
110
|
+
end
|
111
|
+
|
112
|
+
# Generate singular resource
|
113
|
+
# prefix can be 'edit' to generate edit_resource_path
|
114
|
+
# add_params will be used to generate params with the medthod
|
115
|
+
# instance_object if true, it will add "@" to be instance variable
|
116
|
+
# plural if true, pluralize the last resource. It is useful for index actions
|
117
|
+
# reject_last if true, doesn't add last resource to the path. It is used in index view for "Return to parent resource" link
|
118
|
+
def nested_resource_path name, params = {}
|
119
|
+
params = {:prefix => "", :add_params => true, :instance_object => false,
|
120
|
+
:plural => false, :reject_last => false}.merge(params)
|
121
|
+
result = ""
|
122
|
+
|
123
|
+
result += resources[0..-2] * "_"
|
124
|
+
result += "_" unless resources[0..-2].empty?
|
125
|
+
|
126
|
+
# used only in index view for link "Return to"
|
127
|
+
result += params[:plural] && params[:reject_last] ? resources.last.pluralize : resources.last
|
128
|
+
result += '_'
|
129
|
+
|
130
|
+
if params[:reject_last]
|
131
|
+
result += "path"
|
132
|
+
elsif command_has_resources
|
133
|
+
result += params[:plural] ? name.pluralize : name
|
134
|
+
result += "_path"
|
135
|
+
else
|
136
|
+
result = name
|
137
|
+
end
|
138
|
+
|
139
|
+
if !params[:prefix].empty? && !command_has_resources
|
140
|
+
result += "_path"
|
141
|
+
end
|
142
|
+
|
143
|
+
force_params = params[:prefix]=="edit"
|
144
|
+
|
145
|
+
# add parameters to the method
|
146
|
+
param_name = name
|
147
|
+
param_name = "@" + param_name if params[:instance_object]
|
148
|
+
result += nested_resource_path_params(param_name, force_params, params[:plural]) if params[:add_params] || force_params
|
149
|
+
|
150
|
+
# add prefix "edit" or "new"
|
151
|
+
result = params[:prefix] + "_" + result unless params[:prefix].empty?
|
152
|
+
result
|
153
|
+
end
|
154
|
+
|
155
|
+
# Return method parameters
|
156
|
+
def nested_resource_path_params name, force_params, plural
|
157
|
+
result = ""
|
158
|
+
attr_params = resources.collect{|r| r}
|
159
|
+
|
160
|
+
if command_has_resources || force_params
|
161
|
+
attr_params << name
|
162
|
+
i = resources.size
|
163
|
+
while i > 0
|
164
|
+
attr_params[i-1] = attr_params[i] + "." + attr_params[i-1]
|
165
|
+
i -= 1
|
166
|
+
end
|
167
|
+
|
168
|
+
# skip last attribute if plural
|
169
|
+
attr_params = attr_params[0..-2] if plural
|
170
|
+
|
171
|
+
result = "(" + attr_params.join(',') + ")"
|
172
|
+
end
|
173
|
+
|
174
|
+
result
|
175
|
+
end
|
176
|
+
|
177
|
+
# Generate conditions for find statement to be used in view with input select
|
178
|
+
def generate_conditions owner, name
|
179
|
+
result = ""
|
180
|
+
parent = nil
|
181
|
+
has_resource = false
|
182
|
+
resources.each do |resource|
|
183
|
+
if resource == name
|
184
|
+
has_resource = true
|
185
|
+
break
|
186
|
+
end
|
187
|
+
|
188
|
+
parent = resource if resource != name
|
189
|
+
end
|
190
|
+
|
191
|
+
result = ", :conditions => [\"#{parent}_id = ?\", @#{owner}.#{name}.#{parent}.id]" if has_resource && parent
|
192
|
+
result
|
193
|
+
end
|
194
|
+
|
195
|
+
# Add routes to file routes.rb
|
196
|
+
def generate_routes m
|
197
|
+
# routes
|
198
|
+
unless command_has_resources
|
199
|
+
# add routes like unnested scaffold
|
200
|
+
# eg. map.resources books
|
201
|
+
m.route_resources controller_file_name
|
202
|
+
else
|
203
|
+
resource_list = controller_file_name.map { |r| r.to_sym.inspect }.join(', ')
|
204
|
+
parent_resource = parent_resource_name
|
205
|
+
|
206
|
+
path = destination_path('config/routes.rb')
|
207
|
+
content = File.read(path)
|
208
|
+
|
209
|
+
logger.route "resources #{resource_list}"
|
210
|
+
|
211
|
+
# map.resources :parents do |parent|
|
212
|
+
# parent.resources :parents do |parent|
|
213
|
+
sentinel = "\.resources(.*)?:#{parent_resource.pluralize}(.*)do(.*)\\|#{parent_resource}\\|"
|
214
|
+
|
215
|
+
if content =~ /#{sentinel}/
|
216
|
+
gsub_file 'config/routes.rb', sentinel do |match|
|
217
|
+
"#{match}\n #{parent_resource}.resources :#{table_name}"
|
218
|
+
end
|
219
|
+
else
|
220
|
+
# without do block
|
221
|
+
# map.resources :parents
|
222
|
+
# parent.resources :parents
|
223
|
+
sentinel = "\.resources(.*):#{parent_resource.pluralize}"
|
224
|
+
if content =~ /#{sentinel}/
|
225
|
+
gsub_file 'config/routes.rb', sentinel do |match|
|
226
|
+
"#{match} do |#{parent_resource}|\n #{parent_resource}.resources :#{table_name}\n end"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Genearte model, unit_test, fixtures, migration and add has_many relationshipt to parents
|
234
|
+
def generate_model m
|
235
|
+
# Model class, unit test, and fixtures.
|
236
|
+
m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
|
237
|
+
m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
|
238
|
+
|
239
|
+
unless options[:skip_fixture]
|
240
|
+
m.template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml")
|
241
|
+
end
|
242
|
+
|
243
|
+
unless options[:skip_migration]
|
244
|
+
m.migration_template 'migration.rb', 'db/migrate', :assigns => {
|
245
|
+
:migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
|
246
|
+
}, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
|
247
|
+
end
|
248
|
+
|
249
|
+
# add has_many to referenced
|
250
|
+
attributes.find_all{|a| a.type.to_s == "references"}.each do |parent|
|
251
|
+
gsub_file "app/models/#{parent.name}.rb", "class #{parent.name.camelize} < ActiveRecord::Base" do |match|
|
252
|
+
"#{match}\n has_many :#{table_name}"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
protected
|
258
|
+
# Override with your own usage banner.
|
259
|
+
def banner
|
260
|
+
"Usage: #{$0} nested_restful_scaffold ModelName [field:type, field:type, resource1,resource2,...:resources]"
|
261
|
+
end
|
262
|
+
|
263
|
+
def add_options!(opt)
|
264
|
+
opt.separator ''
|
265
|
+
opt.separator 'Options:'
|
266
|
+
opt.on("--skip-timestamps",
|
267
|
+
"Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
|
268
|
+
opt.on("--skip-migration",
|
269
|
+
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
|
270
|
+
end
|
271
|
+
|
272
|
+
def gsub_file(relative_destination, regexp, *args, &block)
|
273
|
+
path = destination_path(relative_destination)
|
274
|
+
content = File.read(path).gsub(/#{regexp}/, *args, &block)
|
275
|
+
File.open(path, 'wb') { |file| file.write(content) }
|
276
|
+
end
|
277
|
+
|
278
|
+
def scaffold_views
|
279
|
+
%w[ index show new edit ]
|
280
|
+
end
|
281
|
+
|
282
|
+
def model_name
|
283
|
+
class_name.demodulize
|
284
|
+
end
|
285
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
2
|
+
# GET /<%= table_name %>
|
3
|
+
# GET /<%= table_name %>.xml
|
4
|
+
<% resource_name = parent_resource_name %>
|
5
|
+
def index
|
6
|
+
@<%= table_name %> = <%= resource_name %>.<%= table_name %>
|
7
|
+
|
8
|
+
respond_to do |format|
|
9
|
+
format.html # index.html.erb
|
10
|
+
format.xml { render :xml => @<%= table_name %> }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# GET /<%= table_name %>/1
|
15
|
+
# GET /<%= table_name %>/1.xml
|
16
|
+
def show
|
17
|
+
@<%= file_name %> = <%= resource_name %>.<%= table_name %>.find(params[:id])
|
18
|
+
|
19
|
+
respond_to do |format|
|
20
|
+
format.html # show.html.erb
|
21
|
+
format.xml { render :xml => @<%= file_name %> }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# GET /<%= table_name %>/new
|
26
|
+
# GET /<%= table_name %>/new.xml
|
27
|
+
def new
|
28
|
+
@<%= file_name %> = <%= class_name %>.new
|
29
|
+
@<%= file_name %>.<%= resource_name %> = <%= resource_name %>
|
30
|
+
|
31
|
+
respond_to do |format|
|
32
|
+
format.html # new.html.erb
|
33
|
+
format.xml { render :xml => @<%= file_name %> }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# GET /<%= table_name %>/1/edit
|
38
|
+
def edit
|
39
|
+
@<%= file_name %> = <%= resource_name %>.<%= table_name %>.find(params[:id])
|
40
|
+
end
|
41
|
+
|
42
|
+
# POST /<%= table_name %>
|
43
|
+
# POST /<%= table_name %>.xml
|
44
|
+
def create
|
45
|
+
@<%= file_name %> = <%= class_name %>.new(params[:<%= file_name %>]) if params[:<%= file_name %>][:<%= resource_name %>_id].nil? || params[:<%= file_name %>][:<%= resource_name %>_id] == <%= resource_name %>.id.to_s
|
46
|
+
@<%= file_name %>.<%= resource_name %>_id = <%= resource_name %>.id
|
47
|
+
|
48
|
+
respond_to do |format|
|
49
|
+
if @<%= file_name %>.save
|
50
|
+
flash[:notice] = '<%= class_name %> was successfully created.'
|
51
|
+
format.html { redirect_to(<%= nested_resource_path singular_name, :instance_object => true %>) }
|
52
|
+
format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> }
|
53
|
+
else
|
54
|
+
format.html { render :action => "new" }
|
55
|
+
format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# PUT /<%= table_name %>/1
|
61
|
+
# PUT /<%= table_name %>/1.xml
|
62
|
+
def update
|
63
|
+
@<%= file_name %> = <%= resource_name %>.<%= table_name %>.find(params[:id])
|
64
|
+
|
65
|
+
respond_to do |format|
|
66
|
+
if @<%= file_name %>.update_attributes(params[:<%= file_name %>])
|
67
|
+
flash[:notice] = '<%= class_name %> was successfully updated.'
|
68
|
+
format.html { redirect_to(<%= nested_resource_path singular_name, :instance_object => true %>) }
|
69
|
+
format.xml { head :ok }
|
70
|
+
else
|
71
|
+
format.html { render :action => "edit" }
|
72
|
+
format.xml { render :xml => @<%= file_name %>.errors, :status => :unprocessable_entity }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# DELETE /<%= table_name %>/1
|
78
|
+
# DELETE /<%= table_name %>/1.xml
|
79
|
+
def destroy
|
80
|
+
@<%= file_name %> = <%= resource_name %>.<%= table_name %>.find(params[:id])
|
81
|
+
url_path = <%= nested_resource_path singular_name, :instance_object => true, :plural => true %>
|
82
|
+
@<%= file_name %>.destroy
|
83
|
+
|
84
|
+
respond_to do |format|
|
85
|
+
format.html { redirect_to(url_path) }
|
86
|
+
format.xml { head :ok }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
protected
|
91
|
+
def <%= resource_name %>
|
92
|
+
@<%= resource_name %> ||= <%= generate_find_statement %>
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
|
2
|
+
|
3
|
+
<% unless attributes.empty? -%>
|
4
|
+
one:
|
5
|
+
<% for attribute in attributes -%>
|
6
|
+
<%= attribute.name %>: <%= attribute.default %>
|
7
|
+
<% end -%>
|
8
|
+
|
9
|
+
two:
|
10
|
+
<% for attribute in attributes -%>
|
11
|
+
<%= attribute.name %>: <%= attribute.default %>
|
12
|
+
<% end -%>
|
13
|
+
<% else -%>
|
14
|
+
# one:
|
15
|
+
# column: value
|
16
|
+
#
|
17
|
+
# two:
|
18
|
+
# column: value
|
19
|
+
<% end -%>
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class <%= controller_class_name %>ControllerTest < ActionController::TestCase
|
4
|
+
def test_should_get_index
|
5
|
+
get :index
|
6
|
+
assert_response :success
|
7
|
+
assert_not_nil assigns(:<%= table_name %>)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_should_get_new
|
11
|
+
get :new
|
12
|
+
assert_response :success
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_should_create_<%= file_name %>
|
16
|
+
assert_difference('<%= class_name %>.count') do
|
17
|
+
post :create, :<%= file_name %> => { }
|
18
|
+
end
|
19
|
+
|
20
|
+
assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_should_show_<%= file_name %>
|
24
|
+
get :show, :id => <%= table_name %>(:one).id
|
25
|
+
assert_response :success
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_should_get_edit
|
29
|
+
get :edit, :id => <%= table_name %>(:one).id
|
30
|
+
assert_response :success
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_should_update_<%= file_name %>
|
34
|
+
put :update, :id => <%= table_name %>(:one).id, :<%= file_name %> => { }
|
35
|
+
assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_should_destroy_<%= file_name %>
|
39
|
+
assert_difference('<%= class_name %>.count', -1) do
|
40
|
+
delete :destroy, :id => <%= table_name %>(:one).id
|
41
|
+
end
|
42
|
+
|
43
|
+
assert_redirected_to <%= table_name %>_path
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,17 @@
|
|
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><%= controller_class_name %>: <%%= controller.action_name %></title>
|
8
|
+
<%%= stylesheet_link_tag 'scaffold' %>
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
|
12
|
+
<p style="color: green"><%%= flash[:notice] %></p>
|
13
|
+
|
14
|
+
<%%= yield %>
|
15
|
+
|
16
|
+
</body>
|
17
|
+
</html>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class <%= migration_name %> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :<%= table_name %> do |t|
|
4
|
+
<% for attribute in attributes -%>
|
5
|
+
t.<%= attribute.type %> :<%= attribute.name %>
|
6
|
+
<% end -%>
|
7
|
+
<% unless options[:skip_timestamps] %>
|
8
|
+
t.timestamps
|
9
|
+
<% end -%>
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
drop_table :<%= table_name %>
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
body { background-color: #fff; color: #333; }
|
2
|
+
|
3
|
+
body, p, ol, ul, td {
|
4
|
+
font-family: verdana, arial, helvetica, sans-serif;
|
5
|
+
font-size: 13px;
|
6
|
+
line-height: 18px;
|
7
|
+
}
|
8
|
+
|
9
|
+
pre {
|
10
|
+
background-color: #eee;
|
11
|
+
padding: 10px;
|
12
|
+
font-size: 11px;
|
13
|
+
}
|
14
|
+
|
15
|
+
a { color: #000; }
|
16
|
+
a:visited { color: #666; }
|
17
|
+
a:hover { color: #fff; background-color:#000; }
|
18
|
+
|
19
|
+
.fieldWithErrors {
|
20
|
+
padding: 2px;
|
21
|
+
background-color: red;
|
22
|
+
display: table;
|
23
|
+
}
|
24
|
+
|
25
|
+
#errorExplanation {
|
26
|
+
width: 400px;
|
27
|
+
border: 2px solid red;
|
28
|
+
padding: 7px;
|
29
|
+
padding-bottom: 12px;
|
30
|
+
margin-bottom: 20px;
|
31
|
+
background-color: #f0f0f0;
|
32
|
+
}
|
33
|
+
|
34
|
+
#errorExplanation h2 {
|
35
|
+
text-align: left;
|
36
|
+
font-weight: bold;
|
37
|
+
padding: 5px 5px 5px 15px;
|
38
|
+
font-size: 12px;
|
39
|
+
margin: -7px;
|
40
|
+
background-color: #c00;
|
41
|
+
color: #fff;
|
42
|
+
}
|
43
|
+
|
44
|
+
#errorExplanation p {
|
45
|
+
color: #333;
|
46
|
+
margin-bottom: 0;
|
47
|
+
padding: 5px;
|
48
|
+
}
|
49
|
+
|
50
|
+
#errorExplanation ul li {
|
51
|
+
font-size: 12px;
|
52
|
+
list-style: square;
|
53
|
+
}
|
54
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<h1>Editing <%= singular_name %></h1>
|
2
|
+
|
3
|
+
<%%= error_messages_for :<%= singular_name %> %>
|
4
|
+
|
5
|
+
<%% form_for(@<%= singular_name %><%= ", :url => " + nested_resource_path(singular_name, :instance_object => true) if command_has_resources %>) do |f| %>
|
6
|
+
<% for attribute in attributes -%>
|
7
|
+
<p>
|
8
|
+
<b><%= attribute.column.human_name %></b><br />
|
9
|
+
<% if attribute.type.to_s != "references" %>
|
10
|
+
<%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
|
11
|
+
<% end %>
|
12
|
+
</p>
|
13
|
+
<% end -%>
|
14
|
+
<p>
|
15
|
+
<%%= f.submit "Update" %>
|
16
|
+
</p>
|
17
|
+
<%% end %>
|
18
|
+
|
19
|
+
<%%= link_to 'Show', <%= nested_resource_path singular_name, :instance_object => true %> %> |
|
20
|
+
<%%= link_to 'Back', <%= nested_resource_path singular_name, :instance_object => true, :plural => true %> %>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<h1>Listing <%= plural_name %></h1>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tr>
|
5
|
+
<% for attribute in attributes -%>
|
6
|
+
<th><%= attribute.column.human_name %></th>
|
7
|
+
<% end -%>
|
8
|
+
</tr>
|
9
|
+
|
10
|
+
<%% for <%= singular_name %> in @<%= plural_name %> %>
|
11
|
+
<tr>
|
12
|
+
<% for attribute in attributes -%>
|
13
|
+
<td><%%=h <%= singular_name %>.<%= attribute.name %> %></td>
|
14
|
+
<% end -%>
|
15
|
+
<td><%%= link_to 'Show', <%= nested_resource_path singular_name %> %></td>
|
16
|
+
<td><%%= link_to 'Edit', <%= nested_resource_path singular_name, :prefix => "edit" %> %></td>
|
17
|
+
<td><%%= link_to 'Destroy', <%= nested_resource_path singular_name %>, :confirm => 'Are you sure?', :method => :delete %></td>
|
18
|
+
</tr>
|
19
|
+
<%% end %>
|
20
|
+
</table>
|
21
|
+
|
22
|
+
<br />
|
23
|
+
|
24
|
+
<%%= link_to 'New <%= singular_name %>', <%= nested_resource_path singular_name, :prefix => "new", :add_params => false %> %>
|
25
|
+
<br />
|
26
|
+
<%%= link_to 'Return to <%= parent_resource_name.pluralize %>', <%= nested_resource_path singular_name, :add_params => false, :plural => true, :reject_last => true %> %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<h1>New <%= singular_name %></h1>
|
2
|
+
|
3
|
+
<%%= error_messages_for :<%= singular_name %> %>
|
4
|
+
|
5
|
+
<%% form_for(@<%= singular_name %><%= ", :url => " + nested_resource_path(singular_name, :instance_object => true, :plural => true) if command_has_resources %>) do |f| %>
|
6
|
+
<% for attribute in attributes -%>
|
7
|
+
<p>
|
8
|
+
<b><%= attribute.column.human_name %></b><br />
|
9
|
+
<% if attribute.type.to_s != "references" %>
|
10
|
+
<%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
|
11
|
+
<% end %>
|
12
|
+
</p>
|
13
|
+
<% end -%>
|
14
|
+
<p>
|
15
|
+
<%%= f.submit "Create" %>
|
16
|
+
</p>
|
17
|
+
<%% end %>
|
18
|
+
|
19
|
+
<%%= link_to 'Back', <%= nested_resource_path singular_name, :instance_object => true, :plural => true %> %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<% for attribute in attributes -%>
|
2
|
+
<p>
|
3
|
+
<b><%= attribute.column.human_name %>:</b>
|
4
|
+
<%%=h @<%= singular_name %>.<%= attribute.name %> %>
|
5
|
+
</p>
|
6
|
+
<% end -%>
|
7
|
+
|
8
|
+
<%%= link_to 'Edit', <%= nested_resource_path(singular_name, :prefix => "edit", :instance_object => true) %> %> |
|
9
|
+
<%%= link_to 'Back', <%= nested_resource_path singular_name, :instance_object => true, :plural => true %> %>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{nested_restful_scaffold}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Mahmoud Khaled"]
|
9
|
+
s.date = %q{2009-09-17}
|
10
|
+
s.description = %q{NestedRestfulScaffold allows you to generate controller, views, model and routes of nested resources.}
|
11
|
+
s.email = %q{mahmoud.khaled@badrit.com}
|
12
|
+
s.extra_rdoc_files = ["README"]
|
13
|
+
s.files = ["README", "Rakefile", "generators/nested_restful_scaffold/USAGE", "generators/nested_restful_scaffold/nested_restful_scaffold_generator.rb", "generators/nested_restful_scaffold/templates/controller.rb", "generators/nested_restful_scaffold/templates/fixtures.yml", "generators/nested_restful_scaffold/templates/functional_test.rb", "generators/nested_restful_scaffold/templates/helper.rb", "generators/nested_restful_scaffold/templates/layout.html.erb", "generators/nested_restful_scaffold/templates/migration.rb", "generators/nested_restful_scaffold/templates/model.rb", "generators/nested_restful_scaffold/templates/style.css", "generators/nested_restful_scaffold/templates/unit_test.rb", "generators/nested_restful_scaffold/templates/view_edit.html.erb", "generators/nested_restful_scaffold/templates/view_index.html.erb", "generators/nested_restful_scaffold/templates/view_new.html.erb", "generators/nested_restful_scaffold/templates/view_show.html.erb", "nested_restful_scaffold.gemspec", "Manifest"]
|
14
|
+
s.homepage = %q{http://nestedrestfulscaffold.rubyforge.org/}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Nested_restful_scaffold", "--main", "README"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{nestedrestscaff}
|
18
|
+
s.rubygems_version = %q{1.3.5}
|
19
|
+
s.summary = %q{NestedRestfulScaffold allows you to generate controller, views, model and routes of nested resources.}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nested_restful_scaffold
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mahmoud Khaled
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-17 00:00:00 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: NestedRestfulScaffold allows you to generate controller, views, model and routes of nested resources.
|
17
|
+
email: mahmoud.khaled@badrit.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- Rakefile
|
27
|
+
- generators/nested_restful_scaffold/USAGE
|
28
|
+
- generators/nested_restful_scaffold/nested_restful_scaffold_generator.rb
|
29
|
+
- generators/nested_restful_scaffold/templates/controller.rb
|
30
|
+
- generators/nested_restful_scaffold/templates/fixtures.yml
|
31
|
+
- generators/nested_restful_scaffold/templates/functional_test.rb
|
32
|
+
- generators/nested_restful_scaffold/templates/helper.rb
|
33
|
+
- generators/nested_restful_scaffold/templates/layout.html.erb
|
34
|
+
- generators/nested_restful_scaffold/templates/migration.rb
|
35
|
+
- generators/nested_restful_scaffold/templates/model.rb
|
36
|
+
- generators/nested_restful_scaffold/templates/style.css
|
37
|
+
- generators/nested_restful_scaffold/templates/unit_test.rb
|
38
|
+
- generators/nested_restful_scaffold/templates/view_edit.html.erb
|
39
|
+
- generators/nested_restful_scaffold/templates/view_index.html.erb
|
40
|
+
- generators/nested_restful_scaffold/templates/view_new.html.erb
|
41
|
+
- generators/nested_restful_scaffold/templates/view_show.html.erb
|
42
|
+
- nested_restful_scaffold.gemspec
|
43
|
+
- Manifest
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: http://nestedrestfulscaffold.rubyforge.org/
|
46
|
+
licenses: []
|
47
|
+
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options:
|
50
|
+
- --line-numbers
|
51
|
+
- --inline-source
|
52
|
+
- --title
|
53
|
+
- Nested_restful_scaffold
|
54
|
+
- --main
|
55
|
+
- README
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "1.2"
|
69
|
+
version:
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project: nestedrestscaff
|
73
|
+
rubygems_version: 1.3.5
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: NestedRestfulScaffold allows you to generate controller, views, model and routes of nested resources.
|
77
|
+
test_files: []
|
78
|
+
|