jsi 0.0.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +3 -1
  3. data/CHANGELOG.md +48 -0
  4. data/LICENSE.md +613 -0
  5. data/README.md +84 -45
  6. data/jsi.gemspec +11 -14
  7. data/lib/jsi.rb +31 -12
  8. data/lib/jsi/base.rb +310 -344
  9. data/lib/jsi/base/to_rb.rb +2 -0
  10. data/lib/jsi/jsi_coder.rb +91 -0
  11. data/lib/jsi/json-schema-fragments.rb +3 -135
  12. data/lib/jsi/json.rb +3 -0
  13. data/lib/jsi/json/node.rb +72 -197
  14. data/lib/jsi/json/pointer.rb +419 -0
  15. data/lib/jsi/metaschema.rb +7 -0
  16. data/lib/jsi/metaschema_node.rb +218 -0
  17. data/lib/jsi/pathed_node.rb +118 -0
  18. data/lib/jsi/schema.rb +168 -223
  19. data/lib/jsi/schema_classes.rb +158 -0
  20. data/lib/jsi/simple_wrap.rb +12 -0
  21. data/lib/jsi/typelike_modules.rb +71 -45
  22. data/lib/jsi/util.rb +47 -57
  23. data/lib/jsi/version.rb +1 -1
  24. data/lib/schemas/json-schema.org/draft-04/schema.rb +7 -0
  25. data/lib/schemas/json-schema.org/draft-06/schema.rb +7 -0
  26. data/resources/icons/AGPL-3.0.png +0 -0
  27. data/test/base_array_test.rb +210 -84
  28. data/test/base_hash_test.rb +201 -58
  29. data/test/base_test.rb +212 -121
  30. data/test/jsi_coder_test.rb +85 -0
  31. data/test/jsi_json_arraynode_test.rb +26 -25
  32. data/test/jsi_json_hashnode_test.rb +40 -39
  33. data/test/jsi_json_node_test.rb +95 -126
  34. data/test/jsi_json_pointer_test.rb +102 -0
  35. data/test/jsi_typelike_as_json_test.rb +53 -0
  36. data/test/metaschema_node_test.rb +19 -0
  37. data/test/schema_module_test.rb +21 -0
  38. data/test/schema_test.rb +109 -97
  39. data/test/spreedly_openapi_test.rb +8 -0
  40. data/test/test_helper.rb +42 -8
  41. data/test/util_test.rb +14 -14
  42. metadata +54 -25
  43. data/LICENSE.txt +0 -21
  44. data/lib/jsi/schema_instance_json_coder.rb +0 -83
  45. data/lib/jsi/struct_json_coder.rb +0 -30
  46. data/test/schema_instance_json_coder_test.rb +0 -121
  47. data/test/struct_json_coder_test.rb +0 -130
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ # JSI Schema Modules are extended with JSI::SchemaModule
5
+ module SchemaModule
6
+ # @return [String] absolute schema_id of the schema this module represents.
7
+ # see {Schema#schema_id}.
8
+ def schema_id
9
+ schema.schema_id
10
+ end
11
+
12
+ # @return [String]
13
+ def inspect
14
+ uri = schema.schema_id || schema.node_ptr.uri
15
+ if name
16
+ "#{name} (#{uri})"
17
+ else
18
+ "(JSI Schema Module: #{uri})"
19
+ end
20
+ end
21
+
22
+ # invokes {JSI::Schema#new_jsi} on this module's schema, passing the given instance.
23
+ # @return [JSI::Base] a JSI whose instance is the given instance
24
+ def new_jsi(instance, *a, &b)
25
+ schema.new_jsi(instance, *a, &b)
26
+ end
27
+ end
28
+
29
+ # this module is just a namespace for schema classes.
30
+ module SchemaClasses
31
+ class << self
32
+ include Util::Memoize
33
+
34
+ # see {JSI.class_for_schemas}
35
+ def class_for_schemas(schema_objects)
36
+ schemas = schema_objects.map { |schema_object| JSI::Schema.from_object(schema_object) }.to_set
37
+ jsi_memoize(:class_for_schemas, schemas) do |schemas|
38
+ Class.new(Base).instance_exec(schemas) do |schemas|
39
+ define_singleton_method(:jsi_class_schemas) { schemas }
40
+ define_method(:jsi_schemas) { schemas }
41
+ schemas.each { |schema| include(schema.jsi_schema_module) }
42
+ jsi_class = self
43
+ define_method(:jsi_class) { jsi_class }
44
+
45
+ self
46
+ end
47
+ end
48
+ end
49
+
50
+ # a module for the given schema, with accessor methods for any object property names the schema
51
+ # identifies (see {JSI::Schema#described_object_property_names}).
52
+ #
53
+ # defines a singleton method #schema to access the {JSI::Schema} this module represents, and extends
54
+ # the module with {JSI::SchemaModule}.
55
+ def module_for_schema(schema_object)
56
+ schema = JSI::Schema.from_object(schema_object)
57
+ jsi_memoize(:module_for_schema, schema) do |schema|
58
+ Module.new.tap do |m|
59
+ m.module_eval do
60
+ define_singleton_method(:schema) { schema }
61
+
62
+ extend SchemaModule
63
+
64
+ include JSI::SchemaClasses.accessor_module_for_schema(schema, conflicting_modules: [JSI::Base, JSI::PathedArrayNode, JSI::PathedHashNode])
65
+
66
+ @possibly_schema_node = schema
67
+ extend(SchemaModulePossibly)
68
+ schema.jsi_schemas.each do |schema_schema|
69
+ extend(JSI::SchemaClasses.accessor_module_for_schema(schema_schema, conflicting_modules: [Module, SchemaModule, SchemaModulePossibly]))
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ # @param schema [JSI::Schema] a schema for which to define accessors for any described property names
77
+ # @param conflicting_modules [Enumerable<Module>] an array of modules (or classes) which
78
+ # may be used alongside the accessor module. methods defined by any conflicting_module
79
+ # will not be defined as accessors.
80
+ # @return [Module] a module of accessors (setters and getters) for described property names of the given
81
+ # schema
82
+ def accessor_module_for_schema(schema, conflicting_modules: )
83
+ unless schema.is_a?(JSI::Schema)
84
+ raise(JSI::Schema::NotASchemaError, "not a schema: #{schema.pretty_inspect.chomp}")
85
+ end
86
+ jsi_memoize(:accessor_module_for_schema, schema, conflicting_modules) do |schema, conflicting_modules|
87
+ Module.new.tap do |m|
88
+ m.module_eval do
89
+ conflicting_instance_methods = (conflicting_modules + [m]).map do |mod|
90
+ mod.instance_methods + mod.private_instance_methods
91
+ end.inject(Set.new, &:|)
92
+ accessors_to_define = schema.described_object_property_names.map(&:to_s) - conflicting_instance_methods.map(&:to_s)
93
+ accessors_to_define.each do |property_name|
94
+ define_method(property_name) do
95
+ self[property_name]
96
+ end
97
+ define_method("#{property_name}=") do |value|
98
+ self[property_name] = value
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ # a JSI::Schema module and a JSI::NotASchemaModule are both a SchemaModulePossibly.
109
+ # this module provides a #[] method.
110
+ module SchemaModulePossibly
111
+ attr_reader :possibly_schema_node
112
+
113
+ # subscripting a JSI schema module or a NotASchemaModule will subscript the node, and
114
+ # if the result is a JSI::Schema, return a JSI::Schema class; if it is a PathedNode,
115
+ # return a NotASchemaModule; or if it is another value (a basic type), return that value.
116
+ #
117
+ # @param token [Object]
118
+ # @return [Class, NotASchemaModule, Object]
119
+ def [](token)
120
+ sub = @possibly_schema_node[token]
121
+ if sub.is_a?(JSI::Schema)
122
+ sub.jsi_schema_module
123
+ elsif sub.is_a?(JSI::PathedNode)
124
+ NotASchemaModule.new(sub)
125
+ else
126
+ sub
127
+ end
128
+ end
129
+ end
130
+
131
+ # a schema module is a module which represents a schema. a NotASchemaModule represents
132
+ # a node in a schema's document which is not a schema, such as the 'properties'
133
+ # node (which contains schemas but is not a schema).
134
+ #
135
+ # a NotASchemaModule is extended with the module_for_schema of the node's schema.
136
+ #
137
+ # NotASchemaModule holds a node which is not a schema. when subscripted, it subscripts
138
+ # its node. if the value is a JSI::Schema, its schema module is returned. if the value
139
+ # is another node, a NotASchemaModule for that node is returned. otherwise - when the
140
+ # value is a basic type - that value itself is returned.
141
+ class NotASchemaModule
142
+ # @param node [JSI::PathedNode]
143
+ def initialize(node)
144
+ unless node.is_a?(JSI::PathedNode)
145
+ raise(TypeError, "not JSI::PathedNode: #{node.pretty_inspect.chomp}")
146
+ end
147
+ if node.is_a?(JSI::Schema)
148
+ raise(TypeError, "cannot instantiate NotASchemaModule for a JSI::Schema node: #{node.pretty_inspect.chomp}")
149
+ end
150
+ @possibly_schema_node = node
151
+ node.jsi_schemas.each do |schema|
152
+ extend(JSI::SchemaClasses.accessor_module_for_schema(schema, conflicting_modules: [NotASchemaModule, SchemaModulePossibly]))
153
+ end
154
+ end
155
+
156
+ include SchemaModulePossibly
157
+ end
158
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ SimpleWrap = JSI::Schema.new({
5
+ "additionalProperties": {"$ref": "#"},
6
+ "items": {"$ref": "#"}
7
+ }).jsi_schema_module
8
+
9
+ # SimpleWrap is a JSI schema module which recursively wraps nested structures
10
+ module SimpleWrap
11
+ end
12
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSI
2
4
  # a module relating to objects that act like Hash or Array instances
