msfl 0.0.1.pre.qa → 0.0.1.pre.rc1
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/.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:
|