msfl 0.0.1.pre.qa → 0.0.1.pre.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +20 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +40 -0
- data/LICENSE +20 -0
- data/README.md +5 -0
- data/Rakefile +10 -0
- data/circle.yml +9 -0
- data/lib/msfl/datasets/base.rb +46 -4
- data/lib/msfl/parsers/json.rb +9 -0
- data/lib/msfl/validators/semantic.rb +12 -2
- data/msfl.gemspec +20 -0
- data/simplecov_custom_profiles.rb +5 -0
- data/spec/msfl/datasets/base_spec.rb +69 -0
- data/spec/msfl/parsers/json_spec.rb +146 -0
- data/spec/msfl/shared_examples.rb +0 -0
- data/spec/msfl/validators/semantic_spec.rb +124 -0
- data/spec/msfl_spec.rb +91 -0
- data/spec/spec_helper.rb +6 -0
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1433fb3fe0766b9a5a4913a5c8236bffb00e9c5e
|
4
|
+
data.tar.gz: 9307024455e4e75530f334e4c91d2a09f7da7090
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a7ac56dec38d01c8dcec0521e27cb03f361d952d8309d72bfa6494e9b5b49c8e34a89d1f2e98c66742a885110519ecd41ea24d3aa05b2508b1eeb20155244e3
|
7
|
+
data.tar.gz: 8a8150eb7c2ded7f183e7f124167a2dd3de92477cdc52731ee6868599625b344308c80bb5707230e51847ba11ad6a0635129a69a77887b112d570ffc3c2df6b5
|
data/.gitignore
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
### Ruby template
|
2
|
+
*.gem
|
3
|
+
*.rbc
|
4
|
+
/coverage/
|
5
|
+
/spec/reports/
|
6
|
+
|
7
|
+
## Documentation cache and generated files:
|
8
|
+
/.yardoc/
|
9
|
+
/_yardoc/
|
10
|
+
/doc/
|
11
|
+
/rdoc/
|
12
|
+
|
13
|
+
## Environment normalisation:
|
14
|
+
/.bundle/
|
15
|
+
/lib/bundler/man/
|
16
|
+
.idea/
|
17
|
+
|
18
|
+
## Gemfile for development
|
19
|
+
#Gemfile
|
20
|
+
#Gemfile.lock
|
data/Gemfile
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
gem 'simplecov', :require => false, :group => :test # MIT https://github.com/colszowka/simplecov/blob/master/MIT-LICENSE
|
3
|
+
gem 'yard' # MIT https://github.com/lsegal/yard/blob/master/LICENSE + Ruby license for one file from the Ruby source lib/parser/ruby/legacy/ruby_lex.rb
|
4
|
+
gem 'rspec' # MIT https://github.com/rspec/rspec/blob/master/License.txt
|
5
|
+
gem 'byebug'
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
byebug (3.5.1)
|
5
|
+
columnize (~> 0.8)
|
6
|
+
debugger-linecache (~> 1.2)
|
7
|
+
slop (~> 3.6)
|
8
|
+
columnize (0.8.9)
|
9
|
+
debugger-linecache (1.2.0)
|
10
|
+
diff-lcs (1.2.5)
|
11
|
+
docile (1.1.5)
|
12
|
+
multi_json (1.10.1)
|
13
|
+
rspec (3.1.0)
|
14
|
+
rspec-core (~> 3.1.0)
|
15
|
+
rspec-expectations (~> 3.1.0)
|
16
|
+
rspec-mocks (~> 3.1.0)
|
17
|
+
rspec-core (3.1.7)
|
18
|
+
rspec-support (~> 3.1.0)
|
19
|
+
rspec-expectations (3.1.2)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.1.0)
|
22
|
+
rspec-mocks (3.1.3)
|
23
|
+
rspec-support (~> 3.1.0)
|
24
|
+
rspec-support (3.1.2)
|
25
|
+
simplecov (0.9.1)
|
26
|
+
docile (~> 1.1.0)
|
27
|
+
multi_json (~> 1.0)
|
28
|
+
simplecov-html (~> 0.8.0)
|
29
|
+
simplecov-html (0.8.0)
|
30
|
+
slop (3.6.0)
|
31
|
+
yard (0.8.7.6)
|
32
|
+
|
33
|
+
PLATFORMS
|
34
|
+
ruby
|
35
|
+
|
36
|
+
DEPENDENCIES
|
37
|
+
byebug
|
38
|
+
rspec
|
39
|
+
simplecov
|
40
|
+
yard
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2015 Mattermark, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/circle.yml
ADDED
data/lib/msfl/datasets/base.rb
CHANGED
@@ -6,7 +6,9 @@ module MSFL
|
|
6
6
|
|
7
7
|
# The descendant class MUST override this method otherwise all field validations will fail
|
8
8
|
#
|
9
|
-
#
|
9
|
+
# The method defines an array of symbols, indicating what fields are supported for the Dataset
|
10
|
+
#
|
11
|
+
# @return [Array<Symbol>] the fields in the dataset
|
10
12
|
def fields
|
11
13
|
raise NoMethodError, "Descendants of MSFL::Datasets::Base are required to implement the #fields method"
|
12
14
|
end
|
@@ -14,11 +16,14 @@ module MSFL
|
|
14
16
|
# The descendant class may override this method to control the operators that are supported for the dataset
|
15
17
|
# - Note that this can only be used to reduce the number of supported operators (you can't add new operators
|
16
18
|
# here, without first adding them to MSFL::Validators::Definitions::HashKey#hash_key_operators)
|
19
|
+
#
|
20
|
+
# @return [Array<Symbol>] the operators supported in the dataset
|
17
21
|
def operators
|
18
22
|
hash_key_operators
|
19
23
|
end
|
20
24
|
|
21
|
-
#
|
25
|
+
# This method returns the errors argument. The errors argument is unchanged if type conformity validation passes,
|
26
|
+
# otherwise an error is added to errors.
|
22
27
|
#
|
23
28
|
# @param obj [Object] the object that should be type checked based on the field argument
|
24
29
|
# @param field [Symbol] which field should the object be checked for conformity
|
@@ -30,11 +35,14 @@ module MSFL
|
|
30
35
|
# # because the type of total_funding must be an integer
|
31
36
|
#
|
32
37
|
def validate_type_conforms(obj, field, errors)
|
38
|
+
errors << "Dataset type conformity validation failed for obj: #{obj} against field: #{field}" unless type_conforms?(obj, field)
|
33
39
|
errors
|
34
40
|
end
|
35
41
|
|
36
42
|
# Method not implemented at this time
|
37
43
|
# Returns true if the object conforms to the types supported by the indicated field
|
44
|
+
# While not currently implemented the intent is that the descendant Dataset would specify a hash of supported
|
45
|
+
# types for each field and this method would then cross reference that list.
|
38
46
|
#
|
39
47
|
# @param obj [Object] the object that should be type checked based on the field argument
|
40
48
|
# @param field [Symbol] which field should the object be checked for conformity
|
@@ -42,19 +50,33 @@ module MSFL
|
|
42
50
|
true
|
43
51
|
end
|
44
52
|
|
45
|
-
#
|
46
|
-
#
|
53
|
+
# This method returns the errors argument. The errors argument is unchanged if operator conformity validation
|
54
|
+
# passes, otherwise an error is added to errors.
|
47
55
|
#
|
48
56
|
# @param operator [Symbol] the operator that we want to know if the particular field supports it
|
49
57
|
# @param field [Symbol] which field should the operator be checked for conformity
|
50
58
|
# @param errors [Array] an array of validation errors - empty indicates that no errors have been encountered
|
51
59
|
# @return [Array] errors merged with any validation errors encountered in validating the set
|
52
60
|
def validate_operator_conforms(operator, field, errors)
|
61
|
+
errors << "Dataset operator conformity validation failed for operator: #{operator} against field: #{field}" unless operator_conforms?(operator, field)
|
53
62
|
errors
|
54
63
|
end
|
55
64
|
|
56
65
|
# Method not implemented at this time
|
57
66
|
#
|
67
|
+
# This method returns true if the operator is supported for the specified field by the dataset. While this
|
68
|
+
# is not currently implemented, the intent is that a hash of fields (as keys) would map to
|
69
|
+
# Arrays<Symbol> (as values) and then this method would validate that the operator argument meets this contract.
|
70
|
+
#
|
71
|
+
# @param operator [Symbol] the operator that needs to be checked for conformity
|
72
|
+
# @param field [Symbol] which field should the operator be checked against
|
73
|
+
# @return [Bool] true if the operator conforms, false otherwise
|
74
|
+
def operator_conforms?(operator, field)
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
# This method returns the errors argument. The errors argument is unchanged if value conformity validation
|
79
|
+
# passes, otherwise an error is added to errors.
|
58
80
|
#
|
59
81
|
# @param value [Object] the precoerced value (the value must be correctly typed)
|
60
82
|
# that should be checked for validity based on the field, if the value does not conform an
|
@@ -72,8 +94,28 @@ module MSFL
|
|
72
94
|
# foo.investors_value_conforms(12345, :total_funding) => # errors is unchanged
|
73
95
|
#
|
74
96
|
def validate_value_conforms(value, field, errors)
|
97
|
+
errors << "Dataset value conformity validation failed for value: #{value} against field: #{field}" unless value_conforms?(value, field, errors)
|
75
98
|
errors
|
76
99
|
end
|
100
|
+
|
101
|
+
# Method not implemented at this time
|
102
|
+
#
|
103
|
+
# This method returns true if the value is supported for the specified field by the dataset. While this is not
|
104
|
+
# currently implemented, the intent is that a hash of fields (as keys) would map to an Array of validation
|
105
|
+
# constraints. These constraints would then be executed against the value and if all are successful the value
|
106
|
+
# would be considered to have passed.
|
107
|
+
#
|
108
|
+
# It is likely that the methods invoked from the Array of validation constraints would actually return an Array
|
109
|
+
# of errors encountered, this method would then concat that Array into the errors array. If the encountered errors
|
110
|
+
# array is empty the method would return true, and false otherwise.
|
111
|
+
#
|
112
|
+
# @param value [Object] the object to on which to perform validation
|
113
|
+
# @param field [Symbol] the field the object should be validated against
|
114
|
+
# @param errors [Array] the array of errors from prior validations
|
115
|
+
# @return [Bool] true if no new errors are encountered, false otherwise
|
116
|
+
def value_conforms?(value, field, errors = [])
|
117
|
+
true
|
118
|
+
end
|
77
119
|
end
|
78
120
|
end
|
79
121
|
end
|
data/lib/msfl/parsers/json.rb
CHANGED
@@ -2,12 +2,21 @@ require 'json'
|
|
2
2
|
module MSFL
|
3
3
|
module Parsers
|
4
4
|
class JSON
|
5
|
+
|
6
|
+
# Parses json encoded MSFL into Ruby encoded MSFL
|
7
|
+
#
|
8
|
+
# @param json [String] the string to parse
|
9
|
+
# @return [Object] the Ruby encoded MSFL, which may be a Hash, MSFL::Types::Set, or any number of scalar types
|
5
10
|
def self.parse(json)
|
6
11
|
obj = ::JSON.parse(json)
|
7
12
|
obj = arrays_to_sets obj
|
8
13
|
obj
|
9
14
|
end
|
10
15
|
|
16
|
+
# Converts Ruby Arrays is a partially parsed Ruby MSFL filter to MSFL::Types::Set objects
|
17
|
+
#
|
18
|
+
# @param obj [Object] the object in which to convert Ruby Array objects to MSFL::Types::Set objects
|
19
|
+
# @return [Object] the result of converting Ruby Arrays to MSFL::Types::Set objects
|
11
20
|
def self.arrays_to_sets(obj)
|
12
21
|
obj = Types::Set.new obj if obj.is_a?(::Array)
|
13
22
|
if obj.respond_to? :each
|
@@ -11,8 +11,18 @@ module MSFL
|
|
11
11
|
BOOLEAN_OPERATORS = [:and, :or]
|
12
12
|
ENUMERATION_OPERATORS = [:in]
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
# Used for creating new semantic validator instances
|
15
|
+
#
|
16
|
+
# If the dataset argument is specified it will be used as the dataset for the validator. Otherwise
|
17
|
+
# the instance's dataset will default to the first value in MSFL.configuration.datasets, unless it is empty,
|
18
|
+
# in which case it will revert to MSFL::Datasets::Base, which will deliberately break execution when #validate
|
19
|
+
# is called on the validator instance, raising a NoMethodError.
|
20
|
+
#
|
21
|
+
# @param dataset [MSFL::Dataset::Base] optionally override the dataset instance that should be used by the validator
|
22
|
+
# @param opts [Hash] optional; currently not used, included for future additions
|
23
|
+
def initialize(dataset = nil, opts = {})
|
24
|
+
@dataset = dataset unless dataset.nil?
|
25
|
+
@dataset ||= MSFL.configuration.datasets.first.new unless MSFL.configuration.datasets.empty?
|
16
26
|
@dataset ||= Datasets::Base.new
|
17
27
|
@current_field = nil
|
18
28
|
@current_operator = nil
|
data/msfl.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'msfl'
|
3
|
+
s.version = '0.0.1-rc1'
|
4
|
+
s.date = '2015-03-05'
|
5
|
+
s.summary = "MSFL in Ruby"
|
6
|
+
s.description = "Serializers, validators, and other tasty goodness for the Mattermark Semantic Filter Language in Ruby."
|
7
|
+
s.authors = ["Courtland Caldwell"]
|
8
|
+
s.email = 'courtland@mattermark.com'
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
11
|
+
s.homepage =
|
12
|
+
'https://github.com/caldwecr/msfl'
|
13
|
+
s.add_runtime_dependency "json", "~> 1.7"
|
14
|
+
s.add_development_dependency "rake", "~> 10.3"
|
15
|
+
s.add_development_dependency "simplecov", "~> 0.9"
|
16
|
+
s.add_development_dependency "yard", "~> 0.8"
|
17
|
+
s.add_development_dependency "rspec", "~> 3.1"
|
18
|
+
s.add_development_dependency "byebug", "~> 3.5"
|
19
|
+
s.license = "MIT"
|
20
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "MSFL::Datasets::Base" do
|
4
|
+
|
5
|
+
let(:test_instance) { MSFL::Datasets::Movies.new }
|
6
|
+
|
7
|
+
let(:errors) { [ ] }
|
8
|
+
|
9
|
+
let(:field) { :title }
|
10
|
+
|
11
|
+
describe "#fields" do
|
12
|
+
|
13
|
+
it "raises a NoMethodError" do
|
14
|
+
expect { MSFL::Datasets::Base.new.fields }.to raise_error NoMethodError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#validate_type_conforms" do
|
19
|
+
|
20
|
+
subject(:mut) { test_instance.validate_type_conforms obj, field, errors }
|
21
|
+
|
22
|
+
let(:obj) { "i am a string" }
|
23
|
+
|
24
|
+
it "is currently a stubbed method that just returns the errors argument" do
|
25
|
+
expect(mut).to eq errors
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#type_conforms?" do
|
30
|
+
|
31
|
+
context "when MSFL is configured to use Movies as the dataset" do
|
32
|
+
|
33
|
+
subject(:mut) { test_instance.type_conforms? obj, field }
|
34
|
+
|
35
|
+
let(:obj) { "i am a string" }
|
36
|
+
|
37
|
+
it "is true for types that conform to the Dataset" do
|
38
|
+
expect(mut).to eq true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "is false for types that do not conform to the Dataset" do
|
42
|
+
pending "Dataset specific semantic validation is not yet implemented."
|
43
|
+
expect(mut).to eq false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#validate_operator_conforms" do
|
49
|
+
|
50
|
+
subject(:mut) { test_instance.validate_operator_conforms operator, field, errors }
|
51
|
+
|
52
|
+
let(:operator) { :and }
|
53
|
+
|
54
|
+
it "is currently a stubbed method that just returns the errors argument" do
|
55
|
+
expect(mut).to eq errors
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#validate_value_conforms" do
|
60
|
+
|
61
|
+
subject(:mut) { test_instance.validate_value_conforms value, field, errors }
|
62
|
+
|
63
|
+
let(:value) { 1234 }
|
64
|
+
|
65
|
+
it "is currently a stubbed method that just returns the errors argument" do
|
66
|
+
expect(mut).to eq errors
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
# Still need to deal with duplicates in array scenarios - is there a nested array variation with a non-obvious deduplication consequence?
|
5
|
+
describe "MSFL::Parsers::JSON" do
|
6
|
+
describe ".parse" do
|
7
|
+
|
8
|
+
subject(:mut) { MSFL::Parsers::JSON.parse test_json }
|
9
|
+
|
10
|
+
let(:test_json) { '{"total_funding": 5000000}' }
|
11
|
+
|
12
|
+
context "when parsing a json hash" do
|
13
|
+
|
14
|
+
it "is an equivalent Ruby Hash" do
|
15
|
+
expect(mut).to eq({ :total_funding => 5000000 })
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when parsing a json array" do
|
20
|
+
|
21
|
+
let(:test_json) { '["abc", "def"]' }
|
22
|
+
|
23
|
+
it { is_expected.to be_a MSFL::Types::Set }
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ".arrays_to_sets" do
|
29
|
+
|
30
|
+
subject(:mut) { MSFL::Parsers::JSON.arrays_to_sets arg }
|
31
|
+
|
32
|
+
let(:arg) { Object.new }
|
33
|
+
|
34
|
+
[55, "five", :fourty, nil].each do |item|
|
35
|
+
context "when the argument is a #{item.class}" do
|
36
|
+
|
37
|
+
let(:arg) { item }
|
38
|
+
|
39
|
+
it "is equal to the argument" do
|
40
|
+
expect(mut).to eq arg
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when the argument is a Hash" do
|
46
|
+
|
47
|
+
context "when the hash's values are scalars" do
|
48
|
+
|
49
|
+
let(:arg) { { foo: "bar", cat: 1337 } }
|
50
|
+
|
51
|
+
it "is equal to the argument" do
|
52
|
+
expect(mut).to eq arg
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "when the hash's values include at least one Hash" do
|
57
|
+
|
58
|
+
let(:arg) do
|
59
|
+
{ foo: { bar: "bar" }, abc: 123 }
|
60
|
+
end
|
61
|
+
|
62
|
+
it "is equal to the argument" do
|
63
|
+
expect(mut).to eq arg
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when the hash's values include at least one Array" do
|
68
|
+
|
69
|
+
let(:arg) do
|
70
|
+
{ inner_array: array_in_arg, abc: 123 }
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:expected) do
|
74
|
+
{ inner_array: MSFL::Types::Set.new(array_in_arg), abc: 123 }
|
75
|
+
end
|
76
|
+
|
77
|
+
let(:array_in_arg) { ["bar", "baz"] }
|
78
|
+
|
79
|
+
it "is the argument with the Array converted to a MSFL::Types::Set" do
|
80
|
+
expect(mut).to eq expected
|
81
|
+
end
|
82
|
+
|
83
|
+
context "when at least one included Array has a duplicate item" do
|
84
|
+
|
85
|
+
let(:array_in_arg) { ["cat", "cat", 44, 55, "dog", 44, :marco, :polo, nil, :polo, "cat", nil] }
|
86
|
+
|
87
|
+
it "includes the duplicate item(s) exactly once" do
|
88
|
+
expect(mut[:inner_array]).to eq MSFL::Types::Set.new(["cat", 44, 55, "dog", :marco, :polo, nil])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "when the argument is an Array" do
|
95
|
+
|
96
|
+
let(:arg) { [:foo, "bar", nil, 99, "bar"] }
|
97
|
+
|
98
|
+
it { is_expected.to be_a MSFL::Types::Set }
|
99
|
+
|
100
|
+
it "is unordered" do
|
101
|
+
expect(mut).to eq MSFL::Types::Set.new(arg.shuffle)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "is deduplicated" do
|
105
|
+
expect(mut).to eq MSFL::Types::Set.new([:foo, "bar", nil, 99])
|
106
|
+
end
|
107
|
+
|
108
|
+
context "when the array's values are scalars" do
|
109
|
+
|
110
|
+
let(:arg) { ["bar", 1337] }
|
111
|
+
|
112
|
+
it "is equal to MSFL::Types::Set.new(argument)" do
|
113
|
+
expect(mut).to eq(MSFL::Types::Set.new arg)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when the argument includes at least one Hash" do
|
118
|
+
|
119
|
+
let(:arg) { ["bar", hash_in_arg, 84] }
|
120
|
+
|
121
|
+
let(:hash_in_arg) { { cat: 1221, dog: "fur" } }
|
122
|
+
|
123
|
+
it "includes the hash from the argument" do
|
124
|
+
expect(mut).to include hash_in_arg
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "when the argument includes at least one Array" do
|
129
|
+
|
130
|
+
let(:arg) { ["bar", array_in_arg, 84] }
|
131
|
+
|
132
|
+
let(:array_in_arg) { [444.3, "where'swaldo"] }
|
133
|
+
|
134
|
+
let(:expected_result) { MSFL::Types::Set.new ["bar", MSFL::Types::Set.new(array_in_arg), 84] }
|
135
|
+
|
136
|
+
it "replaces the outer Array with an equivalent MSFL::Types::Set" do
|
137
|
+
expect(mut).to eq expected_result
|
138
|
+
end
|
139
|
+
|
140
|
+
it "replaces the inner Array with an equivalent MSFL::Types::Set" do
|
141
|
+
expect(mut).to include MSFL::Types::Set.new(array_in_arg)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
File without changes
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "MSFL::Validators::Semantic" do
|
4
|
+
|
5
|
+
describe "#initialize" do
|
6
|
+
|
7
|
+
subject(:mut) { MSFL::Validators::Semantic.new dataset }
|
8
|
+
|
9
|
+
let(:dataset) { nil }
|
10
|
+
|
11
|
+
context "when the dataset argument is specified" do
|
12
|
+
|
13
|
+
let(:dataset) { Object.new }
|
14
|
+
|
15
|
+
it "has the dataset argument's value as the dataset" do
|
16
|
+
expect(mut.dataset).to be dataset
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when the dataset argument is not specified" do
|
21
|
+
|
22
|
+
context "when MSFL is configured for one or more datasets" do
|
23
|
+
|
24
|
+
before { MSFL.configure(reset: true) { |configuration| configuration.datasets = [MSFL::Datasets::Movies] } }
|
25
|
+
|
26
|
+
it "has an instance of the first item in MSFL.configuration.datasets (an instance of Class) as the dataset" do
|
27
|
+
expect(mut.dataset).to be_a MSFL::Datasets::Movies
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when MSFL is not configured for any datasets" do
|
32
|
+
|
33
|
+
before { MSFL.configure(reset: true) }
|
34
|
+
|
35
|
+
it "has an instance of MSFL::Datasets::Base as the dataset" do
|
36
|
+
expect(mut.dataset).to be_a MSFL::Datasets::Base
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#validate_set" do
|
44
|
+
|
45
|
+
subject(:mut) { test_instance.validate_set set, errors, options }
|
46
|
+
|
47
|
+
let(:test_instance) { MSFL::Validators::Semantic.new }
|
48
|
+
|
49
|
+
let(:set) { MSFL::Types::Set.new([ ]) }
|
50
|
+
|
51
|
+
let(:errors) { [ ] }
|
52
|
+
|
53
|
+
let(:options) { { } }
|
54
|
+
|
55
|
+
context "when the errors argument has items" do
|
56
|
+
|
57
|
+
let(:errors) { [error_message] }
|
58
|
+
|
59
|
+
let(:error_message) { "This is an error message" }
|
60
|
+
|
61
|
+
it "is an array containing at least all of the error messages from the original errors argument" do
|
62
|
+
expect(mut).to include error_message
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "when opts does not have the key :parent_operator" do
|
67
|
+
|
68
|
+
let(:error_message) do
|
69
|
+
"Validate set requires the :parent_operator option be set and represented in either the BOOLEAN_OPERATORS
|
70
|
+
or ENUMERATION_OPERATORS constant"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "appends an error to errors" do
|
74
|
+
expect(mut).to include error_message
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when opts[:parent_operator] is in BOOLEAN_OPERATORS" do
|
79
|
+
|
80
|
+
let(:options) { { parent_operator: :and } }
|
81
|
+
|
82
|
+
let(:test_instance) do
|
83
|
+
t_i = MSFL::Validators::Semantic.new
|
84
|
+
allow(t_i).to receive(:validate_boolean_set) { errors }
|
85
|
+
expect(t_i).to receive(:validate_boolean_set).once.with(set, errors, options)
|
86
|
+
t_i
|
87
|
+
end
|
88
|
+
|
89
|
+
it "invokes #validate_boolean_set once with the same arguments" do
|
90
|
+
mut
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "when opts[:parent_operator] is in ENUMERATION_OPERATORS" do
|
95
|
+
|
96
|
+
let(:options) { { parent_operator: :in } }
|
97
|
+
|
98
|
+
let(:test_instance) do
|
99
|
+
t_i = MSFL::Validators::Semantic.new
|
100
|
+
allow(t_i).to receive(:validate_enumeration_set) { errors }
|
101
|
+
expect(t_i).to receive(:validate_enumeration_set).once.with(set, errors, options)
|
102
|
+
t_i
|
103
|
+
end
|
104
|
+
|
105
|
+
it "invokes #validate_enumeration_set once with the same arguments" do
|
106
|
+
mut
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "when opts[:parent_operator] is not in neither BOOLEAN_OPERATORS nor ENUMERATION_OPERATORS" do
|
111
|
+
|
112
|
+
let(:options) { { parent_operator: :not_in_either } }
|
113
|
+
|
114
|
+
let(:error_message) do
|
115
|
+
"Validate set requires the :parent_operator option be set and represented in either the BOOLEAN_OPERATORS
|
116
|
+
or ENUMERATION_OPERATORS constant"
|
117
|
+
end
|
118
|
+
|
119
|
+
it "appends an error to errors" do
|
120
|
+
expect(mut).to include error_message
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/spec/msfl_spec.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'msfl/datasets/movies'
|
3
|
+
describe "MSFL" do
|
4
|
+
|
5
|
+
let(:json_encoded_msfl) do
|
6
|
+
'{
|
7
|
+
"or": [
|
8
|
+
{
|
9
|
+
"and": [
|
10
|
+
{
|
11
|
+
"title": {
|
12
|
+
"in": [
|
13
|
+
"Frozen",
|
14
|
+
"Big Hero 6",
|
15
|
+
"Apollo 13"
|
16
|
+
]
|
17
|
+
}
|
18
|
+
},
|
19
|
+
{
|
20
|
+
"rating": {
|
21
|
+
"gte": "PG"
|
22
|
+
}
|
23
|
+
}
|
24
|
+
]
|
25
|
+
},
|
26
|
+
{
|
27
|
+
"earnings": {
|
28
|
+
"lt": 5000000
|
29
|
+
}
|
30
|
+
}
|
31
|
+
]
|
32
|
+
}'
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:invalid_ruby_encoded_msfl) do
|
36
|
+
{ not_a_field_in_dataset: "foobar" }
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:ruby_encoded_msfl) do
|
40
|
+
{
|
41
|
+
or: MSFL::Types::Set.new([
|
42
|
+
{
|
43
|
+
and: MSFL::Types::Set.new([
|
44
|
+
{
|
45
|
+
title: {
|
46
|
+
in: MSFL::Types::Set.new([
|
47
|
+
"Frozen",
|
48
|
+
"Big Hero 6",
|
49
|
+
"Apollo 13"
|
50
|
+
])
|
51
|
+
}
|
52
|
+
},
|
53
|
+
{
|
54
|
+
rating: {
|
55
|
+
gte: "PG"
|
56
|
+
}
|
57
|
+
}
|
58
|
+
])
|
59
|
+
},
|
60
|
+
{
|
61
|
+
earnings: {
|
62
|
+
lt: 5000000
|
63
|
+
}
|
64
|
+
}
|
65
|
+
])
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:parser) { MSFL::Parsers::JSON }
|
70
|
+
|
71
|
+
let(:validator) { MSFL::Validators::Semantic.new }
|
72
|
+
|
73
|
+
it "is configured using a block" do
|
74
|
+
MSFL.configure(reset: true) { |configuration| configuration.datasets = [MSFL::Datasets::Movies] }
|
75
|
+
expect(MSFL.configuration.datasets).to eq [MSFL::Datasets::Movies]
|
76
|
+
end
|
77
|
+
|
78
|
+
it "parses json encoded MSFL" do
|
79
|
+
expect(parser.parse json_encoded_msfl).to eq ruby_encoded_msfl
|
80
|
+
end
|
81
|
+
|
82
|
+
it "rejects invalid MSFL filters" do
|
83
|
+
MSFL.configure(reset: true) { |conf| conf.datasets = [MSFL::Datasets::Movies] }
|
84
|
+
expect(validator.validate invalid_ruby_encoded_msfl).to be false
|
85
|
+
end
|
86
|
+
|
87
|
+
it "accepts valid MSFL filters" do
|
88
|
+
MSFL.configure(reset: true) { |conf| conf.datasets = [MSFL::Datasets::Movies] }
|
89
|
+
expect(validator.validate ruby_encoded_msfl).to be true
|
90
|
+
end
|
91
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: msfl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.pre.
|
4
|
+
version: 0.0.1.pre.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Courtland Caldwell
|
@@ -101,6 +101,13 @@ executables: []
|
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
|
+
- .gitignore
|
105
|
+
- Gemfile
|
106
|
+
- Gemfile.lock
|
107
|
+
- LICENSE
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- circle.yml
|
104
111
|
- lib/msfl.rb
|
105
112
|
- lib/msfl/configuration.rb
|
106
113
|
- lib/msfl/datasets.rb
|
@@ -114,6 +121,14 @@ files:
|
|
114
121
|
- lib/msfl/validators/definitions.rb
|
115
122
|
- lib/msfl/validators/definitions/hash_key.rb
|
116
123
|
- lib/msfl/validators/semantic.rb
|
124
|
+
- msfl.gemspec
|
125
|
+
- simplecov_custom_profiles.rb
|
126
|
+
- spec/msfl/datasets/base_spec.rb
|
127
|
+
- spec/msfl/parsers/json_spec.rb
|
128
|
+
- spec/msfl/shared_examples.rb
|
129
|
+
- spec/msfl/validators/semantic_spec.rb
|
130
|
+
- spec/msfl_spec.rb
|
131
|
+
- spec/spec_helper.rb
|
117
132
|
homepage: https://github.com/caldwecr/msfl
|
118
133
|
licenses:
|
119
134
|
- MIT
|
@@ -138,5 +153,11 @@ rubygems_version: 2.4.2
|
|
138
153
|
signing_key:
|
139
154
|
specification_version: 4
|
140
155
|
summary: MSFL in Ruby
|
141
|
-
test_files:
|
156
|
+
test_files:
|
157
|
+
- spec/msfl/datasets/base_spec.rb
|
158
|
+
- spec/msfl/parsers/json_spec.rb
|
159
|
+
- spec/msfl/shared_examples.rb
|
160
|
+
- spec/msfl/validators/semantic_spec.rb
|
161
|
+
- spec/msfl_spec.rb
|
162
|
+
- spec/spec_helper.rb
|
142
163
|
has_rdoc:
|