3
5
  module Typelike
@@ -36,7 +38,9 @@ module JSI
36
38
  # @raise [TypeError] when the object (or an object nested with a hash or
37
39
  # array of object) cannot be expressed as json
38
40
  def self.as_json(object, *opt)
39
- if object.respond_to?(:to_hash)
41
+ if object.is_a?(JSI::PathedNode)
42
+ as_json(object.node_content, *opt)
43
+ elsif object.respond_to?(:to_hash)
40
44
  (object.respond_to?(:map) ? object : object.to_hash).map do |k, v|
41
45
  unless k.is_a?(Symbol) || k.respond_to?(:to_str)
42
46
  raise(TypeError, "json object (hash) cannot be keyed with: #{k.pretty_inspect.chomp}")
@@ -72,7 +76,7 @@ module JSI
72
76
  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)
73
77
  DESTRUCTIVE_METHODS = %w(clear delete delete_if keep_if reject! replace select! shift)
74
78
  # these return a modified copy
75
- safe_modified_copy_methods = %w(compact merge)
79
+ safe_modified_copy_methods = %w(compact)
76
80
  # select and reject will return a modified copy but need the yielded block variable value from #[]
77
81
  safe_kv_block_modified_copy_methods = %w(select reject)
78
82
  SAFE_METHODS = SAFE_KEY_ONLY_METHODS | SAFE_KEY_VALUE_METHODS
