strict 1.0.0 → 1.2.0

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.
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