docbrown/app/models/classified_listing.rb
2019-10-10 14:03:11 -04:00

124 lines
4.4 KiB
Ruby

class ClassifiedListing < ApplicationRecord
include AlgoliaSearch
CATEGORIES_AVAILABLE = {
cfp: { cost: 1, name: "Conference CFP", rules: "Currently open for proposals, with link to form." },
forhire: { cost: 1, name: "Available for Hire", rules: "You are available for hire." },
collabs: { cost: 1, name: "Contributors/Collaborators Wanted", rules: "Projects looking for volunteers. Not job listings." },
education: { cost: 1, name: "Education/Courses", rules: "Educational material and/or schools/bootcamps." },
jobs: { cost: 25, name: "Job Listings", rules: "Companies offering employment right now." },
mentors: { cost: 1, name: "Offering Mentorship", rules: "You are available to mentor someone." },
products: { cost: 5, name: "Products/Tools", rules: "Must be available right now." },
mentees: { cost: 1, name: "Seeking a Mentor", rules: "You are looking for a mentor." },
forsale: { cost: 1, name: "Stuff for Sale", rules: "Personally owned physical items for sale." },
events: { cost: 1, name: "Upcoming Events", rules: "In-person or online events with date included." },
misc: { cost: 1, name: "Miscellaneous", rules: "Must not fit in any other category." }
}.with_indifferent_access.freeze
attr_accessor :action
belongs_to :user
belongs_to :organization, optional: true
before_save :evaluate_markdown
before_create :create_slug
before_validation :modify_inputs
acts_as_taggable_on :tags
has_many :credits, as: :purchase, inverse_of: :purchase, dependent: :nullify
validates :user_id, presence: true
validates :organization_id, presence: true, unless: :user_id?
validates :title, presence: true,
length: { maximum: 128 }
validates :body_markdown, presence: true,
length: { maximum: 400 }
validates :location, length: { maximum: 32 }
validate :restrict_markdown_input
validate :validate_tags
validate :validate_category
algoliasearch per_environment: true do
attribute :title, :processed_html, :bumped_at, :tag_list, :category, :id, :user_id, :slug, :contact_via_connect, :location, :expires_at
attribute :author do
{ username: author.username,
name: author.name,
profile_image_90: ProfileImage.new(author).get(90) }
end
tags do
[tag_list,
"user_#{user_id}",
"organization_#{organization_id}"]
end
attributesForFaceting [:category]
customRanking ["desc(bumped_at)"]
searchableAttributes %w[title processed_html tag_list slug location]
end
scope :published, -> { where(published: true) }
def self.cost_by_category(category = "education")
categories_available[category][:cost]
end
def author
organization || user
end
def self.select_options_for_categories
categories_available.keys.map do |key|
category = categories_available[key]
cost = category[:cost]
["#{category[:name]} (#{cost} #{'Credit'.pluralize(cost)})", key]
end
end
def self.categories_for_display
categories_available.keys.map do |key|
{ slug: key, name: categories_available[key][:name] }
end
end
def self.categories_available
CATEGORIES_AVAILABLE
end
def path
"/listings/#{category}/#{slug}"
end
def natural_expiration_date
(bumped_at || created_at) + 30.days
end
private
def evaluate_markdown
self.processed_html = MarkdownParser.new(body_markdown).evaluate_listings_markdown
end
def modify_inputs
ActsAsTaggableOn::Taggable::Cache.included(ClassifiedListing)
ActsAsTaggableOn.default_parser = ActsAsTaggableOn::TagParser
self.category = category.to_s.downcase
self.body_markdown = body_markdown.to_s.gsub(/\r\n/, "\n")
end
def restrict_markdown_input
markdown_string = body_markdown.to_s
errors.add(:body_markdown, "has too many linebreaks. No more than 12 allowed.") if markdown_string.scan(/(?=\n)/).count > 12
errors.add(:body_markdown, "is not allowed to include images.") if markdown_string.include?("![")
errors.add(:body_markdown, "is not allowed to include liquid tags.") if markdown_string.include?("{% ")
end
def validate_tags
errors.add(:tag_list, "exceed the maximum of 8 tags") if tag_list.length > 8
end
def validate_category
errors.add(:category, "not a valid category") unless CATEGORIES_AVAILABLE[category]
end
def create_slug
self.slug = title.to_s.downcase.parameterize.tr("_", "") + "-" + rand(100_000).to_s(26)
end
end