msfl 1.2.1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/msfl/converters/operator.rb +62 -41
- data/lib/msfl/datasets/animal.rb +24 -0
- data/lib/msfl/datasets/base.rb +0 -2
- data/lib/msfl/datasets/person.rb +1 -1
- data/lib/msfl/validators/definitions/hash_key.rb +8 -0
- data/msfl.gemspec +2 -2
- data/spec/msfl/converters/operator_spec.rb +14 -0
- data/spec/msfl/datasets/animal_spec.rb +24 -0
- data/spec/msfl/datasets/base_spec.rb +62 -0
- data/spec/msfl/validators/definitions/hash_key_spec.rb +38 -0
- data/spec/spec_helper.rb +3 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ce057720e95af53947418f9609ab57abcd2051c
|
4
|
+
data.tar.gz: 1786d887042de9117b98de5396dcb8e5087693e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f78c1b92cbcf54eed39828c10018e8fb6578ccb7ef96b48306adbef2e8fc9da3cb3aff6ad9aa995ab00ba452096a0c090e6945023c562195f12cc2c6ee71905
|
7
|
+
data.tar.gz: e08a7c45a8a8d70e19711333073e0632d30f0ebdf59c047e48ab26fef24c25952754405d13e5dc12e2bf0ab7abdc3a67f009a9bb4be28805fdee1a8272a7fb3e
|
@@ -102,49 +102,9 @@ module MSFL
|
|
102
102
|
#
|
103
103
|
# @param obj [Object] the Hash that is an implicit and
|
104
104
|
# @return [Hash] the resulting explicit hash
|
105
|
-
# @todo clean up the if elsif train
|
106
105
|
def implicit_and_to_explicit_recursively(obj, parent_key = nil)
|
107
106
|
if obj.is_a? Hash
|
108
|
-
|
109
|
-
if binary_operators.include?(first_key)
|
110
|
-
result = i_to_e_bin_op obj, parent_key
|
111
|
-
elsif logical_operators.include?(first_key)
|
112
|
-
result = i_to_e_log_op obj, parent_key
|
113
|
-
elsif first_key == :partial
|
114
|
-
result = { partial: { given: implicit_and_to_explicit_recursively(obj[:partial][:given]),
|
115
|
-
filter: implicit_and_to_explicit_recursively(obj[:partial][:filter]) } }
|
116
|
-
elsif first_key == :foreign
|
117
|
-
result = { foreign: { dataset: implicit_and_to_explicit_recursively(obj[:foreign][:dataset]),
|
118
|
-
filter: implicit_and_to_explicit_recursively(obj[:foreign][:filter]) } }
|
119
|
-
else
|
120
|
-
# the first key is not an operator
|
121
|
-
# if there is only one key just assign the result of calling this method recursively on the value to the result for the key
|
122
|
-
if obj.keys.count == 1
|
123
|
-
if obj[first_key].is_a?(Hash)
|
124
|
-
result = implicit_and_to_explicit_recursively obj[first_key], first_key
|
125
|
-
elsif obj[first_key].is_a? MSFL::Types::Set
|
126
|
-
# When an implicit and occurs under an MSFL::Types::Set when the key for the value which is the set
|
127
|
-
# is not a binary or logical operator. This doesn't happen in known current cases.
|
128
|
-
# ex. => { foo: [ { bar: { gte: 1, lte: 5 } } ] }
|
129
|
-
result = Hash.new
|
130
|
-
result[first_key] = recurse_through_set :implicit_and_to_explicit_recursively, obj[first_key]
|
131
|
-
elsif obj[first_key].is_a? Array
|
132
|
-
raise ArgumentError, "#implicit_and_to_explicit requires that it does not contain any Arrays - its argument should preprocessed by .arrays_to_sets and .convert_keys_to_symbols"
|
133
|
-
end
|
134
|
-
else
|
135
|
-
raise ArgumentError, "#implicit_and_to_explicit requires that all or none of a hash's keys be operators" if any_operators?(obj.keys)
|
136
|
-
# none of the keys are operators
|
137
|
-
and_array = []
|
138
|
-
obj.each do |k, v|
|
139
|
-
if v.is_a? Hash
|
140
|
-
and_array << implicit_and_to_explicit_recursively(v, k)
|
141
|
-
else
|
142
|
-
and_array << { k => v }
|
143
|
-
end
|
144
|
-
end
|
145
|
-
result = { and: MSFL::Types::Set.new(and_array) }
|
146
|
-
end
|
147
|
-
end
|
107
|
+
result = i_to_e_rec_hash obj, parent_key
|
148
108
|
elsif obj.is_a? MSFL::Types::Set
|
149
109
|
result = i_to_e_set obj, parent_key
|
150
110
|
elsif obj.is_a? Array
|
@@ -154,6 +114,67 @@ module MSFL
|
|
154
114
|
end
|
155
115
|
|
156
116
|
private
|
117
|
+
|
118
|
+
# Convert a Hash containing an implicit AND to an explicit AND
|
119
|
+
# This is a helper method used by #implicit_and_to_explicit_recursively
|
120
|
+
#
|
121
|
+
# @param hash [Hash] the Hash to be converted
|
122
|
+
# @param parent_key [Symbol] the parent key of the hash to be converted, or nil if one does not exist
|
123
|
+
# @return [Hash] the resulting hash with implicit ANDs converted to explicit ones
|
124
|
+
def i_to_e_rec_hash(hash, parent_key = nil)
|
125
|
+
first_key = hash.keys.first
|
126
|
+
if operator? first_key
|
127
|
+
result = i_to_e_op hash, parent_key
|
128
|
+
else
|
129
|
+
# the first key is not an operator
|
130
|
+
# if there is only one key just assign the result of calling this method recursively on the value to the result for the key
|
131
|
+
if hash.keys.count == 1
|
132
|
+
if hash[first_key].is_a?(Hash)
|
133
|
+
result = implicit_and_to_explicit_recursively hash[first_key], first_key
|
134
|
+
elsif hash[first_key].is_a? MSFL::Types::Set
|
135
|
+
# When an implicit and occurs under an MSFL::Types::Set when the key for the value which is the set
|
136
|
+
# is not a binary or logical operator. This doesn't happen in known current cases.
|
137
|
+
# ex. => { foo: [ { bar: { gte: 1, lte: 5 } } ] }
|
138
|
+
result = Hash.new
|
139
|
+
result[first_key] = recurse_through_set :implicit_and_to_explicit_recursively, hash[first_key]
|
140
|
+
elsif hash[first_key].is_a? Array
|
141
|
+
raise ArgumentError, "#implicit_and_to_explicit requires that it does not contain any Arrays - its argument should preprocessed by .arrays_to_sets and .convert_keys_to_symbols"
|
142
|
+
end
|
143
|
+
else
|
144
|
+
raise ArgumentError, "#implicit_and_to_explicit requires that all or none of a hash's keys be operators" if any_operators?(hash.keys)
|
145
|
+
# none of the keys are operators
|
146
|
+
and_array = []
|
147
|
+
hash.each do |k, v|
|
148
|
+
if v.is_a? Hash
|
149
|
+
and_array << implicit_and_to_explicit_recursively(v, k)
|
150
|
+
else
|
151
|
+
and_array << { k => v }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
result = { and: MSFL::Types::Set.new(and_array) }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
result
|
158
|
+
end
|
159
|
+
|
160
|
+
def i_to_e_op(hash, parent_key = nil)
|
161
|
+
op_key = hash.keys.first
|
162
|
+
|
163
|
+
if binary_operators.include?(op_key)
|
164
|
+
i_to_e_bin_op hash, parent_key
|
165
|
+
elsif logical_operators.include?(op_key)
|
166
|
+
i_to_e_log_op hash, parent_key
|
167
|
+
elsif op_key == :partial
|
168
|
+
{ partial: { given: implicit_and_to_explicit_recursively(hash[:partial][:given]),
|
169
|
+
filter: implicit_and_to_explicit_recursively(hash[:partial][:filter]) } }
|
170
|
+
elsif op_key == :foreign
|
171
|
+
{ foreign: { dataset: implicit_and_to_explicit_recursively(hash[:foreign][:dataset]),
|
172
|
+
filter: implicit_and_to_explicit_recursively(hash[:foreign][:filter]) } }
|
173
|
+
else
|
174
|
+
fail ArgumentError, "Unsupported hash in private method #i_to_e_rec_hash_op_key"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
157
178
|
# Recursively handle a hash containing keys that are all logical operators
|
158
179
|
def i_to_e_log_op(hash, parent_key = nil)
|
159
180
|
raise ArgumentError, "#implicit_and_to_explicit requires that all or none of a hash's keys be logical operators" unless all_logical_operators?(hash.keys)
|
@@ -0,0 +1,24 @@
|
|
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
|
+
# It differs from the other examples in that it overrides #operators
|
8
|
+
class Animal < ::MSFL::Datasets::Base
|
9
|
+
register_dataset
|
10
|
+
|
11
|
+
def foreigns
|
12
|
+
[:person]
|
13
|
+
end
|
14
|
+
|
15
|
+
def fields
|
16
|
+
[:name, :gender, :age, :type]
|
17
|
+
end
|
18
|
+
|
19
|
+
def operators
|
20
|
+
super.concat [:animal_specific_operator]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/msfl/datasets/base.rb
CHANGED
@@ -65,7 +65,6 @@ module MSFL
|
|
65
65
|
#
|
66
66
|
# @param field_name [Symbol] the name of the field to check and see if the dataset supports it
|
67
67
|
# @return [Bool] true if the field is supported by the dataset
|
68
|
-
# @todo write direct test of this
|
69
68
|
def has_field?(field_name)
|
70
69
|
direct_fields = self.fields
|
71
70
|
foreigns.each do |f|
|
@@ -105,7 +104,6 @@ module MSFL
|
|
105
104
|
#
|
106
105
|
# @param operator [Symbol] the operator to check if the dataset supports it
|
107
106
|
# @return [Bool] true if the dataset supports the operator
|
108
|
-
# @todo write test of this guy
|
109
107
|
def has_operator?(operator)
|
110
108
|
ops = operators
|
111
109
|
foreigns.each do |f|
|
data/lib/msfl/datasets/person.rb
CHANGED
@@ -53,6 +53,14 @@ module MSFL
|
|
53
53
|
[:and, :or]
|
54
54
|
end
|
55
55
|
|
56
|
+
# Returns true if the argument is a valid operator
|
57
|
+
#
|
58
|
+
# @param symbol [Symbol] the value to check to see if it is an operator
|
59
|
+
# @return [Bool] true if the argument is a valid operator, false otherwise
|
60
|
+
def operator?(symbol)
|
61
|
+
hash_key_operators.include? symbol
|
62
|
+
end
|
63
|
+
|
56
64
|
# Returns true if all elements of arr are operators, false otherwise
|
57
65
|
#
|
58
66
|
# @param arr [Array<Symbol>] the Array of Symbols to be checked against the operators list
|
data/msfl.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'msfl'
|
3
|
-
s.version = '1.2.
|
4
|
-
s.date = '2015-05-
|
3
|
+
s.version = '1.2.2'
|
4
|
+
s.date = '2015-05-27'
|
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"]
|
@@ -449,6 +449,20 @@ describe MSFL::Converters::Operator do
|
|
449
449
|
end
|
450
450
|
end
|
451
451
|
|
452
|
+
describe "#i_to_e_op" do
|
453
|
+
|
454
|
+
let(:hash) { { some_arbitrary_key: :foobar } }
|
455
|
+
|
456
|
+
subject { described_class.new.send(:i_to_e_op, hash) }
|
457
|
+
|
458
|
+
context "when the hash has a key that is not supported" do
|
459
|
+
|
460
|
+
it "raises an ArgumentError" do
|
461
|
+
expect { subject }.to raise_error ArgumentError
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
452
466
|
describe "#between_to_gte_lte_recursively" do
|
453
467
|
|
454
468
|
subject { test_instance.between_to_gte_lte_recursively arg }
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MSFL::Datasets::Animal do
|
4
|
+
|
5
|
+
it "has a foreign: person" do
|
6
|
+
expect(described_class.new.foreigns).to include :person
|
7
|
+
end
|
8
|
+
|
9
|
+
it "has the name field" do
|
10
|
+
expect(described_class.new.fields).to include :name
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has the gender field" do
|
14
|
+
expect(described_class.new.fields).to include :gender
|
15
|
+
end
|
16
|
+
|
17
|
+
it "has the age field" do
|
18
|
+
expect(described_class.new.fields).to include :age
|
19
|
+
end
|
20
|
+
|
21
|
+
it "has the type field" do
|
22
|
+
expect(described_class.new.fields).to include :type
|
23
|
+
end
|
24
|
+
end
|
@@ -26,6 +26,68 @@ describe "MSFL::Datasets::Base" do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
describe "#has_operator?" do
|
30
|
+
|
31
|
+
let(:test_instance) { MSFL::Datasets::Person.new }
|
32
|
+
|
33
|
+
let(:operator) { :eq }
|
34
|
+
|
35
|
+
subject { test_instance.has_operator? operator }
|
36
|
+
|
37
|
+
context "when the dataset has the specified operator" do
|
38
|
+
|
39
|
+
it { is_expected.to eq true }
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when the dataset does not have the specified operator" do
|
43
|
+
|
44
|
+
context "when the dataset has a foreign dataset that has the specified operator" do
|
45
|
+
|
46
|
+
let(:operator) { :animal_specific_operator }
|
47
|
+
|
48
|
+
it { is_expected.to eq true }
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when the dataset does not have a foreign dataset that has the specified operator" do
|
52
|
+
|
53
|
+
let(:operator) { :not_an_operator }
|
54
|
+
|
55
|
+
it { is_expected.to eq false }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#has_field?" do
|
61
|
+
|
62
|
+
let(:test_instance) { MSFL::Datasets::Car.new }
|
63
|
+
|
64
|
+
let(:field_name) { :make }
|
65
|
+
|
66
|
+
subject { test_instance.has_field? field_name }
|
67
|
+
|
68
|
+
context "when the dataset has the specified field" do
|
69
|
+
|
70
|
+
it { is_expected.to eq true }
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when the dataset has a foreign data set that has the specified field" do
|
74
|
+
|
75
|
+
let(:field_name) { :gender }
|
76
|
+
|
77
|
+
it { is_expected.to eq true }
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when the dataset does not have the specified field" do
|
81
|
+
|
82
|
+
context "when none of the dataset's foreign datasets have the specified field" do
|
83
|
+
|
84
|
+
let(:field_name) { :not_a_field }
|
85
|
+
|
86
|
+
it { is_expected.to eq false }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
29
91
|
describe "#type_conforms?" do
|
30
92
|
|
31
93
|
context "when MSFL is configured to use Movie as the dataset" do
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MSFL::Validators::Definitions::HashKey do
|
4
|
+
|
5
|
+
module TestClassNamespace
|
6
|
+
class IncludesHashKey
|
7
|
+
include MSFL::Validators::Definitions::HashKey
|
8
|
+
attr_accessor :dataset
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "when a class includes #{described_class}" do
|
13
|
+
|
14
|
+
let(:test_instance) { TestClassNamespace::IncludesHashKey.new }
|
15
|
+
|
16
|
+
describe "#valid_hash_keys" do
|
17
|
+
|
18
|
+
before { test_instance.dataset = dataset }
|
19
|
+
|
20
|
+
let(:dataset) do
|
21
|
+
d = double('Dataset')
|
22
|
+
allow(d).to receive(:fields).and_return [:foo, :bar]
|
23
|
+
d
|
24
|
+
end
|
25
|
+
|
26
|
+
subject { test_instance.valid_hash_keys }
|
27
|
+
|
28
|
+
it "includes all of the valid operators" do
|
29
|
+
expect(subject).to include *(test_instance.hash_key_operators)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "includes all of the valid fields" do
|
33
|
+
expect(subject).to include *(test_instance.dataset.fields)
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,4 +5,6 @@ require 'byebug'
|
|
5
5
|
require_relative '../lib/msfl'
|
6
6
|
require_relative 'msfl/shared_examples'
|
7
7
|
require_relative '../lib/msfl/datasets/movie'
|
8
|
-
require_relative '../lib/msfl/datasets/car'
|
8
|
+
require_relative '../lib/msfl/datasets/car'
|
9
|
+
require_relative '../lib/msfl/datasets/animal'
|
10
|
+
require_relative '../lib/msfl/datasets/person'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: msfl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.2
|
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-05-
|
11
|
+
date: 2015-05-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -113,6 +113,7 @@ files:
|
|
113
113
|
- lib/msfl/converters.rb
|
114
114
|
- lib/msfl/converters/operator.rb
|
115
115
|
- lib/msfl/datasets.rb
|
116
|
+
- lib/msfl/datasets/animal.rb
|
116
117
|
- lib/msfl/datasets/base.rb
|
117
118
|
- lib/msfl/datasets/car.rb
|
118
119
|
- lib/msfl/datasets/movie.rb
|
@@ -129,11 +130,13 @@ files:
|
|
129
130
|
- msfl.gemspec
|
130
131
|
- simplecov_custom_profiles.rb
|
131
132
|
- spec/msfl/converters/operator_spec.rb
|
133
|
+
- spec/msfl/datasets/animal_spec.rb
|
132
134
|
- spec/msfl/datasets/base_spec.rb
|
133
135
|
- spec/msfl/parsers/json_spec.rb
|
134
136
|
- spec/msfl/shared_examples.rb
|
135
137
|
- spec/msfl/sinatra/helpers_spec.rb
|
136
138
|
- spec/msfl/sinatra_spec.rb
|
139
|
+
- spec/msfl/validators/definitions/hash_key_spec.rb
|
137
140
|
- spec/msfl/validators/semantic_spec.rb
|
138
141
|
- spec/msfl_spec.rb
|
139
142
|
- spec/spec_helper.rb
|
@@ -163,11 +166,13 @@ specification_version: 4
|
|
163
166
|
summary: MSFL in Ruby
|
164
167
|
test_files:
|
165
168
|
- spec/msfl/converters/operator_spec.rb
|
169
|
+
- spec/msfl/datasets/animal_spec.rb
|
166
170
|
- spec/msfl/datasets/base_spec.rb
|
167
171
|
- spec/msfl/parsers/json_spec.rb
|
168
172
|
- spec/msfl/shared_examples.rb
|
169
173
|
- spec/msfl/sinatra/helpers_spec.rb
|
170
174
|
- spec/msfl/sinatra_spec.rb
|
175
|
+
- spec/msfl/validators/definitions/hash_key_spec.rb
|
171
176
|
- spec/msfl/validators/semantic_spec.rb
|
172
177
|
- spec/msfl_spec.rb
|
173
178
|
- spec/spec_helper.rb
|