msfl 1.1.5 → 1.1.6
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 +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
|
-
[](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
|