grippy-doozer 0.1.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 (72) 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 +59 -0
  6. data/VERSION +1 -0
  7. data/bin/doozer +8 -0
  8. data/doozer.gemspec +114 -0
  9. data/lib/doozer/README.rb +40 -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 +265 -0
  16. data/lib/doozer/configs.rb +131 -0
  17. data/lib/doozer/controller.rb +335 -0
  18. data/lib/doozer/extend.rb +10 -0
  19. data/lib/doozer/initializer.rb +95 -0
  20. data/lib/doozer/lib.rb +32 -0
  21. data/lib/doozer/logger.rb +11 -0
  22. data/lib/doozer/orm/active_record.rb +19 -0
  23. data/lib/doozer/orm/data_mapper.rb +19 -0
  24. data/lib/doozer/orm/sequel.rb +18 -0
  25. data/lib/doozer/partial.rb +99 -0
  26. data/lib/doozer/plugins/paginate/init.rb +2 -0
  27. data/lib/doozer/plugins/paginate/lib/paginate/collection.rb +60 -0
  28. data/lib/doozer/plugins/paginate/lib/paginate/finder.rb +116 -0
  29. data/lib/doozer/plugins/paginate/lib/paginate/view_helpers.rb +37 -0
  30. data/lib/doozer/plugins/paginate/lib/paginate.rb +32 -0
  31. data/lib/doozer/rackup/server.ru +37 -0
  32. data/lib/doozer/rackup/test.rb +19 -0
  33. data/lib/doozer/redirect.rb +12 -0
  34. data/lib/doozer/route.rb +264 -0
  35. data/lib/doozer/scripts/cluster.rb +132 -0
  36. data/lib/doozer/scripts/migrate.rb +108 -0
  37. data/lib/doozer/scripts/task.rb +60 -0
  38. data/lib/doozer/scripts/test.rb +23 -0
  39. data/lib/doozer/version.rb +8 -0
  40. data/lib/doozer/view_helpers.rb +163 -0
  41. data/lib/doozer/watcher.rb +375 -0
  42. data/lib/doozer.rb +30 -0
  43. data/lib/generator/generator.rb +547 -0
  44. data/templates/skeleton/Rakefile +3 -0
  45. data/templates/skeleton/app/controllers/application_controller.rb +2 -0
  46. data/templates/skeleton/app/controllers/index_controller.rb +7 -0
  47. data/templates/skeleton/app/helpers/application_helper.rb +17 -0
  48. data/templates/skeleton/app/views/global/_header.html.erb +7 -0
  49. data/templates/skeleton/app/views/global/_navigation.html.erb +6 -0
  50. data/templates/skeleton/app/views/index/index.html.erb +108 -0
  51. data/templates/skeleton/app/views/layouts/default.html.erb +23 -0
  52. data/templates/skeleton/config/app.yml +31 -0
  53. data/templates/skeleton/config/database.yml +25 -0
  54. data/templates/skeleton/config/environment.rb +13 -0
  55. data/templates/skeleton/config/rack.rb +30 -0
  56. data/templates/skeleton/config/routes.rb +69 -0
  57. data/templates/skeleton/script/cluster +5 -0
  58. data/templates/skeleton/script/migrate +5 -0
  59. data/templates/skeleton/script/task +5 -0
  60. data/templates/skeleton/script/test +4 -0
  61. data/templates/skeleton/static/404.html +16 -0
  62. data/templates/skeleton/static/500.html +16 -0
  63. data/templates/skeleton/static/css/style.css +32 -0
  64. data/templates/skeleton/static/favicon.ico +0 -0
  65. data/templates/skeleton/static/js/application.js +1 -0
  66. data/templates/skeleton/static/js/jquery-1.3.min.js +19 -0
  67. data/templates/skeleton/static/robots.txt +5 -0
  68. data/templates/skeleton/test/fixtures/setup.rb +6 -0
  69. data/templates/skeleton/test/setup.rb +33 -0
  70. data/test/doozer_test.rb +7 -0
  71. data/test/test_helper.rb +10 -0
  72. metadata +126 -0
