doozer 0.2.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/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +57 -0
- data/Rakefile +62 -0
- data/VERSION +1 -0
- data/bin/doozer +6 -0
- data/doozer.gemspec +156 -0
- data/lib/doozer.rb +35 -0
- data/lib/doozer/active_support/array.rb +14 -0
- data/lib/doozer/active_support/class.rb +221 -0
- data/lib/doozer/active_support/date_time.rb +23 -0
- data/lib/doozer/active_support/object.rb +43 -0
- data/lib/doozer/active_support/time.rb +32 -0
- data/lib/doozer/app.rb +294 -0
- data/lib/doozer/configs.rb +146 -0
- data/lib/doozer/controller.rb +340 -0
- data/lib/doozer/exceptions.rb +12 -0
- data/lib/doozer/extend.rb +10 -0
- data/lib/doozer/initializer.rb +104 -0
- data/lib/doozer/lib.rb +32 -0
- data/lib/doozer/logger.rb +12 -0
- data/lib/doozer/orm/active_record.rb +28 -0
- data/lib/doozer/orm/data_mapper.rb +22 -0
- data/lib/doozer/orm/sequel.rb +21 -0
- data/lib/doozer/partial.rb +99 -0
- data/lib/doozer/plugins/paginate/init.rb +2 -0
- data/lib/doozer/plugins/paginate/lib/paginate.rb +32 -0
- data/lib/doozer/plugins/paginate/lib/paginate/collection.rb +60 -0
- data/lib/doozer/plugins/paginate/lib/paginate/finder.rb +116 -0
- data/lib/doozer/plugins/paginate/lib/paginate/view_helpers.rb +37 -0
- data/lib/doozer/rackup/server.ru +35 -0
- data/lib/doozer/rackup/test.rb +20 -0
- data/lib/doozer/redirect.rb +12 -0
- data/lib/doozer/route.rb +292 -0
- data/lib/doozer/scripts/cluster.rb +126 -0
- data/lib/doozer/scripts/console.rb +2 -0
- data/lib/doozer/scripts/migrate.rb +108 -0
- data/lib/doozer/scripts/task.rb +60 -0
- data/lib/doozer/scripts/test.rb +23 -0
- data/lib/doozer/version.rb +8 -0
- data/lib/doozer/view_helpers.rb +251 -0
- data/lib/doozer/watcher.rb +369 -0
- data/lib/generator/generator.rb +548 -0
- data/templates/skeleton/Rakefile +3 -0
- data/templates/skeleton/app/controllers/application_controller.rb +2 -0
- data/templates/skeleton/app/controllers/index_controller.rb +7 -0
- data/templates/skeleton/app/helpers/application_helper.rb +17 -0
- data/templates/skeleton/app/views/global/_header.html.erb +7 -0
- data/templates/skeleton/app/views/global/_navigation.html.erb +6 -0
- data/templates/skeleton/app/views/index/index.html.erb +108 -0
- data/templates/skeleton/app/views/layouts/default.html.erb +23 -0
- data/templates/skeleton/config/app.yml +31 -0
- data/templates/skeleton/config/boot.rb +17 -0
- data/templates/skeleton/config/database.yml +25 -0
- data/templates/skeleton/config/environment.rb +11 -0
- data/templates/skeleton/config/rack.rb +30 -0
- data/templates/skeleton/config/routes.rb +69 -0
- data/templates/skeleton/script/cluster +4 -0
- data/templates/skeleton/script/console +15 -0
- data/templates/skeleton/script/migrate +4 -0
- data/templates/skeleton/script/task +4 -0
- data/templates/skeleton/script/test +4 -0
- data/templates/skeleton/static/404.html +16 -0
- data/templates/skeleton/static/500.html +16 -0
- data/templates/skeleton/static/css/style.css +32 -0
- data/templates/skeleton/static/favicon.ico +0 -0
- data/templates/skeleton/static/js/application.js +1 -0
- data/templates/skeleton/static/js/jquery-1.3.min.js +19 -0
- data/templates/skeleton/static/robots.txt +5 -0
- data/templates/skeleton/test/fixtures/setup.rb +6 -0
- data/templates/skeleton/test/setup.rb +33 -0
- data/test/doozer_test.rb +7 -0
- data/test/project/Rakefile +3 -0
- data/test/project/app/controllers/application_controller.rb +2 -0
- data/test/project/app/controllers/index_controller.rb +7 -0
- data/test/project/app/helpers/application_helper.rb +17 -0
- data/test/project/app/views/global/_header.html.erb +7 -0
- data/test/project/app/views/global/_navigation.html.erb +6 -0
- data/test/project/app/views/index/index.html.erb +108 -0
- data/test/project/app/views/layouts/default.html.erb +23 -0
- data/test/project/config/app.yml +31 -0
- data/test/project/config/boot.rb +17 -0
- data/test/project/config/database.yml +25 -0
- data/test/project/config/environment.rb +11 -0
- data/test/project/config/rack.rb +30 -0
- data/test/project/config/routes.rb +72 -0
- data/test/project/script/cluster +4 -0
- data/test/project/script/console +15 -0
- data/test/project/script/migrate +4 -0
- data/test/project/script/task +4 -0
- data/test/project/script/test +4 -0
- data/test/project/static/404.html +16 -0
- data/test/project/static/500.html +16 -0
- data/test/project/static/css/style.css +32 -0
- data/test/project/static/favicon.ico +0 -0
- data/test/project/static/js/application.js +1 -0
- data/test/project/static/js/jquery-1.3.min.js +19 -0
- data/test/project/static/robots.txt +5 -0
- data/test/project/test/fixtures/setup.rb +6 -0
- data/test/project/test/setup.rb +33 -0
- data/test/routing_test.rb +66 -0
- data/test/test_helper.rb +26 -0
- metadata +169 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
## taken from: http://paulhorman.com/filesystemwatcher/
|
|
2
|
+
require "md5"
|
|
3
|
+
require "thread"
|
|
4
|
+
|
|
5
|
+
# The Runnable module is a generic mixin for including state and
|
|
6
|
+
# status information in a class
|
|
7
|
+
module ServiceState
|
|
8
|
+
# state constants
|
|
9
|
+
NOT_STARTED = 0
|
|
10
|
+
STARTED = 1
|
|
11
|
+
STOPPED = 2
|
|
12
|
+
CONFIGURED = 3
|
|
13
|
+
|
|
14
|
+
attr_reader :startTime, :endTime
|
|
15
|
+
|
|
16
|
+
# Initialize the state information
|
|
17
|
+
def initializeState()
|
|
18
|
+
@configured = false
|
|
19
|
+
@startTime = 0
|
|
20
|
+
@stopTime = 0
|
|
21
|
+
|
|
22
|
+
@stateMutex = Mutex.new()
|
|
23
|
+
@stopWhen = nil
|
|
24
|
+
setState(NOT_STARTED)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Set the callback for when someone calls setState. You
|
|
28
|
+
# will be passed the state CONSTANT being set
|
|
29
|
+
def onStateChange(&callbackBlock)
|
|
30
|
+
@stateCallback = callbackBlock
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# All methods, inside this class or not, should use this
|
|
34
|
+
# method to change the state of the JobRunner
|
|
35
|
+
# @param newState The new state value
|
|
36
|
+
def setState(newState)
|
|
37
|
+
@stateMutex.synchronize {
|
|
38
|
+
if newState == CONFIGURED then
|
|
39
|
+
@configured = true
|
|
40
|
+
else
|
|
41
|
+
@state = newState
|
|
42
|
+
if isStarted? then
|
|
43
|
+
@startTime = Time.now()
|
|
44
|
+
elsif isStopped?
|
|
45
|
+
@stopTime = Time.now()
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
}
|
|
49
|
+
if defined?(@stateCallback) then
|
|
50
|
+
@stateCallback.call(newState)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def isConfigured?
|
|
55
|
+
return @configured
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def isStarted?
|
|
59
|
+
return @state == STARTED
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def isStopped?
|
|
63
|
+
if @state == STOPPED then
|
|
64
|
+
return true
|
|
65
|
+
elsif @stopWhen && @stopWhen.call() then
|
|
66
|
+
setState(STOPPED)
|
|
67
|
+
return true
|
|
68
|
+
else
|
|
69
|
+
return false
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def stopWhen(&block)
|
|
74
|
+
@stopWhen = block
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# This class will watch a directory or a set of directories and alert you of
|
|
80
|
+
# new files, modified files, deleted files. You can optionally only be alerted
|
|
81
|
+
# when a files md5 hash has been changed so you only are alerted to real changes.
|
|
82
|
+
# this of course means slower performance and higher cpu/io usage.
|
|
83
|
+
class FileSystemWatcher
|
|
84
|
+
include ServiceState
|
|
85
|
+
|
|
86
|
+
CREATED = 0
|
|
87
|
+
MODIFIED = 1
|
|
88
|
+
DELETED = 2
|
|
89
|
+
|
|
90
|
+
# the time to wait before checking the directories again
|
|
91
|
+
attr_accessor :sleepTime, :priority, :mongrel
|
|
92
|
+
|
|
93
|
+
# you can optionally use the file contents md5 to detect if a file has changed
|
|
94
|
+
attr_accessor :useMD5
|
|
95
|
+
|
|
96
|
+
attr_accessor :foundFiles, :files, :directories
|
|
97
|
+
|
|
98
|
+
def initialize(dir=nil, expression="**/*")
|
|
99
|
+
@sleepTime = 5
|
|
100
|
+
@useMD5 = false
|
|
101
|
+
@priority = 0
|
|
102
|
+
@stopWhen = nil
|
|
103
|
+
|
|
104
|
+
@directories = Array.new()
|
|
105
|
+
@files = Array.new()
|
|
106
|
+
|
|
107
|
+
@foundFiles = nil
|
|
108
|
+
@firstLoad = true
|
|
109
|
+
@watchThread = nil
|
|
110
|
+
|
|
111
|
+
initializeState()
|
|
112
|
+
|
|
113
|
+
if dir then
|
|
114
|
+
addDirectory(dir, expression)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# add a directory to be watched
|
|
119
|
+
# @param dir the directory to watch
|
|
120
|
+
# @param expression the glob pattern to search under the watched directory
|
|
121
|
+
def addDirectory(dir, expression="**/*")
|
|
122
|
+
if FileTest.exists?(dir) && FileTest.readable?(dir) then
|
|
123
|
+
@directories << FSWatcher::Directory.new(dir, expression)
|
|
124
|
+
else
|
|
125
|
+
raise FSWatcher::InvalidDirectoryError, "Dir '#{dir}' either doesnt exist or isnt readable"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def removeDirectory(dir)
|
|
130
|
+
@directories.delete(dir)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# add a specific file to the watch list
|
|
134
|
+
# @param file the file to watch
|
|
135
|
+
def addFile(file)
|
|
136
|
+
if FileTest.exists?(file) && FileTest.readable?(file) then
|
|
137
|
+
@files << file
|
|
138
|
+
else
|
|
139
|
+
raise FSWatcher::InvalidFileError, "File '#{file}' either doesnt exist or isnt readable"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def removeFile(file)
|
|
144
|
+
@files.delete(file)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# start watching the specified files/directories
|
|
148
|
+
def start(&block)
|
|
149
|
+
if isStarted? then
|
|
150
|
+
raise RuntimeError, "already started"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
setState(STARTED)
|
|
154
|
+
|
|
155
|
+
@firstLoad = true
|
|
156
|
+
@foundFiles = Hash.new()
|
|
157
|
+
|
|
158
|
+
# we watch in a new thread
|
|
159
|
+
@watchThread = Thread.new {
|
|
160
|
+
# we will be stopped if someone calls stop or if someone set a stopWhen that becomes true
|
|
161
|
+
while !isStopped? do
|
|
162
|
+
if (!@directories.empty?) or (!@files.empty?) then
|
|
163
|
+
# this will hold the list of the files we looked at this iteration
|
|
164
|
+
# allows us to not look at the same file again and also to compare
|
|
165
|
+
# with the foundFile list to see if something was deleted
|
|
166
|
+
alreadyExamined = Hash.new()
|
|
167
|
+
|
|
168
|
+
# check the files in each watched directory
|
|
169
|
+
if not @directories.empty? then
|
|
170
|
+
@directories.each { | dirObj |
|
|
171
|
+
examineFileList(dirObj.getFiles(), alreadyExamined, &block)
|
|
172
|
+
}
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# now examine any files the user wants to specifically watch
|
|
176
|
+
examineFileList(@files, alreadyExamined, &block) if not @files.empty?
|
|
177
|
+
|
|
178
|
+
# see if we have to delete files from our found list
|
|
179
|
+
if not @firstLoad then
|
|
180
|
+
if not @foundFiles.empty? then
|
|
181
|
+
# now diff the found files and the examined files to see if
|
|
182
|
+
# something has been deleted
|
|
183
|
+
allFoundFiles = @foundFiles.keys()
|
|
184
|
+
allExaminedFiles = alreadyExamined.keys()
|
|
185
|
+
intersection = allFoundFiles - allExaminedFiles
|
|
186
|
+
intersection.each { |fileName|
|
|
187
|
+
# callback
|
|
188
|
+
block.call(DELETED, fileName)
|
|
189
|
+
# remove deleted file from the foundFiles list
|
|
190
|
+
@foundFiles.delete(fileName)
|
|
191
|
+
}
|
|
192
|
+
end
|
|
193
|
+
else
|
|
194
|
+
@firstLoad = false
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# go to sleep
|
|
199
|
+
sleep(@sleepTime)
|
|
200
|
+
end
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# set the watch thread priority
|
|
204
|
+
@watchThread.priority = @priority
|
|
205
|
+
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# kill the filewatcher thread
|
|
209
|
+
def stop()
|
|
210
|
+
setState(STOPPED)
|
|
211
|
+
@watchThread.wakeup()
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# wait for the filewatcher to finish
|
|
215
|
+
def join()
|
|
216
|
+
@watchThread.join() if @watchThread
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
private
|
|
221
|
+
|
|
222
|
+
# loops over the file list check for new or modified files
|
|
223
|
+
def examineFileList(fileList, alreadyExamined, &block)
|
|
224
|
+
fileList.each { |fileName|
|
|
225
|
+
# expand the file name to the fully qual path
|
|
226
|
+
fullFileName = File.expand_path(fileName)
|
|
227
|
+
|
|
228
|
+
# dont examine the same file 2 times
|
|
229
|
+
if not alreadyExamined.has_key?(fullFileName) then
|
|
230
|
+
# we cant do much if the file isnt readable anyway
|
|
231
|
+
if File.readable?(fullFileName) then
|
|
232
|
+
# set that we have seen this file
|
|
233
|
+
alreadyExamined[fullFileName] = true
|
|
234
|
+
|
|
235
|
+
# get the file info
|
|
236
|
+
modTime, size = File.mtime(fullFileName), File.size(fullFileName)
|
|
237
|
+
|
|
238
|
+
# on the first iteration just load all of the files into the foundList
|
|
239
|
+
if @firstLoad then
|
|
240
|
+
@foundFiles[fullFileName] = FSWatcher::FoundFile.new(fullFileName, modTime, size, false, @useMD5)
|
|
241
|
+
else
|
|
242
|
+
# see if we have found this file already
|
|
243
|
+
foundFile = @foundFiles[fullFileName]
|
|
244
|
+
|
|
245
|
+
if foundFile then
|
|
246
|
+
# if a file is marked as new, we still need to make sure it isnt still
|
|
247
|
+
# being written to. we do this by checking the file sizes.
|
|
248
|
+
|
|
249
|
+
if foundFile.isNew? then
|
|
250
|
+
# if the file size is the same then it is probably done being written to
|
|
251
|
+
# unless the writer is really slow
|
|
252
|
+
if size == foundFile.size then
|
|
253
|
+
# callback
|
|
254
|
+
block.call(CREATED, fullFileName)
|
|
255
|
+
|
|
256
|
+
# mark this file as a changed file now
|
|
257
|
+
foundFile.updateModTime(modTime)
|
|
258
|
+
|
|
259
|
+
# generate the md5 for the file since we know it is done
|
|
260
|
+
# being written to
|
|
261
|
+
foundFile.genMD5() if @useMD5
|
|
262
|
+
else
|
|
263
|
+
# just update the size so we can check again at the next iteration
|
|
264
|
+
foundFile.updateSize(size)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
elsif modTime > foundFile.modTime then
|
|
268
|
+
|
|
269
|
+
# if the mod times are different on files we already have
|
|
270
|
+
# found this is an update
|
|
271
|
+
willYield = true
|
|
272
|
+
|
|
273
|
+
# if we are using md5's then compare them
|
|
274
|
+
if @useMD5 then
|
|
275
|
+
filesMD5 = FSWatcher.genFileMD5(fullFileName)
|
|
276
|
+
if filesMD5 && foundFile.md5 then
|
|
277
|
+
if filesMD5.to_s == foundFile.md5.to_s then
|
|
278
|
+
willYield = false
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
# if we are yielding then the md5s are dif so
|
|
282
|
+
# update the cached md5 value
|
|
283
|
+
foundFile.setMD5(filesMD5) if willYield
|
|
284
|
+
end
|
|
285
|
+
block.call(MODIFIED, fullFileName) if willYield
|
|
286
|
+
foundFile.updateModTime(modTime)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
else
|
|
290
|
+
# this is a new file for our list. dont update the md5 here since
|
|
291
|
+
# the file might not yet be done being written to
|
|
292
|
+
@foundFiles[fullFileName] = FSWatcher::FoundFile.new(fullFileName, modTime, size)
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
}
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Util classes for the FileSystemWatcher
|
|
302
|
+
module FSWatcher
|
|
303
|
+
# The directory to watch
|
|
304
|
+
class Directory
|
|
305
|
+
attr_reader :dir, :expression
|
|
306
|
+
|
|
307
|
+
def initialize(dir, expression)
|
|
308
|
+
@dir, @expression = dir, expression
|
|
309
|
+
@dir.chop! if @dir =~ %r{/$}
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def getFiles()
|
|
313
|
+
return Dir[@dir + "/" + @expression]
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# A FoundFile entry for the FileSystemWatcher
|
|
318
|
+
class FoundFile
|
|
319
|
+
attr_reader :status, :fileName, :modTime, :size, :md5
|
|
320
|
+
|
|
321
|
+
def initialize(fileName, modTime, size, isNewFile=true, useMD5=false)
|
|
322
|
+
@fileName, @modTime, @size, @isNewFile = fileName, modTime, size, isNewFile
|
|
323
|
+
@md5 = nil
|
|
324
|
+
if useMD5 then
|
|
325
|
+
genMD5()
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def updateModTime(modTime)
|
|
330
|
+
@modTime = modTime
|
|
331
|
+
@isNewFile = false
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def updateSize(size)
|
|
335
|
+
@size = size
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def isNew?
|
|
339
|
+
return @isNewFile
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def setMD5(newMD5)
|
|
343
|
+
@md5 = newMD5
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# generate my files md5 value
|
|
347
|
+
def genMD5()
|
|
348
|
+
@md5 = FSWatcher.genFileMD5(@fileName)
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# utility function for generating md5s from a files contents
|
|
353
|
+
def FSWatcher.genFileMD5(fileName)
|
|
354
|
+
if FileTest.file?(fileName) then
|
|
355
|
+
f = File.open(fileName)
|
|
356
|
+
contents = f.read()
|
|
357
|
+
f.close()
|
|
358
|
+
return MD5.new(contents) if contents
|
|
359
|
+
end
|
|
360
|
+
return nil
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# if the directory you want to watch doesnt exist or isnt readable this is thrown
|
|
364
|
+
class InvalidDirectoryError < StandardError; end
|
|
365
|
+
|
|
366
|
+
# if the file you want to watch doesnt exist or isnt readable this is thrown
|
|
367
|
+
class InvalidFileError < StandardError; end
|
|
368
|
+
end
|
|
369
|
+
|
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
module Doozer
|
|
2
|
+
|
|
3
|
+
#= Generate Project Skeletons and Files
|
|
4
|
+
class Generator
|
|
5
|
+
APP_PATH = Dir.pwd
|
|
6
|
+
PATH = File.expand_path( File.join(File.dirname(__FILE__), '..', '..', 'templates') )
|
|
7
|
+
|
|
8
|
+
def self.run(*args)
|
|
9
|
+
return help_all if args.empty?
|
|
10
|
+
|
|
11
|
+
action = args[0].downcase
|
|
12
|
+
if not ['generate', '-h', '--help', '-v', '--version'].include?(action)
|
|
13
|
+
skeleton(action)
|
|
14
|
+
else
|
|
15
|
+
if action == 'generate'
|
|
16
|
+
|
|
17
|
+
action = args[1]
|
|
18
|
+
case action.to_sym
|
|
19
|
+
when :model, :"-M"
|
|
20
|
+
if args.length == 3
|
|
21
|
+
name = args[2]
|
|
22
|
+
Doozer::Configs.load(:development)
|
|
23
|
+
model(Doozer::Configs.orm, name.downcase)
|
|
24
|
+
else
|
|
25
|
+
help?(:help, :model)
|
|
26
|
+
end
|
|
27
|
+
when :view, :"-V"
|
|
28
|
+
args.push('html') if args.length == 3
|
|
29
|
+
if args.length == 4
|
|
30
|
+
name = args[2]
|
|
31
|
+
formats = args[3].split(',')
|
|
32
|
+
view(name.downcase, formats)
|
|
33
|
+
else
|
|
34
|
+
help?(:help, :view)
|
|
35
|
+
end
|
|
36
|
+
when :controller, :"-C"
|
|
37
|
+
if args.length == 3
|
|
38
|
+
name = args[2]
|
|
39
|
+
controller(name.downcase)
|
|
40
|
+
else
|
|
41
|
+
help?(:help, :controller)
|
|
42
|
+
end
|
|
43
|
+
when :helper, :"-H"
|
|
44
|
+
if args.length == 3
|
|
45
|
+
name = args[2]
|
|
46
|
+
helper(name.downcase)
|
|
47
|
+
else
|
|
48
|
+
help?(:help, :helper)
|
|
49
|
+
end
|
|
50
|
+
when :db, :migrate, :migration, :"-D"
|
|
51
|
+
if args.length == 3
|
|
52
|
+
name = args[2]
|
|
53
|
+
Doozer::Configs.load(:development)
|
|
54
|
+
migrate(Doozer::Configs.orm, name.downcase)
|
|
55
|
+
else
|
|
56
|
+
help?(:help, :migrate)
|
|
57
|
+
end
|
|
58
|
+
when :task, :"-T"
|
|
59
|
+
if args.length == 3
|
|
60
|
+
name = args[2]
|
|
61
|
+
task(name.downcase)
|
|
62
|
+
else
|
|
63
|
+
help?(:help, :task)
|
|
64
|
+
end
|
|
65
|
+
else
|
|
66
|
+
help_all
|
|
67
|
+
end
|
|
68
|
+
elsif ['-v', '--version'].include?(action)
|
|
69
|
+
p "Doozer #{Doozer::Version::STRING}"
|
|
70
|
+
else
|
|
71
|
+
help_all
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.help_all
|
|
77
|
+
printf "Doozer Version: #{Doozer::Version::STRING}\n"
|
|
78
|
+
printf "Doozer commands:\n"
|
|
79
|
+
help(:project)
|
|
80
|
+
help(:model)
|
|
81
|
+
help(:view)
|
|
82
|
+
help(:controller)
|
|
83
|
+
help(:helper)
|
|
84
|
+
help(:migrate)
|
|
85
|
+
help(:task)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def self.controller(name)
|
|
89
|
+
return if help?(name, :controller)
|
|
90
|
+
printf "Generating File(s)..."
|
|
91
|
+
path = "#{APP_PATH}/app/controllers/#{name.downcase}_controller.rb"
|
|
92
|
+
if not File.exist?(path)
|
|
93
|
+
p "-- Generating Controller: #{path}"
|
|
94
|
+
file = File.new(path, "w+")
|
|
95
|
+
if file
|
|
96
|
+
template = "class #{Doozer::Lib.classify(name)}Controller < ApplicationController\nend"
|
|
97
|
+
file.syswrite(template)
|
|
98
|
+
#make the view directory for this controller
|
|
99
|
+
path = "#{APP_PATH}/app/views/#{name.downcase}"
|
|
100
|
+
if not File.exist?(path)
|
|
101
|
+
p "-- Generating View Folder: #{path}"
|
|
102
|
+
FileUtils.mkdir path
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
p "Unable to open file!"
|
|
106
|
+
end
|
|
107
|
+
else
|
|
108
|
+
p "-- Skipping: #{path} (already exists)"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def self.model(orm, name)
|
|
113
|
+
return if help?(name, :model)
|
|
114
|
+
raise "No ORM is defined. Please set this in database.yml" if orm.nil?
|
|
115
|
+
p "Loaded ORM: #{orm}"
|
|
116
|
+
path = "#{APP_PATH}/app/models/#{name}.rb"
|
|
117
|
+
if not File.exist?(path)
|
|
118
|
+
p "-- Generating Model: #{path}"
|
|
119
|
+
file = File.new(path, "w+")
|
|
120
|
+
if file
|
|
121
|
+
template = eval("model_#{orm}('#{name}')")
|
|
122
|
+
file.syswrite(template)
|
|
123
|
+
else
|
|
124
|
+
p "Unable to open file!"
|
|
125
|
+
end
|
|
126
|
+
else
|
|
127
|
+
p "-- Skipping: #{path} (already exists)"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
def self.model_active_record(name)
|
|
131
|
+
klass=Doozer::Lib.classify(name)
|
|
132
|
+
return """#= #{klass}
|
|
133
|
+
class #{klass} < ActiveRecord::Base
|
|
134
|
+
end
|
|
135
|
+
"""
|
|
136
|
+
end
|
|
137
|
+
def self.model_data_mapper(name)
|
|
138
|
+
klass = Doozer::Lib.classify(name)
|
|
139
|
+
return """#= #{klass}
|
|
140
|
+
class #{klass}
|
|
141
|
+
include DataMapper::Resource
|
|
142
|
+
property :id, Serial
|
|
143
|
+
end
|
|
144
|
+
"""
|
|
145
|
+
end
|
|
146
|
+
def self.model_sequel(name)
|
|
147
|
+
klass = Doozer::Lib.classify(name)
|
|
148
|
+
return """ #see http://sequel.rubyforge.org/rdoc/index.html
|
|
149
|
+
#= #{klass}
|
|
150
|
+
class #{klass} < Sequel::Model
|
|
151
|
+
end
|
|
152
|
+
"""
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def self.view(view, formats)
|
|
156
|
+
return if help?(view, :view)
|
|
157
|
+
printf "Generating View File(s)..."
|
|
158
|
+
raise "Not sure which controller to associate this view with. Needs to be controller_name/action_name. Example: index/login" if not view.index('/')
|
|
159
|
+
formats.each{|f|
|
|
160
|
+
file="#{APP_PATH}/app/views/#{view}.#{f.strip}.erb"
|
|
161
|
+
if not File.exist?(file)
|
|
162
|
+
p "-- Generating: #{file}"
|
|
163
|
+
FileUtils.touch file
|
|
164
|
+
else
|
|
165
|
+
p "-- Skipping: #{file} (already exists)"
|
|
166
|
+
end
|
|
167
|
+
}
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def self.migrate(orm, name)
|
|
171
|
+
return if help?(name, :migrate)
|
|
172
|
+
|
|
173
|
+
raise "No ORM is defined. Please set this in database.yml" if orm.nil?
|
|
174
|
+
version = migrate_next
|
|
175
|
+
p "Loaded ORM: #{orm}"
|
|
176
|
+
path = "#{APP_PATH}/db/#{version}_#{name.downcase}.rb"
|
|
177
|
+
if not File.exist?(path)
|
|
178
|
+
p "-- Generating Migration: #{path}"
|
|
179
|
+
file = File.new(path, "w+")
|
|
180
|
+
if file
|
|
181
|
+
template = eval("migrate_#{orm}('#{name}')")
|
|
182
|
+
file.syswrite(template)
|
|
183
|
+
else
|
|
184
|
+
p "Unable to open file!"
|
|
185
|
+
end
|
|
186
|
+
else
|
|
187
|
+
p "-- Skipping: #{path} (already exists)"
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
def self.migrate_next
|
|
191
|
+
migrations = Dir.glob(File.join(APP_PATH,'db/*_*.rb')).reverse
|
|
192
|
+
for m in migrations
|
|
193
|
+
file = m.split('/').last
|
|
194
|
+
if file.index('_')
|
|
195
|
+
num = file.split('_').first
|
|
196
|
+
return "%03d" % (num.to_i + 1) if num.index('0')
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
return "%03d" % 1
|
|
200
|
+
end
|
|
201
|
+
def self.migrate_active_record(name)
|
|
202
|
+
klass = Doozer::Lib.classify(name)
|
|
203
|
+
return """
|
|
204
|
+
class #{klass} < ActiveRecord::Migration
|
|
205
|
+
def self.up
|
|
206
|
+
ActiveRecord::Base.transaction do
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
def self.down
|
|
210
|
+
ActiveRecord::Base.transaction do
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
"""
|
|
215
|
+
end
|
|
216
|
+
def self.migrate_data_mapper(name)
|
|
217
|
+
klass = Doozer::Lib.classify(name)
|
|
218
|
+
return """
|
|
219
|
+
# See for more details
|
|
220
|
+
# http://datamapper.rubyforge.org/dm-core/
|
|
221
|
+
# http://datamapper.rubyforge.org/dm-more/
|
|
222
|
+
# http://github.com/datamapper/dm-more/tree/master/dm-migrations
|
|
223
|
+
require 'dm-more'
|
|
224
|
+
class #{klass}
|
|
225
|
+
def self.up
|
|
226
|
+
end
|
|
227
|
+
def self.down
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
"""
|
|
231
|
+
end
|
|
232
|
+
def self.migrate_sequel(name)
|
|
233
|
+
klass = Doozer::Lib.classify(name)
|
|
234
|
+
return """
|
|
235
|
+
class #{klass}
|
|
236
|
+
def self.db
|
|
237
|
+
Doozer::Configs.db
|
|
238
|
+
end
|
|
239
|
+
def self.up
|
|
240
|
+
# db.create_table :name do
|
|
241
|
+
# end
|
|
242
|
+
end
|
|
243
|
+
def self.down
|
|
244
|
+
# db.drop_table :name
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
"""
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def self.task(name)
|
|
251
|
+
return if help?(name, :task)
|
|
252
|
+
p "Generating file..."
|
|
253
|
+
path = "#{APP_PATH}/tasks/#{name}.rb"
|
|
254
|
+
if not File.exist?(path)
|
|
255
|
+
p "-- Generating Task: #{path}"
|
|
256
|
+
file = File.new(path, "w+")
|
|
257
|
+
if file
|
|
258
|
+
klass = Doozer::Lib.classify(name)
|
|
259
|
+
template = """
|
|
260
|
+
#= Task #{klass}
|
|
261
|
+
class #{klass}
|
|
262
|
+
def self.run
|
|
263
|
+
#place your task here
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
"""
|
|
267
|
+
file.syswrite(template)
|
|
268
|
+
else
|
|
269
|
+
p "Unable to open file!"
|
|
270
|
+
end
|
|
271
|
+
else
|
|
272
|
+
p "-- Skipping: #{path} (already exists)"
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def self.helper(name)
|
|
277
|
+
return if help?(name, :helper)
|
|
278
|
+
p "Generating file..."
|
|
279
|
+
path = "#{APP_PATH}/app/helpers/#{name}_helper.rb"
|
|
280
|
+
if not File.exist?(path)
|
|
281
|
+
p "-- Generating Helper: #{path}"
|
|
282
|
+
file = File.new(path, "w+")
|
|
283
|
+
if file
|
|
284
|
+
klass = Doozer::Lib.classify(name)
|
|
285
|
+
template = """
|
|
286
|
+
#= #{klass}Helper
|
|
287
|
+
module #{klass}Helper
|
|
288
|
+
end
|
|
289
|
+
"""
|
|
290
|
+
file.syswrite(template)
|
|
291
|
+
else
|
|
292
|
+
p "Unable to open file!"
|
|
293
|
+
end
|
|
294
|
+
else
|
|
295
|
+
p "-- Skipping: #{path} (already exists)"
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def self.help?(name, action=nil)
|
|
300
|
+
if name.to_sym == :"-h" or name == :help
|
|
301
|
+
printf "doozer commands:\n"
|
|
302
|
+
help(action)
|
|
303
|
+
return true
|
|
304
|
+
end
|
|
305
|
+
return false
|
|
306
|
+
end
|
|
307
|
+
def self.help(action=nil)
|
|
308
|
+
h = ""
|
|
309
|
+
case action
|
|
310
|
+
|
|
311
|
+
when :project
|
|
312
|
+
h += """
|
|
313
|
+
Project - Create a new Doozer skeleton.
|
|
314
|
+
Command: doozer project_name
|
|
315
|
+
Example: doozer hello_world\n"""
|
|
316
|
+
when :model, :"-M"
|
|
317
|
+
h += """
|
|
318
|
+
Model - Create a new model file in project/app/models with a class name of ModelName for the configured ORM.
|
|
319
|
+
Command: doozer generate (model or -M) model_name
|
|
320
|
+
Example: doozer generate model user\n"""
|
|
321
|
+
when :view, :"-V"
|
|
322
|
+
h += """
|
|
323
|
+
View - Create a view erb file in project/app/views for each of the provided formats. If format not specified an html format is automatically created.
|
|
324
|
+
Command: doozer generate (view or -V) controller_name/action_name format_csv
|
|
325
|
+
Example: doozer generate view admin/login html,xml,json,etc\n"""
|
|
326
|
+
when :controller, :"-C"
|
|
327
|
+
h += """
|
|
328
|
+
Controller - Create a controller file in project/app/controllers (and view folder) with a class name ControllerName.
|
|
329
|
+
Command: doozer generate (controller or -C) controller_name
|
|
330
|
+
Example: doozer generate controller admin\n"""
|
|
331
|
+
when :helper, :"-H"
|
|
332
|
+
h += """
|
|
333
|
+
Helper - Create a helper file in project/app/helpers with the module name of HelperName. '_helper' is automatically appended to the helper_name.
|
|
334
|
+
Command: doozer generate (helper or -H) helper_name
|
|
335
|
+
Example: doozer generate helper helper_name\n"""
|
|
336
|
+
when :db, :migrate, :migration, :"-D"
|
|
337
|
+
h += """
|
|
338
|
+
Migration - Create a migration file in project/db with the next available version number and with a class name of MigrationName for the specified ORM.
|
|
339
|
+
Command: doozer generate (db, migrate, migration or -D) migration_name
|
|
340
|
+
Example: doozer generate migrate create_user\n"""
|
|
341
|
+
when :task, :"-T"
|
|
342
|
+
h += """
|
|
343
|
+
Task - Create a task file in project/tasks with the class name of TaskName.
|
|
344
|
+
Command: doozer generate (task or -T) task_name
|
|
345
|
+
Example: doozer generate task task_name\n"""
|
|
346
|
+
end
|
|
347
|
+
printf h
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# TODO: Dry this up...
|
|
351
|
+
def self.skeleton(name)
|
|
352
|
+
|
|
353
|
+
# create application skeleton
|
|
354
|
+
if not File.exist?(name)
|
|
355
|
+
p "Creating #{name}/"
|
|
356
|
+
system("mkdir #{name}")
|
|
357
|
+
else
|
|
358
|
+
p "Skipping application directory (already exists)"
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
#create app folder
|
|
362
|
+
if not File.exist?("#{name}/app")
|
|
363
|
+
p "Creating app directory"
|
|
364
|
+
system("mkdir #{name}/app")
|
|
365
|
+
else
|
|
366
|
+
p "Skipping #{name}/app directory (already exists)"
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
#copy controllers
|
|
370
|
+
if not File.exist?("#{name}/app/controllers")
|
|
371
|
+
p "Creating #{name}/app/controllers directory and files"
|
|
372
|
+
system("mkdir #{name}/app/controllers")
|
|
373
|
+
system("cp #{skeleton_path 'app/controllers/*.rb'} #{name}/app/controllers")
|
|
374
|
+
else
|
|
375
|
+
p "Skipping #{name}/app/controllers directory (already exists)"
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
#copy models
|
|
379
|
+
if not File.exist?("#{name}/app/models")
|
|
380
|
+
p "Creating #{name}/app/models directory and files"
|
|
381
|
+
system("mkdir #{name}/app/models")
|
|
382
|
+
else
|
|
383
|
+
p "Skipping #{name}/app/models directory (already exists)"
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
#copy views
|
|
387
|
+
if not File.exist?("#{name}/app/views")
|
|
388
|
+
p "Creating #{name}/app/views directory and files"
|
|
389
|
+
system("mkdir #{name}/app/views")
|
|
390
|
+
else
|
|
391
|
+
p "Skipping #{name}/app/views directory (already exists)"
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
#copy views/layouts
|
|
395
|
+
if not File.exist?("#{name}/app/views/layouts")
|
|
396
|
+
p "Creating #{name}/app/views/layouts directory and files"
|
|
397
|
+
system("mkdir #{name}/app/views/layouts")
|
|
398
|
+
system("cp #{skeleton_path 'app/views/layouts/*.erb'} #{name}/app/views/layouts")
|
|
399
|
+
else
|
|
400
|
+
p "Skipping #{name}/app/views/layouts directory (already exists)"
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
#copy views/index
|
|
404
|
+
if not File.exist?("#{name}/app/views/index")
|
|
405
|
+
p "Creating #{name}/app/views/index directory and files"
|
|
406
|
+
system("mkdir #{name}/app/views/index")
|
|
407
|
+
system("cp #{skeleton_path 'app/views/index/*.erb'} #{name}/app/views/index")
|
|
408
|
+
else
|
|
409
|
+
p "Skipping #{name}/app/views/index directory (already exists)"
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
#copy views/global
|
|
413
|
+
if not File.exist?("#{name}/app/views/global")
|
|
414
|
+
p "Creating #{name}/app/views/global directory and files"
|
|
415
|
+
system("mkdir #{name}/app/views/global")
|
|
416
|
+
system("cp #{skeleton_path 'app/views/global/*.erb'} #{name}/app/views/global")
|
|
417
|
+
else
|
|
418
|
+
p "Skipping #{name}/app/views/global directory (already exists)"
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
#copy helpers
|
|
422
|
+
if not File.exist?("#{name}/app/helpers")
|
|
423
|
+
p "Creating #{name}/app/helpers directory and files"
|
|
424
|
+
system("mkdir #{name}/app/helpers")
|
|
425
|
+
system("cp #{skeleton_path 'app/helpers/*.rb'} #{name}/app/helpers")
|
|
426
|
+
else
|
|
427
|
+
p "Skipping #{name}/app/helpers directory (already exists)"
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
#copy configs
|
|
431
|
+
if not File.exist?("#{name}/config")
|
|
432
|
+
p "Creating #{name}/config directory and files"
|
|
433
|
+
system("mkdir #{name}/config")
|
|
434
|
+
system("cp #{skeleton_path 'config/*.yml'} #{name}/config")
|
|
435
|
+
system("cp #{skeleton_path 'config/*.rb'} #{name}/config")
|
|
436
|
+
|
|
437
|
+
## load boot.erb replace version number and save as boot.rb
|
|
438
|
+
|
|
439
|
+
else
|
|
440
|
+
p "Skipping #{name}/config directory (already exists)"
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# create log folder
|
|
444
|
+
if not File.exist?("#{name}/log")
|
|
445
|
+
p "Creating #{name}/log directory"
|
|
446
|
+
system("mkdir #{name}/log")
|
|
447
|
+
else
|
|
448
|
+
p "Skipping #{name}/log directory (already exists)"
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
#copy db
|
|
452
|
+
if not File.exist?("#{name}/db")
|
|
453
|
+
p "Creating #{name}/db directory and files"
|
|
454
|
+
system("mkdir #{name}/db")
|
|
455
|
+
else
|
|
456
|
+
p "Skipping #{name}/db directory (already exists)"
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
#copy lib
|
|
460
|
+
if not File.exist?("#{name}/lib")
|
|
461
|
+
p "Creating #{name}/lib directory and files"
|
|
462
|
+
system("mkdir #{name}/lib")
|
|
463
|
+
else
|
|
464
|
+
p "Skipping #{name}/lib directory (already exists)"
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
#copy script
|
|
468
|
+
if not File.exist?("#{name}/script")
|
|
469
|
+
p "Creating #{name}/script directory and files"
|
|
470
|
+
system("mkdir #{name}/script")
|
|
471
|
+
system("cp #{skeleton_path 'script/*'} #{name}/script")
|
|
472
|
+
else
|
|
473
|
+
p "Skipping #{name}/script directory (already exists)"
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
#copy static
|
|
477
|
+
if not File.exist?("#{name}/static")
|
|
478
|
+
p "Creating #{name}/static directory and files"
|
|
479
|
+
system("mkdir #{name}/static")
|
|
480
|
+
system("cp #{skeleton_path 'static/*.*'} #{name}/static/")
|
|
481
|
+
else
|
|
482
|
+
p "Skipping #{name}/static directory (already exists)"
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
#copy static/images
|
|
486
|
+
if not File.exist?("#{name}/static/images")
|
|
487
|
+
p "Creating #{name}/script/images directory and files"
|
|
488
|
+
system("mkdir #{name}/static/images")
|
|
489
|
+
else
|
|
490
|
+
p "Skipping #{name}/static/images directory (already exists)"
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
#copy static/css
|
|
494
|
+
if not File.exist?("#{name}/static/css")
|
|
495
|
+
p "Creating #{name}/script/css directory and files"
|
|
496
|
+
system("mkdir #{name}/static/css")
|
|
497
|
+
system("cp #{skeleton_path 'static/css/*.css'} #{name}/static/css")
|
|
498
|
+
else
|
|
499
|
+
p "Skipping #{name}/static/css directory (already exists)"
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
#copy static/images
|
|
503
|
+
if not File.exist?("#{name}/static/html")
|
|
504
|
+
p "Creating #{name}/script/html directory and files"
|
|
505
|
+
system("mkdir #{name}/static/html")
|
|
506
|
+
else
|
|
507
|
+
p "Skipping #{name}/static/html directory (already exists)"
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
#copy static/images
|
|
511
|
+
if not File.exist?("#{name}/static/js")
|
|
512
|
+
p "Creating #{name}/script/js directory and files"
|
|
513
|
+
system("mkdir #{name}/static/js")
|
|
514
|
+
system("cp #{skeleton_path 'static/js/*.js'} #{name}/static/js")
|
|
515
|
+
else
|
|
516
|
+
p "Skipping #{name}/static/js directory (already exists)"
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
#copy test
|
|
520
|
+
if not File.exist?("#{name}/test")
|
|
521
|
+
p "Creating #{name}/test directory and files"
|
|
522
|
+
system("mkdir #{name}/test")
|
|
523
|
+
system("cp #{skeleton_path 'test/*.rb'} #{name}/test")
|
|
524
|
+
system("mkdir #{name}/test/fixtures")
|
|
525
|
+
system("cp #{skeleton_path 'test/fixtures/*.rb'} #{name}/test/fixtures")
|
|
526
|
+
else
|
|
527
|
+
p "Skipping test directory (already exists)"
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
#copy test
|
|
531
|
+
if not File.exist?("#{name}/tasks")
|
|
532
|
+
p "Creating #{name}/tasks directory and files"
|
|
533
|
+
system("mkdir #{name}/tasks")
|
|
534
|
+
else
|
|
535
|
+
p "Skipping #{name}/test directory (already exists)"
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
#copy rakefile
|
|
539
|
+
system("cp #{skeleton_path 'Rakefile'} #{name}")
|
|
540
|
+
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def self.skeleton_path(file)
|
|
544
|
+
"#{PATH}/skeleton/#{file}"
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
end
|
|
548
|
+
end
|