scorpio 0.2.3 → 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/CHANGELOG.md +5 -0
- data/README.md +4 -2
- data/lib/scorpio.rb +9 -22
- data/lib/scorpio/google_api_document.rb +13 -16
- data/lib/scorpio/openapi.rb +141 -143
- data/lib/scorpio/openapi/document.rb +167 -0
- data/lib/scorpio/openapi/operation.rb +208 -0
- data/lib/scorpio/openapi/operations_scope.rb +29 -0
- data/lib/scorpio/openapi/v3/server.rb +32 -0
- data/lib/scorpio/request.rb +227 -0
- data/lib/scorpio/resource_base.rb +148 -182
- data/lib/scorpio/response.rb +34 -0
- data/lib/scorpio/ur.rb +33 -0
- data/lib/scorpio/version.rb +1 -1
- data/scorpio.gemspec +3 -4
- metadata +32 -36
- data/lib/scorpio/json-schema-fragments.rb +0 -191
- data/lib/scorpio/json.rb +0 -5
- data/lib/scorpio/json/node.rb +0 -256
- data/lib/scorpio/schema.rb +0 -249
- data/lib/scorpio/schema_instance_base.rb +0 -325
- data/lib/scorpio/schema_instance_base/to_rb.rb +0 -127
- data/lib/scorpio/schema_instance_json_coder.rb +0 -83
- data/lib/scorpio/struct_json_coder.rb +0 -30
- data/lib/scorpio/typelike_modules.rb +0 -164
- data/lib/scorpio/util.rb +0 -89
- data/lib/scorpio/util/faraday/response_media_type.rb +0 -15
@@ -1,127 +0,0 @@
|
|
1
|
-
module Scorpio
|
2
|
-
# base class for representing an instance of an instance described by a schema
|
3
|
-
class SchemaInstanceBase
|
4
|
-
class << self
|
5
|
-
def class_comment
|
6
|
-
lines = []
|
7
|
-
|
8
|
-
description = schema &&
|
9
|
-
schema['description'].respond_to?(:to_str) &&
|
10
|
-
schema['description'].to_str
|
11
|
-
if description
|
12
|
-
description.split("\n", -1).each do |descline|
|
13
|
-
lines << "# " + descline
|
14
|
-
end
|
15
|
-
lines << "#"
|
16
|
-
end
|
17
|
-
|
18
|
-
schema.described_hash_property_names.each_with_index do |propname, i|
|
19
|
-
lines << "#" unless i == 0
|
20
|
-
lines << "# @!attribute [rw] #{propname}"
|
21
|
-
|
22
|
-
property_schema = schema['properties'].respond_to?(:to_hash) &&
|
23
|
-
schema['properties'][propname].respond_to?(:to_hash) &&
|
24
|
-
schema['properties'][propname]
|
25
|
-
|
26
|
-
required = property_schema && property_schema['required']
|
27
|
-
required ||= schema['required'].respond_to?(:to_ary) && schema['required'].include?(propname)
|
28
|
-
lines << "# @required" if required
|
29
|
-
|
30
|
-
type = property_schema &&
|
31
|
-
property_schema['type'].respond_to?(:to_str) &&
|
32
|
-
property_schema['type'].to_str
|
33
|
-
simple = {'string' => 'String', 'number' => 'Numeric', 'boolean' => 'Boolean', 'null' => 'nil'}
|
34
|
-
rettypes = []
|
35
|
-
if simple.key?(type)
|
36
|
-
rettypes << simple[type]
|
37
|
-
elsif type == 'object' || type == 'array'
|
38
|
-
rettypes = []
|
39
|
-
schema_class = Scorpio.class_for_schema(property_schema)
|
40
|
-
unless schema_class.name =~ /\AScorpio::SchemaClasses::/
|
41
|
-
rettypes << schema_class.name
|
42
|
-
end
|
43
|
-
rettypes << {'object' => '#to_hash', 'array' => '#to_ary'}[type]
|
44
|
-
elsif type
|
45
|
-
# not really valid, but there's some information in there. whatever it is.
|
46
|
-
rettypes << type
|
47
|
-
end
|
48
|
-
# we'll add Object to all because the accessor methods have no enforcement that their value is
|
49
|
-
# of the specified type, and may return anything really. TODO: consider if this is of any value?
|
50
|
-
rettypes << 'Object'
|
51
|
-
lines << "# @return [#{rettypes.join(', ')}]"
|
52
|
-
|
53
|
-
description = property_schema &&
|
54
|
-
property_schema['description'].respond_to?(:to_str) &&
|
55
|
-
property_schema['description'].to_str
|
56
|
-
if description
|
57
|
-
description.split("\n", -1).each do |descline|
|
58
|
-
lines << "# " + descline
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
lines.join("\n")
|
63
|
-
end
|
64
|
-
|
65
|
-
def to_rb
|
66
|
-
lines = []
|
67
|
-
description = schema &&
|
68
|
-
schema['description'].respond_to?(:to_str) &&
|
69
|
-
schema['description'].to_str
|
70
|
-
if description
|
71
|
-
description.split("\n", -1).each do |descline|
|
72
|
-
lines << "# " + descline
|
73
|
-
end
|
74
|
-
end
|
75
|
-
lines << "class #{name}"
|
76
|
-
schema.described_hash_property_names.each_with_index do |propname, i|
|
77
|
-
lines << "" unless i == 0
|
78
|
-
property_schema = schema['properties'].respond_to?(:to_hash) &&
|
79
|
-
schema['properties'][propname].respond_to?(:to_hash) &&
|
80
|
-
schema['properties'][propname]
|
81
|
-
description = property_schema &&
|
82
|
-
property_schema['description'].respond_to?(:to_str) &&
|
83
|
-
property_schema['description'].to_str
|
84
|
-
if description
|
85
|
-
description.split("\n", -1).each do |descline|
|
86
|
-
lines << " # " + descline
|
87
|
-
end
|
88
|
-
lines << " #" # blank comment line between description and @return
|
89
|
-
end
|
90
|
-
|
91
|
-
required = property_schema && property_schema['required']
|
92
|
-
required ||= schema['required'].respond_to?(:to_ary) && schema['required'].include?(propname)
|
93
|
-
lines << " # @required" if required
|
94
|
-
|
95
|
-
type = property_schema &&
|
96
|
-
property_schema['type'].respond_to?(:to_str) &&
|
97
|
-
property_schema['type'].to_str
|
98
|
-
simple = {'string' => 'String', 'number' => 'Numeric', 'boolean' => 'Boolean', 'null' => 'nil'}
|
99
|
-
rettypes = []
|
100
|
-
if simple.key?(type)
|
101
|
-
rettypes << simple[type]
|
102
|
-
elsif type == 'object' || type == 'array'
|
103
|
-
rettypes = []
|
104
|
-
schema_class = Scorpio.class_for_schema(property_schema)
|
105
|
-
unless schema_class.name =~ /\AScorpio::SchemaClasses::/
|
106
|
-
rettypes << schema_class.name
|
107
|
-
end
|
108
|
-
rettypes << {'object' => '#to_hash', 'array' => '#to_ary'}[type]
|
109
|
-
elsif type
|
110
|
-
# not really valid, but there's some information in there. whatever it is.
|
111
|
-
rettypes << type
|
112
|
-
end
|
113
|
-
# we'll add Object to all because the accessor methods have no enforcement that their value is
|
114
|
-
# of the specified type, and may return anything really. TODO: consider if this is of any value?
|
115
|
-
rettypes << 'Object'
|
116
|
-
lines << " # @return [#{rettypes.join(', ')}]"
|
117
|
-
|
118
|
-
lines << " def #{propname}"
|
119
|
-
lines << " super"
|
120
|
-
lines << " end"
|
121
|
-
end
|
122
|
-
lines << "end"
|
123
|
-
lines.join("\n")
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
module Scorpio
|
2
|
-
# this is a ActiveRecord serialization class intended to store JSON in the
|
3
|
-
# database column and expose a ruby class once loaded on a model instance.
|
4
|
-
# this allows for better ruby idioms to access to properties, and definition
|
5
|
-
# of related methods on the loaded class.
|
6
|
-
#
|
7
|
-
# the first argument, `loaded_class`, is the class which will be used to
|
8
|
-
# instantiate the column data. properties of the loaded class will correspond
|
9
|
-
# to keys of the json object in the database.
|
10
|
-
#
|
11
|
-
# the column data may be either a single instance of the loaded class
|
12
|
-
# (represented as one json object) or an array of them (represented as a json
|
13
|
-
# array of json objects), indicated by the keyword argument `array`.
|
14
|
-
#
|
15
|
-
# the column behind the attribute may be an actual JSON column (postgres json
|
16
|
-
# or jsonb - hstore should work too if you only have string attributes) or a
|
17
|
-
# serialized string, indicated by the keyword argument `string`.
|
18
|
-
class ObjectJSONCoder
|
19
|
-
class Error < StandardError
|
20
|
-
end
|
21
|
-
class LoadError < Error
|
22
|
-
end
|
23
|
-
class DumpError < Error
|
24
|
-
end
|
25
|
-
|
26
|
-
def initialize(loaded_class, string: false, array: false, next_coder: nil)
|
27
|
-
@loaded_class = loaded_class
|
28
|
-
# this notes the order of the keys as they were in the json, used by dump_object to generate
|
29
|
-
# json that is equivalent to the json/jsonifiable that came in, so that AR's #changed_attributes
|
30
|
-
# can tell whether the attribute has been changed.
|
31
|
-
@loaded_class.send(:attr_accessor, :object_json_coder_keys_order)
|
32
|
-
@string = string
|
33
|
-
@array = array
|
34
|
-
@next_coder = next_coder
|
35
|
-
end
|
36
|
-
|
37
|
-
def load(column_data)
|
38
|
-
return nil if column_data.nil?
|
39
|
-
data = @string ? ::JSON.parse(column_data) : column_data
|
40
|
-
object = if @array
|
41
|
-
unless data.respond_to?(:to_ary)
|
42
|
-
raise TypeError, "expected array-like column data; got: #{data.class}: #{data.inspect}"
|
43
|
-
end
|
44
|
-
data.map { |el| load_object(el) }
|
45
|
-
else
|
46
|
-
load_object(data)
|
47
|
-
end
|
48
|
-
object = @next_coder.load(object) if @next_coder
|
49
|
-
object
|
50
|
-
end
|
51
|
-
|
52
|
-
def dump(object)
|
53
|
-
object = @next_coder.dump(object) if @next_coder
|
54
|
-
return nil if object.nil?
|
55
|
-
jsonifiable = begin
|
56
|
-
if @array
|
57
|
-
unless object.respond_to?(:to_ary)
|
58
|
-
raise DumpError, "expected array-like attribute; got: #{object.class}: #{object.inspect}"
|
59
|
-
end
|
60
|
-
object.map do |el|
|
61
|
-
dump_object(el)
|
62
|
-
end
|
63
|
-
else
|
64
|
-
dump_object(object)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
@string ? ::JSON.generate(jsonifiable) : jsonifiable
|
68
|
-
end
|
69
|
-
end
|
70
|
-
# this is a ActiveRecord serialization class intended to store JSON in the
|
71
|
-
# database column and expose a given SchemaInstanceBase subclass once loaded
|
72
|
-
# on a model instance.
|
73
|
-
class SchemaInstanceJSONCoder < ObjectJSONCoder
|
74
|
-
private
|
75
|
-
def load_object(data)
|
76
|
-
@loaded_class.new(data)
|
77
|
-
end
|
78
|
-
|
79
|
-
def dump_object(object)
|
80
|
-
Scorpio::Typelike.as_json(object)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module Scorpio
|
2
|
-
# this is a ActiveRecord serialization class intended to store JSON in the
|
3
|
-
# database column and expose a Struct subclass once loaded on a model instance.
|
4
|
-
class StructJSONCoder < ObjectJSONCoder
|
5
|
-
private
|
6
|
-
def load_object(data)
|
7
|
-
if data.is_a?(Hash)
|
8
|
-
good_keys = @loaded_class.members.map(&:to_s)
|
9
|
-
bad_keys = data.keys - good_keys
|
10
|
-
unless bad_keys.empty?
|
11
|
-
raise LoadError, "expected keys #{good_keys}; got unrecognized keys: #{bad_keys}"
|
12
|
-
end
|
13
|
-
instance = @loaded_class.new(*@loaded_class.members.map { |m| data[m.to_s] })
|
14
|
-
instance.object_json_coder_keys_order = data.keys
|
15
|
-
instance
|
16
|
-
else
|
17
|
-
raise LoadError, "expected instance(s) of #{Hash}; got: #{data.class}: #{data.inspect}"
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def dump_object(object)
|
22
|
-
if object.is_a?(@loaded_class)
|
23
|
-
keys = (object.object_json_coder_keys_order || []) | @loaded_class.members.map(&:to_s)
|
24
|
-
keys.map { |member| {member => object[member]} }.inject({}, &:update)
|
25
|
-
else
|
26
|
-
raise TypeError, "expected instance(s) of #{@loaded_class}; got: #{object.class}: #{object.inspect}"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,164 +0,0 @@
|
|
1
|
-
module Scorpio
|
2
|
-
module Typelike
|
3
|
-
def self.modified_copy(other, &block)
|
4
|
-
if other.respond_to?(:modified_copy)
|
5
|
-
other.modified_copy(&block)
|
6
|
-
else
|
7
|
-
return yield(other)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
# I could require 'json/add/core' and use #as_json but I like this better.
|
12
|
-
def self.as_json(object, *opt)
|
13
|
-
if object.respond_to?(:to_hash)
|
14
|
-
object.map do |k, v|
|
15
|
-
unless k.is_a?(Symbol) || k.respond_to?(:to_str)
|
16
|
-
raise(TypeError, "json object (hash) cannot be keyed with: #{k.pretty_inspect.chomp}")
|
17
|
-
end
|
18
|
-
{k.to_s => as_json(v, *opt)}
|
19
|
-
end.inject({}, &:update)
|
20
|
-
elsif object.respond_to?(:to_ary)
|
21
|
-
object.map { |e| as_json(e, *opt) }
|
22
|
-
elsif [String, TrueClass, FalseClass, NilClass, Numeric].any? { |c| object.is_a?(c) }
|
23
|
-
object
|
24
|
-
elsif object.is_a?(Symbol)
|
25
|
-
object.to_s
|
26
|
-
elsif object.is_a?(Set)
|
27
|
-
as_json(object.to_a, *opt)
|
28
|
-
elsif object.respond_to?(:as_json)
|
29
|
-
as_json(object.as_json(*opt), *opt)
|
30
|
-
else
|
31
|
-
raise(TypeError, "cannot express object as json: #{object.pretty_inspect.chomp}")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
module Hashlike
|
36
|
-
# safe methods which can be delegated to #to_hash (which the includer is assumed to have defined).
|
37
|
-
# 'safe' means, in this context, nondestructive - methods which do not modify the receiver.
|
38
|
-
|
39
|
-
# methods which do not need to access the value.
|
40
|
-
SAFE_KEY_ONLY_METHODS = %w(each_key empty? has_key? include? key? keys length member? size)
|
41
|
-
SAFE_KEY_VALUE_METHODS = %w(< <= > >= any? assoc compact dig each_pair each_value fetch fetch_values has_value? invert key merge rassoc reject select to_h to_proc transform_values value? values values_at)
|
42
|
-
DESTRUCTIVE_METHODS = %w(clear delete delete_if keep_if reject! replace select! shift)
|
43
|
-
# these return a modified copy
|
44
|
-
safe_modified_copy_methods = %w(compact merge)
|
45
|
-
# select and reject will return a modified copy but need the yielded block variable value from #[]
|
46
|
-
safe_kv_block_modified_copy_methods = %w(select reject)
|
47
|
-
SAFE_METHODS = SAFE_KEY_ONLY_METHODS | SAFE_KEY_VALUE_METHODS
|
48
|
-
safe_to_hash_methods = SAFE_METHODS - safe_modified_copy_methods - safe_kv_block_modified_copy_methods
|
49
|
-
safe_to_hash_methods.each do |method_name|
|
50
|
-
define_method(method_name) { |*a, &b| to_hash.public_send(method_name, *a, &b) }
|
51
|
-
end
|
52
|
-
safe_modified_copy_methods.each do |method_name|
|
53
|
-
define_method(method_name) do |*a, &b|
|
54
|
-
Scorpio::Typelike.modified_copy(self) do |object_to_modify|
|
55
|
-
object_to_modify.public_send(method_name, *a, &b)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
safe_kv_block_modified_copy_methods.each do |method_name|
|
60
|
-
define_method(method_name) do |*a, &b|
|
61
|
-
Scorpio::Typelike.modified_copy(self) do |object_to_modify|
|
62
|
-
object_to_modify.public_send(method_name, *a) do |k, _v|
|
63
|
-
b.call(k, self[k])
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def inspect
|
70
|
-
object_group_text = respond_to?(:object_group_text) ? ' ' + self.object_group_text : ''
|
71
|
-
"\#{<#{self.class.to_s}#{object_group_text}>#{empty? ? '' : ' '}#{self.map { |k, v| "#{k.inspect} => #{v.inspect}" }.join(', ')}}"
|
72
|
-
end
|
73
|
-
|
74
|
-
def to_s
|
75
|
-
inspect
|
76
|
-
end
|
77
|
-
|
78
|
-
def pretty_print(q)
|
79
|
-
q.instance_exec(self) do |obj|
|
80
|
-
object_group_text = obj.respond_to?(:object_group_text) ? ' ' + obj.object_group_text : ''
|
81
|
-
text "\#{<#{obj.class.to_s}#{object_group_text}>"
|
82
|
-
group_sub {
|
83
|
-
nest(2) {
|
84
|
-
breakable(obj.any? { true } ? ' ' : '')
|
85
|
-
seplist(obj, nil, :each_pair) { |k, v|
|
86
|
-
group {
|
87
|
-
pp k
|
88
|
-
text ' => '
|
89
|
-
pp v
|
90
|
-
}
|
91
|
-
}
|
92
|
-
}
|
93
|
-
}
|
94
|
-
breakable ''
|
95
|
-
text '}'
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
module Arraylike
|
100
|
-
# safe methods which can be delegated to #to_ary (which the includer is assumed to have defined).
|
101
|
-
# 'safe' means, in this context, nondestructive - methods which do not modify the receiver.
|
102
|
-
|
103
|
-
# methods which do not need to access the element.
|
104
|
-
SAFE_INDEX_ONLY_METHODS = %w(each_index empty? length size)
|
105
|
-
# there are some ambiguous ones that are omitted, like #sort, #map / #collect.
|
106
|
-
SAFE_INDEX_ELEMENT_METHODS = %w(| & * + - <=> abbrev assoc at bsearch bsearch_index combination compact count cycle dig drop drop_while fetch find_index first include? index join last pack permutation product reject repeated_combination repeated_permutation reverse reverse_each rindex rotate sample select shelljoin shuffle slice sort take take_while transpose uniq values_at zip)
|
107
|
-
DESTRUCTIVE_METHODS = %w(<< clear collect! compact! concat delete delete_at delete_if fill flatten! insert keep_if map! pop push reject! replace reverse! rotate! select! shift shuffle! slice! sort! sort_by! uniq! unshift)
|
108
|
-
|
109
|
-
# methods (well, method) that returns a modified copy and doesn't need any handling of block variable(s)
|
110
|
-
safe_modified_copy_methods = %w(compact)
|
111
|
-
|
112
|
-
# methods that return a modified copy and do need handling of block variables
|
113
|
-
safe_el_block_methods = %w(reject select)
|
114
|
-
|
115
|
-
SAFE_METHODS = SAFE_INDEX_ONLY_METHODS | SAFE_INDEX_ELEMENT_METHODS
|
116
|
-
safe_to_ary_methods = SAFE_METHODS - safe_modified_copy_methods - safe_el_block_methods
|
117
|
-
safe_to_ary_methods.each do |method_name|
|
118
|
-
define_method(method_name) { |*a, &b| to_ary.public_send(method_name, *a, &b) }
|
119
|
-
end
|
120
|
-
safe_modified_copy_methods.each do |method_name|
|
121
|
-
define_method(method_name) do |*a, &b|
|
122
|
-
Scorpio::Typelike.modified_copy(self) do |object_to_modify|
|
123
|
-
object_to_modify.public_send(method_name, *a, &b)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
safe_el_block_methods.each do |method_name|
|
128
|
-
define_method(method_name) do |*a, &b|
|
129
|
-
Scorpio::Typelike.modified_copy(self) do |object_to_modify|
|
130
|
-
i = 0
|
131
|
-
object_to_modify.public_send(method_name, *a) do |_e|
|
132
|
-
b.call(self[i]).tap { i += 1 }
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def inspect
|
139
|
-
object_group_text = respond_to?(:object_group_text) ? ' ' + self.object_group_text : ''
|
140
|
-
"\#[<#{self.class.to_s}#{object_group_text}>#{empty? ? '' : ' '}#{self.map { |e| e.inspect }.join(', ')}]"
|
141
|
-
end
|
142
|
-
|
143
|
-
def to_s
|
144
|
-
inspect
|
145
|
-
end
|
146
|
-
|
147
|
-
def pretty_print(q)
|
148
|
-
q.instance_exec(self) do |obj|
|
149
|
-
object_group_text = obj.respond_to?(:object_group_text) ? ' ' + obj.object_group_text : ''
|
150
|
-
text "\#[<#{obj.class.to_s}#{object_group_text}>"
|
151
|
-
group_sub {
|
152
|
-
nest(2) {
|
153
|
-
breakable(obj.any? { true } ? ' ' : '')
|
154
|
-
seplist(obj, nil, :each) { |e|
|
155
|
-
pp e
|
156
|
-
}
|
157
|
-
}
|
158
|
-
}
|
159
|
-
breakable ''
|
160
|
-
text ']'
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
data/lib/scorpio/util.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
module Scorpio
|
2
|
-
module Util
|
3
|
-
def stringify_symbol_keys(hash)
|
4
|
-
unless hash.respond_to?(:to_hash)
|
5
|
-
raise(ArgumentError, "expected argument to be a hash; got #{hash.class.inspect}: #{hash.pretty_inspect.chomp}")
|
6
|
-
end
|
7
|
-
Scorpio::Typelike.modified_copy(hash) do |hash_|
|
8
|
-
changed = false
|
9
|
-
out = {}
|
10
|
-
hash_.each do |k, v|
|
11
|
-
if k.is_a?(Symbol)
|
12
|
-
changed = true
|
13
|
-
k = k.to_s
|
14
|
-
end
|
15
|
-
out[k] = v
|
16
|
-
end
|
17
|
-
changed ? out : hash_
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def deep_stringify_symbol_keys(object)
|
22
|
-
if object.respond_to?(:to_hash)
|
23
|
-
Scorpio::Typelike.modified_copy(object) do |hash|
|
24
|
-
changed = false
|
25
|
-
out = {}
|
26
|
-
hash.each do |k, v|
|
27
|
-
if k.is_a?(Symbol)
|
28
|
-
changed = true
|
29
|
-
k = k.to_s
|
30
|
-
end
|
31
|
-
out_k = deep_stringify_symbol_keys(k)
|
32
|
-
out_v = deep_stringify_symbol_keys(v)
|
33
|
-
changed = true if out_k.object_id != k.object_id
|
34
|
-
changed = true if out_v.object_id != v.object_id
|
35
|
-
out[out_k] = out_v
|
36
|
-
end
|
37
|
-
changed ? out : hash
|
38
|
-
end
|
39
|
-
elsif object.respond_to?(:to_ary)
|
40
|
-
Scorpio::Typelike.modified_copy(object) do |ary|
|
41
|
-
changed = false
|
42
|
-
out = ary.map do |e|
|
43
|
-
out_e = deep_stringify_symbol_keys(e)
|
44
|
-
changed = true if out_e.object_id != e.object_id
|
45
|
-
out_e
|
46
|
-
end
|
47
|
-
changed ? out : ary
|
48
|
-
end
|
49
|
-
else
|
50
|
-
object
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
extend Util
|
55
|
-
|
56
|
-
module FingerprintHash
|
57
|
-
def ==(other)
|
58
|
-
object_id == other.object_id || (other.respond_to?(:fingerprint) && other.fingerprint == self.fingerprint)
|
59
|
-
end
|
60
|
-
|
61
|
-
alias_method :eql?, :==
|
62
|
-
|
63
|
-
def hash
|
64
|
-
fingerprint.hash
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
module Memoize
|
69
|
-
def memoize(key, *args_)
|
70
|
-
@memos ||= {}
|
71
|
-
@memos[key] ||= Hash.new do |h, args|
|
72
|
-
h[args] = yield(*args)
|
73
|
-
end
|
74
|
-
@memos[key][args_]
|
75
|
-
end
|
76
|
-
|
77
|
-
def clear_memo(key, *args)
|
78
|
-
@memos ||= {}
|
79
|
-
if @memos[key]
|
80
|
-
if args.empty?
|
81
|
-
@memos[key].clear
|
82
|
-
else
|
83
|
-
@memos[key].delete(args)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
extend Memoize
|
89
|
-
end
|