msfl 1.1.6 → 1.2.0.dev
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 +205 -20
- data/lib/msfl/datasets/car.rb +1 -1
- data/lib/msfl/datasets/person.rb +15 -0
- data/lib/msfl/parsers/json.rb +1 -1
- data/msfl.gemspec +2 -2
- data/spec/msfl/parsers/json_spec.rb +21 -0
- metadata +20 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 836568a1640ce165512978aa087ea16d8c87088e
|
4
|
+
data.tar.gz: d5898063d32d174b84522ab16a56265b4453b9a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5df4af19fc17ababf06c3570af87f93021d6ff4dc7bfedf936dfe9942f3e4016d15e6eb5a8b4f73bb38292996ff173b36263eff1d0e24fe78a1f376f5662fcb2
|
7
|
+
data.tar.gz: ded9b1ceae6f7806502a2f8a3e0c5c3e5c19968c3a5dec9fac35e3d0f1ff7fe9eab8857feb13ae1ce92764ad085335a1ee16987861a60b2dc4a1172e5330eee6
|
data/README.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
[](https://circleci.com/gh/Referly/msfl)
|
2
2
|
|
3
|
+
# MSFL
|
4
|
+
|
5
|
+
The Mattermark Semantic Filter Language is a language for _filtering_ data. It allows users to construct filters
|
6
|
+
on sets of data in a Venn diagram like fashion. More formally it allows the user to create subsets of data.
|
7
|
+
|
8
|
+
MSFL is different than other languages - it is _not_ a query language. It has no notion of concepts like _order_,
|
9
|
+
_limit_, _offset_, _group_by_, or _having_.
|
10
|
+
|
11
|
+
It does support faceted filtering through a vocabulary added in 1.2 which allows the user to define filters
|
12
|
+
on the hyperset of the data. (In the non-hyper case all of the extra dimensions are simplified to constants.) If
|
13
|
+
you don't know what faceted filtering is or this sounds overwhelming, just ignore this bit. When you get to the point
|
14
|
+
that you need faceted filtering in your application then this will seem second nature.
|
15
|
+
|
3
16
|
# Ruby Gem for the Mattermark Semantic Filter Language
|
4
17
|
|
5
18
|
Contains serializers and validators (and perhaps other) MSFL goodies
|
@@ -8,27 +21,191 @@ Contains serializers and validators (and perhaps other) MSFL goodies
|
|
8
21
|
|
9
22
|
MSFL is a context-free language. The context-free grammar is defined below.
|
10
23
|
|
11
|
-
|
24
|
+
# EXPRESSIONS
|
25
|
+
|
26
|
+
filter = lc , { filter_expr } , rc ;
|
27
|
+
|
28
|
+
filter_expr = range_expr
|
29
|
+
| binary_expr
|
30
|
+
| set_expr
|
31
|
+
| partial_expr
|
32
|
+
| foreign_expr ;
|
33
|
+
|
34
|
+
range_expr = between ;
|
35
|
+
|
36
|
+
binary_expr = comparisons
|
37
|
+
| containment ;
|
38
|
+
|
39
|
+
set_expr = and
|
40
|
+
| or ;
|
41
|
+
|
42
|
+
partial_expr = partial_op , colon , partial ;
|
43
|
+
|
44
|
+
foreign_expr = word , colon , filter ;
|
45
|
+
|
46
|
+
partial = lc , given_expr , comma , partial_filter , rc ;
|
47
|
+
|
48
|
+
given_expr = given_op , colon, filter ;
|
49
|
+
|
50
|
+
partial_filter = filter_op , colon , filter ;
|
51
|
+
|
52
|
+
between = value , colon , start_end
|
53
|
+
| value , colon , between_body ;
|
54
|
+
|
55
|
+
comparisons = comparison , { comma , comparison } ;
|
56
|
+
|
57
|
+
containment = word , colon , in_expr ;
|
58
|
+
|
59
|
+
and = and_op , colon , filters ;
|
60
|
+
|
61
|
+
or = or_op , colon , filters ;
|
62
|
+
|
63
|
+
comparison = word , colon , value
|
64
|
+
| word , colon , lc , comparison_list , rc ;
|
65
|
+
|
66
|
+
comparison_list = comparison_expr , { comma , comparison_expr } ;
|
67
|
+
|
68
|
+
comparison_expr = comparison_op , colon , value ;
|
69
|
+
|
70
|
+
in_expr = lc , in_op , colon , values , rc ;
|
71
|
+
|
72
|
+
filters = ls , { filter } , rs ;
|
73
|
+
|
74
|
+
values = ls , { value } , rs ;
|
75
|
+
|
76
|
+
between_body = lc , between_op , colon , start_end , rc ;
|
77
|
+
|
78
|
+
start_end = lc , start_expr , comma , end_expr , rc ;
|
79
|
+
|
80
|
+
start_expr = start_op , colon , range_value ;
|
81
|
+
|
82
|
+
end_expr = end_op , colon , range_value ;
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
# OPERATORS
|
87
|
+
|
88
|
+
partial_op = dq , "partial" , dq ;
|
89
|
+
|
90
|
+
given_op = dq , "given" , dq ;
|
91
|
+
|
92
|
+
filter_op = dq , "filter" , dq ;
|
93
|
+
|
94
|
+
in_op = dq , "in" , dq ;
|
95
|
+
|
96
|
+
between_op = dq , "between" , dq ;
|
97
|
+
|
98
|
+
start_op = dq , "start" , dq ;
|
99
|
+
|
100
|
+
end_op = dq , "end" , dq ;
|
101
|
+
|
102
|
+
comparison_op = lt_op
|
103
|
+
| gt_op
|
104
|
+
| lte_op
|
105
|
+
| gte_op
|
106
|
+
| eq_op ;
|
107
|
+
|
108
|
+
lt_op = dq , "lt" , dq ;
|
109
|
+
|
110
|
+
gt_op = dq , "gt" , dq ;
|
111
|
+
|
112
|
+
lte_op = dq , "lte" , dq ;
|
113
|
+
|
114
|
+
gte_op = dq , "gte" , dq ;
|
115
|
+
|
116
|
+
eq_op = dq , "eq" , dq ;
|
117
|
+
|
118
|
+
and_op = dq , "and" , dq ;
|
119
|
+
|
120
|
+
or_op = dq , "or" , dq ;
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
# VALUES AND TYPES
|
125
|
+
|
126
|
+
range_value = number
|
127
|
+
| date
|
128
|
+
| datetime
|
129
|
+
| time ;
|
130
|
+
|
131
|
+
value = word
|
132
|
+
| range_value
|
133
|
+
| boolean ;
|
134
|
+
|
135
|
+
word = dq , character , { character } , dq ;
|
136
|
+
|
137
|
+
number = integer | decimal ;
|
138
|
+
|
139
|
+
integer = [ hyphen ] , digit , { digit } ;
|
140
|
+
|
141
|
+
decimal = integer
|
142
|
+
| { integer } , dot , { digit } ;
|
143
|
+
|
144
|
+
boolean = true | false ;
|
145
|
+
|
146
|
+
true = "true"
|
147
|
+
| dq , "true" , dq
|
148
|
+
| "1"
|
149
|
+
| dq , "1" , dq ;
|
150
|
+
|
151
|
+
false = "false"
|
152
|
+
| dq , "false" , dq
|
153
|
+
| "0"
|
154
|
+
| dq , "0" , dq ;
|
155
|
+
|
156
|
+
date = ? ISO 8601 date format http://en.wikipedia.org/wiki/ISO_8601 ? ;
|
157
|
+
|
158
|
+
datetime = ? ISO 8601 combined date and time format http://en.wikipedia.org/wiki/ISO_8601 ? ;
|
159
|
+
|
160
|
+
time = ? ISO 8601 time format http://en.wikipedia.org/wiki/ISO_8601 ? ;
|
161
|
+
|
162
|
+
character = letter
|
163
|
+
| digit
|
164
|
+
| symbol ;
|
165
|
+
|
166
|
+
letter = "A" | "B" | "C" | "D" | "E" | "F" | "G"
|
167
|
+
| "H" | "I" | "J" | "K" | "L" | "M" | "N"
|
168
|
+
| "O" | "P" | "Q" | "R" | "S" | "T" | "U"
|
169
|
+
| "V" | "W" | "X" | "Y" | "Z"
|
170
|
+
| "a" | "b" | "c" | "d" | "e" | "f" | "g"
|
171
|
+
| "h" | "i" | "j" | "k" | "l" | "m" | "n"
|
172
|
+
| "o" | "p" | "q" | "r" | "s" | "t" | "u"
|
173
|
+
| "v" | "w" | "x" | "y" | "z" ;
|
174
|
+
|
175
|
+
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
|
176
|
+
|
177
|
+
symbol = "'" | "~" | "." | "_" | "-" | ":" | "?" | "/" | "=" | "@" | "&" ;
|
178
|
+
|
179
|
+
left_curly = "{" ;
|
180
|
+
|
181
|
+
lc = left_curly ;
|
182
|
+
|
183
|
+
right_curly = "}" ;
|
184
|
+
|
185
|
+
rc = right_curly ;
|
186
|
+
|
187
|
+
left_square = "[" ;
|
188
|
+
|
189
|
+
ls = left_square ;
|
190
|
+
|
191
|
+
right_square = "]" ;
|
192
|
+
|
193
|
+
rs = right_square ;
|
194
|
+
|
195
|
+
comma = "," ;
|
196
|
+
|
197
|
+
hyphen = "-" ;
|
198
|
+
|
199
|
+
colon = ":" ;
|
200
|
+
|
201
|
+
double_quote = '"' ;
|
202
|
+
|
203
|
+
dq = double_quote ;
|
204
|
+
|
205
|
+
dot = "." ;
|
206
|
+
|
207
|
+
|
12
208
|
|
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
209
|
|
33
210
|
## Configuration
|
34
211
|
|
@@ -69,6 +246,14 @@ After parsing a MSFL filter it can be validated. Currently the validation is pri
|
|
69
246
|
semantic validation on a per dataset basis. This will allow per attribute validations to be setup by the consumer
|
70
247
|
of this gem, which will be run automatically during validation.
|
71
248
|
|
249
|
+
Validation works in the following order
|
250
|
+
|
251
|
+
1. Grammar validation
|
252
|
+
|
253
|
+
2. Dataset configured validation
|
254
|
+
|
255
|
+
3. Dataset semantic validation
|
256
|
+
|
72
257
|
## Frameworks
|
73
258
|
|
74
259
|
### Sinatra
|
data/lib/msfl/datasets/car.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module MSFL
|
4
|
+
module Datasets
|
5
|
+
# This is a fake dataset definition that shows the structure for composing your own and is used for testing
|
6
|
+
# msfl
|
7
|
+
class Person < ::MSFL::Datasets::Base
|
8
|
+
register_dataset
|
9
|
+
|
10
|
+
def fields
|
11
|
+
[:name, :gender, :age, :cars]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/msfl/parsers/json.rb
CHANGED
@@ -16,7 +16,7 @@ module MSFL
|
|
16
16
|
obj
|
17
17
|
end
|
18
18
|
|
19
|
-
# Converts Ruby Arrays
|
19
|
+
# Converts Ruby Arrays in a partially parsed Ruby MSFL filter to MSFL::Types::Set objects
|
20
20
|
#
|
21
21
|
# @param obj [Object] the object in which to convert Ruby Array objects to MSFL::Types::Set objects
|
22
22
|
# @return [Object] the result of converting Ruby Arrays to MSFL::Types::Set objects
|
data/msfl.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'msfl'
|
3
|
-
s.version = '1.
|
4
|
-
s.date = '2015-
|
3
|
+
s.version = '1.2.0.dev'
|
4
|
+
s.date = '2015-05-12'
|
5
5
|
s.summary = "MSFL in Ruby"
|
6
6
|
s.description = "Serializers, validators, and other tasty goodness for the Mattermark Semantic Filter Language in Ruby."
|
7
7
|
s.authors = ["Courtland Caldwell"]
|
@@ -34,6 +34,27 @@ describe "MSFL::Parsers::JSON" do
|
|
34
34
|
it { is_expected.to be_a MSFL::Types::Set }
|
35
35
|
end
|
36
36
|
|
37
|
+
context "when parsing a foreign" do
|
38
|
+
|
39
|
+
# The test scenario here is if we are limiting the set of cars by person criteria (only cars owned by people over 25)
|
40
|
+
|
41
|
+
let(:test_json) { '{"person":{"age":{"gte":25}}}' }
|
42
|
+
|
43
|
+
it "is an equivalent Ruby hash" do
|
44
|
+
expect(mut).to eq({ person: { age: { gte: 25 } } })
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when parsing a partial" do
|
50
|
+
|
51
|
+
let(:test_json) { '{"partial":{"given":{"make":"Toyota"},"filter":{"avg_age":10}}}'}
|
52
|
+
|
53
|
+
it "is an equivalent Ruby Hash" do
|
54
|
+
expect(mut).to eq({ partial: { given: { make: "Toyota" }, filter: { avg_age: 10 } } })
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
37
58
|
end
|
38
59
|
|
39
60
|
describe ".arrays_to_sets" do
|
metadata
CHANGED
@@ -1,97 +1,97 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: msfl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0.dev
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Courtland Caldwell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.7'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.7'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '10.3'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.3'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: simplecov
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0.9'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0.9'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: yard
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - ~>
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0.8'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - ~>
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0.8'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - ~>
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '3.1'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - ~>
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '3.1'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: byebug
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - ~>
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '3.5'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - ~>
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '3.5'
|
97
97
|
description: Serializers, validators, and other tasty goodness for the Mattermark
|
@@ -101,7 +101,7 @@ executables: []
|
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
|
-
- .gitignore
|
104
|
+
- ".gitignore"
|
105
105
|
- Gemfile
|
106
106
|
- Gemfile.lock
|
107
107
|
- LICENSE
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- lib/msfl/datasets/base.rb
|
117
117
|
- lib/msfl/datasets/car.rb
|
118
118
|
- lib/msfl/datasets/movie.rb
|
119
|
+
- lib/msfl/datasets/person.rb
|
119
120
|
- lib/msfl/parsers.rb
|
120
121
|
- lib/msfl/parsers/json.rb
|
121
122
|
- lib/msfl/sinatra.rb
|
@@ -146,17 +147,17 @@ require_paths:
|
|
146
147
|
- lib
|
147
148
|
required_ruby_version: !ruby/object:Gem::Requirement
|
148
149
|
requirements:
|
149
|
-
- -
|
150
|
+
- - ">="
|
150
151
|
- !ruby/object:Gem::Version
|
151
152
|
version: '0'
|
152
153
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
154
|
requirements:
|
154
|
-
- -
|
155
|
+
- - ">"
|
155
156
|
- !ruby/object:Gem::Version
|
156
|
-
version:
|
157
|
+
version: 1.3.1
|
157
158
|
requirements: []
|
158
159
|
rubyforge_project:
|
159
|
-
rubygems_version: 2.
|
160
|
+
rubygems_version: 2.2.2
|
160
161
|
signing_key:
|
161
162
|
specification_version: 4
|
162
163
|
summary: MSFL in Ruby
|