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 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-11
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
- https://github.com/obfusk/clj-obfusk-data in ruby.
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 X < Obfusk::Data::ValidHash
56
- data do
57
- field :spam, []
58
- end
77
+ class Foo < Obfusk::Data::ValidHamster
78
+ data { field :foo, [] }
59
79
  end
60
80
 
61
- class Y < Obfusk::Data::ValidHamster
62
- data do
63
- field :spam, []
64
- end
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
- x = X.new spam: 99; x[:spam] = 88
68
- # OK
99
+ y = Bar.new bar: 'ok'
100
+ y[:bar] = 'also ok'
69
101
 
70
- X.new
71
- # raises Obfusk::Data::ValidHash::InvalidError
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
- y = Y.new spam: 'yuck!'
74
- # OK
110
+ []: }}}2
75
111
 
76
- Y.put :invalid, 'oops.'
77
- # raises Obfusk::Data::ValidHamster::InvalidError
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
@@ -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-11
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/base'
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 Base
21
+ include Valid
24
22
 
25
- class InvalidError < RuntimeError; end
26
-
27
- def self.__from_hamster (x)
28
- data = x.instance_eval { [@trie, @default] }
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.new (*a, &b)
42
- x = __from_hamster Hamster::Hash.new(*a, &b)
43
- x.validate!; x
29
+ def self.empty
30
+ @empty ? @empty.validate! : new
44
31
  end
45
32
 
46
- def self.empty
47
- x = @empty ||= new; x.validate!; x
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 except (*a, &b)
51
- y = self.class.__to_hamster self
52
- x = self.class.__from_hamster y.except(*a, &b)
53
- x.validate!; x
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 (*a, &b)
57
- x = super; x.validate!; x
44
+ def transform (&b)
45
+ super.validate!
58
46
  end
59
47
 
60
48
  end # }}}1
@@ -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-11
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/base'
12
+ require 'obfusk/data/valid'
13
13
 
14
14
  module Obfusk
15
15
  module Data
16
16
 
17
17
  # @todo document
18
- class ValidHash < Hash # {{{1
18
+ # @todo what to do about defaults !?
19
+ class ValidHashBase < Hash # {{{1
19
20
 
20
- include Base
21
+ include Valid
21
22
 
22
- class InvalidError < RuntimeError; end
23
+ TRANSFORMATIONS = %w{
24
+ []= clear delete merge! replace shift store update
25
+ }.map(&:to_sym)
23
26
 
24
- %w{
25
- []= clear delete delete_if keep_if merge! reject! replace
26
- select! shift store update
27
- }.map(&:to_sym).each do |m|
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
- r = super(*a, &b); validate!; r
37
+ raise NotImplementedError
30
38
  end
31
39
  end
32
40
 
33
41
  def self.[] (*a, &b)
34
- x = super; x.validate!; x
35
- end
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 compare_by_identity
42
- raise NotImplementedError
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
- x = super; x.validate!; x
56
+ super.validate!
51
57
  end
52
58
 
53
59
  def reject (*a, &b)
54
- x = super; x.validate!; x
60
+ return to_enum :reject, *a unless b
61
+ super.validate!
55
62
  end
56
63
 
57
64
  def select (*a, &b)
58
- x = self.class[super]; x.validate!; x # TODO
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/base.rb
3
+ # File : obfusk/data/valid.rb
4
4
  # Maintainer : Felix C. Stegerman <flx@obfusk.net>
5
- # Date : 2013-02-11
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
- module Base
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?
@@ -1,6 +1,6 @@
1
1
  module Obfusk
2
2
  module Data
3
- VERSION = '0.0.2.SNAPSHOT.20130212031255'
3
+ VERSION = '0.0.2.SNAPSHOT.20130213231507'
4
4
  DATE = '2013-02-11'
5
5
  end
6
6
  end
@@ -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-11
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::ValidHamster::InvalidError
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 E
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 E
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 E
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 E
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 E
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 E
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-11
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 do
24
- field :baz, []
25
- end
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::ValidHash::InvalidError
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 E
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 E
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 E
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 E
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.20130212031255
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: &20985220 !ruby/object:Gem::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: *20985220
24
+ version_requirements: *19084200
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &20928520 !ruby/object:Gem::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: *20928520
35
+ version_requirements: *19083580
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &20927780 !ruby/object:Gem::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: *20927780
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/base.rb
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