lab42_data_class 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70b03224373a4bdfe219cfb5d71d735f4a5b4d02c57531ea0f80a1729e542ce5
4
- data.tar.gz: b04020b335831a2c8fb8070bb17056874bb9103c5dda7b0e313da93c8afd80a0
3
+ metadata.gz: 1819694c4684f3f4e377f04f4eb17d0842a1075ff7859eeef443c66dd7cfb458
4
+ data.tar.gz: 5af74933329ed924d51a1820d666c671973004edbd3ab00f6ba93378df71180f
5
5
  SHA512:
6
- metadata.gz: c360a0eb554b9356c92e0a395d09a2dd29dc76f64d2a805ef6ac8e1699a057bb01fc8a41be4bf2f34f3e3ec5d9eff336817541bc59d39f7d94c74131303e2f0d
7
- data.tar.gz: 63395d4ede760ce63e7d92753ef370b2da666bafec309dee43f8c8549e768cc653d1976742441b583fd09a1a85535cc783834998ce494b903f2a40d380073a46
6
+ metadata.gz: 5e14f2656f03703aa867dce065bf9e4770cf043fd97d035cab9a6b869d9fb6e9749c2de3eef0b948e02c7ade07f85f75423c4b2412ed154574a6672e28411d9f
7
+ data.tar.gz: f831c9c8094f7224de088f6f03847791bcc91a33db6e565fc1ef3f45d5f3f5e29d18b05dfbcdbc7dcc422755d0ab87dc73f0c50a9de45dcd3df0916d87fb1c16
data/README.md CHANGED
@@ -40,6 +40,7 @@ Well let us [speculate about](https://github.com/RobertDober/speculate_about) it
40
40
 
41
41
  ## Context `DataClass`
42
42
 
43
+
43
44
  ### Context: `DataClass` function
44
45
 
45
46
  Given
@@ -452,6 +453,47 @@ And remark how bad unnamed validation errors might be
452
453
  .to raise_error(validation_error, error_message_rgx)
453
454
  ```
454
455
 
456
+ ### Context: Usage with `extend`
457
+
458
+ All the above mentioned features can be achieved with a more conventional syntax by extending a class
459
+ with `Lab42::DataClass`
460
+
461
+ Given a class that extends `DataClass`
462
+ ```ruby
463
+ let :my_class do
464
+ Class.new do
465
+ extend Lab42::DataClass
466
+ attributes :age, member: false
467
+ constraint :member, Set.new([false, true])
468
+ validate(:too_young_for_member) { |instance| !(instance.member && instance.age < 18) }
469
+ end
470
+ end
471
+ let(:constraint_error) { Lab42::DataClass::ConstraintError }
472
+ let(:validation_error) { Lab42::DataClass::ValidationError }
473
+ let(:my_instance) { my_class.new(age: 42) }
474
+ let(:my_vip) { my_instance.merge(member: true) }
475
+ ```
476
+
477
+ Then we can observe that instances of such a class
478
+ ```ruby
479
+ expect(my_instance.to_h).to eq(age: 42, member: false)
480
+ expect(my_vip.to_h).to eq(age: 42, member: true)
481
+ expect(my_instance.member).to be_falsy
482
+ ```
483
+
484
+ And we will get constraint errors if applicable
485
+ ```ruby
486
+ expect{my_instance.merge(member: nil)}
487
+ .to raise_error(constraint_error)
488
+ ```
489
+
490
+ And of course validations still work too
491
+ ```ruby
492
+ expect{ my_vip.merge(age: 17) }
493
+ .to raise_error(validation_error, "too_young_for_member")
494
+ ```
495
+
496
+
455
497
  ## Context: `Pair` and `Triple`
456
498
 
457
499
  Two special cases of a `DataClass` which behave like `Tuple` of size 2 and 3 in _Elixir_
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kernel
4
+ def DataClass(*args, **kwds, &blk)
5
+ proxy = Lab42::DataClass::Proxy.new(*args, **kwds, &blk)
6
+ proxy.define_class!
7
+ end
8
+
9
+ def Pair(first, second)
10
+ Lab42::Pair.new(first, second)
11
+ end
12
+
13
+ def Triple(first, second, third)
14
+ Lab42::Triple.new(first, second, third)
15
+ end
16
+ end
17
+ # SPDX-License-Identifier: Apache-2.0
@@ -23,6 +23,16 @@ module Lab42
23
23
  end
24
24
  end
25
25
 
26
+ def define_constraints(constraints)
27
+ errors = constraints.map(&define_constraint).compact
28
+ unless errors.empty?
29
+ raise ArgumentError,
30
+ "constraints cannot be defined for undefined attributes #{errors.inspect}"
31
+ end
32
+
33
+ check_constraints_against_defaults(constraints)
34
+ end
35
+
26
36
  private
27
37
 
28
38
  def _check_constraint_against_default
@@ -68,13 +78,7 @@ module Lab42
68
78
  proxy = self
69
79
  ->(*) do
70
80
  define_method :with_constraint do |**constraints|
71
- errors = constraints.map(&proxy.define_constraint).compact
72
- unless errors.empty?
73
- raise ArgumentError,
74
- "constraints cannot be defined for undefined attributes #{errors.inspect}"
75
- end
76
-
77
- proxy.check_constraints_against_defaults(constraints)
81
+ proxy.define_constraints(constraints)
78
82
  self
79
83
  end
80
84
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lab42
4
+ module DataClass
5
+ class Proxy
6
+ module Memos
7
+ def constraints
8
+ @__constraints__ ||= Hash.new { |h, k| h[k] = [] }
9
+ end
10
+
11
+ def defaults
12
+ @__defaults__ ||= {}
13
+ end
14
+
15
+ def members
16
+ @__members__ ||= unless (positionals + defaults.keys).empty?
17
+ Set.new(positionals + defaults.keys)
18
+ end
19
+ end
20
+
21
+ def positionals
22
+ @__positionals__ ||= []
23
+ end
24
+
25
+ def validations
26
+ @__validations__ ||= []
27
+ end
28
+
29
+ private
30
+
31
+ def _missing_initializers
32
+ @___missing_initializers__ ||=
33
+ positionals - actual_params.keys
34
+ end
35
+
36
+ def _illegal_initializers
37
+ @___illegal_initializers__ ||=
38
+ actual_params.keys - positionals - defaults.keys
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ # SPDX-License-Identifier: Apache-2.0
@@ -2,22 +2,22 @@
2
2
 
3
3
  require 'set'
4
4
  require_relative 'proxy/constraints'
5
+ require_relative 'proxy/memos'
5
6
  require_relative 'proxy/validations'
6
7
  require_relative 'proxy/mixin'
7
8
  module Lab42
8
9
  module DataClass
9
10
  class Proxy
10
- include Constraints, Validations
11
+ include Constraints, Memos, Validations
11
12
 
12
- attr_reader :actual_params, :all_params, :block, :constraints, :defaults, :klass, :members, :positionals
13
+ attr_reader :actual_params, :block, :klass
13
14
 
14
15
  def check!(**params)
15
16
  @actual_params = params
16
- @all_params = defaults.merge(params)
17
17
  raise ArgumentError, "missing initializers for #{_missing_initializers}" unless _missing_initializers.empty?
18
18
  raise ArgumentError, "illegal initializers #{_illegal_initializers}" unless _illegal_initializers.empty?
19
19
 
20
- _check_constraints!(all_params)
20
+ _check_constraints!(defaults.merge(params))
21
21
  end
22
22
 
23
23
  def define_class!
@@ -39,21 +39,22 @@ module Lab42
39
39
  .to_h
40
40
  end
41
41
 
42
- def validations
43
- @__validations__ ||= []
42
+ def update!(with_positionals, with_keywords)
43
+ positionals.push(*with_positionals)
44
+ defaults.update(with_keywords)
44
45
  end
45
46
 
46
47
  private
47
48
  def initialize(*args, **kwds, &blk)
48
- @klass = Class.new
49
-
50
- @constraints = Hash.new { |h, k| h[k] = [] }
49
+ @klass = if Class === args.first
50
+ args.shift
51
+ else
52
+ Class.new
53
+ end
51
54
 
52
55
  @block = blk
53
- @defaults = kwds
54
- @members = Set.new(args + kwds.keys)
55
- # TODO: Check for all symbols and no duplicates ⇒ v0.5.1
56
- @positionals = args
56
+ defaults.update(kwds)
57
+ positionals.push(*args)
57
58
  end
58
59
 
59
60
  def _define_attr_reader
@@ -128,16 +129,6 @@ module Lab42
128
129
  data_class_instance.instance_variable_set("@#{key}", value)
129
130
  end
130
131
  end
131
-
132
- def _missing_initializers
133
- @___missing_initializers__ ||=
134
- positionals - actual_params.keys
135
- end
136
-
137
- def _illegal_initializers
138
- @___illegal_initializers__ ||=
139
- actual_params.keys - positionals - defaults.keys
140
- end
141
132
  end
142
133
  end
143
134
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Lab42
4
4
  module DataClass
5
- VERSION = "0.5.1"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end
8
8
  # SPDX-License-Identifier: Apache-2.0
@@ -1,24 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './data_class/constraint_error'
4
+ require_relative './data_class/kernel'
4
5
  require_relative './data_class/validation_error'
5
6
  require_relative './data_class/proxy'
6
7
  require_relative './pair'
7
8
  require_relative './triple'
8
9
 
9
- module Kernel
10
- def DataClass(*args, **kwds, &blk)
11
- proxy = Lab42::DataClass::Proxy.new(*args, **kwds, &blk)
12
- proxy.define_class!
13
- end
10
+ module Lab42
11
+ module DataClass
12
+ def self.extended(extender)
13
+ proxy = Proxy.new(extender)
14
14
 
15
- def Pair(first, second)
16
- Lab42::Pair.new(first, second)
17
- end
15
+ extender.module_eval do
16
+ define_singleton_method(:__data_class_proxy__){ proxy }
17
+ end
18
+ end
19
+
20
+ def attributes(*args, **kwds)
21
+ __data_class_proxy__.tap do |proxy|
22
+ proxy.update!(args, kwds)
23
+ proxy.define_class!
24
+ end
25
+ end
18
26
 
19
- def Triple(first, second, third)
20
- Lab42::Triple.new(first, second, third)
27
+ def constraint(member, constraint = nil, &block)
28
+ raise ArgumentError, "must not provide constraint (2nd argument) and a block" if block && constraint
29
+
30
+ __data_class_proxy__.define_constraints(member => constraint || block)
31
+ end
21
32
  end
22
33
  end
23
-
24
34
  # SPDX-License-Identifier: Apache-2.0
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lab42_data_class
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Dober
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2022-02-24 00:00:00.000000000 Z
@@ -25,9 +25,11 @@ files:
25
25
  - README.md
26
26
  - lib/lab42/data_class.rb
27
27
  - lib/lab42/data_class/constraint_error.rb
28
+ - lib/lab42/data_class/kernel.rb
28
29
  - lib/lab42/data_class/proxy.rb
29
30
  - lib/lab42/data_class/proxy/constraints.rb
30
31
  - lib/lab42/data_class/proxy/constraints/maker.rb
32
+ - lib/lab42/data_class/proxy/memos.rb
31
33
  - lib/lab42/data_class/proxy/mixin.rb
32
34
  - lib/lab42/data_class/proxy/validations.rb
33
35
  - lib/lab42/data_class/validation_error.rb
@@ -39,7 +41,7 @@ homepage: https://github.com/robertdober/lab42_data_class
39
41
  licenses:
40
42
  - Apache-2.0
41
43
  metadata: {}
42
- post_install_message:
44
+ post_install_message:
43
45
  rdoc_options: []
44
46
  require_paths:
45
47
  - lib
@@ -55,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
55
57
  version: '0'
56
58
  requirements: []
57
59
  rubygems_version: 3.3.3
58
- signing_key:
60
+ signing_key:
59
61
  specification_version: 4
60
62
  summary: Finally a dataclass in ruby
61
63
  test_files: []