msfl 1.1.1 → 1.1.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 +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 }
|