set_builder 2.0.0.beta2 → 2.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -6
  3. data/Rakefile +2 -2
  4. data/init.rb +1 -1
  5. data/lib/assets/javascripts/set_builder.js +122 -125
  6. data/lib/set_builder/constraint.rb +66 -50
  7. data/lib/set_builder/errors/trait_not_found.rb +3 -0
  8. data/lib/set_builder/modifier/adverb.rb +4 -4
  9. data/lib/set_builder/modifier/base.rb +74 -67
  10. data/lib/set_builder/modifier/verb.rb +1 -16
  11. data/lib/set_builder/modifier.rb +49 -49
  12. data/lib/set_builder/modifier_collection.rb +16 -16
  13. data/lib/set_builder/modifiers/date_preposition.rb +6 -48
  14. data/lib/set_builder/modifiers/number_preposition.rb +4 -25
  15. data/lib/set_builder/modifiers/string_preposition.rb +9 -43
  16. data/lib/set_builder/modifiers.rb +3 -3
  17. data/lib/set_builder/set.rb +48 -8
  18. data/lib/set_builder/trait.rb +75 -49
  19. data/lib/set_builder/traits.rb +13 -4
  20. data/lib/set_builder/value_map.rb +30 -30
  21. data/lib/set_builder/version.rb +1 -1
  22. data/lib/set_builder.rb +8 -7
  23. data/set_builder.gemspec +4 -2
  24. data/spec/commands/example_command.rb +2 -2
  25. data/spec/lib/jspec.css +4 -4
  26. data/spec/lib/jspec.growl.js +11 -11
  27. data/spec/lib/jspec.jquery.js +14 -14
  28. data/spec/lib/jspec.js +210 -210
  29. data/spec/lib/jspec.nodejs.js +2 -2
  30. data/spec/lib/jspec.shell.js +11 -11
  31. data/spec/lib/jspec.timers.js +23 -23
  32. data/spec/rhino.js +1 -1
  33. data/spec/server.rb +1 -1
  34. data/spec/unit/array.spec.js +9 -9
  35. data/spec/unit/set_builder.spec.js +71 -82
  36. data/spec/unit/spec.helper.js +1 -0
  37. data/test/constraint_test.rb +84 -0
  38. data/test/date_preposition_test.rb +17 -7
  39. data/test/modifier_test.rb +26 -1
  40. data/test/set_test.rb +51 -28
  41. data/test/string_preposition_test.rb +10 -9
  42. data/test/test_helper.rb +17 -15
  43. data/test/trait_test.rb +49 -30
  44. data/test/traits_test.rb +35 -30
  45. data/test/value_map_test.rb +1 -1
  46. metadata +37 -15
  47. data/lib/set_builder/query_builders/string.rb +0 -0
  48. data/test/inflector_test.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae95d6f2f3cfb324b760144d8cab1e61fc4b41b5
4
- data.tar.gz: a5f02d71b73862a3251ff48b167a07d3cf1a8b14
3
+ metadata.gz: acd34d467c6aca8cc3c42db795261de035c72c58
4
+ data.tar.gz: 2c08cc3a703c5cc84230cbaa0e047004d4e7f430
5
5
  SHA512:
6
- metadata.gz: f61aa92460102dd67880c0bc3d670f0c5ae5938ab0f83b2bf4f074597ececa1cf6933f9fcf15217d8e06d4913bd4b83192683fd5205d5b57c34aedacacca1932
7
- data.tar.gz: 2803e8640beddb5fb8bbc880a2187af33da4534b98577df9dcde5ef29c7df432b45b886d1f654468d85adb925bb8f7ca5e483051275505ffa9dd385f49585e23
6
+ metadata.gz: d79aec9ff59f398bc844de87cbc9a7946fb43166017d1231c36fc8a1ff267a296a98e88e524d3b01b2c726bd91ed75d4e63833246e32cc6fa25114e038809cb1
7
+ data.tar.gz: d4ba5f0e2dfff5ad5245abd758adcac891c93fe2e0bba30c830e52b7dd63e7749962ed6843dc4f3c4b33b1e7b3a3b33dcb58ceef97312a417cce6abbf8be8962
data/README.md CHANGED
@@ -15,16 +15,16 @@ SetBuilder is a library for:
15
15
 
16
16
  The following Set describes a group of people:
17
17
 
