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 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