oval 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.fixtures.yml +3 -0
- data/.gitignore +18 -0
- data/.rspec +3 -0
- data/.scripts/build-module.sh +25 -0
- data/.travis.yml +22 -0
- data/.yardopts +4 -0
- data/CHANGELOG +2 -0
- data/Gemfile +17 -0
- data/LICENSE +13 -0
- data/Modulefile +8 -0
- data/README.md +280 -0
- data/README_DEVEL.md +27 -0
- data/Rakefile +25 -0
- data/lib/oval.rb +18 -0
- data/lib/oval/anything.rb +12 -0
- data/lib/oval/array_item.rb +25 -0
- data/lib/oval/base.rb +58 -0
- data/lib/oval/class_decl_base.rb +27 -0
- data/lib/oval/collection.rb +125 -0
- data/lib/oval/hash_item.rb +41 -0
- data/lib/oval/instance_of.rb +11 -0
- data/lib/oval/kind_of.rb +11 -0
- data/lib/oval/one_of.rb +36 -0
- data/lib/oval/options.rb +61 -0
- data/lib/oval/subclass_of.rb +11 -0
- data/oval.gemspec +19 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/unit/oval/anything_spec.rb +38 -0
- data/spec/unit/oval/array_item_spec.rb +60 -0
- data/spec/unit/oval/base_spec.rb +184 -0
- data/spec/unit/oval/class_decl_base_spec.rb +73 -0
- data/spec/unit/oval/collection_spec.rb +267 -0
- data/spec/unit/oval/hash_item_spec.rb +146 -0
- data/spec/unit/oval/instance_of_spec.rb +44 -0
- data/spec/unit/oval/kind_of_spec.rb +45 -0
- data/spec/unit/oval/one_of_spec.rb +62 -0
- data/spec/unit/oval/options_spec.rb +215 -0
- data/spec/unit/oval/subclass_of_spec.rb +34 -0
- data/spec/unit/oval_spec.rb +75 -0
- metadata +123 -0
data/.fixtures.yml
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
|
3
|
+
# Build puppet module in /tmp for a given commit or tag.
|
4
|
+
|
5
|
+
set -e
|
6
|
+
|
7
|
+
ROOT=$(readlink -f "$(dirname $0)/..")
|
8
|
+
SOURCE="."
|
9
|
+
TARGET=`mktemp -d`
|
10
|
+
|
11
|
+
function do_build_module {
|
12
|
+
tag=$1
|
13
|
+
tgz="$TARGET/ptomulik-repos-${tag}.tar.gz"
|
14
|
+
dir="ptomulik-repos-$tag/"
|
15
|
+
git archive --prefix $dir --output $tgz $tag
|
16
|
+
(cd $TARGET && tar -xzf $tgz && cd $TARGET/$dir && puppet module build)
|
17
|
+
}
|
18
|
+
|
19
|
+
if [ $# -lt 1 ]; then
|
20
|
+
echo "Usage: $0 <commit>" >&2;
|
21
|
+
echo " or: $0 <tag>" >&2;
|
22
|
+
exit 1
|
23
|
+
fi
|
24
|
+
|
25
|
+
(cd $ROOT && do_build_module $1)
|
data/.travis.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 1.8.7
|
5
|
+
- 1.9.3
|
6
|
+
- 2.0.0
|
7
|
+
script: "bundle exec rake spec SPEC_OPTS='--format documentation'"
|
8
|
+
branches:
|
9
|
+
only:
|
10
|
+
master
|
11
|
+
env:
|
12
|
+
matrix:
|
13
|
+
- FACTER_GEM_VERSION="~> 1.3.0"
|
14
|
+
- FACTER_GEM_VERSION="~> 1.5.0"
|
15
|
+
- FACTER_GEM_VERSION="~> 1.6.0"
|
16
|
+
- FACTER_GEM_VERSION="~> 1.7.0"
|
17
|
+
#matrix:
|
18
|
+
# exclude:
|
19
|
+
# - rvm: 2.0.0
|
20
|
+
# env: FACTER_GEM_VERSION="~> 2.7.0"
|
21
|
+
notifications:
|
22
|
+
email: false
|
data/.yardopts
ADDED
data/CHANGELOG
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
group :development, :test do
|
4
|
+
gem 'rspec-core'
|
5
|
+
gem 'rspec-expectations'
|
6
|
+
gem 'mocha'
|
7
|
+
if RUBY_VERSION >= "1.9"
|
8
|
+
gem 'coveralls', :require => false
|
9
|
+
gem 'yard'
|
10
|
+
gem 'redcarpet'
|
11
|
+
gem 'github-markup'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
gemspec
|
16
|
+
|
17
|
+
# vim: ft=ruby
|
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (C) 2014 Paweł Tomulik <ptomulik@meil.pw.edu.pl>.
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/Modulefile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
name 'ptomulik-options_validator'
|
2
|
+
version '0.0.1'
|
3
|
+
source 'git://github.com/ptomulik/puppet-options_validator.git'
|
4
|
+
author 'ptomulik'
|
5
|
+
license 'Apache License, Version 2.0'
|
6
|
+
summary 'Simple utility to validate Hashes of options.'
|
7
|
+
description 'Simple utility to validate Hashes of options.'
|
8
|
+
project_page 'https://gitbub.com/ptomulik/puppet-options_validator'
|
data/README.md
ADDED
@@ -0,0 +1,280 @@
|
|
1
|
+
#Oval - Options Validator
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/ptomulik/rubygems-oval.png?branch=master)](https://travis-ci.org/ptomulik/rubygems-oval)
|
4
|
+
[![Coverage Status](https://coveralls.io/options_validator/ptomulik/rubygems-oval/badge.png)](https://coveralls.io/r/ptomulik/rubygems-oval)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/ptomulik/rubygems-oval.png)](https://codeclimate.com/github/ptomulik/rubygems-oval)
|
6
|
+
|
7
|
+
####<a id="table-of-contents"></a>Table of Contents
|
8
|
+
|
9
|
+
1. [Overview](#overview)
|
10
|
+
2. [Module Description](#module-description)
|
11
|
+
3. [Usage](#usage)
|
12
|
+
* [Example 1: Declaring simple options](#example-1-declaring-simple-options)
|
13
|
+
* [Example 2: Separating declaration from validation](#example-2-separating-declaration-from-validation)
|
14
|
+
4. [Reference](#reference)
|
15
|
+
* [Declarators](#declarators)
|
16
|
+
* [API Reference](#api-reference)
|
17
|
+
5. [Limitations](#limitations)
|
18
|
+
|
19
|
+
##<a id="overview"></a>Overview
|
20
|
+
|
21
|
+
Validate option hashes when passed to methods.
|
22
|
+
|
23
|
+
[[Table of Contents](#table-of-contents)]
|
24
|
+
|
25
|
+
##<a id="module-description"></a>Module Description
|
26
|
+
|
27
|
+
Using hashes to pass options to methods is a very common ruby practice. With **Oval** method authors may restrict callers to pass only declared options that meet requirements described in a hash declaration.
|
28
|
+
|
29
|
+
The shape of acceptable hashes is described by a simple grammar. The validation is then carried out by a recursive-descent parser that matches the actual values provided by caller against [declarators](#declarators) that comprise the hash declaration.
|
30
|
+
|
31
|
+
A declaration consists of terminal and non-terminal declarators. Non-terminal declarators are created by methods of `Oval` module which have names starting with `ov_` prefix. All other values (such as `:symbol`, `'string'`, `nil`, or `Class`) are terminals. Terminals use `==` operator to match the values provided by caller. Non-teminal use its own logic introducing more elaborate matching criteria (see for example [ov_collection](#ov_collection)).
|
32
|
+
|
33
|
+
**Oval** raises **Oval::DeclError** if the declaration is not well-formed, that is if the description of options shape is erronrneous. This is raised from the point of declaration. Other, more common exception is the **Oval::ValueError** which is raised everytime validation fails. This one is raised from within a method which takes the options as an argument.
|
34
|
+
|
35
|
+
[[Table of Contents](#table-of-contents)]
|
36
|
+
|
37
|
+
##<a id="usage"></a>Usage
|
38
|
+
|
39
|
+
The usage is basically a two-step procedure. The first step is to declare options shape. This would create a validator object. The second step is to validate options within a method using the previously constructed validator. For simple hashes the entire construction may fit to a single line. Let's start with such a simple example.
|
40
|
+
|
41
|
+
###<a id="example-1-declaring-simple-options"></a>Example 1: Declaring Simple Options
|
42
|
+
|
43
|
+
The method `foo` in the following code accepts only `{}` and `{:foo => value}`
|
44
|
+
as `options`, where `value` is arbitrary:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# Options validator
|
48
|
+
require 'oval'
|
49
|
+
class C
|
50
|
+
extend Oval
|
51
|
+
def self.foo(ops = {})
|
52
|
+
ov_options[ :foo => ov_anything ].validate(ops, 'ops')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
What does it do? Just try it out:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
C.foo # should pass
|
61
|
+
C.foo :foo => 10 # should pass
|
62
|
+
C.foo :foo => 10, :bar => 20 # Oval::ValueError "Invalid option :bar for ops. Allowed options are :foo"
|
63
|
+
```
|
64
|
+
|
65
|
+
Options are declared with [ov_xxx declarators](#declarators). The [ov_options](#ov_options) method should always be at the top level. Then all the allowed options should be listed inside of `[]` square brackets. Keys may be any values convertible to strings (i.e. a key given in declaration must `respond_to? :to_s`). Values are declared recursivelly using [ov_xxx declarators](#declarators) or terminal declarators (any other ruby values).
|
66
|
+
|
67
|
+
In [Example 1](#example-1-declaring-simple-options) we have declared options inside of a method for simplicity. This isn't an optimal technique. Usually options' declaration remains same for the entire lifetime of an application, so it is unnecessary to recreate the declaration each time function is called. In other words, we should move the declaration outside of the method, convert it to a singleton and only validate options inside of a function. For that purpose, t
|
68
|
+
he [Example 1](#example-1-declaring-simple-options) could be modified to the following form
|
69
|
+
|
70
|
+
###<a id="example-2-separating-declaration-from-validation"></a>Example 2: Separating declaration from validation
|
71
|
+
|
72
|
+
In this example we separate options declaration from the validation to reduce costs related to options declaration:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
# Options validator
|
76
|
+
require 'oval'
|
77
|
+
class C
|
78
|
+
extend Oval
|
79
|
+
# create a singleton declaration ov
|
80
|
+
def self.ov
|
81
|
+
@ov ||= ov_options[ :foo => ov_anything ]
|
82
|
+
end
|
83
|
+
# use ov to validate ops
|
84
|
+
def self.foo(ops = {})
|
85
|
+
ov.validate(ops, 'ops')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
[[Table of Contents](#table-of-contents)]
|
91
|
+
|
92
|
+
##<a id="reference"></a>Reference
|
93
|
+
|
94
|
+
###<a id="declarators"></a>Declarators
|
95
|
+
|
96
|
+
A declaration of options consists entirely of what we call here **declarators**. The [ov_options](#ov_options) should be used as a root of every declaration (starting symbol in grammar terms). It accepts a Hash of the form **{optname => optdecl, ...}** as an argument. The **optname** is an option name, and **optdecl** is a declarator restricting the options's value. Each option name (key) must be convertible to a `String`. Option value declarators are non-terminal declarators (defined later in this section) or terminals (any other ruby values). The simple declaration
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
ov_options[ :foo => :bar ]
|
100
|
+
```
|
101
|
+
|
102
|
+
uses only terminals inside of **ov_options** and literarly permits only the `{:foo => :bar}` or the empty hash `{}` as options (and nothing else). This is how terminal declarators (`:foo` and `:bar` in this example) work. More freedom may be introduced with non-terminal declarators, for example:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
ov_options[ :foo => ov_anything ]
|
106
|
+
```
|
107
|
+
|
108
|
+
defines an option `:foo` which accepts any value. In what follows, we'll document all the core non-terminal declarators implemented in **Oval**.
|
109
|
+
|
110
|
+
####<a id="ov\_anything"></a>ov\_anything
|
111
|
+
|
112
|
+
- Declaration
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
ov_anything
|
116
|
+
```
|
117
|
+
|
118
|
+
or
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
ov_anything[]
|
122
|
+
```
|
123
|
+
|
124
|
+
- Validation - permits any value
|
125
|
+
- Example
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
ov = ov_options[ :bar => ov_anything ]
|
129
|
+
def foo(ops = {})
|
130
|
+
ov.validate(ops, 'ops')
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
|
135
|
+
####<a id="ov\_collection"></a>ov\_collection
|
136
|
+
|
137
|
+
- Declaration
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
ov_collection[ class_decl, item_decl ]
|
141
|
+
```
|
142
|
+
|
143
|
+
- Validation - permits only collections of type **class_decl** with items matching **item_decl** declaration
|
144
|
+
- Allowed values for **class\_decl** are:
|
145
|
+
- `Hash` or `Array` or any subclass of `Hash` or `Array`,
|
146
|
+
- `ov_subclass_of[klass]` where **klass** is `Hash` or `Array` or a subclass of any of them.
|
147
|
+
- Allowed values for **item_decl**:
|
148
|
+
- if **class\_decl** is `Array`-like, then any value is allowed as **item\_decl**,
|
149
|
+
- if **class\_decl** is `Hash`-like, then **item\_decl** should be a one-element Hash in form **{ key\_decl => val\_decl }**.
|
150
|
+
- Example
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
ov = ov_options[
|
154
|
+
:bar => ov_collection[ Hash, { instance_of[Symbol] => anything } ],
|
155
|
+
:geez => ov_collection [ Array, instance_of[String] ]
|
156
|
+
]
|
157
|
+
def foo(ops = {})
|
158
|
+
ov.validate(ops, 'ops')
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
####<a id="ov\_instance\_of"></a>ov\_instance\_of
|
163
|
+
|
164
|
+
- Declaration
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
ov_instance_of[klass]
|
168
|
+
```
|
169
|
+
|
170
|
+
- Validation - permits only instances of a given class **klass**
|
171
|
+
- Allowed values for **klass** - only class names, for example `String`, `Hash`, etc.
|
172
|
+
- Example
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
ov = ov_options[ :bar => ov_instance_of[String] ]
|
176
|
+
def foo(ops = {})
|
177
|
+
ov.validate(ops,'ops')
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
####<a id="ov\_kind\_of"></a>ov\_kind\_of
|
182
|
+
|
183
|
+
- Declaration
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
ov_kind_of[klass]
|
187
|
+
```
|
188
|
+
|
189
|
+
- Validation - permits only values that are a kind of given class **klass**
|
190
|
+
- Allowed values for **klass** - only class names, for example `String`, `Hash`, etc.
|
191
|
+
- Example
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
ov = ov_options[ :bar => ov_kind_of[Numeric] ]
|
195
|
+
def foo(ops = {})
|
196
|
+
ov.validate(ops,'ops')
|
197
|
+
end
|
198
|
+
```
|
199
|
+
|
200
|
+
####<a id="ov\_one\_of"></a>ov\_one\_of
|
201
|
+
|
202
|
+
- Declaration
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
ov_one_of[decl1,decl2,...]
|
206
|
+
```
|
207
|
+
|
208
|
+
- Validation - permits only values matching one of declarations `decl`, `decl2`, ...
|
209
|
+
- Example
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
ov = ov_options[
|
213
|
+
:bar => ov_one_of[ ov_instance_of[String], ov_kind_of[Numeric], nil ]
|
214
|
+
]
|
215
|
+
def foo(ops = {})
|
216
|
+
ov.validate(ops,'ops')
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
####<a id="ov\_options"></a>ov\_options
|
221
|
+
|
222
|
+
- Declaration
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
ov_options[ optkey_decl1 => optval_decl1, ... ]
|
226
|
+
```
|
227
|
+
|
228
|
+
- Validation - permits only declared options and their values.
|
229
|
+
- Allowed values for `optkey_declN` - anything that is convertible to string (namely, anything that responds to `to_s` method).
|
230
|
+
- Example:
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
ov = ov_options[
|
234
|
+
:bar => ov_anything,
|
235
|
+
:geez => ov_instance_of[String],
|
236
|
+
# ...
|
237
|
+
]
|
238
|
+
def foo(ops = {})
|
239
|
+
ov.validate(ops,'ops')
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
243
|
+
####<a id="ov\_subclass\_of"></a>ov\_subclass\_of
|
244
|
+
|
245
|
+
- Declaration
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
ov_subclass_of[klass]
|
249
|
+
```
|
250
|
+
|
251
|
+
- Validation - permits only subclasses of **klass**
|
252
|
+
- Allowed values for **klass** - only class names, for example `String`, `Hash`, etc.
|
253
|
+
- Example
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
ov = ov_options[ :bar => ov_subclass_of[Numeric] ]
|
257
|
+
def foo(ops = {})
|
258
|
+
ov.validate(ops,'ops')
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
262
|
+
###<a id="api-reference"></a>API Reference
|
263
|
+
|
264
|
+
API reference may be generated with
|
265
|
+
|
266
|
+
```console
|
267
|
+
bundle exec rake yard
|
268
|
+
```
|
269
|
+
|
270
|
+
The generated documentation goes to `doc/` directory. Note that this works only
|
271
|
+
under ruby >= 1.9.
|
272
|
+
|
273
|
+
The API documentation is also available
|
274
|
+
[online](http://rdoc.info/github/ptomulik/rubygems-oval/).
|
275
|
+
|
276
|
+
[[Table of Contents](#table-of-contents)]
|
277
|
+
|
278
|
+
##Limitations
|
279
|
+
|
280
|
+
[[Table of Contents](#table-of-contents)]
|
data/README_DEVEL.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# rubygems-oval
|
2
|
+
|
3
|
+
## Notes for developers
|
4
|
+
|
5
|
+
### Cloning the repository
|
6
|
+
|
7
|
+
git clone git://github.com/ptomulik/rubygems-oval.git
|
8
|
+
|
9
|
+
### Installing required gems
|
10
|
+
|
11
|
+
bundle install --path vendor
|
12
|
+
|
13
|
+
### Runing unit tests
|
14
|
+
|
15
|
+
bundle exec rake spec
|
16
|
+
|
17
|
+
### Runing single test (e.g. **macro_spec.rb**)
|
18
|
+
|
19
|
+
bundle exec rake spec_prep && \
|
20
|
+
bundle exec rspec spec/unit/oval/base.rb
|
21
|
+
|
22
|
+
### Generating API documentation
|
23
|
+
|
24
|
+
bundle exec rake yard
|
25
|
+
|
26
|
+
The generated documentation goes to `doc/` directory. Note that this works only
|
27
|
+
under ruby >= 1.9.
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
Bundler.require :default
|
5
|
+
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
|
8
|
+
RSpec::Core::RakeTask.new(:spec)
|
9
|
+
|
10
|
+
if RUBY_VERSION >= "1.9"
|
11
|
+
# Generating API documentation, run with 'rake yard'
|
12
|
+
require 'yard'
|
13
|
+
YARD::Rake::YardocTask.new do |t|
|
14
|
+
t.options = [
|
15
|
+
'--title', 'Options Validator',
|
16
|
+
'--markup-provider=redcarpet',
|
17
|
+
'--markup=markdown',
|
18
|
+
'lib/**/*.rb'
|
19
|
+
]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
task :default do
|
24
|
+
sh %{rake -T}
|
25
|
+
end
|