msfl 0.0.1.pre.dev → 0.0.1.pre.qa

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: 07d5ab033f428edf6c94db6c4deabbca727102ad
4
- data.tar.gz: 0c0583e4e98b61785e771c376171ffd929469ec9
3
+ metadata.gz: 5192c432f2b0fa92fdc52f791a6d1db94e0e7273
4
+ data.tar.gz: 369cf6c5d51a3b2f996b070a70894a6fd952b7f4
5
5
  SHA512:
6
- metadata.gz: d58df00da55126dabffdbeca04a083fdd975f8d9d9f7e7d844ca4351149efbd3cd04ada377b9c39fe635a5b90457270dbaaf445aed99ce9a2ae4a116837f6c17
7
- data.tar.gz: 889702566901c32969753a490179d194cbbc1ea35940a84e7aa4318bae8dd69dbc8f10de6a96a6b0687d5cf0290f7de2fb4591414ad849253668a42aff851928
6
+ metadata.gz: dadc0e105e90e00c982424d332a2cada2733a676cf9c26f0d65ab3c5a79e4ffe94c78b87903c493aa070ae0c80f813eb1bf6313cd81531593911fde0f0c3b1bf
7
+ data.tar.gz: 86c9aac383c3a267e9344ca3f081b4b1e9fd8c461ca5e86a5962e3811476dad04ac918c521cc9dc0011811e5029caca9ec14858bd9b83ad313a8c0a1c45d6561
@@ -1,5 +1,30 @@
1
- class MSFL
2
- def self.hi
3
- puts "Hi from the MSFL team."
1
+ require 'msfl/parsers'
2
+ require 'msfl/types'
3
+ require 'msfl/validators'
4
+ require 'msfl/configuration'
5
+ require 'msfl/datasets'
6
+
7
+ module MSFL
8
+ class << self
9
+ # Allows the user to set configuration options
10
+ # by yielding the configuration block
11
+ #
12
+ # @param opts [Hash] an optional hash of options, supported options are `reset: true`
13
+ # @param block [Block] an optional configuration block
14
+ # @return [Configuration] the current configuration object
15
+ def configure(opts = {}, &block)
16
+ if opts.has_key?(:reset) && opts[:reset]
17
+ @configuration = nil
18
+ end
19
+ yield(configuration) if block_given?
20
+ configuration
21
+ end
22
+
23
+ # Returns the singleton class's configuration object
24
+ #
25
+ # @return [Configuration] the current configuration object
26
+ def configuration
27
+ @configuration ||= Configuration.new
28
+ end
4
29
  end
5
30
  end