@@ -82,7 +86,7 @@ module JSI
82
86
  end
83
87
  safe_modified_copy_methods.each do |method_name|
84
88
  define_method(method_name) do |*a, &b|
85
- JSI::Typelike.modified_copy(self) do |object_to_modify|
89
+ modified_copy do |object_to_modify|
86
90
  responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_hash
87
91
  responsive_object.public_send(method_name, *a, &b)
88
92
  end
@@ -90,7 +94,7 @@ module JSI
90
94
  end
91
95
  safe_kv_block_modified_copy_methods.each do |method_name|
92
96
  define_method(method_name) do |*a, &b|
93
- JSI::Typelike.modified_copy(self) do |object_to_modify|
97
+ modified_copy do |object_to_modify|
94
98
  responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_hash
95
99
  responsive_object.public_send(method_name, *a) do |k, _v|
96
100
  b.call(k, self[k])
@@ -99,39 +103,66 @@ module JSI
99
103
  end
100
104
  end
101
105
 
106
+ # the same as Hash#update
107
+ # @param other [#to_hash] the other hash to update this hash from
108
+ # @yield [key, oldval, newval] for entries with duplicate keys, the value of each duplicate key
109
+ # is determined by calling the block with the key, its value in hsh and its value in other_hash.
110
+ # @return self, updated with other
111
+ # @raise [TypeError] when `other` does not respond to #to_hash
112
+ def update(other, &block)
113
+ unless other.respond_to?(:to_hash)
114
+ raise(TypeError, "cannot update with argument that does not respond to #to_hash: #{other.pretty_inspect.chomp}")
115
+ end
116
+ self_respondingto_key = self.respond_to?(:key?) ? self : to_hash
117
+ other.to_hash.each_pair do |key, value|
118
+ if block_given? && self_respondingto_key.key?(key)
119
+ value = yield(key, self[key], value)
120
+ end
121
+ self[key] = value
122
+ end
123
+ self
124
+ end
125
+
126
+ alias_method :merge!, :update
127
+
128
+ # the same as Hash#merge
129
+ # @param other [#to_hash] the other hash to merge into this
130
+ # @yield [key, oldval, newval] for entries with duplicate keys, the value of each duplicate key
131
+ # is determined by calling the block with the key, its value in hsh and its value in other_hash.
132
+ # @return duplicate of this hash with the other hash merged in
133
+ # @raise [TypeError] when `other` does not respond to #to_hash
134
+ def merge(other, &block)
135
+ dup.update(other, &block)
136
+ end
137
+
102
138
  # @return [String] basically the same #inspect as Hash, but has the
