Last active
August 29, 2015 14:22
-
-
Save sipple/c388e163a27fb7c8f0f9 to your computer and use it in GitHub Desktop.
Code Sample - Festivity Events List
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
################## | |
# This code sample contains the logic for a page used by arts festival attendees to filter | |
# the festival's events and find a show they're interested in seeing. | |
# You can see the event list in action here: http://pghkids.org/events | |
################## | |
################## | |
# FestivityEventsController, which receives a request with search criteria and returns | |
# a list of matching events to the view. | |
################## | |
class FestivityEventsController < ApplicationController | |
include Festivity::Mixins::NotFound | |
no_login_required | |
trusty_layout 'base' | |
# Event search requests are cached; because some requests are for the full page and some are AJAX | |
# caching separates requests by format. | |
caches_action :index, cache_path: proc { |c| c.params.except(:_).merge(format: request.xhr?)} | |
def index | |
# Default sort is by event start date | |
order_by = params[:sort] ? params[:sort] : "start_date" | |
@title = "#{current_site.festivity_festival_name}: Events" | |
# Pass search criteria to the FestivityEventList model to find relevant events | |
@events = FestivityEventList.search( | |
{dates: search_dates.join(","), | |
categories: params[:categories]}, | |
order_by).events | |
# If the request is AJAX, only return the event list itself, not the full page | |
if request.xhr? | |
render partial: "event_list" | |
else | |
render 'index' | |
end | |
end | |
end | |
################## | |
# FestivityEventList model, used to collect all matching events | |
################## | |
class FestivityEventList | |
attr_reader :events | |
def initialize(event_performances) | |
@events = event_performances.group_by {|perf| perf.event_id }. | |
map { |perfs| FestivityEventList::FestivityEvent.new(perfs[0], perfs[1]) } | |
end | |
def self.search(criteria, order_by) | |
# Build the SQL where clause from the supplied criteria | |
where_clause = parse_criteria(criteria) | |
# Search the FestivityEventPerformance view and build a new FestivityEventList object with the results | |
FestivityEventList.new( | |
FestivityEventList::FestivityEventPerformance. | |
includes(:assets). | |
joins(:festivity_categories). | |
where(where_clause). | |
group("performance_id"). | |
order("featured_item DESC, #{order_by} ASC"). | |
preload(:festivity_categories) | |
) | |
end | |
private | |
# The order of querying, depending on what is passed: | |
# - If dates are passed, we search both start and end date between midnight and 11:59pm of that date. | |
# That query returns any matching event ids. | |
# - The event ids returned, if any, are added to the where clause for the next query | |
# - Any category ids passed are added to the where clause as well. | |
def self.parse_criteria(criteria) | |
where_clause = {} | |
event_ids = event_ids_for_dates(criteria[:dates]) if criteria[:dates] | |
where_clause["site_id"] = Page.current_site.id | |
where_clause["event_id"] = event_ids if event_ids | |
where_clause["festivity_categories.id"] = criteria[:categories].split(",") if criteria[:categories] | |
where_clause | |
end | |
# Return a list of unique event ids that match the provided dates | |
def self.event_ids_for_dates(dates) | |
FestivityEventList::FestivityEventPerformance.where(date_criteria(dates)).map {|e| e.event_id}.uniq | |
end | |
# Create a condition for start and end date between midnight and 11:59pm | |
# for each date passed in and return the SQL condition | |
def self.date_criteria(dates_string) | |
date_queries = dates_string.split(',').map do |date_string| | |
start_date = DateTime.parse(date_string) | |
end_date = start_date.advance(hours: 23, minutes: 59) | |
<<-SQL | |
( | |
(start_date >= '#{start_date}' AND start_date <= '#{end_date}') | |
OR | |
(end_date >= '#{start_date}' AND end_date <= '#{end_date}') | |
) | |
SQL | |
end | |
date_queries.join(" OR ") | |
end | |
end | |
################## | |
# FestivityEventPerformance model, tied to a database view which collects event performance data | |
################## | |
class FestivityEventList::FestivityEventPerformance < ActiveRecord::Base | |
self.table_name = 'festivity_event_performances' | |
after_initialize :readonly! | |
has_many :festivity_page_categories, foreign_key: :page_id, primary_key: :event_id | |
has_many :festivity_categories, through: :festivity_page_categories | |
has_many :page_attachments, primary_key: :event_id, foreign_key: :page_id | |
has_many :assets, through: :page_attachments | |
end | |
################## | |
# FestivityEvent model representing events which matched the search criteria and wrapping matching performances | |
################## | |
class FestivityEventList::FestivityEvent | |
include Festivity::Admin::AssetsHelper | |
attr_reader :id, :performances, :locations, :categories, :title, :short_description, | |
:assets, :header, :sub_header, :featured_item, :buy_url | |
def initialize(event_id, performances) | |
@id = event_id | |
@performances = performances | |
# For event-level information, like title, just use the first event performance | |
@title = performances.first.event_title | |
@short_description = performances.first.short_description | |
@header = performances.first.header | |
@sub_header = performances.first.sub_header | |
@featured_item = performances.first.featured_item | |
@buy_url = performances.first.buy_url | |
@locations = self.performances. | |
map{ |performance| FestivityEventList::FestivityLocation.new ({ | |
id: performance.location_id, | |
slug: performance.location_slug, | |
title: performance.location_title, | |
directions_url: performance.festivity_directions_url, | |
area_id: performance.area_id, | |
area_slug: performance.area_slug, | |
area_title: performance.area_title}) }. | |
uniq{ |location| location.id } | |
@categories = performances.first.festivity_categories | |
@assets = performances.first.assets | |
end | |
end | |
################## | |
# Event List view, demonstrating how matched events are displayed in the browser | |
################## | |
#event-list-items | |
- @events.each do |event| | |
.row.event-list-item{class: event.id, data:{ genre: event.categories.first.id, date: event.performances.first.start_date, location: event.locations.first.id} } | |
%hr | |
.event-list-item__photo.col-xs-12.col-sm-4 | |
.photo | |
= link_to event_path(event.id) do | |
%img.img-responsive{ src: "#{event.image}"} | |
- if event.featured_item | |
.event_list-item__photo-featured-item | |
Featured Event! | |
.event-list-item__info.col-xs-12.col-sm-8 | |
= link_to event_path(event.id) do | |
%h2 | |
= event.title | |
%h3 | |
%span.strong | |
= event.header | |
%span.light | |
= event.sub_header | |
%hr | |
%p | |
- if event.locations.count == 1 | |
- event_location = event.locations.first | |
=link_to location_path(id: event_location.slug) do | |
= event_location.title | |
@ | |
=link_to area_path(id: event_location.area_slug) do | |
= event_location.area_title | |
- else | |
Multiple Locations | |
- if event.performances.count > 1 | |
%p | |
Multiple dates and times | |
%button.btn.btn-sm.btn-default.btn-popover{type: "button", data: {content: date_time_popover(event.performances), html: "true", placement:"top", toggle: "popover"}, title:"All Dates and Times"} | |
Show all | |
- else | |
- event.performances.each do |perf| | |
%p | |
= perf.start_date.strftime("%A, %B %d") | |
= ", " | |
= perf.start_date.strftime("%I:%M%p").downcase | |
= " - " | |
= perf.end_date.strftime('%I:%M%p').downcase | |
%hr | |
%p | |
.event-list-item__info_short_description | |
%span | |
= event.short_description.html_safe | |
%p | |
.event-list-item__button-group | |
= link_to "Details", event_path(event.id), class: 'btn event-list-item__btn' | |
- unless event.buy_url.blank? | |
= link_to "Tickets", "#{event.buy_url}", class: 'btn event-list-item__btn', target: '_blank' | |
- unless event.locations.first.directions_url.blank? | |
= link_to "Directions", "#{event.locations.first.directions_url}", class: 'btn event-list-item__btn', target: '_blank' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment