Статьи

Создайте интернет-магазин с Rails

Онлайн покупки — это форма электронной коммерции, которая позволяет клиентам напрямую покупать товары или услуги, такие как электронные книги, программное обеспечение и потоковое мультимедиа через Интернет, используя веб-браузер. Этот вид покупок является частью нашей повседневной жизни и используется многими известными сайтами, такими как Amazon, E-bay или различными потоковыми медиа / образовательными сайтами.

Из этой серии будет построен интернет-магазин с нуля. Мы узнаем, как использовать Rails с некоторыми замечательными инструментами, такими как:

  • Фонд для разработки адаптивных страниц
  • Redis для быстрого хранения товаров в корзине
  • Braintree принимать платежи и предоставлять подписки премиум-плана.

Давайте начнем!

Основные характеристики

  • Адаптивные страницы: приложение будет построено с использованием адаптивной структуры, чтобы обеспечить оптимальный просмотр для различных устройств.
  • Корзина: Позвольте пользователям накапливать список товаров для покупки во время навигации по сайту, затем подсчитать общую сумму заказа перед оформлением заказа.
  • Простой сценарий оплаты: принимайте платежи с кредитных карт пользователя напрямую, не сохраняя их на свой торговый счет.
  • Сохраните данные кредитной карты: пользователи могут связать свои кредитные карты со своими счетами, чтобы им не нужно было добавлять данные карты каждый раз, когда они хотят что-то купить.
  • Управление кредитными картами. Расширьте возможности процесса оплаты, предоставив возможность добавлять и удалять кредитные карты и выбирать между ними при оформлении покупки.
  • Планы подписки. Предоставьте пользователям несколько планов подписки, позволяя им обновлять, понижать версию и отменять указанные подписки.

Необходимые навыки

  • Хороший опыт работы с Ruby on Rails.
  • Средний уровень HTML / CSS3.
  • Базовые знания JS / Coffescript & AJAX
  • Понимание концепции адаптивного CSS.
  • Вводные знания о Redis, магазине ключей-значений.
  • Обзор работы онлайн платежных систем.

инструменты

  • Ruby 2.1.0 — язык программирования
  • Рельсы — Внутренний каркас
  • Основа 5 — Фронт-энд CSS рамки
  • Devise — аутентификация пользователя
  • Redis — Магазин ключей
  • Брейнтри — Платежная система

Примечание . Исходный код доступен в этом репозитории Github .


Шаг 1: инициализировать проект

Создать проект

Давайте создадим новое Rails-приложение и скаффолд. Мы используем MySQL в качестве нашей базы данных:

$ rails new moviestore -d mysql $ cd moviestore 

подмости

Создайте каркас movie с некоторыми основными атрибутами модели.

 $ rails g scaffold movie title release_year:integer price:float description:text imdb_id poster_url --skip-stylesheets 

Здесь мы использовали --skip-stylesheets со скаффолдом, чтобы избежать генерации каких-либо таблиц стилей, учитывая, что мы будем использовать Foundation.

Теперь мы можем создавать и переносить нашу базу данных.

 $ rake db:create $ rake db:migrate 

Файл семян

Нам нужны некоторые данные для работы, поэтому я создал CSV-файл с именем movies.csv который содержит некоторые записи фильмов. Я положил его в каталог db/seeds_data .

Мы пройдемся по записям в файле csv, чтобы загрузить его строки в нашу базу данных.

 require 'csv' CSV.foreach(Rails.root.join("db/seeds_data/movies.csv"), headers: true) do |row| Movie.find_or_create_by(title: row[0], release_year: row[1], price: row[2], description: row[3], imdb_id: row[4], poster_url: row[5]) end 

После этого вы можете запустить файл rake с помощью rake :

 $ rake db:seed 

Конфигурация маршрутов

Корневой маршрут

Пока что, сделайте корневой маршрут, чтобы перейти к действию index контроллера фильмов:

 root 'movies#index' 

Ресурсы ОТДЫХА

Нам не нужны все конечные точки, предоставляемые resource , такие как create , update и т. Д. Ограничьте доступные действия index и show :

 resources :movies, only: [:show, :index] 

