Skip to content

Commit

Permalink
Merge pull request #95 from belousovAV/21-expose-mapper-location
Browse files Browse the repository at this point in the history
Expose failed mapper location
  • Loading branch information
rous-gg committed Jan 18, 2024
2 parents c87fd9e + f1d583f commit 395810f
Show file tree
Hide file tree
Showing 28 changed files with 321 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@ class ReeDao::PgArray < ReeMapper::AbstractWrapper
Kwargs[
name: String,
role: Nilor[Symbol, ArrayOf[Symbol]],
fields_filters: ArrayOf[ReeMapper::FieldsFilter]
fields_filters: ArrayOf[ReeMapper::FieldsFilter],
location: Nilor[String],
] => Or[Sequel::Postgres::PGArray, String]
)
def db_dump(value, name:, role: nil, fields_filters: [])
def db_dump(value, name:, role: nil, fields_filters: [], location: nil)
if !value.is_a?(Array)
raise ReeMapper::TypeError, "`#{name}` should be an array, got `#{truncate(value.inspect)}`"
raise ReeMapper::TypeError.new("`#{name}` should be an array, got `#{truncate(value.inspect)}`", location)
end

value = value.map.with_index do |el, index|
subject.type.db_dump(
el,
name: "#{name}[#{index}]",
role: role,
fields_filters: fields_filters + [subject.fields_filter]
fields_filters: fields_filters + [subject.fields_filter],
location: subject.location,
)
end

Expand All @@ -37,20 +39,22 @@ def db_dump(value, name:, role: nil, fields_filters: [])
Kwargs[
name: String,
role: Nilor[Symbol, ArrayOf[Symbol]],
fields_filters: ArrayOf[ReeMapper::FieldsFilter]
fields_filters: ArrayOf[ReeMapper::FieldsFilter],
location: Nilor[String],
] => Array
).throws(ReeMapper::TypeError)
def db_load(value, name:, role: nil, fields_filters: [])
def db_load(value, name:, role: nil, fields_filters: [], location: nil)
if !value.is_a?(Sequel::Postgres::PGArray)
raise ReeMapper::TypeError, "`#{name}` should be a Sequel::Postgres::PGArray, got `#{truncate(value.inspect)}`"
raise ReeMapper::TypeError.new("`#{name}` should be a Sequel::Postgres::PGArray, got `#{truncate(value.inspect)}`", location)
end

value.map.with_index do |val, index|
subject.type.db_load(
val,
name: "#{name}[#{index}]",
role: role,
fields_filters: fields_filters + [subject.fields_filter]
fields_filters: fields_filters + [subject.fields_filter],
location: subject.location,
)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class ReeDao::PgJsonb < ReeMapper::AbstractWrapper
Kwargs[
name: String,
role: Nilor[Symbol, ArrayOf[Symbol]],
fields_filters: ArrayOf[ReeMapper::FieldsFilter]
fields_filters: ArrayOf[ReeMapper::FieldsFilter],
location: Nilor[String],
] => Or[
Sequel::Postgres::JSONBHash,
Sequel::Postgres::JSONBArray,
Expand All @@ -20,18 +21,19 @@ class ReeDao::PgJsonb < ReeMapper::AbstractWrapper
Sequel::Postgres::JSONBNull
]
).throws(ReeMapper::TypeError)
def db_dump(value, name:, role: nil, fields_filters: [])
def db_dump(value, name:, role: nil, fields_filters: [], location: nil)
value = subject.type.db_dump(
value,
name: name,
role: role,
fields_filters: fields_filters + [subject.fields_filter]
fields_filters: fields_filters + [subject.fields_filter],
location: subject.location,
)

begin
Sequel.pg_jsonb_wrap(value)
rescue Sequel::Error
raise ReeMapper::TypeError, "`#{name}` should be an jsonb primitive, got `#{truncate(value.inspect)}`"
raise ReeMapper::TypeError.new("`#{name}` should be an jsonb primitive, got `#{truncate(value.inspect)}`", location)
end
end

Expand All @@ -40,7 +42,8 @@ def db_dump(value, name:, role: nil, fields_filters: [])
Kwargs[
name: String,
role: Nilor[Symbol, ArrayOf[Symbol]],
fields_filters: ArrayOf[ReeMapper::FieldsFilter]
fields_filters: ArrayOf[ReeMapper::FieldsFilter],
location: Nilor[String],
] => Or[
Hash,
Array,
Expand All @@ -52,7 +55,7 @@ def db_dump(value, name:, role: nil, fields_filters: [])
Rational,
]
).throws(ReeMapper::TypeError)
def db_load(value, name:, role: nil, fields_filters: [])
def db_load(value, name:, role: nil, fields_filters: [], location: nil)
value = case value
when Sequel::Postgres::JSONBHash
ReeObject::ToHash.new.call(value.to_h)
Expand All @@ -61,14 +64,15 @@ def db_load(value, name:, role: nil, fields_filters: [])
when Numeric, String, TrueClass, FalseClass, NilClass
value
else
raise ReeMapper::TypeError, "`#{name}` should be a Sequel::Postgres::JSONB, got `#{truncate(value.inspect)}`"
raise ReeMapper::TypeError.new("`#{name}` should be a Sequel::Postgres::JSONB, got `#{truncate(value.inspect)}`", location)
end

