obfusk-data 0.0.2.SNAPSHOT.20130212031255 → 0.0.2.SNAPSHOT.20130213231507
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +72 -18
- data/lib/obfusk/data/hamster.rb +19 -31
- data/lib/obfusk/data/hash.rb +82 -20
- data/lib/obfusk/data/{base.rb → valid.rb} +7 -3
- data/lib/obfusk/data/version.rb +1 -1
- data/spec/obfusk/data/hamster_spec.rb +8 -8
- data/spec/obfusk/data/hash_spec.rb +31 -9
- metadata +8 -8
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
File : README.md
|
4
4
|
Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
Date : 2013-02-
|
5
|
+
Date : 2013-02-13
|
6
6
|
|
7
7
|
Copyright : Copyright (C) 2013 Felix C. Stegerman
|
8
8
|
Version : 0.0.2.SNAPSHOT
|
@@ -14,9 +14,27 @@
|
|
14
14
|
|
15
15
|
[rb-]obfusk-data - data validation combinator library for ruby
|
16
16
|
|
17
|
+
https://github.com/obfusk/clj-obfusk-data in ruby.
|
18
|
+
|
17
19
|
...
|
18
20
|
|
19
|
-
|
21
|
+
### ValidHash vs UnsafeValidHash
|
22
|
+
|
23
|
+
A ValidHash is always valid: modifications are rolled back if they
|
24
|
+
made it invalid.
|
25
|
+
|
26
|
+
An UnsafeValidHash may become invalid: modifications are always
|
27
|
+
performed, even if they make it invalid.
|
28
|
+
|
29
|
+
A ValidHash is therefore (probably) less efficient than an
|
30
|
+
UnsafeValidHash, but never becomes invalid. Choose wisely.
|
31
|
+
|
32
|
+
[]: }}}1
|
33
|
+
|
34
|
+
## Examples
|
35
|
+
[]: {{{1
|
36
|
+
|
37
|
+
[]: {{{2
|
20
38
|
|
21
39
|
```ruby
|
22
40
|
isa = ->(cls, obj) { cls === obj } .curry
|
@@ -51,32 +69,68 @@ Obfusk::Data.valid? tree,
|
|
51
69
|
# => true
|
52
70
|
```
|
53
71
|
|
72
|
+
[]: }}}2
|
73
|
+
|
74
|
+
[]: {{{2
|
75
|
+
|
54
76
|
```ruby
|
55
|
-
class
|
56
|
-
data
|
57
|
-
field :spam, []
|
58
|
-
end
|
77
|
+
class Foo < Obfusk::Data::ValidHamster
|
78
|
+
data { field :foo, [] }
|
59
79
|
end
|
60
80
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
81
|
+
x = Foo.new foo: 'ok'
|
82
|
+
|
83
|
+
x.put :invalid, 'oops'
|
84
|
+
# ---> Obfusk::Data::Valid::InvalidError
|
85
|
+
|
86
|
+
Foo.new
|
87
|
+
# ---> Obfusk::Data::Valid::InvalidError
|
88
|
+
```
|
89
|
+
|
90
|
+
[]: }}}2
|
91
|
+
|
92
|
+
[]: {{{2
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
class Bar < Obfusk::Data::ValidHash
|
96
|
+
data { field :bar, [] }
|
65
97
|
end
|
66
98
|
|
67
|
-
|
68
|
-
|
99
|
+
y = Bar.new bar: 'ok'
|
100
|
+
y[:bar] = 'also ok'
|
69
101
|
|
70
|
-
|
71
|
-
|
102
|
+
begin
|
103
|
+
y[:invalid] = 'oops'
|
104
|
+
rescue Obfusk::Data::Valid::InvalidError
|
105
|
+
y.valid? # => true
|
106
|
+
y[:invalid] # => nil
|
107
|
+
end
|
108
|
+
```
|
72
109
|
|
73
|
-
|
74
|
-
# OK
|
110
|
+
[]: }}}2
|
75
111
|
|
76
|
-
|
77
|
-
|
112
|
+
[]: {{{2
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
class Baz < Obfusk::Data::UnsafeValidHash
|
116
|
+
data { field :baz, [] }
|
117
|
+
end
|
118
|
+
|
119
|
+
z = Baz.new baz: 'ok'
|
120
|
+
c = z.dup
|
121
|
+
|
122
|
+
begin
|
123
|
+
z[:invalid] = 'oops'
|
124
|
+
rescue Obfusk::Data::Valid::InvalidError
|
125
|
+
z.valid? # => false
|
126
|
+
c.valid? # => true
|
127
|
+
z[:invalid] # => 'oops'
|
128
|
+
c[:invalid] # => nil
|
129
|
+
end
|
78
130
|
```
|
79
131
|
|
132
|
+
[]: }}}2
|
133
|
+
|
80
134
|
[]: }}}1
|
81
135
|
|
82
136
|
## Specs & Docs
|
data/lib/obfusk/data/hamster.rb
CHANGED
@@ -2,59 +2,47 @@
|
|
2
2
|
#
|
3
3
|
# File : obfusk/data/hamster.rb
|
4
4
|
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
# Date : 2013-02-
|
5
|
+
# Date : 2013-02-13
|
6
6
|
#
|
7
7
|
# Copyright : Copyright (C) 2013 Felix C. Stegerman
|
8
8
|
# Licence : GPLv2 or EPLv1
|
9
9
|
#
|
10
10
|
# -- ; }}}1
|
11
11
|
|
12
|
-
require 'obfusk/data/
|
12
|
+
require 'obfusk/data/valid'
|
13
13
|
|
14
14
|
module Obfusk
|
15
15
|
module Data
|
16
16
|
|
17
17
|
# @todo document
|
18
|
-
# @note
|
19
|
-
# getting this to work depends on Hamster implementation details
|
20
|
-
# as well as some black magic ;-(
|
18
|
+
# @note depends on Hamster implementation details !!!
|
21
19
|
class ValidHamster < Hamster::Hash # {{{1
|
22
20
|
|
23
|
-
include
|
21
|
+
include Valid
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
y = allocate
|
30
|
-
y.instance_eval { @trie, @default = data }
|
31
|
-
y
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.__to_hamster (x)
|
35
|
-
data = x.instance_eval { [@trie, @default] }
|
36
|
-
y = Hamster::Hash.allocate
|
37
|
-
y.instance_eval { @trie, @default = data }
|
38
|
-
y
|
23
|
+
def self.new (pairs = {}, &b)
|
24
|
+
@empty ||= super()
|
25
|
+
x = b ? super(&b) : @empty
|
26
|
+
pairs.empty? ? x.validate! : x.merge_hash(pairs)
|
39
27
|
end
|
40
28
|
|
41
|
-
def self.
|
42
|
-
|
43
|
-
x.validate!; x
|
29
|
+
def self.empty
|
30
|
+
@empty ? @empty.validate! : new
|
44
31
|
end
|
45
32
|
|
46
|
-
def
|
47
|
-
|
33
|
+
def except (*keys)
|
34
|
+
trie = keys.reduce(@trie) { |t, k| t.delete k }
|
35
|
+
transform_unless(trie.equal? @trie) { @trie = trie }
|
48
36
|
end
|
49
37
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
38
|
+
def merge_hash (pairs)
|
39
|
+
transform_unless(pairs.empty?) {
|
40
|
+
@trie = pairs.reduce(@trie) { |t, p| t.put *p }
|
41
|
+
}
|
54
42
|
end
|
55
43
|
|
56
|
-
def transform (
|
57
|
-
|
44
|
+
def transform (&b)
|
45
|
+
super.validate!
|
58
46
|
end
|
59
47
|
|
60
48
|
end # }}}1
|
data/lib/obfusk/data/hash.rb
CHANGED
@@ -2,68 +2,130 @@
|
|
2
2
|
#
|
3
3
|
# File : obfusk/data/hash.rb
|
4
4
|
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
# Date : 2013-02-
|
5
|
+
# Date : 2013-02-13
|
6
6
|
#
|
7
7
|
# Copyright : Copyright (C) 2013 Felix C. Stegerman
|
8
8
|
# Licence : GPLv2 or EPLv1
|
9
9
|
#
|
10
10
|
# -- ; }}}1
|
11
11
|
|
12
|
-
require 'obfusk/data/
|
12
|
+
require 'obfusk/data/valid'
|
13
13
|
|
14
14
|
module Obfusk
|
15
15
|
module Data
|
16
16
|
|
17
17
|
# @todo document
|
18
|
-
|
18
|
+
# @todo what to do about defaults !?
|
19
|
+
class ValidHashBase < Hash # {{{1
|
19
20
|
|
20
|
-
include
|
21
|
+
include Valid
|
21
22
|
|
22
|
-
|
23
|
+
TRANSFORMATIONS = %w{
|
24
|
+
[]= clear delete merge! replace shift store update
|
25
|
+
}.map(&:to_sym)
|
23
26
|
|
24
|
-
%w{
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
TRANSFORMATIONS_ENUMERATORS = %w{
|
28
|
+
delete_if keep_if reject! select!
|
29
|
+
}.map(&:to_sym)
|
30
|
+
|
31
|
+
UNIMPLEMENTED = %w{
|
32
|
+
compare_by_identity default= default_proc=
|
33
|
+
}.map(&:to_sym)
|
34
|
+
|
35
|
+
UNIMPLEMENTED.each do |m|
|
28
36
|
define_method m do |*a, &b|
|
29
|
-
|
37
|
+
raise NotImplementedError
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
33
41
|
def self.[] (*a, &b)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def initialize (data = {}, &block)
|
38
|
-
super(&block); self.merge! data
|
42
|
+
# NB: we must validate b/c super uses allocate instead of new
|
43
|
+
super.validate!
|
39
44
|
end
|
40
45
|
|
41
|
-
def
|
42
|
-
|
46
|
+
def initialize (data = {}, &b)
|
47
|
+
super(&b); self.merge! data
|
43
48
|
end
|
44
49
|
|
50
|
+
# @return [Hash]
|
45
51
|
def invert
|
46
52
|
Hash[self].invert
|
47
53
|
end
|
48
54
|
|
49
55
|
def merge (*a, &b)
|
50
|
-
|
56
|
+
super.validate!
|
51
57
|
end
|
52
58
|
|
53
59
|
def reject (*a, &b)
|
54
|
-
|
60
|
+
return to_enum :reject, *a unless b
|
61
|
+
super.validate!
|
55
62
|
end
|
56
63
|
|
57
64
|
def select (*a, &b)
|
58
|
-
|
65
|
+
return to_enum :select, *a unless b
|
66
|
+
|
67
|
+
# NB: super returns a Hash; implementing select manually is
|
68
|
+
# problematic b/c new() and []=; this is the simplest (but not
|
69
|
+
# the most efficient -- b/c copying) implementation
|
70
|
+
|
71
|
+
self.class[super] # TODO
|
59
72
|
end
|
60
73
|
|
74
|
+
# @return [Hash]
|
61
75
|
def to_hash
|
62
76
|
Hash[self]
|
63
77
|
end
|
64
78
|
|
65
79
|
end # }}}1
|
66
80
|
|
81
|
+
# @todo document
|
82
|
+
class ValidHash < ValidHashBase # {{{1
|
83
|
+
|
84
|
+
alias_method :__validhash__original_replace__, :replace
|
85
|
+
private :__validhash__original_replace__
|
86
|
+
|
87
|
+
def __validhash__validate_and_rollback__ (c, r)
|
88
|
+
begin
|
89
|
+
validate!; r
|
90
|
+
rescue InvalidError
|
91
|
+
__validhash__original_replace__ c; raise
|
92
|
+
end
|
93
|
+
end
|
94
|
+
private :__validhash__validate_and_rollback__
|
95
|
+
|
96
|
+
TRANSFORMATIONS.each do |m|
|
97
|
+
define_method m do |*a, &b|
|
98
|
+
__validhash__validate_and_rollback__ dup, super(*a, &b)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
TRANSFORMATIONS_ENUMERATORS.each do |m|
|
103
|
+
define_method m do |*a, &b|
|
104
|
+
return to_enum m, *a unless b
|
105
|
+
__validhash__validate_and_rollback__ dup, super(*a, &b)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end # }}}1
|
110
|
+
|
111
|
+
# @todo document
|
112
|
+
class UnsafeValidHash < ValidHashBase # {{{1
|
113
|
+
|
114
|
+
TRANSFORMATIONS.each do |m|
|
115
|
+
define_method m do |*a, &b|
|
116
|
+
r = super(*a, &b); validate!; r
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
TRANSFORMATIONS_ENUMERATORS.each do |m|
|
121
|
+
define_method m do |*a, &b|
|
122
|
+
return to_enum m, *a unless b
|
123
|
+
r = super(*a, &b); validate!; r
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end # }}}1
|
128
|
+
|
67
129
|
end
|
68
130
|
end
|
69
131
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# -- ; {{{1
|
2
2
|
#
|
3
|
-
# File : obfusk/data/
|
3
|
+
# File : obfusk/data/valid.rb
|
4
4
|
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
# Date : 2013-02-
|
5
|
+
# Date : 2013-02-13
|
6
6
|
#
|
7
7
|
# Copyright : Copyright (C) 2013 Felix C. Stegerman
|
8
8
|
# Licence : GPLv2 or EPLv1
|
@@ -14,7 +14,10 @@ require 'obfusk/data'
|
|
14
14
|
module Obfusk
|
15
15
|
module Data
|
16
16
|
|
17
|
-
|
17
|
+
# @todo document
|
18
|
+
module Valid
|
19
|
+
|
20
|
+
class InvalidError < RuntimeError; end
|
18
21
|
|
19
22
|
def self.included (base)
|
20
23
|
base.extend ClassMethods
|
@@ -35,6 +38,7 @@ module Obfusk
|
|
35
38
|
def validate!
|
36
39
|
e = Obfusk::Data.validate self.class::VALIDATOR, self
|
37
40
|
raise self.class::InvalidError, e.join('; ') if e
|
41
|
+
self
|
38
42
|
end
|
39
43
|
|
40
44
|
def valid?
|
data/lib/obfusk/data/version.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# File : obfusk/data/hamster_spec.rb
|
4
4
|
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
# Date : 2013-02-
|
5
|
+
# Date : 2013-02-13
|
6
6
|
#
|
7
7
|
# Copyright : Copyright (C) 2013 Felix C. Stegerman
|
8
8
|
# Licence : GPLv2 or EPLv1
|
@@ -26,7 +26,7 @@ module Obfusk::Data::Hamster__Spec
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
E = Obfusk::Data::
|
29
|
+
E = Obfusk::Data::Valid::InvalidError
|
30
30
|
|
31
31
|
# --
|
32
32
|
|
@@ -43,15 +43,15 @@ module Obfusk::Data::Hamster__Spec
|
|
43
43
|
Bar.new some_other_field: 37
|
44
44
|
end
|
45
45
|
it 'invalid Bar' do
|
46
|
-
expect { Bar.new baz: 42 }.to raise_error
|
46
|
+
expect { Bar.new baz: 42 }.to raise_error(E)
|
47
47
|
end
|
48
48
|
it 'invalid Bar merge' do
|
49
49
|
b = Bar.new
|
50
|
-
expect { b.merge Hamster.hash baz: 42 }.to raise_error
|
50
|
+
expect { b.merge Hamster.hash baz: 42 }.to raise_error(E)
|
51
51
|
end
|
52
52
|
it 'invalid Bar put' do
|
53
53
|
b = Bar.new
|
54
|
-
expect { b.put :bar, 'hi!' }.to raise_error
|
54
|
+
expect { b.put :bar, 'hi!' }.to raise_error(E)
|
55
55
|
end
|
56
56
|
end # }}}1
|
57
57
|
|
@@ -64,14 +64,14 @@ module Obfusk::Data::Hamster__Spec
|
|
64
64
|
b.except :maybe
|
65
65
|
end
|
66
66
|
it 'invalid empty Baz (new)' do
|
67
|
-
expect { Baz.new }.to raise_error
|
67
|
+
expect { Baz.new }.to raise_error(E)
|
68
68
|
end
|
69
69
|
it 'invalid empty Baz (empty)' do
|
70
|
-
expect { Baz.empty }.to raise_error
|
70
|
+
expect { Baz.empty }.to raise_error(E)
|
71
71
|
end
|
72
72
|
it 'invalid Baz except' do
|
73
73
|
b = Baz.new baz: 1, maybe: 2
|
74
|
-
expect { b.except :baz }.to raise_error
|
74
|
+
expect { b.except :baz }.to raise_error(E)
|
75
75
|
end
|
76
76
|
end # }}}1
|
77
77
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# File : obfusk/data/hash_spec.rb
|
4
4
|
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
# Date : 2013-02-
|
5
|
+
# Date : 2013-02-13
|
6
6
|
#
|
7
7
|
# Copyright : Copyright (C) 2013 Felix C. Stegerman
|
8
8
|
# Licence : GPLv2 or EPLv1
|
@@ -20,12 +20,14 @@ module Obfusk::Data::Hash__Spec
|
|
20
20
|
end
|
21
21
|
|
22
22
|
class Baz < Obfusk::Data::ValidHash
|
23
|
-
data
|
24
|
-
|
25
|
-
|
23
|
+
data { field :baz, [] }
|
24
|
+
end
|
25
|
+
|
26
|
+
class Qux < Obfusk::Data::UnsafeValidHash
|
27
|
+
data { field :qux, [] }
|
26
28
|
end
|
27
29
|
|
28
|
-
E = Obfusk::Data::
|
30
|
+
E = Obfusk::Data::Valid::InvalidError
|
29
31
|
|
30
32
|
# --
|
31
33
|
|
@@ -42,22 +44,42 @@ module Obfusk::Data::Hash__Spec
|
|
42
44
|
Bar.new some_other_field: 37
|
43
45
|
end
|
44
46
|
it 'invalid Bar' do
|
45
|
-
expect { Bar.new baz: 42 }.to raise_error
|
47
|
+
expect { Bar.new baz: 42 }.to raise_error(E)
|
46
48
|
end
|
47
49
|
it 'invalid Bar merge' do
|
48
50
|
b = Bar.new
|
49
|
-
expect { b.merge baz: 42 }.to raise_error
|
51
|
+
expect { b.merge baz: 42 }.to raise_error(E)
|
52
|
+
b.should be_valid
|
50
53
|
end
|
51
54
|
it 'invalid Bar []=' do
|
52
55
|
b = Bar.new
|
53
|
-
expect { b[:bar] = 'hi!' }.to raise_error
|
56
|
+
expect { b[:bar] = 'hi!' }.to raise_error(E)
|
57
|
+
b.should be_valid
|
54
58
|
end
|
55
59
|
end # }}}1
|
56
60
|
|
57
61
|
context 'Baz' do # {{{1
|
58
62
|
it 'invalid Baz clear' do
|
59
63
|
b = Baz.new baz: 'ok'
|
60
|
-
expect { b.clear }.to raise_error
|
64
|
+
expect { b.clear }.to raise_error(E)
|
65
|
+
b.should be_valid
|
66
|
+
end
|
67
|
+
it 'invalid Baz reject!' do
|
68
|
+
b = Baz.new baz: 'ok'
|
69
|
+
f = b.reject!
|
70
|
+
expect { f.each { true } }.to raise_error(E)
|
71
|
+
b[:baz].should == 'ok'
|
72
|
+
b.should be_valid
|
73
|
+
end
|
74
|
+
end # }}}1
|
75
|
+
|
76
|
+
context 'Qux' do # {{{1
|
77
|
+
it 'invalid Qux reject!' do
|
78
|
+
q = Qux.new qux: 'ok'
|
79
|
+
f = q.reject!
|
80
|
+
expect { f.each { true } }.to raise_error(E)
|
81
|
+
q.should be_empty
|
82
|
+
q.should_not be_valid
|
61
83
|
end
|
62
84
|
end # }}}1
|
63
85
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: obfusk-data
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.2.SNAPSHOT.
|
4
|
+
version: 0.0.2.SNAPSHOT.20130213231507
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2013-02-11 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: hamster
|
16
|
-
requirement: &
|
16
|
+
requirement: &19084200 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *19084200
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake
|
27
|
-
requirement: &
|
27
|
+
requirement: &19083580 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *19083580
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &19082880 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *19082880
|
47
47
|
description: ! '...
|
48
48
|
|
49
49
|
'
|
@@ -59,7 +59,7 @@ files:
|
|
59
59
|
- lib/obfusk/data/hamster.rb
|
60
60
|
- lib/obfusk/data/version.rb
|
61
61
|
- lib/obfusk/data/hash.rb
|
62
|
-
- lib/obfusk/data/
|
62
|
+
- lib/obfusk/data/valid.rb
|
63
63
|
- lib/obfusk/data.rb
|
64
64
|
- spec/_data.rb
|
65
65
|
- spec/obfusk/data/hash_spec.rb
|