Skip to content

Commit

Permalink
Improve work with options (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
afuno authored Apr 29, 2024
1 parent 4d8e4ce commit 7f13fdd
Show file tree
Hide file tree
Showing 12 changed files with 429 additions and 237 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class UserDto < Datory::Base
string :phone
string :website

string :birthDate, to: :birth_date, as: Date
date :birthDate, to: :birth_date

one :login, include: UserLoginDto
one :company, include: UserCompanyDto
Expand All @@ -62,8 +62,10 @@ class UserLoginDto < Datory::Base

string :md5
string :sha1

duration :lifetime

string :registered_at, as: DateTime
datetime :registered_at
end
```

Expand Down
155 changes: 54 additions & 101 deletions lib/datory/attributes/attribute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,84 +2,62 @@

module Datory
module Attributes
class Attribute # rubocop:disable Metrics/ClassLength
attr_reader :name, :options
class Attribute
attr_reader :from, :to

def initialize(name, **options)
@name = name
@options = prepare_options(options)
end

def prepare_options(options) # rubocop:disable Metrics/MethodLength
unless (format = options.fetch(:format, nil)).nil?
options[:format] = if format.is_a?(Hash)
{
from: format.fetch(:from, nil),
to: format.fetch(:to, nil)
}
else
{
from: format,
to: format
}
end
end

options
def initialize(name, **options) # rubocop:disable Metrics/MethodLength
@from = Options::From.new(
name: name,
type: options.fetch(:from),
consists_of: options.fetch(:consists_of, false),
min: options.fetch(:min, nil),
max: options.fetch(:max, nil),
format: options.fetch(:format, nil)
)

@to = Options::To.new(
name: options.fetch(:to, name),
type: options.fetch(:as, @from.type),
# TODO: It is necessary to implement NilClass support for optional
required: options.fetch(:required, true),
consists_of: @from.consists_of,
min: @from.min,
max: @from.max,
format: options.fetch(:format, nil),
include_class: options.fetch(:include, nil)
)
end

##########################################################################

def input_serialization_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
def input_serialization_options # rubocop:disable Metrics/AbcSize
hash = {
as: options.fetch(:to, name),
type: options.fetch(:as, options.fetch(:from)),
required: options.fetch(:required, true),
consists_of: options.fetch(:consists_of, false)
as: to.name,
type: to.type,
required: to.required,
consists_of: to.consists_of
}

if (min = options.fetch(:min, nil)).present?
hash[:min] = min
end
hash[:min] = to.min if to.min.present?

if (max = options.fetch(:max, nil)).present?
hash[:max] = max
end
hash[:max] = to.max if to.max.present?

if (format = options.dig(:format, :to)).present?
hash[:format] = format
end
hash[:format] = to.format if to.format.present?

hash
end

def output_serialization_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
def output_serialization_options # rubocop:disable Metrics/AbcSize
hash = {
consists_of: if (consists_of_type = options.fetch(:consists_of, false)) == Hash
Datory::Result
else
consists_of_type
end,
type: if (as_type = options.fetch(:as, options.fetch(:from))) == Datory::Result
Hash
elsif (from_type = options.fetch(:from)).present?
from_type
else
as_type
end
consists_of: to.consists_of == Hash ? Datory::Result : from.consists_of,
type: to.type == Datory::Result ? Hash : from.type
}

if (min = options.fetch(:min, nil)).present?
hash[:min] = min
end
hash[:min] = from.min if from.min.present?

if (max = options.fetch(:max, nil)).present?
hash[:max] = max
end
hash[:max] = from.max if from.max.present?

if (format = options.dig(:format, :from)).present?
hash[:format] = format
end
hash[:format] = from.format if from.format.present?

hash
end
Expand All @@ -88,66 +66,41 @@ def output_serialization_options # rubocop:disable Metrics/MethodLength, Metrics

def input_deserialization_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
hash = {
as: options.fetch(:to, name),
type: options.fetch(:from),
required: options.fetch(:required, true),
consists_of: options.fetch(:consists_of, false),
as: to.name,
type: from.type,
required: to.required,
consists_of: from.consists_of,
prepare: (lambda do |value:|
include_class = options.fetch(:include, nil)
return value unless include_class.present?

from_type = options.fetch(:from, nil)
return value unless to.include_class.present?

if [Set, Array].include?(from_type)
value.map { |item| include_class.deserialize(**item) }
if [Set, Array].include?(from.type)
value.map { |item| to.include_class.deserialize(**item) }
else
include_class.deserialize(**value)
to.include_class.deserialize(**value)
end
end)
}

if (min = options.fetch(:min, nil)).present?
hash[:min] = min
end
hash[:min] = from.min if from.min.present?

if (max = options.fetch(:max, nil)).present?
hash[:max] = max
end
hash[:max] = from.max if from.max.present?

if (format = options.dig(:format, :from)).present?
hash[:format] = format
end
hash[:format] = from.format if from.format.present?

hash
end

def output_deserialization_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
def output_deserialization_options # rubocop:disable Metrics/AbcSize
hash = {
consists_of: if (consists_of_type = options.fetch(:consists_of, false)) == Hash
Datory::Result
else
consists_of_type
end,
type: if (from_type = options.fetch(:from)) == Hash
Datory::Result
elsif (option_as = options.fetch(:as, nil)).present?
option_as
else
from_type
end
consists_of: from.consists_of == Hash ? Datory::Result : to.consists_of,
type: from.type == Hash ? Datory::Result : to.type
}

if (min = options.fetch(:min, nil)).present?
hash[:min] = min
end
hash[:min] = to.min if to.min.present?

if (max = options.fetch(:max, nil)).present?
hash[:max] = max
end
hash[:max] = to.max if to.max.present?

if (format = options.dig(:format, :to)).present?
hash[:format] = format
end
hash[:format] = to.format if to.format.present?

hash
end
Expand Down
8 changes: 4 additions & 4 deletions lib/datory/attributes/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ def names
end

def internal_names
map { |attribute| attribute.options.fetch(:to, attribute.name) }
map { |attribute| attribute.to.name }
end

def include_exist?
@include_exist ||= filter do |attribute| # rubocop:disable Performance/Count
include_class = attribute.options.fetch(:include, nil)
def include_class_exist?
@include_class_exist ||= filter do |attribute| # rubocop:disable Performance/Count
include_class = attribute.to.include_class

next false if include_class.nil?

Expand Down
17 changes: 7 additions & 10 deletions lib/datory/attributes/descriptor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,18 @@ def describe(service_class_name:, collection_of_attributes:) # rubocop:disable M
headings << "From"
headings << "To"
headings << "As"
headings << "Include" if collection_of_attributes.include_exist?
headings << "Include" if collection_of_attributes.include_class_exist?

collection_of_attributes.each do |attribute|
row = []

row << attribute.name
include_class = attribute.to.include_class.presence || attribute.from.type

from_type = attribute.options.fetch(:from)
include = attribute.options.fetch(:include, from_type)

row << from_type
row << attribute.options.fetch(:to, attribute.name)
row << attribute.options.fetch(:as, include)

row << (include if include <= Datory::Base) if collection_of_attributes.include_exist?
row << attribute.from.name
row << attribute.from.type
row << attribute.to.name
row << attribute.to.type
row << (include_class if include_class <= Datory::Base) if collection_of_attributes.include_class_exist?

rows << row
end
Expand Down
31 changes: 31 additions & 0 deletions lib/datory/attributes/options/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module Datory
module Attributes
module Options
class Base
attr_reader :name, :type, :consists_of, :min, :max, :format

def initialize(name:, type:, consists_of:, min:, max:, format:)
@name = name
@type = type
@consists_of = consists_of
@min = min
@max = max
@format = format
end

def info
{
name: name,
type: type,
min: min,
max: max,
consists_of: consists_of,
format: format
}
end
end
end
end
end
15 changes: 15 additions & 0 deletions lib/datory/attributes/options/from.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Datory
module Attributes
module Options
class From < Base
def initialize(name:, type:, consists_of:, min:, max:, format:)
format = format.fetch(:from, nil) if format.is_a?(Hash)

super(name: name, type: type, consists_of: consists_of, min: min, max: max, format: format)
end
end
end
end
end
27 changes: 27 additions & 0 deletions lib/datory/attributes/options/to.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

module Datory
module Attributes
module Options
class To < Base
attr_reader :required, :include_class

def initialize(name:, type:, required:, consists_of:, min:, max:, format:, include_class:)
@required = required
@include_class = include_class

format = format.fetch(:to, nil) if format.is_a?(Hash)

super(name: name, type: type, consists_of: consists_of, min: min, max: max, format: format)
end

def info
super.merge(
required: required,
include: include_class
)
end
end
end
end
end
17 changes: 7 additions & 10 deletions lib/datory/attributes/serialization/serializator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,20 @@ def serialize(model:)
end
else
@collection_of_attributes.to_h do |attribute|
attribute.options.fetch(:to, attribute.name)
include_class = attribute.options.fetch(:include, nil)
output_formatter = attribute.options.fetch(:output, nil)
include_class = attribute.to.include_class
# output_formatter = attribute.options.fetch(:output, nil) # TODO

value = model.public_send(attribute.name)
value = model.public_send(attribute.from.name)

value =
if include_class.present?
from_type = attribute.options.fetch(:from)

if [Set, Array].include?(from_type)
if [Set, Array].include?(attribute.from.type)
value.map { |item| include_class.serialize(item) }
else
include_class.serialize(value)
end
elsif output_formatter.is_a?(Proc)
output_formatter.call(value: value)
# elsif output_formatter.is_a?(Proc) # TODO
# output_formatter.call(value: value)
elsif [Date, Time, DateTime].include?(value.class)
value.to_s
elsif value.instance_of?(ActiveSupport::Duration)
Expand All @@ -45,7 +42,7 @@ def serialize(model:)
value
end

[attribute.name, value]
[attribute.from.name, value]
end
end
end
Expand Down
Loading

0 comments on commit 7f13fdd

Please sign in to comment.