Также удалите все ненужные файлы в представлениях и ненужный код в MoviesController .

Завершить настройку

Наконец, запустите rails s и откройте localhost: 3000 в браузере, и вы увидите наш список фильмов.

Отлично сработано! мы только что завершили первоначальную настройку проекта, но стиль отсутствует. Давайте добавим немного хорошего стиля, используя Zurb Foundation.


Шаг 2: Адаптивный дизайн с Фондом

Foundation — это интерфейсный фреймворк, похожий на Twitter Bootstrap, он построен на SASS вместо LESS, что облегчает его интеграцию в приложение Rails.

Установка

Чтобы установить Foundation, добавьте эту строку в Gemfile приложения:

 gem 'foundation-rails' 

а потом:

 $ bundle install 

Конфигурирование Фонда

Выполните следующую команду, чтобы добавить основание:

 $ rails g foundation:install 

Это создаст несколько файлов, необходимых для Foundation, включая файл макета, который спросит вас, хотите ли вы перезаписать существующий файл макета. Ты сделаешь.

Подсказка Вам необходимо перезапустить сервер после добавления основного камня.

Переопределить Фонд

Если вы сейчас посмотрите на файл application.css , то заметите, что для него требуется файл foundation_and_overrides , созданный генератором фундаментов. Это позволяет нам настраивать Фонд, устанавливая переменные. Он содержит множество комментариев, показывающих нам, какие переменные мы можем настроить.

Foundation Icon Fonts

Естественно, нам понадобится набор иконок для добавления значимых символов в наш дизайн. К счастью, Zurb Foundation предоставляет шрифты значков, которые вы можете скачать здесь . Добавьте его в свое приложение, скопировав папку fonts в папку app / assets и переместив файл foundation-icons.css в папку app / assets / stylesheets .

Значки фундамента

Настроить макет

На данный момент макет выглядит нормально, но мы можем многое сделать для его улучшения. Мы добавим некоторую структуру на страницу и добавим заголовок, логотип, нижний колонтитул и основной контент.
Чтобы сделать это быстро, возьмите один из HTML-кода шаблона Foundation и поместите его между теги нашей страницы макета. Это очень просто в использовании.

Шаблон приложения

После некоторых настроек одного из шаблонов Foundation макет теперь состоит из 4 основных частей (верхняя панель, оповещения, основное содержимое и нижний колонтитул):

 <body> <%= render "layouts/header" %> <%= render "layouts/alerts" %> <div id="main-content" class="row full-min-height"> <%= yield %> </div> <footer class="row"> <div class="large-12 columns"> <hr> <p>© MovieStore 2014</p> </div> </footer> <%= javascript_include_tag "application" %> </body> 

У нас есть две части, чтобы помочь организовать макет. Первый - _header , который содержит меню верхней панели:

 <div class="row"> <nav class="top-bar column" data-topbar> <ul class="title-area"> <li class="name"> <h1> <a href="/"> <i class="fi-play-video"></i> MovieStore </a> </h1> </li> <li class="toggle-topbar"><a href="#"><span>Menu</span></a></li> </ul> <section class="top-bar-section"> <ul class="right"> <li><%=link_to "Register", "#" %></li> <li><%=link_to "Login", "#" %></li> </ul> </section> </nav> </div> 

Второй - _alerts , который отображает любые флеш-уведомления или уведомления:

 <%if notice.present? || alert.present? %> <div class="row"> <%is_alert = alert.present? ? "alert" : ""%> <div data-alert class="alert-box <%=is_alert%>"> <%=notice || alert%> <a href="#" class="close">&times;</a> </div> </div> <%end%> 

Настроить CSS

CSS Helpers Classes

Создайте новый файл с именем helpers.scss в каталоге app / stylesheets . В этом файле мы определим некоторые вспомогательные классы для хранения наиболее используемого CSS в нашем приложении. Цель этих классов - сделать ваш код многократно используемым, более быстрым и эффективным. Это один из вариантов OOCSS , который я считаю хорошим CSS. Если вы раньше не читали об OOCSS (объектно-ориентированном CSS), я призываю вас сделать это!

