talk 2.0.5 → 2.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f1f1b965faec105c8139b2c31c25093ad3f7a07d
4
- data.tar.gz: 65ad8dfa35eca34ccf71e9348760e42fb68cd639
3
+ metadata.gz: ef1157116afc257fd1bfc78676ddc0cb737015f7
4
+ data.tar.gz: 93d87b8b6b4e8ad6cbd383df6870adf3399e824a
5
5
  SHA512:
6
- metadata.gz: 09583344d986efe5ff4d06b71c54dabcbee808d27586dd623a79d5c1451ce3e4f4e967a7096286fdbc7f9df77fe47e6e718359b402509b11cadf7191a5df8831
7
- data.tar.gz: e4501266f0041de42d2c9b4556a04a98645318b850b68d2c9cc031413d5f4fb0bc838ad1a749430d86c006c8bdafa387c81055fa1564c37dcdc273f48449ab1b
6
+ metadata.gz: e598d2d1ea1997a9c45cbe1c4989eb390ea4f08df5c1c839379b9fb19e25bcd5d2ea451fa5bb88f9d29096afc6f258f2b6d6103c57eb096c2105ad39f605ec2e
7
+ data.tar.gz: 7bbaeab38727d136e065c54113792bd7fd3cea40794bb0f0f9e049cb860e1060269a477ec21ac120136a15eb93e9db22b88f23f8d330928a445d37a23467ff60
data/Gemfile CHANGED
@@ -2,6 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  gem 'trollop'
4
4
  gem 'json'
5
+ gem 'uglifier'
5
6
 
6
7
  group :test do
7
8
  gem 'cucumber'
data/lib/context.rb CHANGED
@@ -18,7 +18,7 @@ module Talk
18
18
 
19
19
  ## Parser interface
20
20
 
21
- def parse(word, file, line)
21
+ def parse(word, file=nil, line=nil)
22
22
  @property_words.push word
23
23
  end
24
24
 
data/lib/context_class.rb CHANGED
@@ -185,6 +185,12 @@ module Talk
185
185
 
186
186
  add_tag_singular(name) unless params[:multi]
187
187
  add_tag_required(name) if params[:required]
188
+ postprocess(lambda do |c|
189
+ return if c.has_key? name
190
+ tag = c.start_tag(name, c.file, c.line)
191
+ tag.parse(params[:default])
192
+ c.end_tag(tag)
193
+ end) if params[:default]
188
194
  end
189
195
 
190
196
  def add_tag_singular(name)
