ساخت اپلیکیشن اینستاگرام با Ruby on Rails (بخش اول) — از صفر تا صد

۲۱۳ بازدید
آخرین به‌روزرسانی: ۲۴ مهر ۱۴۰۱
زمان مطالعه: ۱۶ دقیقه
ساخت اپلیکیشن اینستاگرام با Ruby on Rails (بخش اول) — از صفر تا صد

در این نوشته روش ساخت یک اپلیکیشن مشابه اینستاگرام با استفاده از Ruby on Rails را توضیح می‌دهیم. مواردی که با مطالعه این راهنما یاد می‌گیرید را در ادامه فهرست کرده‌ایم:

  • چگونه یک اپلیکیشن Rails جدید آغاز کنیم؟
  • طراحی سیستم از صفر
  • درک معماری MVC (مدل-ویو-کنترلر)
  • مدل: مهاجرت (Migration) به Active Record، اعتبارسنجی، فراخوانی، ارتباط، و اینترفیس کوئری.
  • ویو: تابع‌های کمکی Layout ،Partial و Form
  • کنترلر: Action-ها، پارامترهای Strong
  • مسیریابی Rails
  • ذخیره‌سازی Active برای آپلود فایل‌ها
  • استفاده از بوت‌استرپ، Devise ،Kaminari gem در اپلیکیشن‌های Rails.

در این مقاله با مباحث زیر آشنا خواهیم شد:

  • مجموعه‌های فناوری
  • کسب بینشی از MVC در Ruby on Rails
  • ایجاد اپلیکیشن‌های Rails جدید
  • استفاده از پایگاه داده PostgreSQL و بوت‌استرپ برای اپلیکیشن
  • فرایندهای Sign Up ،Sign In و Sign Out برای کاربران با استفاده از Devise gem
  • ایجاد صفحه پروفایل کاربری
  • ویرایش/به‌روزرسانی پروفایل کاربر

مجموعه فناوری‌های مورد نیاز در این مقاله به شرح زیر هستند:

بک‌اند:

  • Ruby 2.4
  • Rails 5.2.x
  • Database: Postgres 9.6

