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 +4 -4
- data/lib/msfl.rb +28 -3
- data/lib/msfl/configuration.rb +9 -0
- data/lib/msfl/datasets.rb +1 -0
- data/lib/msfl/datasets/base.rb +79 -0
- data/lib/msfl/datasets/movies.rb +13 -0
- data/lib/msfl/parsers.rb +1 -0
- data/lib/msfl/parsers/json.rb +26 -0
- data/lib/msfl/types.rb +1 -0
- data/lib/msfl/types/set.rb +7 -0
- data/lib/msfl/validators.rb +1 -0
- data/lib/msfl/validators/definitions.rb +1 -0
- data/lib/msfl/validators/definitions/hash_key.rb +36 -0
- data/lib/msfl/validators/semantic.rb +155 -0
- metadata +27 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5192c432f2b0fa92fdc52f791a6d1db94e0e7273
|
4
|
+
data.tar.gz: 369cf6c5d51a3b2f996b070a70894a6fd952b7f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dadc0e105e90e00c982424d332a2cada2733a676cf9c26f0d65ab3c5a79e4ffe94c78b87903c493aa070ae0c80f813eb1bf6313cd81531593911fde0f0c3b1bf
|
7
|
+
data.tar.gz: 86c9aac383c3a267e9344ca3f081b4b1e9fd8c461ca5e86a5962e3811476dad04ac918c521cc9dc0011811e5029caca9ec14858bd9b83ad313a8c0a1c45d6561
|
data/lib/msfl.rb
CHANGED
@@ -1,5 +1,30 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
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 @@
|
|
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
|
data/lib/msfl/parsers.rb
ADDED
@@ -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
|
data/lib/msfl/types.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'types/set'
|
@@ -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.
|
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
|