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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e429121644a1d3c1f05a4044f0e1de31857630f4
4
- data.tar.gz: a232210e6ebbc43905c015d4c141c81352581e4b
3
+ metadata.gz: 0ce057720e95af53947418f9609ab57abcd2051c
4
+ data.tar.gz: 1786d887042de9117b98de5396dcb8e5087693e3
5
5
  SHA512:
6
- metadata.gz: 97f84ad55020f79b9603c443cb577ccac5231e4078421edfa971fb78d721f354336b6b5e092cb15ada810144c844b98f44e3a7c572d1d4ce6290edb6acda1a15
7
- data.tar.gz: 3cafe256b7d10063eb768916e08b916cfac37a3d1b112ed805479875208f8a1063f032015561e8cb7b27d7e49e637a7ad2fe890093931bf7375d953671597342
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
- first_key = obj.keys.first
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
@@ -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|
@@ -8,7 +8,7 @@ module MSFL
8
8
  register_dataset
9
9
 
10
10
  def foreigns
11
- [:car]
11
+ [:car, :animal]
12
12
  end
13
13
 
14
14
  def fields
@@ -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
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'msfl'
3
- s.version = '1.2.1'
4
- s.date = '2015-05-20'
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
@@ -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.1
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-20 00:00:00.000000000 Z
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