lutaml-hal 0.1.9 → 0.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 +4 -4
- data/README.adoc +23 -30
- data/lib/lutaml/hal/cache/cache_configuration.rb +185 -0
- data/lib/lutaml/hal/cache/cache_entry.rb +140 -0
- data/lib/lutaml/hal/cache/cache_manager.rb +334 -0
- data/lib/lutaml/hal/cache/cache_metadata.rb +104 -0
- data/lib/lutaml/hal/cache/simple_cache_store.rb +68 -0
- data/lib/lutaml/hal/client.rb +36 -33
- data/lib/lutaml/hal/errors.rb +3 -0
- data/lib/lutaml/hal/global_register.rb +19 -0
- data/lib/lutaml/hal/link.rb +15 -10
- data/lib/lutaml/hal/link_set.rb +1 -1
- data/lib/lutaml/hal/model_register.rb +117 -30
- data/lib/lutaml/hal/page.rb +1 -1
- data/lib/lutaml/hal/rate_limiter.rb +105 -0
- data/lib/lutaml/hal/resource.rb +5 -6
- data/lib/lutaml/hal/version.rb +1 -1
- data/lib/lutaml/hal.rb +2 -2
- metadata +22 -2
data/lib/lutaml/hal/link.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Lutaml
|
|
|
9
9
|
class Link < Lutaml::Model::Serializable
|
|
10
10
|
# This is the model register that has fetched the origin of this link, and
|
|
11
11
|
# will be used to resolve unless overriden in resource#realize()
|
|
12
|
-
attr_accessor
|
|
12
|
+
attr_accessor :_global_register_id
|
|
13
13
|
|
|
14
14
|
# Store reference to parent resource for automatic embedded content detection
|
|
15
15
|
attr_accessor :parent_resource
|
|
@@ -27,17 +27,22 @@ module Lutaml
|
|
|
27
27
|
# This method will use the global register according to the source of the Link object.
|
|
28
28
|
# If the Link does not have a register, a register needs to be provided explicitly
|
|
29
29
|
# via the `register:` parameter.
|
|
30
|
-
def realize(register: nil, parent_resource: nil)
|
|
30
|
+
def realize(register: nil, parent_resource: nil, force_refresh: false)
|
|
31
31
|
# Use provided parent_resource or fall back to stored parent_resource
|
|
32
32
|
effective_parent = parent_resource || @parent_resource
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
register = find_register(register)
|
|
35
|
+
raise "No register provided for link resolution (class: #{self.class}, href: #{href})" if register.nil?
|
|
36
|
+
|
|
37
|
+
# Priority 1: Check embedded content first (unless force_refresh)
|
|
38
|
+
if !force_refresh && effective_parent && (embedded_content = check_embedded_content(effective_parent, register))
|
|
39
|
+
# Cache embedded content too, so later lookups by href are served locally
|
|
40
|
+
register.cache_manager&.set(href, nil, embedded_content)
|
|
36
41
|
return embedded_content
|
|
37
42
|
end
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
# Force refresh bypasses any cached entry for this href
|
|
45
|
+
register.cache_manager&.invalidate(href) if force_refresh
|
|
41
46
|
|
|
42
47
|
Hal.debug_log "Resolving link href: #{href} using register"
|
|
43
48
|
register.resolve_and_cast(self, href)
|
|
@@ -86,19 +91,19 @@ module Lutaml
|
|
|
86
91
|
|
|
87
92
|
# Try to find the model class for this href
|
|
88
93
|
href_path = href.sub(register.client.api_url, '') if register.client
|
|
89
|
-
model_class = register.
|
|
94
|
+
model_class = register.find_matching_model_class(href_path)
|
|
90
95
|
return nil unless model_class
|
|
91
96
|
|
|
92
97
|
# Create the resource from embedded data
|
|
93
|
-
resource = model_class.from_embedded(embedded_item,
|
|
94
|
-
register.
|
|
98
|
+
resource = model_class.from_embedded(embedded_item, _global_register_id)
|
|
99
|
+
register.mark_model_links_with_register(resource)
|
|
95
100
|
resource
|
|
96
101
|
end
|
|
97
102
|
|
|
98
103
|
def find_register(explicit_register)
|
|
99
104
|
return explicit_register if explicit_register
|
|
100
105
|
|
|
101
|
-
register_id =
|
|
106
|
+
register_id = _global_register_id
|
|
102
107
|
return nil if register_id.nil?
|
|
103
108
|
|
|
104
109
|
register = Lutaml::Hal::GlobalRegister.instance.get(register_id)
|
data/lib/lutaml/hal/link_set.rb
CHANGED
|
@@ -3,18 +3,20 @@
|
|
|
3
3
|
require 'cgi'
|
|
4
4
|
require_relative 'errors'
|
|
5
5
|
require_relative 'endpoint_configuration'
|
|
6
|
+
require_relative 'cache/cache_manager'
|
|
6
7
|
|
|
7
8
|
module Lutaml
|
|
8
9
|
module Hal
|
|
9
10
|
# Register to map URL patterns to model classes with EndpointParameter support
|
|
10
11
|
class ModelRegister
|
|
11
|
-
attr_accessor :models, :client, :register_name
|
|
12
|
+
attr_accessor :models, :client, :register_name, :cache_manager
|
|
12
13
|
|
|
13
|
-
def initialize(name:, client: nil)
|
|
14
|
+
def initialize(name:, client: nil, cache: nil)
|
|
14
15
|
@register_name = name
|
|
15
16
|
# If `client` is not set, it can be set later
|
|
16
17
|
@client = client
|
|
17
18
|
@models = {}
|
|
19
|
+
@cache_manager = Cache::CacheManager.new(cache, client: @client) if cache
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
# Register a model with its URL pattern and parameters
|
|
@@ -75,27 +77,89 @@ module Lutaml
|
|
|
75
77
|
# Add query parameters
|
|
76
78
|
final_url = build_url_with_query_params(url, processed_params[:query])
|
|
77
79
|
|
|
80
|
+
realized_model = nil
|
|
81
|
+
|
|
82
|
+
# Check cache first
|
|
83
|
+
if @cache_manager&.available?
|
|
84
|
+
cached_entry = @cache_manager.get(final_url)
|
|
85
|
+
if cached_entry
|
|
86
|
+
debug_log("Cache hit for fetch: #{final_url}")
|
|
87
|
+
realized_model = cached_entry.hal_resource
|
|
88
|
+
# Return cached model directly if valid
|
|
89
|
+
mark_model_links_with_register(realized_model)
|
|
90
|
+
return realized_model
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Make request if not cached
|
|
95
|
+
# Add conditional headers if we have cached metadata
|
|
96
|
+
request_headers = processed_params[:headers].dup
|
|
97
|
+
if @cache_manager&.available?
|
|
98
|
+
conditional_headers = @cache_manager.conditional_request_headers(final_url)
|
|
99
|
+
request_headers.merge!(conditional_headers) if conditional_headers
|
|
100
|
+
end
|
|
101
|
+
|
|
78
102
|
# Make request with headers
|
|
79
|
-
response = if
|
|
80
|
-
client.get_with_headers(final_url,
|
|
103
|
+
response = if request_headers.any?
|
|
104
|
+
client.get_with_headers(final_url, request_headers)
|
|
81
105
|
else
|
|
82
106
|
client.get(final_url)
|
|
83
107
|
end
|
|
84
108
|
|
|
85
|
-
|
|
109
|
+
# Handle 304 Not Modified
|
|
110
|
+
if response.respond_to?(:status) && response.status == 304
|
|
111
|
+
@cache_manager&.refresh_entry(final_url, response)
|
|
112
|
+
cached_entry = @cache_manager.get(final_url)
|
|
113
|
+
realized_model = cached_entry.hal_resource if cached_entry
|
|
114
|
+
else
|
|
115
|
+
# Create model from response
|
|
116
|
+
realized_model = endpoint[:model].from_json(response.to_json)
|
|
117
|
+
|
|
118
|
+
# Cache the realized model with metadata
|
|
119
|
+
@cache_manager&.set(final_url, response, realized_model)
|
|
120
|
+
end
|
|
86
121
|
|
|
87
122
|
# Store embedded data for later resolution
|
|
88
|
-
realized_model.
|
|
123
|
+
realized_model.embedded_data = response['_embedded'] if realized_model && response && response['_embedded']
|
|
89
124
|
|
|
90
125
|
mark_model_links_with_register(realized_model)
|
|
126
|
+
|
|
91
127
|
realized_model
|
|
92
128
|
end
|
|
93
129
|
|
|
94
130
|
def resolve_and_cast(link, href)
|
|
95
131
|
raise 'Client not configured' unless client
|
|
96
132
|
|
|
97
|
-
|
|
98
|
-
|
|
133
|
+
# Check cache first
|
|
134
|
+
if @cache_manager&.available?
|
|
135
|
+
cached_entry = @cache_manager.get(href)
|
|
136
|
+
if cached_entry
|
|
137
|
+
debug_log("Cache hit for: #{href}")
|
|
138
|
+
cached_model = cached_entry.hal_resource
|
|
139
|
+
# A model rebuilt from a persistent cache needs to be (re)marked so
|
|
140
|
+
# its links can be realized against this register.
|
|
141
|
+
mark_model_links_with_register(cached_model)
|
|
142
|
+
return cached_model
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
debug_log("resolve_and_cast: link #{link}, href #{href}")
|
|
147
|
+
|
|
148
|
+
# Add conditional headers if we have cached metadata
|
|
149
|
+
conditional_headers = @cache_manager&.conditional_request_headers(href) || {}
|
|
150
|
+
|
|
151
|
+
response = if conditional_headers.any?
|
|
152
|
+
client.get_by_url_with_headers(href, conditional_headers)
|
|
153
|
+
else
|
|
154
|
+
client.get_by_url(href)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Handle 304 Not Modified
|
|
158
|
+
if response.respond_to?(:status) && response.status == 304
|
|
159
|
+
@cache_manager&.refresh_entry(href, response)
|
|
160
|
+
cached_entry = @cache_manager.get(href)
|
|
161
|
+
return cached_entry.hal_resource if cached_entry
|
|
162
|
+
end
|
|
99
163
|
|
|
100
164
|
# TODO: Merge full Link content into the resource?
|
|
101
165
|
response_with_link_details = response.to_h.merge({ 'href' => href })
|
|
@@ -105,12 +169,16 @@ module Lutaml
|
|
|
105
169
|
model_class = find_matching_model_class(href_path)
|
|
106
170
|
raise LinkResolutionError, "Unregistered URL pattern: #{href}" unless model_class
|
|
107
171
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
172
|
+
debug_log("resolve_and_cast: resolved to model_class #{model_class}")
|
|
173
|
+
debug_log("resolve_and_cast: response: #{response.inspect}")
|
|
174
|
+
debug_log("resolve_and_cast: amended: #{response_with_link_details}")
|
|
111
175
|
|
|
112
176
|
model = model_class.from_json(response_with_link_details.to_json)
|
|
113
177
|
mark_model_links_with_register(model)
|
|
178
|
+
|
|
179
|
+
# Cache the realized model with metadata
|
|
180
|
+
@cache_manager&.set(href, response, model)
|
|
181
|
+
|
|
114
182
|
model
|
|
115
183
|
end
|
|
116
184
|
|
|
@@ -120,7 +188,7 @@ module Lutaml
|
|
|
120
188
|
def mark_model_links_with_register(inspecting_model)
|
|
121
189
|
return unless inspecting_model.is_a?(Lutaml::Model::Serializable)
|
|
122
190
|
|
|
123
|
-
inspecting_model.
|
|
191
|
+
inspecting_model._global_register_id = @register_name
|
|
124
192
|
|
|
125
193
|
# Recursively process model attributes to mark links with this register
|
|
126
194
|
inspecting_model.class.attributes.each_pair do |key, config|
|
|
@@ -145,8 +213,41 @@ module Lutaml
|
|
|
145
213
|
inspecting_model
|
|
146
214
|
end
|
|
147
215
|
|
|
216
|
+
# Cache management methods
|
|
217
|
+
def cache_stats
|
|
218
|
+
@cache_manager&.stats || {}
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def clear_cache
|
|
222
|
+
@cache_manager&.clear
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def cache_info
|
|
226
|
+
@cache_manager&.info
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Find the registered model class whose URL pattern matches the given href.
|
|
230
|
+
# Public so that Link#realize can resolve embedded resources.
|
|
231
|
+
def find_matching_model_class(href)
|
|
232
|
+
# Find all matching patterns and select the most specific one (longest pattern)
|
|
233
|
+
matching_models = @models.values.select do |model_data|
|
|
234
|
+
matches_url_with_params?(model_data, href)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
return nil if matching_models.empty?
|
|
238
|
+
|
|
239
|
+
# Sort by pattern length (descending) to get the most specific match first
|
|
240
|
+
result = matching_models.max_by { |model_data| model_data[:url].length }
|
|
241
|
+
|
|
242
|
+
result[:model]
|
|
243
|
+
end
|
|
244
|
+
|
|
148
245
|
private
|
|
149
246
|
|
|
247
|
+
def debug_log(message)
|
|
248
|
+
puts "[Lutaml::Hal] DEBUG: #{message}" if ENV['DEBUG_API']
|
|
249
|
+
end
|
|
250
|
+
|
|
150
251
|
def process_parameters(parameter_definitions, provided_params)
|
|
151
252
|
result = { path: {}, query: {}, headers: {}, cookies: {} }
|
|
152
253
|
|
|
@@ -253,20 +354,6 @@ module Lutaml
|
|
|
253
354
|
end
|
|
254
355
|
end
|
|
255
356
|
|
|
256
|
-
def find_matching_model_class(href)
|
|
257
|
-
# Find all matching patterns and select the most specific one (longest pattern)
|
|
258
|
-
matching_models = @models.values.select do |model_data|
|
|
259
|
-
matches_url_with_params?(model_data, href)
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
return nil if matching_models.empty?
|
|
263
|
-
|
|
264
|
-
# Sort by pattern length (descending) to get the most specific match first
|
|
265
|
-
result = matching_models.max_by { |model_data| model_data[:url].length }
|
|
266
|
-
|
|
267
|
-
result[:model]
|
|
268
|
-
end
|
|
269
|
-
|
|
270
357
|
def matches_url_with_params?(model_data, href)
|
|
271
358
|
pattern = model_data[:url]
|
|
272
359
|
parameters = model_data[:parameters]
|
|
@@ -339,12 +426,12 @@ module Lutaml
|
|
|
339
426
|
# This ensures that {param} only matches a single path segment
|
|
340
427
|
regex = Regexp.new("^#{pattern_with_wildcards.gsub('*', '[^/]+')}$")
|
|
341
428
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
429
|
+
debug_log("pattern_match?: regex: #{regex.inspect}")
|
|
430
|
+
debug_log("pattern_match?: href to match #{url}")
|
|
431
|
+
debug_log("pattern_match?: pattern to match #{pattern_with_wildcards}")
|
|
345
432
|
|
|
346
433
|
matches = regex.match?(url)
|
|
347
|
-
|
|
434
|
+
debug_log("pattern_match?: matches = #{matches}")
|
|
348
435
|
|
|
349
436
|
matches
|
|
350
437
|
end
|
data/lib/lutaml/hal/page.rb
CHANGED
|
@@ -60,7 +60,7 @@ module Lutaml
|
|
|
60
60
|
return nil unless prev_page_url
|
|
61
61
|
|
|
62
62
|
# Use the HAL register to fetch the previous page
|
|
63
|
-
register_name =
|
|
63
|
+
register_name = _global_register_id
|
|
64
64
|
return nil unless register_name
|
|
65
65
|
|
|
66
66
|
hal_register = Lutaml::Hal::GlobalRegister.instance.get(register_name)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Hal
|
|
5
|
+
# Rate limiter to handle API rate limiting with exponential backoff
|
|
6
|
+
class RateLimiter
|
|
7
|
+
DEFAULT_MAX_RETRIES = 5
|
|
8
|
+
DEFAULT_BASE_DELAY = 0.05
|
|
9
|
+
DEFAULT_MAX_DELAY = 5.0
|
|
10
|
+
DEFAULT_BACKOFF_FACTOR = 1.5
|
|
11
|
+
|
|
12
|
+
attr_reader :max_retries, :base_delay, :max_delay, :backoff_factor
|
|
13
|
+
|
|
14
|
+
def initialize(options = {})
|
|
15
|
+
@max_retries = options[:max_retries] || DEFAULT_MAX_RETRIES
|
|
16
|
+
@base_delay = options[:base_delay] || DEFAULT_BASE_DELAY
|
|
17
|
+
@max_delay = options[:max_delay] || DEFAULT_MAX_DELAY
|
|
18
|
+
@backoff_factor = options[:backoff_factor] || DEFAULT_BACKOFF_FACTOR
|
|
19
|
+
@enabled = options[:enabled] != false # Default to enabled unless explicitly disabled
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Execute a block with rate limiting and retry logic
|
|
23
|
+
def with_rate_limiting
|
|
24
|
+
return yield unless @enabled
|
|
25
|
+
|
|
26
|
+
attempt = 0
|
|
27
|
+
begin
|
|
28
|
+
attempt += 1
|
|
29
|
+
yield
|
|
30
|
+
rescue TooManyRequestsError, ServerError => e
|
|
31
|
+
raise unless should_retry?(e, attempt)
|
|
32
|
+
|
|
33
|
+
delay = calculate_delay(attempt, e)
|
|
34
|
+
sleep(delay)
|
|
35
|
+
retry
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Check if we should retry based on the error and attempt count
|
|
40
|
+
def should_retry?(error, attempt)
|
|
41
|
+
return false if attempt > @max_retries
|
|
42
|
+
|
|
43
|
+
case error
|
|
44
|
+
when TooManyRequestsError
|
|
45
|
+
true
|
|
46
|
+
when ServerError
|
|
47
|
+
# Always retry on server errors
|
|
48
|
+
true
|
|
49
|
+
else
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Calculate delay with exponential backoff
|
|
55
|
+
def calculate_delay(attempt, error = nil)
|
|
56
|
+
# Check for Retry-After header if it's a rate limit error
|
|
57
|
+
if error.is_a?(TooManyRequestsError) && error.respond_to?(:response) && error.response
|
|
58
|
+
retry_after = extract_retry_after(error.response)
|
|
59
|
+
return retry_after if retry_after
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Exponential backoff: base_delay * (backoff_factor ^ (attempt - 1))
|
|
63
|
+
delay = @base_delay * (@backoff_factor**(attempt - 1))
|
|
64
|
+
|
|
65
|
+
# Cap at max_delay
|
|
66
|
+
[delay, @max_delay].min
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Extract Retry-After header value
|
|
70
|
+
def extract_retry_after(response)
|
|
71
|
+
headers = response[:headers] || {}
|
|
72
|
+
retry_after = headers['retry-after'] || headers['Retry-After']
|
|
73
|
+
return nil unless retry_after
|
|
74
|
+
|
|
75
|
+
# Retry-After can be in seconds (integer) or HTTP date
|
|
76
|
+
if retry_after.match?(/^\d+$/)
|
|
77
|
+
retry_after.to_i
|
|
78
|
+
else
|
|
79
|
+
# Parse HTTP date and calculate seconds from now
|
|
80
|
+
begin
|
|
81
|
+
retry_time = Time.parse(retry_after)
|
|
82
|
+
[retry_time - Time.now, 0].max
|
|
83
|
+
rescue ArgumentError
|
|
84
|
+
nil
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Enable rate limiting
|
|
90
|
+
def enable!
|
|
91
|
+
@enabled = true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Disable rate limiting
|
|
95
|
+
def disable!
|
|
96
|
+
@enabled = false
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Check if rate limiting is enabled
|
|
100
|
+
def enabled?
|
|
101
|
+
@enabled
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
data/lib/lutaml/hal/resource.rb
CHANGED
|
@@ -11,12 +11,11 @@ module Lutaml
|
|
|
11
11
|
class Resource < Lutaml::Model::Serializable
|
|
12
12
|
# This is the model register that has fetched this resource, and
|
|
13
13
|
# will be used to resolve links unless overriden in resource#realize()
|
|
14
|
-
attr_accessor
|
|
14
|
+
attr_accessor :_global_register_id
|
|
15
15
|
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
end
|
|
16
|
+
# Embedded resources from the HAL `_embedded` section, retained so that
|
|
17
|
+
# links can be realized from already-fetched content.
|
|
18
|
+
attr_accessor :embedded_data
|
|
20
19
|
|
|
21
20
|
# Check if embedded data exists for a given key
|
|
22
21
|
def has_embedded?(key)
|
|
@@ -31,7 +30,7 @@ module Lutaml
|
|
|
31
30
|
# Create a resource instance from embedded JSON data
|
|
32
31
|
def self.from_embedded(json_data, register_name = nil)
|
|
33
32
|
instance = from_json(json_data.to_json)
|
|
34
|
-
instance.
|
|
33
|
+
instance._global_register_id = register_name if register_name
|
|
35
34
|
instance
|
|
36
35
|
end
|
|
37
36
|
|
data/lib/lutaml/hal/version.rb
CHANGED
data/lib/lutaml/hal.rb
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'lutaml/model'
|
|
4
|
+
require 'lutaml/store'
|
|
4
5
|
|
|
5
6
|
module Lutaml
|
|
6
7
|
# HAL implementation for Lutaml
|
|
7
8
|
module Hal
|
|
8
|
-
REGISTER_ID_ATTR_NAME = '_global_register_id'
|
|
9
|
-
|
|
10
9
|
def self.debug_log(message)
|
|
11
10
|
puts "[Lutaml::Hal] DEBUG: #{message}" if ENV['DEBUG_API']
|
|
12
11
|
end
|
|
@@ -15,6 +14,7 @@ end
|
|
|
15
14
|
|
|
16
15
|
require_relative 'hal/version'
|
|
17
16
|
require_relative 'hal/errors'
|
|
17
|
+
require_relative 'hal/rate_limiter'
|
|
18
18
|
require_relative 'hal/endpoint_parameter'
|
|
19
19
|
require_relative 'hal/link'
|
|
20
20
|
require_relative 'hal/link_set'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lutaml-hal
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-06-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -52,6 +52,20 @@ dependencies:
|
|
|
52
52
|
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: lutaml-store
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 0.1.1
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 0.1.1
|
|
55
69
|
- !ruby/object:Gem::Dependency
|
|
56
70
|
name: rainbow
|
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -78,6 +92,11 @@ files:
|
|
|
78
92
|
- README.adoc
|
|
79
93
|
- lib/lutaml-hal.rb
|
|
80
94
|
- lib/lutaml/hal.rb
|
|
95
|
+
- lib/lutaml/hal/cache/cache_configuration.rb
|
|
96
|
+
- lib/lutaml/hal/cache/cache_entry.rb
|
|
97
|
+
- lib/lutaml/hal/cache/cache_manager.rb
|
|
98
|
+
- lib/lutaml/hal/cache/cache_metadata.rb
|
|
99
|
+
- lib/lutaml/hal/cache/simple_cache_store.rb
|
|
81
100
|
- lib/lutaml/hal/client.rb
|
|
82
101
|
- lib/lutaml/hal/endpoint_configuration.rb
|
|
83
102
|
- lib/lutaml/hal/endpoint_parameter.rb
|
|
@@ -89,6 +108,7 @@ files:
|
|
|
89
108
|
- lib/lutaml/hal/link_set_class_factory.rb
|
|
90
109
|
- lib/lutaml/hal/model_register.rb
|
|
91
110
|
- lib/lutaml/hal/page.rb
|
|
111
|
+
- lib/lutaml/hal/rate_limiter.rb
|
|
92
112
|
- lib/lutaml/hal/resource.rb
|
|
93
113
|
- lib/lutaml/hal/type_resolver.rb
|
|
94
114
|
- lib/lutaml/hal/version.rb
|