# frozen_string_literal: true # La relación de un sitio con sus artículos, esto nos permite generar # artículos como si estuviésemos usando ActiveRecord. class PostRelation < Array # No necesitamos cambiar el sitio attr_reader :site, :lang def initialize(site:, lang:) @site = site @lang = lang # Proseguimos la inicialización sin valores por defecto super() end # Genera un artículo nuevo con los parámetros que le pasemos y lo suma # al array def build(**args) args[:lang] = lang args[:document] ||= build_document(collection: args[:lang]) args[:layout] = build_layout(args[:layout]) post = Post.new(site: site, **args) self << post post end def create(**args) post = build(**args) post.save post end alias sort_by_generic sort_by alias sort_by_generic! sort_by! # Permite ordenar los artículos por sus atributos # # XXX: Prestar atención cuando estamos mezclando artículos con # diferentes tipos de atributos. def sort_by(*attrs) sort_by_generic do |post| attrs.map do |attr| # TODO: detectar el tipo de atributo faltante y obtener el valor # por defecto para hacer la comparación if post.attributes.include? attr post.public_send(attr).value else 0 end end end end def sort_by!(*attrs) replace sort_by(*attrs) end alias find_generic find # Encontrar un post por su UUID def find(id, uuid: false) find_generic do |p| if uuid p.uuid.value == id else p.id == id end end end # Encuentra el primer post por el valor de los atributos # # @param [Hash] # @return [Post] def find_by(**args) find_generic do |post| args.map do |attr, value| post.attribute?(attr) && post.public_send(attr).value == value end.all? end end # Encuentra todos los Post que cumplan las condiciones # # TODO: Implementar caché # # @param [Hash] Mapa de atributo => valor. Valor puede ser un Array # de valores # @return [PostRelation] def where(**args) return self if args.empty? begin PostRelation.new(site: site, lang: lang).concat(select do |post| result = args.map do |attr, value| next unless post.attribute?(attr) attribute = post[attr] # TODO: Si el valor del atributo también es un Array deberíamos # cruzar ambas. case value when Array then value.include? attribute.value else case attribute.value when Array then attribute.value.include? value else attribute.value == value end end end.compact # Un Array vacío devuelve true para all? result.present? && result.all? end) end end # Como Array#select devolviendo una relación # # @return [PostRelation] alias array_select select def select(&block) PostRelation.new(site: site, lang: lang).concat array_select(&block) end # Intenta guardar todos y devuelve true si pudo def save_all(validate: true) map do |post| post.save(validate: validate) end.all? end private def build_layout(layout = nil) return layout if layout.is_a? Layout site.layouts[layout&.to_sym || :post] end # Devuelve una colección Jekyll que hace pasar el documento def build_collection(label:) Jekyll::Collection.new(site.jekyll, label.to_s) end # Un documento borrador con algunas propiedades por defecto def build_document(collection:) col = build_collection(label: collection) doc = Jekyll::Document.new('', site: site.jekyll, collection: col) doc.data['date'] = Date.today.to_time doc end end