diff --git a/.gitignore b/.gitignore
index f92525c..c0ef15b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,3 +32,5 @@
# Ignore master key for decrypting credentials and more.
/config/master.key
+
+**/.DS_Store
\ No newline at end of file
diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb
new file mode 100644
index 0000000..e84f58f
--- /dev/null
+++ b/app/controllers/categories_controller.rb
@@ -0,0 +1,49 @@
+class CategoriesController < ApplicationController
+ allow_unauthenticated_access only: %i[ index show ]
+ before_action :set_category, only: %i[ show edit update destroy ]
+ def index
+ @categories = Category.all
+ end
+
+ def show
+ @clips = Clip.where("category_id = ?", params[:id])
+ end
+
+ def new
+ @category = Category.new
+ end
+
+ def create
+ @category = Category.new(category_params)
+ if @category.save
+ redirect_to @category
+ else
+ render :new, status: :unprocessable_entity
+ end
+ end
+
+ def edit
+ end
+
+ def update
+ if @category.update(category_params)
+ redirect_to @category
+ else
+ render :edit, status: :unprocessable_entity
+ end
+ end
+
+ def destroy
+ @category.destroy
+ redirect_to categories_path
+ end
+
+ private
+ def set_category
+ @category = Category.find(params[:id])
+ end
+
+ def category_params
+ params.expect(category: [ :name ])
+ end
+end
diff --git a/app/controllers/clips_controller.rb b/app/controllers/clips_controller.rb
new file mode 100644
index 0000000..6fceeff
--- /dev/null
+++ b/app/controllers/clips_controller.rb
@@ -0,0 +1,48 @@
+class ClipsController < ApplicationController
+ allow_unauthenticated_access only: %i[ index show ]
+ before_action :set_clip, only: %i[ show edit update destroy ]
+ def index
+ @clips = Clip.all
+ end
+
+ def show
+ end
+
+ def new
+ @clip = Clip.new
+ end
+
+ def create
+ @clip = Clip.new(clip_params)
+ if @clip.save
+ redirect_to @clip
+ else
+ render :new, status: :unprocessable_entity
+ end
+ end
+
+ def edit
+ end
+
+ def update
+ if @clip.update(clip_params)
+ redirect_to @clip
+ else
+ render :edit, status: :unprocessable_entity
+ end
+ end
+
+ def destroy
+ @clip.destroy
+ redirect_to clips_path
+ end
+
+ private
+ def set_clip
+ @clip = Clip.find(params[:id])
+ end
+
+ def clip_params
+ params.expect(clip: [ :title, :video, :category_id ])
+ end
+end
diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
index 743238c..901e651 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -1,5 +1,5 @@
class HomeController < ApplicationController
def index
- @tapes = Tape.order("updated_at DESC").limit(10)
+ @clips = Clip.order("updated_at DESC").limit(10)
end
end
diff --git a/app/controllers/tapes_controller.rb b/app/controllers/tapes_controller.rb
deleted file mode 100644
index 102b95d..0000000
--- a/app/controllers/tapes_controller.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-class TapesController < ApplicationController
- allow_unauthenticated_access only: %i[ index show ]
- before_action :set_tape, only: %i[ show edit update destroy ]
- def index
- @tapes = Tape.all
- end
-
- def show
- end
-
- def new
- @tape = Tape.new
- end
-
- def create
- @tape = Tape.new(tape_params)
- if @tape.save
- redirect_to @tape
- else
- render :new, status: :unprocessable_entity
- end
- end
-
- def edit
- end
-
- def update
- if @tape.update(tape_params)
- redirect_to @tape
- else
- render :edit, status: :unprocessable_entity
- end
- end
-
- def destroy
- @tape.destroy
- redirect_to tapes_path
- end
-
- private
- def set_tape
- @tape = Tape.find(params[:id])
- end
-
- def tape_params
- params.expect(tape: [ :title, :video ])
- end
-end
diff --git a/app/helpers/categories_helper.rb b/app/helpers/categories_helper.rb
new file mode 100644
index 0000000..e06f315
--- /dev/null
+++ b/app/helpers/categories_helper.rb
@@ -0,0 +1,2 @@
+module CategoriesHelper
+end
diff --git a/app/helpers/clips_helper.rb b/app/helpers/clips_helper.rb
new file mode 100644
index 0000000..131c1f9
--- /dev/null
+++ b/app/helpers/clips_helper.rb
@@ -0,0 +1,9 @@
+module ClipsHelper
+ def seconds_to_time(seconds)
+ if seconds >= 3600 then
+ Time.at(seconds.round).utc.strftime("%H:%M:%S") #=> "01:00:00"
+ else
+ Time.at(seconds.round).utc.strftime("%M:%S") #=> "01:00:00"
+ end
+ end
+end
diff --git a/app/helpers/tapes_helper.rb b/app/helpers/tapes_helper.rb
deleted file mode 100644
index 9f2b0b0..0000000
--- a/app/helpers/tapes_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module TapesHelper
-end
diff --git a/app/models/category.rb b/app/models/category.rb
new file mode 100644
index 0000000..89e0619
--- /dev/null
+++ b/app/models/category.rb
@@ -0,0 +1,3 @@
+class Category < ApplicationRecord
+ has_many :clips
+end
diff --git a/app/models/tape.rb b/app/models/clip.rb
similarity index 54%
rename from app/models/tape.rb
rename to app/models/clip.rb
index ddd6bf3..005bbea 100644
--- a/app/models/tape.rb
+++ b/app/models/clip.rb
@@ -1,4 +1,5 @@
-class Tape < ApplicationRecord
+class Clip < ApplicationRecord
has_one_attached :video
+ belongs_to :category
validates :title, presence: true
end
diff --git a/app/views/categories/_form.html.erb b/app/views/categories/_form.html.erb
new file mode 100644
index 0000000..e36d9d4
--- /dev/null
+++ b/app/views/categories/_form.html.erb
@@ -0,0 +1,10 @@
+<%= form_with model: category do |form| %>
+
+ <%= form.label :name %>
+ <%= form.text_field :name %>
+
+
+
+ <%= form.submit %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/categories/edit.html.erb b/app/views/categories/edit.html.erb
new file mode 100644
index 0000000..e39f429
--- /dev/null
+++ b/app/views/categories/edit.html.erb
@@ -0,0 +1,4 @@
+Edit category
+
+<%= render "form", category: @category %>
+<%= link_to "Cancel", @category %>
\ No newline at end of file
diff --git a/app/views/categories/index.html.erb b/app/views/categories/index.html.erb
new file mode 100644
index 0000000..9f3d5ad
--- /dev/null
+++ b/app/views/categories/index.html.erb
@@ -0,0 +1,9 @@
+Categories
+
+<%= link_to "New category", new_category_path if authenticated? %>
+
+
+ <% @categories.each do |category| %>
+ <%= link_to category.name, category %>
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/categories/new.html.erb b/app/views/categories/new.html.erb
new file mode 100644
index 0000000..5d71c1a
--- /dev/null
+++ b/app/views/categories/new.html.erb
@@ -0,0 +1,4 @@
+New Category
+
+<%= render "form", category: @category %>
+<%= link_to "Cancel", categories_path %>
\ No newline at end of file
diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb
new file mode 100644
index 0000000..a93e8a1
--- /dev/null
+++ b/app/views/categories/show.html.erb
@@ -0,0 +1,42 @@
+<% cache @category do %>
+ <%= @category.name %>
+<% end %>
+
+
+
+ Preview
+ Title
+ Duration
+ Category
+ Updated at
+ Created at
+
+ <% @clips.each do |clip| %>
+
+
+ <%= link_to (image_tag url_for(clip.video.preview(resize_to_limit: [160, 120]).processed)), clip %>
+
+
+ <%= link_to clip.title, clip %>
+
+
+ <%= seconds_to_time(clip.video.metadata["duration"]) %>
+
+
+ <%= clip.category.name %>
+
+
+ <%= clip.updated_at %>
+
+
+ <%= clip.created_at %>
+
+
+ <% end %>
+
+
+<%= link_to "Back", categories_path%>
+<% if authenticated? %>
+ <%= link_to "Edit", edit_category_path(@category) %>
+ <%= button_to "Delete", @category, method: :delete, data: { turbo_confirm: "Are you sure?" } %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/tapes/_form.html.erb b/app/views/clips/_form.html.erb
similarity index 60%
rename from app/views/tapes/_form.html.erb
rename to app/views/clips/_form.html.erb
index 114fc78..43ccea0 100644
--- a/app/views/tapes/_form.html.erb
+++ b/app/views/clips/_form.html.erb
@@ -1,8 +1,9 @@
-<%= form_with model: tape do |form| %>
+<%= form_with model: clip do |form| %>
<%= form.label :title %>
<%= form.text_field :title %>
<%= form.file_field :video, :accept => 'video/quicktime,video/mp4' %>
+ <%= form.select :category_id, Category.all.collect{ |t| [ t.name, t.id ] }%>
diff --git a/app/views/clips/edit.html.erb b/app/views/clips/edit.html.erb
new file mode 100644
index 0000000..2ec8eb0
--- /dev/null
+++ b/app/views/clips/edit.html.erb
@@ -0,0 +1,4 @@
+
Edit clip
+
+<%= render "form", clip: @clip %>
+<%= link_to "Cancel", @clip %>
\ No newline at end of file
diff --git a/app/views/clips/index.html.erb b/app/views/clips/index.html.erb
new file mode 100644
index 0000000..2f08464
--- /dev/null
+++ b/app/views/clips/index.html.erb
@@ -0,0 +1,36 @@
+
Clips
+
+<%= link_to "New clip", new_clip_path if authenticated? %>
+
+
+
+ Preview
+ Title
+ Duration
+ Category
+ Updated at
+ Created at
+
+ <% @clips.each do |clip| %>
+
+
+ <%= link_to (image_tag url_for(clip.video.preview(resize_to_limit: [160, 120]).processed)), clip %>
+
+
+ <%= link_to clip.title, clip %>
+
+
+ <%= seconds_to_time(clip.video.metadata["duration"]) %>
+
+
+ <%= clip.category.name %>
+
+
+ <%= clip.updated_at %>
+
+
+ <%= clip.created_at %>
+
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/clips/new.html.erb b/app/views/clips/new.html.erb
new file mode 100644
index 0000000..1d5b7b4
--- /dev/null
+++ b/app/views/clips/new.html.erb
@@ -0,0 +1,4 @@
+
New Clip
+
+<%= render "form", clip: @clip %>
+<%= link_to "Cancel", clips_path %>
\ No newline at end of file
diff --git a/app/views/clips/show.html.erb b/app/views/clips/show.html.erb
new file mode 100644
index 0000000..00498c7
--- /dev/null
+++ b/app/views/clips/show.html.erb
@@ -0,0 +1,12 @@
+<% cache @clip do %>
+
<%= @clip.title %>
+<% end %>
+
Category: <%= link_to @clip.category.name, @clip.category %>
+<% if @clip.video.attached? %>
+<%= video_tag @clip.video, :controls => true%>
+<% end %>
+<%= link_to "Back", clips_path%>
+<% if authenticated? %>
+ <%= link_to "Edit", edit_clip_path(@clip) %>
+ <%= button_to "Delete", @clip, method: :delete, data: { turbo_confirm: "Are you sure?" } %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb
index bf12125..fe2a98b 100644
--- a/app/views/home/index.html.erb
+++ b/app/views/home/index.html.erb
@@ -1,8 +1,8 @@
Recently Updated
-
- <% @tapes.each do |tape| %>
+
+ <% @clips.each do |clip| %>
- <%= link_to tape.title, tape %>
+ <%= link_to clip.title, clip %>
<% end %>
\ No newline at end of file
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index f25d3ce..fab874b 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -17,6 +17,12 @@
+
+
<%# Includes all stylesheet files in app/assets/stylesheets %>
<%= stylesheet_link_tag :app %>
@@ -26,7 +32,8 @@
<%= link_to "Home", root_path %>
<%= button_to "Log out", session_path, method: :delete if authenticated? %>
<%= link_to "Login", new_session_path unless authenticated? %>
- <%= link_to "Tapes", tapes_path %>
+ <%= link_to "Clips", clips_path %>
+ <%= link_to "Categories", categories_path %>
<%= yield %>
diff --git a/app/views/tapes/edit.html.erb b/app/views/tapes/edit.html.erb
deleted file mode 100644
index 4aa1041..0000000
--- a/app/views/tapes/edit.html.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-Edit tape
-
-<%= render "form", tape: @tape %>
-<%= link_to "Cancel", @tape %>
\ No newline at end of file
diff --git a/app/views/tapes/index.html.erb b/app/views/tapes/index.html.erb
deleted file mode 100644
index 0421520..0000000
--- a/app/views/tapes/index.html.erb
+++ /dev/null
@@ -1,28 +0,0 @@
-Tapes
-
-<%= link_to "New tape", new_tape_path if authenticated? %>
-
-
-
- Preview
- Title
- Updated at
- Created at
-
- <% @tapes.each do |tape| %>
-
-
- <%= link_to (image_tag url_for(tape.video.preview(resize_to_limit: [160, 120]).processed)), tape %>
-
-
- <%= link_to tape.title, tape %>
-
-
- <%= tape.updated_at %>
-
-
- <%= tape.created_at %>
-
-
- <% end %>
-
\ No newline at end of file
diff --git a/app/views/tapes/new.html.erb b/app/views/tapes/new.html.erb
deleted file mode 100644
index 3fcff98..0000000
--- a/app/views/tapes/new.html.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-New tape
-
-<%= render "form", tape: @tape %>
-<%= link_to "Cancel", tapes_path %>
\ No newline at end of file
diff --git a/app/views/tapes/show.html.erb b/app/views/tapes/show.html.erb
deleted file mode 100644
index c975762..0000000
--- a/app/views/tapes/show.html.erb
+++ /dev/null
@@ -1,11 +0,0 @@
-<% cache @tape do %>
- <%= @tape.title %>
-<% end %>
-<% if @tape.video.attached? %>
-<%= video_tag @tape.video, :controls => true%>
-<% end %>
-<%= link_to "Back", tapes_path%>
-<% if authenticated? %>
- <%= link_to "Edit", edit_tape_path(@tape) %>
- <%= button_to "Delete", @tape, method: :delete, data: { turbo_confirm: "Are you sure?" } %>
-<% end %>
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index 684ce5e..68e0863 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -14,7 +14,8 @@ Rails.application.routes.draw do
# Defines the root path route ("/")
# root "posts#index"
- resources :tapes
+ resources :clips
+ resources :categories
root "home#index"
end
diff --git a/db/migrate/20250907225919_create_tapes.rb b/db/migrate/20250907225919_create_tapes.rb
deleted file mode 100644
index a134216..0000000
--- a/db/migrate/20250907225919_create_tapes.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class CreateTapes < ActiveRecord::Migration[8.0]
- def change
- create_table :tapes do |t|
- t.string :title
-
- t.timestamps
- end
- end
-end
diff --git a/db/migrate/20250908185632_create_clips.rb b/db/migrate/20250908185632_create_clips.rb
new file mode 100644
index 0000000..888d3cc
--- /dev/null
+++ b/db/migrate/20250908185632_create_clips.rb
@@ -0,0 +1,11 @@
+class CreateClips < ActiveRecord::Migration[8.0]
+ def change
+ create_table :clips do |t|
+ t.string :title
+
+ t.belongs_to :category, foreign_key: true
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20250909013509_create_categories.rb b/db/migrate/20250909013509_create_categories.rb
new file mode 100644
index 0000000..c47bd02
--- /dev/null
+++ b/db/migrate/20250909013509_create_categories.rb
@@ -0,0 +1,9 @@
+class CreateCategories < ActiveRecord::Migration[8.0]
+ def change
+ create_table :categories do |t|
+ t.string :name
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 942cd46..c7b1d60 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.0].define(version: 2025_09_08_020324) do
+ActiveRecord::Schema[8.0].define(version: 2025_09_09_013509) do
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
@@ -39,6 +39,20 @@ ActiveRecord::Schema[8.0].define(version: 2025_09_08_020324) do
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
+ create_table "categories", force: :cascade do |t|
+ t.string "name"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ create_table "clips", force: :cascade do |t|
+ t.string "title"
+ t.integer "category_id"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["category_id"], name: "index_clips_on_category_id"
+ end
+
create_table "sessions", force: :cascade do |t|
t.integer "user_id", null: false
t.string "ip_address"
@@ -48,12 +62,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_09_08_020324) do
t.index ["user_id"], name: "index_sessions_on_user_id"
end
- create_table "tapes", force: :cascade do |t|
- t.string "title"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- end
-
create_table "users", force: :cascade do |t|
t.string "email_address", null: false
t.string "password_digest", null: false
@@ -64,5 +72,6 @@ ActiveRecord::Schema[8.0].define(version: 2025_09_08_020324) do
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
+ add_foreign_key "clips", "categories"
add_foreign_key "sessions", "users"
end
diff --git a/test/controllers/categories_controller_test.rb b/test/controllers/categories_controller_test.rb
new file mode 100644
index 0000000..10bd601
--- /dev/null
+++ b/test/controllers/categories_controller_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class CategoriesControllerTest < ActionDispatch::IntegrationTest
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/test/controllers/tapes_controller_test.rb b/test/controllers/clips_controller_test.rb
similarity index 56%
rename from test/controllers/tapes_controller_test.rb
rename to test/controllers/clips_controller_test.rb
index 473571f..91abf87 100644
--- a/test/controllers/tapes_controller_test.rb
+++ b/test/controllers/clips_controller_test.rb
@@ -1,6 +1,6 @@
require "test_helper"
-class TapesControllerTest < ActionDispatch::IntegrationTest
+class ClipsControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
diff --git a/test/fixtures/categories.yml b/test/fixtures/categories.yml
new file mode 100644
index 0000000..7d41224
--- /dev/null
+++ b/test/fixtures/categories.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ name: MyString
+
+two:
+ name: MyString
diff --git a/test/fixtures/clips.yml b/test/fixtures/clips.yml
new file mode 100644
index 0000000..64d88ef
--- /dev/null
+++ b/test/fixtures/clips.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ title: MyString
+
+two:
+ title: MyString
diff --git a/test/models/category_test.rb b/test/models/category_test.rb
new file mode 100644
index 0000000..869357c
--- /dev/null
+++ b/test/models/category_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class CategoryTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/test/models/tape_test.rb b/test/models/clip_test.rb
similarity index 65%
rename from test/models/tape_test.rb
rename to test/models/clip_test.rb
index 19967c1..e919e4e 100644
--- a/test/models/tape_test.rb
+++ b/test/models/clip_test.rb
@@ -1,6 +1,6 @@
require "test_helper"
-class TapeTest < ActiveSupport::TestCase
+class ClipTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end