Skip to content

Commit

Permalink
Episode 4: Implement user and room management features
Browse files Browse the repository at this point in the history
  • Loading branch information
giljr committed Feb 17, 2025
1 parent 49b111b commit 2935c4b
Show file tree
Hide file tree
Showing 17 changed files with 163 additions and 102 deletions.
66 changes: 22 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,42 @@
Chat App - Third Implementation
## Chat App - Forth Implementation - in Vanilla Rails - No Docker
### Episode 4: User and Room Management

#### Overview
In this episode, we implement functionality for managing users and rooms in a Rails application. Users can create rooms, view their associated rooms, and log out. Rooms are displayed dynamically, and actions are handled using Turbo Frames for real-time updates.
Features

This is the third iteration of my Chat App, built with Ruby on Rails. Initially, I attempted to run the application using three separate containers, but due to persistent issues during development, I decided to proceed with a plain Rails app instead. This approach simplified the setup and allowed for more streamlined development.
User Authentication: Users can log in and log out.
Room Management: Users can create and view rooms associated with them.
Turbo Frames: Real-time updates for creating rooms and displaying user-specific content.

#### Features
Setup

```
Real-time messaging
Install dependencies:
Run the following command to install all required gems:

User authentication with Devise
Turbo Streams for live updates
Tailwind CSS for UI styling
```

#### Setup Instructions

Clone the repository:

```
git clone https://github.com/yourusername/chat-app-v3.git
cd chat-app-v3
```

Install dependencies:

```
bundle install
```

Set up the database:

```
rails db:create db:migrate db:seed
```

Run the server:
Run the commands below to set up the database:

```
bin/dev
```
rails db:drop
rails db:migrate

Access the app: Open http://localhost:3000 in your browser.
Start the server:

#### Notes
Run the server on 0.0.0.0:3001:

Originally, the app was containerized with three separate services, but due to issues with the setup, I opted for a traditional Rails environment.
rails s -b 0.0.0.0 -p 3001

Turbo Streams is used for real-time updates instead of ActionCable.
Create users and rooms:

#### Future Improvements
Create two users and populate their rooms as shown in the application.
## Documentation

