Skip to content

Commit

Permalink
feat: welcome email
Browse files Browse the repository at this point in the history
feat: add profile.name

TODO: default to Faker random
  • Loading branch information
sean-garwood committed Nov 20, 2024
1 parent a008d28 commit f062a36
Show file tree
Hide file tree
Showing 19 changed files with 270 additions and 11 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,5 @@ end
gem "devise", "~> 4.9"

gem "faker", "~> 3.5"

gem "letter_opener", "~> 1.10", :group => :development

Check failure on line 65 in Gemfile

View workflow job for this annotation

GitHub Actions / lint

Style/HashSyntax: Use the new Ruby 1.9 hash syntax.
8 changes: 8 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ GEM
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
childprocess (5.1.0)
logger (~> 1.5)
coderay (1.1.3)
concurrent-ruby (1.3.4)
connection_pool (2.4.1)
Expand Down Expand Up @@ -149,6 +151,11 @@ GEM
activesupport (>= 5.0.0)
json (2.8.2)
language_server-protocol (3.17.0.3)
launchy (3.0.1)
addressable (~> 2.8)
childprocess (~> 5.0)
letter_opener (1.10.0)
launchy (>= 2.2, < 4)
listen (3.9.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
Expand Down Expand Up @@ -350,6 +357,7 @@ DEPENDENCIES
guard-minitest (~> 2.4, >= 2.4.6)
importmap-rails
jbuilder
letter_opener (~> 1.10)
pg (~> 1.1)
puma (>= 5.0)
rails (~> 7.2.2)
Expand Down
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ This project will give you a chance to take a relatively high level set of requi

### Specifications

Keep the following requirements in mind. We’ll cover specific steps to get started below this list:
Keep the following requirements in mind. We’ll cover specific steps to get
started below this list:

#### done

1. Use PostgreSQL for your database from the beginning (not SQLite3), that way your deployment will go much more smoothly.
1. Users must sign in to see anything except the sign in page.
Expand All @@ -24,20 +27,33 @@ Keep the following requirements in mind. We’ll cover specific steps to get sta
1. Users can like posts.
1. Users can comment on posts.
1. Posts should always display the post content, author, comments, and likes.
1. There should be an index page for posts, which shows all the recent posts from the current user and users they are following.
1. Users can create a profile with a profile picture. You may be able to get the profile picture when users sign in using OmniAuth. If this isn’t the case you can use Gravatar to generate the photo.
1. A user’s profile page should contain their profile information, profile photo, and posts.
1. There should be an index page for users, which shows all users and buttons for sending follow requests to users the user is not already following or have a pending request.
1. There should be an index page for posts, which shows all the recent posts from the current user and users they are following.
<<<<<<< Updated upstream

#### todo

=======
>>>>>>> Stashed changes
1. Set up a mailer to send a welcome email when a new user signs up. Use the Letter Opener gem to test it in development mode.

#### todo

1. Deploy your App to a hosting provider.
1. Set up an email provider and start sending real emails.
1. (NOT SPECIFIED, BUT NICE TO HAVE) Allow users to view and accept friend
requests. This could be done on the views/requests/index.html.erb with
embedded buttons

### Extra credit
##### Extra credit

1. Make posts also allow images (either just via a URL or, more complicated, by uploading one).
1. Use Active Storage to allow users to upload a photo to their profile.
1. Make your post able to be either a text OR a photo by using a polymorphic association (so users can still like or comment on it while being none-the-wiser).
1. Style it up nicely! We’ll dive into HTML/CSS in the next course.
1. (not specified, but cool) Use turbo frames to avoid rendering entire pages

### Getting started

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/posts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class PostsController < ApplicationController

def index
# code to list all posts
@posts = Post.all
@posts = Post.by_followed_users(current_user)
end

def show
Expand Down
2 changes: 1 addition & 1 deletion app/mailers/application_mailer.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class ApplicationMailer < ActionMailer::Base
default from: "from@example.com"
default from: "admin@matltc.com"
layout "mailer"
end
7 changes: 7 additions & 0 deletions app/mailers/user_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class UserMailer < ApplicationMailer
def welcome_email
@user = params[:user]
@url = new_user_session_url
mail(to: @user.email, subject: "Welcome to our site!")
end
end
8 changes: 7 additions & 1 deletion app/models/post.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ class Post < ApplicationRecord
belongs_to :author, class_name: "User", foreign_key: "author_id"
has_many :comments, dependent: :destroy
has_many :likes, dependent: :destroy

validates_presence_of :body, :title, :author_id

private
def self.by_followed_users(user)
followed_user_ids = user.followed_user_ids
followed_user_ids << user.id
where(author_id: followed_user_ids)
end
end
6 changes: 4 additions & 2 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ class User < ApplicationRecord
has_many :liked_posts, through: :likes, source: :post
has_many :posts, inverse_of: :author, dependent: :destroy
has_one :profile, dependent: :destroy, inverse_of: :user, touch: true
delegate :name, to: :profile, allow_nil: true
validates_presence_of :email, unique: true

private
def build_default_profile
@profile = Profile.new(user: self)
@profile.bio = "Welcome to my profile! I have not updated the default bio."
@profile = Profile.new(user: self,
name: Faker::TvShows::Simpsons.character,
bio: Faker::TvShows::Simpsons.quote)
logger = (@profile.save) ? "Profile created successfully." : "Failed to create profile."
Rails.logger.info { logger }
end
Expand Down
30 changes: 28 additions & 2 deletions app/views/layouts/mailer.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,36 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
/* Email styles need to be inline */
body {
font-family: 'Arial', sans-serif;
}

h1 {
font-size: 24px;
font-weight: bold;
margin: 0 0 10px;
}

p {
margin: 0 0 10px;
}

a {
color: #007bff;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

.container {
margin: 0 auto;
max-width: 600px;
padding: 20px;
}
</style>
</head>

<body>
<%= yield %>
</body>
Expand Down
8 changes: 8 additions & 0 deletions app/views/user_mailer/weclome_email.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<h1>Welcome to matltc.com, <%= @user.name %></h1>
<p>
You have successfully signed up to matltc.com.
</p>
<p>
To login to the site, just follow <%= link_to "this link", @url %>
</p>
<p>Thanks for joining and have a great day!</p>
5 changes: 5 additions & 0 deletions app/views/user_mailer/welcome_email.text.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Welcome to matltc.com<%= @user.name ? ", #{@user.name}" : nil %>.
================================================================================
You have successfully signed up to matltc.com.
To login to the site, just follow this link: <%= @url %>
Thanks for joining and have a great day!
2 changes: 2 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
config.action_mailer.perform_caching = false

config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
config.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true

# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20241120203826_add_name_to_profile.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddNameToProfile < ActiveRecord::Migration[7.2]
def change
add_column :profiles, :name, :string
end
end
3 changes: 2 additions & 1 deletion db/schema.rb

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

152 changes: 152 additions & 0 deletions public/gtoons.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>gToons</title>
<style>
body {
font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
background-color: #00ffff;
margin: 0 50px;
/* align the page in the center */
align-items: center;
display: flex;
flex-direction: column;
min-width: 380px;
}

.content,
.header {
max-width: 800px;
}

.img-caption {
margin: 0.5em 1em;
font-style: italic;
font-size: 12px;
color: grey;
}

.img-container {
margin: 0.5em 0 1em;
}

#banner,
#gtoons-app {
max-width: 100%;
height: auto;
display: block;
}

.store-link {
height: 100px;
width: 200px;
}

ul.logo {
padding: 0;
}
</style>
</head>

<body>
<div class="content-container">
<h1>gToons</h1>
<h2>The Game of Kings</h2>
<p>On October 14th, 2002, Cartoon Network released gToons. The game could be played on Cartoon Orbit: a Flash web
app in which users could create an account and earn points to purchase cToons to decorate their cZones with, and
now, purchase and play gToons.</p>
<p>Users could sell these items in auctions, or trade these items with other users. gToons, however, implemented a
lobby-and-leaderboard format in which a player could play a random match--truly random, since rank was not taken
into consideration--or issue a challenge to another player.</p>
<p>Wins would net a player roughly eight to nine rating points against a much lower-rated opponent, while a loss
against such an opponent might net negative four to five points. If one were to defeat someone much higher ranked
than them, they might earn twelve or fourteen points, but a loss against such an opponent would earn them no more
than negative five.</p> Cartoon Orbit, and thus gToons, ceased operations sometime in September 2006, and with it,
the lobby. Luckily, the game can still be played in its entirety at this wonderful site: <div class="img-container">
<a href="https://gtoons.app"><img id="gtoons-app" src="./gtoons_app.png" alt=""></a>
</div>
<h2>Rules</h2>
<h3>Win conditions</h3>
<p>The two win conditions follow, listed in order of precedence:</p>
<ol>
<li>Color: A player wins by color if he or she has more cards in play of two certain colors at the end of the
match.</li>
<li>Points: A player wins by points if he or she has more points than their opponent at the end of the match.</li>
</ol>
<p>If neither of these conditions are met, the game is a tie.</p>
<h3>Attributes of the cards</h3>
<p>Every card in gToons has four attributes that affected gameplay:</p>
<ul>
<li>Color</li>
<li>Point value</li>
<li>Character</li>
<li>Power</li>
</ul>
<h4>Color</h4>
<p>Every card has one of the following colors:</p>
<ul>
<li>Black*</li>
<li>Blue</li>
<li>Green</li>
<li>Orange</li>
<li>Purple</li>
<li>Red</li>
<li>Silver*</li>
<li>Yellow</li>
</ul>
<h5>* - Non-colors</h5>
<ul>
<li>Black - neutral, does not count toward color wins or bonuses.</li>
<li>Silver - same attribute as Black, but also can be chosen to be any color, including Silver itself.</li>
</ul>
<p>More on color to follow in the <a href="#gameplay">gameplay section</a>.</p>
<h4>Point value</h4>
<p>Cards are worth anywhere from one to twelve points. Silver cards are worth 1-3; black cards are worth 8-10.</p>
<h4>Character</h4>
<p>Only one card may be in play with a given character attribute. If a card is revealed to have a character
attribute that is already in play, then the highest-point value card is "cancelled," or removed from play. If they
have the same point value, then both are cancelled.</p>
<h4>Power</h4>
<p>Text that dictates an added effect that a card has while it is in play. Not all cards have powers.</p>
<h3 id="gameplay">Gameplay</h3>
<h4>Phases</h4>
<p>Gameplay can be broken up into three main phases:</p>
<ul>
<li>Cut phase</li>
<li>Turn 1</li>
<li>Turn 2</li>
</ul>
<h5>Cut phase</h5>
<p>Players choose form one of four decks consisting of exactly twelve cards each: A, B, C or D. (These decks are
built outside the game itself.)</p> Then, the bottom card from each player's deck is revealed. There are three
possibilities: <ol>
<li>Two colors--neither of which are "non-colors," i.e. silver or black--are revealed</li>
<ul>*The color win condition is possible if and only if this occurs.*</ul>
<li>One non-color is revealed, and one color is revealed.</li>
<ul>A "color bonus" of fifteen points is added to the total of the player with the most cards of that color in
play at the end of the game.</ul>
<li>No colors are revealed</li>
<ul>Nothing happens.</ul>
</ol>
<h5>Turn 1</h5>
<p>Players are dealt six cards from their deck. They play four of them, face-down, in four slots on the gameboard,
which has fourteen total slots--seven for each player, in a half-honeycomb formation--that forms a hexagon that is
bisected widthwise to delineate each player's side of the gameboard on which they may place their cards. (It is
helpful to think of these as numbered from one to seven on each player's side.)</p>
<p>The cards are revealed one-by-one, powers and cancellations resolve, and points and colors (if applicable) are
tallied.</p>
<p>Of the two remaining cards in each player's hand, they can choose to either discard one, both, or none of them.
</p>
<h5>Turn 2</h5>
<p>Each player is dealt cards until they have six in their hand. They then play three of these cards to the
remaining three slots on their respective sides of the gameboard. Then, the first two cards are revealed.</p>
<p>Each player is then given the option to swap their last card with one of the three remaining cards in their own
hand, at the cost of ten points. Then, the players choose the color for their silver cards in play. Then, once
everything is resolved, the winner is determined.</p>
</div>
</body>

</html>
Binary file added public/gtoons_app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions test/controllers/posts_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
patch post_path(@post), params: { post: { title: "Updated Title", body: "Updated Body" } }
assert_response :redirect, "Failed to update post"
end

test "index should only show posts from self and followed users" do
get posts_path
assert_select ".post-container", (Post.by_followed_users(@user).count + @user.posts.count)
end
end
3 changes: 3 additions & 0 deletions test/mailers/previews/user_mailer_preview.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview
end
Loading

0 comments on commit f062a36

Please sign in to comment.