kindred 0.0.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.
@@ -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