Skip to content

Commit

Permalink
Merge a37f681 into fca9c5e
Browse files Browse the repository at this point in the history
  • Loading branch information
notEthan authored Aug 19, 2018
2 parents fca9c5e + a37f681 commit 615ffbb
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 33 deletions.
8 changes: 8 additions & 0 deletions lib/scorpio/json/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ def [](k)
end
end

def []=(k, v)
if v.is_a?(Node)
content[k] = v.content
else
content[k] = v
end
end

def deref
content = self.content

Expand Down
25 changes: 12 additions & 13 deletions lib/scorpio/schema_instance_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,8 @@ def fingerprint

private
def instance=(thing)
clear_memo(:[])
if instance_variable_defined?(:@instance)
if @instance.class != thing.class
raise(Scorpio::Bug, "will not accept instance of different class #{thing.class} to current instance class #{@instance.class} on #{self.class.inspect}")
end
raise(Scorpio::Bug, "overwriting instance is not supported")
end
if thing.is_a?(SchemaInstanceBase)
warn "assigning instance to a SchemaInstanceBase instance is incorrect. received: #{thing.pretty_inspect.chomp}"
Expand All @@ -152,6 +149,15 @@ def instance=(thing)
@instance = Scorpio::JSON::Node.new_by_type(Scorpio.deep_stringify_symbol_keys(thing), [])
end
end

def subscript_assign(subscript, value)
clear_memo(:[], subscript)
if value.is_a?(SchemaInstanceBase)
instance[subscript] = value.instance
else
instance[subscript] = value
end
end
end

# this module is just a namespace for schema classes.
Expand Down Expand Up @@ -271,11 +277,8 @@ def [](property_name_)
end
end
end

def []=(property_name, value)
self.instance = instance.modified_copy do |hash|
hash.merge(property_name => value)
end
subscript_assign(property_name, value)
end
end

Expand Down Expand Up @@ -313,11 +316,7 @@ def [](i_)
end
end
def []=(i, value)
self.instance = instance.modified_copy do |ary|
ary.each_with_index.map do |el, ary_i|
ary_i == i ? value : el
end
end
subscript_assign(i, value)
end
end
end
10 changes: 8 additions & 2 deletions lib/scorpio/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,15 @@ def memoize(key, *args_)
@memos[key][args_]
end

def clear_memo(key)
def clear_memo(key, *args)
@memos ||= {}
@memos[key].clear if @memos[key]
if @memos[key]
if args.empty?
@memos[key].clear
else
@memos[key].delete(args)
end
end
end
end
extend Memoize
Expand Down
6 changes: 3 additions & 3 deletions test/schema_instance_base_array_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@
assert_instance_of(Scorpio.class_for_schema(schema.schema_node['items'][2]), orig_2)
assert_instance_of(Scorpio.class_for_schema(schema.schema_node['items'][2]), subject[2])
end
it 'updates to a modified copy of the instance without altering the original' do
it 'modifies the instance, visible to other references to the same instance' do
orig_instance = subject.instance

subject[2] = {'y' => 'z'}

refute_equal(orig_instance, subject.instance)
assert_equal(['q', 'r'], orig_instance[2].as_json)
assert_equal(orig_instance, subject.instance)
assert_equal({'y' => 'z'}, orig_instance[2].as_json)
assert_equal({'y' => 'z'}, subject.instance[2].as_json)
assert_equal(orig_instance.class, subject.instance.class)
end
Expand Down
34 changes: 30 additions & 4 deletions test/schema_instance_base_hash_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
'type' => 'object',
'properties' => {
'foo' => {'type' => 'object'},
'bar' => {},
},
}
end
Expand All @@ -28,21 +29,46 @@
assert_instance_of(Scorpio.class_for_schema(schema.schema_node['properties']['foo']), orig_foo)
assert_instance_of(Scorpio.class_for_schema(schema.schema_node['properties']['foo']), subject['foo'])
end
it 'updates to a modified copy of the instance without altering the original' do
it 'sets a property to a schema instance with a different schema' do
orig_foo = subject['foo']

subject['foo'] = subject['bar']

# the content of the subscripts' instances is the same but the subscripts' classes are different
assert_equal([9], subject['foo'].as_json)
assert_equal([9], subject['bar'].as_json)
assert_instance_of(Scorpio.class_for_schema(schema.schema_node['properties']['foo']), subject['foo'])
assert_instance_of(Scorpio.class_for_schema(schema.schema_node['properties']['bar']), subject['bar'])
end
it 'sets a property to a schema instance with the same schema' do
other_subject = class_for_schema.new(Scorpio::JSON::Node.new_by_type({'foo' => {'x' => 'y'}, 'bar' => [9], 'baz' => true}, []))
# Given
assert_equal(other_subject, subject)

