msfl 1.1.5 → 1.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +80 -2
- data/lib/msfl/converters/operator.rb +3 -2
- data/msfl.gemspec +1 -1
- data/spec/msfl/converters/operator_spec.rb +86 -17
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9034a3e616954189429dc66d76be624f065b5f9b
|
4
|
+
data.tar.gz: 8ef828b22da3158dc126e3d8ae8a148bdf82f9b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe03bb90fc8cd612c5914217600a02dfb3a8fd4a5e08e6f701b5e80cdb704f8128e8335c245300d7a2ed6ad2d45b051ac804b496b7386f7350f3484aecf0975d
|
7
|
+
data.tar.gz: 920a0e38dbf3256d45695c60dced75252ddff4d8cf7babaa63825da13b72fb36c220dc13f08ccf364474b0478eb888c7854d01479b39c5e5562865b71ec24fc5
|
data/README.md
CHANGED
@@ -1,5 +1,83 @@
|
|
1
|
-
[![Circle CI](https://circleci.com/gh/
|
1
|
+
[![Circle CI](https://circleci.com/gh/Referly/msfl.svg?style=svg)](https://circleci.com/gh/Referly/msfl)
|
2
2
|
|
3
3
|
# Ruby Gem for the Mattermark Semantic Filter Language
|
4
4
|
|
5
|
-
Contains serializers and validators (and perhaps other) MSFL goodies
|
5
|
+
Contains serializers and validators (and perhaps other) MSFL goodies
|
6
|
+
|
7
|
+
## EBNF
|
8
|
+
|
9
|
+
MSFL is a context-free language. The context-free grammar is defined below.
|
10
|
+
|
11
|
+
I'm not actually sure this is correct, it is definitely not comprehensive as it skips over the shortcut functionality.
|
12
|
+
|
13
|
+
```
|
14
|
+
filter = range_op | binary_op | set_op
|
15
|
+
set_op = and | or
|
16
|
+
and = "{" "\"and\"" ":" list "}"
|
17
|
+
or = "{" "\"or\"" ":" list "}"
|
18
|
+
list = "[" filter? | (filter ("," filter)*) "]";
|
19
|
+
range_op = between;
|
20
|
+
between = "{" field ":" between_body | "{" "\"between\"" ":" between_body "}";
|
21
|
+
between_body = "{" "\"start\"" ":" between_start "," "\"end\"" ":" between_end "}";
|
22
|
+
between_start = integer | double | date | datetime | time;
|
23
|
+
between_end = integer | double | date | datetime | time;
|
24
|
+
binary_op = comparison | containment;
|
25
|
+
comparison = "{" field ":" "{" comparison_op ":" atom "}" "}";
|
26
|
+
field = "\"" ALPHANUMERIC+ "\"";
|
27
|
+
comparison_op = "lt" | "gt" | "lte" | "gte" | "eq";
|
28
|
+
containment = "{" field ":" "{" "\"in\"" ":" atom_list "}" "}";
|
29
|
+
atom = string | integer | double | boolean | date | datetime | time;
|
30
|
+
atom_list = "[" atom? | (atom ("," atom)*) "]";
|
31
|
+
```
|
32
|
+
|
33
|
+
## Configuration
|
34
|
+
|
35
|
+
All configuration options should be set in a block passed to `MSFL.configure { |c| c.datasets = [] }`
|
36
|
+
|
37
|
+
Naturally you should provide an appropriate array of the datasets you are supporting.
|
38
|
+
|
39
|
+
As additional configuration settings are added they will be set similarly.
|
40
|
+
|
41
|
+
## Converters
|
42
|
+
|
43
|
+
The MSFL converters provide convenience methods for transforming a parsed MSFL tree to a different structure that is
|
44
|
+
logically equivalent. The intent is to enable consumers of MSFL to easily manipulate parsed MSFL filters into the form
|
45
|
+
that most easily or efficiently allows adaptation to the storage mechanism upon which the filtering is being effected.
|
46
|
+
|
47
|
+
Note that the order in which converters are run is controlled by the constant MSFL::Converters::CONVERSIONS and cannot
|
48
|
+
be manipulated through configuration. This behavior is currently necessary for ease of implementation but is unlikely
|
49
|
+
to continue to be status quo.
|
50
|
+
|
51
|
+
## Datasets
|
52
|
+
|
53
|
+
The consumer of the MSFL gem defines one or more datasets. The dataset definition enumerates the supported fields and
|
54
|
+
their types.
|
55
|
+
|
56
|
+
## Parsers
|
57
|
+
|
58
|
+
Currently there is only a parser for the JSON encoding of MSFL filters. Any additional parsers will also be placed
|
59
|
+
under this directory.
|
60
|
+
|
61
|
+
## Types
|
62
|
+
|
63
|
+
Because of the behavioral limitations imposed on certain types (currently Sets are the only example) there is a folder
|
64
|
+
for types to be defined.
|
65
|
+
|
66
|
+
## Validators
|
67
|
+
|
68
|
+
After parsing a MSFL filter it can be validated. Currently the validation is primitive. The intent is to enable
|
69
|
+
semantic validation on a per dataset basis. This will allow per attribute validations to be setup by the consumer
|
70
|
+
of this gem, which will be run automatically during validation.
|
71
|
+
|
72
|
+
## Frameworks
|
73
|
+
|
74
|
+
### Sinatra
|
75
|
+
|
76
|
+
There are several helper methods for using this gem with Sinatra. You can register the helpers in your Sinatra app
|
77
|
+
by adding the following inside of your application's class.
|
78
|
+
|
79
|
+
```
|
80
|
+
# This should actually be Sinatra::MSFL but there are some namespacing issues with MSFL currently that prevented
|
81
|
+
# this from being the v0 implementation. This will change in the near future.
|
82
|
+
register MSFL::Sinatra
|
83
|
+
```
|
@@ -116,8 +116,9 @@ module MSFL
|
|
116
116
|
if obj[first_key].is_a?(Hash)
|
117
117
|
result = implicit_and_to_explicit_recursively obj[first_key], first_key
|
118
118
|
elsif obj[first_key].is_a? MSFL::Types::Set
|
119
|
-
#
|
120
|
-
#
|
119
|
+
# When an implicit and occurs under an MSFL::Types::Set when the key for the value which is the set
|
120
|
+
# is not a binary or logical operator. This doesn't happen in known current cases.
|
121
|
+
# ex. => { foo: [ { bar: { gte: 1, lte: 5 } } ] }
|
121
122
|
result = Hash.new
|
122
123
|
result[first_key] = recurse_through_set :implicit_and_to_explicit_recursively, obj[first_key]
|
123
124
|
elsif obj[first_key].is_a? Array
|
data/msfl.gemspec
CHANGED
@@ -140,6 +140,24 @@ describe "MSFL::Converters::Operator" do
|
|
140
140
|
|
141
141
|
let(:expected) { raise ArgumentError, "You are expected to define the expected value" }
|
142
142
|
|
143
|
+
context "when the argument is an Array" do
|
144
|
+
|
145
|
+
let(:arg) { ["foo", "bar"] }
|
146
|
+
|
147
|
+
it "raises an ArgumentError" do
|
148
|
+
expect { subject }.to raise_error ArgumentError
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "when the argument is a Hash containing an Array" do
|
153
|
+
|
154
|
+
let(:arg) { { and: [ { foo: 1 }, { bar: 2 } ] } }
|
155
|
+
|
156
|
+
it "raises an ArgumentError" do
|
157
|
+
expect { subject }.to raise_error ArgumentError
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
143
161
|
context "when there is not an implicit BETWEEN" do
|
144
162
|
|
145
163
|
["foo", { foo: "bar" }, 123, 56.12, :aaaah].each do |arg|
|
@@ -255,9 +273,9 @@ describe "MSFL::Converters::Operator" do
|
|
255
273
|
end
|
256
274
|
end
|
257
275
|
|
258
|
-
describe "#
|
276
|
+
describe "#implicit_and_to_explicit_recursively" do
|
259
277
|
|
260
|
-
subject
|
278
|
+
subject { test_instance.implicit_and_to_explicit_recursively arg }
|
261
279
|
|
262
280
|
let(:test_instance) { MSFL::Converters::Operator.new }
|
263
281
|
|
@@ -274,7 +292,7 @@ describe "MSFL::Converters::Operator" do
|
|
274
292
|
let(:arg) { 50 }
|
275
293
|
|
276
294
|
it "is the arg unchanged" do
|
277
|
-
expect(
|
295
|
+
expect(subject).to eq expected
|
278
296
|
end
|
279
297
|
end
|
280
298
|
|
@@ -283,7 +301,7 @@ describe "MSFL::Converters::Operator" do
|
|
283
301
|
let(:arg) { { gte: 1000 } }
|
284
302
|
|
285
303
|
it "is the arg unchanged" do
|
286
|
-
expect(
|
304
|
+
expect(subject).to eq expected
|
287
305
|
end
|
288
306
|
end
|
289
307
|
|
@@ -292,7 +310,7 @@ describe "MSFL::Converters::Operator" do
|
|
292
310
|
let(:arg) { { value: { gte: 1000 } } }
|
293
311
|
|
294
312
|
it "is the arg unchanged" do
|
295
|
-
expect(
|
313
|
+
expect(subject).to eq expected
|
296
314
|
end
|
297
315
|
end
|
298
316
|
|
@@ -310,7 +328,7 @@ describe "MSFL::Converters::Operator" do
|
|
310
328
|
])}}
|
311
329
|
|
312
330
|
it "converts the implicit AND to an explicit AND" do
|
313
|
-
expect(
|
331
|
+
expect(subject).to eq expected
|
314
332
|
end
|
315
333
|
end
|
316
334
|
|
@@ -325,7 +343,7 @@ describe "MSFL::Converters::Operator" do
|
|
325
343
|
])}}
|
326
344
|
|
327
345
|
it "converts the implicit AND to an explicit AND" do
|
328
|
-
expect(
|
346
|
+
expect(subject).to eq expected
|
329
347
|
end
|
330
348
|
end
|
331
349
|
|
@@ -347,7 +365,7 @@ describe "MSFL::Converters::Operator" do
|
|
347
365
|
end
|
348
366
|
|
349
367
|
it "converts both of the implicit ANDs to explicit ANDs" do
|
350
|
-
expect(
|
368
|
+
expect(subject).to eq expected
|
351
369
|
end
|
352
370
|
end
|
353
371
|
end
|
@@ -375,14 +393,65 @@ describe "MSFL::Converters::Operator" do
|
|
375
393
|
end
|
376
394
|
|
377
395
|
it "converts all of the implicit ANDs" do
|
378
|
-
expect(
|
396
|
+
expect(subject).to eq expected
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
context "when the implicit AND is under an MSFL::Types::Set" do
|
401
|
+
|
402
|
+
let(:arg) do
|
403
|
+
{
|
404
|
+
foo: MSFL::Types::Set.new([
|
405
|
+
{ bar: { gte: 1, lte: 5 } }
|
406
|
+
])
|
407
|
+
}
|
408
|
+
end
|
409
|
+
|
410
|
+
let(:expected) do
|
411
|
+
{
|
412
|
+
foo: MSFL::Types::Set.new([
|
413
|
+
{ and: MSFL::Types::Set.new([
|
414
|
+
{ bar: { gte: 1 } },
|
415
|
+
{ bar: { lte: 5 } }
|
416
|
+
])}
|
417
|
+
])
|
418
|
+
}
|
419
|
+
end
|
420
|
+
|
421
|
+
it "converts the implicit AND" do
|
422
|
+
expect(subject).to eq expected
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
context "when the implict AND is inside of an Array" do
|
427
|
+
|
428
|
+
let(:arg) do
|
429
|
+
{ and: [ { foo: 1 }, { bar: 2 } ] }
|
430
|
+
end
|
431
|
+
|
432
|
+
it "raises an ArgumentError" do
|
433
|
+
expect { subject }.to raise_error ArgumentError
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
context "when the implict AND is inside of an Array that is inside of an MSFL::Types::Set" do
|
438
|
+
|
439
|
+
let(:arg) do
|
440
|
+
{ foo: MSFL::Types::Set.new([
|
441
|
+
{ bar: [{ score: { gte: 1, lte: 5 } }, "efg"] }
|
442
|
+
])
|
443
|
+
}
|
444
|
+
end
|
445
|
+
|
446
|
+
it "raises an ArgumentError" do
|
447
|
+
expect { subject }.to raise_error ArgumentError
|
379
448
|
end
|
380
449
|
end
|
381
450
|
end
|
382
451
|
|
383
452
|
describe "#between_to_gte_lte_recursively" do
|
384
453
|
|
385
|
-
subject
|
454
|
+
subject { test_instance.between_to_gte_lte_recursively arg }
|
386
455
|
|
387
456
|
let(:test_instance) { MSFL::Converters::Operator.new }
|
388
457
|
|
@@ -404,7 +473,7 @@ describe "MSFL::Converters::Operator" do
|
|
404
473
|
let(:arg) { item }
|
405
474
|
|
406
475
|
it "is equal to the argument" do
|
407
|
-
expect(
|
476
|
+
expect(subject).to eq arg
|
408
477
|
end
|
409
478
|
end
|
410
479
|
end
|
@@ -417,7 +486,7 @@ describe "MSFL::Converters::Operator" do
|
|
417
486
|
let(:expected) { { foo: { gte: "2015-01-01", lte: "2015-04-01" } } }
|
418
487
|
|
419
488
|
it "converts between clauses into anded gte / lte clauses" do
|
420
|
-
expect(
|
489
|
+
expect(subject).to eq expected
|
421
490
|
end
|
422
491
|
|
423
492
|
context "when the between clause is below the second level" do
|
@@ -441,7 +510,7 @@ describe "MSFL::Converters::Operator" do
|
|
441
510
|
end
|
442
511
|
|
443
512
|
it "recursively converts between clauses into anded gte / lte clauses" do
|
444
|
-
expect(
|
513
|
+
expect(subject).to eq expected
|
445
514
|
end
|
446
515
|
end
|
447
516
|
end
|
@@ -453,7 +522,7 @@ describe "MSFL::Converters::Operator" do
|
|
453
522
|
let(:expected) { MSFL::Types::Set.new([ { foo: { gte: 1, lte: 5 } } ]) }
|
454
523
|
|
455
524
|
it "recursively converts between clauses into anded gte / lte clauses" do
|
456
|
-
expect(
|
525
|
+
expect(subject).to eq expected
|
457
526
|
end
|
458
527
|
|
459
528
|
context "when the between clause is below the second level" do
|
@@ -463,7 +532,7 @@ describe "MSFL::Converters::Operator" do
|
|
463
532
|
let(:expected) { MSFL::Types::Set.new([ { and: MSFL::Types::Set.new([{ foo: { gte: 1, lte: 5} }, { bar: 123} ]) }]) }
|
464
533
|
|
465
534
|
it "recursively converts between clauses into anded gte / lte clauses" do
|
466
|
-
expect(
|
535
|
+
expect(subject).to eq expected
|
467
536
|
end
|
468
537
|
end
|
469
538
|
end
|
@@ -473,7 +542,7 @@ describe "MSFL::Converters::Operator" do
|
|
473
542
|
let(:arg) { [ { foo: { between: { start: 1, end: 5 } } } ] }
|
474
543
|
|
475
544
|
it "raises an ArgumentError" do
|
476
|
-
expect {
|
545
|
+
expect { subject }.to raise_error ArgumentError
|
477
546
|
end
|
478
547
|
end
|
479
548
|
|
@@ -500,7 +569,7 @@ describe "MSFL::Converters::Operator" do
|
|
500
569
|
end
|
501
570
|
|
502
571
|
it "recursively converts between clauses into anded gte / lte clauses" do
|
503
|
-
expect(
|
572
|
+
expect(subject).to eq expected
|
504
573
|
end
|
505
574
|
end
|
506
575
|
end
|