subject.type.db_load(
value,
name: name,
role: role,
fields_filters: fields_filters + [subject.fields_filter]
fields_filters: fields_filters + [subject.fields_filter],
location: subject.location,
)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
it {
expect {
mapper.db_dump({ tags: 1 })
}.to raise_error(ReeMapper::TypeError, "`tags` should be an array, got `1`")
}.to raise_error(ReeMapper::TypeError, /`tags` should be an array, got `1`/)
}
end

Expand All @@ -66,7 +66,7 @@
mapper.db_load({
tags: 1
})
}.to raise_error(ReeMapper::TypeError, "`tags` should be a Sequel::Postgres::PGArray, got `1`")
}.to raise_error(ReeMapper::TypeError, /`tags` should be a Sequel::Postgres::PGArray, got `1`/)
}
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@
it {
expect {
mapper.db_dump({ numbers: ['1'] })
}.to raise_error(ReeMapper::TypeError, "`numbers[0]` should be an integer, got `\"1\"`")
}.to raise_error(ReeMapper::TypeError, /`numbers\[0\]` should be an integer, got `\"1\"`/)
}

it {
object = Object.new
expect {
mapper.db_dump({ any: object })
}.to raise_error(ReeMapper::TypeError, "`any` should be an jsonb primitive, got `#{object.inspect}`")
}.to raise_error(ReeMapper::TypeError, /`any` should be an jsonb primitive, got `#{object.inspect}`/)
}
end

Expand All @@ -88,14 +88,14 @@
it {
expect {
mapper.db_load({ numbers: Sequel::Postgres::JSONBArray.new([1.1]) })
}.to raise_error(ReeMapper::TypeError, "`numbers[0]` should be an integer, got `1.1`")
}.to raise_error(ReeMapper::TypeError, /`numbers\[0\]` should be an integer, got `1.1`/)
}

it {
object = Object.new
expect {
mapper.db_load({ numbers: object })
}.to raise_error(ReeMapper::TypeError, "`numbers` should be a Sequel::Postgres::JSONB, got `#{object.inspect}`")
}.to raise_error(ReeMapper::TypeError, /`numbers` should be a Sequel::Postgres::JSONB, got `#{object.inspect}`/)
}
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,25 @@ def initialize(enum)
ReeEnum::Value,
Kwargs[
name: String,
role: Nilor[Symbol, ArrayOf[Symbol]]
location: Nilor[String],
] => Or[Integer, String]
)
def db_dump(value, name:, role: nil)
def db_dump(value, name:, location: nil)
value.mapped_value
end

contract(
Or[Integer, String],
Kwargs[
name: String,
role: Nilor[Symbol, ArrayOf[Symbol]]
location: Nilor[String],
] => ReeEnum::Value
).throws(ReeMapper::CoercionError)
def db_load(value, name:, role: nil)
def db_load(value, name:, location: nil)
enum_val = @enum.get_values.by_mapped_value(value)

if !enum_val
raise ReeMapper::CoercionError, "`#{name}` should be one of #{enum_inspection}, got `#{truncate(value.inspect)}`"
raise ReeMapper::CoercionError.new("`#{name}` should be one of #{enum_inspection}, got `#{truncate(value.inspect)}`", location)
end

enum_val
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ class ReeEnum::IntegerValueEnumMapper < ReeEnum::BaseEnumMapper
ReeEnum::Value,
Kwargs[
name: String,
role: Nilor[Symbol, ArrayOf[Symbol]]
location: Nilor[String],
] => Integer
)
def serialize(value, name:, role: nil)
def serialize(value, name:, location: nil)
value.value
end

contract(
Any,
Kwargs[
name: String,
role: Nilor[Symbol, ArrayOf[Symbol]]
location: Nilor[String],
] => ReeEnum::Value
).throws(ReeMapper::CoercionError)
def cast(value, name:, role: nil)
def cast(value, name:, location: nil)
enum_value = case value
when Integer
@enum.get_values.by_value(value)
Expand All @@ -34,7 +34,7 @@ def cast(value, name:, role: nil)
end

if enum_value.nil?
raise ReeMapper::CoercionError, "`#{name}` should be one of #{enum_inspection}, got `#{truncate(value.inspect)}`"
raise ReeMapper::CoercionError.new("`#{name}` should be one of #{enum_inspection}, got `#{truncate(value.inspect)}`", location)
end