Reintroduce containerization with a more refined approach.
[Documentation](https://medium.com/jungletronics/turbo-powered-chat-rooms-real-time-updates-with-hotwire-fb196de42353)

Implement WebSockets for better real-time performance.

Add support for file sharing in chat.
## License

##### License
[MIT](https://choosealicense.com/licenses/mit/)

This project is open-source and available under the MIT License.
4 changes: 4 additions & 0 deletions app/assets/stylesheets/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@
*
* Consider organizing styles into separate files for maintainability.
*/
/* turbo-frame {
display: block;
border: 1px solid green;
} */
18 changes: 18 additions & 0 deletions app/controllers/rooms_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def index
end

def show
@rooms = Room.all
end

def new
Expand All @@ -18,9 +19,26 @@ def edit

def create
@room = Room.new(room_params)

respond_to do |format|
if @room.save
UserRoom.create(room: @room, user: current_user)
format.turbo_stream {render turbo_stream: turbo_stream.append('rooms', partial: 'shared/room', locals: { room: @room })}
else
format.turbo_stream {render turbo_stream: turbo_stream.replace('room_form', partial: 'rooms/form', locals: { room: @room })}
end
end
end

def update
respond_to do |format|
if @room.update(room_params)
format.turbo_stream { render turbo_stream: turbo_stream.replace("room_#{@room.id}", partial: 'shared/room', locals: { room: @room }) }
else
format.html { render :edit }
# format.turbo_stream { render turbo_stream: turbo_stream.replace("room_#{@room.id}", partial: 'rooms/form', locals: { room: @room }) }
end
end
end

def destroy
Expand Down
11 changes: 11 additions & 0 deletions app/javascript/controllers/form_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
resetComponent() {
const form = this.element

const timer = setInterval(() => {
form.reset()
}, 75)
}
}
3 changes: 3 additions & 0 deletions app/models/room.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
class Room < ApplicationRecord
validates :name, presence: true
has_many :user_rooms
has_many :users, through: :user_rooms
end
2 changes: 2 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ class User < ApplicationRecord
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :user_rooms
has_many :rooms, through: :user_rooms
end
4 changes: 4 additions & 0 deletions app/models/user_room.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class UserRoom < ApplicationRecord
belongs_to :user
belongs_to :room
end
42 changes: 22 additions & 20 deletions app/views/rooms/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
<%= form_with(model: room, class: "flex flex-col my-3 w-full text-xs") do |form| %>
<% if room.errors.any? %>
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-md mt-3 text-xs">
<h2><%= pluralize(room.errors.count, "error") %> prohibited this room from being saved:</h2>
<div data-controller="form" >
<%= form_with(model: room, class: "flex flex-col my-3 w-full text-xs" , data: { controller: 'form', action: 'submit->form#resetComponent' }) do |form| %>
<% if room.errors.any? %>
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-md mt-3 text-xs">
<h2><%= pluralize(room.errors.count, "error") %> prohibited this room from being saved:</h2>

<ul class="list-disc ml-6">
<% room.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<ul class="list-disc ml-6">
<% room.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>

<div class="flex flex-col gap-1 justify-center">
<%#= form.label :name, class: 'font-semibold text-xs' %>
<%= form.text_field :name, placeholder: "Enter the room name", class: ["block shadow-sm rounded-md border text-xs px-2 py-1", {"border-gray-400 focus:outline-blue-600": room.errors[:name].none?, "border-red-400 focus:outline-red-600": room.errors[:name].any?}] %>
</div>
<div class="flex flex-col gap-1 justify-center">
<%#= form.label :name, class: 'font-semibold text-xs' %>
<%= form.text_field :name, placeholder: "Enter the room name", class: ["block shadow-sm rounded-md border text-xs px-2 py-1", {"border-gray-400 focus:outline-blue-600": room.errors[:name].none?, "border-red-400 focus:outline-red-600": room.errors[:name].any?}] %>
</div>

<div class="mt-auto flex justify-end">
<%= form.submit class: "rounded-md px-3 py-2 bg-blue-600 hover:bg-blue-500 text-white text-xs" %>
<%= link_to 'Cancel', :back, class: "rounded-md px-3 py-2 bg-gray-300 hover:bg-gray-200 text-xs" %>
</div>
<% end %>
<div class="mt-auto flex justify-end">
<%= form.submit class: "rounded-md px-3 py-2 bg-blue-600 hover:bg-blue-500 text-white text-xs" %>
<%= link_to 'Cancel', :back, class: "rounded-md px-3 py-2 bg-gray-300 hover:bg-gray-200 text-xs" %>
</div>
<% end %>
</div>
12 changes: 6 additions & 6 deletions app/views/rooms/_room.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div id="<%= dom_id room %>">
<p class="my-5">
<strong class="block font-medium mb-1">Name:</strong>
<%= room.name %>
</p>
</div>
<section class="flex flex-col gap-2 mt-10 items-center justify-center" >
<div class="flex flex-col">
<h3 class="font-bold text-3xl"><%= room.name %> - Room </h3>
<span class="font-semibold">Users In This Room:</span>
</div>
</section>
12 changes: 4 additions & 8 deletions app/views/rooms/edit.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
<% content_for :title, "Editing room" %>

<div class="md:w-2/3 w-full">
<h1 class="font-bold text-4xl">Editing room</h1>

<%= render "form", room: @room %>

<%= link_to "Show this room", @room, class: "ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
<%= link_to "Back to rooms", rooms_path, class: "ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
<%= turbo_frame_tag "room_#{@room.id}" do %>
<!-- <h4 class="text-xs font-semibold">Edit</h4> -->
<%= render "form", room: @room %>
<% end %>
</div>
26 changes: 9 additions & 17 deletions app/views/rooms/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
<% content_for :title, "Showing room" %>

<div class="md:w-2/3 w-full">
<% if notice.present? %>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-md inline-block" id="notice"><%= notice %></p>
<% end %>

<h1 class="font-bold text-4xl">Showing room</h1>

<%= render @room %>

<%= link_to "Edit this room", edit_room_path(@room), class: "mt-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
<%= link_to "Back to rooms", rooms_path, class: "ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
<div class="inline-block ml-2">
<%= button_to "Destroy this room", @room, method: :delete, class: "mt-2 rounded-md px-3.5 py-2.5 text-white bg-red-600 hover:bg-red-500 font-medium" %>
</div>
</div>
<%= turbo_frame_tag 'rooms_controller' do %>
<main class="bg-indigo-100 flex w-full h-screen" >
<%= render "shared/side_bar" %>

<section class="w-5/6 flex flex-col gap-5 h-screen px-5">
<%= render 'room', room: @room %>
</section>
</main>
<% end %>
11 changes: 8 additions & 3 deletions app/views/shared/_room.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
<div class="font-semibold text-xs">
<%= room.name %>
</div>
<%= turbo_frame_tag "room_#{room.id}" do %>
<div class="font-semibold text-xs">
<%= link_to room_path(room), class:"text-xs", data: { turbo_frame: 'rooms_controller' } do %>
<%= room.name %>
<% end %>
<%= link_to 'Edit', edit_room_path(room), id: "edit_room_#{room.id}", class: 'text-xs font-medium bg-white text-gray-700 border border-gray-300 rounded-lg px-3 py-1 shadow-sm hover:bg-gray-100'%>
</div>
<% end %>
14 changes: 11 additions & 3 deletions app/views/shared/_user_rooms.html.erb
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
<section class="flex flex-col w-full items-center">
<span class="text-xs ext-center font_semibold mt-5"><%= current_user.email %></span>
<div class='flex items-center justify-center mt-2'>
<%= button_to 'Log Out', destroy_user_session_path, method: :delete, data: {turbo: false }, class: 'px-3 py-1 bg-red-300 rounded font-semibold text-xs' %>
</div>
<h3 class="text-xs font-semibold mt-3 text-center mb-2">My Rooms</h3>

<div class="flex flex-col px-5 gap-2 w-full">
<% @rooms.each do |room| %>
<%= render 'shared/room', room: room %>
<div class="flex flex-col px-5 gap-2 w-full" id="rooms">
<% if @rooms.present? %>
<% current_user.rooms.all.each do |room| %>
<%= render 'shared/room', room: room %>
<% end %>
<% else %>
<p class="text-gray-500 text-xs">No rooms available.</p>
<% end %>
</div>
</section>
10 changes: 10 additions & 0 deletions db/migrate/20250217232446_create_user_rooms.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CreateUserRooms < ActiveRecord::Migration[8.0]
def change
create_table :user_rooms do |t|
t.references :user, null: false, foreign_key: true
t.references :room, null: false, foreign_key: true

t.timestamps
end
end
end
14 changes: 13 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions test/fixtures/user_rooms.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
user: one
room: one

two:
user: two
room: two
7 changes: 7 additions & 0 deletions test/models/user_room_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require "test_helper"

class UserRoomTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

0 comments on commit 2935c4b

Please sign in to comment.