На одном проекте мне понадобилось получать список форматов для баннеров из одной рекламной платформы.
Задача с виду простая, но возникла одна проблема - список форматов приходит не уникальный.
Для решения этой проблемы я решил использовать встроенный в Ruby класс Set и немного DDD.
Итак, по порядку.
Список форматов мы будем получать по API, для этого создадим сервис:
# app/services/my_target_service.rb
class MyTargetService
API_URL = 'https://example.com'
def self.banner_formats
response = RestClient.get("#{API_URL}/api/banner_formats.json")
JSON.parse(response, symbolize_names: true)
end
endВ ответ нам приходит массив примерно следующего вида:
[
{
:id => 1,
:status => 'active',
:name => 'Format 1',
:description => '...',
:width => 240,
:height => 400
},
{
:id => 1,
:status => 'active',
:name => 'Format 1',
:description => '...',
:width => 240,
:height => 400
},
{
:id => 2,
:status => 'inactive',
:name => 'Format 2',
:description => '...',
:width => 1080,
:height => 607
}
]Как видно из примера, форматы в списке повторяются.
Воспользуемся классом Set, чтобы сделать этот список уникальным:
# app/services/my_target_service.rb
require 'set'
class MyTargetService
API_URL = 'https://example.com'
def self.banner_formats
response = RestClient.get("#{API_URL}/api/banner_formats.json")
formats = JSON.parse(response, symbolize_names: true)
banner_formats = Set.new
formats.each do |format|
banner_formats << format
end
banner_formats
end
endДля удобства работы, обернем каждый формат в отдельную сущность.
Для этого создадим класс BannerFormatEntity:
# app/entities/banner_format_entity.rb
class BannerFormatEntity
attr_reader :format
def initialize(format)
@format = format
end
def id
format[:id]
end
def name
format[:name]
end
def description
format[:description]
end
def size
"#{format[:width]}x#{format[:height]}"
end
def active?
format[:status] == 'active'
end
endТеперь обновим наш сервис, чтобы использовать класс BannerFormatEntity.
Так же добавим условие, чтобы в списке были только активные форматы:
# app/services/my_target_service.rb
require 'set'
class MyTargetService
API_URL = 'https://example.com'
def self.banner_formats
response = RestClient.get("#{API_URL}/api/banner_formats.json")
formats = JSON.parse(response, symbolize_names: true)
banner_formats = Set.new
formats.each do |format|
banner_format = BannerFormatEntity.new(format)
banner_formats << banner_format if banner_format.active?
end
banner_formats
end
endОстается последний штрих.
Для того чтобы Set мог определять уникальность наших объектов, необходимо определить в классе BannerFormatEntity 2 метода: Object#eql? и Object#hash.
# app/entities/banner_format_entity.rb
class BannerFormatEntity
attr_reader :format
def initialize(format)
@format = format
end
def id
format[:id]
end
def name
format[:name]
end
def description
format[:description]
end
def size
"#{format[:width]}x#{format[:height]}"
end
def active?
format[:status] == 'active'
end
def eql?(other)
id == other.id
end
def hash
id.hash
end
endВот таким простым и элегантным способом, мы решили нашу задачу с получением уникального списка форматов для баннеров.
Сервис MyTargetService отправляет запрос к API рекламной площадки и возвращает уникальную коллекцию объектов BannerFormatEntity.