strict 1.0.0 → 1.2.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: e55f68c10dc39769cdc9ae894efe2bbcd1a7ff0ce368b0b9b1d8251162cf1c6e
4
- data.tar.gz: a3454e769c6cd9578c3a5fbe4abd6edd6b0518affcd69d89249235df874875f0
3
+ metadata.gz: 82ad76d91d34f1bd4cc46ea38e4cef3c03c7d7e328f04d3dcc3c555da1eabdb4
4
+ data.tar.gz: 73f3ca6bc1d03caa42c23f61a3615ccb55285a1dcd962e978be3a87064abf8a3
5
5
  SHA512:
6
- metadata.gz: 06e647f606756054ece856881aff7de3b7c7fa50301c6b636af116b51ee8f975540b27bd6163391a18b8e8e40db911099e9dc7b1de7eacf38b239267b902287c
7
- data.tar.gz: '09130d2f947390b821c9357c3c17a371236f14c864bb42c8165ef651fedb06943e0d6ad6021ae451549cd368e0546e2e15dfd5a12d3ed090c76091cbe178795f'
6
+ metadata.gz: c442d7d203a993681c89694052856d125ab7c35965fb965ed64250a3eb83476069d844488d64798afc7ddc57b1edafbbbe0d6807b92b3c3a228bf9f3891beecd
7
+ data.tar.gz: 545e4e331d8ed3e578e72dbb52347bb34a0512fa899ca1da60f3181a2836d06a188658b224feed112a2e21464723833230b750628a1d837346566730375ce803
data/.rubocop.yml CHANGED
@@ -4,7 +4,7 @@ require:
4
4
 
5
5
  AllCops:
6
6
  NewCops: enable
7
- TargetRubyVersion: 3.1.2
7
+ TargetRubyVersion: 3.0.0
8
8
 
9
9
  Style/CaseEquality:
10
10
  Enabled: false
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.0.0
data/Gemfile.lock CHANGED
@@ -1,22 +1,22 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- strict (1.0.0)
4
+ strict (1.2.0)
5
5
  zeitwerk (~> 2.6)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  ast (2.4.2)
11
- debug (1.6.1)
11
+ debug (1.6.2)
12
12
  irb (>= 1.3.6)
13
13
  reline (>= 0.3.1)
14
14
  gem-release (2.2.2)
15
15
  io-console (0.5.11)
16
- irb (1.4.1)
16
+ irb (1.4.2)
17
17
  reline (>= 0.3.0)
18
18
  json (2.6.2)
19
- minitest (5.16.2)
19
+ minitest (5.16.3)
20
20
  minitest-spec-context (0.0.4)
21
21
  parallel (1.22.1)
22
22
  parser (3.1.2.1)
@@ -45,7 +45,7 @@ GEM
45
45
  rubocop (~> 1.0)
46
46
  ruby-progressbar (1.11.0)
47
47
  unicode-display_width (2.3.0)
48
- zeitwerk (2.6.0)
48
+ zeitwerk (2.6.1)
49
49
 
50
50
  PLATFORMS
51
51
  arm64-darwin-21
@@ -63,4 +63,4 @@ DEPENDENCIES
63
63
  strict!
64
64
 
65
65
  BUNDLED WITH
66
- 2.3.22
66
+ 2.3.23
data/README.md CHANGED
@@ -108,6 +108,71 @@ UpdateEmail.new.call(user_id: "123", email: "456")
108
108
  # => Strict::MethodReturnError