@@ -0,0 +1,81 @@
1
+ require 'uglifier'
2
+
3
+ def make_source
4
+ @prefix = common_class_prefix if meta(:namespace) == "true"
5
+ transform = meta(:minify) ? lambda { |source| Uglifier.compile(source)} : nil
6
+ generate_template("talk.js", "talk.js.erb", transform)
7
+ end
8
+
9
+ def autogenerated_warning
10
+ <<-AUTOGEN_DONE
11
+ // Autogenerated from Talk
12
+ // Please do not edit this file directly. Instead, modify the underlying .talk files.
13
+ AUTOGEN_DONE
14
+ end
15
+
16
+ def comment_block(tag, indent_level=0)
17
+ lines = []
18
+ indent = "\t" * indent_level
19
+ lines.push(indent + "/*")
20
+ lines.push(wrap_text_to_width(tag[:description], 80, indent + " * ")) unless tag[:description].nil?
21
+ lines.push(indent + " * ")
22
+ lines.push(indent + " * " + definition_reference(tag))
23
+ lines.push(indent + " */")
24
+
25
+ lines.join("\n")
26
+ end
27
+
28
+ def definition_reference(tag)
29
+ "@talkFile #{tag[:__meta][:file]}:#{tag[:__meta][:line]}"
30
+ end
31
+
32
+ def class_line(cls)
33
+ @rendered ||= Set.new
34
+ out = []
35
+
36
+ return nil unless cls[:implement]
37
+ return nil if cls[:name] == rootclass
38
+ return nil if @rendered.include? cls
39
+ @rendered.add(cls)
40
+ out << class_line(class_named(cls[:inherits])) unless cls[:inherits].nil?
41
+ out << comment_block(cls)
42
+ out << "// " + cls[:name]
43
+
44
+ fields = {}
45
+ cls[:field].each do |field|
46
+ mapped = mapped_name(cls[:name], field[:name], :field)
47
+ fields[mapped] = {typeStack:field[:type]}
48
+ fields[mapped][:canonicalName] = field[:name] unless mapped == field[:name]
49
+ end
50
+
51
+ out << "TalkObject.addClass('#{cls[:name]}', #{fields.to_json}, #{truncated_name(superclass(cls))});"
52
+ out.join("\n")
53
+ end
54
+
55
+ def protocol_line(proto)
56
+ methods = proto[:method].map { |m| m[:name] }
57
+ out = []
58
+ out << comment_block(proto)
59
+ out << "TalkObject.addProtocol('#{proto[:name]}', #{methods.to_json});"
60
+ out.join("\n")
61
+ end
62
+
63
+ def glossary_line(glossary)
64
+ terms = {}
65
+ glossary[:term].each { |term| terms[term[:name]] = term[:value] }
66
+
67
+ out = []
68
+ out << comment_block(glossary)
69
+ out << "TalkObject.addGlossary('#{glossary[:name]}', #{terms.to_json});"
70
+ out.join("\n")
71
+ end
72
+
73
+ def enumeration_line(enumeration)
74
+ constants = {}
75
+ enumeration[:constant].each { |constant| constants[constant[:name]] = constant[:value] }
76
+
77
+ out = []
78
+ out << comment_block(enumeration)
79
+ out << "TalkObject.addEnumeration('#{enumeration[:name]}', #{constants.to_json});"
80
+ out.join("\n")
81
+ end
@@ -0,0 +1,860 @@
1
+ <%= autogenerated_warning %>
2
+ /*********************************************************************************
3
+ * TalkObject
4
+ *
5
+ * This module defines the constructor and methods for accessing TalkObjects
6
+ */
7
+
8
+ "use strict";
9
+
10
+ /**
11
+ * TalkObject - variable definition
12
+ *
13
+ * Takes an object and if found loads object
14
+ */
15
+
16
+ var TalkObject = function(obj)
17
+ {
18
+ this.__definition = { __class: { type: "string" } };
19
+ if(obj) this.loadFromObject(obj);
20
+ };
21
+
22
+ /**
23
+ * TalkObject vars, et-al
24
+ */
25
+ TalkObject.glossaries = {};
26
+ TalkObject.shortglossaries = {};
27
+ TalkObject.enumerations = {};
28
+ TalkObject.shortenumerations = {};
29
+ TalkObject.allConstants = {};
30
+ TalkObject.validators = {};
31
+ TalkObject.protocols = {};
32
+ TalkObject.methods = {};
33
+ TalkObject.prototype.__class = "TalkObject";
34
+ TalkObject.classes = { "TalkObject":TalkObject };
35
+ TalkObject.shortclasses = { "TalkObject":TalkObject };
36
+
37
+ /**
38
+ * TalkValidationError - Creates an error message on validation error
39
+ */
40
+ TalkObject.TalkValidationError = function(message)
41
+ {
42
+
43
+ this.name = "TalkValidationError";
44
+ this.message = message || "Error validating Talk object";
45
+ };
46
+
47
+ TalkObject.TalkValidationError.prototype = new Error();
48
+ TalkObject.TalkValidationError.prototype.constructor = TalkObject.TalkValidationError;
49
+ TalkObject.TalkValidationError.makeError = function(cls, key, param, msg)
50
+ {
51
+ var errString = "Invalid value " + cls.__class + "." + key +
52
+ (param ? ("=" + JSON.stringify(param) + ": ") : " - ") + msg;
53
+ if((typeof logger != 'undefined') && logger) logger.debug(errString);
54
+ return new TalkObject.TalkValidationError(errString);
55
+ };
56
+
57
+
58
+ /**
59
+ * TalkImplementationError - Creates an error message on implementation error
60
+ * (which shouldn't happen in production code)
61
+ */
62
+ TalkObject.TalkImplementationError = function(message) {
63
+ //#JSCOVERAGE_IF 0
64
+ // We shouldn't have this happen in production.
65
+ this.name = "TalkImplementationError";
66
+ this.message = message || "Error validating Talk object";
67
+ //#JSCOVERAGE_ENDIF
68
+ };
69
+
70
+ TalkObject.TalkImplementationError.prototype= new Error();
71
+ TalkObject.TalkImplementationError.prototype.constructor = TalkObject.TalkImplementationError;
72
+ TalkObject.TalkImplementationError.makeError = function(cls, key, msg)
73
+ {
74
+ //#JSCOVERAGE_IF 0
75
+ // This should not happen in production.
76
+ var errString = "Invalid server implementation of " + cls.__class + "."
77
+ + key + ": " + msg;
78
+ if((typeof logger != 'undefined') && logger) logger.error(errString);
79
+ return new TalkObject.TalkImplementationError(errString);
80
+ //#JSCOVERAGE_ENDIF
81
+ };
82
+
83
+ /**
84
+ * __validateString - augment the TalkObject with validation of strings
85
+ */
86
+ TalkObject.prototype.__validateString = function(key, param, unsigned, size)
87
+ {
88
+ var def = this.__definition[key];
89
+ if(typeof(param) != 'string') { // Make sure it a string type
90
+ throw TalkObject.TalkValidationError.makeError(
91
+ this, key, param, "Expected to be a string");
92
+ }
93
+
94
+ // Check against pattern matching
95
+ if(def.regex && !param.match(new RegExp(def.regex))) {
96
+ throw TalkObject.TalkValidationError.makeError(
97
+ this, key, param, "Expected to match pattern: " + def.regex);
98
+ }
99
+
100
+ // Check over our constraints if they exist
101
+ if(def.constraints) {
102
+ var found = false;
103
+ for(var constraintIdx in def.constraints)
104
+ {
105
+ var constraint = def.constraints[constraintIdx];
106
+ var glossary = TalkObject.glossary(constraint);
107
+ if(!glossary) {
108
+ /* The constraint references a non-existent glossary. This is an issue
109
+ ** in our Talk renderer. This shouldn't happen. */
110
+ //#JSCOVERAGE_IF 0
111
+ var errMsg = "Constrains on undefined glossary " + constraint;
112
+ throw TalkObject.TalkImplementationError.makeError( this, key, errMsg);
113
+ //#JSCOVERAGE_ENDIF
114
+ }
115
+
116
+ for(var i = 0; i < glossary.__values.length; i++)
117
+ {
118
+ if(glossary.__values[i] == param) {
119
+ found = true;
120
+ break;
121
+ }
122
+ }
123
+ }
124
+
125
+ if(!found) {
126
+ var errMsg = "Expected to be in at least one of glossaries "
127
+ + def.constraints.join(", ");
128
+ throw TalkObject.TalkValidationError.makeError(this, key, param, errMsg);
129
+ }
130
+ }
131
+
132
+ return param;
133
+ };
134
+
135
+
136
+ /**
137
+ * __validateInteger - augment the TalkObject with validation of Integer
138
+ */
139
+ TalkObject.prototype.__validateInteger = function(key, param, unsigned, size)
140
+ {
141
+ var range = {};
142
+ var def = this.__definition[key];
143
+
144
+ // Validate number
145
+ if(typeof(param) != 'number')
146
+ {
147
+ var errMsg = "Expected " + key + " to be a number";
148
+ throw TalkObject.TalkValidationError.makeError(this, key, param, errMsg);
149
+ }
150
+
151
+ // compute the ranges for validation checking
152
+ if(unsigned)
153
+ {
154
+ range.min = 0;
155
+ range.max = Math.pow(2, size);
156
+ } else {
157
+ range.min = -Math.pow(2, size-1);
158
+ range.max = Math.pow(2, size-1)-1;
159
+ }
160
+
161
+ if(def.minValue !== undefined && def.minValue !== null)
162
+ {
163
+ range.min = Math.max(def.minVlaue, range.min);
164
+ }
165
+
166
+ if(def.maxValue !== undefined && def.maxValue !== null)
167
+ {
168
+ range.max = Math.min(def.maxValue, range.max);
169
+ }
170
+
171
+ // check for constraints
172
+ if(def.constraints) {
173
+ var found = false;
174
+ for(var constraintIdx in def.constraints)
175
+ {
176
+ var constraint = def.constraints[constraintIdx];
177
+ var enumeration = TalkObject.enumeration(constraint);
178
+ if(!enumeration)
179
+ {
180
+ //#JSCOVERAGE_IF 0
181
+ /* The constraint references a non-existent enumeration. This is an
182
+ ** issue in our Talk renderer. This shouldn't happen. */
183
+ var errMsg = "Constrains on undefined enumeration " + constraint;
184
+ throw TalkObject.TalkImplementationError.makeError(this, key, errMsg);
185
+ //#JSCOVERAGE_ENDIF
186
+ }
187
+
188
+ for(var i = 0; i < enumeration.__values.length; i++)
189
+ {
190
+ if(enumeration.__values[i] == param)
191
+ {
192
+ found = true;
193
+ break;
194
+ }
195
+ }
196
+ }
197
+
198
+ if(!found)
199
+ {
200
+ var errMsg = "Expected to be in " + def.constraints.join(", ");
201
+ throw TalkObject.TalkValidationError.makeError(this, key, param, errMsg);
202
+ }
203
+ }
204
+
205
+ if(param < range.min)
206
+ {
207
+ throw TalkObject.TalkValidationError.makeError( this, key, param, "Expected >= " + range.min);
208
+ }
209
+
210
+ if(param > range.max)
211
+ {
212
+ throw TalkObject.TalkValidationError.makeError(this, key, param, "Expected <= " + range.max);
213
+ }
214
+
215
+ if(param != Math.floor(param))
216
+ {
217
+ throw TalkObject.TalkValidationError.makeError( this, key, param, "Expected integer");
218
+ }
219
+
220
+ return param;
221
+ };
222
+
223
+ /**
224
+ * __validateReal - augment the TalkObject with validation of Real
225
+ */
226
+ TalkObject.prototype.__validateReal = function(key, param)
227
+ {
228
+ var def = this.__definition[key];
229
+ if(typeof(param) != 'number') return "Expected numeric value for " + key;
230
+ if(def.minValue !== undefined && def.minValue !== null && param < def.minValue)
231
+ {
232
+ throw TalkObject.TalkValidationError.makeError(this, key, param, "Expected >= " + def.minValue);
233
+ }
234
+ if(def.maxValue !== undefined && def.maxValue !== null && param > def.maxValue)
235
+ {
236
+ throw TalkObject.TalkValidationError.makeError(this, key, param, "Expected <= " + def.maxValue);
237
+ }
238
+
239
+ return param;
240
+ };
241
+
242
+ /**
243
+ * __validateBool - augment the TalkObject with validation of Boolean
244
+ */
245
+ TalkObject.prototype.__validateBool = function(key, param)
246
+ {
247
+ if(typeof(param) != 'boolean' && typeof(param) != 'number')
248
+ {
249
+ throw TalkObject.TalkValidationError.makeError(this, key, param, "Expected boolean");
250
+ }
251
+ return param;
252
+ };
253
+
254
+ /**
255
+ * __validateObject - augment the TalkObject with validation of Object
256
+ */
257
+ TalkObject.prototype.__validateObject = function(key, param)
258
+ {
259
+ return param;
260
+ };
261
+
262
+ /**
263
+ * __validateTalkObject - augment the TalkObject with validation of TalkObject
264
+ */
265
+ TalkObject.prototype.__validateTalkObject = function(key, param, type)
266
+ {
267
+ var talkClass;
268
+ if(param && '__class' in param)
269
+ {
270
+ talkClass = TalkObject.classNamed(param.__class);
271
+ if(!TalkObject.isSubclass(param.__class, type))
272
+ {
273
+ throw TalkObject.TalkValidationError.makeError( this, key, param,
274
+ "Expected a subclass of " + type);
275
+ }
276
+ if(!talkClass)
277
+ {
278
+ throw TalkObject.TalkValidationError.makeError( this, key, param,
279
+ "Value has unknown class " + param.__class);
280
+ }
281
+ } else {
282
+ talkClass = TalkObject.classNamed(type);
283
+ if(!talkClass)
284
+ {
285
+ throw TalkObject.TalkImplementationError.makeError( this, key, param,
286
+ "Implementation specifies " + "unknown class " + type);
287
+ }
288
+ }
289
+
290
+ var instance = new talkClass(param);
291
+ instance.loadFromObject(param);
292
+ return instance;
293
+ };
294
+
295
+ /**
296
+ * __validateTypeStack - augment the TalkObject with validation of TypeStack
297
+ */
298
+ TalkObject.prototype.__validateTypeStack = function(key, param, typeStack)
299
+ {
300
+ var def = this.__definition[key];
301
+ var type = typeStack[typeStack.length-1];
302
+ var isBase = typeStack.length == 1;
303
+ typeStack = typeStack.slice(0, def.typeStack.length-1);
304
+
305
+ if(param === undefined || param === null) {
306
+ if(def.required && def.typeStack.length-1 == typeStack.length)
307
+ {
308
+ throw TalkObject.TalkValidationError.makeError( this, key, param, "Field is required");
309
+ }
310
+
311
+ return param;
312
+ }
313
+
314
+ if(isBase)
315
+ {
316
+ if(type == '[]' || type == '{}')
317
+ {
318
+ //#JSCOVERAGE_IF 0
319
+ // Implementation has a bad type stack. This shouldn't happen.
320
+ throw TalkObject.TalkImplementationError.makeError( this, key,
321
+ "Nonsense field definition; expected individual, got " + type);
322
+ //#JSCOVERAGE_ENDIF
323
+ }
324
+
325
+ var val;
326
+ var intMatch = type.match(/^(u)?int([0-9]+)$/);
327
+
328
+ if(intMatch)
329
+ {
330
+ val = this.__validateInteger(key, param, intMatch[1] == 'u', intMatch[2]);
331
+ } else if(type == 'string') {
332
+ val = this.__validateString(key, param);
333
+ } else if(type == 'real') {
334
+ val = this.__validateReal(key, param);
335
+ } else if(type == 'bool') {
336
+ val = this.__validateBool(key, param);
337
+ } else if(type == 'talkobject') {
338
+ if(!param.__class)
339
+ {
340
+ throw new TalkObject.TalkValidationError.makeError( this, key, param,
341
+ "Expected explicit class identification in generic field.");
342
+ }
343
+ val = this.__validateTalkObject(key, param, "TalkObject");
344
+ } else if(TalkObject.classNamed(type)) {
345
+ val = this.__validateTalkObject(key, param, type);
346
+ if(val && !val.__class)
347
+ {
348
+ val.__class = type;
349
+ }
350
+ } else if(type == 'object') {
351
+ val = this.__validateObject(key, param);
352
+ } else {
353
+ throw TalkObject.TalkImplementationError.makeError( this, key, "Unrecognized type " + type);
354
+ }
355
+
356
+ return val;
357
+ } else {
358
+ // This must be a collection type, '[]' for array, '{}' for dict
359
+ if(typeof(param) != 'object' || !('constructor' in param)) {
360
+ throw TalkObject.TalkValidationError.makeError( this, key, param,
361
+ "Expected a " + type + " collection");
362
+ }
363
+
364
+ if(type == '[]')
365
+ {
366
+ if(param.constructor != Array) return undefined;
367
+ var validatedArray = [];
368
+ for(var i = 0; i < param.length; i++)
369
+ {
370
+ var v = this.__validateTypeStack(key, param[i], typeStack);
371
+ if(v === undefined)
372
+ {
373
+ return undefined;
374
+ }
375
+ validatedArray.push(v);
376
+ }
377
+ return validatedArray;
378
+ } else if(type == '{}') {
379
+ if(param.constructor != Object) return undefined;
380
+ var validatedDict = {};
381
+ for(var k in param)
382
+ {
383
+ var v = this.__validateTypeStack(key, param[k], typeStack);
384
+ if(v === undefined) return undefined;
385
+ validatedDict[k] = v;
386
+ }
387
+ return validatedDict;
388
+ } else {
389
+ //#JSCOVERAGE_IF 0
390
+ // Implementation has a bad type stack. This shouldn't happen.
391
+ throw TalkObject.TalkImplementationError.makeError( this, key, param,
392
+ "Nonsense definition; expected collection, got " + type);
393
+ //#JSCOVERAGE_ENDIF
394
+ }
395
+ }
396
+ };
397
+
398
+
399
+
400
+ /**
401
+ * __validateParameter - augment the TalkObject with validation of Parameter
402
+ */
403
+ TalkObject.prototype.__validateParameter = function(key, param)
404
+ {
405
+ var def = this.__definition[key];
406
+ if(!("typeStack" in def))
407
+ {
408
+ //#JSCOVERAGE_IF 0
409
+ throw TalkObject.TalkImplementationError.makeError( this, key,
410
+ "Nonsense field definition; expected typeStack");
411
+ //#JSCOVERAGE_ENDIF
412
+ }
413
+
414
+ return this.__validateTypeStack(key, param, def.typeStack);
415
+ };
416
+
417
+ /**
418
+ * __validate - augment the TalkObject with validation
419
+ */
420
+ TalkObject.prototype.__validate = function(throwError) {
421
+ try {
422
+ for(var key in this.__definition)
423
+ {
424
+ this.__validateParameter(key, this[key]);
425
+ if(TalkObject.validators[this.__class] &&
426
+ TalkObject.validators[this.__class][key])
427
+ {
428
+ var err = TalkObject.validators[this.__class][key](this, key);
429
+ if(err)
430
+ {
431
+ //#JSCOVERAGE_IF 0
432
+ throw TalkObject.TalkImplementationError.makeError( this, key, param, err);
433
+ //#JSCOVERAGE_ENDIF
434
+ }
435
+ }
436
+ }
437
+ return true;
438
+ } catch(err) {
439
+ if(throwError) throw err;
440
+ return false;
441
+ }
442
+ };
443
+
444
+
445
+ /**
446
+ * instantiate - simple instantiation function will make numbers 0, booleans false and all else null
447
+ */
448
+ TalkObject.prototype.instantiate = function() {
449
+ try {
450
+ for(var def in this.__definition)
451
+ {
452
+ if( "typeStack" in def )
453
+ {
454
+ var type = def.typeStack[def.typeStack.length-1];
455
+ var isBase = def.typeStack.length == 1;
456
+ var val;
457
+ if(isBase)
458
+ {
459
+ var intMatch = type.match(/^(u)?int([0-9]+)$/);
460
+
461
+ if(intMatch) { val = 0; }
462
+ else if(type == 'real') { val = 0; }
463
+ else if(type == 'bool') { val = false; }
464
+ // else if(type == 'string') { val = null; }
465
+ else { val = null; }
466
+ return val;
467
+ } else val = null;
468
+ this[key]=val;
469
+ }
470
+ }
471
+ return true;
472
+ } catch(err) {
473
+ return false;
474
+ }
475
+ };
476
+
477
+
478
+ /**
479
+ * loadFromObject - augment the TalkObject with loadFromObject which returns a validated object
480
+ */
481
+ TalkObject.prototype.loadFromObject = function(obj, strict)
482
+ {
483
+ var validatedData = {};
484
+ for(var key in obj)
485
+ {
486
+ if(key == "__class" || key == "constructor") continue;
487
+ if(!(key in this.__definition))
488
+ {
489
+ if(strict)
490
+ {
491
+ throw new TalkObject.TalkValidationError( this, key, obj[key],
492
+ "Attempted to instantiate in strict mode with unsupported field");
493
+ }
494
+ } else {
495
+ validatedData[key] = this.__validateParameter(key, obj[key], strict);
496
+ }
497
+ }
498
+
499
+ for(var key in validatedData)
500
+ {
501
+ this[key] = validatedData[key];
502
+ }
503
+
504
+ // Run the validation check to make sure required fields are set.
505
+ this.__validate(true);
506
+ var keys = [];
507
+ for(var k in this)
508
+ {
509
+ keys.push(k + " (" + typeof(this[k]) + ")");
510
+ }
511
+
512
+ /* If we got __class from the prototype, it won't automatically show in
513
+ ** JSON.stringify. This statement seems to be a no-op, but it actually
514
+ ** fixes that issue. */
515
+ this["__class"] = this.__class;
516
+
517
+ return this;
518
+ };
519
+
520
+ /**
521
+ * set - a javascript setter for a value in the object
522
+ */
523
+ TalkObject.prototype.set = function(key, value)
524
+ {
525
+ if(value === undefined && typeof(key) == "object")
526
+ {
527
+ // Alternative usage; caller supplies key-value pairs to set.
528
+ for(var k in key)
529
+ {
530
+ if(!this.set(k, key[k])) return false;
531
+ }
532
+ }
533
+
534
+ this[key] = this.__validateParameter(key, value, true);
535
+ return true;
536
+ };
537
+
538
+ /**
539
+ * get - a javascript getter for a value in the object
540
+ */
541
+ TalkObject.prototype.get = function(key)
542
+ {
543
+ if(!(key in this.__definition))
544
+ {
545
+ throw TalkObject.TalkValidationError.makeError(
546
+ this, key, null, "not a valid field");
547
+ }
548
+ return this[key] ? this[key] : null;
549
+ };
550
+
551
+
552
+ /**
553
+ * __objectify - not really sure what this does it looks like a deep copy
554
+ */
555
+ TalkObject.prototype.__objectify = function(impliedClass)
556
+ {
557
+ for(var key in this)
558
+ {
559
+ if(key.match(/^__/))
560
+ {
561
+ // Anything that starts with __ is internal use only; disregard it.
562
+ continue;
563
+ } else if(this[key] === null || this[key] === undefined) {
564
+ // null values pass through.
565
+ obj[key] = null;
566
+ } else if(typeof(this[key]) == 'function'){
567
+ // Objectifying functions makes no sense, so don't do that!
568
+ continue;
569
+ } else if(typeof(this[key]) == 'object' && "__objectify" in this[key]) {
570
+ // Talk objects need to have recursive objectification
571
+ var def = this.__definition[key];
572
+ obj[key] = this[key].__objectify(def.type);
573
+ } else {
574
+ // Primitives can be assigned directly.
575
+ obj[key] = this[key];
576
+ }
577
+ }
578
+
579
+ if(TalkObject.shortName(impliedClass) != TalkObject.shortName(this.__class))
580
+ {
581
+ obj.__class = this.__class;
582
+ }
583
+
584
+ return obj;
585
+ };
586
+
587
+ /**
588
+ * shortName - returns short name of object
589
+ */
590
+ TalkObject.shortName = function(className)
591
+ {
592
+ var comps = className.split(".");
593
+ var s = comps[comps.length-1];
594
+ return s;
595
+ };
596
+
597
+ /**
598
+ * shortName - returns the class object for a name. If not strict will look for shortnames
599
+ * as well as canonical
600
+ */
601
+ TalkObject.classNamed = function(className, strict)
602
+ {
603
+ if(TalkObject.classes[className]) return TalkObject.classes[className];
604
+ if(strict) return undefined;
605
+ return TalkObject.shortclasses[className];
606
+ };
607
+
608
+ /**
609
+ * addClass - This is what the talk generator created javascript calls for each generated class
610
+ */
611
+ TalkObject.addClass = function(className, definition, superclassName)
612
+ {
613
+ if(!superclassName) superclassName = "TalkObject";
614
+ var superclass = TalkObject.classNamed(superclassName);
615
+ if(!superclass)
616
+ {
617
+ //#JSCOVERAGE_IF 0
618
+ if((typeof logger != 'undefined') && logger) logger.error("Implementation error: No superclass '"
619
+ + superclassName + "' for class '" + className + "'");
620
+ return undefined;
621
+ //#JSCOVERAGE_ENDIF
622
+ }
623
+
624
+ var cls = function(obj)
625
+ {
626
+ if(obj) this.loadFromObject(obj);
627
+ else this.instantiate();
628
+ };
629
+
630
+ cls.prototype = new superclass();
631
+ cls.prototype.constructor = cls;
632
+ cls.prototype.__super = superclass;
633
+ cls.prototype.__class = className;
634
+ cls.prototype.__definition = {};
635
+
636
+ for(var k in superclass.prototype.__definition) {
637
+ cls.prototype.__definition[k] = superclass.prototype.__definition[k];
638
+ }
639
+
640
+ for(var k in definition)
641
+ {
642
+ cls.prototype.__definition[k] = definition[k];
643
+ }
644
+
645
+ TalkObject.classes[className] = cls;
646
+ TalkObject.shortclasses[TalkObject.shortName(className)] = cls;
647
+
648
+ return cls;
649
+ };
650
+
651
+ /**
652
+ * setValidator - Setup a validator on a fieldname
653
+ */
654
+ TalkObject.setValidator = function(className, fieldName, validator)
655
+ {
656
+ var cls = TalkObject.classNamed(className);
657
+ if(!(cls.prototype.__class in TalkObject.validators))
658
+ {
659
+ TalkObject.validators[cls.prototype.__class] = {};
660
+ }
661
+
662
+ TalkObject.validators[cls.prototype.__class][fieldName] = validator;
663
+ };
664
+
665
+ /**
666
+ * instance - get an instance of a className from obj
667
+ */
668
+ TalkObject.instance = function(className, obj, strict, throwError)
669
+ {
670
+ var cls = TalkObject.classNamed(className, strict);
671
+ if(!cls)
672
+ {
673
+ //#JSCOVERAGE_IF 0
674
+ if((typeof logger != 'undefined') && logger) logger.trace("Cannot create instance of unknown class " + className
675
+ + " (strict = " + (strict ? "yes" : "no") + ")");
676
+ return undefined;
677
+ //#JSCOVERAGE_ENDIF
678
+ }
679
+
680
+ try {
681
+ var instance = new cls(obj);
682
+ return instance;
683
+ } catch(err) {
684
+ if(throwError) throw err;
685
+ return undefined;
686
+ }
687
+ };
688
+
689
+ /**
690
+ * isSubclass - returns true if subclassName is subclass of superclassName
691
+ */
692
+ TalkObject.isSubclass = function(subclassName, superclassName)
693
+ {
694
+ var c = TalkObject.classNamed(subclassName);
695
+ var s = TalkObject.classNamed(superclassName);
696
+ while(c && c != s)
697
+ {
698
+ c = c.prototype.__super;
699
+ }
700
+
701
+ return c != undefined;
702
+ };
703
+
704
+ /**
705
+ * glossary - returns a glossary lookup
706
+ */
707
+ TalkObject.glossary = function(glossaryName, strict)
708
+ {
709
+ var r=undefined;
710
+ if(TalkObject.glossaries[glossaryName]) {
711
+ r = TalkObject.glossaries[glossaryName];
712
+ } else if(strict) {
713
+ r = undefined;
714
+ } else if(TalkObject.shortglossaries[glossaryName]) {
715
+ r = TalkObject.shortglossaries[glossaryName];
716
+ }
717
+
718
+ if(!r)
719
+ {
720
+ //#JSCOVERAGE_IF 0
721
+ if((typeof logger != 'undefined') && logger) logger.error("Undefined glossary: " + glossaryName + " (strict: "
722
+ + (strict ? "yes" : "no") + ")");
723
+ //#JSCOVERAGE_ENDIF
724
+ }
725
+
726
+ return r;
727
+ };
728
+
729
+ /**
730
+ * enumeration - returns an enumeration lookup
731
+ */
732
+ TalkObject.enumeration = function(enumeration, strict)
733
+ {
734
+ var r = undefined;
735
+ if(TalkObject.enumerations[enumeration]) {
736
+ r = TalkObject.enumerations[enumeration];
737
+ } else if(strict) {
738
+ r = undefined;
739
+ } else if(TalkObject.shortenumerations[enumeration]) {
740
+ r = TalkObject.shortenumerations[enumeration];
741
+ }
742
+
743
+ if(!r)
744
+ {
745
+ //#JSCOVERAGE_IF 0
746
+ if((typeof logger != 'undefined') && logger) logger.error("Undefined enumeration: " + enumeration + " (strict: "
747
+ + (strict ? "yes" : "no") + ")");
748
+ //#JSCOVERAGE_ENDIF
749
+ }
750
+ return r;
751
+ };
752
+
753
+ /**
754
+ * constantFromSource - generic function which can be called for eitehr glossary or enumeration
755
+ * epending on the parameters passed in
756
+ */
757
+ TalkObject.constantFromSource = function(container, constant, source, strict)
758
+ {
759
+ var r;
760
+ r = source(container, strict);
761
+ if(!r) r = source(container, strict);
762
+ if(!r) return undefined;
763
+ return r[constant];
764
+ };
765
+
766
+ /**
767
+ * constant - return a constant looking in both glossary and enumerations
768
+ */
769
+ TalkObject.constant = function(container, constant, strict)
770
+ {
771
+ var r = undefined;
772
+ if(constant == undefined)
773
+ {
774
+ r = TalkObject.allConstants[container];
775
+ } else {
776
+ r = TalkObject.constantFromSource(container, constant, TalkObject.glossary, strict)
777
+ || TalkObject.constantFromSource(container, constant, TalkObject.enumeration, strict);
778
+ }
779
+
780
+ if(r === undefined)
781
+ {
782
+ var constantName = container;
783
+ var caller = callerid();
784
+ if(constant) constantName += "."+constant;
785
+ if((typeof logger != 'undefined') && logger) logger.error("Undefined constant: " + constantName + " (strict: "
786
+ + (strict ? "yes" : "no") + ", " + caller.shortFile + ":" + caller.line + ")");
787
+ }
788
+
789
+ return r;
790
+ };
791
+
792
+ /**
793
+ * protocol - return a protocol object
794
+ */
795
+ TalkObject.protocol = function(protocol)
796
+ {
797
+ return TalkObject.protocols[protocol];
798
+ };
799
+
800
+
801
+ /**
802
+ * addGlossary - This is what the talk generator created javascript calls for each generated glossary
803
+ */
804
+ TalkObject.addGlossary = function(glossaryName, glossary)
805
+ {
806
+ var newGloss = { '__values':[] };
807
+ for(var k in glossary)
808
+ {
809
+ var v = glossary[k];
810
+ newGloss.__values.push(v);
811
+ newGloss[k] = v;
812
+ TalkObject.allConstants[k] = v;
813
+ }
814
+
815
+ TalkObject.glossaries[glossaryName] = newGloss;
816
+ TalkObject.shortglossaries[TalkObject.shortName(glossaryName)] = newGloss;
817
+ return newGloss;
818
+ };
819
+
820
+ /**
821
+ * addEnumeration - This is what the talk generator created javascript calls for each generated enumeration
822
+ */
823
+ TalkObject.addEnumeration = function(enumerationName, enumeration)
824
+ {
825
+ var newEnum = { '__values':[] };
826
+ for(var k in enumeration)
827
+ {
828
+ var v = enumeration[k];
829
+ newEnum.__values.push(v);
830
+ newEnum[k] = v;
831
+ TalkObject.allConstants[k] = v;
832
+ }
833
+
834
+ var shortName = TalkObject.shortName(enumerationName);
835
+ TalkObject.enumerations[enumerationName] = newEnum;
836
+ TalkObject.shortenumerations[shortName] = newEnum;
837
+ return newEnum;
838
+ };
839
+
840
+ /**
841
+ * addProtocol - This is what the talk generator created javascript calls for each generated protocol
842
+ */
843
+ TalkObject.addProtocol = function(protocolName, protocol)
844
+ {
845
+ for(var method in protocol )
846
+ TalkObject.methods[protocolName + "." + method] = method;
847
+ TalkObject.protocols[protocolName] = protocol;
848
+ };
849
+
850
+ TalkObject.method = function(protocolName, method)
851
+ {
852
+ var key = protocolName + "." + method;
853
+ if( key in TalkObject.methods ) return TalkObject.methods[key];
854
+
855
+ };
856
+
857
+ <%= (@base[:class].map { |cls| class_line(cls) }).compact.join("\n") %>
858
+ <%= (@base[:protocol].map { |proto| protocol_line(proto) }).compact.join("\n") %>
859
+ <%= (@base[:glossary].map { |glossary| glossary_line(glossary) }).compact.join("\n") %>
860
+ <%= (@base[:enumeration].map { |enumeration| enumeration_line(enumeration) }).compact.join("\n") %>
@@ -126,12 +126,13 @@ module Talk
126
126
  @target[:destination]
