Skip to content

Commit

Permalink
create tracks
Browse files Browse the repository at this point in the history
  • Loading branch information
codez committed Jan 28, 2019
1 parent 157ede7 commit 5fdc990
Show file tree
Hide file tree
Showing 20 changed files with 1,036 additions and 51 deletions.
4 changes: 4 additions & 0 deletions .rubocop.yml
Expand Up @@ -55,6 +55,10 @@ Style/SymbolArray:
Rails/Delegate:
Enabled: false

# with dry_crud, this is a pattern
Rails/LexicallyScopedActionFilter:
Enabled: false

# we should know about update_all constraints.
Rails/SkipsModelValidations:
Enabled: false
4 changes: 3 additions & 1 deletion app/controllers/apidocs_controller.rb
Expand Up @@ -8,6 +8,7 @@ class ApidocsController < ApplicationController
BroadcastsController,
LoginController,
ShowsController,
TracksController,
StatusController,
Admin::AccessCodesController,
Admin::ArchiveFormatsController,
Expand All @@ -23,6 +24,7 @@ class ApidocsController < ApplicationController
BroadcastSerializer,
ShowSerializer,
StatusSerializer,
TrackSerializer,
UnprocessableEntitySerializer,
UserSerializer,
Admin::AccessCodeSerializer,
Expand All @@ -38,7 +40,7 @@ class ApidocsController < ApplicationController
swagger_root do
key :swagger, '2.0'
info do
key :version, '1.0'
key :version, '1.1'
key :title, 'RAAR Radio Archive API'
key :description,
'RAAR Radio Archive API. ' \
Expand Down
47 changes: 3 additions & 44 deletions app/controllers/broadcasts_controller.rb
Expand Up @@ -4,14 +4,12 @@
# user (not by access code).
class BroadcastsController < CrudController

TIME_PARTS = [:year, :month, :day, :hour, :min, :sec].freeze
include TimeFilterable
include WriteAuthenticatable

self.search_columns = %w[label people details shows.name]
self.permitted_attrs = [:label, :details, :people]

before_action :assert_params_given, only: :index
before_action :require_user, only: :update # rubocop:disable Rails/LexicallyScopedActionFilter

# Convenience module to extract common swagger documentation in this controller.
module SwaggerOperationMethods

Expand Down Expand Up @@ -115,7 +113,7 @@ def response_broadcasts
end
end

swagger_path('broadcasts/{id}') do
swagger_path('/broadcasts/{id}') do
operation :get do
key :description, 'Returns a single broadcast.'
key :tags, [:broadcast]
Expand Down Expand Up @@ -162,48 +160,9 @@ def fetch_entries
scope
end

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 if params[:show_id].blank? && params[:year].blank? && params[:q].blank?
end

def accessible_entry_ids(entries)
scope = Broadcast.where(id: entries.map(&:id))
AudioAccess::Broadcasts.new(current_user).filter(scope).pluck(:id)
end

def require_user
render_unauthorized unless current_user
end

def fetch_current_user
if action_name == 'update'
Auth::Jwt.new(request).fetch_user ||
Auth::ApiToken.new(request).fetch_user
else
super
end
end

end
2 changes: 2 additions & 0 deletions app/controllers/concerns/admin/authenticatable.rb
@@ -1,4 +1,6 @@
module Admin
# Requires that all actions of an including controller are performed
# by an user with admin flag set to true.
module Authenticatable

extend ActiveSupport::Concern
Expand Down
43 changes: 43 additions & 0 deletions app/controllers/concerns/time_filterable.rb
@@ -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
26 changes: 26 additions & 0 deletions app/controllers/concerns/write_authenticatable.rb
@@ -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
128 changes: 128 additions & 0 deletions app/controllers/tracks_controller.rb
@@ -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
4 changes: 4 additions & 0 deletions app/models/broadcast.rb
Expand Up @@ -48,6 +48,10 @@ def duration
finished_at - started_at
end

def tracks
Track.within(started_at, finished_at)
end

private

def set_show_label_if_empty
Expand Down
41 changes: 41 additions & 0 deletions app/models/track.rb
@@ -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

0 comments on commit 5fdc990

Please sign in to comment.