scales-monitor 0.0.1.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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