@@ -0,0 +1,9 @@
1
+ module MSFL
2
+ class Configuration
3
+ attr_accessor :datasets
4
+
5
+ def initialize
6
+ @datasets = []
7
+ end
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ require_relative 'datasets/base'
@@ -0,0 +1,79 @@
1
+ module MSFL
2
+ module Datasets
3
+ class Base
4
+
5
+ include Validators::Definitions::HashKey
6
+
7
+ # The descendant class MUST override this method otherwise all field validations will fail
8
+ #
9
+ # @return [Array] the fields in the dataset
10
+ def fields
11
+ raise NoMethodError, "Descendants of MSFL::Datasets::Base are required to implement the #fields method"
12
+ end
13
+
14
+ # The descendant class may override this method to control the operators that are supported for the dataset
15
+ # - Note that this can only be used to reduce the number of supported operators (you can't add new operators
16
+ # here, without first adding them to MSFL::Validators::Definitions::HashKey#hash_key_operators)
17
+ def operators
18
+ hash_key_operators
19
+ end
20
+
21
+ # Method not implemented at this time
22
+ #
23
+ # @param obj [Object] the object that should be type checked based on the field argument
24
+ # @param field [Symbol] which field should the object be checked for conformity
25
+ # @param errors [Array] an array of validation errors - empty indicates that no errors have been encountered
26
+ # @return [Array] errors merged with any validation errors encountered in validating the set
27
+ #
28
+ # @example:
29
+ # foo.investors_validate_type_conforms("abc", :total_funding) => # there is one more error in errors
30
+ # # because the type of total_funding must be an integer
31
+ #
32
+ def validate_type_conforms(obj, field, errors)
33
+ errors
34
+ end
35
+
36
+ # Method not implemented at this time
37
+ # Returns true if the object conforms to the types supported by the indicated field
38
+ #
39
+ # @param obj [Object] the object that should be type checked based on the field argument
40
+ # @param field [Symbol] which field should the object be checked for conformity
41
+ def type_conforms?(obj, field)
42
+ true
43
+ end
44
+
45
+ # Method not implemented at this time
46
+ #
47
+ #
48
+ # @param operator [Symbol] the operator that we want to know if the particular field supports it
49
+ # @param field [Symbol] which field should the operator be checked for conformity
50
+ # @param errors [Array] an array of validation errors - empty indicates that no errors have been encountered
51
+ # @return [Array] errors merged with any validation errors encountered in validating the set
52
+ def validate_operator_conforms(operator, field, errors)
53
+ errors
54
+ end
55
+
56
+ # Method not implemented at this time
57
+ #
58
+ #
59
+ # @param value [Object] the precoerced value (the value must be correctly typed)
60
+ # that should be checked for validity based on the field, if the value does not conform an
61
+ # exception will be raised
62
+ # @param field [Symbol] which field should the value be checked for conformity
63
+ # @param errors [Array] an array of validation errors - empty indicates that no errors have been encountered
64
+ # @return [Array] errors merged with any validation errors encountered in validating the set
65
+ #
66
+ #
67
+ # @example:
68
+ # foo.investors_value_conforms(-6000, :total_funding) => # there is one more error in errors
69
+ # # because the funding cannot be negative
70
+ #
71
+ # @example:
72
+ # foo.investors_value_conforms(12345, :total_funding) => # errors is unchanged
73
+ #
74
+ def validate_value_conforms(value, field, errors)
75
+ errors
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,13 @@
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
+ class Movies < ::MSFL::Datasets::Base
8
+ def fields
9
+ [:title, :rating, :description, :earnings]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1 @@
1
+ require_relative 'parsers/json'
@@ -0,0 +1,26 @@
1
+ require 'json'
2
+ module MSFL
3
+ module Parsers
4
+ class JSON
5
+ def self.parse(json)
6
+ obj = ::JSON.parse(json)
7
+ obj = arrays_to_sets obj
8
+ obj
9
+ end
10
+
11
+ def self.arrays_to_sets(obj)
12
+ obj = Types::Set.new obj if obj.is_a?(::Array)
13
+ if obj.respond_to? :each
14
+ if obj.is_a?(::Hash)
15
+ result = {}
16
+ obj.each { |key, val| result[key.to_sym] = arrays_to_sets val }
17
+ elsif obj.is_a?(Types::Set)
18
+ result = Types::Set.new obj.map { |value| arrays_to_sets value }
19
+ end
20
+ end
21
+ result ||= obj
22
+ result
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ require_relative 'types/set'
@@ -0,0 +1,7 @@
1
+ module MSFL
2
+ module Types
3
+ class Set < ::Set
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ require_relative 'validators/semantic'
@@ -0,0 +1 @@
1
+ require_relative 'definitions/hash_key'
@@ -0,0 +1,36 @@
1
+ module MSFL
2
+ module Validators
3
+ module Definitions
4
+ module HashKey
5
+
6
+ def valid_hash_key?(key)
7
+ valid_hash_keys.include? key
8
+ end
9
+
10
+ def valid_hash_keys
11
+ hash_key_operators.concat self.dataset.fields
12
+ end
13
+
14
+ # Operators still needing parsing: ellipsis2, tilda
15
+ def hash_key_operators
16
+ [
17
+ :and, # logical AND
18
+ :or, # logical OR
19
+ :in, # IN
20
+ :between, # inclusive range for integers, dates, and date times
21
+ :start, # a range bound inclusively to the left
22
+ :end, # a range bound inclusively to the right
23
+ :ellipsis2, # alias to :between
24
+ :tilda, # alias to :between, :start, and :end depending on usage
25
+ :eq, # ==
26
+ :lt, # <
27
+ :lte, # <=
28
+ :gt, # >
29
+ :gte, # >=
30
+ :neg, # logical negation
31
+ ]
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,155 @@
1
+ require_relative 'definitions'
2
+
3
+ module MSFL
4
+ module Validators
5
+ class Semantic
6
+ # Load definitions
7
+ include Definitions::HashKey
8
+
9
+ attr_accessor :dataset, :errors, :current_field, :current_operator
10
+
11
+ BOOLEAN_OPERATORS = [:and, :or]
12
+ ENUMERATION_OPERATORS = [:in]
13
+
14
+ def initialize(attributes = nil, opts = {})
15
+ @dataset = MSFL.configuration.datasets.first.new unless MSFL.configuration.datasets.empty?
16
+ @dataset ||= Datasets::Base.new
17
+ @current_field = nil
18
+ @current_operator = nil
19
+ end
20
+
21
+ # Returns true if the object is valid, false if the object is invalid
22
+ # An array of hashes of errors is available at #errors
23
+ #
24
+ # This method is not meant to be called recursively, the private method recursive_validate is used
25
+ # for this purpose
26
+ #
27
+ # @param hash [Hash] the object to be validated
28
+ # @param errors [Array] optionally provide an array that contains errors from previous validators in the
29
+ # validation chain
30
+ def validate(hash, errors = [], opts = {})
31
+ errors << "Object to validate must be a Hash" unless hash.is_a?(Hash)
32
+ recursive_validate hash, errors, opts
33
+ @errors = errors
34
+ result = true if @errors.empty?
35
+ result ||= false
36
+ result
37
+ end
38
+
39
+ # Returns the result of merging errors with any newly encountered errors found in validating the hash
40
+ #
41
+ # @param hash [Hash] the Hash to validate
42
+ # @param errors [Array] an array of validation errors - empty indicates that no errors have been encountered
43
+ # @param opts [Hash] the options hash
44
+ # @return [Array] errors merged with any validation errors encountered in validating the hash
45
+ def validate_hash(hash, errors, opts)
46
+ # set current field
47
+ current_field = nil
48
+ # validate the keys and values
49
+ hash.each do |k, value|
50
+ key = k.to_sym
51
+ # validate the current hash key using broad hash key validation
52
+ errors << "Hash key encountered that is broadly invalid." unless valid_hash_key?(key)
53
+
54
+ # validate that the hash key is supported as an operator or dataset field
55
+
56
+ # if they key is an operator validate the dataset supports the operator for the current field
57
+ #
58
+ # if the key is a field of the dataset then we need to validate that the _value_ conforms to the dataset
59
+ # specific validation rules for that field
60
+ #
61
+ # if they key is neither an operator nor a field we raise an ArgumentError
62
+ #
63
+ # Then make a recursive call to validate on the value so that it and its elements are validated
64
+ # later I might be able to optimize this by only making the recursive call for Sets and Hashes
65
+ #
66
+ if dataset.operators.include? key
67
+ dataset.validate_operator_conforms key, current_field, errors
68
+ opts[:parent_operator] = key
69
+ elsif dataset.fields.include? key
70
+ current_field = key
71
+ dataset.validate_type_conforms value, current_field, errors
72
+ dataset.validate_value_conforms value, current_field, errors
73
+ opts[:parent_field] = current_field
74
+ else
75
+ errors << "Encountered hash key that is neither an operator nor a property of the dataset"
76
+ end
77
+ recursive_validate value, errors, opts
78
+ end
79
+ errors
80
+ end
81
+
82
+ # Acts as a helper method that forwards validation requests for sets to the right handler based on the
83
+ # :parent_operator option's value
84
+ #
85
+ # @param set [MSFL::Types::Set] the set to validate
86
+ # @param errors [Array] the existing array of validation errors
87
+ # @param opts [Hash] the options hash
88
+ # @return [Array] the errors array argument with any additional validation errors appended
89
+ def validate_set(set, errors, opts)
90
+ error_message =
91
+ "Validate set requires the :parent_operator option be set and represented in either the BOOLEAN_OPERATORS
92
+ or ENUMERATION_OPERATORS constant"
93
+ errors << error_message unless opts.has_key?(:parent_operator)
94
+
95
+ if BOOLEAN_OPERATORS.include?(opts[:parent_operator])
96
+ validate_boolean_set set, errors, opts
97
+ elsif ENUMERATION_OPERATORS.include?(opts[:parent_operator])
98
+ validate_enumeration_set set, errors, opts
99
+ else
100
+ errors << error_message
101
+ end
102
+ errors
103
+ end
104
+
105
+ # Returns the result of merging errors with any newly encountered errors found in validating the set
106
+ #
107
+ # @param set [MSFL::Types::Set] the set to validate
108
+ # @param errors [Array] an array of validation errors - empty indicates that no errors have been encountered
109
+ # @return [Array] errors merged with any validation errors encountered in validating the set
110
+ def validate_boolean_set(set, errors, opts)
111
+ # Every member needs to be a hash
112
+ set.each do |value|
113
+ errors << "Every member of a boolean set must be a Hash" unless value.is_a?(Hash)
114
+ # recursively call validate on each member
115
+ recursive_validate value, errors, opts
116
+ end
117
+ errors
118
+ end
119
+
120
+ # Validates a set of enumerationd scalar values
121
+ def validate_enumeration_set(set, errors, opts)
122
+ current_field = opts[:parent_field] if opts.has_key?(:parent_field)
123
+ current_field ||= nil
124
+ errors << "Validate enumeration set requires the parent_field option to be set" if current_field.nil?
125
+ set.each do |value|
126
+ # this isn't quite right, it feels dirty to use :each
127
+ errors << "No members of an enumeration set may permit iteration across itself" if value.respond_to?(:each)
128
+ dataset.validate_type_conforms value, current_field, errors
129
+ dataset.validate_value_conforms value, current_field, errors
130
+ end
131
+ errors
132
+ end
133
+
134
+ private
135
+
136
+ def recursive_validate(obj, errors, opts)
137
+ # store a copy of parent_operator and parent_field to restore after recursion
138
+ parent_operator = opts[:parent_operator] if opts.has_key?(:parent_operator)
139
+ parent_operator ||= nil
140
+ parent_field = opts[:parent_field] if opts.has_key?(:parent_field)
141
+ parent_field ||= nil
142
+ if obj.is_a?(Hash)
143
+ validate_hash obj, errors, opts
144
+ elsif obj.is_a?(Types::Set)
145
+ validate_set obj, errors, opts
146
+ end
147
+ opts.delete :parent_operator
148
+ opts[:parent_operator] = parent_operator unless parent_operator.nil?
149
+ opts.delete :parent_field
150
+ opts[:parent_field] = parent_field unless parent_field.nil?
151
+ errors
152
+ end
153
+ end
154
+ end
155
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: msfl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre.dev
4
+ version: 0.0.1.pre.qa
5
5
  platform: ruby
6
6
  authors:
7
7
  - Courtland Caldwell
@@ -10,6 +10,20 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2015-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rake
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -88,6 +102,18 @@ extensions: []
88
102
  extra_rdoc_files: []
89
103
  files:
90
104
  - lib/msfl.rb
105
+ - lib/msfl/configuration.rb
106
+ - lib/msfl/datasets.rb
107
+ - lib/msfl/datasets/base.rb
108
+ - lib/msfl/datasets/movies.rb
109
+ - lib/msfl/parsers.rb
110
+ - lib/msfl/parsers/json.rb
111
+ - lib/msfl/types.rb
112
+ - lib/msfl/types/set.rb
113
+ - lib/msfl/validators.rb
114
+ - lib/msfl/validators/definitions.rb
115
+ - lib/msfl/validators/definitions/hash_key.rb
116
+ - lib/msfl/validators/semantic.rb
91
117
  homepage: https://github.com/caldwecr/msfl
92
118
  licenses:
93
119
  - MIT