rails_jskit 4.0.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.
@@ -0,0 +1,543 @@
1
+ /**
2
+ * JSkit global namespace object
3
+ *
4
+ * @module JSkit
5
+ * @class JSkit
6
+ */
7
+ var JSkit = (function() {
8
+ if (!_) throw new Error("JSkit: lodash or underscore is required");
9
+ if (!$) throw new Error("JSkit: jQuery or equivalent is required");
10
+
11
+ return {
12
+ /**
13
+ * Returns a new Application object.
14
+ *
15
+ * @static
16
+ * @method createApplication
17
+ * @return {Application}
18
+ */
19
+ createApplication: function() {
20
+ return JSkit.Application.create();
21
+ }
22
+ };
23
+ })();
24
+
25
+ /**
26
+ * @module JSkit
27
+ * @class Dispatcher
28
+ */
29
+ JSkit.Dispatcher = (function() {
30
+ var contains = _.contains;
31
+ var pluck = _.pluck;
32
+
33
+ /**
34
+ Get all handler functions for a given dispatcher and event.
35
+
36
+ @method getEventHandlers
37
+ @param {Dispatcher} dispatcher
38
+ @param {String} eventName event name for which you wish to find handlers
39
+ @return {Array} handler functions for the given event
40
+ */
41
+ function getEventHandlers(dispatcher, eventName) {
42
+ dispatcher.__events__[eventName] = dispatcher.__events__[eventName] || [];
43
+ return dispatcher.__events__[eventName];
44
+ }
45
+
46
+ /**
47
+ Create a handler object that contains the context and handler function
48
+
49
+ @method createHandlerObject
50
+ @param {Object,Function} context `this` context for handler function (defaults to `null`)
51
+ @param {Function} handler function to handle event
52
+ @return {Object} handler object with `handler` and `context`
53
+ */
54
+ function createHandlerObject(context, handler) {
55
+ context = context || null;
56
+ return {
57
+ context: context,
58
+ handler: handler
59
+ };
60
+ }
61
+
62
+ /**
63
+ Add an event handler to the array of registered handlers.
64
+
65
+ @method registerHandler
66
+ @param {Array} registeredHandlers registered handlers for an event
67
+ @param {Function} eventHandler to handle event
68
+ @param {String} [method="push"] method to add the handler to the array
69
+ */
70
+ function registerHandler(registeredHandlers, eventHandler, method) {
71
+ method = method || "push";
72
+ if (!contains(pluck(registeredHandlers, "handler"), eventHandler.handler)) {
73
+ registeredHandlers[method](eventHandler);
74
+ }
75
+ }
76
+
77
+ return {
78
+ /**
79
+ Create a new Dispatcher object.
80
+
81
+ @static
82
+ @method create
83
+ @return {Dispatcher}
84
+ */
85
+ create: function() {
86
+ return {
87
+ __events__: {},
88
+
89
+ /**
90
+ Register a handler to a given event.
91
+
92
+ @method on
93
+ @param {String} eventName Name of the event
94
+ @param {Function} handler Function to handle the event
95
+ @param {Controller} [context] `this` context of the handler
96
+ */
97
+ on: function(eventName, handler, context) {
98
+ var eventHandler = createHandlerObject(context, handler);
99
+ var registeredHandlers = getEventHandlers(this, eventName);
100
+ registerHandler(registeredHandlers, eventHandler);
101
+ },
102
+
103
+ /**
104
+ Register a handler to a given event that will
105
+ fire before the handlers registerd with `on`.
106
+
107
+ @method before
108
+ @param {String} eventName Name of the event
109
+ @param {Function} handler Function to handle the event
110
+ @param {Controller} [context] `this` context of the handler
111
+ */
112
+ before: function(eventName, handler, context) {
113
+ var eventHandler = createHandlerObject(context, handler);
114
+ var registeredHandlers = getEventHandlers(this, eventName);
115
+ registerHandler(registeredHandlers, eventHandler, "unshift");
116
+ },
117
+
118
+ /**
119
+ Remove a handler from a given event.
120
+
121
+ @method off
122
+ @param {String} eventName Name of the event
123
+ @param {Function} handler Function to unbind from the `event`
124
+ */
125
+ off: function(eventName, handler) {
126
+ var registeredHandlers = this.__events__[eventName];
127
+ var retainedHandlers = [];
128
+
129
+ if (handler) {
130
+ this.__events__[eventName] = _.reject(registeredHandlers, function(eventHandler) {
131
+ return eventHandler.handler !== handler;
132
+ });
133
+ } else {
134
+ this.__events__[eventName] = [];
135
+ }
136
+ },
137
+
138
+ /**
139
+ Trigger a given event, causing all handlers
140
+ for that event to be fired.
141
+
142
+ @method trigger
143
+ @param {String} eventName Name of the event
144
+ */
145
+ trigger: function(eventName) {
146
+ var eventHhandlers = this.__events__[eventName] || [];
147
+ var args = _.rest(arguments);
148
+
149
+ _.each(eventHhandlers, function(eventHandler) {
150
+ var handler = eventHandler.handler;
151
+ var context = eventHandler.context;
152
+ handler.apply(context, args);
153
+ });
154
+ }
155
+ };
156
+ }
157
+ };
158
+ })();
159
+
160
+ /**
161
+ * @module JSkit
162
+ * @class Controller
163
+ */
164
+ JSkit.Controller = (function() {
165
+ var bindAll = _.bindAll;
166
+ var compact = _.compact;
167
+ var defaults = _.defaults;
168
+ var each = _.each;
169
+ var first = _.first;
170
+ var flatten = _.flatten;
171
+ var functions = _.functions;
172
+ var isFunc = _.isFunction;
173
+ var isObject = _.isObject;
174
+ var keys = _.keys;
175
+ var map = _.map;
176
+ var pairs = _.pairs;
177
+ var uniq = _.uniq;
178
+ var values = _.values;
179
+
180
+ /**
181
+ * Take a string and put underscores between names and delimiters
182
+ *
183
+ * @private
184
+ * @method constantize
185
+ * @param {String} string
186
+ * @return {String}
187
+ */
188
+ function underscore(string) {
189
+ string = string || "";
190
+ return string.replace(/([A-Z])/g, " $1").replace(/^\s?/, "").replace(/-|\s/g, "_").toLowerCase();
191
+ }
192
+
193
+ /**
194
+ * Register the controller's actions to the dispatcher
195
+ *
196
+ * @private
197
+ * @method registerActions
198
+ * @param {Controller} controller
199
+ */
200
+ function registerActions(controller) {
201
+ each(controller.actions, function(action) {
202
+ each(mapAction(action), function(actionMap) {
203
+ ensureActionIsDefined(controller, actionMap);
204
+ controller.dispatcher.on(actionEventName(controller, actionMap.name), controller[actionMap.method], controller);
205
+ }, controller);
206
+ }, controller);
207
+ }
208
+
209
+ /**
210
+ * Take an action string or mapped action and return
211
+ * an object containing the action name and method.
212
+ *
213
+ * @private
214
+ * @method mapAction
215
+ * @param {String,Object} action/mappedAction
216
+ * @return {Array} action/event maps
217
+ */
218
+ function mapAction(action) {
219
+ return isObject(action) ? map(action, createActionMap) : [createActionMap(action, action)];
220
+ }
221
+
222
+ /**
223
+ * Create a list of maps of action name/method pairs.
224
+ *
225
+ * @private
226
+ * @method createActionMap
227
+ * @param {String} method to map to action
228
+ * @param {String} action to map to method
229
+ */
230
+ function createActionMap(method, action) {
231
+ return { name: action, method: method };
232
+ }
233
+
234
+ /**
235
+ * Take a controller and an actionMap and determine if
236
+ * the action is defined on the controller. If not, throw
237
+ * an error.
238
+ *
239
+ * @private
240
+ * @method ensureActionIsDefined
241
+ * @param {Controller} controller
242
+ * @param {Object} actionMap
243
+ */
244
+ function ensureActionIsDefined(controller, actionMap) {
245
+ if (!isFunc(controller[actionMap.method])) {
246
+ throw new Error(controller.name + ' action "' + actionMap.name + ":" + actionMap.method + '" method is undefined');
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Return an event name based on the controller properties
252
+ * that make up an event name.
253
+ *
254
+ * @private
255
+ * @method actionEventName
256
+ * @param {Controller} controller
257
+ * @param {String} action
258
+ * @return String
259
+ */
260
+ function actionEventName(controller, action) {
261
+ return compact([
262
+ controller.namespace,
263
+ controller.channel,
264
+ controller.controllerEventName,
265
+ action
266
+ ]).join(controller.eventSeparator);
267
+ }
268
+
269
+ /**
270
+ * Iterate over the given controller's elements object
271
+ * and cache a reference to each selected element.
272
+ *
273
+ * @private
274
+ * @method cacheElements
275
+ * @param {Controller} controller
276
+ * @param {String} action
277
+ */
278
+ function cacheElements(controller, action) {
279
+ if (controller.elements[action]) {
280
+ each(controller.elements[action], function(selector, name) {
281
+ controller["$" + name] = $(selector);
282
+ }, controller);
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Iterate over the given action's events and register
288
+ * the event handlers on each element.
289
+ *
290
+ * @private
291
+ * @method registerEvents
292
+ * @param {Controller} controller
293
+ * @param {String} action
294
+ */
295
+ function registerEvents(controller, action) {
296
+ if (controller.events[action]) {
297
+ each(controller.events[action], function(eventMap, element) {
298
+ each(eventMap, function(method, evnt) {
299
+ var handler = controller[method];
300
+ var $element = controller["$" + element];
301
+ $element.on(evnt, handler);
302
+ }, controller);
303
+ }, controller);
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Iterate over the controller's elements
309
+ * and register to cache each action's events.
310
+ *
311
+ * @private
312
+ * @method registerElementCaching
313
+ * @param {Controller} controller
314
+ */
315
+ function registerElementCaching(controller) {
316
+ each(controller.elements, function(elements, action) {
317
+ controller.dispatcher.before(actionEventName(controller, action), function() {
318
+ cacheElements(controller, action);
319
+ }, controller);
320
+ }, controller);
321
+ }
322
+
323
+ /**
324
+ * Iterate over the controller's events
325
+ * and register to handle each action's element events.
326
+ *
327
+ * @private
328
+ * @method registerControllerEvents
329
+ * @param {Controller} controller
330
+ */
331
+ function registerControllerEvents(controller) {
332
+ each(controller.events, function(eventMap, action) {
333
+ controller.dispatcher.on(actionEventName(controller, action), function() {
334
+ registerEvents(controller, action);
335
+ }, controller);
336
+ }, controller);
337
+ }
338
+
339
+ return {
340
+ /**
341
+ * Factory function to create fresh controller objects
342
+ * with the given attributes.
343
+ *
344
+ * @method create
345
+ * @static
346
+ * @param {Object} [attrs={}]
347
+ *
348
+ * @return {Controller}
349
+ */
350
+ create: function(attrs) {
351
+ attrs = attrs || {};
352
+ if (!attrs.name) throw new Error("Controller.create(name): name is undefined");
353
+
354
+ var controller = defaults(attrs, {
355
+ /**
356
+ * Array of actions to be wired up.
357
+ *
358
+ * @property actions
359
+ * @type Array
360
+ * @default []
361
+ */
362
+ actions: [],
363
+ /**
364
+ * Namespace that controller events are namespaced under.
365
+ *
366
+ * @property namespace
367
+ * @type String
368
+ * @default ""
369
+ */
370
+ namespace: "",
371
+ /**
372
+ * Channel that controller events use under namespace.
373
+ *
374
+ * @property channel
375
+ * @type String
376
+ * @default "controller"
377
+ */
378
+ channel: "controller",
379
+ /**
380
+ * Underscored name of controller for use in events.
381
+ *
382
+ * @property controllerEventName
383
+ * @type String
384
+ * @default "controller"
385
+ */
386
+ controllerEventName: underscore(attrs.name),
387
+ /**
388
+ * Event dispatcher for registering events.
389
+ *
390
+ * @property dispatcher
391
+ * @type Dispatcher
392
+ * @default JSkit.Dispatcher.create()
393
+ */
394
+ dispatcher: JSkit.Dispatcher.create(),
395
+ /**
396
+ * Object of element names/selectors to
397
+ * cache per action.
398
+ *
399
+ * @property elements
400
+ * @type Object
401
+ * @default {}
402
+ */
403
+ elements: {},
404
+ /**
405
+ * Object of events for each action to
406
+ * register on given elements.
407
+ *
408
+ * @property events
409
+ * @type Object
410
+ * @default {}
411
+ */
412
+ events: {},
413
+ /**
414
+ * String to seperate event name segments
415
+ *
416
+ * @property eventSeparator
417
+ * @type String
418
+ * @default ":"
419
+ */
420
+ eventSeparator: ":",
421
+ /**
422
+ * Default implementation that commits no operation
423
+ * @method all
424
+ */
425
+ all: function() {},
426
+ /**
427
+ * Default implementation that commits no operation
428
+ * @method initialize
429
+ */
430
+ initialize: function() {}
431
+ });
432
+ bindAll(controller);
433
+ controller.actions.unshift("all");
434
+ registerElementCaching(controller);
435
+ registerControllerEvents(controller);
436
+ registerActions(controller);
437
+
438
+ controller.initialize();
439
+
440
+ return controller;
441
+ }
442
+ };
443
+ })();
444
+
445
+ /**
446
+ * @module JSkit
447
+ * @class Application
448
+ */
449
+ JSkit.Application = (function() {
450
+ var extend = _.extend;
451
+ var map = _.map;
452
+
453
+ /**
454
+ * Takes a string and creates a constant name
455
+ * by uppercasing and camel-casing each word
456
+ * delimited by a space.
457
+ *
458
+ * @private
459
+ * @method constantize
460
+ * @param {String} [string=""]
461
+ * @return {String}
462
+ */
463
+ function constantize(string) {
464
+ string = string || "";
465
+ if (string.match(/_|-|\s/)) {
466
+ var s = map(string.split(/_|-|\s/g), function(part, i) {
467
+ return (i > 0) ? part.charAt(0).toUpperCase() + part.slice(1) : part.toLowerCase();
468
+ }).join("");
469
+ string = s;
470
+ } else {
471
+ string = string.toString();
472
+ }
473
+ return string.charAt(0).toUpperCase() + string.slice(1);
474
+ }
475
+
476
+ return {
477
+ /**
478
+ * Creates a new application object.
479
+ *
480
+ * @method create
481
+ * @static
482
+ * @return {Object} Application object
483
+ */
484
+ create: function() {
485
+ var dispatcher = JSkit.Dispatcher.create();
486
+ return {
487
+ /**
488
+ * Controllers namespace to store Controller objects built at runtime
489
+ *
490
+ * @property Controllers
491
+ * @type Object
492
+ * @default {}
493
+ */
494
+ Controllers: {},
495
+ /**
496
+ * Dispatcher to subcribe and publish events
497
+ *
498
+ * @property Dispatcher
499
+ * @type Dispatcher
500
+ * @default Dispatcher
501
+ */
502
+ Dispatcher: dispatcher,
503
+ /**
504
+ * Creates a controller with the given name and attributes
505
+ * and returns it. It also saves a reference to the Controller
506
+ * factory used to create the controller for testing purposes.
507
+ *
508
+ * @method createController
509
+ * @param {String} name Name of the controller
510
+ * @param {Object} [attributes={}] Controller attributes
511
+ * @return {Controller}
512
+ */
513
+ createController: function(name, attrs) {
514
+ attrs = attrs || {};
515
+ if (!name) throw new Error("Application.createController(name, attrs): name is undefined");
516
+ attrs.name = name;
517
+ var controllerName = constantize(name) + "Controller";
518
+ /**
519
+ * @class ControllerFactory
520
+ */
521
+ var factory = this[controllerName] = {
522
+ /**
523
+ * Creates a fresh controller object with the original defaults
524
+ *
525
+ * @static
526
+ * @method create
527
+ * @param {Object} [attributes]
528
+ * @return {Controller}
529
+ */
530
+ create: function(attributes) {
531
+ attributes = attributes || { name: name };
532
+ return JSkit.Controller.create(extend({}, attrs, attributes));
533
+ }
534
+ };
535
+
536
+ this.Controllers[name] = factory.create({ dispatcher: dispatcher });
537
+
538
+ return this.Controllers[name];
539
+ }
540
+ };
541
+ }
542
+ };
543
+ })();
@@ -0,0 +1,3 @@
1
+ //= require jskit
2
+
3
+ var <%= RailsJskit.configuration.app_namespace %> = JSkit.createApplication();
@@ -0,0 +1,59 @@
1
+ ApplicationController.class_eval do
2
+ helper_method :jskit
3
+
4
+ def jskit(config = { namespace: nil })
5
+ events = [
6
+ application_event(config),
7
+ controller_event(config),
8
+ action_event(config)
9
+ ].join("\n")
10
+
11
+ view_context.javascript_tag(events)
12
+ end
13
+
14
+ def set_action_payload(*args)
15
+ @action_payload = payload_js(args)
16
+ end
17
+
18
+ def set_controller_payload(*args)
19
+ @controller_payload = payload_js(args)
20
+ end
21
+
22
+ def set_app_payload(*args)
23
+ @app_payload = payload_js(args)
24
+ end
25
+
26
+ private
27
+
28
+ def payload_js(payload)
29
+ comma = ", "
30
+ comma + payload.map(&:to_json).join(comma)
31
+ end
32
+
33
+ def action_event(config)
34
+ build_event_trigger(config, controller_name, action_name, @action_payload)
35
+ end
36
+
37
+ def controller_event(config)
38
+ build_event_trigger(config, controller_name, "all", @controller_payload)
39
+ end
40
+
41
+ def application_event(config)
42
+ build_event_trigger(config, "application", "all", @app_payload)
43
+ end
44
+
45
+ def build_event_trigger(config, middle_namespace, final_namespace, payload)
46
+ event = [
47
+ config[:namespace],
48
+ "controller",
49
+ middle_namespace,
50
+ final_namespace
51
+ ].compact.join(":")
52
+
53
+ [
54
+ RailsJskit.configuration.app_namespace,
55
+ "Dispatcher",
56
+ %Q(trigger("#{event}"#{payload});)
57
+ ].join(".")
58
+ end
59
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ rails generate jskit Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,8 @@
1
+ <%= RailsJskit.config.app_namespace %>.createController(<%= name %>, {
2
+ actions: [],
3
+
4
+ all: function() {
5
+ console.log("<%= name %>Controller");
6
+ }
7
+ });
8
+
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ rails generate jskit Thing
6
+
7
+ This will create:
8
+ what/will/it/create