trestle_generator 1.0.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/USAGE +35 -0
- data/templates/controller.rb +56 -0
- data/templates/form.rhtml +3 -0
- data/templates/form_trestle.rhtml +1 -0
- data/templates/functional_test.rb +104 -0
- data/templates/helper.rb +2 -0
- data/templates/layout.rhtml +13 -0
- data/templates/routes.rb +21 -0
- data/templates/style.css +74 -0
- data/templates/view__index_with_id.rhtml +8 -0
- data/templates/view__index_without_id.rhtml +27 -0
- data/templates/view_edit.rhtml +10 -0
- data/templates/view_new.rhtml +8 -0
- data/trestle_generator.rb +188 -0
- metadata +99 -0
data/USAGE
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
Description:
|
2
|
+
The trestle generator (an adaptation of the Rails scaffold generator)
|
3
|
+
creates a controller to interact with a model. If the model does not exist,
|
4
|
+
it creates the model as well. The generated code is equivalent to the
|
5
|
+
"scaffold :model" declaration, making it easy to migrate when you wish to
|
6
|
+
customize your controller and views.
|
7
|
+
|
8
|
+
The generator takes a model name, an optional controller name, and a
|
9
|
+
list of views as arguments. Trestled actions and views are created
|
10
|
+
automatically. Any views left over generate empty stubs.
|
11
|
+
|
12
|
+
The trestled actions are:
|
13
|
+
index, new, edit, destroy
|
14
|
+
|
15
|
+
The trestled views are:
|
16
|
+
_index_without_id, _index_with_id, new, edit
|
17
|
+
|
18
|
+
If a controller name is not given, the plural form of the model name
|
19
|
+
will be used. The model and controller names may be given in CamelCase
|
20
|
+
or under_score and should not be suffixed with 'Model' or 'Controller'.
|
21
|
+
Both model and controller names may be prefixed with a module like a
|
22
|
+
file path; see the Modules Example for usage.
|
23
|
+
|
24
|
+
Example:
|
25
|
+
./script/generate trestle Account Bank debit credit
|
26
|
+
|
27
|
+
This will generate an Account model and BankController with a full test
|
28
|
+
suite and a basic user interface. Now create the accounts table in your
|
29
|
+
database and browse to http://localhost/bank/ -- voila, you're on Rails!
|
30
|
+
|
31
|
+
Modules Example:
|
32
|
+
./script/generate trestle CreditCard 'admin/credit_card' suspend late_fee
|
33
|
+
|
34
|
+
This will generate a CreditCard model and CreditCardController controller
|
35
|
+
in the admin module.
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
2
|
+
verify :only => [ :edit<%= suffix %>, :destroy<%= suffix %> ],
|
3
|
+
:params => :id,
|
4
|
+
:add_flash => { :notice => 'Missing <%= singular_name %> ID.' },
|
5
|
+
:redirect_to => { :action => '<%= suffix || 'index' %>' }
|
6
|
+
|
7
|
+
<% for action in untrestled_actions -%>
|
8
|
+
def <%= action %><%= suffix %>
|
9
|
+
end
|
10
|
+
|
11
|
+
<% end -%>
|
12
|
+
def destroy<%= suffix %>
|
13
|
+
if request.post?
|
14
|
+
<%= model_name %>.find(params[:id]).destroy
|
15
|
+
flash[:notice] = 'The <%= singular_name %> was successfully destroyed.'
|
16
|
+
redirect_to :action => '<%= suffix || 'index' %>'
|
17
|
+
else
|
18
|
+
flash[:notice] = 'Click Destroy to destroy the <%= model_name %>.'
|
19
|
+
redirect_to :action => 'edit<%= suffix %>', :id => params[:id]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def edit<%= suffix %>
|
24
|
+
if request.post?
|
25
|
+
@<%= singular_name %> = <%= model_name %>.find(params[:id])
|
26
|
+
if @<%= singular_name %>.update_attributes(params[:<%= singular_name %>])
|
27
|
+
flash[:notice] = 'The <%= singular_name %> was successfully edited.'
|
28
|
+
redirect_to :action => '<%= suffix || 'index' %>', :id => @<%= singular_name %>
|
29
|
+
end
|
30
|
+
else
|
31
|
+
@<%= singular_name %> = <%= model_name %>.find(params[:id])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def <%= suffix || 'index' %>
|
36
|
+
if params[:id]
|
37
|
+
@<%= singular_name %> = <%= model_name %>.find(params[:id])
|
38
|
+
render '<%= plural_name %>/_<%= suffix || 'index' %>_with_id'
|
39
|
+
else
|
40
|
+
@<%= singular_name %>_pages, @<%= plural_name %> = paginate(:<%= plural_name %>)
|
41
|
+
render '<%= plural_name %>/_<%= suffix || 'index' %>_without_id'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def new<%= suffix %>
|
46
|
+
if request.post?
|
47
|
+
@<%= singular_name %> = <%= model_name %>.new(params[:<%= singular_name %>])
|
48
|
+
if @<%= singular_name %>.save
|
49
|
+
flash[:notice] = 'A new <%= singular_name %> was successfully added.'
|
50
|
+
redirect_to :action => '<%= suffix || 'index' %>'
|
51
|
+
end
|
52
|
+
else
|
53
|
+
@<%= singular_name %> = <%= model_name %>.new
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= all_input_tags(@model_instance, @singular_name, {}) %>
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require File.dirname(__FILE__) + '<%= "/.." * controller_class_nesting_depth %>/../test_helper'
|
2
|
+
require '<%= controller_file_path %>_controller'
|
3
|
+
|
4
|
+
# Re-raise errors caught by the controller.
|
5
|
+
class <%= controller_class_name %>Controller; def rescue_action(e) raise e end; end
|
6
|
+
|
7
|
+
class <%= controller_class_name %>ControllerTest < Test::Unit::TestCase
|
8
|
+
fixtures :<%= table_name %>
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@controller = <%= controller_class_name %>Controller.new
|
12
|
+
@request = ActionController::TestRequest.new
|
13
|
+
@response = ActionController::TestResponse.new
|
14
|
+
end
|
15
|
+
|
16
|
+
<% for action in untrestled_actions -%>
|
17
|
+
def test_<%= action %>
|
18
|
+
get :<%= action %>
|
19
|
+
assert_response :success
|
20
|
+
assert_template '<%= action %>'
|
21
|
+
end
|
22
|
+
|
23
|
+
<% end -%>
|
24
|
+
def test_<%= suffix || 'index' %>
|
25
|
+
get :<%= suffix || 'index' %>
|
26
|
+
assert_response :success
|
27
|
+
assert_template '<%= plural_name %>/_<%= suffix || 'index' %>_without_id'
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_destroy<%= suffix %>_using_get
|
31
|
+
assert_not_nil <%= model_name %>.find(1)
|
32
|
+
|
33
|
+
get :destroy<%= suffix %>, :id => 1
|
34
|
+
assert_response :redirect
|
35
|
+
assert_redirected_to :action => 'edit<%= suffix %>'
|
36
|
+
|
37
|
+
assert_not_nil <%= model_name %>.find(1)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_destroy<%= suffix %>_using_post
|
41
|
+
assert_not_nil <%= model_name %>.find(1)
|
42
|
+
|
43
|
+
post :destroy<%= suffix %>, :id => 1
|
44
|
+
assert_response :redirect
|
45
|
+
assert_redirected_to :action => '<%= suffix || 'index' %>'
|
46
|
+
|
47
|
+
assert_raise(ActiveRecord::RecordNotFound) { <%= model_name %>.find(1) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_edit<%= suffix %>_using_get
|
51
|
+
get :edit<%= suffix %>, :id => 1
|
52
|
+
|
53
|
+
assert_response :success
|
54
|
+
assert_template 'edit<%= suffix %>'
|
55
|
+
|
56
|
+
assert_not_nil assigns(:<%= singular_name %>)
|
57
|
+
assert assigns(:<%= singular_name %>).valid?
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_edit<%= suffix %>_using_post
|
61
|
+
post :edit<%= suffix %>, :id => 1
|
62
|
+
assert_response :redirect
|
63
|
+
assert_redirected_to :action => '<%= suffix || 'index' %>', :id => 1
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_<%= suffix || 'index' %>_without_id
|
67
|
+
get :<%= suffix || 'index' %>
|
68
|
+
|
69
|
+
assert_response :success
|
70
|
+
assert_template '<%= plural_name %>/_<%= suffix || 'index' %>_without_id'
|
71
|
+
|
72
|
+
assert_not_nil assigns(:<%= plural_name %>)
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_<%= suffix || 'index' %>_with_id
|
76
|
+
get :<%= suffix || 'index' %>, :id => 1
|
77
|
+
|
78
|
+
assert_response :success
|
79
|
+
assert_template '<%= plural_name %>/_<%= suffix || 'index' %>_with_id'
|
80
|
+
|
81
|
+
assert_not_nil assigns(:<%= singular_name %>)
|
82
|
+
assert assigns(:<%= singular_name %>).valid?
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_new<%= suffix %>_using_get
|
86
|
+
get :new<%= suffix %>
|
87
|
+
|
88
|
+
assert_response :success
|
89
|
+
assert_template 'new<%= suffix %>'
|
90
|
+
|
91
|
+
assert_not_nil assigns(:<%= singular_name %>)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_new<%= suffix %>_using_post
|
95
|
+
num_<%= plural_name %> = <%= model_name %>.count
|
96
|
+
|
97
|
+
post :new<%= suffix %>, :<%= singular_name %> => {}
|
98
|
+
|
99
|
+
assert_response :redirect
|
100
|
+
assert_redirected_to :action => '<%= suffix || 'index' %>'
|
101
|
+
|
102
|
+
assert_equal num_<%= plural_name %> + 1, <%= model_name %>.count
|
103
|
+
end
|
104
|
+
end
|
data/templates/helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title><%= controller_class_name %>: <%%= controller.action_name %></title>
|
4
|
+
<%%= stylesheet_link_tag 'trestle' %>
|
5
|
+
</head>
|
6
|
+
<body>
|
7
|
+
|
8
|
+
<p style="color: green"><%%= flash[:notice] %></p>
|
9
|
+
|
10
|
+
<%%= @content_for_layout %>
|
11
|
+
|
12
|
+
</body>
|
13
|
+
</html>
|
data/templates/routes.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
ActionController::Routing::Routes.draw do |map|
|
2
|
+
# Add your own custom routes here.
|
3
|
+
# The priority is based upon order of creation: first created -> highest priority.
|
4
|
+
|
5
|
+
# Here's a sample route:
|
6
|
+
# map.connect 'products/:id', :controller => 'catalog', :action => 'view'
|
7
|
+
# Keep in mind you can assign values other than :controller and :action
|
8
|
+
|
9
|
+
# You can have the root of your site routed by hooking up ''
|
10
|
+
# -- just remember to delete public/index.html.
|
11
|
+
# map.connect '', :controller => "welcome"
|
12
|
+
|
13
|
+
# Allow downloading Web Service WSDL as a file with an extension
|
14
|
+
# instead of a file named 'wsdl'
|
15
|
+
map.connect ':controller/service.wsdl', :action => 'wsdl'
|
16
|
+
|
17
|
+
# Install the default route as the lowest priority.
|
18
|
+
map.connect ':controller/:action', :action => 'index', :requirements => { :action => /[^\d]+/ }
|
19
|
+
map.connect ':controller/:id', :action => 'index', :requirements => { :id => /[\d]+/ }
|
20
|
+
map.connect ':controller/:id/:action'
|
21
|
+
end
|
data/templates/style.css
ADDED
@@ -0,0 +1,74 @@
|
|
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
|
+
|
55
|
+
div.uploadStatus {
|
56
|
+
margin: 5px;
|
57
|
+
}
|
58
|
+
|
59
|
+
div.progressBar {
|
60
|
+
margin: 5px;
|
61
|
+
}
|
62
|
+
|
63
|
+
div.progressBar div.border {
|
64
|
+
background-color: #fff;
|
65
|
+
border: 1px solid grey;
|
66
|
+
width: 100%;
|
67
|
+
}
|
68
|
+
|
69
|
+
div.progressBar div.background {
|
70
|
+
background-color: #333;
|
71
|
+
height: 18px;
|
72
|
+
width: 0%;
|
73
|
+
}
|
74
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%% for column in <%= model_name %>.content_columns %>
|
2
|
+
<p>
|
3
|
+
<b><%%= column.human_name %>:</b> <%%= h @<%= singular_name %>.send(column.name) %>
|
4
|
+
</p>
|
5
|
+
<%% end %>
|
6
|
+
|
7
|
+
<%%= link_to 'Edit', :action => 'edit<%= suffix %>', :id => @<%= singular_name %> %> |
|
8
|
+
<%%= link_to 'Back to list', :action => '<%= suffix || 'index' %>' %>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<h1>Listing <%= plural_name %></h1>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tr>
|
5
|
+
<%% for column in <%= model_name %>.content_columns %>
|
6
|
+
<th><%%= column.human_name %></th>
|
7
|
+
<%% end %>
|
8
|
+
</tr>
|
9
|
+
|
10
|
+
<%% for <%= singular_name %> in @<%= plural_name %> %>
|
11
|
+
<tr>
|
12
|
+
<%% for column in <%= model_name %>.content_columns %>
|
13
|
+
<td><%%=h <%= singular_name %>.send(column.name) %></td>
|
14
|
+
<%% end %>
|
15
|
+
<td><%%= link_to 'Show', :action => '<%= suffix || 'index' %>', :id => <%= singular_name %> %></td>
|
16
|
+
<td><%%= link_to 'Edit', :action => 'edit<%= suffix %>', :id => <%= singular_name %> %></td>
|
17
|
+
<td><%%= link_to 'Destroy', { :action => 'destroy<%= suffix %>', :id => <%= singular_name %> }, :post => true, :confirm => 'Are you sure you want to destroy this <%= singular_name %>?' %>
|
18
|
+
</tr>
|
19
|
+
<%% end %>
|
20
|
+
</table>
|
21
|
+
|
22
|
+
<%%= link_to 'Previous page', { :page => @<%= singular_name %>_pages.current.previous } if @<%= singular_name %>_pages.current.previous %>
|
23
|
+
<%%= link_to 'Next page', { :page => @<%= singular_name %>_pages.current.next } if @<%= singular_name %>_pages.current.next %>
|
24
|
+
|
25
|
+
<br />
|
26
|
+
|
27
|
+
<%%= link_to 'New <%= singular_name %>', :action => 'new<%= suffix %>' %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<h1>Editing <%= singular_name %></h1>
|
2
|
+
|
3
|
+
<%%= start_form_tag :action => 'edit<%= @suffix %>', :id => @<%= singular_name %> %>
|
4
|
+
<%%= render :partial => 'form' %>
|
5
|
+
<%%= submit_tag 'Save' %>
|
6
|
+
<%%= end_form_tag %>
|
7
|
+
<%%= button_to 'Destroy', { :action => 'destroy<%= suffix %>', :id => @<%= singular_name %> }, :confirm => 'Are you sure you want to destroy this <%= singular_name %>?' %>
|
8
|
+
|
9
|
+
<%%= link_to 'Show', :action => '<%= suffix || 'index' %>', :id => @<%= singular_name %> %> |
|
10
|
+
<%%= link_to 'Back to list', :action => '<%= suffix || 'index' %>' %>
|
@@ -0,0 +1,188 @@
|
|
1
|
+
class TrestleSandbox
|
2
|
+
include ActionView::Helpers::ActiveRecordHelper
|
3
|
+
|
4
|
+
attr_accessor :form_action, :singular_name, :suffix, :model_instance
|
5
|
+
|
6
|
+
def sandbox_binding
|
7
|
+
binding
|
8
|
+
end
|
9
|
+
|
10
|
+
def default_input_block
|
11
|
+
Proc.new { |record, column| "<p><label for=\"#{record}_#{column.name}\">#{column.human_name}</label><br/>\n#{input(record, column.name)}</p>\n" }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class ActionView::Helpers::InstanceTag
|
16
|
+
def to_input_field_tag(field_type, options={})
|
17
|
+
field_meth = "#{field_type}_field"
|
18
|
+
"<%= #{field_meth} '#{@object_name}', '#{@method_name}' #{options.empty? ? '' : ', '+options.inspect} %>"
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_text_area_tag(options = {})
|
22
|
+
"<%= text_area '#{@object_name}', '#{@method_name}' #{options.empty? ? '' : ', '+ options.inspect} %>"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_date_select_tag(options = {})
|
26
|
+
"<%= date_select '#{@object_name}', '#{@method_name}' #{options.empty? ? '' : ', '+ options.inspect} %>"
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_datetime_select_tag(options = {})
|
30
|
+
"<%= datetime_select '#{@object_name}', '#{@method_name}' #{options.empty? ? '' : ', '+ options.inspect} %>"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class TrestleGenerator < Rails::Generator::NamedBase
|
35
|
+
attr_reader :controller_name,
|
36
|
+
:controller_class_path,
|
37
|
+
:controller_file_path,
|
38
|
+
:controller_class_nesting,
|
39
|
+
:controller_class_nesting_depth,
|
40
|
+
:controller_class_name,
|
41
|
+
:controller_singular_name,
|
42
|
+
:controller_plural_name
|
43
|
+
alias_method :controller_file_name, :controller_singular_name
|
44
|
+
alias_method :controller_table_name, :controller_plural_name
|
45
|
+
|
46
|
+
def initialize(runtime_args, runtime_options = {})
|
47
|
+
super
|
48
|
+
|
49
|
+
# Take controller name from the next argument. Default to the pluralized model name.
|
50
|
+
@controller_name = args.shift
|
51
|
+
@controller_name ||= ActiveRecord::Base.pluralize_table_names ? @name.pluralize : @name
|
52
|
+
|
53
|
+
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
|
54
|
+
@controller_class_name_without_nesting, @controller_singular_name, @controller_plural_name = inflect_names(base_name)
|
55
|
+
|
56
|
+
if @controller_class_nesting.empty?
|
57
|
+
@controller_class_name = @controller_class_name_without_nesting
|
58
|
+
else
|
59
|
+
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def manifest
|
64
|
+
record do |m|
|
65
|
+
# Check for class naming collisions.
|
66
|
+
m.class_collisions controller_class_path, "#{controller_class_name}Controller", "#{controller_class_name}ControllerTest", "#{controller_class_name}Helper"
|
67
|
+
|
68
|
+
# Controller, helper, views, test, and config directories.
|
69
|
+
m.directory File.join('app/controllers', controller_class_path)
|
70
|
+
m.directory File.join('app/helpers', controller_class_path)
|
71
|
+
m.directory File.join('app/views', controller_class_path, controller_file_name)
|
72
|
+
m.directory File.join('test/functional', controller_class_path)
|
73
|
+
m.directory File.join('config', controller_class_path)
|
74
|
+
|
75
|
+
# Depend on model generator but skip if the model exists.
|
76
|
+
m.dependency 'model', [singular_name], :collision => :skip
|
77
|
+
|
78
|
+
# Scaffolded forms.
|
79
|
+
m.complex_template "form.rhtml",
|
80
|
+
File.join('app/views',
|
81
|
+
controller_class_path,
|
82
|
+
controller_file_name,
|
83
|
+
"_form.rhtml"),
|
84
|
+
:insert => 'form_trestle.rhtml',
|
85
|
+
:sandbox => lambda { create_sandbox },
|
86
|
+
:begin_mark => 'form',
|
87
|
+
:end_mark => 'eoform',
|
88
|
+
:mark_id => singular_name
|
89
|
+
|
90
|
+
# Scaffolded views.
|
91
|
+
trestle_views.each do |action|
|
92
|
+
m.template "view_#{action}.rhtml",
|
93
|
+
File.join('app/views',
|
94
|
+
controller_class_path,
|
95
|
+
controller_file_name,
|
96
|
+
"#{action}.rhtml"),
|
97
|
+
:assigns => { :action => action }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Controller class, functional test, helper, and views.
|
101
|
+
m.template 'controller.rb',
|
102
|
+
File.join('app/controllers',
|
103
|
+
controller_class_path,
|
104
|
+
"#{controller_file_name}_controller.rb")
|
105
|
+
|
106
|
+
m.template 'functional_test.rb',
|
107
|
+
File.join('test/functional',
|
108
|
+
controller_class_path,
|
109
|
+
"#{controller_file_name}_controller_test.rb")
|
110
|
+
|
111
|
+
m.template 'helper.rb',
|
112
|
+
File.join('app/helpers',
|
113
|
+
controller_class_path,
|
114
|
+
"#{controller_file_name}_helper.rb")
|
115
|
+
|
116
|
+
# Layout and stylesheet.
|
117
|
+
m.template 'layout.rhtml', "app/views/layouts/#{controller_file_name}.rhtml"
|
118
|
+
m.template 'style.css', 'public/stylesheets/trestle.css'
|
119
|
+
|
120
|
+
|
121
|
+
# Untrestled views.
|
122
|
+
untrestled_actions.each do |action|
|
123
|
+
path = File.join('app/views',
|
124
|
+
controller_class_path,
|
125
|
+
controller_file_name,
|
126
|
+
"#{action}.rhtml")
|
127
|
+
m.template "controller:view.rhtml", path,
|
128
|
+
:assigns => { :action => action, :path => path}
|
129
|
+
end
|
130
|
+
|
131
|
+
# Routes.
|
132
|
+
m.template 'routes.rb', 'config/routes.rb'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
protected
|
138
|
+
|
139
|
+
# Override with your own usage banner.
|
140
|
+
def banner
|
141
|
+
"Usage: #{$0} trestle ModelName [ControllerName] [action, ...]"
|
142
|
+
end
|
143
|
+
|
144
|
+
def trestle_views
|
145
|
+
%w(edit _index_without_id _index_with_id new)
|
146
|
+
end
|
147
|
+
|
148
|
+
def trestle_actions
|
149
|
+
%w(destroy edit index new)
|
150
|
+
end
|
151
|
+
|
152
|
+
def model_name
|
153
|
+
class_name.demodulize
|
154
|
+
end
|
155
|
+
|
156
|
+
def untrestled_actions
|
157
|
+
args - trestle_actions
|
158
|
+
end
|
159
|
+
|
160
|
+
def suffix
|
161
|
+
"_#{singular_name}" if options[:suffix]
|
162
|
+
end
|
163
|
+
|
164
|
+
def create_sandbox
|
165
|
+
sandbox = TrestleSandbox.new
|
166
|
+
sandbox.singular_name = singular_name
|
167
|
+
begin
|
168
|
+
sandbox.model_instance = model_instance
|
169
|
+
sandbox.instance_variable_set("@#{singular_name}", sandbox.model_instance)
|
170
|
+
rescue ActiveRecord::StatementInvalid => e
|
171
|
+
logger.error "Before updating trestle from new DB schema, try creating a table for your model (#{class_name})"
|
172
|
+
raise SystemExit
|
173
|
+
end
|
174
|
+
sandbox.suffix = suffix
|
175
|
+
sandbox
|
176
|
+
end
|
177
|
+
|
178
|
+
def model_instance
|
179
|
+
base = class_nesting.split('::').inject(Object) do |base, nested|
|
180
|
+
break base.const_get(nested) if base.const_defined?(nested)
|
181
|
+
base.const_set(nested, Module.new)
|
182
|
+
end
|
183
|
+
unless base.const_defined?(@class_name_without_nesting)
|
184
|
+
base.const_set(@class_name_without_nesting, Class.new(ActiveRecord::Base))
|
185
|
+
end
|
186
|
+
class_name.constantize.new
|
187
|
+
end
|
188
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.10
|
3
|
+
specification_version: 1
|
4
|
+
name: trestle_generator
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2006-01-03
|
8
|
+
summary: "[Rails] An adaptation of the scaffold generator that produces production-ready
|
9
|
+
controllers that are safe from state-changing HTTP GET requests and that have
|
10
|
+
streamlined URLs."
|
11
|
+
require_paths:
|
12
|
+
- templates
|
13
|
+
email: nils@alumni.rice.edu
|
14
|
+
homepage: http://www.rubyforge.org/projects/trestle
|
15
|
+
rubyforge_project: trestle
|
16
|
+
description: "The name of this RubyGem points out that nobody intentionally sends a train
|
17
|
+
across a gorge with nothing but a scaffold to support it. That�s what a trestle
|
18
|
+
is for! Why use the trestle generator instead of the scaffold generator? The
|
19
|
+
trestle generator produces code that is closer to production-quality in two
|
20
|
+
respects: SAFETY The trestle generator uses the HTTP POST method for actions
|
21
|
+
that change data. Well-behaved web applications protect themselves against
|
22
|
+
errant HTTP GET requests (such as come from Google Web Accelerator and the
|
23
|
+
like). The scaffold generator fails to do this. USABILITY The trestle
|
24
|
+
generator produces controllers with just four actions (index, new, edit, and
|
25
|
+
destroy). Controllers produced by the scaffold generator have eight actions.
|
26
|
+
Fewer actions exposed to the outside world is better if the behavior of these
|
27
|
+
actions is in line with the semantics of HTTP GET and HTTP POST. The net effect
|
28
|
+
is that you will not have to throw away or tweak as much trestle-generated code
|
29
|
+
as you do scaffold-generated code. Less work for you means your application gets
|
30
|
+
built sooner. Tobias L�tke�s postback_generator RubyGem has the same idea. The
|
31
|
+
scaffold generator produces a controller with the following public interface for
|
32
|
+
a database table named �people�: /people - lists existing person records
|
33
|
+
/people/list - lists existing person records /people/new - shows an empty
|
34
|
+
person form /people/create - creates a new person record from request
|
35
|
+
parameters /people/show/99 - shows the person record having ID 99
|
36
|
+
/people/edit/99 - shows a person form for the person record having ID 99
|
37
|
+
/people/update/99 - updates the person record having ID 99 using request
|
38
|
+
parameters /people/destroy/99 - deletes the person record having ID 99, even
|
39
|
+
for HTTP GET! Contrast this with the public interface produced by the trestle
|
40
|
+
generator: /people - lists existing person records /people/new - HTTP GET
|
41
|
+
shows an empty person form; HTTP POST creates a new person record from request
|
42
|
+
parameters /people/99 - shows the person record having ID 99 /people/99/edit -
|
43
|
+
HTTP GET shows a person form for the person record having ID 99; HTTP POST
|
44
|
+
updates the person record having ID 99 using request parameters
|
45
|
+
/people/99/destroy - HTTP GET redirects to /people/99/edit with a notice that
|
46
|
+
the user must click the form�s Destroy button to destroy a record; HTTP POST
|
47
|
+
deletes the person record having ID 99 after prompting the user for confirmation
|
48
|
+
Notice the hierarchical nature of trestle URLs. Because the ID comes before the
|
49
|
+
verb, there are fewer illegal variations that with scaffold URLs. This
|
50
|
+
encourages the user to use the Up One Level button on the Google Toolbar that
|
51
|
+
clips one element off the end of the current URL. Scaffold URLs do not play
|
52
|
+
nicely with the Up One Level button."
|
53
|
+
autorequire:
|
54
|
+
default_executable:
|
55
|
+
bindir: bin
|
56
|
+
has_rdoc: false
|
57
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
58
|
+
requirements:
|
59
|
+
-
|
60
|
+
- ">"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 0.0.0
|
63
|
+
version:
|
64
|
+
platform: ruby
|
65
|
+
authors:
|
66
|
+
- Nils Jonsson
|
67
|
+
files:
|
68
|
+
- trestle_generator.rb
|
69
|
+
- USAGE
|
70
|
+
- templates/controller.rb
|
71
|
+
- templates/form.rhtml
|
72
|
+
- templates/form_trestle.rhtml
|
73
|
+
- templates/functional_test.rb
|
74
|
+
- templates/helper.rb
|
75
|
+
- templates/layout.rhtml
|
76
|
+
- templates/routes.rb
|
77
|
+
- templates/style.css
|
78
|
+
- templates/view_edit.rhtml
|
79
|
+
- templates/view_new.rhtml
|
80
|
+
- templates/view__index_without_id.rhtml
|
81
|
+
- templates/view__index_with_id.rhtml
|
82
|
+
test_files: []
|
83
|
+
rdoc_options: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
requirements:
|
88
|
+
- Ruby on Rails v1.0.0
|
89
|
+
dependencies:
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: rails
|
92
|
+
version_requirement:
|
93
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
94
|
+
requirements:
|
95
|
+
-
|
96
|
+
- ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: 1.0.0
|
99
|
+
version:
|