У меня есть Сегмент модели Rails, который имеет атрибут, состоящий из массива диапазонов: [0..20, 32..41, 90..200]
, например.
Есть несколько методов, которые я хотел бы добавить в массив, которые были бы полезны специально для работы с массивами диапазонов, например, методом для суммирования длины каждого диапазона в массиве:
class RangeArray < Array
def sum
inject(0) { |total, range| total + (1 + range.max - range.min) }
end
end
В идеале, я мог бы переопределить методы получения и установки в сегменте так, чтобы при Массив диапазонов читается из базы данных, он автоматически преобразуется в RangeArray, и всякий раз, когда он записывается в базу данных, он записывается как массив. Кажется, что это должно быть легко сделать, так как RangeArray наследуется от Array, но у меня есть некоторые реальные проблемы с некоторыми вещами. Я довольно близко подошел к этой настройке:
class Segment < ApplicationRecord
attr_accessor :ranges
def ranges
puts "read"
RangeArray.new(super)
end
def ranges=(var)
puts "write"
super(RangeArray.new(var))
end
end
С этой настройкой я могу прочитать диапазоны, установить их явно, сохранить в базе данных и т. Д. c:
2.6.5 :071 > s = Segment.new
=> #<Segment id: nil, ranges: [], type: nil, created_at: nil, updated_at: nil>
2.6.5 :072 > s.ranges = [2000..3000]
write
=> [2000..3000]
2.6.5 :073 > s.ranges.class
read
=> RangeArray
2.6.5 :074 > s.ranges.sum
read
=> 1001
У меня проблемы с методами push
и <<
, которые RangeArray должен наследовать от Array:
2.6.5 :075 > s.ranges << (40..50)
read
=> [2000..3000, 40..50]
2.6.5 :076 > s.ranges
read
=> [2000..3000]
Кажется, что это не удается, потому что попытка добавить элемент в диапазоны с помощью <<
сначала вызывает читателя, который генерирует новый объект RangeArray, помещает в него 40..50
, а затем отбрасывает объект.
Сохранение значений в промежуточной переменной экземпляра кажется, что оно должно работать, но не работает кажется, что многое изменится:
def ranges
puts "read"
@ranges = RangeArray.new(super)
end
def ranges=(var)
puts "write"
@ranges = super(RangeArray.new(var))
end
Я чувствую, что я здесь близко, но я что-то упускаю, и я не уверен, как заставить писателя быть вызванным во время <<
или push
. Что мне делать? Вместо того, чтобы переопределять методы получения и установки, есть ли другой способ изменить тип, к которому ActiveRecord приводит атрибут ranges
? Или, может быть, мне просто нужно добавить модуль, который добавляет методы непосредственно в массив, и include
, который используется для каждой модели, которая в этом нуждается?