set_builder 2.0.0.beta2 → 2.0.0.beta3
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.
- 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
|