kindred 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,511 @@
1
+ // Domain Public by Eric Wendelin http://www.eriwen.com/ (2008)
2
+ // Luke Smith http://lucassmith.name/ (2008)
3
+ // Loic Dachary <loic@dachary.org> (2008)
4
+ // Johan Euphrosine <proppy@aminche.com> (2008)
5
+ // Oyvind Sean Kinsey http://kinsey.no/blog (2010)
6
+ // Victor Homyakov <victor-homyakov@users.sourceforge.net> (2010)
7
+ /*global module, exports, define, ActiveXObject*/
8
+ (function(global, factory) {
9
+ if (typeof exports === 'object') {
10
+ // Node
11
+ module.exports = factory();
12
+ } else if (typeof define === 'function' && define.amd) {
13
+ // AMD
14
+ define(factory);
15
+ } else {
16
+ // Browser globals
17
+ global.printStackTrace = factory();
18
+ }
19
+ }(this, function() {
20
+ /**
21
+ * Main function giving a function stack trace with a forced or passed in Error
22
+ *
23
+ * @cfg {Error} e The error to create a stacktrace from (optional)
24
+ * @cfg {Boolean} guess If we should try to resolve the names of anonymous functions
25
+ * @return {Array} of Strings with functions, lines, files, and arguments where possible
26
+ */
27
+ function printStackTrace(options) {
28
+ options = options || {guess: true};
29
+ var ex = options.e || null, guess = !!options.guess, mode = options.mode || null;
30
+ var p = new printStackTrace.implementation(), result = p.run(ex, mode);
31
+ return (guess) ? p.guessAnonymousFunctions(result) : result;
32
+ }
33
+
34
+ printStackTrace.implementation = function() {
35
+ };
36
+
37
+ printStackTrace.implementation.prototype = {
38
+ /**
39
+ * @param {Error} [ex] The error to create a stacktrace from (optional)
40
+ * @param {String} [mode] Forced mode (optional, mostly for unit tests)
41
+ */
42
+ run: function(ex, mode) {
43
+ ex = ex || this.createException();
44
+ mode = mode || this.mode(ex);
45
+ if (mode === 'other') {
46
+ return this.other(arguments.callee);
47
+ } else {
48
+ return this[mode](ex);
49
+ }
50
+ },
51
+
52
+ createException: function() {
53
+ try {
54
+ this.undef();
55
+ } catch (e) {
56
+ return e;
57
+ }
58
+ },
59
+
60
+ /**
61
+ * Mode could differ for different exception, e.g.
62
+ * exceptions in Chrome may or may not have arguments or stack.
63
+ *
64
+ * @return {String} mode of operation for the exception
65
+ */
66
+ mode: function(e) {
67
+ if (typeof window !== 'undefined' && window.navigator.userAgent.indexOf('PhantomJS') > -1) {
68
+ return 'phantomjs';
69
+ }
70
+
71
+ if (e['arguments'] && e.stack) {
72
+ return 'chrome';
73
+ }
74
+
75
+ if (e.stack && e.sourceURL) {
76
+ return 'safari';
77
+ }
78
+
79
+ if (e.stack && e.number) {
80
+ return 'ie';
81
+ }
82
+
83
+ if (e.stack && e.fileName) {
84
+ return 'firefox';
85
+ }
86
+
87
+ if (e.message && e['opera#sourceloc']) {
88
+ // e.message.indexOf("Backtrace:") > -1 -> opera9
89
+ // 'opera#sourceloc' in e -> opera9, opera10a
90
+ // !e.stacktrace -> opera9
91
+ if (!e.stacktrace) {
92
+ return 'opera9'; // use e.message
93
+ }
94
+ if (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length) {
95
+ // e.message may have more stack entries than e.stacktrace
96
+ return 'opera9'; // use e.message
97
+ }
98
+ return 'opera10a'; // use e.stacktrace
99
+ }
100
+
101
+ if (e.message && e.stack && e.stacktrace) {
102
+ // e.stacktrace && e.stack -> opera10b
103
+ if (e.stacktrace.indexOf("called from line") < 0) {
104
+ return 'opera10b'; // use e.stacktrace, format differs from 'opera10a'
105
+ }
106
+ // e.stacktrace && e.stack -> opera11
107
+ return 'opera11'; // use e.stacktrace, format differs from 'opera10a', 'opera10b'
108
+ }
109
+
110
+ if (e.stack && !e.fileName) {
111
+ // Chrome 27 does not have e.arguments as earlier versions,
112
+ // but still does not have e.fileName as Firefox
113
+ return 'chrome';
114
+ }
115
+
116
+ return 'other';
117
+ },
118
+
119
+ /**
120
+ * Given a context, function name, and callback function, overwrite it so that it calls
121
+ * printStackTrace() first with a callback and then runs the rest of the body.
122
+ *
123
+ * @param {Object} context of execution (e.g. window)
124
+ * @param {String} functionName to instrument
125
+ * @param {Function} callback function to call with a stack trace on invocation
126
+ */
127
+ instrumentFunction: function(context, functionName, callback) {
128
+ context = context || window;
129
+ var original = context[functionName];
130
+ context[functionName] = function instrumented() {
131
+ callback.call(this, printStackTrace().slice(4));
132
+ return context[functionName]._instrumented.apply(this, arguments);
133
+ };
134
+ context[functionName]._instrumented = original;
135
+ },
136
+
137
+ /**
138
+ * Given a context and function name of a function that has been
139
+ * instrumented, revert the function to it's original (non-instrumented)
140
+ * state.
141
+ *
142
+ * @param {Object} context of execution (e.g. window)
143
+ * @param {String} functionName to de-instrument
144
+ */
145
+ deinstrumentFunction: function(context, functionName) {
146
+ if (context[functionName].constructor === Function &&
147
+ context[functionName]._instrumented &&
148
+ context[functionName]._instrumented.constructor === Function) {
149
+ context[functionName] = context[functionName]._instrumented;
150
+ }
151
+ },
152
+
153
+ /**
154
+ * Given an Error object, return a formatted Array based on Chrome's stack string.
155
+ *
156
+ * @param e - Error object to inspect
157
+ * @return Array<String> of function calls, files and line numbers
158
+ */
159
+ chrome: function(e) {
160
+ return (e.stack + '\n')
161
+ .replace(/^[\s\S]+?\s+at\s+/, ' at ') // remove message
162
+ .replace(/^\s+(at eval )?at\s+/gm, '') // remove 'at' and indentation
163
+ .replace(/^([^\(]+?)([\n$])/gm, '{anonymous}() ($1)$2')
164
+ .replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}() ($1)')
165
+ .replace(/^(.+) \((.+)\)$/gm, '$1@$2')
166
+ .split('\n')
167
+ .slice(0, -1);
168
+ },
169
+
170
+ /**
171
+ * Given an Error object, return a formatted Array based on Safari's stack string.
172
+ *
173
+ * @param e - Error object to inspect
174
+ * @return Array<String> of function calls, files and line numbers
175
+ */
176
+ safari: function(e) {
177
+ return e.stack.replace(/\[native code\]\n/m, '')
178
+ .replace(/^(?=\w+Error\:).*$\n/m, '')
179
+ .replace(/^@/gm, '{anonymous}()@')
180
+ .split('\n');
181
+ },
182
+
183
+ /**
184
+ * Given an Error object, return a formatted Array based on IE's stack string.
185
+ *
186
+ * @param e - Error object to inspect
187
+ * @return Array<String> of function calls, files and line numbers
188
+ */
189
+ ie: function(e) {
190
+ return e.stack
191
+ .replace(/^\s*at\s+(.*)$/gm, '$1')
192
+ .replace(/^Anonymous function\s+/gm, '{anonymous}() ')
193
+ .replace(/^(.+)\s+\((.+)\)$/gm, '$1@$2')
194
+ .split('\n')
195
+ .slice(1);
196
+ },
197
+
198
+ /**
199
+ * Given an Error object, return a formatted Array based on Firefox's stack string.
200
+ *
201
+ * @param e - Error object to inspect
202
+ * @return Array<String> of function calls, files and line numbers
203
+ */
204
+ firefox: function(e) {
205
+ return e.stack.replace(/(?:\n@:0)?\s+$/m, '')
206
+ .replace(/^(?:\((\S*)\))?@/gm, '{anonymous}($1)@')
207
+ .split('\n');
208
+ },
209
+
210
+ opera11: function(e) {
211
+ var ANON = '{anonymous}', lineRE = /^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/;
212
+ var lines = e.stacktrace.split('\n'), result = [];
213
+
214
+ for (var i = 0, len = lines.length; i < len; i += 2) {
215
+ var match = lineRE.exec(lines[i]);
216
+ if (match) {
217
+ var location = match[4] + ':' + match[1] + ':' + match[2];
218
+ var fnName = match[3] || "global code";
219
+ fnName = fnName.replace(/<anonymous function: (\S+)>/, "$1").replace(/<anonymous function>/, ANON);
220
+ result.push(fnName + '@' + location + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
221
+ }
222
+ }
223
+
224
+ return result;
225
+ },
226
+
227
+ opera10b: function(e) {
228
+ // "<anonymous function: run>([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" +
229
+ // "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" +
230
+ // "@file://localhost/G:/js/test/functional/testcase1.html:15"
231
+ var lineRE = /^(.*)@(.+):(\d+)$/;
232
+ var lines = e.stacktrace.split('\n'), result = [];
233
+
234
+ for (var i = 0, len = lines.length; i < len; i++) {
235
+ var match = lineRE.exec(lines[i]);
236
+ if (match) {
237
+ var fnName = match[1] ? (match[1] + '()') : "global code";
238
+ result.push(fnName + '@' + match[2] + ':' + match[3]);
239
+ }
240
+ }
241
+
242
+ return result;
243
+ },
244
+
245
+ /**
246
+ * Given an Error object, return a formatted Array based on Opera 10's stacktrace string.
247
+ *
248
+ * @param e - Error object to inspect
249
+ * @return Array<String> of function calls, files and line numbers
250
+ */
251
+ opera10a: function(e) {
252
+ // " Line 27 of linked script file://localhost/G:/js/stacktrace.js\n"
253
+ // " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function foo\n"
254
+ var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;
255
+ var lines = e.stacktrace.split('\n'), result = [];
256
+
257
+ for (var i = 0, len = lines.length; i < len; i += 2) {
258
+ var match = lineRE.exec(lines[i]);
259
+ if (match) {
260
+ var fnName = match[3] || ANON;
261
+ result.push(fnName + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
262
+ }
263
+ }
264
+
265
+ return result;
266
+ },
267
+
268
+ // Opera 7.x-9.2x only!
269
+ opera9: function(e) {
270
+ // " Line 43 of linked script file://localhost/G:/js/stacktrace.js\n"
271
+ // " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n"
272
+ var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)/i;
273
+ var lines = e.message.split('\n'), result = [];
274
+
275
+ for (var i = 2, len = lines.length; i < len; i += 2) {
276
+ var match = lineRE.exec(lines[i]);
277
+ if (match) {
278
+ result.push(ANON + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
279
+ }
280
+ }
281
+
282
+ return result;
283
+ },
284
+
285
+ phantomjs: function(e) {
286
+ var ANON = '{anonymous}', lineRE = /(\S+) \((\S+)\)/i;
287
+ var lines = e.stack.split('\n'), result = [];
288
+
289
+ for (var i = 1, len = lines.length; i < len; i++) {
290
+ lines[i] = lines[i].replace(/^\s+at\s+/gm, '');
291
+ var match = lineRE.exec(lines[i]);
292
+ if (match) {
293
+ result.push(match[1] + '()@' + match[2]);
294
+ }
295
+ else {
296
+ result.push(ANON + '()@' + lines[i]);
297
+ }
298
+ }
299
+
300
+ return result;
301
+ },
302
+
303
+ // Safari 5-, IE 9-, and others
304
+ other: function(curr) {
305
+ var ANON = '{anonymous}', fnRE = /function(?:\s+([\w$]+))?\s*\(/, stack = [], fn, args, maxStackSize = 10;
306
+ var slice = Array.prototype.slice;
307
+ while (curr && stack.length < maxStackSize) {
308
+ fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
309
+ try {
310
+ args = slice.call(curr['arguments'] || []);
311
+ } catch (e) {
312
+ args = ['Cannot access arguments: ' + e];
313
+ }
314
+ stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')';
315
+ try {
316
+ curr = curr.caller;
317
+ } catch (e) {
318
+ stack[stack.length] = 'Cannot access caller: ' + e;
319
+ break;
320
+ }
321
+ }
322
+ return stack;
323
+ },
324
+
325
+ /**
326
+ * Given arguments array as a String, substituting type names for non-string types.
327
+ *
328
+ * @param {Arguments,Array} args
329
+ * @return {String} stringified arguments
330
+ */
331
+ stringifyArguments: function(args) {
332
+ var result = [];
333
+ var slice = Array.prototype.slice;
334
+ for (var i = 0; i < args.length; ++i) {
335
+ var arg = args[i];
336
+ if (arg === undefined) {
337
+ result[i] = 'undefined';
338
+ } else if (arg === null) {
339
+ result[i] = 'null';
340
+ } else if (arg.constructor) {
341
+ // TODO constructor comparison does not work for iframes
342
+ if (arg.constructor === Array) {
343
+ if (arg.length < 3) {
344
+ result[i] = '[' + this.stringifyArguments(arg) + ']';
345
+ } else {
346
+ result[i] = '[' + this.stringifyArguments(slice.call(arg, 0, 1)) + '...' + this.stringifyArguments(slice.call(arg, -1)) + ']';
347
+ }
348
+ } else if (arg.constructor === Object) {
349
+ result[i] = '#object';
350
+ } else if (arg.constructor === Function) {
351
+ result[i] = '#function';
352
+ } else if (arg.constructor === String) {
353
+ result[i] = '"' + arg + '"';
354
+ } else if (arg.constructor === Number) {
355
+ result[i] = arg;
356
+ } else {
357
+ result[i] = '?';
358
+ }
359
+ }
360
+ }
361
+ return result.join(',');
362
+ },
363
+
364
+ sourceCache: {},
365
+
366
+ /**
367
+ * @return {String} the text from a given URL
368
+ */
369
+ ajax: function(url) {
370
+ var req = this.createXMLHTTPObject();
371
+ if (req) {
372
+ try {
373
+ req.open('GET', url, false);
374
+ //req.overrideMimeType('text/plain');
375
+ //req.overrideMimeType('text/javascript');
376
+ req.send(null);
377
+ //return req.status == 200 ? req.responseText : '';
378
+ return req.responseText;
379
+ } catch (e) {
380
+ }
381
+ }
382
+ return '';
383
+ },
384
+
385
+ /**
386
+ * Try XHR methods in order and store XHR factory.
387
+ *
388
+ * @return {XMLHttpRequest} XHR function or equivalent
389
+ */
390
+ createXMLHTTPObject: function() {
391
+ var xmlhttp, XMLHttpFactories = [
392
+ function() {
393
+ return new XMLHttpRequest();
394
+ }, function() {
395
+ return new ActiveXObject('Msxml2.XMLHTTP');
396
+ }, function() {
397
+ return new ActiveXObject('Msxml3.XMLHTTP');
398
+ }, function() {
399
+ return new ActiveXObject('Microsoft.XMLHTTP');
400
+ }
401
+ ];
402
+ for (var i = 0; i < XMLHttpFactories.length; i++) {
403
+ try {
404
+ xmlhttp = XMLHttpFactories[i]();
405
+ // Use memoization to cache the factory
406
+ this.createXMLHTTPObject = XMLHttpFactories[i];
407
+ return xmlhttp;
408
+ } catch (e) {
409
+ }
410
+ }
411
+ },
412
+
413
+ /**
414
+ * Given a URL, check if it is in the same domain (so we can get the source
415
+ * via Ajax).
416
+ *
417
+ * @param url {String} source url
418
+ * @return {Boolean} False if we need a cross-domain request
419
+ */
420
+ isSameDomain: function(url) {
421
+ return typeof location !== "undefined" && url.indexOf(location.hostname) !== -1; // location may not be defined, e.g. when running from nodejs.
422
+ },
423
+
424
+ /**
425
+ * Get source code from given URL if in the same domain.
426
+ *
427
+ * @param url {String} JS source URL
428
+ * @return {Array} Array of source code lines
429
+ */
430
+ getSource: function(url) {
431
+ // TODO reuse source from script tags?
432
+ if (!(url in this.sourceCache)) {
433
+ this.sourceCache[url] = this.ajax(url).split('\n');
434
+ }
435
+ return this.sourceCache[url];
436
+ },
437
+
438
+ guessAnonymousFunctions: function(stack) {
439
+ for (var i = 0; i < stack.length; ++i) {
440
+ var reStack = /\{anonymous\}\(.*\)@(.*)/,
441
+ reRef = /^(.*?)(?::(\d+))(?::(\d+))?(?: -- .+)?$/,
442
+ frame = stack[i], ref = reStack.exec(frame);
443
+
444
+ if (ref) {
445
+ var m = reRef.exec(ref[1]);
446
+ if (m) { // If falsey, we did not get any file/line information
447
+ var file = m[1], lineno = m[2], charno = m[3] || 0;
448
+ if (file && this.isSameDomain(file) && lineno) {
449
+ var functionName = this.guessAnonymousFunction(file, lineno, charno);
450
+ stack[i] = frame.replace('{anonymous}', functionName);
451
+ }
452
+ }
453
+ }
454
+ }
455
+ return stack;
456
+ },
457
+
458
+ guessAnonymousFunction: function(url, lineNo, charNo) {
459
+ var ret;
460
+ try {
461
+ ret = this.findFunctionName(this.getSource(url), lineNo);
462
+ } catch (e) {
463
+ ret = 'getSource failed with url: ' + url + ', exception: ' + e.toString();
464
+ }
465
+ return ret;
466
+ },
467
+
468
+ findFunctionName: function(source, lineNo) {
469
+ // FIXME findFunctionName fails for compressed source
470
+ // (more than one function on the same line)
471
+ // function {name}({args}) m[1]=name m[2]=args
472
+ var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/;
473
+ // {name} = function ({args}) TODO args capture
474
+ // /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function(?:[^(]*)/
475
+ var reFunctionExpression = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/;
476
+ // {name} = eval()
477
+ var reFunctionEvaluation = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/;
478
+ // Walk backwards in the source lines until we find
479
+ // the line which matches one of the patterns above
480
+ var code = "", line, maxLines = Math.min(lineNo, 20), m, commentPos;
481
+ for (var i = 0; i < maxLines; ++i) {
482
+ // lineNo is 1-based, source[] is 0-based
483
+ line = source[lineNo - i - 1];
484
+ commentPos = line.indexOf('//');
485
+ if (commentPos >= 0) {
486
+ line = line.substr(0, commentPos);
487
+ }
488
+ // TODO check other types of comments? Commented code may lead to false positive
489
+ if (line) {
490
+ code = line + code;
491
+ m = reFunctionExpression.exec(code);
492
+ if (m && m[1]) {
493
+ return m[1];
494
+ }
495
+ m = reFunctionDeclaration.exec(code);
496
+ if (m && m[1]) {
497
+ //return m[1] + "(" + (m[2] || "") + ")";
498
+ return m[1];
499
+ }
500
+ m = reFunctionEvaluation.exec(code);
501
+ if (m && m[1]) {
502
+ return m[1];
503
+ }
504
+ }
505
+ }
506
+ return '(?)';
507
+ }
508
+ };
509
+
510
+ return printStackTrace;
511
+ }));
@@ -0,0 +1,3 @@
1
+ class App.Template
2
+ @set_templates: (args) ->
3
+ @template_info = args
@@ -0,0 +1,9 @@
1
+ class App.UUID
2
+ @generate = ->
3
+ d = new Date().getTime()
4
+ uuid = "xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) ->
5
+ r = (d + Math.random() * 16) % 16 | 0
6
+ d = Math.floor(d / 16)
7
+ ((if c is "x" then r else (r & 0x7 | 0x8))).toString 16
8
+ )
9
+ uuid
@@ -0,0 +1,25 @@
1
+ App.VirtualClass = (classes...)->
2
+ classes.reduceRight (Parent, Child)->
3
+ class Child_Projection extends Parent
4
+ constructor: ->
5
+ # Temporary replace Child.__super__ and call original `constructor`
6
+ child_super = Child.__super__
7
+ Child.__super__ = Child_Projection.__super__
8
+ Child.apply @, arguments
9
+ Child.__super__ = child_super
10
+
11
+ # If Child.__super__ not exists, manually call parent `constructor`
12
+ unless child_super?
13
+ super
14
+
15
+ # Mixin prototype properties, except `constructor`
16
+ for own key of Child::
17
+ if Child::[key] isnt Child
18
+ Child_Projection::[key] = Child::[key]
19
+
20
+ # Mixin static properties, except `__super__`
21
+ for own key of Child
22
+ if Child[key] isnt Object.getPrototypeOf(Child::)
23
+ Child_Projection[key] = Child[key]
24
+
25
+ Child_Projection
@@ -0,0 +1,59 @@
1
+ module TemplateHelper
2
+ def template(model: nil, collection: nil, target_uuid: nil, &block)
3
+ model_name = if collection.present?
4
+ ActiveModel::Naming.singular(collection.first)
5
+ else
6
+ model
7
+ end
8
+
9
+ @kindred_hash ||= {}
10
+ @kindred_hash.merge!({
11
+ model_name => {
12
+ template: capture(&block),
13
+ collection: collection,
14
+ target_uuid: target_uuid,
15
+ }
16
+ })
17
+ self.controller.instance_variable_set(:@kindred_hash, @kindred_hash)
18
+ return nil
19
+ end
20
+
21
+ def target(object)
22
+ "data-target data-target-uuid=" + k_try(object, :uuid).to_s
23
+ end
24
+
25
+ def k_content_tag(element, attribute = nil, object = nil, content_or_options_with_block = nil, options = {}, escape = true, &block)
26
+ content_tag(element, nil, options.merge({data: { attr: attribute, k_uuid: k_try(object, :uuid), val: ""} }))
27
+ end
28
+
29
+ def k_hidden_field_tag(name, value=nil, object=nil, delegate_to=nil, options = {})
30
+ hidden_field_tag name, value, options.merge({data: { attr: name, k_uuid: k_try(object, :uuid), val: value } })
31
+ end
32
+
33
+ def k_text_field_tag(object, attribute, options={})
34
+ text_field_tag attribute, nil, options.merge({data: { attr: attribute, k_uuid: k_try(object, :uuid), val: "" } })
35
+ end
36
+
37
+ def k_check_box_tag(object, name, value=nil, checked = false, options = {})
38
+ check_box_tag name, value, checked, options.merge({data: { attr: name, k_uuid: k_try(object, :uuid), val: ""} })
39
+ end
40
+
41
+ def k_select_tag(object, name, option_tags = nil, options = {})
42
+ select_tag name, option_tags, options.merge(data: { attr: name, k_uuid: k_try(object, :uuid), val: "" })
43
+ end
44
+
45
+ def error_for(object, attribute)
46
+ tag("small", data: {error: "", k_uuid: '', attr: attribute}, class: "error")
47
+ end
48
+
49
+ def kindred_model_data
50
+ "<div data-kindred-model style='display:none;'></div>".html_safe
51
+ end
52
+
53
+ def k_try(object, method)
54
+ unless object.is_a?(Symbol)
55
+ object.try method
56
+ end
57
+ end
58
+
59
+ end
data/kindred.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'kindred/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "kindred"
8
+ spec.version = Kindred::VERSION
9
+ spec.authors = ["Mike Piccolo"]
10
+ spec.email = ["mpiccolo@newleaders.com"]
11
+ spec.summary = %q{}
12
+ spec.description = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "rails", ">= 3.0"
22
+ spec.add_runtime_dependency "happy_place", ">= 0.0.6"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ end
@@ -0,0 +1,8 @@
1
+ require 'rails'
2
+
3
+ module Kindred
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Kindred
2
+ VERSION = "0.0.1"
3
+ end
data/lib/kindred.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "kindred/version"
2
+
3
+ module Kindred
4
+ require "kindred/engine"
5
+ end