jsi 0.0.4 → 0.4.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.
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