Skip to content

Commit

Permalink
search to define type in dynamic attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
Giallombardo Nathan committed Jul 15, 2024
1 parent 2793b20 commit 80ec914
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 3 deletions.
35 changes: 32 additions & 3 deletions lib/couchbase-orm/attributes/dynamic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,42 @@ def _assign_attribute(name, value)
if responds
public_send(name.writer, value)
else
type = value.class.to_s.underscore.to_sym
type = :hash if type == :"active_support/hash_with_indifferent_access"
type = ActiveModel::Type.lookup(type)
type = define_attribute_type(value)
type = if type == :array
item_type = define_attribute_type(value.first)
ActiveModel::Type.lookup(type, type: item_type)
else
ActiveModel::Type.lookup(type)
end
@attributes[name] = ActiveModel::Attribute.from_database(name, value, type)
end
end

# Determines the attribute type based on the value provided.
#
# This method converts the class of the value to a symbol and handles special cases
# for `ActiveSupport::HashWithIndifferentAccess`, booleans, and nil values.
#
# @param [Object] value The value whose type needs to be determined.
# @return [Symbol] The determined type of the attribute.
#
# @example Determining types of various values
# define_attribute_type(123) # => :integer
# define_attribute_type("Hello") # => :string
# define_attribute_type(true) # => :boolean
# define_attribute_type(false) # => :boolean
# define_attribute_type(nil) # => :raw
# define_attribute_type(ActiveSupport::HashWithIndifferentAccess.new) # => :hash
def define_attribute_type(value)
type = value.class.to_s.underscore.to_sym
return :hash if type == :"active_support/hash_with_indifferent_access"
return :boolean if type == :true_class
return :boolean if type == :false_class
return :raw if type == :nil_class

type
end

# Define a reader method for a dynamic attribute.
#
# @example Define a reader method.
Expand Down
2 changes: 2 additions & 0 deletions lib/couchbase-orm/types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require 'couchbase-orm/types/nested'
require 'couchbase-orm/types/encrypted'
require 'couchbase-orm/types/hash'
require 'couchbase-orm/types/raw'

if ActiveModel::VERSION::MAJOR <= 6
# In Rails 5, the type system cannot allow overriding the default types
Expand All @@ -22,3 +23,4 @@
ActiveModel::Type.register(:nested, CouchbaseOrm::Types::Nested)
ActiveModel::Type.register(:encrypted, CouchbaseOrm::Types::Encrypted)
ActiveModel::Type.register(:hash, CouchbaseOrm::Types::Hash)
ActiveModel::Type.register(:raw, CouchbaseOrm::Types::Raw)
15 changes: 15 additions & 0 deletions lib/couchbase-orm/types/raw.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module CouchbaseOrm
module Types
class Raw < ActiveModel::Type::Value
def cast(values)
values
end

def serialize(values)
values
end
end
end
end
74 changes: 74 additions & 0 deletions spec/attribute_dynamic_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,79 @@ class AttributeDynamicTest < CouchbaseOrm::Base
expect(AttributeDynamicTest.find_by_id(dynamic.id)).to have_attributes(new_attribute: 2)
dynamic.destroy
end

context 'with integer' do
it 'accepts unknown attribute from Coucbbase' do
dynamic = AttributeDynamicTest.create!(name: 'joe', new_attribute: 2)
expect(AttributeDynamicTest.find_by_id(dynamic.id)).to have_attributes(new_attribute: 2)
dynamic.destroy
end
end

context 'with decimal' do
it 'accepts unknown attribute from Coucbbase' do
dynamic = AttributeDynamicTest.create!(name: 'joe', new_attribute: 2.0)
expect(AttributeDynamicTest.find_by_id(dynamic.id)).to have_attributes(new_attribute: 2.0)
dynamic.destroy
end
end

context 'with true_class' do
it 'accepts unknown attribute from Coucbbase' do
dynamic = AttributeDynamicTest.create!(name: 'joe', new_attribute: true)
expect(AttributeDynamicTest.find_by_id(dynamic.id)).to have_attributes(new_attribute: true)
dynamic.destroy
end
end

context 'with false_class' do
it 'accepts unknown attribute from Coucbbase' do
dynamic = AttributeDynamicTest.create!(name: 'joe', new_attribute: false)
expect(AttributeDynamicTest.find_by_id(dynamic.id)).to have_attributes(new_attribute: false)
dynamic.destroy
end
end

context 'with string' do
it 'accepts unknown attribute from Coucbbase' do
dynamic = AttributeDynamicTest.create!(name: 'joe', new_attribute: 'a string')
expect(AttributeDynamicTest.find_by_id(dynamic.id)).to have_attributes(new_attribute: 'a string')
dynamic.destroy
end
end

context 'with hash' do
it 'accepts unknown attribute from Coucbbase' do
dynamic = AttributeDynamicTest.create!(name: 'joe', new_attribute: { a: 'hash' })
expect(AttributeDynamicTest.find_by_id(dynamic.id)).to have_attributes(new_attribute: { a: 'hash' })
dynamic.destroy
end
end

context 'with nil' do
it 'accepts unknown attribute from Coucbbase' do
dynamic = AttributeDynamicTest.create!(name: 'joe', new_attribute: nil)
expect(AttributeDynamicTest.find_by_id(dynamic.id)).to have_attributes(new_attribute: nil)
dynamic.destroy
end
end

context 'with array' do
it 'accepts unknown attribute from Coucbbase' do
dynamic = AttributeDynamicTest.create!(name: 'joe', new_attribute: [{ a: 'hash' }])
expect(AttributeDynamicTest.find_by_id(dynamic.id)).to have_attributes(new_attribute: [{ a: 'hash' }])
dynamic.destroy
end
end

context 'with raw' do
it 'not accepts to change string to number' do
dynamic = AttributeDynamicTest.create!(name: 'joe', new_attribute: 'an string')
dynamic.new_attribute = 1
dynamic.save!
expect(AttributeDynamicTest.find_by_id(dynamic.id)).to have_attributes(new_attribute: '1')
dynamic.destroy!
end
end
end
end

0 comments on commit 80ec914

Please sign in to comment.