18
- [[:awesome],
19
- [:attended, "school"],
20
- [:died, :not],
21
- [:name, {:is => "Jerome"}]]
18
+ [{ trait: :awesome, choices: ["are"] },
19
+ { trait: :attended, choices: ["have"], school: "school" },
20
+ { trait: :died, choices: ["have not"] },
21
+ { trait: :name, modifiers: [{ operator: :is, values: ["Jerome"] }] }]
22
22
 
23
23
  SetBuilder can render this Set in plain English:
24
24
 
25
- [Everyone] who is awesome, who attended school, who has not died, and whose name is Jerome.
25
+ [Those] who are awesome, who have attended school, who have not died, and whose name is Jerome.
26
26
 
27
- It can also generate a NamedScope on an ActiveRecord model to fetch the people who fit in this set.
27
+ It can also generate an `ActiveRecord::Relation` on an ActiveRecord model to fetch the people who fit in this set.
28
28
 
29
29
 
30
30
  ### Running the tests
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
- require 'rake'
2
- require 'rake/testtask'
1
+ require "rake"
2
+ require "rake/testtask"
3
3
 
4
4
  desc 'Run tests.'
5
5
  task :default => :test
data/init.rb CHANGED
@@ -1 +1 @@
1
- require 'set_builder'
1
+ require "set_builder"
@@ -1,9 +1,9 @@
1
1
  var SetBuilder = (function() {
2
-
2
+
3
3
  var _modifiers;
4
4
  var _traits;
5
5
  var _value_maps = {};
6
-
6
+
7
7
  return {
8
8
  init: function(traits, modifiers) {
9
9
  registerTraits(traits);
@@ -22,7 +22,7 @@ var SetBuilder = (function() {
22
22
  for(key in _data) {
23
23
  _value_maps[key] = _data[key];
24
24
  }
25
- },
25
+ },
26
26
  traits: function() {
27
27
  return _traits;
28
28
  },
@@ -43,7 +43,7 @@ var SetBuilder = (function() {
43
43
  default: return values.slice(0, -1).join(', ') + ', or ' + values[values.length - 1];
44
44
  }
45
45
  }
46
-
46
+
47
47
  var match = value.toString(),
48
48
  map = SetBuilder.getValueMap(key);
49
49
  if(map) {
@@ -57,7 +57,7 @@ var SetBuilder = (function() {
57
57
  return Object.keys(_value_maps);
58
58
  }
59
59
  }
60
-
60
+
61
61
  })();
62
62
 
63
63
 
@@ -65,64 +65,65 @@ var SetBuilder = (function() {
65
65
  /*
66
66
  Constraint (c.f. /lib/set_builder/constraint.rb)
67
67
  ===========================================================
68
-
68
+
69
69
  a constrained trait
70
70
  */
71
71
 
72
- SetBuilder.Constraint = function(_trait, args) {
73
-
74
- var _direct_object;
72
+ SetBuilder.Constraint = function(_trait, params) {
73
+
75
74
  if(typeof(_trait) == 'string') {
76
75
  _trait = SetBuilder.traits().__find(_trait);
77
76
  }
78
77
 
79
- args = args.dup();
80
- if(_trait.requiresDirectObject()) _direct_object = args.shift();
81
- var _modifiers = _trait.modifiers().collect(function(modifier_type) {
82
- return SetBuilder.modifiers().apply(modifier_type, args.shift());
83
- });
84
- var _negative = false;
85
-
86
-
87
-
78
+ var _enums = params.enums || [];
79
+ var _direct_object;
80
+ if(_trait.requiresDirectObject()) {
81
+ _direct_object = params[_trait.directObjectType()];
82
+ }
83
+
84
+ var _modifiers = [];
85
+ for(var i=0, ii=_trait.modifiers().length; i<ii; i++) {
86
+ _modifiers.push(
87
+ SetBuilder.modifiers().apply(
88
+ _trait.modifiers()[i],
89
+ (params.modifiers||[])[i]));
90
+ }
91
+
92
+
93
+
88
94
  // Public methods
89
-
95
+
90
96
  this.trait = function(val) {
91
97
  if(val !== undefined) {
92
98
  _trait = val;
93
99
  _direct_object = undefined;
100
+ _enums = [];
94
101
  _modifiers = [];
95
102
  }
96
103
  return _trait;
97
104
  }
98
-
105
+
99
106
  this.direct_object = function(val) {
100
107
  if(val !== undefined) {
101
108
  _direct_object = val;
102
109
  }
103
110
  return _direct_object;
104
111
  }
105
-
112
+
113
+ this.enums = function() {
114
+ return _enums;
115
+ }
116
+
106
117
  this.modifiers = function() {
107
118
  return _modifiers;
108
119
  }
109
-
110
- this.negate = function(value) {
111
- _negative = value;
112
- if(!_trait.hasNegative()) _negative = false;
113
- return this;
114
- }
115
-
116
- this.negative = function() {
117
- return _negative;
118
- }
119
-
120
+
120
121
  this.requires_direct_object = function() {
121
122
  return _trait.requires_direct_object();
122
123
  }
123
-
124
+
124
125
  this.toString = function() {
125
- var type, text, i = 0;
126
+ var type, text, enum_index = 0, modifier_index = 0;
126
127
  return _trait.tokens().collect(function(token) {
127
128
  type = token[0], text = token[1];
128
129
  switch(type) {
@@ -130,17 +131,17 @@ SetBuilder.Constraint = function(_trait, args) {
130
131
  return text;
131
132
  case 'name':
132
133
  return _trait.name();
133
- case 'negative':
134
- return _negative && text;
134
+ case 'enum':
135
+ return (_enums[enum_index++] || '').toString();
135
136
  case 'direct_object_type':
136
137
  return SetBuilder.getValue(text, _direct_object);
137
138
  case 'modifier':
138
- return _modifiers[i++].toString().replace('_', ' ');
139
+ return (_modifiers[modifier_index++] || '').toString().replace('_', ' ');
139
140
  default:
140
141
  if(console && console.log) console.log('[SetBuilder.Constraint] unknown type: "' + type + '" (text: "' + text + '")');
141
142
  return false;
142
143
  }
143
- }).compact().join(' ');
144
+ }).join('');
144
145
  }
145
146
 
146
147
  };
@@ -155,23 +156,23 @@ SetBuilder.Constraint = function(_trait, args) {
155
156
  */
156
157
 
157
158
  SetBuilder.Modifier = function(_name, _operator, _values, _params) {
158
-
159
-
160
-
159
+
160
+
161
+
161
162
  // Public methods
162
-
163
+
163
164
  this.name = function() {
164
165
  return _name;
165
166
  }
166
-
167
+
167
168
  this.operator = function() {
168
169
  return _operator;
169
170
  }
170
-
171
+
171
172
  this.values = function() {
172
173
  return _values;
173
174
  }
174
-
175
+
175
176
  this.toString = function() {
176
177
  var words = [_operator.replace(/_/, ' ')];
177
178
  for(var i=0; i<_values.length; i++) {
@@ -187,7 +188,7 @@ SetBuilder.Modifier = function(_name, _operator, _values, _params) {
187
188
  /*
188
189
  Modifiers (c.f. /lib/set_builder/modifier.rb)
189
190
  ===========================================================
190
-
191
+
191
192
  [
192
193
  [modifier_name, {
193
194
  operator: ['argument 1 type', 'argument 2 type'],
@@ -198,9 +199,9 @@ SetBuilder.Modifier = function(_name, _operator, _values, _params) {
198
199
  */
199
200
 
200
201
  SetBuilder.Modifiers = function(_modifiers) {
201
-
202
+
202
203
  var keys = Object.keys(_modifiers);
203
-
204
+
204
205
  function _operators_for(name) {
205
206
  var operators = _modifiers[name];
206
207
  if(operators) {
@@ -209,19 +210,19 @@ SetBuilder.Modifiers = function(_modifiers) {
209
210
  throw ('"' + name.toString() + '" is not a registered modifier.');
210
211
  }
211
212
  }
212
-
213
-
214
-
213
+
214
+
215
+
215
216
  // Public methods
216
217
  this.length = function() {
217
218
  return keys.length;
218
219
  }
219
-
220
+
220
221
  // Returns the names of the operators allowed for the given modifier
221
222
  this.operators_for = function(modifier_type) {
222
223
  return Object.keys(_operators_for(modifier_type));
223
224
  }
224
-
225
+
225
226
  // Returns the names of the arguments a given operator anticipates
226
227
  this.params_for_operator = function(modifier_type, operator_name) {
227
228
  if(!operator_name) throw 'An operator name was not supplied.'
@@ -232,31 +233,29 @@ SetBuilder.Modifiers = function(_modifiers) {
232
233
  throw ('"' + operator_name.toString() + '" is not an operator for the ' + modifier_type + ' modifier.');
233
234
  }
234
235
  }
235
-
236
+
236
237
  // Examples of Usage:
237
238
  //
238
239
  // An operator that takes one argument:
239
- // apply('date', {'before': ['2012-11-12']})
240
+ // apply('date', {operator: 'before', values: ['2012-11-12']})
240
241
  //
241
242
  // An operator that takes two arguments:
242
- // apply('date', {'in_the_last': [5, 'days']})
243
- //
244
- //
245
- // `args` is expected to be an object that has one key and one value.
243
+ // apply('date', {operator: 'in_the_last', values: [5, 'days']})
246
244
  //
247
245
  this.apply = function(modifier_type, args) {
248
246
  args = args || {};
249
- var operator = Object.keys(args)[0];
247
+ var operator = args.operator;
250
248
  if(!operator) throw 'An operator name was not supplied.'
251
-
249
+
252
250
  var params = this.params_for_operator(modifier_type, operator);
253
- var values = (args)[operator];
251
+ var values = args.values;
254
252
 
253
+ if(!values) values = [];
255
254
  if(!(values instanceof Array)) values = [values];
256
255
  if(values.length != params.length) {
257
256
  throw ('The operator "' + operator.toString() + '" expects ' + params.length + ' arguments but received ' + values.length + '.');
258
257
  }
259
-
258
+
260
259
  return new SetBuilder.Modifier(name, operator, values, params);
261
260
  }
262
261
 
@@ -267,74 +266,67 @@ SetBuilder.Modifiers = function(_modifiers) {
267
266
  /*
268
267
  Set (c.f. /lib/set_builder/set.rb)
269
268
  ===========================================================
270
-
269
+
271
270
  a set of constrained traits that describe a group
272
271
  */
273
272
 
274
273
  SetBuilder.Set = function(_raw_data) {
275
274
  if(!_raw_data) _raw_data = [];
276
275
  if(_raw_data.constructor !== Array) _raw_data = convertObjectSetToArray(_raw_data);
277
-
276
+
278
277
  var _constraints = [];
279
-
280
- _raw_data.__each(function(line) {
281
- var trait_name = line[0];
282
- var negative = false;
283
-
284
- if(trait_name[0] == '!') {
285
- negative = true;
286
- trait_name = trait_name.slice(1);
287
- }
278
+
279
+ _raw_data.__each(function(params) {
280
+ var trait_name = params.trait;
288
281
  var trait = SetBuilder.traits().__find(trait_name);
289
- var args = line.slice(1);
290
282
  if(trait) {
291
- _constraints.push(trait.apply(args).negate(negative));
283
+ _constraints.push(trait.apply(params));
292
284
  } else if(window.console && window.console.log) {
293
- window.console.log('trait not found with name "' + line[0] + '"');
285
+ window.console.log('trait not found with name "' + trait_name + '"');
294
286
  }
295
287
  });
296
-
297
- function convertObjectSetToArray(data) {
298
- var set = [];
299
-
300
- Object.keys(data).__each(function(key) {
301
- var convertedTrait = [];
302
- var trait = data[key];
303
- convertedTrait.push(trait.negative ? "!" + trait.trait : trait.trait)
304
- if(trait.direct_object) convertedTrait.push(trait.direct_object);
305
- if(trait.modifiers) {
306
- Object.keys(trait.modifiers).__each(function(key) {
307
- var modifier = trait.modifiers[key];
308
- var convertedModifier = {};
309
- convertedModifier[modifier.operator] = modifier.values ? Object.values(modifier.values) : [];
310
- convertedTrait.push(convertedModifier);
288
+
289
+ function convertObjectSetToArray(constraints) {
290
+ return convertHashToArray(constraints).__collect(function(constraint) {
291
+ if(constraint.modifiers) {
292
+ constraint.modifiers = convertHashToArray(constraint.modifiers).__collect(function(modifier) {
293
+ if(modifier.values) {
294
+ modifier.values = convertHashToArray(modifier.values);
295
+ }
296
+ return modifier;
311
297
  });
312
298
  }
313
- set.push(convertedTrait);
299
+ return constraint;
314
300
  });
315
- return set;
316
301
  }
317
-
302
+
303
+ function convertHashToArray(hash) {
304
+ if(hash === Array) return hash;
305
+ return Object.values(hash);
306
+ }
307
+
308
+
309
+
318
310
  // Public methods
319
-
311
+
320
312
  this.data = function() {
321
-
313
+
322
314
  // TODO: write the data structure from constraints!
323
-
315
+
324
316
  }
325
-
317
+
326
318
  this.constraints = function() {
327
319
  return _constraints;
328
320
  }
329
-
321
+
330
322
  this.toString = function() {
331
323
  return this.constraints().toSentence();
332
324
  }
333
-
325
+
334
326
  this.isEqualTo = function(other) {
335
327
  return this.toString() == other.toString();
336
328
  }
337
-
329
+
338
330
  };
339
331
 
340
332
 
@@ -342,14 +334,17 @@ SetBuilder.Set = function(_raw_data) {
342
334
  /*
343
335
  Trait (c.f. /lib/set_builder/trait/base.rb)
344
336
  ===========================================================
345
-
337
+
346
338
  an individual trait that can be constrained
347
339
  */
348
340
 
349
341
  SetBuilder.Trait = function(_tokens) {
350
-
342
+
351
343
  var type, text;
352
- var _name, _modifiers = [], _direct_object_type, _negative;
344
+ var _name,
345
+ _modifiers = [],
346
+ _direct_object_type,
347
+ _enums = [];
353
348
  _tokens.each(function(token) {
354
349
  type = token[0], text = token[1];
355
350
  switch(type) {
@@ -362,35 +357,35 @@ SetBuilder.Trait = function(_tokens) {
362
357
  case 'direct_object_type':
363
358
  _direct_object_type = text;
364
359
  break;
365
- case 'negative':
366
- _negative = text;
360
+ case 'enums':
361
+ _enums.push(text);
367
362
  break;
368
363
  }
369
364
  });
370
-
371
-
365
+
366
+
372
367
  // Public methods
373
-
368
+
374
369
  this.requiresDirectObject = function() {
375
370
  return !!_direct_object_type;
376
371
  }
377
-
378
- this.direct_object_type = function() {
372
+
373
+ this.directObjectType = function() {
379
374
  return _direct_object_type;
380
375
  }
381
-
376
+
382
377
  this.name = function() {
383
378
  return _name;
384
379
  }
385
-
386
- this.hasNegative = function() {
387
- return !!_negative;
380
+
381
+ this.enums = function() {
382
+ return _enums;
388
383
  }
389
-
384
+
390
385
  this.modifiers = function() {
391
386
  return _modifiers;
392
387
  }
393
-
388
+
394
389
  this.tokens = function() {
395
390
  return _tokens;
396
391
  }
@@ -411,24 +406,24 @@ SetBuilder.Trait = function(_tokens) {
411
406
  */
412
407
 
413
408
  SetBuilder.Traits = function(_raw_data) {
414
-
409
+
415
410
  var _traits = _raw_data.collect(function(line) {
416
411
  return new SetBuilder.Trait(line);
417
412
  });
418
-
419
-
413
+
414
+
420
415
  // Public methods
421
-
416
+
422
417
  this.length = function() {
423
418
  return _traits.length;
424
419
  }
425
-
420
+
426
421
  this.names = function() {
427
422
  return _traits.collect(function(trait) {
428
423
  return trait.name();
429
424
  });
430
425
  }
431
-
426
+
432
427
  this.__find = function(name) {
433
428
  return _traits.__find(function(trait) {
434
429
  return (trait.name() == name);
@@ -452,7 +447,7 @@ if(!Object.keys) {
452
447
  for(key in o) {
453
448
  keys.push(key);
454
449
  }
455
- return keys;
450
+ return keys;
456
451
  }
457
452
  }
458
453
 
@@ -568,6 +563,8 @@ Array.prototype.__find = function(fn) {
568
563
  }
569
564
  if(!Array.prototype.find) Array.prototype.find = Array.prototype.__find;
570
565
 
566
+
567
+
571
568
  //
572
569
  // .select
573
570
  //
@@ -1,68 +1,84 @@
1
- require 'set_builder/modifier'
1
+ require "set_builder/modifier"
2
2
 
3
3
 
4
4
  module SetBuilder
5
5
  class Constraint
6
-
7
-
6
+ attr_reader :trait, :params, :direct_object, :enums, :modifiers
7
+ delegate :direct_object_required?,
8
+ :direct_object_type,
9
+ :to => :trait
10
+
11
+
12
+
8
13
  #
9
14
  # Sample constraints:
10
- #
11
- # [:awesome],
12
- # [:attended, "school"],
13
- # [:died],
14
- # [:name, {:is => "Jerome"}]]
15
- #
16
- def initialize(trait, *args, &block)
15
+ #
16
+ # { trait: :awesome }
17
+ # { trait: :attended, school: 2 }
18
+ # { trait: :died, enums: ["have not"] }
19
+ # { trait: :name, modifiers: [{ operator: :is, values: ["Jerome"] }] }
20
+ #
21
+ def initialize(trait, params, &block)
17
22
  @trait, @block = trait, block
18
- @direct_object = args.shift if trait.requires_direct_object?
19
- @modifiers = trait.modifiers.collect {|modifier_type| modifier_type.new(args.shift)}
23
+ @params = params
24
+
25
+ @direct_object = params[direct_object_type] if trait.requires_direct_object?
26
+ @enums = params[:enums] || []
27
+ modifiers = params.fetch(:modifiers, [])
28
+ @modifiers = trait.modifiers.each_with_index.map { |modifier, i|
29
+ modifier.new(modifiers[i] || {}) }
20
30
  end
21
-
22
-
23
-
24
- attr_reader :trait, :direct_object, :modifiers, :negative
25
- alias :negative? :negative
26
-
27
-
28
-
29
- delegate :direct_object_required?,
30
- :direct_object_type,
31
- :to => :trait
32
-
33
-
34
-
31
+
32
+
33
+
35
34
  def valid?
36
- (!direct_object_required? or !direct_object.nil?) and modifiers.all?(&:valid?)
35
+ errors.none?
37
36
  end
38
-
39
-
40
-
41
- def to_s
42
- # p "ValueMap.to_s(#{direct_object_type} (#{direct_object_type.class}), #{direct_object} (#{direct_object.class}))"
43
- @description ||= begin
44
- description = trait.to_s(@negative)
45
- description << " #{ValueMap.to_s(direct_object_type, direct_object)}" if direct_object_required?
46
- description << " #{modifiers.collect{|m| m.to_s(@negative)}.join(" ")}" unless modifiers.empty?
47
- description
37
+
38
+ def errors
39
+ [].tap do |errors|
40
+ errors.push "#{direct_object_type} is blank" if direct_object_required? && direct_object.nil?
41
+ if enums.length != trait.enums.length
42
+ errors.push "should have values for #{trait.enums.length} enums"
43
+ else
44
+ trait.enums.each_with_index do |expected_values, i|
45
+ errors.push "enum ##{i + 1} should be #{expected_values.map { |value| "'#{value}'" }.to_sentence(two_words_connector: " or ", last_word_connector: ", or ")}" unless expected_values.member?(enums[i])
46
+ end
47
+ end
48
+ errors.concat modifiers.flat_map(&:errors)
48
49
  end
49
50
  end
50
-
51
-
52
-
53
- def negate(value)
54
- @negative = value
55
- @negative = false unless trait.negative?
56
- self
51
+
52
+
53
+
54
+ def to_s
55
+ @description ||= stringify(trait.tokens)
57
56
  end
58
-
59
-
60
-
57
+
58
+
59
+
61
60
  def perform(scope)
62
61
  @block.call(self, scope)
63
62
  end
64
-
65
-
66
-
63
+
64
+
65
+
66
+ private
67
+
68
+ def stringify(tokens)
69
+ enum_index = 0
70
+ modifier_index = 0
71
+ tokens.map do |token, value|
72
+ case token
73
+ when :string then value
74
+ when :name then trait.name
75
+ when :enum then enums[enum_index].to_s.tap { enum_index += 1 }
76
+ when :direct_object_type then ValueMap.to_s(value, direct_object)
77
+ when :modifier then modifiers[modifier_index].to_s.tap { modifier_index += 1 }
78
+ else raise NotImplementedError, "Unrecognized token type #{token.inspect}"
79
+ end
80
+ end.join
81
+ end
82
+
67
83
  end
68
84
  end
@@ -0,0 +1,3 @@
1
+ module SetBuilder
2
+ class TraitNotFound < RuntimeError; end
3
+ end