127
127
  end
128
128
 
129
- def generate_template(output_file, template_file=nil)
129
+ def generate_template(output_file, template_file=nil, transform=nil)
130
130
  template_file ||= output_file + ".erb"
131
131
  template_contents = IO.read(File.join(self.path, "templates", template_file))
132
132
  erb = ERB.new(template_contents)
133
133
  erb.filename = template_file
134
134
  source = erb.result(binding)
135
+ source = transform.call(source) unless transform.nil?
135
136
  filename = File.join(@output_path, output_file)
136
137
  write(filename, source)
137
138
  source
@@ -197,5 +198,21 @@ module Talk
197
198
  name = name[:name] if name.is_a? Hash
198
199
  name.split('.').last
199
200
  end
201
+
202
+ def mapped_name(container_name, object_name, type, name_key=:name)
203
+ return object_name if @target[:map].nil? or @target[:map].empty?
204
+
205
+ object_name = object_name[:name] if object_name.is_a? Hash
206
+ container_name = container_name[:name] if container_name.is_a? Hash
207
+ container_name = truncated_name(container_name)
208
+
209
+ @target[:map].each do |map|
210
+ matches = (map[:type] == type.to_s && map[:class_name] == container_name && map[:field_name] == object_name)
211
+ return map[:new_field_name] if matches
212
+ end
213
+
214
+ object_name
215
+ end
216
+
200
217
  end
