Since the fields are (through the thread-local BufferStack) only ever
accessed from a single Thread, there is no need to accept the additional
locking overhead of the Concurrent raw values.
We can just use simple Hashes and Arrays here for higher performance.
When setting `force: true` (the default), in both cases we not raise an
ArgumentError when setting a forbidden field and overwrite existing
fields. When setting it to `false`, we ignore forbidden or existing
fields in both cases.
We also allow a custom conflict resolution block to be passed to both
methods. In the case of deep_merge! and deep_merge, this applies to all
(potentially deeply nested) fields. Compatible objects, i.e. Hashes and
Arrays are still always merged without calling the block.
With this, we optimize the common case where we do have valid UTF-8
strings to begin with. If the given String is already frozen, as is
common for e.g. Hash keys, we don't even need to create a new object.
With this change, we also always return frozen strings from
`Rackstash::Fields::AbstractCollection#utf9_encode`. This avoids an
unecessary object copy when inserting it in a Hash and still ensures
that values are always frozen anyway.
`each_with_object` allocates an array for each kv pair. Switching to
the slightly more verbose but less allocatey `each_pair` eliminates
array allocations.
This follows the similar change in Rails:
960de47f0e
These methods are useful convenience methods to add (nested) fields to a
hash while optionally retaining existing values. Usually, `#deep_merge!`
or `#set` will be the commonly used ways to set fields to a hash.
This follows the rule that two objects which are eql? also have the same
hash value. It is also required to ensure that we can use collections in
sets, hashes and arrays where the hash-equality is checked for certain
operations.