msfl 1.1.1 → 1.1.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 +72 -14
- data/msfl.gemspec +1 -1
- data/spec/msfl/converters/operator_spec.rb +202 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e60cd25d855a736231fddc1f1d893df5a1b45296
|
4
|
+
data.tar.gz: cd75be374c724735b63103dcd8c9f5d2b021d480
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 888ff6048f6f675f75f1f518ef928ce66d1d0fe40f651d6597738050b0845064392533d0f0c28bfebf7cc2ce1495df0d27f3014414bbbe1e133063d42202052d
|
7
|
+
data.tar.gz: 6c0e6b5f896bb6061abcfdd9e572cc0c90d804bee82127d09f6ace0b7278403288c48fa63ca0374408cbefcbc9dd31639ded8297df9a68f51d75bb4995449471
|
@@ -3,6 +3,58 @@ module MSFL
|
|
3
3
|
class Operator
|
4
4
|
include MSFL::Validators::Definitions::HashKey
|
5
5
|
|
6
|
+
# Order is respected by run_conversions
|
7
|
+
# in otherwords run_conversions executes conversions in the order they occur
|
8
|
+
# in CONVERSIONS, not in the order in which they are passed into the method
|
9
|
+
#
|
10
|
+
# conversion order is context-free
|
11
|
+
CONVERSIONS = [
|
12
|
+
:implicit_between_to_explicit_recursively,
|
13
|
+
:between_to_gte_lte_recursively,
|
14
|
+
:implicit_and_to_explict_recursively
|
15
|
+
]
|
16
|
+
|
17
|
+
# Runs conversions on an object
|
18
|
+
# It respects the order of CONVERSIONS, not the order of elements in conversions_to_run
|
19
|
+
#
|
20
|
+
# @param obj [Object] the object to run the conversions on
|
21
|
+
# @param conversions_to_run [Array<Symbol>] an array of the conversions that should be run, duplicates are ignored
|
22
|
+
# @return [Object] the object with the conversions applied
|
23
|
+
def run_conversions(obj, conversions_to_run = nil)
|
24
|
+
conversions_to_run ||= CONVERSIONS
|
25
|
+
unless all_conversions?(conversions_to_run)
|
26
|
+
raise ArgumentError, "#run_conversions second argument is optional, if specified it must be an Array of Symbols"
|
27
|
+
end
|
28
|
+
|
29
|
+
CONVERSIONS.each do |conv|
|
30
|
+
# In the order that items are in CONVERSIONS run all of the conversions_to_run
|
31
|
+
obj = send(conv, obj) if conversions_to_run.include?(conv)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
# { year: { start: 2001, end: 2005 } }
|
38
|
+
# => { year: { between: { start: 2001, end: 2015 } } }
|
39
|
+
def implicit_between_to_explicit_recursively(obj)
|
40
|
+
if obj.is_a? Hash
|
41
|
+
# if the hash has two keys :start and :end, nest it inside a between and recurse on the values
|
42
|
+
if obj.has_key?(:start) && obj.has_key?(:end)
|
43
|
+
result = { between: { start: implicit_between_to_explicit_recursively(obj[:start]), end: implicit_between_to_explicit_recursively(obj[:end]) } }
|
44
|
+
else
|
45
|
+
result = Hash.new
|
46
|
+
obj.each do |k, v|
|
47
|
+
result[k] = implicit_between_to_explicit_recursively(v)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
elsif obj.is_a? Types::Set
|
51
|
+
result = recurse_through_set :implicit_between_to_explicit_recursively, obj
|
52
|
+
elsif obj.is_a? Array
|
53
|
+
raise ArgumentError, "#implicit_between_to_explicit_recursively requires that it does not contain any Arrays - its argument should preprocessed by .arrays_to_sets and .convert_keys_to_symbols"
|
54
|
+
end
|
55
|
+
result ||= obj
|
56
|
+
end
|
57
|
+
|
6
58
|
# Recursively converts all between operators to equivalent anded gte / lte
|
7
59
|
# it currently creates the converted operators into the implied AND format
|
8
60
|
#
|
@@ -21,10 +73,7 @@ module MSFL
|
|
21
73
|
end
|
22
74
|
end
|
23
75
|
elsif obj.is_a? Types::Set
|
24
|
-
result =
|
25
|
-
obj.each do |v|
|
26
|
-
result << between_to_gte_lte_recursively(v)
|
27
|
-
end
|
76
|
+
result = recurse_through_set :between_to_gte_lte_recursively, obj
|
28
77
|
elsif obj.is_a? Array
|
29
78
|
raise ArgumentError, "#between_to_gte_lte requires that it does not contain any Arrays - its argument should preprocessed by .arrays_to_sets and .convert_keys_to_symbols"
|
30
79
|
end
|
@@ -62,13 +111,8 @@ module MSFL
|
|
62
111
|
elsif obj[first_key].is_a? MSFL::Types::Set
|
63
112
|
# This situation occurs when there are nested logical operators
|
64
113
|
# obj is a hash, with one key that is not a binary operator which has a value that is a MSFL::Types::Set
|
65
|
-
result =
|
66
|
-
|
67
|
-
obj[first_key].each do |v|
|
68
|
-
# byebug
|
69
|
-
and_array << implicit_and_to_explicit_recursively(v)
|
70
|
-
end
|
71
|
-
result[first_key] = and_array
|
114
|
+
result = Hash.new
|
115
|
+
result[first_key] = recurse_through_set :implicit_and_to_explicit_recursively, obj[first_key]
|
72
116
|
elsif obj[first_key].is_a? Array
|
73
117
|
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"
|
74
118
|
end
|
@@ -110,7 +154,6 @@ module MSFL
|
|
110
154
|
# the first key an operator
|
111
155
|
raise ArgumentError, "#implicit_and_to_explicit requires that all or none of a hash's keys be operators" unless all_operators?(hash.keys)
|
112
156
|
# all keys are operators
|
113
|
-
|
114
157
|
first_key = hash.keys.first
|
115
158
|
if hash.keys.count == 1
|
116
159
|
# There's only one key so there cannot be an implied AND at this level
|
@@ -122,7 +165,6 @@ module MSFL
|
|
122
165
|
else
|
123
166
|
{ first_key => implicit_and_to_explicit_recursively(hash[first_key]) }
|
124
167
|
end
|
125
|
-
|
126
168
|
else
|
127
169
|
raise ArgumentError, "#implicit_and_to_explicit requires that parent_key be specified when converting operators" if parent_key.nil?
|
128
170
|
# parent key is non nil
|
@@ -135,12 +177,28 @@ module MSFL
|
|
135
177
|
end
|
136
178
|
|
137
179
|
def i_to_e_set(set, parent_key = nil)
|
180
|
+
recurse_through_set :implicit_and_to_explicit_recursively, set
|
181
|
+
end
|
182
|
+
|
183
|
+
def recurse_through_set(method, set)
|
138
184
|
result = MSFL::Types::Set.new
|
139
185
|
set.each do |v|
|
140
|
-
result <<
|
186
|
+
result << send(method, v)
|
141
187
|
end
|
142
188
|
result
|
143
189
|
end
|
190
|
+
|
191
|
+
# Returns true if the argument is an Array of Symbols, otherwise false
|
192
|
+
#
|
193
|
+
# @param obj [Obj] the object to check
|
194
|
+
# @return [Bool] true if the argument is an Array of Symbols
|
195
|
+
def all_conversions?(obj)
|
196
|
+
return false unless obj.is_a?(Array)
|
197
|
+
obj.each do |v|
|
198
|
+
return false unless v.is_a?(Symbol)
|
199
|
+
end
|
200
|
+
true
|
201
|
+
end
|
144
202
|
end
|
145
203
|
end
|
146
204
|
end
|
data/msfl.gemspec
CHANGED
@@ -2,6 +2,208 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "MSFL::Converters::Operator" do
|
4
4
|
|
5
|
+
describe "#run_conversions" do
|
6
|
+
|
7
|
+
subject { test_instance.run_conversions obj, conversions_to_run }
|
8
|
+
|
9
|
+
let(:klass) do
|
10
|
+
class OpTestConverter < MSFL::Converters::Operator
|
11
|
+
end
|
12
|
+
OpTestConverter
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:test_instance) { klass.new }
|
16
|
+
|
17
|
+
let(:obj) { Object.new }
|
18
|
+
|
19
|
+
let(:conversions_to_run) { nil }
|
20
|
+
|
21
|
+
context "when the conversions_to_run argument is nil" do
|
22
|
+
|
23
|
+
let(:test_instance) do
|
24
|
+
t_i = klass.new
|
25
|
+
|
26
|
+
allow(t_i).to receive(:implicit_between_to_explicit_recursively)
|
27
|
+
expect(t_i).to receive(:implicit_between_to_explicit_recursively).once
|
28
|
+
|
29
|
+
allow(t_i).to receive(:between_to_gte_lte_recursively)
|
30
|
+
expect(t_i).to receive(:between_to_gte_lte_recursively).once
|
31
|
+
|
32
|
+
allow(t_i).to receive(:implicit_and_to_explict_recursively)
|
33
|
+
expect(t_i).to receive(:implicit_and_to_explict_recursively).once
|
34
|
+
t_i
|
35
|
+
end
|
36
|
+
|
37
|
+
it "runs all conversions in CONVERSIONS" do
|
38
|
+
subject
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when conversions_to_run is an array of symbols" do
|
43
|
+
|
44
|
+
let(:conversions_to_run) { [:implicit_and_to_explict_recursively, :between_to_gte_lte_recursively]}
|
45
|
+
|
46
|
+
it "runs all elements in CONVERSIONS that are in conversions_to_run" do
|
47
|
+
allow(test_instance).to receive :implicit_and_to_explict_recursively
|
48
|
+
expect(test_instance).to receive(:implicit_and_to_explict_recursively)
|
49
|
+
|
50
|
+
allow(test_instance).to receive :between_to_gte_lte_recursively
|
51
|
+
expect(test_instance).to receive(:between_to_gte_lte_recursively)
|
52
|
+
|
53
|
+
allow(test_instance).to receive :implicit_between_to_explicit_recursively
|
54
|
+
expect(test_instance).to receive(:implicit_between_to_explicit_recursively).never
|
55
|
+
|
56
|
+
subject
|
57
|
+
end
|
58
|
+
|
59
|
+
it "runs the indicated conversions exactly once" do
|
60
|
+
|
61
|
+
allow(test_instance).to receive :implicit_and_to_explict_recursively
|
62
|
+
expect(test_instance).to receive(:implicit_and_to_explict_recursively).once
|
63
|
+
|
64
|
+
allow(test_instance).to receive :between_to_gte_lte_recursively
|
65
|
+
expect(test_instance).to receive(:between_to_gte_lte_recursively).once
|
66
|
+
|
67
|
+
subject
|
68
|
+
end
|
69
|
+
|
70
|
+
it "runs the indicated conversions in the order they appear in CONVERSIONS" do
|
71
|
+
|
72
|
+
allow(test_instance).to receive :between_to_gte_lte_recursively
|
73
|
+
expect(test_instance).to receive(:between_to_gte_lte_recursively).ordered
|
74
|
+
|
75
|
+
allow(test_instance).to receive :implicit_and_to_explict_recursively
|
76
|
+
expect(test_instance).to receive(:implicit_and_to_explict_recursively).ordered
|
77
|
+
|
78
|
+
subject
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
context "when conversions_to_run is not nil and not an array of symbols" do
|
84
|
+
|
85
|
+
let(:conversions_to_run) { "foo" }
|
86
|
+
|
87
|
+
it "raises an ArgumentError" do
|
88
|
+
expect { subject }.to raise_error ArgumentError
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#implicit_between_to_explicit_recursively" do
|
94
|
+
|
95
|
+
subject { test_instance.implicit_between_to_explicit_recursively arg }
|
96
|
+
|
97
|
+
let(:test_instance) { MSFL::Converters::Operator.new }
|
98
|
+
|
99
|
+
let(:arg) { raise ArgumentError, "You are expected to define the arg variable" }
|
100
|
+
|
101
|
+
let(:expected) { raise ArgumentError, "You are expected to define the expected value" }
|
102
|
+
|
103
|
+
context "when there is not an implicit BETWEEN" do
|
104
|
+
|
105
|
+
["foo", { foo: "bar" }, 123, 56.12, :aaaah].each do |arg|
|
106
|
+
|
107
|
+
context "when the argument is #{arg}" do
|
108
|
+
|
109
|
+
let(:arg) { arg }
|
110
|
+
|
111
|
+
it "is the argument unchanged" do
|
112
|
+
expect(subject).to eq arg
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when there is an implicit BETWEEN" do
|
119
|
+
|
120
|
+
context "when the implicit BETWEEN is at the highest possible level of the filter" do
|
121
|
+
|
122
|
+
let(:arg) { { year: { start: 2001, end: 2005 } } }
|
123
|
+
|
124
|
+
let(:expected) { { year: { between: { start: 2001, end: 2005 } } } }
|
125
|
+
|
126
|
+
it "is an explicit BETWEEN" do
|
127
|
+
expect(subject).to eq expected
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "when the implicit BETWEEN is inside of a MSFL::Types::Set" do
|
132
|
+
|
133
|
+
let(:arg) do
|
134
|
+
{ and:
|
135
|
+
MSFL::Types::Set.new([
|
136
|
+
{ make: "Honda"},
|
137
|
+
{ year: { start: 2001, end: 2005 } }
|
138
|
+
])
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
let(:expected) do
|
143
|
+
{ and: MSFL::Types::Set.new([
|
144
|
+
{ make: "Honda" },
|
145
|
+
{ year: { between: { start: 2001, end: 2005 } } }
|
146
|
+
])}
|
147
|
+
end
|
148
|
+
|
149
|
+
it "recursively converts the implicit BETWEEN to an explicit BETWEEN" do
|
150
|
+
expect(subject).to eq expected
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# This can't actually happen in the current MSFL syntax (at least I don't think it can)
|
155
|
+
context "when the implicit BETWEEN is inside of another Hash" do
|
156
|
+
|
157
|
+
let(:arg) do
|
158
|
+
{ foo: { bar: { start: "2015-01-01", end: "2015-03-01" } } }
|
159
|
+
end
|
160
|
+
|
161
|
+
let(:expected) do
|
162
|
+
{ foo: { bar: { between: { start: "2015-01-01", end: "2015-03-01" } } } }
|
163
|
+
end
|
164
|
+
|
165
|
+
it "recursively converts the implicit BETWEEN to an explicit BETWEEN" do
|
166
|
+
expect(subject).to eq expected
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "when there are multiple implicit BETWEENs that are deeply nested" do
|
171
|
+
|
172
|
+
let(:deep_nest) do
|
173
|
+
{
|
174
|
+
cat: 1221,
|
175
|
+
dog: "fur",
|
176
|
+
lol: MSFL::Types::Set.new([ { hat: { start: 1, end: 5 } } ]),
|
177
|
+
:"1337" => 1337.1337,
|
178
|
+
noob: MSFL::Types::Set.new([
|
179
|
+
MSFL::Types::Set.new([123]),
|
180
|
+
{ :"123" => 456, onetwo: { start: 3, end: 4 } } ]) }
|
181
|
+
end
|
182
|
+
|
183
|
+
let(:arg) { deep_nest }
|
184
|
+
|
185
|
+
let(:expected) do
|
186
|
+
{
|
187
|
+
cat: 1221,
|
188
|
+
dog: "fur",
|
189
|
+
lol: MSFL::Types::Set.new([
|
190
|
+
{ hat: { between: { start: 1, end: 5 } } }
|
191
|
+
]),
|
192
|
+
:"1337" => 1337.1337,
|
193
|
+
noob: MSFL::Types::Set.new([
|
194
|
+
MSFL::Types::Set.new([123]),
|
195
|
+
{ :"123" => 456, onetwo: { between: { start: 3, end: 4 } } }
|
196
|
+
])
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
it "recursively converts the implicit BETWEENs to an explicit BETWEENs" do
|
201
|
+
expect(subject).to eq expected
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
5
207
|
describe "#implicit_and_to_explicit" do
|
6
208
|
|
7
209
|
subject(:mut) { test_instance.implicit_and_to_explicit_recursively arg }
|