103
139
  # class name and, if responsive, self's #object_group_text
104
140
  def inspect
105
- object_group_text = respond_to?(:object_group_text) ? ' ' + self.object_group_text : ''
106
- "\#{<#{self.class}#{object_group_text}>#{empty? ? '' : ' '}#{self.map { |k, v| "#{k.inspect} => #{v.inspect}" }.join(', ')}}"
141
+ object_group_str = (respond_to?(:object_group_text) ? self.object_group_text : [self.class]).join(' ')
142
+ "\#{<#{object_group_str}>#{empty? ? '' : ' '}#{self.map { |k, v| "#{k.inspect} => #{v.inspect}" }.join(', ')}}"
107
143
  end
108
144
 
109
- # @return [String] see #inspect
110
- def to_s
111
- inspect
112
- end
145
+ alias_method :to_s, :inspect
113
146
 
114
147
  # pretty-prints a representation this node to the given printer
115
148
  # @return [void]
116
149
  def pretty_print(q)
117
- q.instance_exec(self) do |obj|
118
- object_group_text = obj.respond_to?(:object_group_text) ? ' ' + obj.object_group_text : ''
119
- text "\#{<#{obj.class}#{object_group_text}>"
120
- group_sub {
121
- nest(2) {
122
- breakable(obj.any? { true } ? ' ' : '')
123
- seplist(obj, nil, :each_pair) { |k, v|
124
- group {
125
- pp k
126
- text ' => '
127
- pp v
128
- }
150
+ object_group_str = (respond_to?(:object_group_text) ? object_group_text : [self.class]).join(' ')
151
+ q.text "\#{<#{object_group_str}>"
152
+ q.group_sub {
153
+ q.nest(2) {
154
+ q.breakable(any? { true } ? ' ' : '')
155
+ q.seplist(self, nil, :each_pair) { |k, v|
156
+ q.group {
157
+ q.pp k
158
+ q.text ' => '
159
+ q.pp v
129
160
  }
130
161
  }
131
162
  }
132
- breakable ''
133
- text '}'
134
- end
163
+ }
164
+ q.breakable ''
165
+ q.text '}'
135
166
  end
136
167
  end
137
168
 
@@ -162,7 +193,7 @@ module JSI
162
193
  end
163
194
  safe_modified_copy_methods.each do |method_name|
164
195
  define_method(method_name) do |*a, &b|
165
- JSI::Typelike.modified_copy(self) do |object_to_modify|
196
+ modified_copy do |object_to_modify|
166
197
  responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_ary
167
198
  responsive_object.public_send(method_name, *a, &b)
168
199
  end
@@ -170,7 +201,7 @@ module JSI
170
201
  end
171
202
  safe_el_block_methods.each do |method_name|
172
203
  define_method(method_name) do |*a, &b|
173
- JSI::Typelike.modified_copy(self) do |object_to_modify|
204
+ modified_copy do |object_to_modify|
174
205
  i = 0
175
206
  responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_ary
176
207
  responsive_object.public_send(method_name, *a) do |_e|
@@ -183,32 +214,27 @@ module JSI
183
214
  # @return [String] basically the same #inspect as Array, but has the
184
215
  # class name and, if responsive, self's #object_group_text
185
216
  def inspect
186
- object_group_text = respond_to?(:object_group_text) ? ' ' + self.object_group_text : ''
187
- "\#[<#{self.class}#{object_group_text}>#{empty? ? '' : ' '}#{self.map { |e| e.inspect }.join(', ')}]"
217
+ object_group_str = (respond_to?(:object_group_text) ? object_group_text : [self.class]).join(' ')
218
+ "\#[<#{object_group_str}>#{empty? ? '' : ' '}#{self.map { |e| e.inspect }.join(', ')}]"
188
219
  end
189
220
 
190
- # @return [String] see #inspect
191
- def to_s
192
- inspect
193
- end
221
+ alias_method :to_s, :inspect
194
222
 
195
223
  # pretty-prints a representation this node to the given printer
196
224
  # @return [void]
197
225
  def pretty_print(q)
198
- q.instance_exec(self) do |obj|
199
- object_group_text = obj.respond_to?(:object_group_text) ? ' ' + obj.object_group_text : ''
200
- text "\#[<#{obj.class}#{object_group_text}>"
201
- group_sub {
202
- nest(2) {
203
- breakable(obj.any? { true } ? ' ' : '')
204
- seplist(obj, nil, :each) { |e|
205
- pp e
206
- }
226
+ object_group_str = (respond_to?(:object_group_text) ? object_group_text : [self.class]).join(' ')
227
+ q.text "\#[<#{object_group_str}>"
228
+ q.group_sub {
229
+ q.nest(2) {
230
+ q.breakable(any? { true } ? ' ' : '')
231
+ q.seplist(self, nil, :each) { |e|
232
+ q.pp e
207
233
  }
208
234
  }
209
- breakable ''
210
- text ']'
211
- end
235
+ }
236
+ q.breakable ''
237
+ q.text ']'
212
238
  end
213
239
  end
214
240
  end
@@ -1,5 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSI
4
+ # JSI::Util classes, modules, constants, and methods are INTERNAL and will be added and removed without warning.
5
+ # do not rely on them.
2
6
  module Util
7
+ # a proc which does nothing
8
+ NOOP = -> (*_) { }
9
+
3
10
  # returns a version of the given hash, in which any symbol keys are
4
11
  # converted to strings. behavior on collisions is undefined (but in the
5
12
  # future could take a block like
@@ -11,54 +18,36 @@ module JSI
11
18
  # the return if you need to ensure it is not the same instance as the
12
19
  # argument instance.
13
20
  #
14
- # @param hash [#to_hash] the hash from which to convert symbol keys to strings
21
+ # @param hashlike [#to_hash] the hash from which to convert symbol keys to strings
15
22
  # @return [same class as the param `hash`, or Hash if the former cannot be done] a
16
23
  # hash(-like) instance containing no symbol keys
17
- def stringify_symbol_keys(hash)
18
- unless hash.respond_to?(:to_hash)
19
- raise(ArgumentError, "expected argument to be a hash; got #{hash.class.inspect}: #{hash.pretty_inspect.chomp}")
24
+ def stringify_symbol_keys(hashlike)
25
+ unless hashlike.respond_to?(:to_hash)
26
+ raise(ArgumentError, "expected argument to be a hash; got #{hashlike.class.inspect}: #{hashlike.pretty_inspect.chomp}")
20
27
  end
21
- JSI::Typelike.modified_copy(hash) do |hash_|
22
- changed = false
28
+ JSI::Typelike.modified_copy(hashlike) do |hash|
23
29
  out = {}
24
- hash_.each do |k, v|
25
- if k.is_a?(Symbol)
26
- changed = true
27
- k = k.to_s
28
- end
29
- out[k] = v
30
+ hash.each do |k, v|
31
+ out[k.is_a?(Symbol) ? k.to_s : k] = v
30
32
  end
31
- changed ? out : hash_
33
+ out
32
34
  end
33
35
  end
34
36
 
35
37
  def deep_stringify_symbol_keys(object)
36
38
  if object.respond_to?(:to_hash)
37
39
  JSI::Typelike.modified_copy(object) do |hash|
38
- changed = false
39
40
  out = {}
40
41
  (hash.respond_to?(:each) ? hash : hash.to_hash).each do |k, v|
41
- if k.is_a?(Symbol)
42
- changed = true
43
- k = k.to_s
44
- end
45
- out_k = deep_stringify_symbol_keys(k)
46
- out_v = deep_stringify_symbol_keys(v)
47
- changed = true if out_k.object_id != k.object_id
48
- changed = true if out_v.object_id != v.object_id
49
- out[out_k] = out_v
42
+ out[k.is_a?(Symbol) ? k.to_s : deep_stringify_symbol_keys(k)] = deep_stringify_symbol_keys(v)
50
43
  end
51
- changed ? out : hash
44
+ out
52
45
  end
53
46
  elsif object.respond_to?(:to_ary)
54
47
  JSI::Typelike.modified_copy(object) do |ary|
55
- changed = false
56
- out = (ary.respond_to?(:each) ? ary : ary.to_ary).map do |e|
57
- out_e = deep_stringify_symbol_keys(e)
58
- changed = true if out_e.object_id != e.object_id
59
- out_e
48
+ (ary.respond_to?(:each) ? ary : ary.to_ary).map do |e|
49
+ deep_stringify_symbol_keys(e)
60
50
  end
61
- changed ? out : ary
62
51
  end
63
52
  else
64
53
  object
@@ -69,7 +58,7 @@ module JSI
69
58
  # to define a recursive function to return the length of an array:
70
59
  #
71
60
  # length = ycomb do |len|
72
- # proc{|list| list == [] ? 0 : 1 + len.call(list[1..-1]) }
61
+ # proc { |list| list == [] ? 0 : 1 + len.call(list[1..-1]) }
73
62
  # end
74
63
  #
75
64
  # see https://secure.wikimedia.org/wikipedia/en/wiki/Fixed_point_combinator#Y_combinator
@@ -78,41 +67,42 @@ module JSI
78
67
  proc { |f| f.call(f) }.call(proc { |f| yield proc { |*x| f.call(f).call(*x) } })
79
68
  end
80
69
  module_function :ycomb
81
- end
82
- public
83
- extend Util
84
70
 
85
- module FingerprintHash
86
- def ==(other)
87
- object_id == other.object_id || (other.respond_to?(:fingerprint) && other.fingerprint == self.fingerprint)
88
- end
71
+ module FingerprintHash
72
+ # overrides BasicObject#==
73
+ def ==(other)
74
+ object_id == other.object_id || (other.respond_to?(:jsi_fingerprint) && other.jsi_fingerprint == self.jsi_fingerprint)
75
+ end
89
76
 
90
- alias_method :eql?, :==
77
+ alias_method :eql?, :==
91
78
 
92
- def hash
93
- fingerprint.hash
79
+ # overrides Kernel#hash
80
+ def hash
81
+ jsi_fingerprint.hash
82
+ end
94
83
  end
95
- end
96
84
 
97
- module Memoize
98
- def memoize(key, *args_)
99
- @memos ||= {}
100
- @memos[key] ||= Hash.new do |h, args|
101
- h[args] = yield(*args)
85
+ module Memoize
86
+ def jsi_memoize(key, *args_)
87
+ @jsi_memos ||= {}
88
+ @jsi_memos[key] ||= Hash.new do |h, args|
89
+ h[args] = yield(*args)
90
+ end
91
+ @jsi_memos[key][args_]
102
92
  end
103
- @memos[key][args_]
104
- end
105
93
 
106
- def clear_memo(key, *args)
107
- @memos ||= {}
108
- if @memos[key]
109
- if args.empty?
110
- @memos[key].clear
111
- else
112
- @memos[key].delete(args)
94
+ def jsi_clear_memo(key, *args)
95
+ @jsi_memos ||= {}
96
+ if @jsi_memos[key]
97
+ if args.empty?
98
+ @jsi_memos[key].clear
99
+ else
100
+ @jsi_memos[key].delete(args)
101
+ end
113
102
  end
114
103
  end
115
104
  end
116
105
  end
117
- extend Memoize
106
+ public
107
+ extend Util
118
108
  end