scales-monitor 0.0.1.beta.1

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 (69) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +6 -0
  3. data/LICENSE +22 -0
  4. data/Rakefile +8 -0
  5. data/bin/scales-monitor +8 -0
  6. data/lib/scales-monitor.rb +11 -0
  7. data/lib/scales-monitor/app/Gemfile +3 -0
  8. data/lib/scales-monitor/app/Rakefile +0 -0
  9. data/lib/scales-monitor/app/app/assets/images/.gitignore +0 -0
  10. data/lib/scales-monitor/app/app/assets/images/glyphicons-halflings-white.png +0 -0
  11. data/lib/scales-monitor/app/app/assets/images/glyphicons-halflings.png +0 -0
  12. data/lib/scales-monitor/app/app/assets/javascripts/app/config/config.js.coffee +8 -0
  13. data/lib/scales-monitor/app/app/assets/javascripts/app/config/routes.js.coffee +15 -0
  14. data/lib/scales-monitor/app/app/assets/javascripts/app/controllers/.gitignore +0 -0
  15. data/lib/scales-monitor/app/app/assets/javascripts/app/controllers/log.jst.coffee +58 -0
  16. data/lib/scales-monitor/app/app/assets/javascripts/app/controllers/machines.js.coffee +60 -0
  17. data/lib/scales-monitor/app/app/assets/javascripts/app/controllers/queues.js.coffee +55 -0
  18. data/lib/scales-monitor/app/app/assets/javascripts/app/controllers/resources.js.coffee +128 -0
  19. data/lib/scales-monitor/app/app/assets/javascripts/app/index.js.coffee +8 -0
  20. data/lib/scales-monitor/app/app/assets/javascripts/app/models/.gitignore +0 -0
  21. data/lib/scales-monitor/app/app/assets/javascripts/app/models/socket.js.coffee +9 -0
  22. data/lib/scales-monitor/app/app/assets/javascripts/app/views/.gitignore +0 -0
  23. data/lib/scales-monitor/app/app/assets/javascripts/app/views/_format_bar.jst.eco +10 -0
  24. data/lib/scales-monitor/app/app/assets/javascripts/app/views/_machine.jst.eco +11 -0
  25. data/lib/scales-monitor/app/app/assets/javascripts/app/views/_queue_item.jst.eco +10 -0
  26. data/lib/scales-monitor/app/app/assets/javascripts/app/views/_resource.jst.eco +5 -0
  27. data/lib/scales-monitor/app/app/assets/javascripts/app/views/_top.jst.eco +1 -0
  28. data/lib/scales-monitor/app/app/assets/javascripts/app/views/log.jst.eco +11 -0
  29. data/lib/scales-monitor/app/app/assets/javascripts/app/views/machines.jst.eco +33 -0
  30. data/lib/scales-monitor/app/app/assets/javascripts/app/views/queues.jst.eco +33 -0
  31. data/lib/scales-monitor/app/app/assets/javascripts/app/views/resources.jst.eco +31 -0
  32. data/lib/scales-monitor/app/app/assets/javascripts/application.js.coffee +16 -0
  33. data/lib/scales-monitor/app/app/assets/javascripts/lib/bootstrap.js +1825 -0
  34. data/lib/scales-monitor/app/app/assets/javascripts/lib/jquery.js +4 -0
  35. data/lib/scales-monitor/app/app/assets/javascripts/lib/jquery.timeago.js +152 -0
  36. data/lib/scales-monitor/app/app/assets/javascripts/lib/json2.js +485 -0
  37. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/ajax.coffee +208 -0
  38. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/list.coffee +43 -0
  39. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/local.coffee +16 -0
  40. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/manager.coffee +83 -0
  41. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/relation.coffee +144 -0
  42. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/route.coffee +145 -0
  43. data/lib/scales-monitor/app/app/assets/javascripts/lib/spine/spine.coffee +537 -0
  44. data/lib/scales-monitor/app/app/assets/stylesheets/application.css.scss +58 -0
  45. data/lib/scales-monitor/app/app/assets/stylesheets/bootstrap-docs.css +845 -0
  46. data/lib/scales-monitor/app/app/assets/stylesheets/bootstrap-responsive.css +815 -0
  47. data/lib/scales-monitor/app/app/assets/stylesheets/bootstrap.css +4983 -0
  48. data/lib/scales-monitor/app/app/views/index.html.erb +45 -0
  49. data/lib/scales-monitor/app/config.ru +3 -0
  50. data/lib/scales-monitor/app/config/config.rb +17 -0
  51. data/lib/scales-monitor/app/config/routes.rb +13 -0
  52. data/lib/scales-monitor/app/public/assets/.gitignore +0 -0
  53. data/lib/scales-monitor/app/public/assets/application.css +6709 -0
  54. data/lib/scales-monitor/app/public/assets/application.js +5563 -0
  55. data/lib/scales-monitor/app/public/assets/glyphicons-halflings-white.png +0 -0
  56. data/lib/scales-monitor/app/public/assets/glyphicons-halflings.png +0 -0
  57. data/lib/scales-monitor/app/public/index.html +45 -0
  58. data/lib/scales-monitor/base.rb +17 -0
  59. data/lib/scales-monitor/boot/autoload.rb +5 -0
  60. data/lib/scales-monitor/boot/initializers/goliath.rb +3 -0
  61. data/lib/scales-monitor/monitor.rb +33 -0
  62. data/lib/scales-monitor/version.rb +5 -0
  63. data/lib/scales-monitor/web_socket.rb +180 -0
  64. data/scales-monitor.gemspec +24 -0
  65. data/spec/gem_spec.rb +7 -0
  66. data/spec/helper.rb +50 -0
  67. data/spec/monitor_spec.rb +50 -0
  68. data/spec/web_socket_spec.rb +103 -0
  69. metadata +174 -0
