Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
1,036 additions
and
51 deletions.
There are no files selected for viewing
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 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 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 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 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
module TimeFilterable | ||
|
||
extend ActiveSupport::Concern | ||
|
||
TIME_PARTS = [:year, :month, :day, :hour, :min, :sec].freeze | ||
|
||
included do | ||
before_action :assert_params_given, only: :index | ||
end | ||
|
||
private | ||
|
||
def start_finish | ||
parts = params.values_at(*TIME_PARTS).compact | ||
start = get_timestamp(parts) | ||
finish = start + range(parts) | ||
[start, finish] | ||
end | ||
|
||
def range(parts) | ||
range = TIME_PARTS[parts.size - 1] | ||
case range | ||
when :min then 1.minute | ||
when :sec then 1.second | ||
else 1.send(range) | ||
end | ||
end | ||
|
||
def get_timestamp(parts) | ||
Time.zone.local(*parts) | ||
rescue ArgumentError | ||
not_found | ||
end | ||
|
||
def assert_params_given | ||
not_found unless index_params? | ||
end | ||
|
||
def index_params? | ||
params[:show_id].present? || params[:year].present? || params[:q].present? | ||
end | ||
|
||
end |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Requires that all write actions of an including controller are | ||
# performed by a logged in user (and not only a guest with an access code). | ||
module WriteAuthenticatable | ||
|
||
extend ActiveSupport::Concern | ||
|
||
included do | ||
before_action :require_user, only: [:create, :update, :destroy] | ||
end | ||
|
||
private | ||
|
||
def require_user | ||
render_unauthorized unless current_user | ||
end | ||
|
||
def fetch_current_user | ||
if %w[create update destroy].include?(action_name) | ||
Auth::Jwt.new(request).fetch_user || | ||
Auth::ApiToken.new(request).fetch_user | ||
else | ||
super | ||
end | ||
end | ||
|
||
end |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
class TracksController < CrudController | ||
|
||
include TimeFilterable | ||
include WriteAuthenticatable | ||
include Admin::CrudSwag | ||
|
||
self.search_columns = %w[title artist] | ||
self.permitted_attrs = [:title, :artist, :started_at, :finished_at] | ||
|
||
crud_swagger_paths(data_class: 'Track', tags_read: :public, query_params: [:q]) | ||
|
||
# Convenience module to extract common swagger documentation in this controller. | ||
module SwaggerOperationMethods | ||
def parameter_date(name) | ||
parameter name: name, | ||
in: :path, | ||
description: "Optional two-digit #{name} to get the tracks for. " \ | ||
'Requires all preceeding parameters.', | ||
required: true, # false, actually. Swagger path params must be required. | ||
type: :integer | ||
end | ||
end | ||
include_missing(Swagger::Blocks::Nodes::OperationNode, SwaggerOperationMethods) | ||
|
||
swagger_path '/tracks/{year}/{month}/{day}/{hour}{minute}{second}' do | ||
operation :get do | ||
key :description, 'Returns a list of tracks at the given date/time span.' | ||
key :tags, [:track, :public] | ||
|
||
parameter name: :year, | ||
in: :path, | ||
description: 'The four-digit year to get the tracks for.', | ||
required: true, | ||
type: :integer | ||
|
||
parameter_date :month | ||
parameter_date :day | ||
parameter_date :hour | ||
parameter_date :minute | ||
parameter_date :second | ||
|
||
parameter :q | ||
parameter :page_number | ||
parameter :page_size | ||
parameter :sort | ||
|
||
response_entity('Track') | ||
|
||
security http_token: [] | ||
security api_token: [] | ||
security access_code: [] | ||
end | ||
end | ||
|
||
swagger_path '/shows/{show_id}/tracks' do | ||
operation :get do | ||
key :description, 'Returns a list of tracks of the given show.' | ||
key :tags, [:track, :public] | ||
|
||
parameter name: :show_id, | ||
in: :path, | ||
description: 'ID of the show to list the tracks for', | ||
required: true, | ||
type: :integer | ||
|
||
parameter :q | ||
parameter :page_number | ||
parameter :page_size | ||
parameter :sort | ||
|
||
response_entity('Track') | ||
|
||
security http_token: [] | ||
security api_token: [] | ||
security access_code: [] | ||
end | ||
end | ||
|
||
swagger_path('/broadcasts/{broadcast_id}/tracks') do | ||
operation :get do | ||
key :description, 'Returns a list of tracks of the given broadcast.' | ||
key :tags, [:track, :public] | ||
|
||
parameter name: :broadcast_id, | ||
in: :path, | ||
description: 'ID of the broadcast to list the tracks for', | ||
required: true, | ||
type: :integer | ||
|
||
parameter :q | ||
parameter :page_number | ||
parameter :page_size | ||
parameter :sort | ||
|
||
response_entity('Track') | ||
|
||
security http_token: [] | ||
security api_token: [] | ||
security access_code: [] | ||
end | ||
end | ||
|
||
private | ||
|
||
def fetch_entries | ||
scope = super | ||
scope = scope.within(*start_finish) if params[:year] | ||
scope = scope.for_show(params[:show_id]) if params[:show_id] | ||
scope | ||
end | ||
|
||
def model_scope | ||
if params[:broadcast_id] | ||
Broadcast.find(params[:broadcast_id]).tracks | ||
else | ||
super | ||
end | ||
end | ||
|
||
def index_params? | ||
super || params[:broadcast_id].present? | ||
end | ||
|
||
def entry_url | ||
track_path(entry) | ||
end | ||
|
||
end |
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 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# == Schema Information | ||
# | ||
# Table name: tracks | ||
# | ||
# id :integer not null, primary key | ||
# title :string not null | ||
# artist :string | ||
# started_at :datetime not null | ||
# finished_at :datetime not null | ||
# | ||
|
||
class Track < ApplicationRecord | ||
|
||
validates :title, :started_at, :finished_at, presence: true | ||
validates :started_at, :finished_at, uniqueness: true | ||
|
||
scope :list, -> { order('tracks.started_at') } | ||
|
||
class << self | ||
def within(start, finish) | ||
where('tracks.finished_at > ? AND tracks.started_at < ?', start, finish) | ||
end | ||
|
||
def for_show(show_id) | ||
joins('INNER JOIN broadcasts ' \ | ||
'ON tracks.started_at >= broadcasts.started_at ' \ | ||
'AND tracks.started_at < broadcasts.finished_at') | ||
.where(broadcasts: { show_id: show_id }) | ||
end | ||
end | ||
|
||
def to_s | ||
"#{I18n.l(started_at)}: #{[artist, title].compact.join(' - ')}" | ||
end | ||
|
||
# duration in seconds | ||
def duration | ||
finished_at - started_at | ||
end | ||
|
||
end |
Oops, something went wrong.