2016-02-11 4 views
0

У меня есть модель, которая имеет две колонки (started_at и ended_at). Я хочу добавить настраиваемый валидатор, который гарантирует отсутствие другой записи с датами, которые совпадают с проверкой, которую я проверяю. До сих пор у меня есть:Проверка сроков наложения в Ruby on Rails

# app/models/event.rb 

class Event < ActiveRecord::Base 
    validates_with EventValidator 
end 

# app/validators/event_validator.rb 

class EventValidator < ActiveModel::Validator 
    attr_reader :record 

    def validate(record) 
    @record = record 

    validate_dates 
    end 

    private 

    def validate_dates 
    started_at = record.started_at 
    ended_at = record.ended_at 
    arel_table = record.class.arel_table 

    # This is where I'm not quite sure what type of query I need to perform... 
    constraints = arel_table[:started_at].gteq(ended_at) 
     .and(arel_table[:ended_at].lteq(started_at)) 

    if record.persisted? 
     constraints = constraints 
     .and(arel_table[:id].not_eq(record.id)) 
    end 

    if record.class.where(constraints).exists? 
     record.error[:base] << "Overlaps with another event" 
    end 
    end 
end 

Я не знаю точно, какой запрос мне нужен, чтобы гарантировать, что нет перекрытия. Любая помощь очень ценится

ответ

1

Я не использую Arel, но я думаю, что запрос должен быть:

constraints = arel_table[:started_at].lteq(ended_at) 
     .and(arel_table[:ended_at].gteq(started_at)) 

Два периода перекрываются, когда

period1.start < period2.end 
period1.end > period2.start 
0

Посмотрите Validates Overlap gem

Вы можете использовать его вместо кода или взять condition code от него

# Return the condition string depend on exclude_edges option. 
    def condition_string(starts_at_attr, ends_at_attr) 
    except_option = Array(options[:exclude_edges]).map(&:to_s) 
    starts_at_sign = except_option.include?(starts_at_attr.to_s.split(".").last) ? "<" : "<=" 
    ends_at_sign = except_option.include?(ends_at_attr.to_s.split(".").last) ? ">" : ">=" 
    query = [] 
    query << "(#{ends_at_attr} IS NULL OR #{ends_at_attr} #{ends_at_sign} :starts_at_value)" 
    query << "(#{starts_at_attr} IS NULL OR #{starts_at_attr} #{starts_at_sign} :ends_at_value)" 
    query.join(" AND ") 
    end 
0

Я бы построить запрос, который выглядит примерно так:

Event.exists?('started_at < ? AND ended_at > ?', ended_at, started_at) 

Если это возвращает истину, перекрывающийся запись существует.

Смежные вопросы