# When:
subject['foo'] = other_subject['foo']

# Then:
# still equal
assert_equal(other_subject, subject)
# but different instances
refute_equal(other_subject['foo'].object_id, subject['foo'].object_id)
end
it 'modifies the instance, visible to other references to the same instance' do
orig_instance = subject.instance

subject['foo'] = {'y' => 'z'}

refute_equal(orig_instance, subject.instance)
assert_equal({'x' => 'y'}, orig_instance['foo'].as_json)
assert_equal(orig_instance, subject.instance)
assert_equal({'y' => 'z'}, orig_instance['foo'].as_json)
assert_equal({'y' => 'z'}, subject.instance['foo'].as_json)
assert_equal(orig_instance.class, subject.instance.class)
end
describe 'when the instance is not hashlike' do
let(:instance) { nil }
it 'errors' do
err = assert_raises(NoMethodError) { subject['foo'] = 0 }
assert_match(%r(\Aundefined method `\[\]=' for #<Scorpio::SchemaClasses::X.*>\z), err.message)
assert_match(%r(\Aundefined method `\[\]=' for #<Scorpio::SchemaClasses::.*>\z), err.message)
end
end
end
Expand Down
17 changes: 6 additions & 11 deletions test/schema_instance_base_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -319,13 +319,13 @@
assert_instance_of(Scorpio.class_for_schema(schema.schema_node['properties']['foo']), orig_foo)
assert_instance_of(Scorpio.class_for_schema(schema.schema_node['properties']['foo']), subject.foo)
end
it 'updates to a modified copy of the instance without altering the original' do
it 'modifies the instance, visible to other references to the same instance' do
orig_instance = subject.instance

subject.foo = {'y' => 'z'}

refute_equal(orig_instance, subject.instance)
assert_equal({'x' => 'y'}, orig_instance['foo'].as_json)
assert_equal(orig_instance, subject.instance)
assert_equal({'y' => 'z'}, orig_instance['foo'].as_json)
assert_equal({'y' => 'z'}, subject.instance['foo'].as_json)
assert_equal(orig_instance.class, subject.instance.class)
end
Expand Down Expand Up @@ -360,18 +360,13 @@
assert_equal(['a'], Scorpio::class_for_schema({}).new(['a']).as_json(some_option: true))
end
end
describe 'ridiculous way to test instance= getting the wrong type' do
describe 'overwrite schema instance with instance=' do
# this error message indicates an internal bug (hence Bug class), so there isn't an intended way to
# trigger it using SchemaInstanceBase properly. we use it improperly just to test that code path. this
# is definitely not defined behavior.
#
# make thing whose #modified_copy behaves incorrectly, to abuse the internals of []=
let(:schema_content) { {'type' => 'object'} }

it 'errors' do
subject.instance.define_singleton_method(:modified_copy) { |*_a| [] }
err = assert_raises(Scorpio::Bug) { subject['foo'] = 'bar' }
assert_match(%r(\Awill not accept instance of different class Array to current instance class Scorpio::JSON::HashNode on Scorpio::SchemaClasses\["[a-z0-9\-]+#"\]\z), err.message)
err = assert_raises(Scorpio::Bug) { subject.send(:instance=, {'foo' => 'bar'}) }
assert_match(%r(\Aoverwriting instance is not supported\z), err.message)
end
end
end
16 changes: 16 additions & 0 deletions test/scorpio_json_node_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,22 @@
end
end
end
describe '#[]=' do
let(:document) { [0, {'x' => [{'a' => ['b']}]}] }
it 'assigns' do
node[0] = 'abcdefg'
assert_equal(['abcdefg', {'x' => [{'a' => ['b']}]}], document)
string_node = Scorpio::JSON::Node.new(document, [0])
string_node[0..2] = '0'
assert_equal(['0defg', {'x' => [{'a' => ['b']}]}], document)
node[0] = node[1]
assert_equal([{'x' => [{'a' => ['b']}]}, {'x' => [{'a' => ['b']}]}], document)
end
it 'assigns, deeper' do
node[1]['y'] = node[1]['x'][0]
assert_equal([0, {'x' => [{'a' => ['b']}], 'y' => {'a' => ['b']}}], document)
end
end
describe '#document_node' do
let(:document) { {'a' => {'b' => 3}} }
it 'has content that is the document' do
Expand Down

0 comments on commit 615ffbb

Please sign in to comment.