@@ -0,0 +1,145 @@
1
+ Spine = @Spine or require('spine')
2
+ $ = Spine.$
3
+
4
+ hashStrip = /^#*/
5
+ namedParam = /:([\w\d]+)/g
6
+ splatParam = /\*([\w\d]+)/g
7
+ escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g
8
+
9
+ class Spine.Route extends Spine.Module
10
+ @extend Spine.Events
11
+
12
+ @historySupport: window.history?.pushState?
13
+
14
+ @routes: []
15
+
16
+ @options:
17
+ trigger: true
18
+ history: false
19
+ shim: false
20
+
21
+ @add: (path, callback) ->
22
+ if (typeof path is 'object' and path not instanceof RegExp)
23
+ @add(key, value) for key, value of path
24
+ else
25
+ @routes.push(new @(path, callback))
26
+
27
+ @setup: (options = {}) ->
28
+ @options = $.extend({}, @options, options)
29
+
30
+ if (@options.history)
31
+ @history = @historySupport && @options.history
32
+
33
+ return if @options.shim
34
+
35
+ if @history
36
+ $(window).bind('popstate', @change)
37
+ else
38
+ $(window).bind('hashchange', @change)
39
+ @change()
40
+
41
+ @unbind: ->
42
+ if @history
43
+ $(window).unbind('popstate', @change)
44
+ else
45
+ $(window).unbind('hashchange', @change)
46
+
47
+ @navigate: (args...) ->
48
+ options = {}
49
+
50
+ lastArg = args[args.length - 1]
51
+ if typeof lastArg is 'object'
52
+ options = args.pop()
53
+ else if typeof lastArg is 'boolean'
54
+ options.trigger = args.pop()
55
+
56
+ options = $.extend({}, @options, options)
57
+
58
+ path = args.join('/')
59
+ return if @path is path
60
+ @path = path
61
+
62
+ @trigger('navigate', @path)
63
+
64
+ @matchRoute(@path, options) if options.trigger
65
+
66
+ return if options.shim
67
+
68
+ if @history
69
+ history.pushState(
70
+ {},
71
+ document.title,
72
+ @path
73
+ )
74
+ else
75
+ window.location.hash = @path
76
+
77
+ # Private
78
+
79
+ @getPath: ->
80
+ path = window.location.pathname
81
+ if path.substr(0,1) isnt '/'
82
+ path = '/' + path
83
+ path
84
+
85
+ @getHash: -> window.location.hash
86
+
87
+ @getFragment: -> @getHash().replace(hashStrip, '')
88
+
89
+ @getHost: ->
90
+ (document.location + '').replace(@getPath() + @getHash(), '')
91
+
92
+ @change: ->
93
+ path = if @getFragment() isnt '' then @getFragment() else @getPath()
94
+ return if path is @path
95
+ @path = path
96
+ @matchRoute(@path)
97
+
98
+ @matchRoute: (path, options) ->
99
+ for route in @routes
100
+ if route.match(path, options)
101
+ @trigger('change', route, path)
102
+ return route
103
+
104
+ constructor: (@path, @callback) ->
105
+ @names = []
106
+
107
+ if typeof path is 'string'
108
+ namedParam.lastIndex = 0
109
+ while (match = namedParam.exec(path)) != null
110
+ @names.push(match[1])
111
+
112
+ path = path.replace(escapeRegExp, '\\$&')
113
+ .replace(namedParam, '([^\/]*)')
114
+ .replace(splatParam, '(.*?)')
115
+
116
+ @route = new RegExp('^' + path + '$')
117
+ else
118
+ @route = path
119
+
120
+ match: (path, options = {}) ->
121
+ match = @route.exec(path)
122
+ return false unless match
123
+ options.match = match
124
+ params = match.slice(1)
125
+
126
+ if @names.length
127
+ for param, i in params
128
+ options[@names[i]] = param
129
+
130
+ @callback.call(null, options) isnt false
131
+
132
+ # Coffee-script bug
133
+ Spine.Route.change = Spine.Route.proxy(Spine.Route.change)
134
+
135
+ Spine.Controller.include
136
+ route: (path, callback) ->
137
+ Spine.Route.add(path, @proxy(callback))
138
+
139
+ routes: (routes) ->
140
+ @route(key, value) for key, value of routes
141
+
142
+ navigate: ->
143
+ Spine.Route.navigate.apply(Spine.Route, arguments)
144
+
145
+ module?.exports = Spine.Route
@@ -0,0 +1,537 @@
1
+ Events =
2
+ bind: (ev, callback) ->
3
+ evs = ev.split(' ')
4
+ calls = @hasOwnProperty('_callbacks') and @_callbacks or= {}
5
+
6
+ for name in evs
7
+ calls[name] or= []
8
+ calls[name].push(callback)
9
+ this
10
+
11
+ one: (ev, callback) ->
12
+ @bind ev, ->
13
+ @unbind(ev, arguments.callee)
14
+ callback.apply(@, arguments)
15
+
16
+ trigger: (args...) ->
17
+ ev = args.shift()
18
+
19
+ list = @hasOwnProperty('_callbacks') and @_callbacks?[ev]
20
+ return unless list
21
+
22
+ for callback in list
23
+ if callback.apply(@, args) is false
24
+ break
25
+ true
26
+
27
+ unbind: (ev, callback) ->
28
+ unless ev
29
+ @_callbacks = {}
30
+ return this
31
+
32
+ list = @_callbacks?[ev]
33
+ return this unless list
34
+
35
+ unless callback
36
+ delete @_callbacks[ev]
37
+ return this
38
+
39
+ for cb, i in list when cb is callback
40
+ list = list.slice()
41
+ list.splice(i, 1)
42
+ @_callbacks[ev] = list
43
+ break
44
+ this
45
+
46
+ Log =
47
+ trace: true
48
+
49
+ logPrefix: '(App)'
50
+
51
+ log: (args...) ->
52
+ return unless @trace
53
+ if @logPrefix then args.unshift(@logPrefix)
54
+ console?.log?(args...)
55
+ this
56
+
57
+ moduleKeywords = ['included', 'extended']
58
+
59
+ class Module
60
+ @include: (obj) ->
61
+ throw('include(obj) requires obj') unless obj
62
+ for key, value of obj when key not in moduleKeywords
63
+ @::[key] = value
64
+ obj.included?.apply(@)
65
+ this
66
+
67
+ @extend: (obj) ->
68
+ throw('extend(obj) requires obj') unless obj
69
+ for key, value of obj when key not in moduleKeywords
70
+ @[key] = value
71
+ obj.extended?.apply(@)
72
+ this
73
+
74
+ @proxy: (func) ->
75
+ => func.apply(@, arguments)
76
+
77
+ proxy: (func) ->
78
+ => func.apply(@, arguments)
79
+
80
+ constructor: ->
81
+ @init?(arguments...)
82
+
83
+ class Model extends Module
84
+ @extend Events
85
+
86
+ @records: {}
87
+ @crecords: {}
88
+ @attributes: []
89
+
90
+ @configure: (name, attributes...) ->
91
+ @className = name
92
+ @records = {}
93
+ @crecords = {}
94
+ @attributes = attributes if attributes.length
95
+ @attributes and= makeArray(@attributes)
96
+ @attributes or= []
97
+ @unbind()
98
+ this
99
+
100
+ @toString: -> "#{@className}(#{@attributes.join(", ")})"
101
+
102
+ @find: (id) ->
103
+ record = @records[id]
104
+ if !record and ("#{id}").match(/c-\d+/)
105
+ return @findCID(id)
106
+ throw('Unknown record') unless record
107
+ record.clone()
108
+
109
+ @findCID: (cid) ->
110
+ record = @crecords[cid]
111
+ throw('Unknown record') unless record
112
+ record.clone()
113
+
114
+ @exists: (id) ->
115
+ try
116
+ return @find(id)
117
+ catch e
118
+ return false
119
+
120
+ @refresh: (values, options = {}) ->
121
+ if options.clear
122
+ @records = {}
123
+ @crecords = {}
124
+
125
+ records = @fromJSON(values)
126
+ records = [records] unless isArray(records)
127
+
128
+ for record in records
129
+ record.id or= record.cid
130
+ @records[record.id] = record
131
+ @crecords[record.cid] = record
132
+
133
+ @resetIdCounter()
134
+
135
+ @trigger('refresh', @cloneArray(records))
136
+ this
137
+
138
+ @select: (callback) ->
139
+ result = (record for id, record of @records when callback(record))
140
+ @cloneArray(result)
141
+
142
+ @findByAttribute: (name, value) ->
143
+ for id, record of @records
144
+ if record[name] is value
145
+ return record.clone()
146
+ null
147
+
148
+ @findAllByAttribute: (name, value) ->
149
+ @select (item) ->
150
+ item[name] is value
151
+
152
+ @each: (callback) ->
153
+ for key, value of @records
154
+ callback(value.clone())
155
+
156
+ @all: ->
157
+ @cloneArray(@recordsValues())
158
+
159
+ @first: ->
160
+ record = @recordsValues()[0]
161
+ record?.clone()
162
+
163
+ @last: ->
164
+ values = @recordsValues()
165
+ record = values[values.length - 1]
166
+ record?.clone()
167
+
168
+ @count: ->
169
+ @recordsValues().length
170
+
171
+ @deleteAll: ->
172
+ for key, value of @records
173
+ delete @records[key]
174
+
175
+ @destroyAll: ->
176
+ for key, value of @records
177
+ @records[key].destroy()
178
+
179
+ @update: (id, atts, options) ->
180
+ @find(id).updateAttributes(atts, options)
181
+
182
+ @create: (atts, options) ->
183
+ record = new @(atts)
184
+ record.save(options)
185
+
186
+ @destroy: (id, options) ->
187
+ @find(id).destroy(options)
188
+
189
+ @change: (callbackOrParams) ->
190
+ if typeof callbackOrParams is 'function'
191
+ @bind('change', callbackOrParams)
192
+ else
193
+ @trigger('change', callbackOrParams)
194
+
195
+ @fetch: (callbackOrParams) ->
196
+ if typeof callbackOrParams is 'function'
197
+ @bind('fetch', callbackOrParams)
198
+ else
199
+ @trigger('fetch', callbackOrParams)
200
+
201
+ @toJSON: ->
202
+ @recordsValues()
203
+
204
+ @fromJSON: (objects) ->
205
+ return unless objects
206
+ if typeof objects is 'string'
207
+ objects = JSON.parse(objects)
208
+ if isArray(objects)
209
+ (new @(value) for value in objects)
210
+ else
211
+ new @(objects)
212
+
213
+ @fromForm: ->
214
+ (new this).fromForm(arguments...)
215
+
216
+ # Private
217
+
218
+ @recordsValues: ->
219
+ result = []
220
+ for key, value of @records
221
+ result.push(value)
222
+ result
223
+
224
+ @cloneArray: (array) ->
225
+ (value.clone() for value in array)
226
+
227
+ @idCounter: 0
228
+
229
+ @resetIdCounter: ->
230
+ ids = (model.id for model in @all()).sort((a, b) -> a > b)
231
+ lastID = ids[ids.length - 1]
232
+ lastID = lastID?.replace?(/^c-/, '') or lastID
233
+ lastID = parseInt(lastID, 10)
234
+ @idCounter = (lastID + 1) or 0
235
+
236
+ @uid: (prefix = '') ->
237
+ prefix + @idCounter++
238
+
239
+ # Instance
240
+
241
+ constructor: (atts) ->
242
+ super
243
+ @load atts if atts
244
+ @cid or= @constructor.uid('c-')
245
+
246
+ isNew: ->
247
+ not @exists()
248
+
249
+ isValid: ->
250
+ not @validate()
251
+
252
+ validate: ->
253
+
254
+ load: (atts) ->
255
+ for key, value of atts
256
+ if typeof @[key] is 'function'
257
+ @[key](value)
258
+ else
259
+ @[key] = value
260
+ this
261
+
262
+ attributes: ->
263
+ result = {}
264
+ for key in @constructor.attributes when key of this
265
+ if typeof @[key] is 'function'
266
+ result[key] = @[key]()
267
+ else
268
+ result[key] = @[key]
269
+ result.id = @id if @id
270
+ result
271
+
272
+ eql: (rec) ->
273
+ !!(rec and rec.constructor is @constructor and
274
+ (rec.cid is @cid) or (rec.id and rec.id is @id))
275
+
276
+ save: (options = {}) ->
277
+ unless options.validate is false
278
+ error = @validate()
279
+ if error
280
+ @trigger('error', error)
281
+ return false
282
+
283
+ @trigger('beforeSave', options)
284
+ record = if @isNew() then @create(options) else @update(options)
285
+ @trigger('save', options)
286
+ record
287
+
288
+ updateAttribute: (name, value, options) ->
289
+ @[name] = value
290
+ @save(options)
291
+
292
+ updateAttributes: (atts, options) ->
293
+ @load(atts)
294
+ @save(options)
295
+
296
+ changeID: (id) ->
297
+ records = @constructor.records
298
+ records[id] = records[@id]
299
+ delete records[@id]
300
+ @id = id
301
+ @save()
302
+
303
+ destroy: (options = {}) ->
304
+ @trigger('beforeDestroy', options)
305
+ delete @constructor.records[@id]
306
+ delete @constructor.crecords[@cid]
307
+ @destroyed = true
308
+ @trigger('destroy', options)
309
+ @trigger('change', 'destroy', options)
310
+ @unbind()
311
+ this
312
+
313
+ dup: (newRecord) ->
314
+ result = new @constructor(@attributes())
315
+ if newRecord is false
316
+ result.cid = @cid
317
+ else
318
+ delete result.id
319
+ result
320
+
321
+ clone: ->
322
+ createObject(@)
323
+
324
+ reload: ->
325
+ return this if @isNew()
326
+ original = @constructor.find(@id)
327
+ @load(original.attributes())
328
+ original
329
+
330
+ toJSON: ->
331
+ @attributes()
332
+
333
+ toString: ->
334
+ "<#{@constructor.className} (#{JSON.stringify(@)})>"
335
+
336
+ fromForm: (form) ->
337
+ result = {}
338
+ for key in $(form).serializeArray()
339
+ result[key.name] = key.value
340
+ @load(result)
341
+
342
+ exists: ->
343
+ @id && @id of @constructor.records
344
+
345
+ # Private
346
+
347
+ update: (options) ->
348
+ @trigger('beforeUpdate', options)
349
+ records = @constructor.records
350
+ records[@id].load @attributes()
351
+ clone = records[@id].clone()
352
+ clone.trigger('update', options)
353
+ clone.trigger('change', 'update', options)
354
+ clone
355
+
356
+ create: (options) ->
357
+ @trigger('beforeCreate', options)
358
+ @id = @cid unless @id
359
+
360
+ record = @dup(false)
361
+ @constructor.records[@id] = record
362
+ @constructor.crecords[@cid] = record
363
+
364
+ clone = record.clone()
365
+ clone.trigger('create', options)
366
+ clone.trigger('change', 'create', options)
367
+ clone
368
+
369
+ bind: (events, callback) ->
370
+ @constructor.bind events, binder = (record) =>
371
+ if record && @eql(record)
372
+ callback.apply(@, arguments)
373
+ @constructor.bind 'unbind', unbinder = (record) =>
374
+ if record && @eql(record)
375
+ @constructor.unbind(events, binder)
376
+ @constructor.unbind('unbind', unbinder)
377
+ binder
378
+
379
+ one: (events, callback) ->
380
+ binder = @bind events, =>
381
+ @constructor.unbind(events, binder)
382
+ callback.apply(@, arguments)
383
+
384
+ trigger: (args...) ->
385
+ args.splice(1, 0, @)
386
+ @constructor.trigger(args...)
387
+
388
+ unbind: ->
389
+ @trigger('unbind')
390
+
391
+ class Controller extends Module
392
+ @include Events
393
+ @include Log
394
+
395
+ eventSplitter: /^(\S+)\s*(.*)$/
396
+ tag: 'div'
397
+
398
+ constructor: (options) ->
399
+ @options = options
400
+
401
+ for key, value of @options
402
+ @[key] = value
403
+
404
+ @el = document.createElement(@tag) unless @el
405
+ @el = $(@el)
406
+
407
+ @el.addClass(@className) if @className
408
+ @el.attr(@attributes) if @attributes
409
+
410
+ @events = @constructor.events unless @events
411
+ @elements = @constructor.elements unless @elements
412
+
413
+ @delegateEvents(@events) if @events
414
+ @refreshElements() if @elements
415
+
416
+ super
417
+
418
+ release: =>
419
+ @el.remove()
420
+ @unbind()
421
+ @trigger 'release'
422
+
423
+ $: (selector) -> $(selector, @el)
424
+
425
+ delegateEvents: (events) ->
426
+ for key, method of events
427
+
428
+ unless typeof(method) is 'function'
429
+ # Always return true from event handlers
430
+ method = do (method) => =>
431
+ @[method].apply(this, arguments)
432
+ true
433
+
434
+ match = key.match(@eventSplitter)
435
+ eventName = match[1]
436
+ selector = match[2]
437
+
438
+ if selector is ''
439
+ @el.bind(eventName, method)
440
+ else
441
+ @el.delegate(selector, eventName, method)
442
+
443
+ refreshElements: ->
444
+ for key, value of @elements
445
+ @[value] = @$(key)
446
+
447
+ delay: (func, timeout) ->
448
+ setTimeout(@proxy(func), timeout || 0)
449
+
450
+ html: (element) ->
451
+ @el.html(element.el or element)
452
+ @refreshElements()
453
+ @el
454
+
455
+ append: (elements...) ->
456
+ elements = (e.el or e for e in elements)
457
+ @el.append(elements...)
458
+ @refreshElements()
459
+ @el
460
+
461
+ appendTo: (element) ->
462
+ @el.appendTo(element.el or element)
463
+ @refreshElements()
464
+ @el
465
+
466
+ prepend: (elements...) ->
467
+ elements = (e.el or e for e in elements)
468
+ @el.prepend(elements...)
469
+ @refreshElements()
470
+ @el
471
+
472
+ replace: (element) ->
473
+ [previous, @el] = [@el, $(element.el or element)]
474
+ previous.replaceWith(@el)
475
+ @delegateEvents(@events)
476
+ @refreshElements()
477
+ @el
478
+
479
+ # Utilities & Shims
480
+
481
+ $ = window?.jQuery or window?.Zepto or (element) -> element
482
+
483
+ createObject = Object.create or (o) ->
484
+ Func = ->
485
+ Func.prototype = o
486
+ new Func()
487
+
488
+ isArray = (value) ->
489
+ Object::toString.call(value) is '[object Array]'
490
+
491
+ isBlank = (value) ->
492
+ return true unless value
493
+ return false for key of value
494
+ true
495
+
496
+ makeArray = (args) ->
497
+ Array::slice.call(args, 0)
498
+
499
+ # Globals
500
+
501
+ Spine = @Spine = {}
502
+ module?.exports = Spine
503
+
504
+ Spine.version = '1.0.6'
505
+ Spine.isArray = isArray
506
+ Spine.isBlank = isBlank
507
+ Spine.$ = $
508
+ Spine.Events = Events
509
+ Spine.Log = Log
510
+ Spine.Module = Module
511
+ Spine.Controller = Controller
512
+ Spine.Model = Model
513
+
514
+ # Global events
515
+
516
+ Module.extend.call(Spine, Events)
517
+
518
+ # JavaScript compatability
519
+
520
+ Module.create = Module.sub =
521
+ Controller.create = Controller.sub =
522
+ Model.sub = (instances, statics) ->
523
+ class result extends this
524
+ result.include(instances) if instances
525
+ result.extend(statics) if statics
526
+ result.unbind?()
527
+ result
528
+
529
+ Model.setup = (name, attributes = []) ->
530
+ class Instance extends this
531
+ Instance.configure(name, attributes...)
532
+ Instance
533
+
534
+ Module.init = Controller.init = Model.init = (a1, a2, a3, a4, a5) ->
535
+ new this(a1, a2, a3, a4, a5)
536
+
537
+ Spine.Class = Module