Вот таблица стилей помощников .

Базовые переменные

Как объяснялось ранее, мы настроим некоторые переменные Foundation для изменения цветов и стилей по умолчанию, таких как кнопки, метки, входные данные и т. Д. Лучший способ узнать, какие переменные вам нужно изменить, можно найти в документах Foundation. В конце каждого раздела показаны соответствующие переменные и способы их настройки. Найдите эти переменные внутри foundation_overrides.scss и измените их на нужные значения. Вы можете найти мои собственные переменные здесь .

Пользовательский стиль макета

Мы хотим добавить больше настроек в макет. Для этого создайте файл layout.scss и добавьте следующее:

 // Custom layout styles // Default font family @font-face { font-family: 'gotham-rounded'; src: url(gotham-rounded-medium.otf); } // Body background body { background-image: url(body_bg.png); background-position: center; color: #fff; } // Logo .title-area{ a { padding:0 !important } i { font-size: 30px; position: relative; top: 2px; } } // Topbar items .top-bar-section ul li { background: transparent } // Forms .form-container { padding: 3% 3%; .switch > div { position: relative; top: -5px; } } 

Страницы стиля

Главная страница

Использование вида сетки будет самым простым решением для отображения фильмов. Мы можем сделать это, используя основную сеточную систему, которая дает вам возможность равномерно разделить содержимое списка в сетке. Давайте настроим страницу index и добавим классы сетки:

 <h4 class="column">Featured Movies</h4> <div class="column"> <ul class="movies-grid no-bullet row"> <% @movies.each do |movie| %> <li class="large-3 medium-4 small-12 column"> <div class="movie-card padly"> <%= link_to movie, class: "poster" do %> <%= image_tag movie.poster %> <% end %> <div class="movie-info ell glassy-bg padmy padlx"> <div class="title"> <h6><%= movie.title %> <span>(<%= movie.release_year %>)</span></h6> </div> <p class="left price label movie-label radius">$ <%= movie.price %></p> <%= link_to movie.imdb, class: "right" do %> <%= image_tag asset_path("imdb_logo.png") %> <% end %> </div> </div> </li> <% end %> </ul> </div> 

Основа Grid System основана на двенадцати столбцах, и мы определяем их с помощью CSS-классов, поэтому вы можете быстро и легко создавать мощные макеты для нескольких устройств с помощью стандартной 12-колоночной несущей сетки.

Мы также добавили некоторые из наших собственных классов CSS, чтобы улучшить дизайн и исправить его ошибки:

 .movies-grid { .movie-card { width:220px; margin:auto; .poster img { height: 325px; width:100%; } .movie-info { .title { height: 4em; } } } } 

Обратите внимание, мы называем imdb и poster на @movie которые не являются атрибутами в модели Movie . Это потому, что я создал два метода экземпляра, которые возвращают соответствующие URL на основе других атрибутов.

 class Movie < ActiveRecord::Base def poster "http://ia.media-imdb.com/images/M/#{poster_url}" end def imdb "http://www.imdb.com/title/#{imdb_id}/" end end 

Скриншот финального магазина фильмов

Показать страницу

Последняя задача, связанная со стилем, - это редизайн страницы show на основе нашего нового макета и классов:

 <div class="large-3 small-12 column"> <%=image_tag @movie.poster%> </div> <div class="large-9 small-12 column"> <h3> <%= @movie.title %> (<%= @movie.release_year %>) <%=link_to @movie.imdb do%> <%=image_tag asset_path("imdb_logo.png")%> <%end%> </h3> <p class="label movie-label radius mb1">$ <%= @movie.price %></p> <p><%= @movie.description %></p> </div> 

Отлично! Теперь вы понимаете, как работать с Foundation, как его переопределить и как добавлять свои собственные таблицы стилей.


Шаг 3: Разработка пользовательской модели (аутентификация)

Чтобы обрабатывать аутентификацию в нашем приложении, мы должны создать модель User с простыми формами регистрации и авторизации. Разработать это один из самых простых способов сделать это.

Установить Devise

