re_sorcery 0.1.0 → 0.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: 0b2890e5efcd2dbd01d5f849d9ba9887ac5c78077057b6406ae458ef3c048337
4
- data.tar.gz: 3971c5c49f0c6d6bc0ac407b6b79091e1e2e9d16fa7d80c7deab0e12d3a94954
3
+ metadata.gz: 37fc462892cc20c02ed1a1dd5d3aaeaf8712bfd2708cccdfbe026944015adc66
4
+ data.tar.gz: ac6f04c6ad4992e8d0babcb9de137702eb62ec489fa336c708e9b4a5700e2c99
5
5
  SHA512:
6
- metadata.gz: 6281d8f26287f854baccd61aa8e800dd27720053262b99380c5f2a26b4336b8bef238f7073722f8789d4da9b84f17a021df2e74030b64be995425c6cf5ae9f70
7
- data.tar.gz: 546cc421b749cd28393494f10190bd34e9e7ed1ff5170b91a07724fccc94ece8351e03fc3d672e98e3c1e9908aa21abbdf91fc8eb86d0db2426ea9131eb7f0dd
6
+ metadata.gz: 833577165a1a176f7b3237d59f8bf55339dfc3709bab21a20ce3d463385ab6a98b1184dbb7bb3eb872d8f7f299072050caf0f2fe1f98bedb4744d522c175b343
7
+ data.tar.gz: e65b27a43d44d32d0ca27b13e1f058f3972448353874f50aecf259c9378644e2fbd781aa21a192c554d8ebf4b110645b03a6058407dde36db97cea48a8561e12
data/Gemfile.lock CHANGED
@@ -1,28 +1,32 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- re_sorcery (0.1.0)
4
+ re_sorcery (0.2.0)
5
+ addressable (~> 2.8, >= 2.8.1)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ addressable (2.8.1)
11
+ public_suffix (>= 2.0.2, < 6.0)
9
12
  coderay (1.1.3)
10
13
  method_source (1.0.0)
11
- minitest (5.14.4)
12
- pry (0.14.1)
14
+ minitest (5.17.0)
15
+ pry (0.14.2)
13
16
  coderay (~> 1.1)
14
17
  method_source (~> 1.0)
15
- rake (10.5.0)
18
+ public_suffix (5.0.1)
19
+ rake (13.0.6)
16
20
 
17
21
  PLATFORMS
18
- ruby
22
+ arm64-darwin-21
19
23
 
20
24
  DEPENDENCIES
21
- bundler (~> 2.0)
22
- re_sorcery!
23
- minitest (~> 5.0)
25
+ bundler (~> 2.3)
26
+ minitest (~> 5.16)
24
27
  pry
25
- rake (~> 10.0)
28
+ rake (~> 13.0)
29
+ re_sorcery!
26
30
 
27
31
  BUNDLED WITH
28
- 2.0.2
32
+ 2.3.7
data/README.md CHANGED
@@ -15,11 +15,14 @@ and link validation before sending resources to the client.
15
15
  `Array` of `<link>` objects).
