Skip to content

Commit

Permalink
Merge pull request #849 from Dynamoid/ak/fix-dumping-of-custom-type-t…
Browse files Browse the repository at this point in the history
…hat-mixes-adapter-interface

Fix dumping of custom type that mixes adapter interface
  • Loading branch information
andrykonchin authored Jan 13, 2025
2 parents 30ab6b6 + 85111cf commit 4902a5c
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
bundler:
- latest
ruby:
- "3.3"
- "3.4"
runs-on: ubuntu-latest
env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/style.gemfile
Expand Down
4 changes: 2 additions & 2 deletions .rubocop_thread_safety.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# It would be good to make the gem more thread safe, but at the moment it is not entirely.
# TODO: Comment out the following to see code needing to be refactored for thread safety!
ThreadSafety/ClassAndModuleAttributes:
Enabled: false
ThreadSafety/ClassInstanceVariable:
Enabled: false
ThreadSafety/ClassAndModuleAttributes:
Enabled: false
6 changes: 3 additions & 3 deletions lib/dynamoid/dumping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,10 @@ class CustomTypeDumper < Base
def process(value)
field_class = @options[:type]

if value.respond_to?(:dynamoid_dump)
value.dynamoid_dump
elsif field_class.respond_to?(:dynamoid_dump)
if field_class.respond_to?(:dynamoid_dump)
field_class.dynamoid_dump(value)
elsif value.respond_to?(:dynamoid_dump)
value.dynamoid_dump
else
raise ArgumentError, "Neither #{field_class} nor #{value} supports serialization for Dynamoid."
end
Expand Down
15 changes: 15 additions & 0 deletions spec/dynamoid/dumping_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,21 @@ def self.load(str)
end
end

context 'Custom type with adapter interface provided' do
let(:klass) do
new_class do |_options|
field :user, DumpingSpecs::UserWithAdapterInterface
end
end

it "prefers adapter's .dynamoid_dump method over #dynamoid_dump" do
user = DumpingSpecs::UserWithAdapterInterface.new('John')
obj = klass.create(user: user)

expect(raw_attributes(obj)[:user]).to eql('John (dumped with .dynamoid_dump)')
end
end

context 'DynamoDB type specified' do
let(:klass) do
new_class do
Expand Down
35 changes: 32 additions & 3 deletions spec/fixtures/dumping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,35 @@ def self.dynamoid_load(string)
end
end

# implements both #dynamoid_dump and .dynamoid_dump methods
class UserWithAdapterInterface
attr_accessor :name

def initialize(name)
self.name = name
end

def dynamoid_dump
"#{name} (dumped with #dynamoid_dump)"
end

def eql?(other)
name == other.name
end

def hash
name.hash
end

def self.dynamoid_dump(user)
"#{user.name} (dumped with .dynamoid_dump)"
end

def self.dynamoid_load(string)
new(string.to_s)
end
end

# doesn't implement #dynamoid_dump/#dynamoid_load methods so requires an adapter
class UserValue
attr_accessor :name
Expand All @@ -48,7 +77,7 @@ def self.dynamoid_dump(user)
end

def self.dynamoid_load(string)
User.new(string.to_s)
UserValue.new(string.to_s)
end
end

Expand All @@ -58,8 +87,8 @@ def self.dynamoid_dump(user)
end

def self.dynamoid_load(array)
array = array.name.split if array.is_a?(User)
User.new(array.join(' '))
array = array.name.split if array.is_a?(UserValue)
UserValue.new(array.join(' '))
end

def self.dynamoid_field_type
Expand Down

0 comments on commit 4902a5c

Please sign in to comment.