Сначала, как мы использовали, добавьте devise gem в ваш Gemfile и выполните команду bundle чтобы установить его:

 gem 'devise' 

после установки Devise запустите генератор, чтобы создать инициализатор devise.rb, который описывает параметры конфигурации Devise:

 $ rails generate devise:install 

Добавить модель пользователя

Создайте модель User и свяжите ее с Devise, используя ее генератор:

 $ rails generate devise User 

Это создаст модель User и настроит ее с модулями Devise по умолчанию. Генератор также настраивает ваш файл config / rout.rb так, чтобы он указывал на контроллер Devise.

 $ rake db:migrate 

Настройка маршрутов

Devise предоставляет метод devise_for в нашем файле devise_for , что позволяет нам настраивать имена путей для разработки маршрутов следующим образом:

 devise_for :users, path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'register' } 

Настройка представлений

Devise - это законченное MVC-решение на основе движков Rails, основанное на модульной концепции: «используйте то, что вам действительно нужно».

Как вы могли заметить, для Devise нет сгенерированных представлений. Это потому, что взгляды Devise упакованы в жемчужине. Нам нужно только сгенерировать несколько наборов представлений (например, формы register и login ) и настроить их. Запустите следующий генератор и передайте ему список этих модулей с флагом -v и он скопирует выбранные представления в ваше приложение.

 $ rails generate devise:views -v registrations sessions 

Хорошо! Теперь мы можем изменить формы регистрации и входа в систему с помощью нашего индивидуального макета.

Форма регистрации

просмотры / регистрация / new.html.erb

 <div class="form-container radius-box glassy-bg small-10 small-centered medium-8 large-6 columns"> <h2>Register</h2> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= devise_error_messages! %> <div class="mb1"><%= f.email_field :email, autofocus: true, placeholder: "Email", class: "radius" %></div> <div class="mb1"><%= f.password_field :password, autocomplete: "off", placeholder: "Password", class: "radius" %></div> <div class="mb1"><%= f.password_field :password_confirmation, autocomplete: "off", placeholder: "Confirm password", class: "radius" %></div> <div><%= f.submit "Let's Go", class: "button" %></div> <% end %> <%= render "devise/shared/links" %> </div> 

Форма входа

просмотры / сессия / new.html.erb

 <div class="form-container radius-box glassy-bg small-10 small-centered medium-8 large-6 columns"> <h2>Login</h2> <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> <div class="mb1"><%= f.email_field :email, autofocus: true, placeholder: "Email", class: "radius" %></div> <div class="mb1"><%= f.password_field :password, autocomplete: "off", placeholder: "Password", class: "radius" %></div> <% if devise_mapping.rememberable? -%> <div class="switch round small mb1"> <div class="inline-block left prm"><%= f.check_box :remember_me %> <%= f.label :remember_me %></div> <span>Remember me</span> </div> <% end -%> <div class="clear"><%= f.submit "Let me in", class: "button" %></div> <% end %> <%= render "devise/shared/links" %> </div> 

Скриншот входа в систему

Раздел пользователя Topbar

Поскольку теперь у нас есть вошедшие и вышедшие пользователи, отредактируйте раздел topbar внутри файла layout / _header.html.erb, чтобы показать правильные ссылки в зависимости от Devise signed_in? помощник.

 <ul class="right"> <%if signed_in?%> <li><%=link_to current_user.email, edit_user_registration_path%></li> <li><%=link_to "Logout", destroy_user_session_path, method: :delete%></li> <%else%> <li><%=link_to "Register", new_user_registration_path%></li> <li><%=link_to "Login", new_user_session_path%></li> <%end%> 

Шаг 4: Корзина

На этом этапе мы хотим добавить корзину покупок, чтобы пользователи могли добавлять фильмы, которые они хотят купить, в свои тележки. У них также будет возможность посетить страницу корзины и просмотреть фильмы, которые они добавили в корзину, удалив при желании любой из них.

Для реализации этого сценария есть несколько полезных камней, таких как carter & act как shopping_cart , но я предпочитаю создавать его с нуля, используя Redis Redis очень быстрый и имеет некоторые функции, которые идеально соответствуют нашим потребностям.

