ruby on rails - que - Usar un campo de duración en un modelo de Rails
ruby time difference (4)
Estoy buscando la mejor manera de usar un campo de duración en un modelo de Rails. Me gustaría que el formato sea HH: MM: SS (ej .: 01:30:23). La base de datos en uso es sqlite localmente y Postgres en producción.
También me gustaría trabajar con este campo para poder ver todos los objetos en el campo y obtener el tiempo total de todos los objetos en ese modelo y terminar con algo como:
30 registros que suman 45 horas, 25 minutos y 34 segundos.
Entonces, ¿qué funcionaría mejor?
- Tipo de campo para la migración
- ¿Campo de formulario para los formularios CRUD (hora, minuto, segundo desplegable?)
- Método menos costoso para generar la duración total de todos los registros en el modelo
En Rails 5, puede usar ActiveRecord :: Attributes para almacenar ActiveSupport :: Durations como cadenas ISO8601. La ventaja de utilizar ActiveSupport :: Duration en enteros es que puede usarlos para cálculos de fecha / hora al instante. Puedes hacer cosas como Time.now + 1.month
y siempre es correcto.
Así es cómo:
Agregar config/initializers/duration_type.rb
class DurationType < ActiveRecord::Type::String
def cast(value)
return value if value.blank? || value.is_a?(ActiveSupport::Duration)
ActiveSupport::Duration.parse(value)
end
def serialize(duration)
duration ? duration.iso8601 : nil
end
end
ActiveRecord::Type.register(:duration, DurationType)
Migración
create_table :somethings do |t|
t.string :duration
end
Modelo
class Something < ApplicationRecord
attribute :duration, :duration
end
Uso
something = Something.new
something.duration = 1.year # 1 year
something.duration = nil
something.duration = "P2M3D" # 2 months, 3 days (ISO8601 string)
Time.now + something.duration # calculation is always correct
He escrito un apéndice para admitir y usar el tipo de interval
de PostgreSQL como ActiveRecord::Duration
.
Vea esta idea (puede usarla como inicializador en Rails 4.1): https://gist.github.com/Envek/7077bfc36b17233f60ad
También he abierto solicitudes de extracción a los Rails allí: https://github.com/rails/rails/pull/16919
Intenté usar ActiveSupport :: Duration, pero tuve problemas para obtener la salida clara.
Puede que le guste ruby-duration , un tipo inmutable que representa cierta cantidad de tiempo con precisión en segundos. Tiene muchas pruebas y un tipo de campo modelo Mongoid.
También quería analizar fácilmente las secuencias de duración humana, así que fui con Duración crónica . Aquí hay un ejemplo de agregarlo a un modelo que tiene un campo time_spent in seconds.
class Completion < ActiveRecord::Base
belongs_to :task
belongs_to :user
def time_spent_text
ChronicDuration.output time_spent
end
def time_spent_text= text
self.time_spent = ChronicDuration.parse text
logger.debug "time_spent: ''#{self.time_spent_text}'' for text ''#{text}''"
end
end
- Almacene como enteros en su base de datos (probablemente sea el número de segundos).
- Su formulario de inscripción dependerá del caso de uso exacto. Los desplegables son dolorosos; es mejor usar campos de texto pequeños para la duración en horas + minutos + segundos.
- Simplemente ejecute una consulta
SUM
en la columna de duración para generar un total general. Si usas números enteros, esto es fácil y rápido.
Adicionalmente:
- Use un ayudante para mostrar la duración en sus vistas. Puede convertir fácilmente una duración como un número entero de segundos en
ActiveSupport::Duration
utilizando123.seconds
(reemplace123
con el número entero de la base de datos). Utiliceinspect
laDuration
resultante para un buen formato. (No es perfecto. Puede escribir algo usted mismo). - En su modelo, probablemente querrá lectores de atributos y escritores que devuelvan / tomen
ActiveSupport::Duration
objects, en lugar de enteros. Simplemente definaduration=(new_duration)
yduration
, que internamenteread_attribute
/write_attribute
con argumentos enteros.