109
109
  ```
110
110
 
111
+ ### `Strict::Interface`
112
+
113
+ ```rb
114
+ class Storage
115
+ extend Strict::Interface
116
+
117
+ expose(:write) do
118
+ key String
119
+ contents String
120
+ returns Boolean()
121
+ end
122
+
123
+ expose(:read) do
124
+ key String
125
+ returns AnyOf(String, nil)
126
+ end
127
+ end
128
+
129
+ module Storages
130
+ class Memory
131
+ def initialize
132
+ @storage = {}
133
+ end
134
+
135
+ def write(key:, contents:)
136
+ storage[key] = contents
137
+ true
138
+ end
139
+
140
+ def read(key:)
141
+ storage[key]
142
+ end
143
+
144
+ private
145
+
146
+ attr_reader :storage
147
+ end
148
+ end
149
+
150
+ storage = Storage.new(Storages::Memory.new)
151
+ # => #<Storage implementation=#<Storages::Memory>>
152
+
153
+ storage.write(key: "some/path/to/file.rb", contents: "Hello")
154
+ # => true
155
+
156
+ storage.write(key: "some/path/to/file.rb", contents: {})
157
+ # => Strict::MethodCallError
158
+
159
+ storage.read(key: "some/path/to/file.rb")
160
+ # => "Hello"
161
+
162
+ storage.read(key: "some/path/to/other.rb")
163
+ # => nil
164
+
165
+ module Storages
166
+ class Wat
167
+ def write(key:)
168
+ end
169
+ end
170
+ end
171
+
172
+ storage = Storage.new(Storages::Wat.new)
173
+ # => Strict::ImplementationDoesNotConformError
174
+ ```
175
+
111
176
  ## Development
112
177
 
113
178
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -8,7 +8,7 @@ module Strict
8
8
  configuration = Strict::Attributes::Dsl.run(&block)
9
9
  include Module.new(configuration)
10
10
  include Strict::Attributes::Instance
11
- extend Strict::Attributes::Configured
11
+ extend Strict::Attributes::Class
12
12
  end
13
13
  end
14
14
  end
@@ -10,13 +10,13 @@ module Strict
10
10
  super()
11
11
 
12
12
  @configuration = configuration
13
- const_set(Strict::Attributes::Configured::CONSTANT, configuration)
13
+ const_set(Strict::Attributes::Class::CONSTANT, configuration)
14
14
  configuration.attributes.each do |attribute|
15
- module_eval(
16
- "def #{attribute.name} = #{attribute.instance_variable}", # def name = @instance_variable
17
- __FILE__,
18
- __LINE__ - 2
19
- )
15
+ module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
16
+ def #{attribute.name} # def name
17
+ #{attribute.instance_variable} # @instance_variable
18
+ end # end
19
+ RUBY
20
20
 
21
21
  module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
22
22
  def #{attribute.name}=(value) # def name=(value)
@@ -28,7 +28,7 @@ module Strict
28
28
  raise Strict::AssignmentError.new( # raise Strict::AssignmentError.new(
29
29
  assignable_class: self.class, # assignable_class: self.class,
30
30
  invalid_attribute: attribute, # invalid_attribute: attribute,
31
- value: # value:
31
+ value: value # value: value
32
32
  ) # )
33
33
  end # end
34
34
  end # end
@@ -5,7 +5,7 @@ module Strict
5
5
  attr_reader :invalid_attribute, :value
6
6
 
7
7
  def initialize(assignable_class:, invalid_attribute:, value:)
8
- super(message_from(assignable_class:, invalid_attribute:, value:))
8
+ super(message_from(assignable_class: assignable_class, invalid_attribute: invalid_attribute, value: value))
9
9
 
10
10
  @invalid_attribute = invalid_attribute
11
11
  @value = value
@@ -10,7 +10,12 @@ module Strict
10
10
  raise ArgumentError, "Only one of 'default', 'default_value', or 'default_generator' can be provided"
11
11
  end
12
12
 
13
- new(name: name.to_sym, validator:, default_generator: make_default_generator(**defaults), coercer: coerce)
13
+ new(
14
+ name: name.to_sym,
15
+ validator: validator,
16
+ default_generator: make_default_generator(**defaults),
17
+ coercer: coerce
18
+ )
14
19
  end
15
20
 
16
21
  private
@@ -2,12 +2,16 @@
2
2
 
3
3
  module Strict
4
4
  module Attributes
5
- module Configured
5
+ module Class
6
6
  CONSTANT = :STRICT_INTERNAL_ATTRIBUTES_CONFIGURATION__
7
7
 
8
8
  def strict_attributes
9
9
  self::STRICT_INTERNAL_ATTRIBUTES_CONFIGURATION__
10
10
  end
11
+
12
+ def coercer
13
+ Coercer.new(self)
14
+ end
11
15
  end
12
16
  end
13
17
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strict
4
+ module Attributes
5
+ class Coercer
6
+ attr_reader :attributes_class
7
+
8
+ def initialize(attributes_class)
9
+ @attributes_class = attributes_class
10
+ end
11
+
12
+ def call(value)
13
+ return value if value.nil? || !value.respond_to?(:to_h)
14
+
15
+ coerce(value.to_h)
16
+ end
17
+
18
+ private
19
+
20
+ NOT_PROVIDED = ::Object.new.freeze
21
+
22
+ def coerce(hash)
23
+ attributes_class.new(
24
+ **attributes_class.strict_attributes.each_with_object({}) do |attribute, attributes|
25
+ value = hash.fetch(attribute.name) { hash.fetch(attribute.name.to_s, NOT_PROVIDED) }
26
+ attributes[attribute.name] = value unless value.equal?(NOT_PROVIDED)
27
+ end
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
@@ -12,7 +12,7 @@ module Strict
12
12
  attr_reader :attribute_name
13
13
 
14
14
  def initialize(attribute_name:)
15
- super(message_from(attribute_name:))
15
+ super(message_from(attribute_name: attribute_name))
16
16
 
17
17
  @attribute_name = attribute_name
18
18
  end
@@ -4,13 +4,14 @@ module Strict
4
4
  module Attributes
5
5
  class Dsl < BasicObject
6
6
  class << self
7
- def run(&)
7
+ def run(&block)
8
8
  dsl = new
9
- dsl.instance_eval(&)
9
+ dsl.instance_eval(&block)
10
10
  ::Strict::Attributes::Configuration.new(attributes: dsl.__strict_dsl_internal_attributes.values)
11
11
  end
12
12
  end
13
13
 
14
+ include ::Strict::Dsl::Coercible
14
15
  include ::Strict::Dsl::Validatable
15
16
 
16
17
  attr_reader :__strict_dsl_internal_attributes
@@ -33,9 +33,9 @@ module Strict
33
33
 
34
34
  raise InitializationError.new(
35
35
  initializable_class: self.class,
36
- remaining_attributes:,
37
- invalid_attributes:,
38
- missing_attributes:
36
+ remaining_attributes: remaining_attributes,
37
+ invalid_attributes: invalid_attributes,
38
+ missing_attributes: missing_attributes
39
39
  )
40
40
  end
41
41
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strict
4
+ module Coercers
5
+ class Array
6
+ attr_reader :element_coercer
7
+
8
+ def initialize(element_coercer = nil)
9
+ @element_coercer = element_coercer
10
+ end
11
+
12
+ def call(value)
13
+ return value if value.nil? || !value.respond_to?(:to_a)
14
+
15
+ array = value.to_a
16
+ return array unless element_coercer
17
+
18
+ array.map { |element| element_coercer.call(element) }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strict
4
+ module Coercers
5
+ class Hash
6
+ attr_reader :key_coercer, :value_coercer
7
+
8
+ def initialize(key_coercer = nil, value_coercer = nil)
9
+ @key_coercer = key_coercer
10
+ @value_coercer = value_coercer
11
+ end
12
+
13
+ def call(value)
14
+ return value if value.nil? || !value.respond_to?(:to_h)
15
+
16
+ if key_coercer && value_coercer
17
+ coerce_keys_and_values(value.to_h)
18
+ elsif key_coercer
19
+ coerce_keys(value.to_h)
20
+ elsif value_coercer
21
+ coerce_values(value.to_h)
22
+ else
23
+ value.to_h
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def coerce_keys_and_values(hash) = hash.to_h { |key, value| [key_coercer.call(key), value_coercer.call(value)] }
30
+ def coerce_keys(hash) = hash.transform_keys { |key| key_coercer.call(key) }
31
+ def coerce_values(hash) = hash.transform_values { |value| value_coercer.call(value) }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strict
4
+ module Dsl
5
+ module Coercible
6
+ # rubocop:disable Naming/MethodName
7
+
8
+ def ToArray(with: nil) = ::Strict::Coercers::Array.new(with)
9
+ def ToHash(with_keys: nil, with_values: nil) = ::Strict::Coercers::Hash.new(with_keys, with_values)
10
+
11
+ # rubocop:enable Naming/MethodName
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strict
4
+ class ImplementationDoesNotConformError < Error
5
+ attr_reader :interface, :receiver, :missing_methods, :invalid_method_definitions
6
+
7
+ def initialize(interface:, receiver:, missing_methods:, invalid_method_definitions:) # rubocop:disable Metrics/MethodLength
8
+ super(
9
+ message_from(
10
+ interface: interface,
11
+ receiver: receiver,
12
+ missing_methods: missing_methods,
13
+ invalid_method_definitions: invalid_method_definitions
14
+ )
15
+ )
16
+
17
+ @interface = interface
18
+ @receiver = receiver
19
+ @missing_methods = missing_methods
20
+ @invalid_method_definitions = invalid_method_definitions
21
+ end
22
+
23
+ private
24
+
25
+ def message_from(interface:, receiver:, missing_methods:, invalid_method_definitions:)
26
+ details = [
27
+ missing_methods_message_from(missing_methods),
28
+ invalid_method_definitions_message_from(invalid_method_definitions)
29
+ ].compact.join("\n")
30
+
31
+ case receiver
32
+ when ::Class, ::Module
33
+ "#{receiver}'s implementation of #{interface} does not conform:\n#{details}"
34
+ else
35
+ "#{receiver.class}'s implementation of #{interface} does not conform:\n#{details}"
36
+ end
37
+ end
38
+
39
+ def missing_methods_message_from(missing_methods)
40
+ return nil unless missing_methods
41
+
42
+ details = missing_methods.map do |method_name|
43
+ " - #{method_name}"
44
+ end.join("\n")
45
+
46
+ " Some methods exposed in the interface were not defined in the implementation:\n#{details}"
47
+ end
48
+
49
+ def invalid_method_definitions_message_from(invalid_method_definitions)
50
+ return nil if invalid_method_definitions.empty?
51
+
52
+ methods_details = invalid_method_definitions.map do |name, invalid_method_definition|
53
+ method_details = [
54
+ missing_parameters_message_from(invalid_method_definition.fetch(:missing_parameters)),
55
+ additional_parameters_message_from(invalid_method_definition.fetch(:additional_parameters)),
56
+ non_keyword_parameters_message_from(invalid_method_definition.fetch(:non_keyword_parameters))
57
+ ].compact.join("\n")
58
+
59
+ " #{name}:\n#{method_details}"
60
+ end.join("\n")
61
+
62
+ " Some methods defined in the implementation did not conform to their interface:\n#{methods_details}"
63
+ end
64
+
65
+ def missing_parameters_message_from(missing_parameters)
66
+ return nil unless missing_parameters.any?
67
+
68
+ details = missing_parameters.map do |parameter_name|
69
+ " - #{parameter_name}"
70
+ end.join("\n")
71
+
72
+ " Some parameters were expected, but were not in the parameter list:\n#{details}"
73
+ end
74
+
75
+ def additional_parameters_message_from(additional_parameters)
76
+ return nil unless additional_parameters.any?
77
+
78
+ details = additional_parameters.map do |parameter_name|
79
+ " - #{parameter_name}"
80
+ end.join("\n")
81
+
82
+ " Some parameters were not expected, but were in the parameter list:\n#{details}"
83
+ end
84
+
85
+ def non_keyword_parameters_message_from(non_keyword_parameters)
86
+ return nil unless non_keyword_parameters.any?
87
+
88
+ details = non_keyword_parameters.map do |parameter_name|
89
+ " - #{parameter_name}"
90
+ end.join("\n")
91
+
92
+ " Some parameters were not keywords, but only keywords are supported:\n#{details}"
93
+ end
94
+ end
95
+ end
@@ -4,8 +4,15 @@ module Strict
4
4
  class InitializationError < Error
5
5
  attr_reader :remaining_attributes, :invalid_attributes, :missing_attributes
6
6
 
7
- def initialize(initializable_class:, remaining_attributes:, invalid_attributes:, missing_attributes:)
8
- super(message_from(initializable_class:, remaining_attributes:, invalid_attributes:, missing_attributes:))
7
+ def initialize(initializable_class:, remaining_attributes:, invalid_attributes:, missing_attributes:) # rubocop:disable Metrics/MethodLength
8
+ super(
9
+ message_from(
10
+ initializable_class: initializable_class,
11
+ remaining_attributes: remaining_attributes,
12
+ invalid_attributes: invalid_attributes,
13
+ missing_attributes: missing_attributes
14
+ )
15
+ )
9
16
 
10
17
  @remaining_attributes = remaining_attributes
11
18
  @invalid_attributes = invalid_attributes
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strict
4
+ module Interface
5
+ def self.extended(mod)
6
+ mod.extend(Strict::Method)
7
+ mod.include(Interfaces::Instance)
8
+ end
9
+
10
+ def expose(method_name, &block)
11
+ sig = sig(&block)
12
+ parameter_list = sig.parameters.map { |parameter| "#{parameter.name}:" }.join(", ")
13
+ argument_list = sig.parameters.map { |parameter| "#{parameter.name}: #{parameter.name}" }.join(", ")
14
+
15
+ module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
16
+ def #{method_name}(#{parameter_list}, &block) # def method_name(one:, two:, three:, &block)
17
+ implementation.#{method_name}(#{argument_list}, &block) # implementation.method_name(one: one, two: two, three: three, &block)
18
+ end # end
19
+ RUBY
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Strict
4
+ module Interfaces
5
+ module Instance
6
+ attr_reader :implementation
7
+
8
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
9
+ def initialize(implementation)
10
+ missing_methods = nil
11
+ invalid_method_definitions = Hash.new do |h, k|
12
+ h[k] = { additional_parameters: [], missing_parameters: [], non_keyword_parameters: [] }
13
+ end
14
+
15
+ self.class.strict_instance_methods.each do |method_name, strict_method|
16
+ unless implementation.respond_to?(method_name)
17
+ missing_methods ||= []
18
+ missing_methods << method_name
19
+ next
20
+ end
21
+
22
+ expected_parameters = Set.new(strict_method.parameters.map(&:name))
23
+ defined_parameters = Set.new
24
+
25
+ implementation.method(method_name).parameters.each do |kind, parameter_name|
26
+ next if kind == :block
27
+
28
+ if expected_parameters.include?(parameter_name)
29
+ defined_parameters.add(parameter_name)
30
+ invalid_method_definitions[method_name][:non_keyword_parameters] << parameter_name if kind != :keyreq
31
+ else
32
+ invalid_method_definitions[method_name][:additional_parameters] << parameter_name
33
+ end
34
+ end
35
+
36
+ missing_parameters = expected_parameters - defined_parameters
37
+ invalid_method_definitions[method_name][:missing_parameters] = missing_parameters if missing_parameters.any?
38
+ end
39
+
40
+ if missing_methods || !invalid_method_definitions.empty?
41
+ raise Strict::ImplementationDoesNotConformError.new(
42
+ interface: self.class,
43
+ receiver: implementation,
44
+ missing_methods: missing_methods,
45
+ invalid_method_definitions: invalid_method_definitions
46
+ )
47
+ end
48
+
49
+ @implementation = implementation
50
+ end
51
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
52
+ end
53
+ end
54
+ end
data/lib/strict/method.rb CHANGED
@@ -8,9 +8,27 @@ module Strict
8
8
  mod.singleton_class.extend(self)
9
9
  end
10
10
 
11
- def sig(&)
11
+ def sig(&block)
12
12
  instance = singleton_class? ? self : singleton_class
13
- instance.instance_variable_set(:@__strict_method_internal_last_sig_configuration, Methods::Dsl.run(&))
13
+ instance.instance_variable_set(:@__strict_method_internal_last_sig_configuration, Methods::Dsl.run(&block))
14
+ end
15
+
16
+ def strict_class_methods
17
+ instance = singleton_class? ? self : singleton_class
18
+ if instance.instance_variable_defined?(:@__strict_method_internal_class_methods)
19
+ instance.instance_variable_get(:@__strict_method_internal_class_methods)
20
+ else
21
+ instance.instance_variable_set(:@__strict_method_internal_class_methods, {})
22
+ end
23
+ end
24
+
25
+ def strict_instance_methods
26
+ instance = singleton_class? ? self : singleton_class
27
+ if instance.instance_variable_defined?(:@__strict_method_internal_instance_methods)
28
+ instance.instance_variable_get(:@__strict_method_internal_instance_methods)
29
+ else
30
+ instance.instance_variable_set(:@__strict_method_internal_instance_methods, {})
31
+ end
14
32
  end
15
33
 
16
34
  # rubocop:disable Metrics/MethodLength
@@ -28,6 +46,7 @@ module Strict
28
46
  instance: false
29
47
  )
30
48
  verifiable_method.verify_definition!
49
+ strict_class_methods[method_name] = verifiable_method
31
50
  singleton_class.prepend(Methods::Module.new(verifiable_method))
32
51
  end
33
52
 
@@ -45,6 +64,7 @@ module Strict
45
64
  instance: true
46
65
  )
47
66
  verifiable_method.verify_definition!
67
+ strict_instance_methods[method_name] = verifiable_method
48
68
  prepend(Methods::Module.new(verifiable_method))
49
69
  end
50
70
  # rubocop:enable Metrics/MethodLength
@@ -4,9 +4,15 @@ module Strict
4
4
  class MethodCallError < Error
5
5
  attr_reader :verifiable_method, :remaining_args, :remaining_kwargs, :invalid_parameters, :missing_parameters
6
6
 
7
- def initialize(verifiable_method:, remaining_args:, remaining_kwargs:, invalid_parameters:, missing_parameters:)
7
+ def initialize(verifiable_method:, remaining_args:, remaining_kwargs:, invalid_parameters:, missing_parameters:) # rubocop:disable Metrics/MethodLength
8
8
  super(
9
- message_from(verifiable_method:, remaining_args:, remaining_kwargs:, invalid_parameters:, missing_parameters:)
9
+ message_from(
10
+ verifiable_method: verifiable_method,
11
+ remaining_args: remaining_args,
12
+ remaining_kwargs: remaining_kwargs,
13
+ invalid_parameters: invalid_parameters,
14
+ missing_parameters: missing_parameters
15
+ )
10
16
  )
11
17
 
12
18
  @verifiable_method = verifiable_method
@@ -5,7 +5,13 @@ module Strict
5
5
  attr_reader :verifiable_method, :missing_parameters, :additional_parameters
6
6
 
7
7
  def initialize(verifiable_method:, missing_parameters:, additional_parameters:)
8
- super(message_from(verifiable_method:, missing_parameters:, additional_parameters:))
8
+ super(
9
+ message_from(
10
+ verifiable_method: verifiable_method,
11
+ missing_parameters: missing_parameters,
12
+ additional_parameters: additional_parameters
13
+ )
14
+ )
9
15
 
10
16
  @verifiable_method = verifiable_method
11
17
  @missing_parameters = missing_parameters
@@ -30,7 +36,7 @@ module Strict
30
36
  " - #{parameter_name}"
31
37
  end.join("\n")
32
38
 
33
- " Some parameters were in the `sig`, but were not in the parameter list:\n#{details}"
39
+ " Some parameters were in the sig, but were not in the parameter list:\n#{details}"
34
40
  end
35
41
 
36
42
  def additional_parameters_message_from(additional_parameters)
@@ -40,7 +46,7 @@ module Strict
40
46
  " - #{parameter_name}"
41
47
  end.join("\n")
42
48
 
43
- " Some parameters were not in the `sig`, but were in the parameter list:\n#{details}"
49
+ " Some parameters were not in the sig, but were in the parameter list:\n#{details}"
44
50
  end
45
51
  end
46
52
  end
@@ -5,7 +5,7 @@ module Strict
5
5
  attr_reader :verifiable_method, :value
6
6
 
7
7
  def initialize(verifiable_method:, value:)
8
- super(message_from(verifiable_method:, value:))
8
+ super(message_from(verifiable_method: verifiable_method, value: value))
9
9
 
10
10
  @verifiable_method = verifiable_method
11
11
  @value = value
@@ -4,9 +4,9 @@ module Strict
4
4
  module Methods
5
5
  class Dsl < BasicObject
6
6
  class << self
7
- def run(&)
7
+ def run(&block)
8
8
  dsl = new
9
- dsl.instance_eval(&)
9
+ dsl.instance_eval(&block)
10
10
  ::Strict::Methods::Configuration.new(
11
11
  parameters: dsl.__strict_dsl_internal_parameters.values,
12
12
  returns: dsl.__strict_dsl_internal_returns
@@ -14,6 +14,7 @@ module Strict
14
14
  end
15
15
  end
16
16
 
17
+ include ::Strict::Dsl::Coercible
17
18
  include ::Strict::Dsl::Validatable
18
19
 
19
20
  attr_reader :__strict_dsl_internal_parameters, :__strict_dsl_internal_returns
@@ -11,7 +11,7 @@ module Strict
11
11
  attr_reader :parameter_name
12
12
 
13
13
  def initialize(parameter_name:)
14
- super(message_from(parameter_name:))
14
+ super(message_from(parameter_name: parameter_name))
15
15
 
16
16
  @parameter_name = parameter_name
17
17
  end
@@ -47,7 +47,11 @@ module Strict
47
47
 
48
48
  missing_parameters = expected_parameters - defined_parameters
49
49
  additional_parameters = defined_parameters - expected_parameters
50
- raise Strict::MethodDefinitionError.new(verifiable_method: self, missing_parameters:, additional_parameters:)
50
+ raise Strict::MethodDefinitionError.new(
51
+ verifiable_method: self,
52
+ missing_parameters: missing_parameters,
53
+ additional_parameters: additional_parameters
54
+ )
51
55
  end
52
56
 
53
57
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/BlockLength
@@ -113,8 +117,8 @@ module Strict
113
117
  verifiable_method: self,
114
118
  remaining_args: args,
115
119
  remaining_kwargs: kwargs,
116
- invalid_parameters:,
117
- missing_parameters:
120
+ invalid_parameters: invalid_parameters,
121
+ missing_parameters: missing_parameters
118
122
  )
119
123
  end
120
124
  end
@@ -124,7 +128,7 @@ module Strict
124
128
  value = returns.coerce(value)
125
129
  return if returns.valid?(value)
126
130
 
127
- raise Strict::MethodReturnError.new(verifiable_method: self, value:)
131
+ raise Strict::MethodReturnError.new(verifiable_method: self, value: value)
128
132
  end
129
133
 
130
134
  private
@@ -10,7 +10,12 @@ module Strict
10
10
  raise ArgumentError, "Only one of 'default', 'default_value', or 'default_generator' can be provided"
11
11
  end
12
12
 
13
- new(name: name.to_sym, validator:, default_generator: make_default_generator(**defaults), coercer: coerce)
13
+ new(
14
+ name: name.to_sym,
15
+ validator: validator,
16
+ default_generator: make_default_generator(**defaults),
17
+ coercer: coerce
18
+ )
14
19
  end
15
20
 
16
21
  private
@@ -8,7 +8,7 @@ module Strict
8
8
  configuration = Strict::Attributes::Dsl.run(&block)
9
9
  include Module.new(configuration)
10
10
  include Strict::Attributes::Instance
11
- extend Strict::Attributes::Configured
11
+ extend Strict::Attributes::Class
12
12
  end
13
13
  end
14
14
  end
@@ -9,7 +9,7 @@ module Strict
9
9
  super()
10
10
 
11
11
  @configuration = configuration
12
- const_set(Strict::Attributes::Configured::CONSTANT, configuration)
12
+ const_set(Strict::Attributes::Class::CONSTANT, configuration)
13
13
  configuration.attributes.each do |attribute|
14
14
  module_eval(
15
15
  "def #{attribute.name} = #{attribute.instance_variable}", # def name = @instance_variable
data/lib/strict/return.rb CHANGED
@@ -4,7 +4,7 @@ module Strict
4
4
  class Return
5
5
  class << self
6
6
  def make(validator = Validators::Anything.instance, coerce: false)
7
- new(validator:, coercer: coerce)
7
+ new(validator: validator, coercer: coerce)
8
8
  end
9
9
  end
10
10
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Strict
4
- VERSION = "1.0.0"
4
+ VERSION = "1.2.0"
5
5
  end
data/strict.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.summary = "Strictly define a contract for your objects and methods"
12
12
  spec.homepage = "https://github.com/kylekthompson/strict"
13
13
  spec.license = "MIT"
14
- spec.required_ruby_version = ">= 3.1.2"
14
+ spec.required_ruby_version = ">= 3.0.0"
15
15
 
16
16
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
17
17
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strict
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyle Thompson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-12 00:00:00.000000000 Z
11
+ date: 2022-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -144,6 +144,7 @@ extensions: []
144
144
  extra_rdoc_files: []
145
145
  files:
146
146
  - ".rubocop.yml"
147
+ - ".tool-versions"
147
148
  - CHANGELOG.md
148
149
  - CODE_OF_CONDUCT.md
149
150
  - Gemfile
@@ -156,13 +157,20 @@ files:
156
157
  - lib/strict/accessor/module.rb
157
158
  - lib/strict/assignment_error.rb
158
159
  - lib/strict/attribute.rb
160
+ - lib/strict/attributes/class.rb
161
+ - lib/strict/attributes/coercer.rb
159
162
  - lib/strict/attributes/configuration.rb
160
- - lib/strict/attributes/configured.rb
161
163
  - lib/strict/attributes/dsl.rb
162
164
  - lib/strict/attributes/instance.rb
165
+ - lib/strict/coercers/array.rb
166
+ - lib/strict/coercers/hash.rb
167
+ - lib/strict/dsl/coercible.rb
163
168
  - lib/strict/dsl/validatable.rb
164
169
  - lib/strict/error.rb
170
+ - lib/strict/implementation_does_not_conform_error.rb
165
171
  - lib/strict/initialization_error.rb
172
+ - lib/strict/interface.rb
173
+ - lib/strict/interfaces/instance.rb
166
174
  - lib/strict/method.rb
167
175
  - lib/strict/method_call_error.rb
168
176
  - lib/strict/method_definition_error.rb
@@ -204,14 +212,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
204
212
  requirements:
205
213
  - - ">="
206
214
  - !ruby/object:Gem::Version
207
- version: 3.1.2
215
+ version: 3.0.0
208
216
  required_rubygems_version: !ruby/object:Gem::Requirement
209
217
  requirements:
210
218
  - - ">="
211
219
  - !ruby/object:Gem::Version
212
220
  version: '0'
213
221
  requirements: []
214
- rubygems_version: 3.3.7
222
+ rubygems_version: 3.2.3
215
223
  signing_key:
216
224
  specification_version: 4
217
225
  summary: Strictly define a contract for your objects and methods