forest_admin_rpc_agent 1.22.3 → 2.0.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/forest_admin_rpc_agent.gemspec +0 -3
- data/lib/forest_admin_rpc_agent/agent.rb +181 -60
- data/lib/forest_admin_rpc_agent/routes/base_route.rb +4 -15
- data/lib/forest_admin_rpc_agent/routes/schema.rb +1 -1
- data/lib/forest_admin_rpc_agent/version.rb +1 -1
- data/lib/forest_admin_rpc_agent.rb +1 -2
- metadata +2 -44
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '06883f603b147464ddf1ad642f47af2c3230b41cd043cf663ad9214ad22f98ec'
|
|
4
|
+
data.tar.gz: 13cdf53f4abc7147fd38d73efa4ee74f7829abbf320ce363ac315f42e8f1e322
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2e680dc41c11c375aafe885afee8870b78ee67c81b9853f33c8eb214f1ddd4a60e82c4fe11bbd78f30c894c7288ad66f211c1d354cc7c780fb4c22e5abe4cb48
|
|
7
|
+
data.tar.gz: 02e09ab9ba06a77619dcd42785c3965f578e799014517a85535234c6079e673d4d31b0491fced672764b82fa7186022394b8570415d63f7797a4cc7ee2d9e760
|
|
@@ -31,12 +31,9 @@ admin work on any Ruby application."
|
|
|
31
31
|
spec.require_paths = ["lib"]
|
|
32
32
|
|
|
33
33
|
spec.add_dependency "base64"
|
|
34
|
-
spec.add_dependency "benchmark"
|
|
35
34
|
spec.add_dependency "bigdecimal"
|
|
36
|
-
spec.add_dependency "cgi"
|
|
37
35
|
spec.add_dependency "csv"
|
|
38
36
|
spec.add_dependency "dry-configurable", "~> 1.1"
|
|
39
|
-
spec.add_dependency "logger"
|
|
40
37
|
spec.add_dependency "mutex_m"
|
|
41
38
|
spec.add_dependency "ostruct"
|
|
42
39
|
spec.add_dependency "thor", "~> 1.3"
|
|
@@ -2,7 +2,7 @@ require 'digest'
|
|
|
2
2
|
require 'fileutils'
|
|
3
3
|
|
|
4
4
|
module ForestAdminRpcAgent
|
|
5
|
-
class Agent < ForestAdminAgent::Builder::AgentFactory
|
|
5
|
+
class Agent < ForestAdminAgent::Builder::AgentFactory # rubocop:disable Metrics/ClassLength
|
|
6
6
|
include ForestAdminAgent::Http::Exceptions
|
|
7
7
|
|
|
8
8
|
attr_reader :rpc_collections, :cached_schema, :cached_schema_hash
|
|
@@ -17,23 +17,35 @@ module ForestAdminRpcAgent
|
|
|
17
17
|
def send_schema(force: false)
|
|
18
18
|
if should_skip_schema_update? && !force
|
|
19
19
|
log_schema_skip
|
|
20
|
+
load_and_cache_schema
|
|
20
21
|
return
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
schema_path = ForestAdminRpcAgent::Facades::Container.cache(:schema_path)
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
if ForestAdminRpcAgent::Facades::Container.cache(:is_production)
|
|
27
|
+
unless schema_path && File.exist?(schema_path)
|
|
28
|
+
raise InternalServerError.new(
|
|
29
|
+
'Schema file not found in production',
|
|
30
|
+
details: { schema_path: schema_path }
|
|
31
|
+
)
|
|
32
|
+
end
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
load_and_cache_schema_from_file(schema_path)
|
|
35
|
+
|
|
36
|
+
ForestAdminRpcAgent::Facades::Container.logger.log(
|
|
37
|
+
'Info',
|
|
38
|
+
'RPC agent running in production mode, using existing schema file.'
|
|
39
|
+
)
|
|
40
|
+
else
|
|
41
|
+
generate_and_cache_schema(schema_path)
|
|
42
|
+
|
|
43
|
+
ForestAdminRpcAgent::Facades::Container.logger.log(
|
|
44
|
+
'Info',
|
|
45
|
+
"RPC agent schema generated and saved to #{schema_path}"
|
|
46
|
+
)
|
|
47
|
+
end
|
|
32
48
|
|
|
33
|
-
ForestAdminRpcAgent::Facades::Container.logger.log(
|
|
34
|
-
'Info',
|
|
35
|
-
'RPC agent schema computed from datasource and cached.'
|
|
36
|
-
)
|
|
37
49
|
ForestAdminRpcAgent::Facades::Container.logger.log(
|
|
38
50
|
'Info',
|
|
39
51
|
'RPC agent does not send schema to Forest Admin servers.'
|
|
@@ -45,6 +57,15 @@ module ForestAdminRpcAgent
|
|
|
45
57
|
self
|
|
46
58
|
end
|
|
47
59
|
|
|
60
|
+
# Returns the cached schema for the /rpc-schema route
|
|
61
|
+
# Falls back to building schema from datasource if not cached
|
|
62
|
+
def rpc_schema
|
|
63
|
+
return @cached_schema if @cached_schema
|
|
64
|
+
|
|
65
|
+
build_and_cache_schema_from_datasource
|
|
66
|
+
@cached_schema
|
|
67
|
+
end
|
|
68
|
+
|
|
48
69
|
# Check if provided hash matches the cached schema hash
|
|
49
70
|
def schema_hash_matches?(provided_hash)
|
|
50
71
|
return false unless @cached_schema_hash && provided_hash
|
|
@@ -61,70 +82,170 @@ module ForestAdminRpcAgent
|
|
|
61
82
|
def log_schema_skip
|
|
62
83
|
logger = ForestAdminRpcAgent::Facades::Container.logger
|
|
63
84
|
logger.log('Warn', '[ForestAdmin] Schema update skipped (skip_schema_update flag is true)')
|
|
85
|
+
environment = ForestAdminRpcAgent::Facades::Container.cache(:is_production) ? 'production' : 'development'
|
|
86
|
+
logger.log('Info', "[ForestAdmin] RPC agent running in #{environment} mode")
|
|
64
87
|
end
|
|
65
88
|
|
|
66
|
-
def
|
|
89
|
+
def load_and_cache_schema
|
|
67
90
|
schema_path = ForestAdminRpcAgent::Facades::Container.cache(:schema_path)
|
|
91
|
+
|
|
92
|
+
if ForestAdminRpcAgent::Facades::Container.cache(:is_production) && schema_path && File.exist?(schema_path)
|
|
93
|
+
load_and_cache_schema_from_file(schema_path)
|
|
94
|
+
else
|
|
95
|
+
# In development with skip_schema_update, still build from datasource
|
|
96
|
+
build_and_cache_schema_from_datasource
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def load_and_cache_schema_from_file(_schema_path)
|
|
101
|
+
# File exists but RPC schema needs internal format - build from datasource
|
|
102
|
+
# The file is kept for reference/frontend but RPC always uses internal format
|
|
103
|
+
datasource = @container.resolve(:datasource)
|
|
104
|
+
@cached_schema = build_rpc_schema_from_datasource(datasource)
|
|
105
|
+
compute_and_cache_hash
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def generate_and_cache_schema(schema_path)
|
|
109
|
+
datasource = @container.resolve(:datasource)
|
|
110
|
+
|
|
111
|
+
# Generate frontend schema for file (used by Forest Admin)
|
|
112
|
+
generated = ForestAdminAgent::Utils::Schema::SchemaEmitter.generate(datasource)
|
|
113
|
+
meta = ForestAdminAgent::Utils::Schema::SchemaEmitter.meta
|
|
114
|
+
|
|
115
|
+
schema = {
|
|
116
|
+
meta: meta,
|
|
117
|
+
collections: generated
|
|
118
|
+
}
|
|
119
|
+
|
|
68
120
|
FileUtils.mkdir_p(File.dirname(schema_path))
|
|
69
|
-
|
|
70
|
-
File.write(schema_path, JSON.pretty_generate(JSON.parse(@cached_schema.to_json)))
|
|
121
|
+
File.write(schema_path, format_schema_json(schema))
|
|
71
122
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
)
|
|
123
|
+
# Build RPC schema in internal format (used by master agent)
|
|
124
|
+
@cached_schema = build_rpc_schema_from_datasource(datasource)
|
|
125
|
+
compute_and_cache_hash
|
|
76
126
|
end
|
|
77
127
|
|
|
78
|
-
def
|
|
79
|
-
|
|
128
|
+
def build_and_cache_schema_from_datasource
|
|
129
|
+
datasource = @container.resolve(:datasource)
|
|
80
130
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
datasource.collections.each_value do |collection|
|
|
85
|
-
relations = {}
|
|
86
|
-
|
|
87
|
-
if @rpc_collections.include?(collection.name)
|
|
88
|
-
# RPC collection → extract relations to non-RPC collections
|
|
89
|
-
collection.schema[:fields].each do |field_name, field|
|
|
90
|
-
next if field.type == 'Column'
|
|
91
|
-
next if @rpc_collections.include?(field.foreign_collection)
|
|
92
|
-
|
|
93
|
-
relations[field_name] = field
|
|
94
|
-
end
|
|
95
|
-
else
|
|
96
|
-
fields = {}
|
|
97
|
-
|
|
98
|
-
collection.schema[:fields].each do |field_name, field|
|
|
99
|
-
if field.type != 'Column' && @rpc_collections.include?(field.foreign_collection)
|
|
100
|
-
relations[field_name] = field
|
|
101
|
-
else
|
|
102
|
-
if field.type == 'Column'
|
|
103
|
-
field.filter_operators = ForestAdminAgent::Utils::Schema::FrontendFilterable.sort_operators(
|
|
104
|
-
field.filter_operators
|
|
105
|
-
)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
fields[field_name] = field
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Normal collection → include in schema
|
|
113
|
-
collections << collection.schema.merge({ name: collection.name, fields: fields })
|
|
114
|
-
end
|
|
131
|
+
@cached_schema = build_rpc_schema_from_datasource(datasource)
|
|
132
|
+
compute_and_cache_hash
|
|
133
|
+
end
|
|
115
134
|
|
|
116
|
-
|
|
117
|
-
|
|
135
|
+
def build_rpc_schema_from_datasource(datasource)
|
|
136
|
+
schema = customizer.schema
|
|
118
137
|
|
|
138
|
+
# Serialize collections with internal schema format (fields as hash with :type keys)
|
|
139
|
+
collections = datasource.collections.map { |_name, collection| serialize_collection_for_rpc(collection) }
|
|
119
140
|
schema[:collections] = collections.sort_by { |c| c[:name] }
|
|
120
|
-
schema[:rpc_relations] = rpc_relations
|
|
121
141
|
|
|
122
|
-
|
|
123
|
-
|
|
142
|
+
connections = datasource.live_query_connections.keys.map { |connection_name| { name: connection_name } }
|
|
143
|
+
schema[:native_query_connections] = connections
|
|
124
144
|
|
|
125
145
|
schema
|
|
126
146
|
end
|
|
127
147
|
|
|
148
|
+
def serialize_collection_for_rpc(collection)
|
|
149
|
+
{
|
|
150
|
+
name: collection.name,
|
|
151
|
+
countable: collection.schema[:countable],
|
|
152
|
+
searchable: collection.schema[:searchable],
|
|
153
|
+
segments: collection.schema[:segments] || [],
|
|
154
|
+
charts: collection.schema[:charts] || [],
|
|
155
|
+
actions: serialize_actions_for_rpc(collection.schema[:actions] || {}),
|
|
156
|
+
fields: serialize_fields_for_rpc(collection.schema[:fields] || {})
|
|
157
|
+
}
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def serialize_fields_for_rpc(fields)
|
|
161
|
+
fields.transform_values do |field_schema|
|
|
162
|
+
serialize_field_schema(field_schema)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def serialize_field_schema(field_schema)
|
|
167
|
+
case field_schema
|
|
168
|
+
when ForestAdminDatasourceToolkit::Schema::ColumnSchema
|
|
169
|
+
{
|
|
170
|
+
type: 'Column',
|
|
171
|
+
column_type: field_schema.column_type,
|
|
172
|
+
filter_operators: field_schema.filter_operators,
|
|
173
|
+
is_primary_key: field_schema.is_primary_key,
|
|
174
|
+
is_read_only: field_schema.is_read_only,
|
|
175
|
+
is_sortable: field_schema.is_sortable,
|
|
176
|
+
default_value: field_schema.default_value,
|
|
177
|
+
enum_values: field_schema.enum_values,
|
|
178
|
+
validation: field_schema.validation
|
|
179
|
+
}
|
|
180
|
+
when ForestAdminDatasourceToolkit::Schema::Relations::ManyToOneSchema
|
|
181
|
+
{
|
|
182
|
+
type: 'ManyToOne',
|
|
183
|
+
foreign_collection: field_schema.foreign_collection,
|
|
184
|
+
foreign_key: field_schema.foreign_key,
|
|
185
|
+
foreign_key_target: field_schema.foreign_key_target
|
|
186
|
+
}
|
|
187
|
+
when ForestAdminDatasourceToolkit::Schema::Relations::OneToOneSchema
|
|
188
|
+
{
|
|
189
|
+
type: 'OneToOne',
|
|
190
|
+
foreign_collection: field_schema.foreign_collection,
|
|
191
|
+
origin_key: field_schema.origin_key,
|
|
192
|
+
origin_key_target: field_schema.origin_key_target
|
|
193
|
+
}
|
|
194
|
+
when ForestAdminDatasourceToolkit::Schema::Relations::OneToManySchema
|
|
195
|
+
{
|
|
196
|
+
type: 'OneToMany',
|
|
197
|
+
foreign_collection: field_schema.foreign_collection,
|
|
198
|
+
origin_key: field_schema.origin_key,
|
|
199
|
+
origin_key_target: field_schema.origin_key_target
|
|
200
|
+
}
|
|
201
|
+
when ForestAdminDatasourceToolkit::Schema::Relations::ManyToManySchema
|
|
202
|
+
{
|
|
203
|
+
type: 'ManyToMany',
|
|
204
|
+
foreign_collection: field_schema.foreign_collection,
|
|
205
|
+
foreign_key: field_schema.foreign_key,
|
|
206
|
+
foreign_key_target: field_schema.foreign_key_target,
|
|
207
|
+
origin_key: field_schema.origin_key,
|
|
208
|
+
origin_key_target: field_schema.origin_key_target,
|
|
209
|
+
through_collection: field_schema.through_collection
|
|
210
|
+
}
|
|
211
|
+
when ForestAdminDatasourceToolkit::Schema::Relations::PolymorphicManyToOneSchema
|
|
212
|
+
{
|
|
213
|
+
type: 'PolymorphicManyToOne',
|
|
214
|
+
foreign_collections: field_schema.foreign_collections,
|
|
215
|
+
foreign_key: field_schema.foreign_key,
|
|
216
|
+
foreign_key_type_field: field_schema.foreign_key_type_field,
|
|
217
|
+
foreign_key_targets: field_schema.foreign_key_targets
|
|
218
|
+
}
|
|
219
|
+
when ForestAdminDatasourceToolkit::Schema::Relations::PolymorphicOneToOneSchema
|
|
220
|
+
{
|
|
221
|
+
type: 'PolymorphicOneToOne',
|
|
222
|
+
foreign_collection: field_schema.foreign_collection,
|
|
223
|
+
origin_key: field_schema.origin_key,
|
|
224
|
+
origin_key_target: field_schema.origin_key_target,
|
|
225
|
+
origin_type_field: field_schema.origin_type_field,
|
|
226
|
+
origin_type_value: field_schema.origin_type_value
|
|
227
|
+
}
|
|
228
|
+
when ForestAdminDatasourceToolkit::Schema::Relations::PolymorphicOneToManySchema
|
|
229
|
+
{
|
|
230
|
+
type: 'PolymorphicOneToMany',
|
|
231
|
+
foreign_collection: field_schema.foreign_collection,
|
|
232
|
+
origin_key: field_schema.origin_key,
|
|
233
|
+
origin_key_target: field_schema.origin_key_target,
|
|
234
|
+
origin_type_field: field_schema.origin_type_field,
|
|
235
|
+
origin_type_value: field_schema.origin_type_value
|
|
236
|
+
}
|
|
237
|
+
else
|
|
238
|
+
# Fallback: try to convert to hash if possible
|
|
239
|
+
field_schema.respond_to?(:to_h) ? field_schema.to_h : field_schema
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def serialize_actions_for_rpc(actions)
|
|
244
|
+
actions.transform_values do |action|
|
|
245
|
+
action.respond_to?(:to_h) ? action.to_h : action
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
128
249
|
def compute_and_cache_hash
|
|
129
250
|
return unless @cached_schema
|
|
130
251
|
|
|
@@ -39,16 +39,16 @@ module ForestAdminRpcAgent
|
|
|
39
39
|
|
|
40
40
|
# Skip authentication for health check (root path)
|
|
41
41
|
if @url == '/'
|
|
42
|
-
params =
|
|
43
|
-
result = handle_request({ params: params
|
|
42
|
+
params = request.query_parameters.merge(request.request_parameters)
|
|
43
|
+
result = handle_request({ params: params, caller: nil, request: request })
|
|
44
44
|
build_rails_response(result)
|
|
45
45
|
else
|
|
46
46
|
auth_middleware = ForestAdminRpcAgent::Middleware::Authentication.new(->(_env) { [200, {}, ['OK']] })
|
|
47
47
|
status, headers, response = auth_middleware.call(request.env)
|
|
48
48
|
|
|
49
49
|
if status == 200
|
|
50
|
-
params =
|
|
51
|
-
result = handle_request({ params: params
|
|
50
|
+
params = request.query_parameters.merge(request.request_parameters)
|
|
51
|
+
result = handle_request({ params: params, caller: headers[:caller], request: request })
|
|
52
52
|
build_rails_response(result)
|
|
53
53
|
else
|
|
54
54
|
[status, headers, response]
|
|
@@ -87,17 +87,6 @@ module ForestAdminRpcAgent
|
|
|
87
87
|
|
|
88
88
|
private
|
|
89
89
|
|
|
90
|
-
def deep_symbolize_keys(obj)
|
|
91
|
-
case obj
|
|
92
|
-
when Hash
|
|
93
|
-
obj.transform_keys(&:to_sym).transform_values { |v| deep_symbolize_keys(v) }
|
|
94
|
-
when Array
|
|
95
|
-
obj.map { |v| deep_symbolize_keys(v) }
|
|
96
|
-
else
|
|
97
|
-
obj
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
90
|
def serialize_response(result)
|
|
102
91
|
return result if result.is_a?(String) && (result.start_with?('{', '['))
|
|
103
92
|
|
|
@@ -20,13 +20,12 @@ module ForestAdminRpcAgent
|
|
|
20
20
|
setting :prefix, default: nil
|
|
21
21
|
setting :cache_dir, default: :'tmp/cache/forest_admin'
|
|
22
22
|
setting :project_dir, default: Dir.pwd
|
|
23
|
-
setting :schema_path, default: File.join(Dir.pwd, '.forestadmin-
|
|
23
|
+
setting :schema_path, default: File.join(Dir.pwd, '.forestadmin-schema.json')
|
|
24
24
|
setting :skip_schema_update, default: false
|
|
25
25
|
setting :logger_level, default: 'info'
|
|
26
26
|
setting :logger, default: nil
|
|
27
27
|
setting :customize_error_message, default: nil
|
|
28
28
|
setting :disable_route_cache, default: false
|
|
29
|
-
setting :rpc_max_polling_threads, default: nil
|
|
30
29
|
|
|
31
30
|
begin
|
|
32
31
|
require 'thor'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: forest_admin_rpc_agent
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthieu
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2025-12-11 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: base64
|
|
@@ -25,20 +25,6 @@ dependencies:
|
|
|
25
25
|
- - ">="
|
|
26
26
|
- !ruby/object:Gem::Version
|
|
27
27
|
version: '0'
|
|
28
|
-
- !ruby/object:Gem::Dependency
|
|
29
|
-
name: benchmark
|
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
|
31
|
-
requirements:
|
|
32
|
-
- - ">="
|
|
33
|
-
- !ruby/object:Gem::Version
|
|
34
|
-
version: '0'
|
|
35
|
-
type: :runtime
|
|
36
|
-
prerelease: false
|
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
-
requirements:
|
|
39
|
-
- - ">="
|
|
40
|
-
- !ruby/object:Gem::Version
|
|
41
|
-
version: '0'
|
|
42
28
|
- !ruby/object:Gem::Dependency
|
|
43
29
|
name: bigdecimal
|
|
44
30
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,20 +39,6 @@ dependencies:
|
|
|
53
39
|
- - ">="
|
|
54
40
|
- !ruby/object:Gem::Version
|
|
55
41
|
version: '0'
|
|
56
|
-
- !ruby/object:Gem::Dependency
|
|
57
|
-
name: cgi
|
|
58
|
-
requirement: !ruby/object:Gem::Requirement
|
|
59
|
-
requirements:
|
|
60
|
-
- - ">="
|
|
61
|
-
- !ruby/object:Gem::Version
|
|
62
|
-
version: '0'
|
|
63
|
-
type: :runtime
|
|
64
|
-
prerelease: false
|
|
65
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
-
requirements:
|
|
67
|
-
- - ">="
|
|
68
|
-
- !ruby/object:Gem::Version
|
|
69
|
-
version: '0'
|
|
70
42
|
- !ruby/object:Gem::Dependency
|
|
71
43
|
name: csv
|
|
72
44
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -95,20 +67,6 @@ dependencies:
|
|
|
95
67
|
- - "~>"
|
|
96
68
|
- !ruby/object:Gem::Version
|
|
97
69
|
version: '1.1'
|
|
98
|
-
- !ruby/object:Gem::Dependency
|
|
99
|
-
name: logger
|
|
100
|
-
requirement: !ruby/object:Gem::Requirement
|
|
101
|
-
requirements:
|
|
102
|
-
- - ">="
|
|
103
|
-
- !ruby/object:Gem::Version
|
|
104
|
-
version: '0'
|
|
105
|
-
type: :runtime
|
|
106
|
-
prerelease: false
|
|
107
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
108
|
-
requirements:
|
|
109
|
-
- - ">="
|
|
110
|
-
- !ruby/object:Gem::Version
|
|
111
|
-
version: '0'
|
|
112
70
|
- !ruby/object:Gem::Dependency
|
|
113
71
|
name: mutex_m
|
|
114
72
|
requirement: !ruby/object:Gem::Requirement
|