jrhicks-static-generators 0.1.6
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/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
|
+
|