jrhicks-static-generators 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/LICENSE +21 -0
- data/Manifest +32 -0
- data/README.md +27 -0
- data/Rakefile +15 -0
- data/TODO +4 -0
- data/USAGE +17 -0
- data/lib/static_generators.rb +3 -0
- data/rails_generators/static_app/USAGE +0 -0
- data/rails_generators/static_app/static_app_generator.rb +26 -0
- data/rails_generators/static_app/templates/application_helper.rb +11 -0
- data/rails_generators/static_app/templates/layout.html.erb +0 -0
- data/rails_generators/static_app/templates/layout_helper.rb +9 -0
- data/rails_generators/static_app/templates/sorttable.js +568 -0
- data/rails_generators/static_app/templates/static_style.css +90 -0
- data/rails_generators/static_gen_specs/USAGE +0 -0
- data/rails_generators/static_gen_specs/static_gen_specs_generator.rb +343 -0
- data/rails_generators/static_gen_specs/templates/generator_specs.rb +60 -0
- data/rails_generators/static_gen_specs/templates/model_gen_specs.rb +145 -0
- data/rails_generators/static_scaffold/USAGE +29 -0
- data/rails_generators/static_scaffold/static_scaffold_generator.rb +150 -0
- data/rails_generators/static_scaffold/templates/controller.rb +85 -0
- data/rails_generators/static_scaffold/templates/functional_test.rb +45 -0
- data/rails_generators/static_scaffold/templates/helper.rb +2 -0
- data/rails_generators/static_scaffold/templates/helper_test.rb +4 -0
- data/rails_generators/static_scaffold/templates/layout.html.erb +18 -0
- data/rails_generators/static_scaffold/templates/model.rb +109 -0
- data/rails_generators/static_scaffold/templates/view_edit.html.erb +19 -0
- data/rails_generators/static_scaffold/templates/view_index.html.erb +26 -0
- data/rails_generators/static_scaffold/templates/view_new.html.erb +18 -0
- data/rails_generators/static_scaffold/templates/view_show.html.erb +13 -0
- data/static-generators.gemspec +31 -0
- data/tasks/deployment.rake +9 -0
- metadata +95 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
body {
|
2
|
+
font-family: "lucida grande", verdana, lucida, sans-serif;
|
3
|
+
font-size: 8pt;
|
4
|
+
}
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
pre {
|
9
|
+
background-color: #eee;
|
10
|
+
padding: 10px;
|
11
|
+
font-size: 11px;
|
12
|
+
}
|
13
|
+
|
14
|
+
a { color: #000; }
|
15
|
+
a:visited { color: #666; }
|
16
|
+
a:hover { color: #fff; background-color:#000; }
|
17
|
+
|
18
|
+
.fieldWithErrors {
|
19
|
+
padding: 2px;
|
20
|
+
background-color: red;
|
21
|
+
display: table;
|
22
|
+
}
|
23
|
+
|
24
|
+
#errorExplanation {
|
25
|
+
width: 400px;
|
26
|
+
border: 2px solid red;
|
27
|
+
padding: 7px;
|
28
|
+
padding-bottom: 12px;
|
29
|
+
margin-bottom: 20px;
|
30
|
+
background-color: #f0f0f0;
|
31
|
+
}
|
32
|
+
|
33
|
+
#errorExplanation h2 {
|
34
|
+
text-align: left;
|
35
|
+
font-weight: bold;
|
36
|
+
padding: 5px 5px 5px 15px;
|
37
|
+
font-size: 12px;
|
38
|
+
margin: -7px;
|
39
|
+
background-color: #c00;
|
40
|
+
color: #fff;
|
41
|
+
}
|
42
|
+
|
43
|
+
#errorExplanation p {
|
44
|
+
color: #333;
|
45
|
+
margin-bottom: 0;
|
46
|
+
padding: 5px;
|
47
|
+
}
|
48
|
+
|
49
|
+
#errorExplanation ul li {
|
50
|
+
font-size: 12px;
|
51
|
+
list-style: square;
|
52
|
+
}
|
53
|
+
|
54
|
+
|
55
|
+
.sortable {
|
56
|
+
border: 1px solid #D9D9D9;
|
57
|
+
#border-collapse:separate;
|
58
|
+
border-spacing:0;
|
59
|
+
}
|
60
|
+
|
61
|
+
.sortable th {
|
62
|
+
border: 1px solid #FFF;
|
63
|
+
border-right-color: #CCC;
|
64
|
+
border-bottom-color: #CCC;
|
65
|
+
padding: 0 6px;
|
66
|
+
float: none !important; /*For Opera*/
|
67
|
+
float: left; /*For IE*/
|
68
|
+
background: #EEE;
|
69
|
+
color: #666;
|
70
|
+
font: bold 10px/22px Verdana, Arial, Helvetica, sans-serif;
|
71
|
+
text-decoration: none;
|
72
|
+
height: auto !important;
|
73
|
+
height: 1%; /*For IE*/
|
74
|
+
}
|
75
|
+
|
76
|
+
.sortable tbody tr td {
|
77
|
+
font-family: "lucida grande", verdana, sans-serif;
|
78
|
+
font-size: 8pt;
|
79
|
+
padding: 3px 8px;
|
80
|
+
border-left: 1px solid #D9D9D9;
|
81
|
+
}
|
82
|
+
|
83
|
+
.sortable tbody tr.selected td {
|
84
|
+
background-color: #3d80df;
|
85
|
+
color: #ffffff;
|
86
|
+
font-weight: bold;
|
87
|
+
border-left: 1px solid #346DBE;
|
88
|
+
border-bottom: 1px solid #7DAAEA;
|
89
|
+
}
|
90
|
+
|
File without changes
|
@@ -0,0 +1,343 @@
|
|
1
|
+
class StaticGenSpecsGenerator < Rails::Generator::NamedBase
|
2
|
+
default_options :skip_timestamps => false, :skip_migration => false, :force_plural => 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
|
+
|
17
|
+
def initialize(runtime_args, runtime_options = {})
|
18
|
+
super
|
19
|
+
|
20
|
+
if @name == @name.pluralize && !options[:force_plural]
|
21
|
+
logger.warning "Plural version of the model detected, using singularized version. Override with --force-plural."
|
22
|
+
@name = @name.singularize
|
23
|
+
end
|
24
|
+
|
25
|
+
@controller_name = @name.pluralize
|
26
|
+
|
27
|
+
base_name, @controller_class_path, @controller_file_path, @controller_class_nesting, @controller_class_nesting_depth = extract_modules(@controller_name)
|
28
|
+
@controller_class_name_without_nesting, @controller_underscore_name, @controller_plural_name = inflect_names(base_name)
|
29
|
+
@controller_singular_name=base_name.singularize
|
30
|
+
if @controller_class_nesting.empty?
|
31
|
+
@controller_class_name = @controller_class_name_without_nesting
|
32
|
+
else
|
33
|
+
@controller_class_name = "#{@controller_class_nesting}::#{@controller_class_name_without_nesting}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def manifest
|
38
|
+
record do |m|
|
39
|
+
# Check for class naming collisions.
|
40
|
+
m.class_collisions("#{controller_class_name}Controller", "#{controller_class_name}Helper")
|
41
|
+
m.class_collisions(class_name)
|
42
|
+
|
43
|
+
# Controller, helper, views, test and stylesheets directories.
|
44
|
+
m.directory("static_scaffold")
|
45
|
+
|
46
|
+
# Model
|
47
|
+
m.template(
|
48
|
+
"generator_specs.rb",
|
49
|
+
File.join('static_scaffold',"generator_specs.rb"))
|
50
|
+
m.template(
|
51
|
+
"model_gen_specs.rb",
|
52
|
+
File.join('static_scaffold',"#{@controller_singular_name.underscore.downcase}_gen_specs.rb"))
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
# Override with your own usage banner.
|
59
|
+
def banner
|
60
|
+
"Usage: #{$0} static_gen_specs ModelName [table_name]"
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_options!(opt)
|
64
|
+
opt.separator ''
|
65
|
+
opt.separator 'Options:'
|
66
|
+
opt.on("--skip-timestamps",
|
67
|
+
"Don't add timestamps to the migration file for this model") { |v| options[:skip_timestamps] = v }
|
68
|
+
opt.on("--skip-migration",
|
69
|
+
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
|
70
|
+
opt.on("--force-plural",
|
71
|
+
"Forces the generation of a plural ModelName") { |v| options[:force_plural] = v }
|
72
|
+
end
|
73
|
+
|
74
|
+
def scaffold_views
|
75
|
+
%w[ index show new edit ]
|
76
|
+
end
|
77
|
+
|
78
|
+
def model_name
|
79
|
+
class_name.demodulize
|
80
|
+
end
|
81
|
+
|
82
|
+
def columns
|
83
|
+
ActiveRecord::Base.connection.columns(table_name)
|
84
|
+
end
|
85
|
+
|
86
|
+
def table_name
|
87
|
+
if @args and @args.length>=1
|
88
|
+
return @args.first
|
89
|
+
else
|
90
|
+
return @controller_plural_name
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def belongs_to_columns
|
95
|
+
# return columns indending in '_id'
|
96
|
+
columns.select {|c| c.name.slice(-3,3)=="_id"}
|
97
|
+
end
|
98
|
+
|
99
|
+
def has_many_columns
|
100
|
+
# search other tables for foreign keys, match based on naming convention
|
101
|
+
# for example: friend_user_id would be assumed to be a foreign key to a User
|
102
|
+
results = []
|
103
|
+
tables = ActiveRecord::Base.connection.tables
|
104
|
+
ref_id = "#{model_name.underscore.singularize.downcase}_id"
|
105
|
+
for t in tables
|
106
|
+
for c in ActiveRecord::Base.connection.columns(t)
|
107
|
+
if c.name.slice(0-ref_id.length,ref_id.length)==ref_id
|
108
|
+
results << {:table=>t, :column=>c.name}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
return results
|
113
|
+
end
|
114
|
+
|
115
|
+
# a helper method
|
116
|
+
|
117
|
+
def max_length(objects, method)
|
118
|
+
(objects.map {|item| item.send(method.to_s).inspect.length} << method.to_s.length).max
|
119
|
+
end
|
120
|
+
|
121
|
+
def column(col_name)
|
122
|
+
columns.select {|c| c.name==col_name }.first
|
123
|
+
end
|
124
|
+
|
125
|
+
def column_names
|
126
|
+
cnames = columns.map {|c| c.name}
|
127
|
+
end
|
128
|
+
|
129
|
+
def estimate_cols(cname)
|
130
|
+
c=column(cname)
|
131
|
+
case c.type
|
132
|
+
when :text then 30
|
133
|
+
when :integer then
|
134
|
+
if belongs_to_columns.map {|bt| bt.name}.select {|name| name==cname}.first
|
135
|
+
30
|
136
|
+
else
|
137
|
+
5
|
138
|
+
end
|
139
|
+
when :datetime then 18
|
140
|
+
when :float then 8
|
141
|
+
when :string then estimate_string(cname)
|
142
|
+
else 30
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def estimate_rows(cname)
|
147
|
+
c=column(cname)
|
148
|
+
case c.type
|
149
|
+
when :text then 3
|
150
|
+
else 1
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def estimate_string(cname)
|
155
|
+
# Look for commonly named fields and guess length otherwise make an educated guess based on db limits
|
156
|
+
c=column(cname)
|
157
|
+
common_lengths = { "phone"=>15,"fax"=>15, "zip"=>12, "first_name"=> 15, "last_name"=>15 }
|
158
|
+
key=common_lengths.keys.select {|key| cname[key]}.first
|
159
|
+
if key
|
160
|
+
common_lengths[key]
|
161
|
+
else
|
162
|
+
case c.limit
|
163
|
+
when 0..15
|
164
|
+
c.limit
|
165
|
+
when 15..30
|
166
|
+
20
|
167
|
+
else
|
168
|
+
30
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def guess_type(cname)
|
174
|
+
if belongs_to_columns.map {|bt| bt.name}.select {|name| name==cname}.first
|
175
|
+
return :foreign_key
|
176
|
+
end
|
177
|
+
if cname=="id"
|
178
|
+
return :primary_key
|
179
|
+
end
|
180
|
+
if is_file_column?(cname)
|
181
|
+
return :file
|
182
|
+
end
|
183
|
+
if is_photo_column?(cname)
|
184
|
+
return :photo
|
185
|
+
end
|
186
|
+
return column(cname).type
|
187
|
+
end
|
188
|
+
|
189
|
+
def guess_alignment(cname)
|
190
|
+
c=column(cname)
|
191
|
+
case guess_type(cname)
|
192
|
+
when :integer
|
193
|
+
"right"
|
194
|
+
when :float
|
195
|
+
"right"
|
196
|
+
when :decimal
|
197
|
+
"right"
|
198
|
+
else
|
199
|
+
"left"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def guess_decimals(cname)
|
204
|
+
c=column(cname)
|
205
|
+
case guess_type(cname)
|
206
|
+
when :integer
|
207
|
+
0
|
208
|
+
when :float
|
209
|
+
3
|
210
|
+
when :decimal
|
211
|
+
3
|
212
|
+
else
|
213
|
+
nil
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def find_column_names_matching(name_list)
|
218
|
+
results = []
|
219
|
+
for item in name_list
|
220
|
+
results = results+column_names.select {|cname| cname.match(item)}
|
221
|
+
end
|
222
|
+
return results
|
223
|
+
end
|
224
|
+
|
225
|
+
def is_photo_column?(cname)
|
226
|
+
find_column_names_matching("photo thumbnail".split).include?(cname)
|
227
|
+
end
|
228
|
+
|
229
|
+
def is_file_column?(cname)
|
230
|
+
find_column_names_matching("file fname attachment document fpath".split).include?(cname)
|
231
|
+
end
|
232
|
+
|
233
|
+
def guess_short_name
|
234
|
+
guess = find_column_names_matching("name")
|
235
|
+
if guess.length==0
|
236
|
+
["id"]
|
237
|
+
else
|
238
|
+
guess
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def guess_ordered_columns
|
243
|
+
# return names and dates otherwise id
|
244
|
+
names = columns.map {|c| c.name}.select {|name| name.match("name")}
|
245
|
+
dates = columns.select {|c| c.type==:datetime}.map {|c| c.name }
|
246
|
+
combined = names+dates
|
247
|
+
if combined
|
248
|
+
return combined
|
249
|
+
else
|
250
|
+
return "#{table_name}.id"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def guess_list_columns
|
255
|
+
text_fields = columns.select {|c|c.type==:text}.map{|c|c.name}
|
256
|
+
meta_fields = ["id","created_at", "updated_at", "version", "guid"]
|
257
|
+
return column_names - text_fields - meta_fields
|
258
|
+
end
|
259
|
+
|
260
|
+
# the idea here is to guess at whate columns should be included in the list/table view
|
261
|
+
# the anticipation is that some columns will be hidden by default
|
262
|
+
|
263
|
+
def guess_in_list(cname)
|
264
|
+
c=column(cname)
|
265
|
+
if c.type==:text
|
266
|
+
return false
|
267
|
+
end
|
268
|
+
hidden_fields = "password guid".split
|
269
|
+
for h in hidden_fields
|
270
|
+
if cname.match(h)
|
271
|
+
return false
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
return true
|
276
|
+
end
|
277
|
+
|
278
|
+
# units helpers - the idea is to encourage folks to append their units to the end of their db column names
|
279
|
+
|
280
|
+
def units_hash
|
281
|
+
{
|
282
|
+
"_hrs" => "hours",
|
283
|
+
"_hours"=>"hours",
|
284
|
+
"_min"=>"minutes",
|
285
|
+
"_minutes"=>"minutes",
|
286
|
+
"_ppm"=>"ppm",
|
287
|
+
"_ppb"=>"ppb",
|
288
|
+
"_mgm3"=>"mg/m3",
|
289
|
+
"_f"=>"°f",
|
290
|
+
"_c"=>"°c",
|
291
|
+
"_rh"=>"% rh",
|
292
|
+
"_utf"=>""
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
def units(cname)
|
297
|
+
u=units_hash[units_key(cname)]
|
298
|
+
if u
|
299
|
+
u
|
300
|
+
else
|
301
|
+
nil
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def label(cname)
|
306
|
+
ukey = units_key(cname)
|
307
|
+
if ukey
|
308
|
+
cname.slice(0,cname.length-ukey.length).titleize
|
309
|
+
else
|
310
|
+
cname.titleize
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def units_key(cname)
|
315
|
+
units_hash.keys.select {|k| cname.slice((0-k.length),k.length)==k }.first
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
|
320
|
+
end
|
321
|
+
|
322
|
+
class CodeJustifier
|
323
|
+
# This class is designed to generate readable code, but is a hypocrate
|
324
|
+
|
325
|
+
attr_accessor :objects, :parameters
|
326
|
+
|
327
|
+
def initialize(objects)
|
328
|
+
self.objects = objects
|
329
|
+
self.parameters = []
|
330
|
+
end
|
331
|
+
|
332
|
+
def add_parameter(&p)
|
333
|
+
self.parameters << p
|
334
|
+
end
|
335
|
+
|
336
|
+
def render(object)
|
337
|
+
self.parameters.map {|p| p.call(object).ljust(max_length(p))}.join("")
|
338
|
+
end
|
339
|
+
|
340
|
+
def max_length(parameter)
|
341
|
+
self.objects.map {|o| parameter.call(o).to_s.length }.max
|
342
|
+
end
|
343
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Load all generator specs
|
2
|
+
#spec_files = Dir.glob(File.join(RAILS_ROOT,"static_scaffold","*.rb"))
|
3
|
+
#for f in spec_files
|
4
|
+
# require f
|
5
|
+
#end
|
6
|
+
|
7
|
+
class GenSpecFactory
|
8
|
+
|
9
|
+
def GenSpecFactory.constantize(mname)
|
10
|
+
if mname.type==Array
|
11
|
+
return mname.map {|m|m.constantize_gen_spec}
|
12
|
+
else
|
13
|
+
# Instantiate the GenSpec based on model name
|
14
|
+
mname = model_name if not mname
|
15
|
+
gen_specs_mname = "#{mname}GenSpecs"
|
16
|
+
Object::const_get(gen_specs_mname).new()
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class GeneratorSpecs < GenSpecFactory
|
23
|
+
|
24
|
+
|
25
|
+
def table_name
|
26
|
+
raise Exception("table_name method not implimented.")
|
27
|
+
end
|
28
|
+
|
29
|
+
def schema
|
30
|
+
ActiveRecord::Base.connection.columns(table_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def small_icon
|
34
|
+
"small_icon_#{table_name}.gif"
|
35
|
+
end
|
36
|
+
|
37
|
+
def model_file_name
|
38
|
+
# <%=model_name.underscore.inspect%>
|
39
|
+
model_name.underscore
|
40
|
+
end
|
41
|
+
|
42
|
+
def controller_class_name
|
43
|
+
# <%="#{model_name.pluralize}Controller".inspect%>
|
44
|
+
"#{model_name.pluralize}Controller"
|
45
|
+
end
|
46
|
+
|
47
|
+
def controller_file_name
|
48
|
+
# "<%="#{model_name.pluralize}Controller".underscore.inspect%>"
|
49
|
+
"#{model_name.pluralize}Controller".underscore
|
50
|
+
end
|
51
|
+
|
52
|
+
def view_folder_name
|
53
|
+
# <%=model_name.underscore.inspect%>
|
54
|
+
model_name.pluralize.underscore
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|