msfl 1.2.1 → 1.2.2
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/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
|