obfusk-data 0.0.2.SNAPSHOT.20130212031255 → 0.0.2.SNAPSHOT.20130213231507
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.
- 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
|