@@ -0,0 +1,375 @@
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
+
50
+ if defined?(@stateCallback) then
51
+ @stateCallback.call(newState)
52
+ end
53
+ end
54
+
55
+ def isConfigured?
56
+ return @configured
57
+ end
58
+
59
+ def isStarted?
60
+ return @state == STARTED
61
+ end
62
+
63
+ def isStopped?
64
+ if @state == STOPPED then
65
+ return true
66
+ elsif @stopWhen && @stopWhen.call() then
67
+ setState(STOPPED)
68
+ return true
69
+ else
70
+ return false
71
+ end
72
+ end
73
+
74
+ def stopWhen(&block)
75
+ @stopWhen = block
76
+ end
77
+ end
78
+
79
+
80
+ # This class will watch a directory or a set of directories and alert you of
81
+ # new files, modified files, deleted files. You can optionally only be alerted
82
+ # when a files md5 hash has been changed so you only are alerted to real changes.
83
+ # this of course means slower performance and higher cpu/io usage.
84
+ class FileSystemWatcher
85
+ include ServiceState
86
+
87
+ CREATED = 0
88
+ MODIFIED = 1
89
+ DELETED = 2
90
+
91
+ # the time to wait before checking the directories again
92
+ attr_accessor :sleepTime, :priority, :mongrel
93
+
94
+ # you can optionally use the file contents md5 to detect if a file has changed
95
+ attr_accessor :useMD5
96
+
97
+ def initialize(dir=nil, expression="**/*")
98
+ @sleepTime = 5
99
+ @useMD5 = false
100
+ @priority = 0
101
+ @stopWhen = nil
102
+
103
+ @directories = Array.new()
104
+ @files = Array.new()
105
+
106
+ @foundFiles = nil
107
+ @firstLoad = true
108
+ @watchThread = nil
109
+
110
+ initializeState()
111
+
112
+ if dir then
113
+ addDirectory(dir, expression)
114
+ end
115
+ end
116
+
117
+ # add a directory to be watched
118
+ # @param dir the directory to watch
119
+ # @param expression the glob pattern to search under the watched directory
120
+ def addDirectory(dir, expression="**/*")
121
+ if FileTest.exists?(dir) && FileTest.readable?(dir) then
122
+ @directories << FSWatcher::Directory.new(dir, expression)
123
+ else
124
+ raise FSWatcher::InvalidDirectoryError, "Dir '#{dir}' either doesnt exist or isnt readable"
125
+ end
126
+ end
127
+
128
+ def removeDirectory(dir)
129
+ @directories.delete(dir)
130
+ end
131
+
132
+ # add a specific file to the watch list
133
+ # @param file the file to watch
134
+ def addFile(file)
135
+ if FileTest.exists?(file) && FileTest.readable?(file) then
136
+ @files << file
137
+ else
138
+ raise FSWatcher::InvalidFileError, "File '#{file}' either doesnt exist or isnt readable"
139
+ end
140
+ end
141
+
142
+ def removeFile(file)
143
+ @files.delete(file)
144
+ end
145
+
146
+ # start watching the specified files/directories
147
+ def start(&block)
148
+ if isStarted? then
149
+ raise RuntimeError, "already started"
150
+ end
151
+
152
+ setState(STARTED)
153
+
154
+ @firstLoad = true
155
+ @foundFiles = Hash.new()
156
+
157
+ # we watch in a new thread
158
+ @watchThread = Thread.new {
159
+ # we will be stopped if someone calls stop or if someone set a stopWhen that becomes true
160
+ while !isStopped? do
161
+ if (!@directories.empty?) or (!@files.empty?) then
162
+ # this will hold the list of the files we looked at this iteration
163
+ # allows us to not look at the same file again and also to compare
164
+ # with the foundFile list to see if something was deleted
165
+ alreadyExamined = Hash.new()
166
+
167
+ # check the files in each watched directory
168
+ if not @directories.empty? then
169
+ @directories.each { |dirObj|
170
+ examineFileList(dirObj.getFiles(), alreadyExamined, &block)
171
+ }
172
+ end
173
+
174
+ # now examine any files the user wants to specifically watch
175
+ examineFileList(@files, alreadyExamined, &block) if not @files.empty?
176
+
177
+ # see if we have to delete files from our found list
178
+ if not @firstLoad then
179
+ if not @foundFiles.empty? then
180
+ # now diff the found files and the examined files to see if
181
+ # something has been deleted
182
+ allFoundFiles = @foundFiles.keys()
183
+ allExaminedFiles = alreadyExamined.keys()
184
+ intersection = allFoundFiles - allExaminedFiles
185
+ intersection.each { |fileName|
186
+ # callback
187
+ block.call(DELETED, fileName)
188
+ # remove deleted file from the foundFiles list
189
+ @foundFiles.delete(fileName)
190
+ }
191
+ end
192
+ else
193
+ @firstLoad = false
194
+ end
195
+ end
196
+
197
+ # go to sleep
198
+ sleep(@sleepTime)
199
+ end
200
+ }
201
+
202
+ # set the watch thread priority
203
+ @watchThread.priority = @priority
204
+
205
+ end
206
+
207
+ # kill the filewatcher thread
208
+ def stop()
209
+ setState(STOPPED)
210
+ @watchThread.wakeup()
211
+ end
212
+
213
+ # wait for the filewatcher to finish
214
+ def join()
215
+ @watchThread.join() if @watchThread
216
+ end
217
+
218
+
219
+ private
220
+
221
+ # loops over the file list check for new or modified files
222
+ def examineFileList(fileList, alreadyExamined, &block)
223
+ fileList.each { |fileName|
224
+ # expand the file name to the fully qual path
225
+ fullFileName = File.expand_path(fileName)
226
+
227
+ # dont examine the same file 2 times
228
+ if not alreadyExamined.has_key?(fullFileName) then
229
+ # we cant do much if the file isnt readable anyway
230
+ if File.readable?(fullFileName) then
231
+ # set that we have seen this file
232
+ alreadyExamined[fullFileName] = true
233
+
234
+ # get the file info
235
+ modTime, size = File.mtime(fullFileName), File.size(fullFileName)
236
+
237
+ # on the first iteration just load all of the files into the foundList
238
+ if @firstLoad then
239
+ @foundFiles[fullFileName] = FSWatcher::FoundFile.new(fullFileName, modTime, size, false, @useMD5)
240
+ else
241
+ # see if we have found this file already
242
+ foundFile = @foundFiles[fullFileName]
243
+
244
+ if foundFile then
245
+
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
+ if foundFile.isNew? then
249
+
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
+
254
+ # callback
255
+ block.call(CREATED, fullFileName)
256
+
257
+ # mark this file as a changed file now
258
+ foundFile.updateModTime(modTime)
259
+
260
+ # generate the md5 for the file since we know it is done
261
+ # being written to
262
+ foundFile.genMD5() if @useMD5
263
+
264
+ else
265
+
266
+ # just update the size so we can check again at the next iteration
267
+ foundFile.updateSize(size)
268
+
269
+ end
270
+
271
+ elsif modTime > foundFile.modTime then
272
+
273
+ # if the mod times are different on files we already have
274
+ # found this is an update
275
+ willYield = true
276
+
277
+ # if we are using md5's then compare them
278
+ if @useMD5 then
279
+ filesMD5 = FSWatcher.genFileMD5(fullFileName)
280
+ if filesMD5 && foundFile.md5 then
281
+ if filesMD5.to_s == foundFile.md5.to_s then
282
+ willYield = false
283
+ end
284
+ end
285
+
286
+ # if we are yielding then the md5s are dif so
287
+ # update the cached md5 value
288
+ foundFile.setMD5(filesMD5) if willYield
289
+
290
+ end
291
+
292
+ block.call(MODIFIED, fullFileName) if willYield
293
+ foundFile.updateModTime(modTime)
294
+ end
295
+ else
296
+ # this is a new file for our list. dont update the md5 here since
297
+ # the file might not yet be done being written to
298
+ @foundFiles[fullFileName] = FSWatcher::FoundFile.new(fullFileName, modTime, size)
299
+ end
300
+ end
301
+ end
302
+ end
303
+ }
304
+ end
305
+ end
306
+
307
+ # Util classes for the FileSystemWatcher
308
+ module FSWatcher
309
+ # The directory to watch
310
+ class Directory
311
+ attr_reader :dir, :expression
312
+
313
+ def initialize(dir, expression)
314
+ @dir, @expression = dir, expression
315
+ @dir.chop! if @dir =~ %r{/$}
316
+ end
317
+
318
+ def getFiles()
319
+ return Dir[@dir + "/" + @expression]
320
+ end
321
+ end
322
+
323
+ # A FoundFile entry for the FileSystemWatcher
324
+ class FoundFile
325
+ attr_reader :status, :fileName, :modTime, :size, :md5
326
+
327
+ def initialize(fileName, modTime, size, isNewFile=true, useMD5=false)
328
+ @fileName, @modTime, @size, @isNewFile = fileName, modTime, size, isNewFile
329
+ @md5 = nil
330
+ if useMD5 then
331
+ genMD5()
332
+ end
333
+ end
334
+
335
+ def updateModTime(modTime)
336
+ @modTime = modTime
337
+ @isNewFile = false
338
+ end
339
+
340
+ def updateSize(size)
341
+ @size = size
342
+ end
343
+
344
+ def isNew?
345
+ return @isNewFile
346
+ end
347
+
348
+ def setMD5(newMD5)
349
+ @md5 = newMD5
350
+ end
351
+
352
+ # generate my files md5 value
353
+ def genMD5()
354
+ @md5 = FSWatcher.genFileMD5(@fileName)
355
+ end
356
+ end
357
+
358
+ # utility function for generating md5s from a files contents
359
+ def FSWatcher.genFileMD5(fileName)
360
+ if FileTest.file?(fileName) then
361
+ f = File.open(fileName)
362
+ contents = f.read()
363
+ f.close()
364
+ return MD5.new(contents) if contents
365
+ end
366
+ return nil
367
+ end
368
+
369
+ # if the directory you want to watch doesnt exist or isnt readable this is thrown
370
+ class InvalidDirectoryError < StandardError; end
371
+
372
+ # if the file you want to watch doesnt exist or isnt readable this is thrown
373
+ class InvalidFileError < StandardError; end
374
+ end
375
+
data/lib/doozer.rb ADDED
@@ -0,0 +1,30 @@
1
+ DOOZER_PATH = File.expand_path(File.dirname(__FILE__))
2
+ $:.unshift(DOOZER_PATH) unless $:.include?(DOOZER_PATH)
3
+
4
+ # load active_support hooks
5
+ require File.join(DOOZER_PATH, 'doozer/extend')
6
+
7
+ module Doozer
8
+
9
+ autoload :App, "doozer/app"
10
+ autoload :Configs, "doozer/configs"
11
+ autoload :Controller, "doozer/controller"
12
+ autoload :Initializer, "doozer/initializer"
13
+ autoload :Lib, "doozer/lib"
14
+ autoload :Partial, "doozer/partial"
15
+ autoload :Redirect, "doozer/redirect"
16
+
17
+ module Routing
18
+ autoload :Route, "doozer/route"
19
+ autoload :Routes, "doozer/route"
20
+ end
21
+
22
+ module Util
23
+ autoload :Logger, "doozer/logger"
24
+ end
25
+ autoload :ViewHelpers, "doozer/view_helpers"
26
+ autoload :Version, "doozer/version"
27
+
28
+ # Generator methods
29
+ autoload :Generator, "generator/generator"
30
+ end