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

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