فرانت‌اند:

  • HTML, CSS, Javascript, jQuery
  • (Bootstrap (3.x or 4.x

MVC (مدل-ویو-کنترلر) در Ruby on Rails

MVC یک الگوی معماری برای اپلیکیشن‌های نرم‌افزاری است. این معماری برای طراحی وب اپلیکیشن‌ها بسیار محبوب است. MVC در PHP‌ به عنوان چارچوبی رایج برای توسعه وب کاربرد دارد. این معماری موجب جداسازی اپلیکیشن به دسته‌های زیر می‌شود:

  • مدل‌ها (Active Record): به مدیریت داده و منطق تجاری اپلیکیشن می‌پردازد.
  • ویو ها (ActionView): به مدیریت شیءهای اینترفیس و ارائه می‌پردازد.
  • کنترلرها (ActionController): بین مدل و ویو قرار می‌گیرد و با دریافت ورودی کاربر تصمیم می‌گیرد که با آن چه بکند.

چرخه درخواست-پاسخ در Rails

Request-Response Cycle in Rails

اگر بخواهیم این فرایند را به صورت فهرستی نشان دهیم، به صورت زیر خواهد بود:

  1. کاربر مرورگرش را باز می‌کند، یک URL وارد می‌کند و اینتر را می‌زند. زمانی که یک کاربر اینتر را میزند، مرورگر درخواستی به آن URL می‌فرستد.
  2. این درخواست به روتر Rails می‌رسد (config/routes.rb).
  3. روتر، URL را به کنترلر و اکشن صحیح نگاشت می‌کند تا این درخواست مدیریت شود.
  4. اکشن مربوطه درخواست را دریافت کرده و از مدل درخواست می‌کند که داده‌های مربوطه از پایگاه داده واکشی شوند.
  5. مدل، داده‌ها را به اکشن کنترلر بازمی‌گرداند.
  6. اکشن کنترلر داده‌ها را به ویو بازمی‌گرداند.
  7. ویو صفحه را به صورت HTML رندر می‌کند.
  8. کنترلر HTML را به مرورگر بازمی‌گرداند. سپس صفحه بارگذاری می‌شوند و کاربر می‌تواند آن را ببیند.

ایجاد اپلیکیشن‌های Rails جدید

برای ایجاد یک اپلیکیشن جدید در Rails باید مراحل زیر را طی کنیم:

نصب Rails

برای نصب Rails باید از دستور gem install استفاده کنیم که از سوی RubyGems ارائه شده است:

gem install rails -v 5.2.1

پس از نصب Rails اقدام به بررسی نسخه نصب شده می‌کنیم:

rails --version
=> Rails 5.2.1

اگر چیزی مانند Rails 5.2.1 بازگشت یابد، می‌توانیم به کار خود ادامه داده و اپلیکیشن‌های جدید Rails ایجاد کنیم.

نصب PostgreSQL

در سیستم عامل Mac OSX می‌توان سرور و کلاینت PostgreSQL را از Homebrew نصب کرده:

brew install postgresql

سرویس Postgresql به صورت زیر آغاز می‌شود:

brew services start postgresql

ایجاد اپلیکیشن جدید Rails

rails new instagram --version=5.2.1

پس از ایجاد اپلیکیشن اینستاگرام به پوشه آن مراجعه کنید:

cd Instagram

Gems را برای اپلیکیشن‌مان ایجاد کنید:

bundle install

با دستور زیر نیز وب‌سرور را راه‌اندازی کنید:

rails server

برای مشاهده اپلیکیشن می‌توانید مرورگر خود را باز کرده و به آدرس http://localhost:3000/ مراجعه کنید. در این مرحله صفحه پیش‌فرض Rails را مشاهده خواهید کرد:

جهت متوقف کردن سرور نیز می‌توانید دکمه‌های Ctrl+G را در ترمینال وارد کنید.

ایجاد صفحه اصلی

  • یک کنترلر با یک اکشن ایجاد کنید.
  • یک مسیر اضافه کنید.

یک کنترلر به نام Home را با اکشن index ایجاد کنید:

rails g controller Home index

Rails برخی فایل‌ها و یک مسیر برای شما ایجاد می‌کند:

create app/controllers/home_controller.rb
route get 'home/index'
invoke erb
create app/views/home
create app/views/home/index.html.erb
invoke test_unit
create test/controllers/home_controller_test.rb
invoke helper
create app/helpers/home_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/home.coffee
invoke scss
create app/assets/stylesheets/home.scss

فایل app/views/home/index.html.erb را باز کرده و کد موجود را با کد زیر جایگزین کنید:

<h1>This is my home page</h1>

وب‌سرور را ری‌استارت کنید. به این منظور می‌توانید ابتدا با دکمه‌های Ctrl+G سرور را متوقف کرده و سپس با دستور rails server آن را آغاز کنید و در مرورگر خود به آدرس http://localhost:3000/home/index بروید. بدین ترتیب پیام «This is my home page» را که در فایل iews/home/index.html.erb قرار داده‌اید، مشاهده می‌کنید. اینک فایل config/routes.rb را باز کنید:

Rails.application.routes.draw do
   get 'home/index'

   # For details on the DSL available within this file, see
   http://guides.rubyonrails.org/routing.html
end

و خط کدی که به صورت root 'home#index' است را اضافه نمایید. اینک کل کد باید به صورت زیر در آمده باشد:

Rails.application.routes.draw do
   get 'home/index'

   root to: ‘home#index’
end
  • بخش 'get 'home/index به Rails اعلام می‌کند که درخواست‌ها به آدرس http://localhost:3000/home/index را به اکشن index در کنترلر Home هدایت کند.
  • بخش 'root 'home#index به Rails اعلام می‌کند که درخواست‌ها به ریشه اپلیکیشن را به اکشن index در کنترلر home هدایت کند.

اینک وب‌سرور را ری‌استارت کرده و به آدرس http://localhost:3000 مراجعه کنید تا با پیامی به صورت «This is my home page» مواجه شوید. شما می‌توانید همه مسیرهای کنونی اپلیکیشن را با دستور زیر مشاهده کنید:

rails routes

استفاده از PostgreSQL در اپلیکیشن Rails

برای استفاده از PostgreSQL در اپلیکیشن‌های Rails باید یک gem به نام pg را به Gemfile اضافه کنیم:

gem ‘pg’

اینک برای نصب gem به نام pg دستور bundle install را اجرا کنید.

پیکربندی پایگاه داده (config/database.yml)

default: &default
   adapter: postgresql
   pool: <%= ENV.fetch(“RAILS_MAX_THREADS”) { 5 } %>
   timeout: 5000

development:
   <<: *default
   database: development_instagram

test:
   <<: *default
   database: test_instagram

production:
   <<: *default
   database: production_instagram

ایجاد پایگاه داده

برای ایجاد پایگاه داده برای اپلیکیشن می‌توانید از دستور مهاجرت (migration) استفاده کنید:

rails db:create

>> Created database ‘development_instagram’
>> Created database ‘test_instagram’

نصب بوت‌استرپ برای اپلیکیشن‌های Rails

«بوت‌استرپ» (Bootstrap) یک کیت ابزار اوپن‌سورس برای توسعه صفحه‌هایی با استفاده از HTML ،CSS و JS است. این فریمورک به ایجاد سریع نمونه‌های اولیه از ایده‌ها یا ساخت کل اپلیکیشن با استفاده از متغیرهای Saas و mixin ها، سیستم شبکه‌ای واکنش‌گرا، کامپوننت‌های پیش‌ساخته گسترده و افزونه‌های قدرتمند برای به‌کارگیری jQuery کمک می‌کند.

ما برای یکپارچه‌سازی بوت‌استرپ با اپلیکیشن‌های Rails از bootstrap-rubygem استفاده می‌کنیم. به این منظور bootstrap را به Gemfile خود اضافه کنید:

gem ‘bootstrap’, ‘~> 4.1.1’

دستور bundle install را برای نصب bootstrap gem و ری‌استارت کردن سرور اجرا کنید تا فایل‌ها از طریق pipline در دسترس قرار گیرند.

پیکربندی روی application.css یا همان فایل app/assets/stylesheets/application.css

  • تغییر نام application.css به application.scss
  • سپس همه گزاره‌های *= require و *= require_tree را از فایل Sass حذف کنید. در عوض از import@ برای ایمپورت کردن فایل‌های Sass استفاده می‌کنیم.
  • ایمپورت کردن سبک‌های Bootstrap در application.scss:
@import "bootstrap";

پیکربندی روی application.js

بخش جاوا اسکریپت بوت‌استرپ به jQuety وابسته است. از آنجا که ما از Rails 5.1+ استفاده می‌کنیم، gem به نام jquery-rails را به Gemfile خود اضافه کنید:

gem 'jquery-rails'

وابستگی‌های بوت‌استرپ را اضافه کرده و به صورت application.js بوت‌استرپ کنید:

//= require jquery3
//= require popper
//= require bootstrap

طرح‌بندی اپلیکیشن

در ادامه قصد داریم اپلیکیشن را در 3 بخش اصلی طرح‌بندی کنیم:

  • نوار ناوبری
  • محتوای اصلی
  • فوتر

این طرح‌بندی چیزی مانند تصویر زیر خواهد بود:

Rails از فایل طرح‌بندی پیش‌فرض در مسیر app/views/layouts/application.html.erb استفاده می‌کند.

کد HTML را به طرح‌بندی اضافه کنید (layouts/application.html.erb/):

<!DOCTYPE html>
<html>
  <head>
    <title>Instuigram</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <!--Navigation Bar-->
    <nav class="navbar navbar-light bg-light d-flex">
      <div class="container">
        <div class="logo">
          <a class="navbar-brand" href="#">
            <%= fa_stacked_icon "camera inverse", base: "circle"%>
            Instagram
          </a>
        </div>
        <div class="m-auto">
          <form class="form-inline search-form">
            <input class="form-control mr-sm-2" type="search" placeholder="@search" aria-label="Search">
          </form>
        </div>
        <div class="user-section">
          <a><i class="fa fa-compass"></i></a>
          <a><i class="fa fa-heart"></i></a>
          <a><i class="fa fa-user"></i></a>
        </div>
      </div>
    </nav>

    <!--Main content-->
    <main class="container" style="min-height: 500px;">
      <%= yield %>
    </main>

    <!--Footer-->
    <footer class="container footer">
      <nav class="navbar d-flex">
        <ul>
          <li>ABOUT US
          </li>
          <li>SUPPORT</li>
          <li>PRESS</li>
          <li>API</li>
          <li>JOBS</li>
          <li>PRIVACY</li>
          <li>TERMS</li>
          <li>DIRECTORY</li>
          <li>PROFILES</li>
          <li>HASHTAGS</li>
          <li>LANGUAGE</li>
        </ul>
        <p> © 2018 INSTAGRAM </p>
      </nav>
    </footer>
  </body>
</html>

استفاده از Awesome Icon

ما در اپلیکیشن خود از برخی آیکون‌های Font Awesome Icon استفاده می‌کنیم. برای استفاده آسان از این آیکون‌ها باید بتوانیم gem به نام font-awesome-rails استفاده کنیم.

کد زیر را به فایل زیر اضافه کنید:

gem 'font-awesome-rails'

و دستور bundle install را اجرا کنید. اینک font-awesome را به فایل application.scss اضافه کنید.

@import "font-awesome";

کد CSS را برای طرح‌بندی (application.scss) اضافه کنید.

.container{
  max-width: 1000px;
}
.navbar{
  border-bottom: 1px solid rgba(0,0,0,.0975);
}
.user-section{
  a{
    padding-left: 15px;
  }
  i.fa{
    font-size: 22px;
    color: #000000d4;
  }
}
.search-form{
  input{
    font-size: 13px;
    text-align: center;
  }
}
.footer{
  font-size: 12px;
  padding-top: 30px;
  ul{
    padding-left: 0;
    > li{
      display: inline;
      padding-right: 15px;
      color: #003569;
    }
  }
  p{
    color: #999;
  }
}

کاربران: Sign Up ،Sign In و Sign Out

در این بخش ما قصد داریم از gem به نام devise برای ایجاد کارکردهای sign up، sign in و sign out برای کاربران استفاده کنیم. Devise یک راه‌حل احراز هویت انعطاف‌پذیر برای Rails است. این راه‌حل از 10 ماژول زیر تشکیل یافته است:

  • Database Authenticatable: این ماژول یک رمز عبور را هش کرده و در پایگاه داده برای اعتبار سنجی و احراز هویت کاربر در زمان ورود به اپلیکیشن استفاده می‌کند. احراز هویت می‌تواند از طریق درخواست‌های POST یا احراز هویت مقدماتی HTTP صورت بگیرد.
  • Omniauthable: پشتیبانی از (OmniAuth (https://github.com/omniauth/omniauth را ارائه می‌کند.
  • Confirmable: ایمیل‌هایی با دستور تأیید ارسال کرده و بررسی می‌کند که آیا یک حساب کاربری قبلاً در طی ورود کاربر تأیید شده یا نه.
  • Recoverable: رمز عبور کاربر را ریست کرده و دستورالعمل تنظیم مجدد آن را به ایمیلش ارسال می‌کند.
  • Registerable: ثبت نام کاربر را از طریق فرایند registration مدیریت کرده و به کاربران امکان ویرایش و حذف حساب‌هایشان را نیز می‌دهد.
  • Rememberable: ایجاد و پاک‌سازی توکن برای به‌خاطرسپاری کاربر از روی کوکی‌های ذخیره شده را مدیریت می‌کند.
  • Trackable: تعداد ورودها، زمان‌ها و آدرس‌های IP را ردگیری می‌کند.
  • Timeoutable: نشست‌هایی که در دوره زمانی مشخصی فعال نبوده‌اند را منقضی می‌کند.
  • Validatable: اعتبارسنجی‌های ایمیل و رمز عبور را انجام می‌دهد. این گزینه اختیاری است و می‌توان آن را سفارشی‌سازی کرد، به طوری که می‌توان تابع‌های خاص برای اعتبار سنجی تعریف کرد.
  • Lockable: یک حساب را پس از تعداد مشخصی از تلاش‌های شکست‌خورده برای ورود قفل می‌کند. چنین حساب‌هایی را می‌توان پس از گذشت زمان مشخصی از حالت قفل خارج کرد.

نصب Devise Gem

برای نصب devise باید کد زیرا را به Gemfile اضافه کنید:

gem 'devise'

سپس دستور bundle install را وارد کرده و generator را اجرا کنید:

rails generate devise:install

ما 2 فایل زیر را به صورت خودکار تولید کرده‌ایم:

  • config/initializers/devise.rb
  • config/locales/devise.en.yml

به منظور راه اندازی گزینه‌های پیش‌فرض URL برای ارسال کننده ایمیل Devise در محیط توسعه، یعنی فایل config/environments/development.rb، کد زیر را وارد کنید:

config.action_mailer.default_url_options = { host: ‘localhost’, port: 3000 }

در محیط Production مقدار :host باید به میزان واقعی اپلیکیشن اشاره کند.

تولید مدل کاربر

به این منظور باید از دستور زیر استفاده شود:

rails generate devise User
   invoke active_record
      create db/migrate/20180722043305_devise_create_users.rb
      create app/models/user.rb
   invoke test_unit
      create test/models/user_test.rb
      create test/fixtures/users.yml
   insert app/models/user.rb
   route devise_for:users

پس از اجرای دستور فوق یک فایل برای ایجاد کاربر، یک فایل در app/models به نام user.rb و یک مسیر برای کاربر و فایل تست ایجاد می‌شوند.

فایل مدل یعنی app/models/user.rb را باز کنید تا ماژول‌های پیش‌فرض devise را که در مدل کاربر وجود دارند ببینید.

devise:database_authenticatable,:registerable,
         :recoverable,:rememberable,:trackable,:validatable

سپس دستور زیر را اجرا کنید:

rails db:migrate

اینک باید اپلیکیشن را پس از تغییر دادن گزینه‌های پیکربندی Devise ری‌استارت کنید. در غیر این صورت با خطاهای عجیبی مواجه خواهید شد، برای نمونه ممکن است کاربران نتوانند وارد اپلیکیشن شوند یا تابع‌های کمکی مسیر تعریف‌نشده باشند.

افزودن لینک‌های ورود و خروج

در نوار ناوبری سمت راست باید لینک‌های sign_in و sign_out را به صورت زیر اضافه کنید. دقت کنید که کد HTML زیر را در طرح‌بندی (application.html.erb):

<a><i class=”fa fa-user”></i></a>

با کد زیر عوض کنید:

<% if user_signed_in? %>
<a href="<%= destroy_user_session_path%>" data-method="delete">
<i class="fa fa-user"></i>
</a>
<% else%>
<a href="<%= new_user_session_path%>">
<i class="fa fa-sign-in"></i>
</a>
<% end%>

توضیح برخی عناصر به صورت زیر است:

  • ?user_signed_in: یک تابع کمکی devise است که کاربر وارد شده را تأیید می‌کند.
  • destroy_user_session_path: مسیر خروجی است، method::delete متد پیش‌فرض HTML مورد استفاده برای خارج شدن یک منبع (کاربر) است.
  • new_user_session_path: مسیر خروجی است.

زمانی که روی آیکون خروج کلیک کنید، با ویویی مانند زیر مواجه می‌شوید:

اما پیش از این که بتوانید دوباره وارد شوید باید یک حساب جدید با کلیک روی لینک Sign up در زیر دکمه Log in ایجاد کنید. ویوی این صفحه ثبت نام به صورت زیر است:

با پر کردن آدرس ایمیل و رمز عبور می‌توانید ثبت نام کنید و سپس به صفحه ورود اپلیکیشن بروید.

زمانی که ثبت نام موفق بود، آیکون ورود با آیکون کاربر در نوار ناوبری تعویض می‌شود و اگر روی آیکون کاربر کلیک کنید می‌توانید از اپلیکیشن خارج شوید.

ایجاد صفحه پروفایل کاربر

جدول کاربر ستون‌هایی به صورت username ،name ،email ،password ،website، bio ،phone و gender است. کاربر از قبل دارای ستون‌های ایمیل و رمز عبور است و اینک باید فیلدهای دیگر را با استفاده از migration اضافه کنیم:

rails g migration AddMoreFieldsToUsers username:string name:string website:string bio:text phone:integer gender:string

migration را اجرا کنید:

rails db:migrate

== 20180813140820 AddMoreFieldsToUsers: migrating =============================

— add_column(:users,:username,:string)

-> 0.0333s

— add_column(:users,:name,:string)

-> 0.0006s

== 20180813140820 AddMoreFieldsToUsers: migrated (0.0363s) ====================

ایجاد کنترلر کاربر

برای ایجاد صفحه پروفایل کاربر نخستین گام این است که یک کنترلر به نام UsersController را با استفاده از دستور زیر ایجاد کنیم:

rails generate controller Users

سپس فایل app/controllers/users_controller.rb را در ویرایشگر باز کنید تا کدی مانند زیر را ببینید:

class UsersController < ApplicationController
end

در مرحله بعد یک اکشن show به UsersController اضافه می‌کنیم:

class UsersController < ApplicationController
   def show
   end
end

سپس یک ویوی متناظر به صورت زیر ایجاد می‌کنیم:

app/views/users/show.html.erb

در نهایت اکشن show را به مسیرها (config/routes.rb) اضافه می‌کنیم:

Rails.application.routes.draw do

   ...

   resources:users, only: [:show]

end

بخش [resources:users, only: [:show به Rails اعلام می‌کند که درخواست‌هایی که به آدرس http://localhost:3000/users/:id دریافت می‌کند را به اکشن show کنترلر کاربر با id به صورت ID جاری کاربر هدایت کند. اگر ID کاربر جاری برابر با 1 باشد، مسیر به صفحه پروفایل کاربر به صورت زیر خواهد بود:

http://localhost:3000/users/1

به‌روزرسانی لینک پروفایل کاربر در نوار ناوبری

در سمت راست نوار ناوبری، لینک logout را با لینک پروفایل کاربر تعویض می‌کنیم. این بدان معنی است که وقتی روی آیکون کاربر کلیک می‌کنیم باید به صفحه پروفایل کاربری برود. پس از به‌روزرسانی کد HTML به صورت زیر درمی‌آید:

<!-- app/views/layouts/application.html.erb -->

<% if user_signed_in? %>
 <a href=”<%= user_path(current_user)%>”>
  <i class=”fa fa-user”></i>
 </a>
<% else %>
 <a href=”<%= new_user_session_path%>”>
   <i class=”fa fa-sign-in”></i>
 </a>
<% end %>

ایجاد UI برای صفحه پروفایل کاربر

برای طرح‌بندی صفحه پروفایل کاربر دو بخش را در نظر داریم: یکی اطلاعات پایه و دیگر پست‌ها.

  • اطلاعات پایه: شامل آواتار، نام، نام کاربری، پست‌ها، فالوورها، فالووینگ‌ها.
  • پست‌ها: شامل تصاویر کاربر.

بخش 1: اطلاعات پایه کاربر

 تصویر نمونه‌ای از بخش اطلاعات پایه
تصویر نمونه‌ای از بخش اطلاعات پایه

این بخش شامل دو ستون است که ستون سمت چپ آواتار کاربر و ستون سمت راست شامل اطلاعات کاربر است. برای طرح‌بندی این وضعیت از ردیف‌ها و ستون‌ها استفاده می‌کنیم و کد HTML مربوطه به صورت زیر است:

# app/views/users/show.html.erb

<div class="profile row">
  <div class="col-md-4 avatar">
    <!-- LEFT: AVATAR HERE -->
  </div>

  <div class="col-md-8 basic-info">
    <!-- RIGHT: USER INFORMATION HERE -->
  </div>
</div>

از آنجا که ما هیچ داده‌‌ای برای برخی فیلدهای اطلاعات کاربر مانند نام، تعداد پست، فالوور، فالووینگ و تصاویر نداریم، از این رو باید از برخی اطلاعات ساختگی برای ساخت UI استفاده کنیم تا بعداً بتوانیم آن‌ها را به‌روزرسانی کنیم. پس از افزودن کامپوننت‌های دیگر در این بخش، کد HTML به صورت زیر درمی‌آید:

<div class="user-profile">
  <div class="profile row">
    <div class="col-md-4 avatar">
      <div class="wrapper">
        <img src="https://instagram.fsgn5-1.fna.fbcdn.net/vp/3ef315d2478029b529d3c3f38daadeb3/5C12B330/t51.2885-19/s150x150/21980557_344791629305453_6312020290528346112_n.jpg">
      </div>
    </div>
    
    <div class="col-md-8 basic-info">
      <div class="user-wrapper">
        <h2 class="username">luanotes</h2>
        <a class="edit-profile" href="<%= edit_user_path(current_user) %>">
          <button>Edit Profile</button>
        </a>
      </div>
      <ul class="posts">
        <li><span>12</span> posts</li>
        <li><span>195</span> followers</li>
        <li><span>345</span> following</li>
      </ul>
      <h2 class="name">Luan Nguyen</h2>
      <a class="website">luanotes.com</a>
    </div>
  </div>
</div>

برای ایجاد CSS برای این ویو، یک فایل به نام assets/stylesheets/users.scss می‌سازیم که شامل سبک‌های مورد نیاز باری ین صفحه است و آن را در فایل application.scss ایمپورت می‌کنیم:

@import "users";

کدهای CSS زیر را به فایل users.scss اضافه کنید:

.profile .avatar{
  .wrapper{
    width: 140px;
    height: 140px;
    margin: 40px auto;
  }
  .wrapper img{
    width: 100%;
    border-radius: 50%;
  }
}

.profile .basic-info{
  padding: 40px 0;
  .user-wrapper{
    display: flex;
  }
  .username{
    font-size: 30px;
    line-height: 40px;
    font-weight: 200;
  }
  .edit-profile{
    margin-left: 20px;
    margin-top: 5px;
  }
  .edit-profile button{
    background-color: transparent;
    border-radius: 5px;
    border: 1px solid #dbdbdb;
    color: #262626;
    font-size: 14px;
    padding: 4px 10px;
    font-weight: 500;
  }
  ul.posts{
    padding-left: 0;
    > li{
      display: inline;
      padding-right: 40px;
      font-size: 15px;
      span{
        color: #262626;
        font-weight: 600;
      }
    }
  }
  .name{
    font-size: 15px;
    font-weight: 600;
  }
  .website{
    color: #003569;
    text-decoration: none;
    font-size: 15px;
    font-weight: 600;
  }
}

اینک ویوی ما به صورت زیر درمی‌آید:

 اطلاعات پایه کاربر
بخش 1: اطلاعات پایه کاربر

بخش 2: پست‌های کاربر

در این مرحله 4 برگه زیر به صورت POSTS ،IGTV ،SAVED، و TAGGED را مانند تصویر زیر اضافه می‌کنیم:

<div class="user-tabs">
  <a class="tab active" href="">
    <i class="fa fa-th"></i>
    POSTS
  </a>
  <a class="tab" href="">
    <i class="fa fa-tv"></i>
    IGTV
  </a>
  <a class="tab" href="">
    <i class="fa fa-bookmark"></i>
    SAVED
  </a>
  <a class="tab" href="">
    <i class="fa fa-tag"></i>
    TAGGED
  </a>
</div>

و کد سبک دهی آن‌ها نیز به صورت زیر است:

.user-tabs{
  border-top: 1px solid #efefef;
  display: flex;
  justify-content: center;

a.tab{
    height: 35px;
    margin-right: 50px;
    line-height: 45px;
    color: #999;
    font-size: 12px;
    font-weight: 500;
    text-align: center;
    i{
      padding-right: 1px;
    }
    &:hover{
      text-decoration: none;
    }
  }
  a.active{
    border-top: 1px solid #262626;
    color: #262626;
  }
}

بخش تصاویر شامل سه ستون خواهد بود که در هر ردیف سه تصویر به نمایش درمی‌آید:

کد HTML:

<div class="user-images">
  <div class="wrapper">
    <img src="your_image_url">
  </div>
  <div class="wrapper">
    <img src="your_image_url">
  </div>
  <div class="wrapper">
    <img src="your_image_url">
  </div>
</div>

ما از تکنیک Flexbox در CSS برای طرح‌بندی این تصاویر استفاده کردیم که کد آن به صورت زیر است:

.user-images{
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  margin: 0 20px;

.wrapper{
    width: 280px;
    height: 280px;
    margin: 15px;
    img{
      width: 100%;
      border-radius: 4%;
    }
  }
}

در نهایت صفحه پروفایل کاربر به صورت زیر درمی‌آید:

صفحه پروفایل کاربر

اینک ما توانسته‌ایم صفحه پروفایل کاربر را ایجاد کنیم و در گام بعدی صفحه ویرایش/به‌روزرسانی پروفایل کاربر را اضافه می‌کنیم.

ویرایش/به‌روزرسانی کاربر

مراحل ایجاد صفحه ویرایش کاربر را می‌توان در 4 گام زیر خلاصه کرد:

  • مرحله 1: افزودن صفحه ویرایش کاربر
  • مرحله 2: طرح‌بندی صفحه ویرایش کاربر
  • مرحله 3: افزودن لینک‌های ناوبری به سمت چپ صفحه
  • مرحله 4: افزودن فرم ویرایش پروفایل به سمت راست صفحه

UI صفحه ویرایش پروفایل به صورت زیر است:

صفحه ویرایش کاربر
صفحه ویرایش کاربر

مرحله 1: افزودن صفحه ویرایش کاربر

در گام نخست باید یک اکشن با نام edit در کنترلر UsersController اضافه کنیم:

class UsersController < ApplicationController
...

def edit

end
end

سپس یک ویوی متناظر به نام app/views/users/edit.html.erb اضافه می‌کنیم. پس از آن اکشن edit را به مسیرها اضافه می‌کنیم:

resources:users, only: [:show,:edit]

اینک می‌توانید مسیر صفحه ویرایش کاربر را با وارد کردن دستور زیر مشاهده کنید:

rake routes
...
...
   edit_user GET /users/:id/edit(.:format) users#edit

بخش [resources:users, only: [...,:edit به Rails اعلام می‌کند که درخواست‌های دریافتی به آدرس http://localhost:3000/users/:id/edi را به اکشن edit کنترلر کاربر هدایت کنید. لینک ویرایش پروفایل را به دکمه Edit Profile در صفحه پروفایل کاربر اضافه کنید:

<a class="edit-profile" href="<%= edit_user_path(current_user) %>">
  <button>Edit Profile</button>
</a>

current_user یک تابع کمکی devise است که کاربر جاری آن خارج شده است.

مرحله 2: طرح‌بندی صفحه ویرایش پروفایل

ما طرح‌بندی صفحه ویرایش کاربر را با 2 ستون انجام دادیم. ستون چپ action-ها و ستون راست جزییات متناظر هستند.

طرح‌بندی صفحه ویرایش کاربر
طرح‌بندی صفحه ویرایش کاربر

کد HTML این صفحه به صورت زیر است:

<div class="user-edit-page">
  <div class="actions">
    <!-- Left column -->
  </div>
  
 <div class="details">
    <!-- Right column -->
  </div>
</div>

کد CSS آن نیز با استفاده از تکنیک flex به صورت زیر خواهد بود:

.user-edit-page{
  display: flex;
  margin-top: 60px;
  min-height: 500px;
  
  .actions{
    width: 220px;
    border: 1px solid #dbdbdb;  
  }
  .details{
    flex: 1;
    border: 1px solid #dbdbdb;
    border-left:none;
  }
}

مرحله 3: افزودن ناوبری به سمت چپ صفحه

ما از کامپوننت‌های navs بوت‌استرپ 4 با چینش عمودی در این بخش استفاده می‌کنیم. کد HTML آن به صورت زیر است:

<div class="user-edit-page">
  <div class="actions">
    <div class="nav flex-column nav-pills" role="tablist" aria-orientation="vertical">
      <a class="nav-link active" data-toggle="pill" href="#v-pills-home" role="tab" aria-selected="true">Edit Profile</a>
      <a class="nav-link" data-toggle="pill">Change Password</a>
      <a class="nav-link" data-toggle="pill">Authorized Applications</a>
      <a class="nav-link" data-toggle="pill">Email and SMS</a>
      <a class="nav-link" data-toggle="pill">Manage Contacts</a>
      <a class="nav-link" data-toggle="pill">Privacy and Security</a>
    </div>
  </div>
  <div class="details tab-content">
    <div class="tab-pane fade show active" id="v-pills-home" role="tabpanel" aria-labelledby="v-pills-home-tab">
      <!-- Form edit profile -->
    </div>
  </div>
</div>

همچنین کد CSS برای بخش اکشن‌ها به صورت زیر است:

.actions{
  width: 220px;
  border: 1px solid #dbdbdb;

.nav-link{
    font-size: 14px;
    background-color: white;
    color: black;
    border-radius: 0;
    padding: 12px 0 12px 30px;
    &:hover{
      cursor: pointer;
    }
  }
  .nav-pills .nav-link.active{
    border-left: 2px solid;
    font-weight: 600;
  }
}

اینک UI این بخش به صورت زیر در آمده است:

سمت چپ صفحه ویرایش کاربر

مرحله 4: افزودن فرم ویرایش کاربر به سمت راست صفحه

یک خط کد برای یافتن کاربر جاری به اکشن edit در articles_controller.rb اضافه می‌کنیم:

def edit
   @user = User.find(params[:id])
End

در ستون سمت راست یک فرم برای به‌روزرسانی کاربر جاری اضافه می‌کنیم. ما از تابع کمکی form_with در Rails برای ایجاد فرم استفاده می‌کنیم. فرم ویرایش کاربر به صورت زیر است:

<%= form_with model: current_user, local: true, html: {class: "form-horizontal form-edit-user"} do |f| %>
  <div class="form-group row">
    <div class="col-sm-3 col-form-label">
      <img class="avatar" src="https://instagram.fsgn5-1.fna.fbcdn.net/vp/3ef315d2478029b529d3c3f38daadeb3/5C12B330/t51.2885-19/s150x150/21980557_344791629305453_6312020290528346112_n.jpg">
    </div>
    <div class="col-sm-9">
      <p class="username"><%=current_user.username %></p>
      <a class="change-photo" href="">Change Profile Photo</a>
    </div>
  </div>
  <div class="form-group row">
    <%= f.label :name, class: 'col-sm-3 col-form-label' %>
    <div class="col-sm-9">
      <%= f.text_field :name, class: 'form-control' %>
    </div>
  </div>
  <div class="form-group row">
    <%= f.label :username, class: 'col-sm-3 col-form-label' %>
    <div class="col-sm-9">
      <%= f.text_field :username, class: 'form-control' %>
    </div>
  </div>
  <div class="form-group row">
    <%= f.label :website, class: 'col-sm-3 col-form-label' %>
    <div class="col-sm-9">
      <%= f.text_field :website, class: 'form-control' %>
    </div>
  </div>
  <div class="form-group row">
    <%= f.label :bio, class: 'col-sm-3 col-form-label' %>
    <div class="col-sm-9">
      <%= f.text_area :bio, class: 'form-control' %>
    </div>
  </div>
  <div class="form-group row">
    <%= f.label :email, class: 'col-sm-3 col-form-label' %>
    <div class="col-sm-9">
      <%= f.text_field :email, class: 'form-control' %>
    </div>
  </div>
  <div class="form-group row">
    <%= f.label :phone, class: 'col-sm-3 col-form-label' %>
    <div class="col-sm-9">
      <%= f.text_field :phone, class: 'form-control' %>
    </div>
  </div>
  <div class="form-group row">
    <%= f.label :gender, class: 'col-sm-3 col-form-label' %>
    <div class="col-sm-9">
      <%= f.text_field :gender, class: 'form-control' %>
    </div>
  </div>
  <div class="form-group row">
    <div class="col-sm-3">
    </div>
    <div class="col-sm-9">
      <%= f.submit "Submit", class: 'btn btn-primary' %>
    </div>
  </div>
<% end %>

کد CSS نیز به صورت زیر خواهد بود:

.form-edit-user{
  padding: 30px 100px 10px 50px;

.form-group, input{
    font-size: 14px;
    color: black;
  }
  input, textarea{
    border: 1px solid #efefef;
  }
  .col-form-label{
    text-align: right;
    font-weight: 600;
  }

.avatar{
    height: 38px;
    width: 38px;
    border-radius: 50%;
  }
  .username{
    font-size: 20px;
    line-height: 22px;
    margin-bottom: 0;
  }
  .change-photo{
    color: #3897f0;
    text-decoration: none;
    font-size: 13px;
  }
  input[type='submit']{
    color: white;
  }
}

اینک UI فرم به صورت زیر آمده است:

زمانی که اطلاعات را در فرم وارد کرده و دکمه submit را برای پردازش به‌روزرسانی کاربر بزنید، با خطای عدم مطابقت هیچ مسیر مانند تصویر زیر مواجه می‌شوید:

این خطا به این جهت رخ می‌دهد که تا به این جا هیچ مسیری برای کاربر تعریف نکرده‌ایم. اینک باید یک مسیر برای update کاربر به routes.rb اضافه کنیم:

resources:users, only: [:show,:edit,:update]

اینک اگر مجدداً فرم را پر کرده و دکمه submit را بزنیم، با یک خطای دیگر به صورت زیر مواجه می‌شویم:

اکشن update نمی‌تواند پیدا شود، بنابراین باید یک اکشن update در UserController به صورت زیر ایجاد کنیم:

def update
   current_user.update(params[:user])
   redirect_to current_user
end

ما در اکشن update سعی می‌کنیم کاربر جاری را بر اساس پارامترهایی که در فرم ویرایش پر شده‌اند، به‌روزرسانی کنیم. منظور از current_user همان کاربری است که در حال حاضر به اپلیکیشن وارد شده است. اگر بار دیگر تلاش کنیم دکمه submit را بزنیم، با خطایی مانند زیر مواجه می‌شویم:

خطای خصوصیات ممنوعه

Rails از برخی خصوصیات پشتیبانی می‌کند که به نوشتن اپلیکیشن‌های امن کمک می‌کند. این خصوصیت strong parameters نامیده می‌شوند که ما را ملزم می‌کنند تا تعریف کنیم کدام پارامترها در اکشن‌های کنترلرها مجاز هستند. ما باید پارامترهای خود را در لیست سفید قرار دهیم تا از انتساب‌های جمعی اشتباه جلوگیری کنیم.

بنابراین فعلاً برای اصلاح ForbiddenAttributesError باید از پارامترهای strong پیش از به‌روزرسانی کاربر بهره بگیریم. برای بهره‌گیری از پارامترهای strong در این مثال باید از متدهای require و permit به صورت زیر استفاده کنیم:

params.require(:user).permit(:username,:name,:website,:bio,:email,:phone,:gender)

این بدان معنی است که پارامترهای username, name, website, bio, email, phone و gender به عنوان پارامترهای معتبر، مجاز هستند. اکنون اکشن update به صورت زیر در آمده است:

def update
   current_user.update(user_params)
   redirect_to current_user
end

private

def user_params
   params.require(:user).permit(:username,:name,:website,
   :bio,:email,:phone,:gender)
End

اینک باید به فرم ویرایش بازگردیم و دوباره فرم را ارائه کنیم. این بار فرم کار می‌کند و به‌روزرسانی کاربر موفق خواهد بود و سپس به صفحه پروفایل کاربر هدایت می‌شویم.

برای این که بتوانیم تغییر اطلاعات را پس از به‌روزرسانی اطلاعات کاربر به سهولت ببینیم، باید به صفحه پروفایل کاربر (users/show.html.erb) بازگردیم تا برخی داده‌های جعلی را با وارد کردن داده‌های واقعی کاربر عوض کنیم. کد HTML به صورت زیر است:

<div class="col-md-8 basic-info">
  <div class="user-wrapper">
    <h2 class="username"><%= current_user.username %></h2>
    ...
  </div>

  ...

  <h2 class="name"><%= current_user.name %></h2>
  <%=link_to current_user.website, current_user.website, class:  'website' %>
</div>

سخن پایانی

در این مقاله به توضیح گام به گام روش ساخت یک اپلیکیشن Rails به کمک PostgreSQL و Bootstrap پرداختیم. همچنین درکی از مفهوم MVC در Rails کسب کردیم. با استفاده از devise gem اقدام به ساخت تابع‌های احراز هویت کردیم و در نهایت تابع‌هایی برای مشاهده و به‌روزرسانی پروفایل کاربر نوشتیم. در مقاله بعدی جزییاتی در مورد (Active Record (CRUD، اعتبارسنجی، ارتباط، خصوصیت Active Storage و استفاده از Kaminari gem برای صفحه‌بندی ارائه می‌کنیم.

در صورت تمایل برای مطالعه بخش دوم این مطلب، روی لینک زیر کلیک کنید:

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۳ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
luanotes
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *