[deploy] Classified listings refactor part 3 (#7498)
* Ignore category column * Move view oriented class methods to helper * Start refactoring ClassifiedListingsToolkit * Add attr_accessor instead of category * Replace old category column with association * More cleanup * Trigger Travis * Don't rename associations, category_slug -> category * Indiana Kohl and the missing newline * More refactoring and spec fixes * Add dashboard for classified listing categories * Remove TODO comment Co-authored-by: rhymes <rhymesete@gmail.com>
This commit is contained in:
parent
7ebd95f193
commit
46f94b135d
21 changed files with 303 additions and 109 deletions
|
|
@ -0,0 +1,46 @@
|
|||
module Admin
|
||||
class ClassifiedListingCategoriesController < Admin::ApplicationController
|
||||
# Overwrite any of the RESTful controller actions to implement custom behavior
|
||||
# For example, you may want to send an email after a foo is updated.
|
||||
#
|
||||
# def update
|
||||
# super
|
||||
# send_foo_updated_email(requested_resource)
|
||||
# end
|
||||
|
||||
# Override this method to specify custom lookup behavior.
|
||||
# This will be used to set the resource for the `show`, `edit`, and `update`
|
||||
# actions.
|
||||
#
|
||||
# def find_resource(param)
|
||||
# Foo.find_by!(slug: param)
|
||||
# end
|
||||
|
||||
# The result of this lookup will be available as `requested_resource`
|
||||
|
||||
# Override this if you have certain roles that require a subset
|
||||
# this will be used to set the records shown on the `index` action.
|
||||
#
|
||||
# def scoped_resource
|
||||
# if current_user.super_admin?
|
||||
# resource_class
|
||||
# else
|
||||
# resource_class.with_less_stuff
|
||||
# end
|
||||
# end
|
||||
|
||||
# Override `resource_params` if you want to transform the submitted
|
||||
# data before it's persisted. For example, the following would turn all
|
||||
# empty values into nil values. It uses other APIs such as `resource_class`
|
||||
# and `dashboard`:
|
||||
#
|
||||
# def resource_params
|
||||
# params.require(resource_class.model_name.param_key).
|
||||
# permit(dashboard.permitted_attributes).
|
||||
# transform_values { |value| value == "" ? nil : value }
|
||||
# end
|
||||
|
||||
# See https://administrate-prototype.herokuapp.com/customizing_controller_actions
|
||||
# for more information
|
||||
end
|
||||
end
|
||||
|
|
@ -55,7 +55,7 @@ module Api
|
|||
|
||||
ATTRIBUTES_FOR_SERIALIZATION = %i[
|
||||
id user_id organization_id title slug body_markdown cached_tag_list
|
||||
category classified_listing_category_id category processed_html published
|
||||
classified_listing_category_id processed_html published
|
||||
].freeze
|
||||
private_constant :ATTRIBUTES_FOR_SERIALIZATION
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
class ClassifiedListingsController < ApplicationController
|
||||
include ClassifiedListingsToolkit
|
||||
|
||||
JSON_OPTIONS = {
|
||||
only: %i[
|
||||
title processed_html tag_list category id user_id slug contact_via_connect location
|
||||
],
|
||||
include: {
|
||||
author: { only: %i[username name], methods: %i[username profile_image_90] }
|
||||
}
|
||||
}.freeze
|
||||
|
||||
before_action :set_classified_listing, only: %i[edit update destroy]
|
||||
before_action :set_cache_control_headers, only: %i[index]
|
||||
before_action :raise_suspended, only: %i[new create update]
|
||||
|
|
@ -13,18 +22,22 @@ class ClassifiedListingsController < ApplicationController
|
|||
|
||||
if params[:view] == "moderate"
|
||||
not_found unless @displayed_classified_listing
|
||||
return redirect_to "/internal/listings/#{@displayed_classified_listing.id}/edit"
|
||||
return redirect_to edit_internal_listing_path(id: @displayed_classified_listing.id)
|
||||
end
|
||||
|
||||
@classified_listings =
|
||||
if params[:category].blank?
|
||||
published_listings.
|
||||
order("bumped_at DESC").
|
||||
includes(:user, :organization, :taggings, :classified_listing_category).
|
||||
includes(:user, :organization, :taggings).
|
||||
limit(12)
|
||||
else
|
||||
ClassifiedListing.none
|
||||
end
|
||||
|
||||
@listings_json = @classified_listings.to_json(JSON_OPTIONS)
|
||||
@displayed_listing_json = @displayed_classified_listing.to_json(JSON_OPTIONS)
|
||||
|
||||
set_surrogate_key_header "classified-listings-#{params[:category]}"
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,33 +4,22 @@ module ClassifiedListingsToolkit
|
|||
MANDATORY_FIELDS_FOR_UPDATE = %i[body_markdown title tag_list].freeze
|
||||
|
||||
def unpublish_listing
|
||||
@classified_listing.published = false
|
||||
@classified_listing.save
|
||||
@classified_listing.update(published: false)
|
||||
end
|
||||
|
||||
def publish_listing
|
||||
@classified_listing.published = true
|
||||
@classified_listing.save
|
||||
@classified_listing.update(published: true)
|
||||
end
|
||||
|
||||
# TODO: why not just @classified_listing.update(listing_params)?
|
||||
def update_listing_details
|
||||
@classified_listing.title = listing_params[:title] if listing_params[:title]
|
||||
@classified_listing.body_markdown = listing_params[:body_markdown] if listing_params[:body_markdown]
|
||||
@classified_listing.tag_list = listing_params[:tag_list] if listing_params[:tag_list]
|
||||
if listing_params[:classified_listing_category_id]
|
||||
@classified_listing.classified_listing_category_id = listing_params[:classified_listing_category_id]
|
||||
end
|
||||
@classified_listing.location = listing_params[:location] if listing_params[:location]
|
||||
@classified_listing.expires_at = listing_params[:expires_at] if listing_params[:expires_at]
|
||||
@classified_listing.contact_via_connect = listing_params[:contact_via_connect] if listing_params[:contact_via_connect]
|
||||
@classified_listing.save
|
||||
# [thepracticaldev/oss] Not entirely sure what the intention behind the
|
||||
# original code was, but at least this is more compact.
|
||||
filtered_params = listing_params.reject { |_k, v| v.nil? }
|
||||
@classified_listing.update(filtered_params)
|
||||
end
|
||||
|
||||
def bump_listing_success
|
||||
@classified_listing.bumped_at = Time.current
|
||||
saved = @classified_listing.save
|
||||
saved
|
||||
@classified_listing.update(bumped_at: Time.current)
|
||||
end
|
||||
|
||||
def clear_listings_cache
|
||||
|
|
@ -59,7 +48,6 @@ module ClassifiedListingsToolkit
|
|||
available_user_credits = current_user.credits.unspent
|
||||
|
||||
unless @classified_listing.valid?
|
||||
# TODO: [thepracticaldev/oss] For now the credits are needed in the view
|
||||
@credits = current_user.credits.unspent
|
||||
process_unsuccessful_creation
|
||||
return
|
||||
|
|
@ -77,15 +65,15 @@ module ClassifiedListingsToolkit
|
|||
end
|
||||
|
||||
ALLOWED_PARAMS = %i[
|
||||
title body_markdown category classified_listing_category_id tag_list
|
||||
title body_markdown classified_listing_category_id tag_list
|
||||
expires_at contact_via_connect location organization_id action
|
||||
].freeze
|
||||
|
||||
# Never trust parameters from the scary internet, only allow a specific list through.
|
||||
# Filter for a set of known safe params
|
||||
def listing_params
|
||||
if params["classified_listing"]["tags"].present?
|
||||
params["classified_listing"]["tags"] = params["classified_listing"]["tags"].join(", ")
|
||||
params["classified_listing"]["tag_list"] = params["classified_listing"].delete "tags"
|
||||
tags = params["classified_listing"].delete("tags")
|
||||
if tags.present?
|
||||
params["classified_listing"]["tag_list"] = tags.join(", ")
|
||||
end
|
||||
params.require(:classified_listing).permit(ALLOWED_PARAMS)
|
||||
end
|
||||
|
|
|
|||
73
app/dashboards/classified_listing_category_dashboard.rb
Normal file
73
app/dashboards/classified_listing_category_dashboard.rb
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
require "administrate/base_dashboard"
|
||||
|
||||
class ClassifiedListingCategoryDashboard < Administrate::BaseDashboard
|
||||
# ATTRIBUTE_TYPES
|
||||
# a hash that describes the type of each of the model's fields.
|
||||
#
|
||||
# Each different type represents an Administrate::Field object,
|
||||
# which determines how the attribute is displayed
|
||||
# on pages throughout the dashboard.
|
||||
ATTRIBUTE_TYPES = {
|
||||
classified_listings: Field::HasMany,
|
||||
id: Field::Number,
|
||||
cost: Field::Number,
|
||||
created_at: Field::DateTime,
|
||||
name: Field::String,
|
||||
rules: Field::String,
|
||||
slug: Field::String,
|
||||
updated_at: Field::DateTime
|
||||
}.freeze
|
||||
|
||||
# COLLECTION_ATTRIBUTES
|
||||
# an array of attributes that will be displayed on the model's index page.
|
||||
#
|
||||
# By default, it's limited to four items to reduce clutter on index pages.
|
||||
# Feel free to add, remove, or rearrange items.
|
||||
COLLECTION_ATTRIBUTES = %i[
|
||||
name
|
||||
slug
|
||||
rules
|
||||
cost
|
||||
].freeze
|
||||
|
||||
# SHOW_PAGE_ATTRIBUTES
|
||||
# an array of attributes that will be displayed on the model's show page.
|
||||
SHOW_PAGE_ATTRIBUTES = %i[
|
||||
id
|
||||
name
|
||||
slug
|
||||
rules
|
||||
cost
|
||||
created_at
|
||||
updated_at
|
||||
].freeze
|
||||
|
||||
# FORM_ATTRIBUTES
|
||||
# an array of attributes that will be displayed
|
||||
# on the model's form (`new` and `edit`) pages.
|
||||
FORM_ATTRIBUTES = %i[
|
||||
name
|
||||
slug
|
||||
rules
|
||||
cost
|
||||
].freeze
|
||||
|
||||
# COLLECTION_FILTERS
|
||||
# a hash that defines filters that can be used while searching via the search
|
||||
# field of the dashboard.
|
||||
#
|
||||
# For example to add an option to search for open resources by typing "open:"
|
||||
# in the search field:
|
||||
#
|
||||
# COLLECTION_FILTERS = {
|
||||
# open: ->(resources) { resources.where(open: true) }
|
||||
# }.freeze
|
||||
COLLECTION_FILTERS = {}.freeze
|
||||
|
||||
# Overwrite this method to customize how classified listing categories are displayed
|
||||
# across all pages of the admin dashboard.
|
||||
#
|
||||
# def display_resource(classified_listing_category)
|
||||
# "ClassifiedListingCategory ##{classified_listing_category.id}"
|
||||
# end
|
||||
end
|
||||
|
|
@ -30,6 +30,7 @@ class DashboardManifest
|
|||
html_variant_successes
|
||||
sponsorships
|
||||
pro_memberships
|
||||
classified_listing_categories
|
||||
].freeze
|
||||
# DASHBOARDS = [
|
||||
# :users,
|
||||
|
|
|
|||
19
app/helpers/classified_listing_helper.rb
Normal file
19
app/helpers/classified_listing_helper.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
module ClassifiedListingHelper
|
||||
def select_options_for_categories
|
||||
ClassifiedListingCategory.select(:id, :name, :cost).map do |cl|
|
||||
["#{cl.name} (#{cl.cost} #{'Credit'.pluralize(cl.cost)})", cl.id]
|
||||
end
|
||||
end
|
||||
|
||||
def categories_for_display
|
||||
ClassifiedListingCategory.pluck(:slug, :name).map do |slug, name|
|
||||
{ slug: slug, name: name }
|
||||
end
|
||||
end
|
||||
|
||||
def categories_available
|
||||
ClassifiedListingCategory.all.each_with_object({}) do |cat, h|
|
||||
h[cat.slug] = cat.attributes.slice("cost", "name", "rules")
|
||||
end.deep_symbolize_keys
|
||||
end
|
||||
end
|
||||
|
|
@ -2,8 +2,8 @@ class ClassifiedListingTag < LiquidTagBase
|
|||
PARTIAL = "classified_listings/liquid".freeze
|
||||
|
||||
def initialize(_tag_name, slug_path_url, _tokens)
|
||||
striped_path = ActionController::Base.helpers.strip_tags(slug_path_url).strip
|
||||
@listing = get_listing(striped_path)
|
||||
stripped_path = ActionController::Base.helpers.strip_tags(slug_path_url).strip
|
||||
@listing = get_listing(stripped_path)
|
||||
end
|
||||
|
||||
def render(_context)
|
||||
|
|
@ -24,7 +24,7 @@ class ClassifiedListingTag < LiquidTagBase
|
|||
hash = get_hash(url)
|
||||
raise StandardError, "Invalid URL or slug. Listing not found." if hash.nil?
|
||||
|
||||
listing = ClassifiedListing.find_by(hash)
|
||||
listing = ClassifiedListing.in_category(hash[:category]).find_by(slug: hash[:slug])
|
||||
raise StandardError, "Invalid URL or slug. Listing not found." unless listing
|
||||
|
||||
listing
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
class ClassifiedListing < ApplicationRecord
|
||||
self.ignored_columns = ["category"]
|
||||
|
||||
include Searchable
|
||||
|
||||
SEARCH_SERIALIZER = Search::ClassifiedListingSerializer
|
||||
|
|
@ -6,10 +8,9 @@ class ClassifiedListing < ApplicationRecord
|
|||
|
||||
attr_accessor :action
|
||||
|
||||
# This allows to create a listing from a catgory string.
|
||||
# TODO: [mkohl] refactor once column was dropped.
|
||||
before_validation :assign_classified_listing_category
|
||||
|
||||
# Note: categories were hardcoded at first and this model was only added later,
|
||||
# so the association name is a bit verbose since the original "category" attribute
|
||||
# was kept to minimize code changes.
|
||||
belongs_to :classified_listing_category
|
||||
belongs_to :user
|
||||
belongs_to :organization, optional: true
|
||||
|
|
@ -29,38 +30,19 @@ class ClassifiedListing < ApplicationRecord
|
|||
validates :location, length: { maximum: 32 }
|
||||
validate :restrict_markdown_input
|
||||
validate :validate_tags
|
||||
validate :validate_category
|
||||
|
||||
scope :published, -> { where(published: true) }
|
||||
scope :in_category, lambda { |slug|
|
||||
joins(:classified_listing_category).
|
||||
where("classified_listing_categories.slug" => slug)
|
||||
}
|
||||
|
||||
# TODO: refactor this class method block
|
||||
def self.select_options_for_categories
|
||||
ClassifiedListingCategory.select(:id, :name, :cost).map do |cl|
|
||||
["#{cl.name} (#{cl.cost} #{'Credit'.pluralize(cl.cost)})", cl.id]
|
||||
end
|
||||
end
|
||||
|
||||
def self.categories_for_display
|
||||
ClassifiedListingCategory.pluck(:slug, :name).map do |slug, name|
|
||||
{ slug: slug, name: name }
|
||||
end
|
||||
end
|
||||
|
||||
def self.categories_available
|
||||
ClassifiedListingCategory.all.each_with_object({}) do |cat, h|
|
||||
h[cat.slug] = cat.attributes.slice("cost", "name", "rules")
|
||||
end.deep_symbolize_keys
|
||||
end
|
||||
delegate :cost, to: :classified_listing_category
|
||||
|
||||
def category
|
||||
classified_listing_category&.slug
|
||||
end
|
||||
|
||||
def cost
|
||||
@cost = classified_listing_category&.cost ||
|
||||
ClassifiedListingCategory.select(:cost).find_by(slug: category)&.cost
|
||||
end
|
||||
|
||||
def author
|
||||
organization || user
|
||||
end
|
||||
|
|
@ -82,7 +64,6 @@ class ClassifiedListing < ApplicationRecord
|
|||
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
|
||||
|
||||
|
|
@ -93,11 +74,6 @@ class ClassifiedListing < ApplicationRecord
|
|||
errors.add(:body_markdown, "is not allowed to include liquid tags.") if markdown_string.include?("{% ")
|
||||
end
|
||||
|
||||
def validate_category
|
||||
categories = ClassifiedListingCategory.pluck(:slug)
|
||||
errors.add(:category, "not a valid category") unless category.in?(categories)
|
||||
end
|
||||
|
||||
def validate_tags
|
||||
errors.add(:tag_list, "exceed the maximum of 8 tags") if tag_list.length > 8
|
||||
end
|
||||
|
|
@ -105,14 +81,4 @@ class ClassifiedListing < ApplicationRecord
|
|||
def create_slug
|
||||
self.slug = "#{title.downcase.parameterize.delete('_')}-#{rand(100_000).to_s(26)}"
|
||||
end
|
||||
|
||||
def assign_classified_listing_category
|
||||
return if classified_listing_category_id.present?
|
||||
|
||||
category = ClassifiedListingCategory.find_by(slug: attributes["category"])
|
||||
return unless category
|
||||
|
||||
self.category = category.slug
|
||||
self.classified_listing_category_id = category.id
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
<div id="listingform-data"
|
||||
data-listing="<%= classified_listing.to_json(only: %i[id title body_markdown category cached_tag_list]) %>"
|
||||
data-organizations="<%= @organizations.to_json(only: %i[id name]) %>"
|
||||
data-categories-for-select="<%= ClassifiedListing.select_options_for_categories.to_json %>"
|
||||
data-categories-for-details="<%= ClassifiedListing.categories_available.transform_values { |value_hash| value_hash.except(:cost) }.values.to_json %>">
|
||||
data-categories-for-select="<%= select_options_for_categories.to_json %>"
|
||||
data-categories-for-details="<%= categories_available.transform_values { |value_hash| value_hash.except(:cost) }.values.to_json %>">
|
||||
<div style="height: 745px;">
|
||||
<div class="field">
|
||||
<%= form.label "title" %>
|
||||
|
|
|
|||
|
|
@ -28,25 +28,19 @@
|
|||
|
||||
<div class="home">
|
||||
<div class="classifieds-container" id="classifieds-index-container"
|
||||
data-category="<%= params[:category] %>" data-listings="<%= @classified_listings.to_json(
|
||||
only: %i[title processed_html tag_list category id user_id slug contact_via_connect location],
|
||||
include: {
|
||||
author: { only: %i[username name], methods: %i[username profile_image_90] }
|
||||
}
|
||||
) %>"
|
||||
data-allcategories="<%= ClassifiedListing.categories_for_display.to_json %>"
|
||||
data-category="<%= params[:category] %>" data-listings="<%= @listings_json %>"
|
||||
data-allcategories="<%= categories_for_display.to_json %>"
|
||||
<% if @displayed_classified_listing %>
|
||||
data-displayedlisting="<%= @displayed_classified_listing.to_json(
|
||||
only: %i[title processed_html tag_list category id user_id slug contact_via_connect location],
|
||||
include: {
|
||||
author: { only: %i[username name], methods: %i[username profile_image_90] }
|
||||
},
|
||||
) %> "
|
||||
data-displayedlisting="<%= @displayed_listing_json %> "
|
||||
<% end %>
|
||||
>
|
||||
<div class="classified-filters">
|
||||
<div class="classified-filters-categories">
|
||||
<a href="/listings" class="<%= "selected" if params[:category].blank? %> data-no-instant="true">all</a><% ClassifiedListing.categories_for_display.each do |cat| %><a href="/listings/<%= cat[:slug] %>" class="<%= "selected" if params[:category] == cat[:slug] %> data-no-instant="true"><%= cat[:name] %></a><% end %><a href='/listings/new' class='classified-create-link'>Create a Listing</a>
|
||||
<a href="/listings" class="<%= "selected" if params[:category].blank? %> data-no-instant="true">all</a>
|
||||
<% categories_for_display.each do |cat| %>
|
||||
<a href="/listings/<%= cat[:slug] %>" class="<%= "selected" if params[:category] == cat[:slug] %> data-no-instant="true"><%= cat[:name] %></a>
|
||||
<% end %>
|
||||
<a href='/listings/new' class='classified-create-link'>Create a Listing</a>
|
||||
<% if @displayed_classified_listing %>
|
||||
<div class="classified-listings-modal-background" role='presentation'></div>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<%= form.label :classified_listing_category_id %>
|
||||
<%= form.select :classified_listing_category_id, ClassifiedListing.select_options_for_categories %>
|
||||
<%= form.select :classified_listing_category_id, select_options_for_categories %>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<%= form.check_box :published, checked: @classified_listing.published? %>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<%= label_tag(:filter, "Category") %>
|
||||
<%= select_tag(:filter, options_for_select(ClassifiedListing.categories_available.keys, params[:filter]), include_blank: true) %>
|
||||
<%= select_tag(:filter, options_for_select(categories_available.keys, params[:filter]), include_blank: true) %>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<%= label_tag(:include_unpublished, "Include unpublished listings") %>
|
||||
|
|
|
|||
|
|
@ -308,6 +308,7 @@ ActiveRecord::Schema.define(version: 2020_04_26_124118) do
|
|||
t.string "slug"
|
||||
t.string "status", default: "active"
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["slug"], name: "index_chat_channels_on_slug", unique: true
|
||||
end
|
||||
|
||||
create_table "classified_listing_categories", force: :cascade do |t|
|
||||
|
|
@ -1216,9 +1217,9 @@ ActiveRecord::Schema.define(version: 2020_04_26_124118) do
|
|||
t.datetime "updated_at", null: false
|
||||
t.string "username"
|
||||
t.string "website_url"
|
||||
t.string "youtube_url"
|
||||
t.boolean "welcome_notifications", default: true, null: false
|
||||
t.datetime "workshop_expiration"
|
||||
t.string "youtube_url"
|
||||
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
||||
t.index ["created_at"], name: "index_users_on_created_at"
|
||||
t.index ["email"], name: "index_users_on_email", unique: true
|
||||
|
|
|
|||
45
db/seeds.rb
45
db/seeds.rb
|
|
@ -4,7 +4,7 @@ SEEDS_MULTIPLIER = [1, ENV["SEEDS_MULTIPLIER"].to_i].max
|
|||
counter = 0
|
||||
Rails.logger.info "Seeding with multiplication factor: #{SEEDS_MULTIPLIER}"
|
||||
|
||||
##############################################################################
|
||||
##############################################################################/
|
||||
|
||||
counter += 1
|
||||
Rails.logger.info "#{counter}. Creating Organizations"
|
||||
|
|
@ -371,6 +371,49 @@ end
|
|||
|
||||
##############################################################################
|
||||
|
||||
counter += 1
|
||||
Rails.logger.info "#{counter}. Creating Classified Listings"
|
||||
|
||||
users_in_random_order.each { |user| Credit.add_to(user, rand(100)) }
|
||||
users = users_in_random_order.to_a
|
||||
|
||||
listings_categories = ClassifiedListingCategory.pluck(:id)
|
||||
listings_categories.each.with_index(1) do |category_id, index|
|
||||
# rotate users if they are less than the categories
|
||||
user = users.at(index % users.length)
|
||||
2.times do
|
||||
ClassifiedListing.create!(
|
||||
user: user,
|
||||
title: Faker::Lorem.sentence,
|
||||
body_markdown: Faker::Markdown.random,
|
||||
location: Faker::Address.city,
|
||||
organization_id: user.organizations.first&.id,
|
||||
classified_listing_category_id: category_id,
|
||||
contact_via_connect: true,
|
||||
published: true,
|
||||
bumped_at: Time.current,
|
||||
tag_list: Tag.order(Arel.sql("RANDOM()")).first(2).pluck(:name),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
|
||||
counter += 1
|
||||
Rails.logger.info "#{counter}. Creating Pages"
|
||||
|
||||
5.times do
|
||||
Page.create!(
|
||||
title: Faker::Hacker.say_something_smart,
|
||||
body_markdown: Faker::Markdown.random,
|
||||
slug: Faker::Internet.slug,
|
||||
description: Faker::Books::Dune.quote,
|
||||
template: %w[contained full_within_layout].sample,
|
||||
)
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
|
||||
counter += 1
|
||||
Rails.logger.info "#{counter}. Creating Classified Listing Categories"
|
||||
|
||||
|
|
|
|||
|
|
@ -10,5 +10,11 @@ FactoryBot.define do
|
|||
slug { "cfp" }
|
||||
cost { 5 }
|
||||
end
|
||||
|
||||
trait :jobs do
|
||||
name { "Job Listings" }
|
||||
slug { "jobs" }
|
||||
cost { 1 }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
41
spec/helpers/classified_listing_helper_spec.rb
Normal file
41
spec/helpers/classified_listing_helper_spec.rb
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe ClassifiedListingHelper, type: :helper do
|
||||
let_it_be_readonly(:cat1) { create(:classified_listing_category, cost: 1) }
|
||||
let_it_be_readonly(:cat2) { create(:classified_listing_category, :cfp, cost: 5) }
|
||||
|
||||
describe "select_options_for_categories" do
|
||||
it "returns the correct options array" do
|
||||
expect(helper.select_options_for_categories).to match_array(
|
||||
[
|
||||
["#{cat1.name} (1 Credit)", cat1.id],
|
||||
["#{cat2.name} (5 Credits)", cat2.id],
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "categories_for_display" do
|
||||
it "return the correct hash of slug and name pairs" do
|
||||
expect(helper.categories_for_display).to match_array(
|
||||
[
|
||||
{ slug: cat1.slug, name: cat1.name },
|
||||
{ slug: cat2.slug, name: cat2.name },
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "categories_available" do
|
||||
it "returns a hash with slugs as keys" do
|
||||
expected = [cat1.slug.to_sym, cat2.slug.to_sym]
|
||||
expect(helper.categories_available.keys).to match_array(expected)
|
||||
end
|
||||
|
||||
it "categories have the correct keys" do
|
||||
cfp_category = helper.categories_available[:cfp]
|
||||
expect(cfp_category.keys).to match_array(%i[cost name rules])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -6,7 +6,7 @@ RSpec.describe CacheBuster, type: :labor do
|
|||
let(:article) { create(:article, user_id: user.id) }
|
||||
let(:comment) { create(:comment, user_id: user.id, commentable: article) }
|
||||
let(:organization) { create(:organization) }
|
||||
let(:listing) { create(:classified_listing, user_id: user.id, category: "cfp") }
|
||||
let(:listing) { create(:classified_listing, user_id: user.id) }
|
||||
let(:podcast) { create(:podcast) }
|
||||
let(:podcast_episode) { create(:podcast_episode, podcast_id: podcast.id) }
|
||||
let(:tag) { create(:tag) }
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ RSpec.describe ClassifiedListingTag, type: :liquid_tag do
|
|||
title: "save me pls",
|
||||
body_markdown: "sigh sigh sigh",
|
||||
processed_html: "<p>sigh sigh sigh</p>",
|
||||
category: "cfp",
|
||||
tag_list: %w[a b c],
|
||||
organization_id: nil,
|
||||
)
|
||||
|
|
@ -22,7 +21,6 @@ RSpec.describe ClassifiedListingTag, type: :liquid_tag do
|
|||
title: "this old af",
|
||||
body_markdown: "exxpired",
|
||||
processed_html: "<p>exxpired</p>",
|
||||
category: "cfp",
|
||||
tag_list: %w[x y z],
|
||||
organization_id: nil,
|
||||
bumped_at: datetime,
|
||||
|
|
@ -43,7 +41,6 @@ RSpec.describe ClassifiedListingTag, type: :liquid_tag do
|
|||
title: "this is a job posting",
|
||||
body_markdown: "wow code lots get not only money but satisfaction from work",
|
||||
processed_html: "<p>wow code lots get not only money but satisfaction from work</p>",
|
||||
category: "misc",
|
||||
tag_list: %w[a b c],
|
||||
organization_id: org.id,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ RSpec.describe "Api::V0::ClassifiedListings", type: :request do
|
|||
|
||||
describe "GET /api/listings/:id" do
|
||||
include_context "with 7 listings and 2 user"
|
||||
let(:listing) { ClassifiedListing.where(category: "cfp").last }
|
||||
let(:listing) { ClassifiedListing.in_category("cfp").last }
|
||||
|
||||
context "when unauthenticated" do
|
||||
it "returns a published listing" do
|
||||
|
|
@ -293,7 +293,8 @@ RSpec.describe "Api::V0::ClassifiedListings", type: :request do
|
|||
it "fails if category is invalid" do
|
||||
post_classified_listing(title: "Title", body_markdown: "body", category: "unknown")
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(response.parsed_body.dig("errors", "category").first).to match(/not a valid category/)
|
||||
expect(response.parsed_body.dig("errors", "classified_listing_category").first).
|
||||
to match(/must exist/)
|
||||
end
|
||||
|
||||
it "does not subtract credits or create a listing if the listing is not valid" do
|
||||
|
|
@ -312,8 +313,7 @@ RSpec.describe "Api::V0::ClassifiedListings", type: :request do
|
|||
post_classified_listing(listing_params)
|
||||
expect(response).to have_http_status(:created)
|
||||
|
||||
listing_cost = ClassifiedListing.categories_available[:cfp][:cost]
|
||||
expect(user.credits.spent.size).to eq(listing_cost)
|
||||
expect(user.credits.spent.size).to eq(cfp_category.cost)
|
||||
end
|
||||
|
||||
it "creates a listing draft under the org" do
|
||||
|
|
@ -357,7 +357,8 @@ RSpec.describe "Api::V0::ClassifiedListings", type: :request do
|
|||
it "cannot create a draft due to internal error" do
|
||||
allow(Organization).to receive(:find_by)
|
||||
post_classified_listing(draft_params.except(:classified_listing_category_id))
|
||||
expect(response.parsed_body["errors"]["category"]).to eq(["not a valid category"])
|
||||
expect(response.parsed_body.dig("errors", "classified_listing_category").first).
|
||||
to match(/must exist/)
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
end
|
||||
|
||||
|
|
@ -608,7 +609,8 @@ RSpec.describe "Api::V0::ClassifiedListings", type: :request do
|
|||
max_id = ClassifiedListingCategory.maximum(:id)
|
||||
put_classified_listing(listing.id, title: "New title", classified_listing_category_id: max_id + 1)
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(response.parsed_body.dig("errors", "category").first).to match(/not a valid category/)
|
||||
expect(response.parsed_body.dig("errors", "classified_listing_category").first).
|
||||
to match(/must exist/)
|
||||
end
|
||||
|
||||
it "updates the title of his listing" do
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ RSpec.describe Search::ClassifiedListing, type: :service do
|
|||
end
|
||||
|
||||
it "searches by slug" do
|
||||
slug_classified_listing = FactoryBot.create(:classified_listing, title: "A slug is created from this title in a callback")
|
||||
slug_classified_listing = create(:classified_listing, title: "A slug is created from this title in a callback")
|
||||
index_documents(slug_classified_listing)
|
||||
params = { size: 5, slug: "slug" }
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ RSpec.describe Search::ClassifiedListing, type: :service do
|
|||
|
||||
it "sorts documents by bumped_at by default" do
|
||||
classified_listing.update(bumped_at: 1.year.ago)
|
||||
classified_listing2 = FactoryBot.create(:classified_listing, bumped_at: Time.current)
|
||||
classified_listing2 = create(:classified_listing, bumped_at: Time.current)
|
||||
index_documents([classified_listing, classified_listing2])
|
||||
params = { size: 5 }
|
||||
|
||||
|
|
@ -148,7 +148,7 @@ RSpec.describe Search::ClassifiedListing, type: :service do
|
|||
|
||||
it "paginates the results" do
|
||||
classified_listing.update(bumped_at: 1.year.ago)
|
||||
classified_listing2 = FactoryBot.create(:classified_listing, bumped_at: Time.current)
|
||||
classified_listing2 = create(:classified_listing, bumped_at: Time.current)
|
||||
index_documents([classified_listing, classified_listing2])
|
||||
first_page_params = { page: 0, per_page: 1, sort_by: "bumped_at", order: "dsc" }
|
||||
|
||||
|
|
@ -162,8 +162,12 @@ RSpec.describe Search::ClassifiedListing, type: :service do
|
|||
end
|
||||
|
||||
it "returns an empty Array if no results are found" do
|
||||
classified_listing.update(category: "forhire")
|
||||
classified_listing2 = FactoryBot.create(:classified_listing, category: "cfp")
|
||||
jobs_category = create(:classified_listing_category, :jobs)
|
||||
classified_listing.update(classified_listing_category: jobs_category)
|
||||
|
||||
cfp_category = create(:classified_listing_category, :cfp)
|
||||
classified_listing2 = create(:classified_listing,
|
||||
classified_listing_category: cfp_category)
|
||||
index_documents([classified_listing, classified_listing2])
|
||||
params = { page: 3, per_page: 1 }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue