Skip to content

Commit 9c2dede

Browse files
Merge pull request #20 from vigetlabs/nd/10-sounds-controller
[#10] Add sound controller and active storage
2 parents 3a6b3ca + b0e8051 commit 9c2dede

12 files changed

Lines changed: 224 additions & 31 deletions

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ cd extension && pnpm install
2222

2323
### Database
2424

25-
TODO
25+
```bash
26+
cd api
27+
bundle exec rails db:create db:migrate
28+
```
29+
30+
TODO: seed database with default sounds
2631

2732
### Run servers
2833

api/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ tmp/
1212
!/tmp/.keep
1313
/log/*
1414
!/log/.keep
15+
/storage/*
16+
!/storage/.keep
1517

1618
# Used by dotenv library to load environment variables.
1719
.env
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
class SoundsController < ApplicationController
2+
def index
3+
sounds = Sound.all
4+
render json: SoundSerializer.new(sounds).serializable_hash.to_json
5+
end
6+
7+
def show
8+
render json: SoundSerializer.new(sound).serializable_hash.to_json
9+
end
10+
11+
def create
12+
sound = Sound.new(sound_params)
13+
sound.audio_file.attach(sound_params[:audio_file])
14+
sound.tag_ids = sound_params[:tag_ids] if sound_params[:tag_ids]
15+
if sound.save
16+
render json: SoundSerializer.new(sound).serializable_hash.to_json, status: :created
17+
else
18+
render json: { errors: sound.errors }, status: :unprocessable_entity
19+
end
20+
end
21+
22+
def update
23+
if sound.update(sound_params)
24+
render json: SoundSerializer.new(sound).serializable_hash.to_json
25+
else
26+
render json: { errors: sound.errors }, status: :unprocessable_entity
27+
end
28+
end
29+
30+
def destroy
31+
sound.destroy
32+
head :no_content
33+
end
34+
35+
private
36+
37+
def sound
38+
@sound ||= Sound.find(params[:id])
39+
end
40+
41+
def sound_params
42+
params.require(:sound).permit(:name, :audio_file, tag_ids: [])
43+
end
44+
end

api/app/models/sound.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
class Sound < ApplicationRecord
22
has_and_belongs_to_many :tags
3+
has_one_attached :audio_file
34

4-
validates :name, :file_url, presence: true
5+
validates :name, presence: true
6+
validate :audio_file_presence
7+
8+
def audio_file_presence
9+
errors.add(:audio_file, "must be attached") unless audio_file.attached?
10+
end
511
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class SoundSerializer
2+
include JSONAPI::Serializer
3+
attributes :id, :name, :tag_ids
4+
5+
attribute :audio_file_url do |object|
6+
if object.audio_file.attached?
7+
Rails.application.routes.url_helpers.rails_blob_url(object.audio_file, only_path: true)
8+
end
9+
end
10+
end

api/config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
sessions: "users/sessions",
99
registrations: "users/registrations"
1010
}
11+
resources :sounds
1112
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
1213

1314
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# This migration comes from active_storage (originally 20170806125915)
2+
class CreateActiveStorageTables < ActiveRecord::Migration[7.0]
3+
def change
4+
# Use Active Record's configured type for primary and foreign keys
5+
primary_key_type, foreign_key_type = primary_and_foreign_key_types
6+
7+
create_table :active_storage_blobs, id: primary_key_type do |t|
8+
t.string :key, null: false
9+
t.string :filename, null: false
10+
t.string :content_type
11+
t.text :metadata
12+
t.string :service_name, null: false
13+
t.bigint :byte_size, null: false
14+
t.string :checksum
15+
16+
if connection.supports_datetime_with_precision?
17+
t.datetime :created_at, precision: 6, null: false
18+
else
19+
t.datetime :created_at, null: false
20+
end
21+
22+
t.index [ :key ], unique: true
23+
end
24+
25+
create_table :active_storage_attachments, id: primary_key_type do |t|
26+
t.string :name, null: false
27+
t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
28+
t.references :blob, null: false, type: foreign_key_type
29+
30+
if connection.supports_datetime_with_precision?
31+
t.datetime :created_at, precision: 6, null: false
32+
else
33+
t.datetime :created_at, null: false
34+
end
35+
36+
t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
37+
t.foreign_key :active_storage_blobs, column: :blob_id
38+
end
39+
40+
create_table :active_storage_variant_records, id: primary_key_type do |t|
41+
t.belongs_to :blob, null: false, index: false, type: foreign_key_type
42+
t.string :variation_digest, null: false
43+
44+
t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
45+
t.foreign_key :active_storage_blobs, column: :blob_id
46+
end
47+
end
48+
49+
private
50+
def primary_and_foreign_key_types
51+
config = Rails.configuration.generators
52+
setting = config.options[config.orm][:primary_key_type]
53+
primary_key_type = setting || :primary_key
54+
foreign_key_type = setting || :bigint
55+
[ primary_key_type, foreign_key_type ]
56+
end
57+
end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class RemoveFileUrlFromSounds < ActiveRecord::Migration[8.0]
2+
def change
3+
remove_column :sounds, :file_url, :string
4+
end
5+
end

api/db/schema.rb

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,41 +10,40 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[8.0].define(version: 2025_07_22_172833) do
13+
ActiveRecord::Schema[8.0].define(version: 2025_07_22_223841) do
1414
# These are extensions that must be enabled in order to support this database
1515
enable_extension "pg_catalog.plpgsql"
1616

17-
# create_table "active_storage_attachments", force: :cascade do |t|
18-
# t.string "name", null: false
19-
# t.string "record_type", null: false
20-
# t.bigint "record_id", null: false
21-
# t.bigint "blob_id", null: false
22-
# t.datetime "created_at", null: false
23-
# t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
24-
# t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
25-
# end
17+
create_table "active_storage_attachments", force: :cascade do |t|
18+
t.string "name", null: false
19+
t.string "record_type", null: false
20+
t.bigint "record_id", null: false
21+
t.bigint "blob_id", null: false
22+
t.datetime "created_at", null: false
23+
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
24+
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
25+
end
2626

27-
# create_table "active_storage_blobs", force: :cascade do |t|
28-
# t.string "key", null: false
29-
# t.string "filename", null: false
30-
# t.string "content_type"
31-
# t.text "metadata"
32-
# t.string "service_name", null: false
33-
# t.bigint "byte_size", null: false
34-
# t.string "checksum"
35-
# t.datetime "created_at", null: false
36-
# t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
37-
# end
27+
create_table "active_storage_blobs", force: :cascade do |t|
28+
t.string "key", null: false
29+
t.string "filename", null: false
30+
t.string "content_type"
31+
t.text "metadata"
32+
t.string "service_name", null: false
33+
t.bigint "byte_size", null: false
34+
t.string "checksum"
35+
t.datetime "created_at", null: false
36+
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
37+
end
3838

39-
# create_table "active_storage_variant_records", force: :cascade do |t|
40-
# t.bigint "blob_id", null: false
41-
# t.string "variation_digest", null: false
42-
# t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
43-
# end
39+
create_table "active_storage_variant_records", force: :cascade do |t|
40+
t.bigint "blob_id", null: false
41+
t.string "variation_digest", null: false
42+
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
43+
end
4444

4545
create_table "sounds", force: :cascade do |t|
4646
t.string "name", null: false
47-
t.string "file_url", null: false
4847
t.datetime "created_at", null: false
4948
t.datetime "updated_at", null: false
5049
end
@@ -77,6 +76,6 @@
7776
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
7877
end
7978

80-
# add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
81-
# add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
79+
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
80+
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
8281
end
159 KB
Binary file not shown.

0 commit comments

Comments
 (0)