1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
|
require 'generators/nifty'
require 'rails/generators/migration'
require 'rails/generators/generated_attribute'
module Nifty
module Generators
class ScaffoldGenerator < Base
include Rails::Generators::Migration
no_tasks { attr_accessor :scaffold_name, :model_attributes, :controller_actions }
argument :scaffold_name, :type => :string, :required => true, :banner => 'ModelName'
argument :args_for_c_m, :type => :array, :default => [], :banner => 'controller_actions and model:attributes'
class_option :skip_model, :desc => 'Don\'t generate a model or migration file.', :type => :boolean
class_option :skip_migration, :desc => 'Don\'t generate migration file for model.', :type => :boolean
class_option :skip_timestamps, :desc => 'Don\'t add timestamps to migration file.', :type => :boolean
class_option :skip_controller, :desc => 'Don\'t generate controller, helper, or views.', :type => :boolean
class_option :invert, :desc => 'Generate all controller actions except these mentioned.', :type => :boolean
class_option :namespace_model, :desc => 'If the resource is namespaced, include the model in the namespace.', :type => :boolean
class_option :haml, :desc => 'Generate HAML views instead of ERB.', :type => :boolean, :default => true
class_option :testunit, :desc => 'Use test/unit for test files.', :group => 'Test framework', :type => :boolean
class_option :rspec, :desc => 'Use RSpec for test files.', :group => 'Test framework', :type => :boolean
class_option :shoulda, :desc => 'Use shoulda for test files.', :group => 'Test framework', :type => :boolean
def initialize(*args, &block)
super
print_usage unless scaffold_name.underscore =~ /^[a-z][a-z0-9_\/]+$/
@controller_actions = []
@model_attributes = []
@skip_model = options.skip_model?
@namespace_model = options.namespace_model?
@invert_actions = options.invert?
args_for_c_m.each do |arg|
if arg == '!'
@invert_actions = true
elsif arg.include?(':')
@model_attributes << Rails::Generators::GeneratedAttribute.new(*arg.split(':'))
else
@controller_actions << arg
@controller_actions << 'create' if arg == 'new'
@controller_actions << 'update' if arg == 'edit'
end
end
@controller_actions.uniq!
@model_attributes.uniq!
if @invert_actions || @controller_actions.empty?
@controller_actions = all_actions - @controller_actions
end
if @model_attributes.empty?
@skip_model = true # skip model if no attributes
if model_exists?
model_columns_for_attributes.each do |column|
@model_attributes << Rails::Generators::GeneratedAttribute.new(column.name.to_s, column.type.to_s)
end
else
@model_attributes << Rails::Generators::GeneratedAttribute.new('name', 'string')
end
end
end
def add_gems
# add_gem "mocha", :group => :test
add_gem 'haml'
add_gem 'simple_form'
add_gem 'cancan'
end
def create_model
unless @skip_model
template 'model.rb', "app/models/#{model_path}.rb"
if test_framework == :rspec
template "tests/rspec/model.rb", "spec/models/#{model_path}_spec.rb"
# FUCK YOU FIXTURES, WE USE FACTORY GIRL.
# template 'fixtures.yml', "spec/fixtures/#{model_path.pluralize}.yml"
else
template "tests/#{test_framework}/model.rb", "test/unit/#{model_path}_test.rb"
template 'fixtures.yml', "test/fixtures/#{model_path.pluralize}.yml"
end
end
end
def create_migration
unless @skip_model || options.skip_migration?
migration_template 'migration.rb', "db/migrate/create_#{model_path.pluralize.gsub('/', '_')}.rb"
end
end
def create_controller
unless options.skip_controller?
template 'controller.rb', "app/controllers/#{plural_name}_controller.rb"
template 'helper.rb', "app/helpers/#{plural_name}_helper.rb"
controller_actions.each do |action|
if %w[index show new edit].include?(action) # Actions with templates
template "views/#{view_language}/#{action}.html.#{view_language}", "app/views/#{plural_name}/#{action}.html.#{view_language}"
end
end
# Move the index_core (can't do it on the top.)
template "views/#{view_language}/_index_core.html.#{view_language}", "app/views/#{plural_name}/_index_core.html.#{view_language}"
if form_partial?
template "views/#{view_language}/_form.html.#{view_language}", "app/views/#{plural_name}/_form.html.#{view_language}"
template "views/#{view_language}/_form_core.html.#{view_language}", "app/views/#{plural_name}/_form_core.html.#{view_language}"
end
namespaces = plural_name.split('/')
resource = namespaces.pop
route namespaces.reverse.inject("resources :#{resource}") { |acc, namespace|
"namespace(:#{namespace}){ #{acc} }"
}
if test_framework == :rspec
template "tests/#{test_framework}/controller.rb", "spec/controllers/#{plural_name}_controller_spec.rb"
else
template "tests/#{test_framework}/controller.rb", "test/functional/#{plural_name}_controller_test.rb"
end
end
end
def create_locales
template 'locale.yml', "config/locales/views/#{plural_name}/en.yml"
template 'locale_de.yml', "config/locales/views/#{plural_name}/de.yml"
# template 'locale.yml', "config/locales/views/#{plural_name}/es.yml"
# gsub_file("config/locales/views/#{plural_name}/es.yml", 'en:', 'es:')
# gsub_file("config/locales/views/#{plural_name}/de.yml", 'en:', 'de:')
end
def configuration
gsub_file('config/application.rb', "# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]",
"config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '*', '*', '*.{rb,yml}').to_s]")
end
private
def form_partial?
actions? :new, :edit
end
def all_actions
%w[index show new create edit update destroy]
end
def action?(name)
controller_actions.include? name.to_s
end
def actions?(*names)
names.all? { |name| action? name }
end
def singular_name
scaffold_name.underscore
end
def plural_name
scaffold_name.underscore.pluralize
end
def human_name
scaffold_name.humanize
end
def table_name
if scaffold_name.include?('::') && @namespace_model
plural_name.gsub('/', '_')
end
end
def class_name
if @namespace_model
scaffold_name.camelize
else
scaffold_name.split('::').last.camelize
end
end
def model_path
class_name.underscore
end
def plural_class_name
plural_name.camelize
end
def instance_name
if @namespace_model
singular_name.gsub('/','_')
else
singular_name.split('/').last
end
end
def instances_name
instance_name.pluralize
end
def controller_methods(dir_name)
controller_actions.map do |action|
read_template("#{dir_name}/#{action}.rb")
end.join("\n").strip
end
def render_form
if form_partial?
if options.haml?
"= render \"form\""
else
"<%= render \"form\" %>"
end
else
read_template("views/#{view_language}/_form.html.#{view_language}")
end
end
def item_resource
scaffold_name.underscore.gsub('/','_')
end
def items_path
if action? :index
"#{item_resource.pluralize}_path"
else
"root_path"
end
end
def item_path(options = {})
name = options[:instance_variable] ? "@#{instance_name}" : instance_name
suffix = options[:full_url] ? "url" : "path"
if options[:action].to_s == "new"
"new_#{item_resource}_#{suffix}"
elsif options[:action].to_s == "edit"
"edit_#{item_resource}_#{suffix}(#{name})"
else
if scaffold_name.include?('::') && !@namespace_model
namespace = singular_name.split('/')[0..-2]
"[:#{namespace.join(', :')}, #{name}]"
else
name
end
end
end
def item_url
if action? :show
item_path(:full_url => true, :instance_variable => true)
else
items_url
end
end
def items_url
if action? :index
item_resource.pluralize + '_url'
else
"root_url"
end
end
def item_path_for_spec(suffix = 'path')
if action? :show
"#{item_resource}_#{suffix}(assigns[:#{instance_name}])"
else
if suffix == 'path'
items_path
else
items_url
end
end
end
def item_path_for_test(suffix = 'path')
if action? :show
"#{item_resource}_#{suffix}(assigns(:#{instance_name}))"
else
if suffix == 'path'
items_path
else
items_url
end
end
end
def model_columns_for_attributes
class_name.constantize.columns.reject do |column|
column.name.to_s =~ /^(id|created_at|updated_at)$/
end
end
def view_language
options.haml? ? 'haml' : 'erb'
end
def test_framework
return @test_framework if defined?(@test_framework)
if options.testunit?
return @test_framework = :testunit
elsif options.rspec?
return @test_framework = :rspec
elsif options.shoulda?
return @test_framework = :shoulda
else
return @test_framework = default_test_framework
end
end
def default_test_framework
File.exist?(destination_path("spec")) ? :rspec : :testunit
end
def model_exists?
File.exist? destination_path("app/models/#{singular_name}.rb")
end
def read_template(relative_path)
ERB.new(File.read(find_in_source_paths(relative_path)), nil, '-').result(binding)
end
def destination_path(path)
File.join(destination_root, path)
end
# FIXME: Should be proxied to ActiveRecord::Generators::Base
# Implement the required interface for Rails::Generators::Migration.
def self.next_migration_number(dirname) #:nodoc:
if ActiveRecord::Base.timestamped_migrations
Time.now.utc.strftime("%Y%m%d%H%M%S")
else
"%.3d" % (current_migration_number(dirname) + 1)
end
end
end
end
end
|