re_sorcery 0.1.0 → 0.3.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 +4 -4
- data/Gemfile.lock +14 -10
- data/README.md +17 -9
- data/lib/re_sorcery/configuration.rb +29 -8
- data/lib/re_sorcery/decoder/builtin_decoders.rb +63 -1
- data/lib/re_sorcery/decoder.rb +33 -13
- data/lib/re_sorcery/fielded/expand_internal_fields.rb +4 -1
- data/lib/re_sorcery/fielded.rb +6 -3
- data/lib/re_sorcery/linked/link_class_factory.rb +55 -43
- data/lib/re_sorcery/linked.rb +3 -2
- data/lib/re_sorcery/result/ok.rb +8 -0
- data/lib/re_sorcery/version.rb +1 -1
- data/re_sorcery.gemspec +10 -5
- metadata +33 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f72b419777c7c5f2f57db936293ccb8fd1fbc7fd4b2ca5de14c21ac4be9ee7f7
|
4
|
+
data.tar.gz: 88d0385f9d01c64b0ad1e95fcb6350bb78ea301d0b6f291c0cb967b99d17a46e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fff11e2b74ec4c68f5c6de80815495583097d5fda2994a016382b93deaf4f0abe7492f292cf8a1831b514c0a68cd296680196051ca94be3b9c81a98f205489b
|
7
|
+
data.tar.gz: b4446f2fec6609d8a5042ced152cee096bbd44d3d041de781275017ee7153f6633e5eeef917f8ea8daac131f9750853d756013c4fae48bb98410a95e56f2164e
|
data/Gemfile.lock
CHANGED
@@ -1,28 +1,32 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
re_sorcery (0.
|
4
|
+
re_sorcery (0.3.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.
|
12
|
-
pry (0.14.
|
14
|
+
minitest (5.17.0)
|
15
|
+
pry (0.14.2)
|
13
16
|
coderay (~> 1.1)
|
14
17
|
method_source (~> 1.0)
|
15
|
-
|
18
|
+
public_suffix (5.0.1)
|
19
|
+
rake (13.0.6)
|
16
20
|
|
17
21
|
PLATFORMS
|
18
|
-
|
22
|
+
arm64-darwin-21
|
19
23
|
|
20
24
|
DEPENDENCIES
|
21
|
-
bundler (~> 2.
|
22
|
-
|
23
|
-
minitest (~> 5.0)
|
25
|
+
bundler (~> 2.3)
|
26
|
+
minitest (~> 5.16)
|
24
27
|
pry
|
25
|
-
rake (~>
|
28
|
+
rake (~> 13.0)
|
29
|
+
re_sorcery!
|
26
30
|
|
27
31
|
BUNDLED WITH
|
28
|
-
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.
|
37
|
-
#
|
38
|
-
# :payload=>{
|
39
|
-
#
|
40
|
-
#
|
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:
|
94
|
-
# got
|
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
|
18
|
-
#
|
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:
|
25
|
-
|
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(&
|
52
|
+
def configure(&)
|
33
53
|
raise Error::InvalidConfigurationError, @configured if configured?
|
34
54
|
|
35
55
|
@configured = "configured at #{caller_locations.first}"
|
36
|
-
instance_exec(&
|
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,
|
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
|
-
|
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
|
data/lib/re_sorcery/decoder.rb
CHANGED
@@ -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
|
-
|
55
|
-
case
|
55
|
+
block_result = @block.call(unknown)
|
56
|
+
case block_result
|
56
57
|
when Result::Ok, Result::Err
|
57
|
-
|
58
|
+
block_result
|
58
59
|
when TrueClass
|
59
60
|
ok(unknown)
|
60
61
|
else
|
61
|
-
err(
|
62
|
-
end
|
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
|
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.")
|
data/lib/re_sorcery/fielded.rb
CHANGED
@@ -26,6 +26,10 @@ module ReSorcery
|
|
26
26
|
|
27
27
|
(@fields ||= {})[name] = { type: is(type), pro: pro }
|
28
28
|
end
|
29
|
+
|
30
|
+
def fields
|
31
|
+
(superclass&.include?(Fielded) ? superclass.send(:fields) : {}).merge(@fields || {})
|
32
|
+
end
|
29
33
|
end
|
30
34
|
|
31
35
|
def self.included(base)
|
@@ -39,11 +43,10 @@ module ReSorcery
|
|
39
43
|
#
|
40
44
|
# @return [Result<String, Hash>]
|
41
45
|
def fields
|
42
|
-
self.class.
|
43
|
-
result_hash.
|
46
|
+
self.class.send(:fields).inject(ok({})) do |result_hash, (name, field_hash)|
|
47
|
+
result_hash.assign(name) do
|
44
48
|
field_hash[:type].test(instance_exec(&field_hash[:pro]))
|
45
49
|
.and_then { |tested| ExpandInternalFields.expand(tested) }
|
46
|
-
.map { |fielded| ok_hash.merge(name => fielded) }
|
47
50
|
.map_error { |error| "Error at field `#{name}` of `#{self.class}`: #{error}" }
|
48
51
|
end
|
49
52
|
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
|
-
|
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
|
-
|
21
|
-
|
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
|
-
|
34
|
-
|
15
|
+
Class.new do
|
16
|
+
include Fielded
|
35
17
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
26
|
+
private
|
27
|
+
|
28
|
+
def valid_rels
|
29
|
+
ReSorcery.configuration.fetch(:link_rels)
|
30
|
+
end
|
46
31
|
|
47
|
-
|
48
|
-
|
32
|
+
def valid_methods
|
33
|
+
ReSorcery.configuration.fetch(:link_methods)
|
34
|
+
end
|
49
35
|
|
50
|
-
|
51
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
data/lib/re_sorcery/linked.rb
CHANGED
@@ -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 =
|
68
|
+
def link(rel, href, method = nil, type = nil)
|
69
69
|
klass = Linked.link_class
|
70
|
-
|
70
|
+
args = {rel:, href:, method:, type:}.compact
|
71
|
+
(@_created_links ||= []) << klass.new(**args).fields
|
71
72
|
end
|
72
73
|
end
|
73
74
|
end
|
data/lib/re_sorcery/result/ok.rb
CHANGED
data/lib/re_sorcery/version.rb
CHANGED
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
|
-
|
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
|
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.
|
31
|
-
|
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", "~>
|
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.
|
4
|
+
version: 0.3.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:
|
11
|
+
date: 2023-03-25 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.
|
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.
|
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.
|
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.
|
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: '
|
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: '
|
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: '
|
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.
|
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
|