Skip to content

Instantly share code, notes, and snippets.

@tanraya
Created December 1, 2012 15:15
Show Gist options
  • Select an option

  • Save tanraya/4182837 to your computer and use it in GitHub Desktop.

Select an option

Save tanraya/4182837 to your computer and use it in GitHub Desktop.
# encoding: utf-8
require 'valid_email'
class Vacancy < ActiveRecord::Base
include Workflow
# Сколько рубрик разрешено иметь
RUBRICS = 1..3
# Разрешенные периоды публикации
PUBLICATION_PERIODS = {
:one_week => 1.week,
:two_weeks => 2.weeks,
:three_weeks => 3.weeks,
:one_month => 1.month
}
# Состояния вакансии, в которых ей разрешено показываться на сайте
DISPLAYES_STATES = [:approved, :pending, :archived]
# Предупреждать об истечении срока публикации вакансии за 1 и 3 дня до истечения срока
WARN_EXPIRING_FOR = [1.day, 3.days]
# see https://github.com/rsl/stringex
acts_as_url :post, :url_attribute => :slug,
:sync_url => true,
:allow_duplicates => true
belongs_to :city
belongs_to :company, :counter_cache => true
# TODO Креатором может быть и манагер, сменить на User?
belongs_to :creator, class_name: 'User::Employer'
belongs_to :company_activity, class_name: 'Rubric'
belongs_to :experience, class_name: 'Rubric'
belongs_to :education, class_name: 'Rubric'
belongs_to :gender, class_name: 'Rubric'
belongs_to :work_scheldule, class_name: 'Rubric'
has_many :vacancy_rubrics
has_many :rubrics, :through => :vacancy_rubrics
has_one :favorite, :as => :favoriteable, :dependent => :destroy
# Соблюдаем Law Of Demeter
delegate :subdomain, :to => :city, :prefix => true
accepts_nested_attributes_for :company
# Аттрибуты, которые может менять Работодатель
attr_accessible :age_from, :age_to, :city_id, :company_activity_id,
:conditions, :contact_person, :description, :education_id, :email, :experience_id,
:gender_id, :phones, :post, :publication_period, :requirements, :responsibilities,
:salary, :salary_by_interview, :work_scheldule_id, :workplace_address, :company_attributes,
:rubric_ids, :as => [:user, :admin]
# Аттрибуты, которые может менять Администратор или Менеджер
attr_accessible :state, :company_id, :created_at, :as => :admin
validates :post, :city_id, :gender_id, :age_from, :age_to, :education_id,
:experience_id, :work_scheldule_id, :company_activity_id, :contact_person,
:phones, :presence => true
validates :city_id, :gender_id, :age_from, :age_to, :education_id,
:experience_id, :work_scheldule_id, :company_activity_id,
:numericality => true
validates :publication_period, :inclusion => { :in => PUBLICATION_PERIODS.keys.map(&:to_s) }
validates :salary, :presence => true, :numericality => true#, :unless => :salary_by_interview?
validates :email, :email => true, :allow_blank => true
validates :rubric_ids, :length => {
:minimum => RUBRICS.min,
:maximum => RUBRICS.max
}
# Описываем состояния
workflow_column :state
workflow do
# На рассмотрении
state :pending do
event :approve, :transitions_to => :approved
event :archive, :transitions_to => :archived
event :remove, :transitions_to => :deleted
end
# Принята
state :approved do
event :suspend, :transitions_to => :pending
event :archive, :transitions_to => :archived
event :remove, :transitions_to => :deleted
end
# Заархивирована
state :archived do
event :approve, :transitions_to => :approved
event :remove, :transitions_to => :deleted
end
# Удалена
state :deleted
end
# Scope по-умолчанию. Добавляется к каждому запросу. Нужно это помнить.
default_scope do
where(:city_id => City.current_city).displayed.approved.descending
end
# Вакансии, отображаемые на сайте. Вакансии со статусом :deleted вообще
# не должны отображаться на сайте в любых случаях.
scope :displayed, ->{ where(:state => DISPLAYES_STATES) }
# Принятые/на рассмотрении
scope :approved, ->{ where(:state => :approved) }
scope :suspended, ->{ where(:state => :pending) }
scope :archived, ->{ where(:state => :archived) }
scope :deleted, ->{ where(:state => :deleted) }
# Вакансии, у которых истекает срок публикации
scope :expiring, ->{ approved.where(build_expiring_conditions) }
# Актуальные/неактуальные вакансии
scope :relevant, ->{ displayed.where("NOW() < expired_at") }
scope :irrelevant, ->{ displayed.where("NOW() > expired_at") }
scope :descending, ->{ order('approved_at, created_at DESC') }
scope :recent, ->{ displayed.approved.limit(20) }
# Выборка вакансий по рубрике
scope :by_rubric, ->(rubric_id){ joins(:rubrics).where(:rubrics => { :id => rubric_id }) }
before_save :bind_to_company_and_creator
before_save :setup_approve
after_initialize :setup_defaults
# Вакансия актуальна?
def relevant?
(Time.now > expired_at) && approved?
end
# Вакансия неактуальна?
def irrelevant?
not relevant?
end
# Находится ли вакансия в избранном?
def favorite?
favorite.present?
end
def to_s
post
end
def to_param
"#{id}-#{slug}"
end
class << self
def states
workflow_spec.states.keys
end
# Архивирование вакансий. Устанавливаем для неактуальных статус archived
def archive_irrelevant!
Vacancy.unscoped.irrelevant.update_all(:state => 'archived')
end
# Построение условий для поиска вакансий, у которых истекает срок публикации
def build_expiring_conditions
conditions = []
WARN_EXPIRING_FOR.each do |expire_in_seconds|
# Время в будущем, когда вакансия будет снята с публикации
expires_in = Time.now + expire_in_seconds
# Составляем запрос: expires_in_future BEETWEEN expired_at_begin_of_day AND expired_at_end_of_day
conditions << "('#{expires_in}'::timestamp BETWEEN DATE_TRUNC('day', expired_at) AND DATE_TRUNC('day', expired_at + INTERVAL '1 day'))"
end
"(#{conditions.join(' OR ')})"
end
end
private
# Прикрепляем компанию и пользователя (создателя) к вакансии
def bind_to_company_and_creator
creator = User.current
self.creator_id ||= creator.id
self.company_id = creator.profile.company.id if User.current.is_a?(User::Employer)
end
# Значения по-умолчанию
def setup_defaults
case User.current
when User::Employer
self.contact_person ||= User.current.profile.company.contact_person
self.workplace_address ||= User.current.profile.company.office_address
when User::Manager, User::Admin
self.created_at = Time.now.to_date
end
end
def setup_approve
if state_was != 'approved' && approved?
# При переходе в состояние approved обновляем время аппрува
self.approved_at = Time.now
# Рассчитываем время окончания публикации вакансии
self.expired_at = self.approved_at + PUBLICATION_PERIODS[publication_period.to_sym]
# Уведомляем работодателя
VacancyMailer.vacancy_approved(self).deliver
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment