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.
- checksums.yaml +4 -4
- data/README.md +6 -6
- data/Rakefile +2 -2
- data/init.rb +1 -1
- data/lib/assets/javascripts/set_builder.js +122 -125
- data/lib/set_builder/constraint.rb +66 -50
- data/lib/set_builder/errors/trait_not_found.rb +3 -0
- data/lib/set_builder/modifier/adverb.rb +4 -4
- data/lib/set_builder/modifier/base.rb +74 -67
- data/lib/set_builder/modifier/verb.rb +1 -16
- data/lib/set_builder/modifier.rb +49 -49
- data/lib/set_builder/modifier_collection.rb +16 -16
- data/lib/set_builder/modifiers/date_preposition.rb +6 -48
- data/lib/set_builder/modifiers/number_preposition.rb +4 -25
- data/lib/set_builder/modifiers/string_preposition.rb +9 -43
- data/lib/set_builder/modifiers.rb +3 -3
- data/lib/set_builder/set.rb +48 -8
- data/lib/set_builder/trait.rb +75 -49
- data/lib/set_builder/traits.rb +13 -4
- data/lib/set_builder/value_map.rb +30 -30
- data/lib/set_builder/version.rb +1 -1
- data/lib/set_builder.rb +8 -7
- data/set_builder.gemspec +4 -2
- data/spec/commands/example_command.rb +2 -2
- data/spec/lib/jspec.css +4 -4
- data/spec/lib/jspec.growl.js +11 -11
- data/spec/lib/jspec.jquery.js +14 -14
- data/spec/lib/jspec.js +210 -210
- data/spec/lib/jspec.nodejs.js +2 -2
- data/spec/lib/jspec.shell.js +11 -11
- data/spec/lib/jspec.timers.js +23 -23
- data/spec/rhino.js +1 -1
- data/spec/server.rb +1 -1
- data/spec/unit/array.spec.js +9 -9
- data/spec/unit/set_builder.spec.js +71 -82
- data/spec/unit/spec.helper.js +1 -0
- data/test/constraint_test.rb +84 -0
- data/test/date_preposition_test.rb +17 -7
- data/test/modifier_test.rb +26 -1
- data/test/set_test.rb +51 -28
- data/test/string_preposition_test.rb +10 -9
- data/test/test_helper.rb +17 -15
- data/test/trait_test.rb +49 -30
- data/test/traits_test.rb +35 -30
- data/test/value_map_test.rb +1 -1
- metadata +37 -15
- data/lib/set_builder/query_builders/string.rb +0 -0
- data/test/inflector_test.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: acd34d467c6aca8cc3c42db795261de035c72c58
|
4
|
+
data.tar.gz: 2c08cc3a703c5cc84230cbaa0e047004d4e7f430
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
[
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
[
|
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
|
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
data/init.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require
|
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,
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
}
|
84
|
-
|
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,
|
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 '
|
134
|
-
return
|
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[
|
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
|
-
}).
|
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 =
|
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 =
|
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(
|
281
|
-
var trait_name =
|
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(
|
283
|
+
_constraints.push(trait.apply(params));
|
292
284
|
} else if(window.console && window.console.log) {
|
293
|
-
window.console.log('trait not found with name "' +
|
285
|
+
window.console.log('trait not found with name "' + trait_name + '"');
|
294
286
|
}
|
295
287
|
});
|
296
|
-
|
297
|
-
function convertObjectSetToArray(
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
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
|
-
|
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,
|
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 '
|
366
|
-
|
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.
|
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.
|
387
|
-
return
|
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
|
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
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
def initialize(trait,
|
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
|
-
@
|
19
|
-
|
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
|
-
|
35
|
+
errors.none?
|
37
36
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
54
|
-
@
|
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
|