201
218
  end
@@ -59,19 +59,6 @@ def definition_reference(tag)
59
59
  "@talkFile #{tag[:__meta][:file]}:#{tag[:__meta][:line]}"
60
60
  end
61
61
 
62
- def mapped_name(container_name, object_name, type, name_key=:name)
63
- object_name = object_name[:name] if object_name.is_a? Hash
64
- container_name = container_name[:name] if container_name.is_a? Hash
65
- container_name = truncated_name(container_name)
66
-
67
- @target[:map].each do |map|
68
- matches = (map[:type] == type.to_s && map[:class_name] == container_name && map[:field_name] == object_name)
69
- return map[:new_field_name] if matches
70
- end
71
-
72
- object_name
73
- end
74
-
75
62
  def assist_line(field)
76
63
  return nil if field[:type].length <= 1
77
64
  elements = []
data/talk.gemspec CHANGED
@@ -1,8 +1,8 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'talk'
3
3
  s.executables << 'talk'
4
- s.version = '2.0.5'
5
- s.date = '2014-05-30'
4
+ s.version = '2.0.6'
5
+ s.date = '2014-06-04'
6
6
  s.summary = "Compile-to-source protocol contract specification language"
7
7
  s.description = "A lightweight language for specifying protocol contracts. Compiles to source in Java, Javascript, ObjC and Ruby."
8
8
  s.authors = ["Jonas Acres"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: talk
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.5
4
+ version: 2.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Acres
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-30 00:00:00.000000000 Z
11
+ date: 2014-06-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A lightweight language for specifying protocol contracts. Compiles to
14
14
  source in Java, Javascript, ObjC and Ruby.
@@ -62,6 +62,8 @@ files:
62
62
  - lib/languages/java/templates/class.java.erb
63
63
  - lib/languages/java/templates/enumeration.java.erb
64
64
  - lib/languages/java/templates/glossary.java.erb
65
+ - lib/languages/js/js.rb
66
+ - lib/languages/js/templates/talk.js.erb
65
67
  - lib/languages/language.rb
66
68
  - lib/languages/objc/objc.rb
67
69
  - lib/languages/objc/templates/TalkClasses.h.erb