Redis - это расширенное хранилище ключей в памяти с открытым исходным кодом. Его часто называют сервером структуры данных, поскольку ключи могут содержать строки, хэши, списки, наборы и отсортированные наборы.

Настройка Redis

Установка

Если вы используете OS X, самый простой способ установить Redis - это использовать Homebrew ( brew install redis ).

Затем добавьте следующие гемы в наш Gemfile и запустите bundle

 gem 'redis', '~> 3.0.1' gem 'hiredis', '~> 0.4.5' 

По умолчанию redis использует библиотеку сокетов Ruby для общения с Redis. Поскольку у нас будет несколько больших ответов (например, SMEMBERS списка SMEMBERS которая будет использоваться в нашем приложении), мы используем альтернативный драйвер соединения, называемый hiredis который оптимизирует скорость за счет переносимости.

Наконец, мы можем запустить его, выполнив следующую команду:

 $ redis-server 

Скриншот сервера Redis

Инициализировать глобальный объект

Создайте файл инициализатора с именем /config/initializers/redis.rb, где мы настроим наше соединение Redis. Будет создана глобальная переменная, чтобы обеспечить легкий доступ к остальной части нашего приложения. Также укажите hiredis в качестве драйвера при создании экземпляра объекта client.

 $redis = Redis.new(:driver => :hiredis) 

Ницца! Теперь мы успешно настроили Redis, и он готов к использованию в нашем приложении.

Тележка Контроллер

Сначала создайте контроллер тележек, передавая show как параметр, потому что это единственное действие, которое мы будем использовать.

 $ rails g controller carts show 

Маршруты

Поскольку мы имеем дело с одной корзиной на пользователя, cart может быть единственным ресурсом . Добавьте only параметр с действием show и определите два не RESTful-маршрута для действий add и remove .

 resource :cart, only: [:show] do put 'add/:movie_id', to: 'carts#add', as: :add_to put 'remove/:movie_id', to: 'carts#remove', as: :remove_from end 

Действия контроллера

Внутри CartsController мы будем использовать команды SMEMBERS , SADD и SREM Redis для перечисления, добавления и удаления идентификаторов фильмов в уникальный набор с именем current_user_cart , идентифицируемый с помощью current_user.id . Для получения более подробной информации о командах Redis я рекомендую вам ознакомиться с их документацией , она очень понятна и полезна.

 class CartsController < ApplicationController before_action :authenticate_user! def show cart_ids = $redis.smembers current_user_cart @cart_movies = Movie.find(cart_ids) end def add $redis.sadd current_user_cart, params[:movie_id] render json: current_user.cart_count, status: 200 end def remove $redis.srem current_user_cart, params[:movie_id] render json: current_user.cart_count, status: 200 end private def current_user_cart "cart#{current_user.id}" end end 

Мы добавили :authenticate_user! в качестве обратного вызова before_action для ограничения доступа к действиям контроллера только зарегистрированным пользователям. Кроме того, в действиях добавления и удаления мы отвечаем current_user.cart_count который является методом экземпляра в модели User который возвращает количество фильмов в корзине текущих пользователей с помощью команды SCARD :

 def cart_count $redis.scard "cart#{id}" end 

Последнее, что нужно сделать, это добавить ссылку « My Cart в меню верхней панели, чтобы пользователи могли получить к ней прямой доступ в любое время. Также,
давайте представим количество фильмов в их тележках:

 <li> <%= link_to cart_path do%> <i class="fi-shopping-cart"></i> My Cart (<span class="cart-count"><%=current_user.cart_count%></span>) <%end%> </li> 

Моя корзина (3) скриншот

Кнопка «Добавить в корзину»

Создайте кнопку «Добавить в корзину», которая обрабатывает действия add и remove . Мы добавим его на страницу show фильмов:

 <%if signed_in?%> <%=link_to "", class: "button", data: {target: @cart_action, addUrl: add_to_cart_path(@movie), removeUrl: remove_from_cart_path(@movie)} do%> <i class="fi-shopping-cart"></i> <span><%=@cart_action%></span> Cart <%end%> <%end%> 