16
16
  - Each entry in the `:payload` is type checked using a `Decoder` (inspired by [Elm's Decoders]).
17
17
  - A `<link>` is a `Hash` with four fields:
18
- - `:href`, which is either a `URI` or a `String` that parses as a valid `URI`;
18
+ - `:href`, which is either a `URI` or a `String` that parses as a valid `URI` ([Addressable] is
19
+ used for URI-parsing, as it handles URI templates.)
19
20
  - `:rel`, which is a white-listed `String`;
20
21
  - `:method`, which is also a white-listed `String`; and
21
22
  - `:type`, which is a `String`.
22
23
 
24
+ [Addressable]: https://github.com/sporkmonger/addressable
25
+
23
26
  Demo:
24
27
 
25
28
  ```ruby
@@ -33,12 +36,17 @@ class StaticResource
33
36
  end
34
37
  end
35
38
 
36
- StaticResource.new.resource
37
- # #<ReSorcery::Result::Ok @value={
38
- # :payload=>{:string=>"a string", :number=>42},
39
- # :links=>[{:rel=>"self", :href=>"/here", :method=>"get", :type=>"application/json"},
40
- # {:rel=>"create", :href=>"/here", :method=>"post", :type=>"application/json"}]
41
- # }>
39
+ StaticResource.new.as_json
40
+ # {
41
+ # :payload=>{
42
+ # :string=>"a string",
43
+ # :number=>42
44
+ # },
45
+ # :links=>[
46
+ # {:rel=>"self", :href=>"/here", :method=>"get", :type=>"application/json"},
47
+ # {:rel=>"create", :href=>"/here", :method=>"post", :type=>"application/json"}
48
+ # ]
49
+ # }
42
50
  ```
43
51
 
44
52
  ## Installation
@@ -90,8 +98,8 @@ User.new(name: "Spencer", id: 1, admin: true).as_json #=> {
90
98
 
91
99
  # An invalid `name` raises an error when calling `as_json`
92
100
  User.new(name: :Invalid, id: 1).as_json
93
- # ReSorcery::Error::InvalidResourceError: Error at field `name` of `User`: Expected a(n) String, but
94
- # got a(n) Symbol
101
+ # ReSorcery::Error::InvalidResourceError:
102
+ # Error at field `name` of `User`: Expected kind String, but got Symbol
95
103
  ```
96
104
 
97
105
  The implementation of `ReSorcery#as_json` raises an exception on invalid data, instead of serving
@@ -14,26 +14,46 @@ module ReSorcery
14
14
  # link_methods ['get', 'post', 'put']
15
15
  # end
16
16
  #
17
- # @see `Configuration::CONFIGURABLES` for a list of what can be configured and
18
- # what value each configurable takes.
17
+ # @see `Configuration::CONFIGURABLES`, whose keys define the list of
18
+ # configuration methods. Each entry contains a `:decoder` key, which is used
19
+ # to check the value passed to the configuration method, and a `:default`
20
+ # key, which is the default value for that entry.
19
21
  #
20
22
  module Configuration
21
23
  extend Decoder::BuiltinDecoders
22
24
 
25
+ UNIQUE_STRING_OR_SYMBOL = non_empty_array(is(String, Symbol).map(&:to_s)).map(&:uniq)
26
+ DEFAULT_LINK_METHOD_DECODER = is(Proc)
27
+ .and { |p| p.arity == 1 || "default_link_method Proc must accept exactly one argument" }
28
+
23
29
  CONFIGURABLES = {
24
- link_rels: array(String),
25
- link_methods: array(String),
30
+ link_rels: {
31
+ decoder: UNIQUE_STRING_OR_SYMBOL,
32
+ default: %w[self create update destroy].freeze,
33
+ }.freeze,
34
+ link_methods: {
35
+ decoder: UNIQUE_STRING_OR_SYMBOL,
36
+ default: %w[get post patch put delete].freeze,
37
+ }.freeze,
38
+ default_link_method: {
39
+ decoder: DEFAULT_LINK_METHOD_DECODER,
40
+ default: ->(link_methods) { link_methods.first }.freeze,
41
+ }.freeze,
42
+ default_link_type: {
43
+ decoder: is(String),
44
+ default: "application/json",
45
+ }.freeze,
26
46
  }.freeze
27
47
 
28
48
  def configuration
29
- @configuration ||= {}
49
+ @configuration ||= CONFIGURABLES.transform_values { |v| v.fetch(:default) }
30
50
  end
31
51
 
32
- def configure(&block)
52
+ def configure(&)
33
53
  raise Error::InvalidConfigurationError, @configured if configured?
34
54
 
35
55
  @configured = "configured at #{caller_locations.first}"
36
- instance_exec(&block)
56
+ instance_exec(&)
37
57
  end
38
58
 
39
59
  private
@@ -42,7 +62,8 @@ module ReSorcery
42
62
  @configured ||= false
43
63
  end
44
64
 
45
- CONFIGURABLES.each do |name, decoder|
65
+ CONFIGURABLES.each do |name, metadata|
66
+ decoder = metadata.fetch(:decoder)
46
67
  define_method(name) do |value|
47
68
  decoder.test(value).cata(
48
69
  ok: ->(v) { configuration[name] = v },
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  module ReSorcery
4
6
  class Decoder
5
7
  # Common decoders implemented here for convenience
@@ -9,17 +11,60 @@ module ReSorcery
9
11
  private
10
12
 
11
13
  # A Decoder that always succeeds with a given value
14
+ #
15
+ # Useful for starting decoder chains that build up successful values.
12
16
  def succeed(value)
13
17
  Decoder.new { ok(value) }
14
18
  end
15
19
 
16
20
  # A decoder that always fails with some error message
21
+ #
22
+ # Useful for starting decoder chains that search for a successful value.
17
23
  def fail(message = '`fail` Decoder always fails')
18
24
  ArgCheck['message', message, String]
19
25
 
20
26
  Decoder.new { message }
21
27
  end
22
28
 
29
+ # Test if an object is_a?(first_mod) (or is one of a list of modules)
30
+ #
31
+ # Note that classes are also modules.
32
+ #
33
+ # In general prefer `#is`.
34
+ #
35
+ # @param [Module] first_mod
36
+ # @param [Array<Module>] others
37
+ # @return [Decoder]
38
+ def mod(first_mod, *others)
39
+ all_mods = ([first_mod] + others).map.with_index { |o, i| ArgCheck["mods[#{i}]", o, Module] }
40
+ raise ReSorcery::Error::ArgumentError, "Do not use `nil`" if all_mods.include?(NilClass)
41
+
42
+ mods = all_mods.select { |m| ((m.ancestors - [m]) & all_mods).empty? }
43
+
44
+ msg = lambda {
45
+ prefix = mods.count == 1 ? "Expected" : "Expected one of"
46
+ "#{prefix} (#{mods.join(' | ')}) but received"
47
+ }
48
+
49
+ Decoder.new { |u| mods.any? { |m| u.is_a?(m) } || "#{msg.call}: #{u.class}" }
50
+ end
51
+
52
+ # Test if an object is one of a specific set of things
53
+ #
54
+ # In general prefer `#is`.
55
+ def one_of_exactly(thing, *others)
56
+ things = ([thing] + others).to_set
57
+ nillish = things.include?(nil)
58
+ raise ReSorcery::Error::ArgumentError, "Do not use `nil`" if nillish
59
+
60
+ msg = lambda {
61
+ prefix = things.count == 1 ? "Expected exactly" : "Expected one of exactly"
62
+ "#{prefix} (#{things.map(&:inspect).join(' | ')}) but received"
63
+ }
64
+
65
+ Decoder.new { |u| things.include?(u) || "#{msg.call}: #{u.inspect}" }
66
+ end
67
+
23
68
  # Test if an object is a thing (or one of a list of things)
24
69
  #
25
70
  # Convenience method for creating common types of `Decoder`s.
@@ -44,7 +89,11 @@ module ReSorcery
44
89
  # @return [Decoder]
45
90
  def is(thing, *others)
46
91
  things = [thing] + others
47
- decoders = things.map { |t| make_decoder_from(t) }
92
+ groups = things.group_by { |t| [t.is_a?(Decoder), t.is_a?(Module)] }
93
+ decoders =
94
+ groups.fetch([true, false], []) +
95
+ (groups[[false, true]].nil? ? [] : [mod(*groups[[false, true]])]) +
96
+ (groups[[false, false]].nil? ? [] : [one_of_exactly(*groups[[false, false]])])
48
97
 
49
98
  Decoder.new do |instance|
50
99
  test_multiple(decoders, instance).map_error do |errors|
@@ -73,6 +122,19 @@ module ReSorcery
73
122
  end
74
123
  end
75
124
 
125
+ # Like #array, but test the array contains at least one item
126
+ #
127
+ # @param thing @see `is` for details
128
+ # @param others @see `is` for details
129
+ # @return [Decoder]
130
+ def non_empty_array(thing, *others)
131
+ array(thing, *others).and_then do |a|
132
+ Decoder.new do
133
+ a.empty? ? "Expected a non-empty array, but received an empty array" : ok(a)
134
+ end
135
+ end
136
+ end
137
+
76
138
  # Test that an object is a Maybe whose `value` passes some `Decoder`
77
139
  #
78
140
  # @param thing @see `is` for details
@@ -42,6 +42,7 @@ module ReSorcery
42
42
 
43
43
  def initialize(&block)
44
44
  @block = block
45
+ freeze
45
46
  end
46
47
 
47
48
  # Use the decoder to `test` that an `unknown` object satisfies some property
@@ -51,15 +52,15 @@ module ReSorcery
51
52
  # @param [unknown] unknown
52
53
  # @return [Result]
53
54
  def test(unknown)
54
- result = @block.call(unknown)
55
- case result
55
+ block_result = @block.call(unknown)
56
+ case block_result
56
57
  when Result::Ok, Result::Err
57
- result
58
+ block_result
58
59
  when TrueClass
59
60
  ok(unknown)
60
61
  else
61
- err(result)
62
- end.and_then { |r| r.nil? ? err("`nil` was returned on a successful test!") : ok(r) }
62
+ err(block_result)
63
+ end
63
64
  end
64
65
 
65
66
  # Apply some block within the context of a successful decoder
@@ -77,6 +78,14 @@ module ReSorcery
77
78
  # The second decoder can be chosen based on the (successful) result of the
78
79
  # first decoder.
79
80
  #
81
+ # Note: the second decoder decodes the "original" unknown value, not the
82
+ # potentially-changed result of the first decoder.
83
+ #
84
+ # Decoder.new { |u| u.is_a?(Symbol) || "Not symbol" }
85
+ # .map(&:to_s)
86
+ # .and_then { |v| Decoder.new { |u| true } } # v is a String & u is a Symbol
87
+ # .test(:a_symbol)
88
+ #
80
89
  # The block must return a `Decoder`.
81
90
  def and_then(decoder = nil, &block)
82
91
  ArgCheck['decoder', decoder, Decoder] unless decoder.nil?
@@ -89,6 +98,24 @@ module ReSorcery
89
98
  end
90
99
  end
91
100
 
101
+ # Chain decoders and maintain any transformed value
102
+ #
103
+ # pos = is(String, Symbol)
104
+ # .map(&:to_sym)
105
+ # .and { |s, u| [:a, :b, :c].include?(s) || "Invalid option: #{u.inspect}" }
106
+ #
107
+ # pos.test(:a) == ok(:a)
108
+ # pos.test("a") == ok(:a)
109
+ # pos.test("d") == err('Invalid option: "d"')
110
+ #
111
+ def and
112
+ and_then do |value|
113
+ Decoder.new do |unknown|
114
+ Decoder.new { yield(value, unknown) }.test(value)
115
+ end
116
+ end
117
+ end
118
+
92
119
  # If the `Decoder` failed, try another `Decoder`
93
120
  #
94
121
  # The block must return a `Decoder`.
@@ -97,18 +124,11 @@ module ReSorcery
97
124
 
98
125
  Decoder.new do |unknown|
99
126
  test(unknown).or_else do |value|
100
- decoder ||= ArgCheck['block.call(value)', block.call(value), Decoder]
101
-
102
- decoder.test(unknown)
127
+ (decoder || ArgCheck['block.call(value)', block.call(value), Decoder]).test(unknown)
103
128
  end
104
129
  end
105
130
  end
106
131
 
107
- # Chain decoders like `and_then`, but always with a specific decoder
108
- def and(&block)
109
- and_then { Decoder.new(&block) }
110
- end
111
-
112
132
  # Chain decoders like and_then, but use the chain to build an object
113
133
  def assign(key, other)
114
134
  ArgCheck['key', key, Symbol]
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "uri"
4
+ require "addressable/uri"
5
+
3
6
  module ReSorcery
4
7
  module Fielded
5
8
  module ExpandInternalFields
@@ -23,7 +26,7 @@ module ReSorcery
23
26
  ok(obj)
24
27
  when Array
25
28
  expand_for_array(obj)
26
- when URI
29
+ when URI, Addressable::URI
27
30
  ok(obj.to_s)
28
31
  when Hash
29
32
  err("`Hash` cannot be safely expanded as a `field`. Use a `Fielded` class instead.")
@@ -40,10 +40,9 @@ module ReSorcery
40
40
  # @return [Result<String, Hash>]
41
41
  def fields
42
42
  self.class.instance_exec { @fields ||= [] }.inject(ok({})) do |result_hash, (name, field_hash)|
43
- result_hash.and_then do |ok_hash|
43
+ result_hash.assign(name) do
44
44
  field_hash[:type].test(instance_exec(&field_hash[:pro]))
45
45
  .and_then { |tested| ExpandInternalFields.expand(tested) }
46
- .map { |fielded| ok_hash.merge(name => fielded) }
47
46
  .map_error { |error| "Error at field `#{name}` of `#{self.class}`: #{error}" }
48
47
  end
49
48
  end
@@ -1,60 +1,72 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "uri"
4
+ require "addressable/uri"
5
+
3
6
  module ReSorcery
4
7
  module Linked
5
8
  class LinkClassFactory
6
- extend Decoder::BuiltinDecoders
7
-
8
- def self.valid_rels
9
- ReSorcery.configuration.fetch(
10
- :link_rels,
11
- %w[
12
- self
13
- create
14
- update
15
- destroy
16
- ],
17
- )
18
- end
9
+ class << self
10
+ include Decoder::BuiltinDecoders
19
11
 
20
- def self.valid_methods
21
- ReSorcery.configuration.fetch(
22
- :link_methods,
23
- %w[
24
- get
25
- post
26
- patch
27
- put
28
- delete
29
- ],
30
- )
31
- end
12
+ def make_link_class
13
+ fields = build_fields
32
14
 
33
- URI_ABLE = is(String, URI).and do |s|
34
- next true if s.is_a?(URI)
15
+ Class.new do
16
+ include Fielded
35
17
 
36
- begin
37
- ok(URI.parse(s))
38
- rescue URI::InvalidURIError
39
- err("Not a valid URI: #{s}")
18
+ def initialize(args)
19
+ @args = args
20
+ end
21
+
22
+ fields.each { |(name, decoder, value)| field name, decoder, value }
23
+ end
40
24
  end
41
- end
42
25
 
43
- def self.make_link_class
44
- default_method = valid_methods.first
45
- this = self
26
+ private
27
+
28
+ def valid_rels
29
+ ReSorcery.configuration.fetch(:link_rels)
30
+ end
46
31
 
47
- Class.new do
48
- include Fielded
32
+ def valid_methods
33
+ ReSorcery.configuration.fetch(:link_methods)
34
+ end
49
35
 
50
- def initialize(args)
51
- @args = args
36
+ def uri_able
37
+ is(URI, Addressable::URI).or_else do
38
+ is(String).and_then do
39
+ Decoder.new do |u|
40
+ ok(Addressable::URI.parse(u))
41
+ rescue Addressable::URI::InvalidURIError
42
+ err("Not a valid URI: #{u}")
43
+ end
44
+ end
52
45
  end
46
+ end
47
+
48
+ def str_or_sym
49
+ is(String, Symbol).map(&:to_s)
50
+ end
51
+
52
+ def rel_decoder
53
+ str_or_sym.and_then { |v| Decoder.new { is(*valid_rels).test(v) } }
54
+ end
55
+
56
+ def method_decoder
57
+ str_or_sym.and_then { |v| Decoder.new { is(*valid_methods).test(v) } }
58
+ end
59
+
60
+ def build_fields
61
+ default_method = ReSorcery.configuration.fetch(:default_link_method).call(valid_methods)
62
+ default_type = ReSorcery.configuration.fetch(:default_link_type)
53
63
 
54
- field :rel, is(*this.valid_rels), -> { @args[:rel] }
55
- field :href, URI_ABLE, -> { @args[:href] }
56
- field :method, is(*this.valid_methods), -> { @args.fetch(:method, default_method) }
57
- field :type, String, -> { @args.fetch(:type, 'application/json') }
64
+ [
65
+ [:rel, rel_decoder, -> { @args[:rel] }],
66
+ [:href, uri_able, -> { @args[:href] }],
67
+ [:method, method_decoder, -> { @args.fetch(:method, default_method) }],
68
+ [:type, String, -> { @args.fetch(:type, default_type) }],
69
+ ]
58
70
  end
59
71
  end
60
72
  end
@@ -65,9 +65,10 @@ module ReSorcery
65
65
  # Define a `Link` for an object
66
66
  #
67
67
  # @see `ReSorcery::Linked::Link#initialize` for param types
68
- def link(rel, href, method = 'get', type = 'application/json')
68
+ def link(rel, href, method = nil, type = nil)
69
69
  klass = Linked.link_class
70
- (@_created_links ||= []) << klass.new(rel: rel, href: href, method: method, type: type).fields
70
+ args = {rel:, href:, method:, type:}.compact
71
+ (@_created_links ||= []) << klass.new(**args).fields
71
72
  end
72
73
  end
73
74
  end
@@ -3,6 +3,14 @@
3
3
  module ReSorcery
4
4
  module Result
5
5
  class Ok
6
+ class << self
7
+ def new(value)
8
+ return Result::Err.new("`nil` was provided as a successful result value!") if value.nil?
9
+
10
+ super
11
+ end
12
+ end
13
+
6
14
  def initialize(value)
7
15
  @value = value
8
16
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReSorcery
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/re_sorcery.gemspec CHANGED
@@ -14,21 +14,26 @@ Gem::Specification.new do |spec|
14
14
 
15
15
  # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
16
16
 
17
+ spec.required_ruby_version = '~> 3.1'
18
+
17
19
  spec.metadata["homepage_uri"] = spec.homepage
18
- # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
20
+ spec.metadata["source_code_uri"] = "https://github.com/spejamchr/re_sorcery"
19
21
  # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
20
23
 
21
24
  # Specify which files should be added to the gem when it is released.
22
25
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
27
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
28
  end
26
29
  spec.bindir = "exe"
27
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
31
  spec.require_paths = ["lib"]
29
32
 
30
- spec.add_development_dependency "bundler", "~> 2.0"
31
- spec.add_development_dependency "minitest", "~> 5.0"
33
+ spec.add_runtime_dependency "addressable", "~> 2.8", ">= 2.8.1"
34
+
35
+ spec.add_development_dependency "bundler", "~> 2.3"
36
+ spec.add_development_dependency "minitest", "~> 5.16"
32
37
  spec.add_development_dependency "pry"
33
- spec.add_development_dependency "rake", "~> 10.0"
38
+ spec.add_development_dependency "rake", "~> 13.0"
34
39
  end
metadata CHANGED
@@ -1,43 +1,63 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: re_sorcery
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Spencer Christiansen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-21 00:00:00.000000000 Z
11
+ date: 2023-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.8'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.8.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '2.8'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 2.8.1
13
33
  - !ruby/object:Gem::Dependency
14
34
  name: bundler
15
35
  requirement: !ruby/object:Gem::Requirement
16
36
  requirements:
17
37
  - - "~>"
18
38
  - !ruby/object:Gem::Version
19
- version: '2.0'
39
+ version: '2.3'
20
40
  type: :development
21
41
  prerelease: false
22
42
  version_requirements: !ruby/object:Gem::Requirement
23
43
  requirements:
24
44
  - - "~>"
25
45
  - !ruby/object:Gem::Version
26
- version: '2.0'
46
+ version: '2.3'
27
47
  - !ruby/object:Gem::Dependency
28
48
  name: minitest
29
49
  requirement: !ruby/object:Gem::Requirement
30
50
  requirements:
31
51
  - - "~>"
32
52
  - !ruby/object:Gem::Version
33
- version: '5.0'
53
+ version: '5.16'
34
54
  type: :development
35
55
  prerelease: false
36
56
  version_requirements: !ruby/object:Gem::Requirement
37
57
  requirements:
38
58
  - - "~>"
39
59
  - !ruby/object:Gem::Version
40
- version: '5.0'
60
+ version: '5.16'
41
61
  - !ruby/object:Gem::Dependency
42
62
  name: pry
43
63
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +78,14 @@ dependencies:
58
78
  requirements:
59
79
  - - "~>"
60
80
  - !ruby/object:Gem::Version
61
- version: '10.0'
81
+ version: '13.0'
62
82
  type: :development
63
83
  prerelease: false
64
84
  version_requirements: !ruby/object:Gem::Requirement
65
85
  requirements:
66
86
  - - "~>"
67
87
  - !ruby/object:Gem::Version
68
- version: '10.0'
88
+ version: '13.0'
69
89
  description:
70
90
  email:
71
91
  - jc.spencer92@gmail.com
@@ -106,22 +126,24 @@ licenses:
106
126
  - MIT
107
127
  metadata:
108
128
  homepage_uri: https://github.com/spejamchr/re_sorcery
129
+ source_code_uri: https://github.com/spejamchr/re_sorcery
130
+ rubygems_mfa_required: 'true'
109
131
  post_install_message:
110
132
  rdoc_options: []
111
133
  require_paths:
112
134
  - lib
113
135
  required_ruby_version: !ruby/object:Gem::Requirement
114
136
  requirements:
115
- - - ">="
137
+ - - "~>"
116
138
  - !ruby/object:Gem::Version
117
- version: '0'
139
+ version: '3.1'
118
140
  required_rubygems_version: !ruby/object:Gem::Requirement
119
141
  requirements:
120
142
  - - ">="
121
143
  - !ruby/object:Gem::Version
122
144
  version: '0'
123
145
  requirements: []
124
- rubygems_version: 3.0.3.1
146
+ rubygems_version: 3.3.7
125
147
  signing_key:
126
148
  specification_version: 4
127
149
  summary: Create resources with run-time payload type checking and link validation