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.
Files changed (104) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +57 -0
  5. data/Rakefile +62 -0
  6. data/VERSION +1 -0
  7. data/bin/doozer +6 -0
  8. data/doozer.gemspec +156 -0
  9. data/lib/doozer.rb +35 -0
  10. data/lib/doozer/active_support/array.rb +14 -0
  11. data/lib/doozer/active_support/class.rb +221 -0
  12. data/lib/doozer/active_support/date_time.rb +23 -0
  13. data/lib/doozer/active_support/object.rb +43 -0
  14. data/lib/doozer/active_support/time.rb +32 -0
  15. data/lib/doozer/app.rb +294 -0
  16. data/lib/doozer/configs.rb +146 -0
  17. data/lib/doozer/controller.rb +340 -0
  18. data/lib/doozer/exceptions.rb +12 -0
  19. data/lib/doozer/extend.rb +10 -0
  20. data/lib/doozer/initializer.rb +104 -0
  21. data/lib/doozer/lib.rb +32 -0
  22. data/lib/doozer/logger.rb +12 -0
  23. data/lib/doozer/orm/active_record.rb +28 -0
  24. data/lib/doozer/orm/data_mapper.rb +22 -0
  25. data/lib/doozer/orm/sequel.rb +21 -0
  26. data/lib/doozer/partial.rb +99 -0
  27. data/lib/doozer/plugins/paginate/init.rb +2 -0
  28. data/lib/doozer/plugins/paginate/lib/paginate.rb +32 -0
  29. data/lib/doozer/plugins/paginate/lib/paginate/collection.rb +60 -0
  30. data/lib/doozer/plugins/paginate/lib/paginate/finder.rb +116 -0
  31. data/lib/doozer/plugins/paginate/lib/paginate/view_helpers.rb +37 -0
  32. data/lib/doozer/rackup/server.ru +35 -0
  33. data/lib/doozer/rackup/test.rb +20 -0
  34. data/lib/doozer/redirect.rb +12 -0
  35. data/lib/doozer/route.rb +292 -0
  36. data/lib/doozer/scripts/cluster.rb +126 -0
  37. data/lib/doozer/scripts/console.rb +2 -0
  38. data/lib/doozer/scripts/migrate.rb +108 -0
  39. data/lib/doozer/scripts/task.rb +60 -0
  40. data/lib/doozer/scripts/test.rb +23 -0
  41. data/lib/doozer/version.rb +8 -0
  42. data/lib/doozer/view_helpers.rb +251 -0
  43. data/lib/doozer/watcher.rb +369 -0
  44. data/lib/generator/generator.rb +548 -0
  45. data/templates/skeleton/Rakefile +3 -0
  46. data/templates/skeleton/app/controllers/application_controller.rb +2 -0
  47. data/templates/skeleton/app/controllers/index_controller.rb +7 -0
  48. data/templates/skeleton/app/helpers/application_helper.rb +17 -0
  49. data/templates/skeleton/app/views/global/_header.html.erb +7 -0
  50. data/templates/skeleton/app/views/global/_navigation.html.erb +6 -0
  51. data/templates/skeleton/app/views/index/index.html.erb +108 -0
  52. data/templates/skeleton/app/views/layouts/default.html.erb +23 -0
  53. data/templates/skeleton/config/app.yml +31 -0
  54. data/templates/skeleton/config/boot.rb +17 -0
  55. data/templates/skeleton/config/database.yml +25 -0
  56. data/templates/skeleton/config/environment.rb +11 -0
  57. data/templates/skeleton/config/rack.rb +30 -0
  58. data/templates/skeleton/config/routes.rb +69 -0
  59. data/templates/skeleton/script/cluster +4 -0
  60. data/templates/skeleton/script/console +15 -0
  61. data/templates/skeleton/script/migrate +4 -0
  62. data/templates/skeleton/script/task +4 -0
  63. data/templates/skeleton/script/test +4 -0
  64. data/templates/skeleton/static/404.html +16 -0
  65. data/templates/skeleton/static/500.html +16 -0
  66. data/templates/skeleton/static/css/style.css +32 -0
  67. data/templates/skeleton/static/favicon.ico +0 -0
  68. data/templates/skeleton/static/js/application.js +1 -0
  69. data/templates/skeleton/static/js/jquery-1.3.min.js +19 -0
  70. data/templates/skeleton/static/robots.txt +5 -0
  71. data/templates/skeleton/test/fixtures/setup.rb +6 -0
  72. data/templates/skeleton/test/setup.rb +33 -0
  73. data/test/doozer_test.rb +7 -0
  74. data/test/project/Rakefile +3 -0
  75. data/test/project/app/controllers/application_controller.rb +2 -0
  76. data/test/project/app/controllers/index_controller.rb +7 -0
  77. data/test/project/app/helpers/application_helper.rb +17 -0
  78. data/test/project/app/views/global/_header.html.erb +7 -0
  79. data/test/project/app/views/global/_navigation.html.erb +6 -0
  80. data/test/project/app/views/index/index.html.erb +108 -0
  81. data/test/project/app/views/layouts/default.html.erb +23 -0
  82. data/test/project/config/app.yml +31 -0
  83. data/test/project/config/boot.rb +17 -0
  84. data/test/project/config/database.yml +25 -0
  85. data/test/project/config/environment.rb +11 -0
  86. data/test/project/config/rack.rb +30 -0
  87. data/test/project/config/routes.rb +72 -0
  88. data/test/project/script/cluster +4 -0
  89. data/test/project/script/console +15 -0
  90. data/test/project/script/migrate +4 -0
  91. data/test/project/script/task +4 -0
  92. data/test/project/script/test +4 -0
  93. data/test/project/static/404.html +16 -0
  94. data/test/project/static/500.html +16 -0
  95. data/test/project/static/css/style.css +32 -0
  96. data/test/project/static/favicon.ico +0 -0
  97. data/test/project/static/js/application.js +1 -0
  98. data/test/project/static/js/jquery-1.3.min.js +19 -0
  99. data/test/project/static/robots.txt +5 -0
  100. data/test/project/test/fixtures/setup.rb +6 -0
  101. data/test/project/test/setup.rb +33 -0
  102. data/test/routing_test.rb +66 -0
  103. data/test/test_helper.rb +26 -0
  104. 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