Мы определяем @cart_action экземпляра @cart_action в фильме movies#show action как:

 def show @movie = Movie.find(params[:id]) @cart_action = @movie.cart_action current_user.try :id end 

Он вызывает метод @movie , который проверяет, является ли этот фильм участником корзины текущего пользователя. Он возвращает соответствующий текст, который будет отображаться как метка кнопки, а также использоваться в нашем coffescript, как мы увидим позже.

 def cart_action(current_user_id) if $redis.sismember "cart#{current_user_id}", id "Remove from" else "Add to" end end 

Здесь мы использовали команду SISMEMBER Redis, которая определяет, является ли данное значение членом набора.

Снимок экрана карты

Добавить / удалить запросы

Нам нужно подключить кнопку «Добавить в корзину», используя некоторые скрипты jQuery и запросы AJAX.

 $(window).load -> $('a[data-target]').click (e) -> e.preventDefault() $this = $(this) if $this.data('target') == 'Add to' url = $this.data('addurl') new_target = "Remove from" else url = $this.data('removeurl') new_target = "Add to" $.ajax url: url, type: 'put', success: (data) -> $('.cart-count').html(data) $this.find('span').html(new_target) $this.data('target', new_target) 

Мы проверяем состояние текущего фильма с помощью target атрибута данных, который имеет значение @cart_action как мы упоминали ранее. Следовательно, мы можем указать url ajax, который является либо add_to_cart либо remove_from_cart , так как мы передаем их в наш скрипт, используя addurl данных addurl & removeurl .
После успешного выполнения ajax-запроса установите количество фильмов в корзине, которое находится в верхнем меню, с новым номером, измените метку кнопки и установите target атрибут данных с новым значением.

Примечание : я отключил Turbolinks.js чтобы мы могли сделать наш скрипт максимально простым. Вы можете сделать это, просто удалив require turbolinks из файла application.js .

Страница моей корзины

Теперь, последнее, что нужно сделать, это представить элементы carts/show странице carts/show . Давайте добавим несколько тегов HTML:

 <div id="mycart" class="small-10 small-centered medium-8 large-8 column"> <div class="p1 glassy-bg mb1 text-center radius-l1 radius-r1"> <h4>My Cart</h4> <p class="mb0"> You've selected <span class="cart-count"><%=current_user.cart_count%></span> movies!</p> </div> <% @cart_movies.each do |movie|%> <div data-equalizer class="cart-movie large-12 column mb1"> <div class="column large-2 text-center p0" data-equalizer-watch> <%=link_to movie do%> <%=image_tag movie.poster, class: "radius-l1"%> <%end%> </div> <div class="column large-7 glassy-bg text-center" data-equalizer-watch> <p class="scale ptm"> <%= movie.title %> </p> </div> <div class="column large-3 primary-bg text-center radius-r1" data-equalizer-watch> <%=link_to "" , data: {targetUrl: remove_from_cart_path(movie)} do%> <i class="fi-x right"></i> <%end%> <h4 class="scale">$ <%= movie.price %></h4> </div> </div> <%end%> </div> 

Эквалайзер , предоставляемый Фондом, является отличным способом для создания контента равной высоты на вашей странице. Вы можете найти более подробную информацию о том, как это работает здесь .

Добавьте немного особого стиля в файл carts.scss :

 #mycart { .scale { position: relative; top: 35%; transform: translateY(-35%); } .fi-x { position: relative; top: 10px; right: -5px; } } 

Наконец, напишите аналогичный скрипт для активации функции remove значка.

 $(window).load -> $('#mycart .fi-x').click (e) -> e.preventDefault() $this = $(this).closest('a') url = $this.data('targeturl') $.ajax url: url, type: 'put', success: (data) -> $('.cart-count').html(data) $this.closest('.cart-movie').slideUp() 

Вау, кажется, что сейчас все работает гладко. Прекрасная работа!

Вы выбрали 3 фильма скриншот

Еще больше делать

Еще предстоит еще много работы, прежде чем мы сможем сказать, что наш интернет-магазин завершен. В моей следующей статье я расскажу об интеграции Braintree с нашим приложением для приема платежей. Спасибо за прочтение!