beds 0.0.2 → 0.1.2
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/bin/beds +11 -65
- data/lib/beds.rb +90 -290
- data/lib/cli.rb +3 -0
- data/lib/new.rb +6 -0
- data/lib/scaffold.rb +102 -0
- data/templates/erb/edit.erb +8 -0
- data/templates/erb/list.erb +21 -0
- data/templates/erb/new.erb +9 -0
- data/templates/hooks/bootstrap.rb +14 -0
- data/templates/hooks/jquery.rb +7 -0
- data/templates/routes/delete.erb +10 -0
- data/templates/routes/edit.erb +25 -0
- data/templates/routes/list.erb +7 -0
- data/templates/routes/new.erb +4 -0
- data/templates/routes/save.erb +17 -0
- data/templates/static_erb/index.erb +10 -0
- data/templates/static_erb/layout.erb +86 -0
- data/templates/static_routes/before.erb +4 -0
- data/templates/static_routes/index.erb +4 -0
- metadata +74 -39
data/bin/beds
CHANGED
@@ -1,70 +1,16 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
:
|
7
|
-
|
8
|
-
:
|
9
|
-
|
10
|
-
:index => true,
|
11
|
-
:exclude => []
|
12
|
-
}
|
13
|
-
optparser = OptionParser.new do |opts|
|
14
|
-
opts.banner = " Usage: #{File.basename($0)} -m <models.rb> [OPTIONS]"
|
15
|
-
opts.on("-m","--models FILE", "Specify file with DataMapper models defined.") do |app|
|
16
|
-
@options[:app] = app
|
17
|
-
end
|
18
|
-
opts.on("-v","--viewdir","Specify which directory to place the views in. Default: ./views/") do |viewdir|
|
19
|
-
@options[:viewdir] = viewdir
|
20
|
-
end
|
21
|
-
opts.on("-r","--routefile", "Sepcify the file to generate the routes in. Default: scaffold.rb") do |routefile|
|
22
|
-
@options[:routefile] = routefile
|
23
|
-
end
|
24
|
-
opts.on("--exclude x,y,z", Array, "Do not generate anything for specified models, or before, index, and/or layout") do |models|
|
25
|
-
@options[:exclude] = models
|
26
|
-
end
|
27
|
-
opts.on("--only x,y,z", Array, "Only process the specified models.") do |models|
|
28
|
-
@options[:only] = models
|
29
|
-
end
|
30
|
-
opts.on("-f","--[no-]force", "Overwtie files") do |force|
|
31
|
-
@options[:force] = force
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
begin
|
36
|
-
optparser.parse! ARGV
|
37
|
-
raise OptionParser::InvalidOption, "[NONE]" if @options[:app].nil?
|
38
|
-
rescue OptionParser::InvalidOption => e
|
39
|
-
puts e
|
40
|
-
puts optparser
|
41
|
-
exit 1
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
load "#{@options[:app]}"
|
47
|
-
scaffold = SinScaffold.new(@options)
|
48
|
-
scaffold.generate
|
49
|
-
|
50
|
-
### Write Files
|
51
|
-
|
52
|
-
if File.exist?(@options[:routefile]) and ! @options[:force] then
|
53
|
-
puts "Error: Route file #{@options[:routefile]} already exists. Not writing"
|
2
|
+
require "#{File.dirname(__FILE__)}/../lib/beds.rb"
|
3
|
+
require "#{File.dirname(__FILE__)}/../lib/cli.rb"
|
4
|
+
|
5
|
+
## A bit of magic to call Beds::CLI::<CLI-Command>
|
6
|
+
cmd = ARGV[0].nil? ? "" : ARGV[0]
|
7
|
+
if ! cmd == "" and Beds::CLI.const_defined?(cmd.capitalize.to_sym) then
|
8
|
+
Beds::CLI.const_get(cmd.capitalize.to_sym).send(:new,ARGV[1..-1])
|
9
|
+
exit 0
|
54
10
|
else
|
55
|
-
File.
|
11
|
+
puts " Usage: #{File.basename($0)} [scaffold|new] [OPTIONS]"
|
12
|
+
puts " See #{File.basename($0)} scaffold --help or #{File.basename($0)} new --help foor more information"
|
13
|
+
exit 1
|
56
14
|
end
|
57
15
|
|
58
|
-
Dir.mkdir(@options[:viewdir]) unless Dir.exist?(@options[:viewdir])
|
59
|
-
|
60
|
-
@gen_files = []
|
61
|
-
scaffold.views.each {|file,content|
|
62
|
-
if File.exist?(File.join(@options[:viewdir],file)) and ! @options[:force] then
|
63
|
-
puts "Warning: file #{File.join(@options[:viewdir],file)} exists. Not overwriting"
|
64
|
-
else
|
65
|
-
@gen_files << File.join(@options[:viewdir],file)
|
66
|
-
File.open(File.join(@options[:viewdir],file),"w") {|file| file.write(content) }
|
67
|
-
end
|
68
|
-
}
|
69
16
|
|
70
|
-
puts "Generated files: #{@gen_files.join(", ")}"
|
data/lib/beds.rb
CHANGED
@@ -1,298 +1,98 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'erb'
|
3
3
|
require 'optparse'
|
4
|
+
## == Beds - BootStrap Erb DataMapper Sinatra [Scaffolder]
|
5
|
+
##
|
6
|
+
## * For general information on using Beds to scaffold your Sinatra projects please see README.rdoc
|
7
|
+
## * Or see Beds:Scaffold
|
4
8
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
module Beds
|
10
|
+
## Generates Scaffolds based off of DataMappr models.
|
11
|
+
class Scaffold
|
12
|
+
# Returns an array of generated routes
|
13
|
+
attr_reader :routes
|
14
|
+
# Returns an array of views and file names.
|
15
|
+
attr_reader :views
|
16
|
+
# Returns a hash containing file_name => file_contents key pairs.
|
17
|
+
attr_reader :templates
|
18
|
+
## * opts should be a hash
|
19
|
+
## * Defautly loads data for all models.
|
20
|
+
##
|
21
|
+
## Valid Options:
|
22
|
+
## :only - An array that only generates views for the specified models.
|
23
|
+
## :exclude - An array containing models that should not be generatd.
|
24
|
+
def initialize(opts = {:exclude => [], :only => nil})
|
25
|
+
@routes = {}
|
26
|
+
@views = {}
|
27
|
+
@models = []
|
28
|
+
@opts = opts
|
29
|
+
DataMapper::Model.descendants.entries.each {|entry|
|
30
|
+
@models << entry
|
31
|
+
}
|
32
|
+
@models.delete_if { |model| @opts[:exclude].include? model.name or @opts[:exclude].include? model.name.downcase }
|
33
|
+
@models.select! { |model| @opts[:only].include? model.name or @opts[:only].include? model.name.downcase } unless @opts[:only].nil?
|
34
|
+
load_templates
|
35
|
+
end
|
36
|
+
## Generate views and routes for models. Will genrate the *view* and *routes* attributes.
|
37
|
+
def generate
|
38
|
+
result = Hash.new
|
39
|
+
routes = Hash.new
|
40
|
+
@templates[:static_routes].each { |key,value| next if @opts[:exclude].include? key.to_s; puts "Processing Route: #{key}"; @routes['default'] ||= String.new ; @routes['default'] << ERB.new(value,nil,"<>").result(binding) } if @opts[:only].nil?
|
41
|
+
@templates[:static_erb].each { |key,value| next if @opts[:exclude].include? key.to_s; puts "Processing Template: #{key}";@views["#{key.to_s}.erb"]=ERB.new(value,nil,"<>").result(binding) } if @opts[:only].nil?
|
42
|
+
@models.each { |entry|
|
43
|
+
@model_name = entry.inspect
|
44
|
+
@properties = entry.properties
|
45
|
+
puts "Processing Model: #{@model_name}"
|
46
|
+
@templates[:routes].each {|key,value|
|
47
|
+
@routes[entry.to_s.downcase] ||= String.new
|
48
|
+
@routes[entry.to_s.downcase] << ERB.new(value,nil,"<>").result(binding)
|
49
|
+
}
|
50
|
+
@templates[:erb].each { |key,value|
|
51
|
+
@views["#{key.to_s}_#{@model_name.downcase}.erb"] = ERB.new(value,nil,"<>").result(binding)
|
52
|
+
}
|
53
|
+
}
|
54
|
+
return true
|
55
|
+
end
|
56
|
+
private
|
57
|
+
## Called by .new, Locates and loads templaes into memory.
|
58
|
+
def load_templates
|
59
|
+
home_dir = "~/.beds"
|
60
|
+
template_dir = "#{File.dirname(__FILE__)}/../templates"
|
61
|
+
@templates=Hash.new
|
62
|
+
@templates[:static_routes] = { }
|
63
|
+
@templates[:static_erb] = { }
|
64
|
+
@templates[:routes] = { }
|
65
|
+
@templates[:erb] = { }
|
66
|
+
@templates[:hooks] = { }
|
67
|
+
@templates.each do |key,val|
|
68
|
+
template_files = Dir.exist?("#{home_dir}/#{key.to_s}") ? Dir.glob("/#{home_dir}/#{key.to_s}/*") : Dir.glob("/#{template_dir}/#{key.to_s}/*")
|
69
|
+
template_files.each { |file| @templates[key][File.basename(file).sub(/\.erb$/,"")] = File.open(file).read }
|
11
70
|
end
|
12
|
-
^,
|
13
|
-
:index => %Q^
|
14
|
-
get '/' do
|
15
|
-
erb :index
|
16
71
|
end
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
<title>Application</title>
|
25
|
-
<meta name="GeneratedBy" content="sdbscaffold"
|
26
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
27
|
-
<meta name="description" content="">
|
28
|
-
<meta name="author" content="">
|
29
|
-
<!-- Le styles -->
|
72
|
+
|
73
|
+
## Determines the type of form to generate for DataMapper objects.
|
74
|
+
def form_type(object,properties)
|
75
|
+
append = String.new
|
76
|
+
properties.each {|key,value|
|
77
|
+
append << " #{key}='#{value}'"
|
78
|
+
}
|
30
79
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
<!-- Le fav and touch icons -->
|
50
|
-
<link rel="shortcut icon" href="/assets/ico/favicon.ico">
|
51
|
-
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/assets/ico/apple-touch-icon-144-precomposed.png">
|
52
|
-
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/assets/ico/apple-touch-icon-114-precomposed.png">
|
53
|
-
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/assets/ico/apple-touch-icon-72-precomposed.png">
|
54
|
-
<link rel="apple-touch-icon-precomposed" href="/assets/ico/apple-touch-icon-57-precomposed.png">
|
55
|
-
</head>
|
56
|
-
|
57
|
-
<body>
|
58
|
-
|
59
|
-
<div class="navbar navbar-fixed-top">
|
60
|
-
<div class="navbar-inner">
|
61
|
-
<div class="container">
|
62
|
-
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
63
|
-
<span class="icon-bar"></span>
|
64
|
-
<span class="icon-bar"></span>
|
65
|
-
<span class="icon-bar"></span>
|
66
|
-
</a>
|
67
|
-
<a class="brand" href="#">YourApp</a>
|
68
|
-
<div class="nav-collapse">
|
69
|
-
<ul class="nav">
|
70
|
-
<li class="divider-vertical"></li>
|
71
|
-
<li id="index" ><a href="/">Home</a></li>
|
72
|
-
<% @models.each do |model| %>
|
73
|
-
<li id="<%=model.name.downcase%>" ><a href="/<%=model.name.downcase%>/"><%= model.name %></a></li>
|
74
|
-
<% end %>
|
75
|
-
</ul>
|
76
|
-
<ul class="nav pull-right">
|
77
|
-
<li class="divider-vertical"></li>
|
78
|
-
<li><a href="/login">Log<%%= session[:valid] ? "out" : "in"%></a></li>
|
79
|
-
</ul>
|
80
|
-
</div><!--/.nav-collapse -->
|
81
|
-
</div>
|
82
|
-
</div>
|
83
|
-
</div>
|
84
|
-
|
85
|
-
<div class="container">
|
86
|
-
<%% if ! @flash.nil? then %>
|
87
|
-
<span class="span9 offset1">
|
88
|
-
<%% @flash.each do |alert| %>
|
89
|
-
<div class="alert alert-<%%=alert[:level].downcase%>" style="margin:0px; margin-bottom:8px;">
|
90
|
-
<button class="close" data-dismiss="alert">x</button>
|
91
|
-
<strong><%%=alert[:level].capitalize%>!</strong> <%%=alert[:message]%>
|
92
|
-
</div>
|
93
|
-
<%% end %>
|
94
|
-
</span>
|
95
|
-
<%%end %>
|
96
|
-
<%%= yield %>
|
97
|
-
</div> <!-- /container -->
|
98
|
-
|
99
|
-
<!-- Le javascript
|
100
|
-
================================================== -->
|
101
|
-
<!-- Placed at the end of the document so the pages load faster -->
|
102
|
-
<script src="/assets/js/bootstrap.min.js"></script>
|
103
|
-
</body>
|
104
|
-
</html>
|
105
|
-
^,
|
106
|
-
:index => %Q^
|
107
|
-
<div class="well">
|
108
|
-
<div class="hero-unit">
|
109
|
-
<h1>Models</h1>
|
110
|
-
<p class="btn-group">
|
111
|
-
<% @models.each do |model| %>
|
112
|
-
<a class="btn btn-large btn-info" href="/<%=model.name.downcase%>/"><%= model.name %></a>
|
113
|
-
<% end %>
|
114
|
-
</p>
|
115
|
-
</div>
|
116
|
-
</div>
|
117
|
-
^
|
118
|
-
}
|
119
|
-
ROUTE_TEMPLATES = {
|
120
|
-
:list => %Q^
|
121
|
-
get '/<%=@model_name.downcase%>/?' do
|
122
|
-
@offset = params["page"] if params["page"] =~ /[0-9]+/
|
123
|
-
@offset ||= 0
|
124
|
-
@db_objects = <%=@model_name%>.all(:limit => 20,:offset => @offset * 20)
|
125
|
-
erb :list_<%=@model_name.downcase%>
|
126
|
-
end
|
127
|
-
^,
|
128
|
-
:new => %q^
|
129
|
-
get '/<%=@model_name.downcase%>/new/?' do
|
130
|
-
erb :new_<%=@model_name.downcase%>
|
131
|
-
end
|
132
|
-
^,
|
133
|
-
:save => %q^
|
134
|
-
post '/<%=@model_name.downcase%>/save/?' do
|
135
|
-
db_object = <%=@model_name%>.new
|
136
|
-
params.each do |key,value|
|
137
|
-
db_object.send("#{key}=",value) if <%=@model_name%>.properties.collect{|x| x.name}.include? key.to_sym
|
138
|
-
end
|
139
|
-
if db_object.save then
|
140
|
-
redirect "/<%=@model_name.downcase%>/#{db_object.id}"
|
141
|
-
else
|
142
|
-
result = []
|
143
|
-
db_object.errors.each do |e|
|
144
|
-
result << "<li>#{e}</li>"
|
145
|
-
end
|
146
|
-
return "<UL>#{result.join("")}</UL>"
|
147
|
-
end
|
148
|
-
end
|
149
|
-
^,
|
150
|
-
:edit => %q^
|
151
|
-
get '/<%=@model_name.downcase%>/:id/?' do |id|
|
152
|
-
pass if "#{id.to_i}" != "#{id}"
|
153
|
-
@db_object = <%=@model_name%>.first(:id => id)
|
154
|
-
return 404 if @db_object.nil?
|
155
|
-
erb :edit_<%=@model_name.downcase%>
|
156
|
-
end
|
157
|
-
|
158
|
-
post '/<%=@model_name.downcase%>/:id/?' do |id|
|
159
|
-
db_object = <%=@model_name%>.first(:id => id)
|
160
|
-
redirect 404 if ! db_object
|
161
|
-
params.each do |key,value|
|
162
|
-
db_object.send("#{key}=",value) if <%=@model_name%>.properties.collect{|x| x.name}.include? key.to_sym
|
163
|
-
end
|
164
|
-
if db_object.save then
|
165
|
-
redirect "/<%=@model_name.downcase%>/#{db_object.id}"
|
166
|
-
else
|
167
|
-
result = []
|
168
|
-
db_object.errors.each do |e|
|
169
|
-
STDERR.puts e
|
170
|
-
result << "<li>#{e}</li>"
|
171
|
-
end
|
172
|
-
return "<UL>#{result.join("")}</UL>"
|
173
|
-
end
|
174
|
-
end
|
175
|
-
^,
|
176
|
-
:delete => %q^
|
177
|
-
get '/<%=@model_name.downcase%>/delete/:id/?' do |id|
|
178
|
-
db_object = <%=@model_name%>.first(:id => id)
|
179
|
-
if db_object then
|
180
|
-
db_object.destroy
|
181
|
-
redirect '/<%=@model_name.downcase%>/'
|
182
|
-
else
|
183
|
-
redirect 404
|
184
|
-
end
|
185
|
-
end
|
186
|
-
^
|
187
|
-
}
|
188
|
-
ERB_TEMPLATES = {
|
189
|
-
:list => %Q^
|
190
|
-
<a href="/<%=@model_name.downcase%>/new" class="btn btn-info">New</a>
|
191
|
-
<table class="table table-bordered">
|
192
|
-
<thead>
|
193
|
-
<% @properties.each do |header| %>
|
194
|
-
<th><%=header.name%></th>
|
195
|
-
<% end %>
|
196
|
-
<th>Actions</th>
|
197
|
-
</thead>
|
198
|
-
<tbody>
|
199
|
-
<%% @db_objects.each do |object| %>
|
200
|
-
<tr>
|
201
|
-
<% @properties.each do |prop| %>
|
202
|
-
<td><%%=object.<%=prop.name%>%></td>
|
203
|
-
<%end%>
|
204
|
-
<td><a href="/<%=@model_name.downcase%>/<%%=object.id%>" class="btn btn-mini">Edit</a>
|
205
|
-
<a href="/<%=@model_name.downcase%>/delete/<%%=object.id%>" class="btn btn-mini">Delete</a></td>
|
206
|
-
</tr>
|
207
|
-
<%%end%>
|
208
|
-
</tbody>
|
209
|
-
</table>
|
210
|
-
^,
|
211
|
-
:new => %q^
|
212
|
-
<form class="well row" method="post" action="/<%=@model_name.downcase%>/save">
|
213
|
-
<% @properties.each do |prop| %>
|
214
|
-
<% next if prop.name == :id %>
|
215
|
-
<label><%=prop.name.to_s%></label>
|
216
|
-
<%=form_type(prop,:name => prop.name)%>
|
217
|
-
|
218
|
-
<% end %>
|
219
|
-
<button type="submit" class="btn">Submit</button>
|
220
|
-
</form>
|
221
|
-
^,
|
222
|
-
:edit => %q^
|
223
|
-
<form class="well" method="post" action="/<%=@model_name.downcase%>/<%%= @db_object.id%>">
|
224
|
-
<% @properties.each do |prop| %>
|
225
|
-
<% next if prop.name == :id %>
|
226
|
-
<label><%=prop.name.to_s%></label>
|
227
|
-
<%=form_type(prop,:name => prop.name,:value => "<%= @db_object.#{prop.name} %%>")%>
|
228
|
-
|
229
|
-
<% end %>
|
230
|
-
<button type="submit" class="btn">Submit</button>
|
231
|
-
</form>
|
232
|
-
^
|
233
|
-
}
|
234
|
-
|
235
|
-
def initialize(opts)
|
236
|
-
@routes = []
|
237
|
-
@views = {}
|
238
|
-
@models = []
|
239
|
-
@opts = opts
|
240
|
-
DataMapper::Model.descendants.entries.each {|entry|
|
241
|
-
@models << entry
|
242
|
-
#@model_name=entry.inspect
|
243
|
-
#@properties=entry.properties
|
244
|
-
|
245
|
-
#puts ERB.new(ERB_TEMPLATES[:new]).result(binding)
|
246
|
-
}
|
247
|
-
@models.delete_if { |model| @opts[:exclude].include? model.name or @opts[:exclude].include? model.name.downcase }
|
248
|
-
@models.select! { |model| @opts[:only].include? model.name or @opts[:only].include? model.name.downcase } unless @opts[:only].nil?
|
249
|
-
end
|
250
|
-
def generate
|
251
|
-
result = Hash.new
|
252
|
-
routes = Hash.new
|
253
|
-
STATIC_ROUTE_TEMPLATES.each { |key,value| next if @opts[:exclude].include? key.to_s; puts "Processing Route: #{key}"; @routes << ERB.new(value,nil,"<>").result(binding) } if @opts[:only].nil?
|
254
|
-
STATIC_ERB_TEMPLATES.each { |key,value| next if @opts[:exclude].include? key.to_s; puts "Processing Template: #{key}";@views["#{key.to_s}.erb"]=ERB.new(value,nil,"<>").result(binding) } if @opts[:only].nil?
|
255
|
-
@models.each { |entry|
|
256
|
-
@model_name = entry.inspect
|
257
|
-
@properties = entry.properties
|
258
|
-
puts "Processing Model: #{@model_name}"
|
259
|
-
ROUTE_TEMPLATES.each {|key,value|
|
260
|
-
@routes << ERB.new(value,nil,"<>").result(binding)
|
261
|
-
}
|
262
|
-
ERB_TEMPLATES.each { |key,value|
|
263
|
-
@views["#{key.to_s}_#{@model_name.downcase}.erb"] = ERB.new(value,nil,"<>").result(binding)
|
264
|
-
}
|
265
|
-
}
|
266
|
-
return true
|
267
|
-
end
|
268
|
-
def routes
|
269
|
-
@routes
|
270
|
-
end
|
271
|
-
def views
|
272
|
-
@views
|
273
|
-
end
|
274
|
-
private
|
275
|
-
def form_type(object,properties)
|
276
|
-
append = String.new
|
277
|
-
properties.each {|key,value|
|
278
|
-
append << " #{key}='#{value}'"
|
279
|
-
}
|
280
|
-
|
281
|
-
case object
|
282
|
-
when DataMapper::Property::Serial
|
283
|
-
""
|
284
|
-
when DataMapper::Property::String
|
285
|
-
"<input type='text' class='span2' #{append} />"
|
286
|
-
when DataMapper::Property::Boolean
|
287
|
-
"<input type='checkbox' #{append}/>"
|
288
|
-
when DataMapper::Property::Text
|
289
|
-
"<textbox class='span3' #{append - append[:value]}>#{append[:value] unless append[:value].nil?}</textbox>"
|
290
|
-
when DataMapper::Property::Float, DataMapper::Property::Integer, DataMapper::Property::Decimal
|
291
|
-
"<input type='text' class='span1' #{append} />"
|
292
|
-
when DateTime, Date, Time
|
293
|
-
"<input type='text' class='span2' #{append} />"
|
294
|
-
else
|
295
|
-
"<input type='text' class='span1' #{append} />"
|
296
|
-
end
|
297
|
-
end
|
80
|
+
case object
|
81
|
+
when DataMapper::Property::Serial # Serial #'s should be generated. Do not create a form for entry for it.
|
82
|
+
""
|
83
|
+
when DataMapper::Property::String
|
84
|
+
"<input type='text' class='span2' #{append} />"
|
85
|
+
when DataMapper::Property::Boolean
|
86
|
+
"<input type='checkbox' #{append}/>"
|
87
|
+
when DataMapper::Property::Text
|
88
|
+
"<textbox class='span3' #{append - append[:value]}>#{append[:value] unless append[:value].nil?}</textbox>"
|
89
|
+
when DataMapper::Property::Float, DataMapper::Property::Integer, DataMapper::Property::Decimal
|
90
|
+
"<input type='text' class='span1' #{append} />"
|
91
|
+
when DateTime, Date, Time
|
92
|
+
"<input type='text' class='span2' #{append} />"
|
93
|
+
else
|
94
|
+
"<input type='text' class='span1' #{append} />"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
298
98
|
end
|
data/lib/cli.rb
ADDED
data/lib/new.rb
ADDED
data/lib/scaffold.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
module Beds
|
2
|
+
module CLI
|
3
|
+
class Scaffold
|
4
|
+
def initialize(options)
|
5
|
+
@options = {
|
6
|
+
:viewdir => "./views",
|
7
|
+
:public => "./public",
|
8
|
+
:controllers => "./controllers",
|
9
|
+
:routefile => "./scaffold.rb",
|
10
|
+
:list => true,
|
11
|
+
:save => true,
|
12
|
+
:template => true,
|
13
|
+
:new => true,
|
14
|
+
:index => true,
|
15
|
+
:run_hooks => true,
|
16
|
+
:exclude => []
|
17
|
+
}; @options[:assets] = "#{@options[:public]}/assets"
|
18
|
+
get_options(options)
|
19
|
+
load_models
|
20
|
+
make_files
|
21
|
+
run_hooks if @options[:run_hooks]
|
22
|
+
end
|
23
|
+
private
|
24
|
+
# Run post-scaffold hooks
|
25
|
+
def run_hooks
|
26
|
+
#begin
|
27
|
+
@scaffold.templates[:hooks].each {|key,hook| puts "Running hook: #{key}"; instance_eval(hook) }
|
28
|
+
#rescue Exception => e
|
29
|
+
# puts "Error running hook #{e.message}"
|
30
|
+
#end
|
31
|
+
end
|
32
|
+
## Load model data from data mapper
|
33
|
+
def load_models
|
34
|
+
begin
|
35
|
+
load "#{@options[:app]}"
|
36
|
+
rescue Exception => e
|
37
|
+
puts "Unable to load models. #{e.message}"
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
@scaffold = Beds::Scaffold.new(@options)
|
41
|
+
@scaffold.generate
|
42
|
+
end
|
43
|
+
|
44
|
+
## Write the generated files to disk.
|
45
|
+
def make_files
|
46
|
+
@gen_files = []
|
47
|
+
[@options[:viewdir],@options[:public],@options[:assets],@options[:controllers]].each {|directory| Dir.mkdir(directory) unless Dir.exist?(directory) }
|
48
|
+
@scaffold.routes.each do |controller,contents|
|
49
|
+
if File.exist?(File.join(@options[:controllers],"#{controller}.rb")) and @options[:force] != true then
|
50
|
+
puts "Error: Controller #{File.join(@options[:controllers],"#{controller}.rb")} already exists. Not writing"
|
51
|
+
else
|
52
|
+
File.open(File.join(@options[:controllers],"#{controller}.rb"),"w") {|file| file.write(contents) }
|
53
|
+
@gen_files << File.join(@options[:controllers],"#{controller}.rb")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@scaffold.views.each {|file,content|
|
58
|
+
if File.exist?(File.join(@options[:viewdir],file)) and ! @options[:force] then
|
59
|
+
puts "Warning: file #{File.join(@options[:viewdir],file)} exists. Not overwriting"
|
60
|
+
else
|
61
|
+
@gen_files << File.join(@options[:viewdir],file)
|
62
|
+
File.open(File.join(@options[:viewdir],file),"w") {|file| file.write(content) }
|
63
|
+
end
|
64
|
+
}
|
65
|
+
|
66
|
+
puts "Generated files: #{@gen_files.join(", ")}"
|
67
|
+
end
|
68
|
+
## Get optoins from CLI input
|
69
|
+
def get_options(options)
|
70
|
+
optparser = OptionParser.new do |opts|
|
71
|
+
opts.banner = " Usage: #{File.basename($0)} scaffold -m <models.rb> [OPTIONS]"
|
72
|
+
opts.on("-m","--models FILE", "Specify file with DataMapper models defined.") do |app|
|
73
|
+
@options[:app] = app
|
74
|
+
end
|
75
|
+
opts.on("-v","--viewdir","Specify which directory to place the views in. Default: ./views/") do |viewdir|
|
76
|
+
@options[:viewdir] = viewdir
|
77
|
+
end
|
78
|
+
opts.on("-r","--routefile", "Sepcify the file to generate the routes in. Default: scaffold.rb") do |routefile|
|
79
|
+
@options[:routefile] = routefile
|
80
|
+
end
|
81
|
+
opts.on("--exclude x,y,z", Array, "Do not generate anything for specified models, or before, index, and/or layout") do |models|
|
82
|
+
@options[:exclude] = models
|
83
|
+
end
|
84
|
+
opts.on("--only x,y,z", Array, "Only process the specified models.") do |models|
|
85
|
+
@options[:only] = models
|
86
|
+
end
|
87
|
+
opts.on("-f","--[no-]force", "Overwtie files") do |force|
|
88
|
+
@options[:force] = force
|
89
|
+
end
|
90
|
+
end
|
91
|
+
begin
|
92
|
+
optparser.parse! options
|
93
|
+
raise OptionParser::InvalidOption, "[NONE]" if @options[:app].nil?
|
94
|
+
rescue OptionParser::InvalidOption => e
|
95
|
+
puts e
|
96
|
+
puts optparser
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<form class="well" method="post" action="/<%=@model_name.downcase%>/<%%= @db_object.id%>">
|
2
|
+
<% @properties.each do |prop| %>
|
3
|
+
<% next if prop.name == :id %>
|
4
|
+
<label><%=prop.name.to_s%></label>
|
5
|
+
<%=form_type(prop,:name => prop.name,:value => "<%= @db_object.#{prop.name} %%>")%>
|
6
|
+
<% end %>
|
7
|
+
<button type="submit" class="btn">Submit</button>
|
8
|
+
</form>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<a href="/<%=@model_name.downcase%>/new" class="btn btn-info">New</a>
|
2
|
+
<table class="table table-bordered">
|
3
|
+
<thead>
|
4
|
+
<% @properties.each do |header| %>
|
5
|
+
<th><%=header.name%></th>
|
6
|
+
<% end %>
|
7
|
+
<th>Actions</th>
|
8
|
+
</thead>
|
9
|
+
<tbody>
|
10
|
+
<%% @db_objects.each do |object| %>
|
11
|
+
<tr>
|
12
|
+
<% @properties.each do |prop| %>
|
13
|
+
<td><%%=object.<%=prop.name%>%></td>
|
14
|
+
<%end%>
|
15
|
+
<td><a href="/<%=@model_name.downcase%>/<%%=object.id%>" class="btn btn-mini">Edit</a>
|
16
|
+
<a href="/<%=@model_name.downcase%>/delete/<%%=object.id%>" class="btn btn-mini">Delete</a></td>
|
17
|
+
</tr>
|
18
|
+
<%%end%>
|
19
|
+
</tbody>
|
20
|
+
</table>
|
21
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<form class="well row" method="post" action="/<%=@model_name.downcase%>/save">
|
2
|
+
<% @properties.each do |prop| %>
|
3
|
+
<% next if prop.name == :id %>
|
4
|
+
<label><%=prop.name.to_s%></label>
|
5
|
+
<%=form_type(prop,:name => prop.name)%>
|
6
|
+
<% end %>
|
7
|
+
<button type="submit" class="btn">Submit</button>
|
8
|
+
</form>
|
9
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'zip/zip'
|
3
|
+
require 'fileutils'
|
4
|
+
BOOTSTRAP_ZIP_URL="http://twitter.github.com/bootstrap/assets/bootstrap.zip"
|
5
|
+
|
6
|
+
File.open("bootstrap.zip","w") {|file| file.write(open(BOOTSTRAP_ZIP_URL).read) }
|
7
|
+
Zip::ZipFile.open('bootstrap.zip') { |zip_file|
|
8
|
+
zip_file.each { |f|
|
9
|
+
f_path=File.join("#{@options[:public]}/assets", f.name)
|
10
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
11
|
+
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
12
|
+
}
|
13
|
+
}
|
14
|
+
File.delete("bootstrap.zip")
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'fileutils'
|
3
|
+
JQUERY_URL="http://code.jquery.com/jquery-1.8.1.min.js"
|
4
|
+
f_path=File.join("#{@options[:public]}/assets/js")
|
5
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
6
|
+
File.open("#{f_path}/jquery.min.js") { |file| file.write(open(JQUERY_URL).read) }
|
7
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
get '/<%=@model_name.downcase%>/:id/?' do |id|
|
2
|
+
pass if "#{id.to_i}" != "#{id}"
|
3
|
+
@db_object = <%=@model_name%>.first(:id => id)
|
4
|
+
return 404 if @db_object.nil?
|
5
|
+
erb :edit_<%=@model_name.downcase%>
|
6
|
+
end
|
7
|
+
|
8
|
+
post '/<%=@model_name.downcase%>/:id/?' do |id|
|
9
|
+
db_object = <%=@model_name%>.first(:id => id)
|
10
|
+
pass if ! db_object
|
11
|
+
params.each do |key,value|
|
12
|
+
db_object.send("#{key}=",value) if <%=@model_name%>.properties.collect{|x| x.name}.include? key.to_sym
|
13
|
+
end
|
14
|
+
if db_object.save then
|
15
|
+
redirect "/<%=@model_name.downcase%>/#{db_object.id}"
|
16
|
+
else
|
17
|
+
result = []
|
18
|
+
db_object.errors.each do |e|
|
19
|
+
STDERR.puts e
|
20
|
+
result << "<li>#{e}</li>"
|
21
|
+
end
|
22
|
+
return "<UL>#{result.join("")}</UL>"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
post '/<%=@model_name.downcase%>/save' do
|
2
|
+
db_object = <%=@model_name%>.new
|
3
|
+
params.each do |key,value|
|
4
|
+
db_object.send("#{key}=",value) if <%=@model_name%>.properties.collect{|x| x.name}.include? key.to_sym
|
5
|
+
end
|
6
|
+
if db_object.save! then
|
7
|
+
redirect "/<%=@model_name.downcase%>/#{db_object.id}"
|
8
|
+
else
|
9
|
+
result = []
|
10
|
+
db_object.errors.each do |e|
|
11
|
+
result << "Error:<li>#{e}</li>"
|
12
|
+
end
|
13
|
+
return "<UL>#{result.join("")}</UL>"
|
14
|
+
end
|
15
|
+
return "YAY"
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<title>Application</title>
|
6
|
+
<meta name="GeneratedBy" content="sdbscaffold"
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
8
|
+
<meta name="description" content="">
|
9
|
+
<meta name="author" content="">
|
10
|
+
<!-- Le styles -->
|
11
|
+
|
12
|
+
<link href="/assets/bootstrap/css/bootstrap.css" rel="stylesheet">
|
13
|
+
<link href="/style.css" rel="stylesheet">
|
14
|
+
<style>
|
15
|
+
body {
|
16
|
+
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
|
17
|
+
}
|
18
|
+
</style>
|
19
|
+
|
20
|
+
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
|
21
|
+
<!--[if lt IE 9]>
|
22
|
+
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
23
|
+
<![endif]-->
|
24
|
+
<script src="/assets/bootstrap/js/jquery.js" type="text/javascript"></script>
|
25
|
+
<script>
|
26
|
+
$(document).ready(function() {
|
27
|
+
$("#<%%=@active_page%>").addClass("active");
|
28
|
+
});
|
29
|
+
</script>
|
30
|
+
<!-- Le fav and touch icons -->
|
31
|
+
<link rel="shortcut icon" href="/assets/ico/favicon.ico">
|
32
|
+
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/assets/ico/apple-touch-icon-144-precomposed.png">
|
33
|
+
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/assets/ico/apple-touch-icon-114-precomposed.png">
|
34
|
+
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/assets/ico/apple-touch-icon-72-precomposed.png">
|
35
|
+
<link rel="apple-touch-icon-precomposed" href="/assets/ico/apple-touch-icon-57-precomposed.png">
|
36
|
+
</head>
|
37
|
+
|
38
|
+
<body>
|
39
|
+
|
40
|
+
<div class="navbar navbar-fixed-top">
|
41
|
+
<div class="navbar-inner">
|
42
|
+
<div class="container">
|
43
|
+
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
44
|
+
<span class="icon-bar"></span>
|
45
|
+
<span class="icon-bar"></span>
|
46
|
+
<span class="icon-bar"></span>
|
47
|
+
</a>
|
48
|
+
<a class="brand" href="#">YourApp</a>
|
49
|
+
<div class="nav-collapse">
|
50
|
+
<ul class="nav">
|
51
|
+
<li class="divider-vertical"></li>
|
52
|
+
<li id="index" ><a href="/">Home</a></li>
|
53
|
+
<% @models.each do |model| %>
|
54
|
+
<li id="<%=model.name.downcase%>" ><a href="/<%=model.name.downcase%>/"><%= model.name %></a></li>
|
55
|
+
<% end %>
|
56
|
+
</ul>
|
57
|
+
<ul class="nav pull-right">
|
58
|
+
<li class="divider-vertical"></li>
|
59
|
+
<li><a href="/login">Log<%%= session[:valid] ? "out" : "in"%></a></li>
|
60
|
+
</ul>
|
61
|
+
</div><!--/.nav-collapse -->
|
62
|
+
</div>
|
63
|
+
</div>
|
64
|
+
</div>
|
65
|
+
|
66
|
+
<div class="container">
|
67
|
+
<%% if ! @flash.nil? then %>
|
68
|
+
<span class="span9 offset1">
|
69
|
+
<%% @flash.each do |alert| %>
|
70
|
+
<div class="alert alert-<%%=alert[:level].downcase%>" style="margin:0px; margin-bottom:8px;">
|
71
|
+
<button class="close" data-dismiss="alert">x</button>
|
72
|
+
<strong><%%=alert[:level].capitalize%>!</strong> <%%=alert[:message]%>
|
73
|
+
</div>
|
74
|
+
<%% end %>
|
75
|
+
</span>
|
76
|
+
<%%end %>
|
77
|
+
<%%= yield %>
|
78
|
+
</div> <!-- /container -->
|
79
|
+
|
80
|
+
<!-- Le javascript
|
81
|
+
================================================== -->
|
82
|
+
<!-- Placed at the end of the document so the pages load faster -->
|
83
|
+
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
|
84
|
+
</body>
|
85
|
+
</html>
|
86
|
+
|
metadata
CHANGED
@@ -1,65 +1,100 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: beds
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
4
5
|
prerelease:
|
5
|
-
version: 0.0.2
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- James Paterni
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
date: 2012-09-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sinatra
|
16
|
+
requirement: &2156320300 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.3.3
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2156320300
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rubyzip
|
27
|
+
requirement: &2156319800 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.9.9
|
33
|
+
type: :runtime
|
17
34
|
prerelease: false
|
18
|
-
|
35
|
+
version_requirements: *2156319800
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &2156319380 !ruby/object:Gem::Requirement
|
19
39
|
none: false
|
20
|
-
requirements:
|
21
|
-
- -
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version:
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
24
44
|
type: :development
|
25
|
-
|
26
|
-
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2156319380
|
47
|
+
description: Easily create Sinatra applications quikcly and easily by scaffolding
|
48
|
+
your models.
|
27
49
|
email: james@ruby-code.com
|
28
|
-
executables:
|
50
|
+
executables:
|
29
51
|
- beds
|
30
52
|
extensions: []
|
31
|
-
|
32
53
|
extra_rdoc_files: []
|
33
|
-
|
34
|
-
|
35
|
-
- lib/
|
54
|
+
files:
|
55
|
+
- ./lib/beds.rb
|
56
|
+
- ./lib/cli.rb
|
57
|
+
- ./lib/new.rb
|
58
|
+
- ./lib/scaffold.rb
|
59
|
+
- ./templates/erb/edit.erb
|
60
|
+
- ./templates/erb/list.erb
|
61
|
+
- ./templates/erb/new.erb
|
62
|
+
- ./templates/hooks/bootstrap.rb
|
63
|
+
- ./templates/hooks/jquery.rb
|
64
|
+
- ./templates/routes/delete.erb
|
65
|
+
- ./templates/routes/edit.erb
|
66
|
+
- ./templates/routes/list.erb
|
67
|
+
- ./templates/routes/new.erb
|
68
|
+
- ./templates/routes/save.erb
|
69
|
+
- ./templates/static_erb/index.erb
|
70
|
+
- ./templates/static_erb/layout.erb
|
71
|
+
- ./templates/static_routes/before.erb
|
72
|
+
- ./templates/static_routes/index.erb
|
36
73
|
- bin/beds
|
37
|
-
homepage:
|
74
|
+
homepage: http://github.com/jamez01/beds
|
38
75
|
licenses: []
|
39
|
-
|
40
76
|
post_install_message:
|
41
77
|
rdoc_options: []
|
42
|
-
|
43
|
-
require_paths:
|
78
|
+
require_paths:
|
44
79
|
- lib
|
45
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
81
|
none: false
|
47
|
-
requirements:
|
48
|
-
- -
|
49
|
-
- !ruby/object:Gem::Version
|
50
|
-
version:
|
51
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
87
|
none: false
|
53
|
-
requirements:
|
54
|
-
- -
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version:
|
57
|
-
requirements:
|
58
|
-
|
59
|
-
|
88
|
+
requirements:
|
89
|
+
- - ! '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements:
|
93
|
+
- sinatra, version 1.3.3
|
94
|
+
- rubyzip, version 0.9.9
|
95
|
+
rubyforge_project:
|
60
96
|
rubygems_version: 1.8.15
|
61
97
|
signing_key:
|
62
98
|
specification_version: 3
|
63
99
|
summary: The BootStrap ERb DataMapper Sinatra [scaffolder]
|
64
100
|
test_files: []
|
65
|
-
|