enum_value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ class ReeEnum::StringValueEnumMapper < ReeEnum::BaseEnumMapper
ReeEnum::Value,
Kwargs[
name: String,
role: Nilor[Symbol, ArrayOf[Symbol]]
location: Nilor[String],
] => String
)
def serialize(value, name:, role: nil)
def serialize(value, name:, location: nil)
value.value
end

contract(
Any,
Kwargs[
name: String,
role: Nilor[Symbol, ArrayOf[Symbol]]
location: Nilor[String],
] => ReeEnum::Value
).throws(ReeMapper::CoercionError)
def cast(value, name:, role: nil)
def cast(value, name:, location: nil)
enum_value = case value
when String
@enum.get_values.by_value(value)
Expand All @@ -29,7 +29,7 @@ def cast(value, name:, role: nil)
end

if enum_value.nil?
raise ReeMapper::CoercionError, "`#{name}` should be one of #{enum_inspection}, got `#{truncate(value.inspect)}`"
raise ReeMapper::CoercionError.new("`#{name}` should be one of #{enum_inspection}, got `#{truncate(value.inspect)}`", location)
end

enum_value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,15 @@ def call(value)
type: 'invalid',
number: 0,
})
}.to raise_error(ReeMapper::CoercionError, '`type` should be one of ["account"], got `"invalid"`')
}.to raise_error(ReeMapper::CoercionError, /`type` should be one of \["account"\], got `"invalid"`/)

expect {
mapper.db_load({
state: 'first',
type: 'invalid',
number: 0,
})
}.to raise_error(ReeMapper::CoercionError, '`type` should be one of ["account"], got `"invalid"`')
}.to raise_error(ReeMapper::CoercionError, /`type` should be one of \["account"\], got `"invalid"`/)

expect(
mapper.cast({
Expand Down Expand Up @@ -242,4 +242,4 @@ def call(value)
swagger_definition_fetcher.call(TestReeEnum::ContentTypes.type_for_mapper, -> {})
)
}
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module ReeMapper

require_relative 'ree_mapper/types/abstract_type'
require_relative 'ree_mapper/errors/error'
require_relative 'ree_mapper/errors/error_with_location'
require_relative 'ree_mapper/errors/coercion_error'
require_relative 'ree_mapper/errors/type_error'
require_relative 'ree_mapper/errors/unsupported_type_error'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# frozen_string_literal: true

class ReeMapper::CoercionError < ReeMapper::Error
class ReeMapper::CoercionError < ReeMapper::ErrorWithLocation
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

class ReeMapper::ErrorWithLocation < ReeMapper::Error
attr_reader :location

def initialize(message, location = nil)
if message.is_a?(String) && location && ENV["RUBY_ENV"] == "test"
message = "#{message}, located at #{location}"
end

super(message)
@location = location
end

def full_message(...)
msg = super
return msg if location.nil?

idx = msg.index(/\).*\n/)
return msg if idx.nil?
return msg if ENV["RUBY_ENV"] == "test"

msg.insert(idx + 1, ", located at #{location}")
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# frozen_string_literal: true

class ReeMapper::TypeError < ReeMapper::Error
class ReeMapper::TypeError < ReeMapper::ErrorWithLocation
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
class ReeMapper::Field
attr_reader :type, :name, :from, :doc, :optional, :null, :roles, :default,
:name_as_str, :name_as_instance_var_name, :from_as_str,
:fields_filter
:fields_filter, :location

NO_DEFAULT = Object.new.freeze

Expand All @@ -18,11 +18,12 @@ class ReeMapper::Field
role: Nilor[ArrayOf[Symbol], Symbol],
default: Any,
only: Nilor[ReeMapper::FilterFieldsContract],
except: Nilor[ReeMapper::FilterFieldsContract]
except: Nilor[ReeMapper::FilterFieldsContract],
location: Nilor[String],
] => Any
).throws(ArgumentError)
def initialize(type, name = nil, from: nil, doc: nil, optional: false, null: false, role: nil, default: NO_DEFAULT,
only: nil, except: nil)
def initialize(type, name = nil, from: nil, doc: nil, optional: false, null: false, role: nil, default: NO_DEFAULT,
only: nil, except: nil, location: nil)
@type = type
@name = name
@from = from || name
Expand All @@ -38,6 +39,13 @@ def initialize(type, name = nil, from: nil, doc: nil, optional: false, null: fal
@name_as_instance_var_name = :"@#{@name}"
@from_as_str = @from.to_s

@location = location
if @location
@location = @location
.sub(Ree.root_dir, ".")
.sub(/:in.+/, "")
end

raise ArgumentError, 'required fields do not support defaults' if has_default? && !optional
end

Expand Down

0 comments on commit 395810f

Please sign in to comment.