20190411のRailsに関する記事は14件です。

[Rails][MySQL] 絵文字を保存できるようにする

「?」みたいな絵文字をMySQLに保存したい。

方法

my.cnf に以下を追記

「/etc/my.cnf」「/etc/mysql/my.cnf」あたりにあるはず。

※最後の3行は、MariaDB >= 10.3.1 ならデフォルトで設定されているので追記しなくてよい。
 (設定自体が出来なくなってもいる。 参考:https://github.com/frappe/bench/issues/670))

my.cnf
[mysql]
default-character-set=utf8mb4

[mysqld]
character-set-server = utf8mb4
skip-character-set-client-handshake
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
init-connect = SET NAMES utf8mb4
innodb_file_format = Barracuda         # MariaDB >= 10.3.1 なら削除
innodb_file_per_table = 1              # MariaDB >= 10.3.1 なら削除
innodb_large_prefix                    # MariaDB >= 10.3.1 なら削除

database.ymlを変更

database.yml
encoding: utf8 # 変更前

encoding: utf8mb4 # 変更後

databaseを作成

bash
bundle exec rake db:create
bundle exec rake db:migrate

参考

https://qiita.com/okamu_/items/5eb81688849fbe351350
https://y-hilite.com/3192/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker-composeでrails開発時のA server is already running. Check xxxx/tmp/pids/server.pid. 対応

docker-compose.yml
version: '3.7'

services:
  backend:
    tty: true
    stdin_open: true
    build:
      context: ./backend
    working_dir: /usr/src/app
    environment:
      BUNDLE_JOBS: 4
      DB_HOST: db
      AUTODOC: $AUTODOC
    command: bin/rails s
    volumes:
      - ./backend:/usr/src/app
      - bundle:/usr/local/bundle
      - /usr/src/app/.bundle
    tmpfs:
      - /usr/src/app/tmp/pids

tmpfsに乗っけることで、停止時に消えてくれるはず。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

21日目:H30秋基本情報技術者試験の問3データベース

前回のアウトプットから、だいぶ間が空いたが、今回から暫く、基本情報技術者試験問題に向き合う。

コンサートチケットの予約販売サイト、特にチケット販売サブシステムを用いる問題だった。
特に、今回のスクールの大成すべく、問3は実際にDBのみならず、Appも作成したい。
出典: 平成30年度 秋期 基本情報技術者試験(FE)試験区分 午後 問1 問3

DB仕様、ER図

Cacooを用いてみた。カラム名が冗長な気がする。
Entity Relationship Diagram.png

販売サブシステムの(引用、一部編集)

  1. コンサートの席種はS,A,Bがあり、コンサート毎にその価格と発売席数は異なる。
  2. 取り扱うコンサートの席種(S,A,B)毎の販売可能な席数を管理する。
  3. 会員が購入申込すると、一意な販売IDを生成して販売表にレコード追加
  4. 会員が支払いをすると、決済処理として決済表に追加。
  5. 決済期限の翌日に、決済期限を過ぎた販売表中のレコードと販売IDが同じレコードが決済表にない場合、購入申込は取り消され、バッチ処理として、決済表に当該販売IDを主キーとして決済表に決済日をnull、決済額を-1としてレコード追加
  6. バッチ処理は毎夜0~4時の販売サイトのシステムメンテナンス時に行う
  7. 購入申込席数が販売可能な席数を上回る場合は、販売終了と表示し、購入申込を受け付けない 等々。。。

※注意事項
コンサート席種のカラムが、商品詳細表と販売表に1つずつあり、重複していて気持ち悪いが、
試験問題がそうなっており、敢えてそうしてる模様なので、そのままにする。
その意図を反映するため、席種の商品詳細表側はinteger、販売表側はstringにしておく。

使用環境

  • ホストOS: Windows10 Home
  • 仮想環境OS: Ubuntu Bento/Bionic
  • Ruby:2.51
  • Rails: 5.2.2
  • DB: PostgreSQL

rails gコマ

今回もdeviseを利用することを、頭に入れておきたい。

terminal
rails new concert_ticket -d postgesql
Gemfile
# miniracerをコメントアウト
gem 'miniracer'
# authentication
gem 'devise'
terminal
bundle
database.yml
username: admin
password: 〇×△
host: localhost
terminal
rails db:create
terminal
# deviseのインストール
rails g devise:install
# Userモデルにdeviseを追加。モデルなので、単数形
rails g devise User name:string email:string point:integer

# 商品表と商品詳細表テーブルはscaffoldを用いる
rails g scaffold Concert concert_info:text concert_date:datetime
rails g scaffold ConcertDetail concert:references seat_grade:integer seat_price:integer tickets_total:integer

# Saleモデルの追加
# カラムが多い。販売表モデル 商品表外部キー 会員表外部キー 席種        席数         販売日        販売額        決済期限日         使用ポイント       
rails g model Sale concert:references user:references seat_grade:string seats_total:integer sale_date:datetime sales_total:integer payment_deadline:datetime used_point:integer

# 決済表モデルを追加
rails g model Payment sale:references payment_date:datetime payment_total:integer added_point:integer

関連付け

app/models/concert.rb
has_many :concert_detail
has_many :sale
app/models/concert_detail.rb
belongs_to :concert
app/models/user.rb
has_many :sale
app/models/sale.rb
has_one :payment
belongs_to :concert
belongs_to :user
app/models/payment.rb
belongs_to :sale

userテーブルにadminカラム追加

単に入れ忘れた。

terminal
rails g migration AddAdminToUser admin:boolean
/db/migrate/(timestanp)_add_admin_to_student.rb
class AddAdminToUser < ActiveRecord::Migration[5.2]
  def change
    add_column :students, :admin, :boolean,default: false
  end
end

validation追加

後からさらに追加していくと思うが、次のデータ入力の為に。
なお、ファイル中のvalidation前後のコードは省略。

app/models/user.rb
# name の空欄不可
validates :name, presence: true
# emailの空欄不可、一意指定
validates :email, presence: true, uniqueness: true
# emailの構成文字の大文字小文字の区別はしない
# あとで、正規表現込みのvalidation追加等するが、あとまわし。
validates :email, presence: { case_sensitive: false }
app/models/sale.rb
# 購入席数の空欄不可
validates :seats_total, presence: true
# 購入席数は4席まで。(試験問題には無いけど)
validates :seats_total, numericality: {less_than_or_equal_to: 4}

初期データ作成 (seeds.rb使用)

普段はコンソールからが多いが、rails tutorialをやって、seeds.rbの方が勝手が良かったので。
あと、gem Fakerは、SAOなど『遊び』があって、好きだが、rubyメソッドの勉強にならないので、今回はパス

ユーザ作成

カラムは氏名とemailアドレス

db/seeds.rb
# 管理者
User.create!(name: 'おりばー', email: 'ないしょ@gmail.com', password: 'ないしょ', admin: true)

# 一般ユーザ。とりあえず、20人作る。
20.times do |i|
  nm = (1..5).map { ('あ'..'ん').to_a[rand(20)] }.join
  em = ('a'..'z').to_a.shuffle[1..5].join

  User.create!(name: nm, email: "#{em}-#{i}@gmail.com", password: SecureRandom.base64)
end

商品表と商品詳細表の作成

(まだ足りないと思うが)リファクタリングの結果、コードが合わさったので、纏めて表示。
なお、qiita用に、一部コードの順序を変えてある、が、見づらい。

db/seeds.rb
# concert
# 20回分のコンサート、20組分のアーティストを作成
require 'active_support/all'

concert_name = %w[雅楽 令和 ジャズ ラスト ロック アニソン]
local = %w[東京 札幌 名古屋 大阪 広島 宮崎 台南 Cebu Perth]
artists = []

# 20回分のコンサート、20組分のアーティストを作成
(1..20).each do |n|
  # コンサート開催日として、期間s1 ~ s2までのランダムな年月日を作成
  s1 = Date.new(2019,5,31)
  s2 = Date.new(2020,3,31)
  s = Random.rand(s1..s2)

  # 出演アーティストをランダムで作成して、配列に入れる。
  # 作成した配列の並びをシャッフルし、先頭から1~5アーティストを取得し、カンマで繋げた文字列に。
  artists << (1..3).map { ('A'..'Z').to_a[rand(20)] }.join.to_s
  who_join = artists.shuffle.take(rand(5)+1).join('、')

  # 試験問題の"クリスマスコンサート2018 in 東京 出演:Xバンド・・・"の表記に沿ってあげる。
  infomation = "#{concert_name.sample}コンサート #{s.strftime('%Y')} in #{local.sample} 出演:#{who_join}"
  # コンサート情報の開催年と、開催日時が合致するようにした。
  Concert.create!(concert_info: infomation, concert_date: s.strftime('%Y-%-m-%-d'))

  con = Concert.find(n)
  (0..2).each do |i|
    # seat_price S > A > B
    # prices = [price_s , price_a , price_b]
    # capa= [sの席数、aの席数、bの席数]
    prices = [[40000, 35000, 30000], [28000, 25000, 23000, 19000], [18000, 15000, 13000, 11000, 9000]]
    capa = [rand(10..50), rand(100..500), rand(500..1000)]
    ConcertDetail.create!(concert:con,seat_grade:i, seat_price:prices[i].sample, tickets_total: capa[i-1])
  end      
end

actrive_recordを使おう

参照

初期データ投入

terminal
ralis db:seed

カラム名を変更しよう

思った。非常に分かりづらい。controller作成時に疲れそう。
会員表(User)テーブルはパス。あと、コードは一部省略

商品表(Concert)テーブル
現状カラム:コンサート情報:concert_info , 開催日時:concert_date
=>infomation , date

db/migrate/(timestanp)_create_concerts.rb
    create_table :concerts do |t|
      t.text :infomation
      t.datetime :date
      t.timestamps
    end

商品詳細表(ConcertDetail)テーブル
現状:席種:seat_grade , 販売価格:seat_price , 販売席数:ticket_total
=> grade , price , capacity

db/migrate/(timestanp)_create_concert_details.rb
create_table :concert_details do |t|
      t.references :concert, foreign_key: true
      t.integer :grade
      t.integer :price
      t.integer :capacity
      t.timestamps
    end

販売表(Sale)テーブル
席種:seat_grade、席数:seats_total、販売日:sale_date、販売額:sales_total、決済期限日:payment_deadline、使用ポイント:used_point
=> grade , number_of_seats , date , amount , payment_deadline , used_point
このテーブルは安易にリネームすると尚更分かりづらくなるので、注意した。
販売額amountとごっちゃにならず、席数に最適な短い訳が思いつかないので、一応英語に従った。、

db/migrate/(timestanp)_create_sales.rb
create_table :sales do |t|
      t.references :concert, foreign_key: true
      t.references :user, foreign_key: true
      t.string :grade
      t.integer :number_of_seats
      t.datetime :date
      t.integer :amount
      t.datetime :payment_deadline
      t.integer :used_point
      t.timestamps
    end

決済表(Payment)テーブル
決済日:payment_date、決済額:payment_total、付与ポイント:added_point
=> date , amount , added_point

db/migrate/(timestanp)_create_payments.rb
create_table :payments do |t|
      t.references :sale, foreign_key: true
      t.datetime :date
      t.integer :amount
      t.integer :added_point
      t.timestamps
    end

seeds.rbファイルもカラム名を使用しているので、一部変更

terminal
rails db:migrate:reset
rails db:migrate
rails db:seed

ER図を修正

modified.png

あとは、controllerとviewを弄っていくか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyのバージョン指定があるプロジェクトで良くあるエラーと対処法

rbenv global 2.3.0 

などで、プロジェクトに応じてバージョンを下げた際に起こるエラーに対しての対処法。

$ rails s
rbenv: rails: command not found

The `rails' command exists in these Ruby versions:
  2.3.0
  2.3.1
  2.6.2

こんなエラーが出てしまった・・・・・

対処法

gem i -v 5.1.3 rails

これでもだめ、、、、、、


Gemfile.lockにあるbundlerのバージョンに合わせて、インストール

$ gem install bundler -v 1.3.0

これで行けた!!
ただ、bundle installする時に、エラー文に沿ってGemfileの修正が必要。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

なぜあなたは初心者エンジニアから脱出できないのか

概要

タイトルの通り。それ以上でもそれ以下でもありません。
自分自身がRubyとRailsをゴリゴリやっていたのに初心者からいつまでも抜け出せないなぁというところから今に至ります。
とくに初心者から抜け出せない、だけど将来はエンジニアになりたい独学大学生に送りたい。

なぜ初心者か

ずっとRubyとRailsを書き、それにこだわってきたときは気づけなかったんですが、答えは単純。RubyとRailsは知ってても他のことは知らなかったから。
要はコンピューター内で動くプログラムは少しかじれてたけど、動く機械のことを知らなさ過ぎたから。
要はコンピューターサイエンスについてだったりWebの知識がない。(Webアプリを作りながらこの状況だからそりゃ脱出はできない。)

Railsにこだわりすぎた

Railsは一行でいろいろできすぎる。
すごくありがたいけど、そのせいで理解したフリで先へ先へとすすめてしまっている。(特にScaffoldはよくないと思ってる。)
知識があって省略するのと、知らないまま省略するのでは大きな差があるとめちゃめちゃに実感した。要はフレームワークはRailsに限らず便利でいいんだけど、ちゃんと同時に知識をつけないと無知のまま世界が広がることになる
作ってたり勉強している分には別にどうってことないけれど、仕事にするぞ、ってなったときに、苦労が半端じゃない。

言語の選び方が雑だった

正直最初は興味のある分野、言語をやっていいと思う。入りがつまらないとしんどいし、やめてしまうから。実際自分も最初はゲームを作りたくてUnityとC#を触ってキャッキャウフフしてた。
だけど、その後職業としてWEBエンジニアになりたいと勉強し始めた方法が失敗だった。RubyとRailsは学習コスト的に楽そうだと始めた。確かに楽だった。でも昔の人はいい事を言うもので、楽あれば苦あり。のちに苦が訪れた。
確かに手を動かして、ポートフォリオを作ってみたいなところは簡単に進む。だけれども小手先感が半端じゃない。それは当然で、あとから考えれば本当に必要な知識はついていなかったのだから

どうやったら脱出できるのか

正直なところ、今抜け出そうとしている最中だからわからない。ただ、WEBやコンピューターサイエンスの勉強をしていると、少しずつブラックボックスになっていたことはわかってきた
こうして一個一個知らないことをつぶすほかないと思う。正直面倒だし、地味だけどすごい大切
RubyやRailsも初心者がやっちゃダメというわけでなくて、ただちゃんと理解してますか?って考えながらやった方がいい。(あと個人的にはこれらはMVPがすぐほしいStart Upがやるといいのかなぁと思ったりもする。あとはちゃんと勉強できる人。)

終わりに

職業でエンジニアになりたい、という人以外は別に初心者から抜け出さなくてもいいと思う。大変だし。
ただ、もしエンジニアに何が何でもなる、というなら、手を動かすだけなのもダメな気がする。ちゃんと勉強して、なぜ動くか、どう動くかきっちり確認する。
車を運転する人は中でどう動作するか知らなくてもいいけど、作ったりメンテナンスする人が適当な理解はまずいに決まっている。ちゃんと勉強していった方が、遠回りかもしれないけど、必要なことだと思う。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[学習用]Rails5にてCarrierWaveでサムネイル画像と最大画像と拡張子の制限をしたアプリを作る

画像アップアプリ.jpg
※Bootstrap入れて調整したもの。

スムーズにうまく行ったのでメモ。

Ralis5
Ubuntu16

GemFileに追加する

gem 'carrierwave'
gem 'mini_magick'

コマンド入力

$ bundle
$ rails g scaffold upload image:string content:text name:string
$ rails g uploader images
$ rails db:migrate

必要事項の追記

Scaffoldで作ったuploadのmodelsに追記

app/models/upload.rb
class Upload < ApplicationRecord
  mount_uploader :image, ImagesUploader
end

CarrierWaveのファイルに追加

app/uploaders/images_uploader.rb
class ImagesUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick

  # Choose what kind of storage to use for this uploader:
  storage :file
  # storage :fog

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # 許可する画像の拡張子
  def extension_whitelist
    %w(jpg jpeg gif png)
  end

  # サムネイル画像
  version :thumb do
    process resize_to_fill: [80, 80]
  end

  # Maxサイズ
  version :content do
    process resize_to_limit: [640, 640]
  end
end

Viewsファイルを修正・追加

_horm.html.erb

app/views/uploads/_horm.html.erb
  <div class="field">
    <%= form.label :image %>
    <%= form.file_field :image %>
  </div>

show.html.erb

app/views/uploads/show.html.erb
<%= image_tag @upload.image_url(:content) if @upload.image.present? %>

index.html.erb

app/views/uploads/index.html.erb
<table>
  <thead>
    <tr>
      <th>Image</th>
      <th>Content</th>
      <th>Name</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @uploads.each do |upload| %>
      <tr>
        <td>
          <%= image_tag upload.image_url(:thumb) if upload.image.present? %>
        </td>
        <td><%= upload.content %></td>
        <td><%= upload.name %></td>
        <td><%= link_to 'Show', upload %></td>
        <td><%= link_to 'Edit', edit_upload_path(upload) %></td>
        <td><%= link_to 'Destroy', upload, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

これで画像のサムネイル投稿、最大サイズの制限、投稿拡張子の制限ができる。

サムネイルには

ImageMagick が必要なのでサーバーに入れてない場合は入れる。

画像にリンクをつける

app/views/uploads/index.html.erb
          <%= link_to (image_tag upload.image_url(:thumb) if upload.image.present?), upload %>

または

app/views/uploads/index.html.erb
          <%= link_to upload do %>
            <%= image_tag upload.image_url(:thumb) if upload.image.present? %>
          <% end %>

サムネイル画像

imges1.jpg
完成直後のアプリ

詳細ページの最大画像

image2.jpg
完成直後のアプリ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Rails で動画投稿機能を実装してみた

基本的には画像の投稿と同じ

1.carrierwaveの導入
2.カラムを追加
3.モデルにアップローダを追加
4.コントローラにパラメータ追加
5.フォームの作成
6.ビューに表示

1.carrierwaveの導入

Gemfile
gem 'carrierwave'

Gemfileに追記↑

ターミナル
$ bundle install

rails s 再起動忘れないように...

ターミナル
$ rails g uploader video

carrierwave generatorを使用しビデオをアップロードするためのアップローダを作成する。
app/uploaders/video_uploader.rb が生成されていることを確認。

2.モデルでカラムを追加

ターミナル
$ rails g migration add_video_to_post video:string
# rails g migration add_カラム名_to_モデル名 カラム名:string
ターミナル
$ rake db:migrate

3.モデルにアップローダを追加

tweet.rb
class Tweet < ApplicationRecord
  mount_uploader :video, VideoUploader
# mount_uploader :カラム名, carrierwaveの設定ファイルのクラス名
end

4.コントローラにパラメータ追加

tweets_controller.rb
class TweetsController < ApplicationController

#(略)

  def new
    @tweet = Tweet.new
  end

  def create
    @tweet = Tweet.create(tweet_params)
  end

  private
  def tweet_params
    params.require(:tweet).permit(:video)
   #params.require(:モデル名).permit(:カラム名)
  end

end

5.フォームの作成

new.html.haml
 = form_for @tweet do |f|
    = f.file_field :video
    = f.submit "送信"

6.ビューに表示

index.html.haml
= video_tag tweet.video_url.to_s,loop: true, controls: true,;

#loop: true loop処理
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsアプリをHeroku上で、NginxとUnicornで動かす方法

unicornの導入

Gemfile
gem 'unicorn'

追加し、bundle install

Procfileも編集するが、後でnginxの設定をするために、ここではやらない。

※参考サイト
https://workabroad.jp/posts/1225

nginxの導入

※参考サイト
https://github.com/heroku/heroku-buildpack-nginx
あとは、このサイトのSetupに沿って進めていく。

herokuにデプロイできたら、下記コマンドで確認。

heroku logs -t

約2日間、分からずに積んだため、このようにまとめました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Oauth2.0 providerをrailsで作る

以下の記事を参考
https://qiita.com/kyonsuke19101/items/407f3cdfec38d1108e9d

エラー回避

403error → admin_authenticatorのコメント欄をactivation
adminカラムをuserに追加
root to: 'home#show'をroutesに追加

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ratyrateを使用したRailsの星型評価を実装する

Railsで星の評価機能を実装する方法です。

環境

Rails 5.2.3
mysql
gem ratyrate
gem devise

Goals

以下のように星の評価を実装したアプリの作成
GitHub:testGemRatyrate

FireShot Capture 116 - TestGetRatyrate - http___localhost_3000_cars_1.png

背景

Railsで星の評価機能を実装しようとした際、jquery.ratyを使用する方法は参考記事が沢山あるのですが、jqueryの知識を要求されたり、画像をダウンロードしたりと手間がかかります。

もっと手軽に実現できないか調べたところ、ratyrateというgemがあることを知りました。
しかし、Rails5.2.3では公式の方法ではエラーで使用することができませんでした。

そこで、Rails5.2.3で使用する方法をまとめました。

準備

ratyrateのインストール

ratyrateはjqueryを使用しているので、jquery-railsも一緒にインストールします。
また、deviseと連携させるのでdeviseも加えます。
Gemfileに以下をグローバルに記入します。

gem 'devise'
gem 'jquery-rails'
gem 'ratyrate'

インストールをします。

bundle install

評価者の設定

devise:userとratyrateを作成します。

rails g devise:install
rails g devise user
# 以下を実行すると、星画像やmigrationファイル等が作成される
rails g ratyrate user

Migration

生成されるmirationファイルは、以下の4つになります。
- db/migrate/20140814135421_create_rating_caches.rb
- db/migrate/20140706144643_create_rates.rb
- db/migrate/20140814135422_create_average_caches.rb
- db/migrate/20140814135423_create_overall_averages.rb

この状態でmigrationを実行するとエラーになり、migrationに失敗してしまいます。
そこで、各ファイルを以下のように編集します。

create_rating_caches.rb

class CreateRatingCaches < ActiveRecord::Migration[5.2]

  def self.up
      create_table :rating_caches do |t|
        t.belongs_to :cacheable, :polymorphic => true
        t.float :avg, :null => false
        t.integer :qty, :null => false
        t.string :dimension
        t.timestamps
      end
    end

    def self.down
      drop_table :rating_caches
    end

end

create_rates.rb

class CreateRates < ActiveRecord::Migration[5.2]

    def self.change
      create_table :rates do |t|
        t.belongs_to :rater
        t.belongs_to :rateable, :polymorphic => true
        t.float :stars, :null => false
        t.string :dimension
        t.timestamps
      end
    end

    def self.down
      drop_table :rates
    end

end

create_average_caches.rb

class CreateAverageCaches < ActiveRecord::Migration[5.2]

  def self.up
    create_table :average_caches do |t|
      t.belongs_to :rater
      t.belongs_to :rateable, :polymorphic => true
      t.float :avg, :null => false
      t.timestamps
    end
  end

  def self.down
    drop_table :average_caches
  end

end

create_overall_averages.rb

class CreateOverallAverages < ActiveRecord::Migration[5.2]

  def self.up
    create_table :overall_averages do |t|
      t.belongs_to :rateable, :polymorphic => true
      t.float :overall_avg, :null => false
      t.timestamps
    end
  end

  def self.down
    drop_table :overall_averages
  end
end

編集したらmigrationを実施します。

rails db:migrate

javascriptの読み込み

app/assets/javascripts/application.jsに以下を追記します。
この時、//= require rails-ujsより上に記述します。

//= require jquery
//= require jquery.raty
//= require ratyrate

以上で、ratyrateを使用する準備が整いました。

アプリ作成

実際にratyrateを実装したアプリを作成します。
作成するアプリは車のスピードを評価するものです。

準備

scaffoldを使用してモデルを作成

rails g scaffold car name:string

migrationを実施します。

rails db:migrate

コードの編集

メインとなる箇所を抜粋します。

Model

car.rb

class Car < ApplicationRecord
  ratyrate_rateable "speed" # 追加
end

補足:
ratyrate_rateableで評価対象となる項目を指定します。
複数ある場合は、カンマ区切りになります。

例:

ratyrate_rateable "speed", "engine", "price"

user.rb

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  ratyrate_rater # 追加
end

View

app/views/cars/show.html.erb

星を表示したい場所へ以下を記述します。

<p>Speed : <%= rating_for @car, 'speed' %></p>

以上で完成です。

その他オプション

再評価

デフォルトの設定では、一旦評価すると変更できません。
そこで以下のオプションを付けることで、評価を変更できるようになります。

<%= rating_for @car, 'speed', disable_after_rate: false %>

FireShot Capture 117 - TestGetRatyrate - http___localhost_3000_cars_1.png

星の数の変更

<%= rating_for @car, 'speed', star: 10 %>

FireShot Capture 118 - TestGetRatyrate - http___localhost_3000_cars_1.png

星半分

<%= rating_for @car, 'speed', disable_after_rate: false, enable_half: true %>

FireShot Capture 119 - TestGetRatyrate - http___localhost_3000_cars_1.png

平均値の表示

<%= rating_for car, 'speed', disable_after_rate: true, imdb_avg: true %>

FireShot Capture 120 - TestGetRatyrate - http___localhost_3000_cars.png

その他にもオプションが用意されているので、wazery/ratyrateRatyrate: Add Rating to Your Rails Appで確認してください。

最後に

ratyrateを使用することで、評価用のテーブルは不要になり、実装もシンプルでスマートになりました。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ポートが既に使われているエラーの場合は、不要なポート番号を削除する。

Address already in use... のエラー

Railsにて$rails sを実行時、下記のようなERRORが発生した。

$ rails s
.
.
.

/Users/kikkutonton/.rbenv/gems/2.6.0/gems/puma-3.12.1/lib/puma/binder.rb:273:in `initialize': Address already in use - bind(2) for "::1" po
rt 3000 (Errno::EADDRINUSE)

エラー原因

エラー文通り、localhost:3000のポートが既に他のポートに使われていることが原因のエラー。

対処法

不要なポートを削除する。

使われているポート番号を確認

$sudo lsof -i:3000
COMMAND     PID  USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
com.docke 17067 kikkutonton   22u  IPv4 0xeexxxxxxxxxxxxxx      0t0  TCP *:hbci (LISTEN)
com.docke 17067 kikkutonton   23u  IPv6 0xeeyyyyyyyyyyyyyy      0t0  TCP localhost:hbci (LISTEN)

使用ポートを削除

//PID番号
$sudo kill xxxx 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

気持ちいいRuby/Ruby on Rails開発環境を作ったので解説(Mac/Win両対応)

hayakasa/docker-vagrant-template-rails

以下の要件を満たすRuby/Ruby on Railsの開発・実行環境を環境を作成しました。

  • ホストOSの環境を極力汚さないこと
  • 必要最低限の手順で環境構築できること
  • 実行環境のバージョンを管理する手間を極力省くこと
  • ソースコードを共有ディレクトリでマウントし、編集後に即時反映されること
  • Dockerコンテナ側からファイル操作が可能なこと(bundle install、tmp内のファイル作成など)
  • Mac・Windowsどちらでも可能な限り同じ手順で構築し動作すること

rvm、rbenvなどで実行環境のバージョン管理をすることが手間に感じていたのでDockerを採用することを決めたのですが、公式で配布されているDocker Desktop for Mac/Windowsにはソースコードを即時反映できるようにマウントした場合に実行速度が遅くなるなどの問題があります。
この問題を解決するため、ホストOSとDockerコンテナの間にVagrantで管理するVMを挟み、共有ディレクトリをNFSでマウントすることによって実行速度を犠牲にせずにアプリ開発ができる環境を作りました。

前提アプリケーションのインストールや使い方は下記GitHubリポジトリのREADMEを参照ください。
hayakasa/docker-vagrant-template-rails

こちらでは、少し突っ込んだ解説を行います。

NFSについて

Vagrantfileの以下の設定で共有ディレクトリをNFSマウントしています。

Vagrantfile
config.vm.synced_folder ".", "/vagrant-nfs",type:"nfs" #DocRootをvagrant-nfsという名前でNFSマウント

Macの場合

Macでは標準でNFSサーバーが準備されているので以下のコマンドで有効化します。

YourPC$ sudo touch /etc/exports #nfsd設定を保存するファイルを作成
YourPC$ sudo nfsd enable #nfsdの有効化

Windowsの場合

WindowsではVagrantのプラグインを使うことによってNFSサーバーを立てずにNFSでマウントができるためそちらを使用します。

YourPC$ vagrant plugin install vagrant-winnfsd

vagrant-bindfsプラグインについて

NFSでマウントした共有ディレクトリはホストOSのユーザーがownerになっているので、bindfsでvagrantユーザーがownerになるようにマウントし直します。

Vagrantfile
config.bindfs.bind_folder "/vagrant-nfs", "/vagrant" #vagrant-nfsをvagrantという名前でbindfsマウント

vagrant-hostsupdaterプラグインについて

vagrant upでVMを起動するときにhostsファイルにホスト名の登録を行います。これによってホスト名でアプリケーションにアクセスすることができるようになります。
(hostsを操作するためWindowsではコマンドプロンプト・PowerShellを管理者権限で実行する必要があります)

Vagrantfile
config.vm.hostname = "myapp.localhost"

ポートフォワーディングについて

以下の設定によって一般的に使用されるポートでホストOSからアクセスができるようにしています。

docker-compose.yml
services:
  db:
    ports:
      - "5432:5432" #例:PostgreSQLの場合。Dockerコンテナの5432番をVMの5432番にポートフォワーディング
  app:
    command: bundle exec rails s -p 3000 -b '0.0.0.0' --no-dev-caching #rails s を3000番ポートで実行
    ports:
      - "80:3000" #Dockerコンテナの3000番ポートをVMの80番にポートフォワーディング
Vagrantfile
  config.vm.network "forwarded_port", guest: 80, host: 80 #VMの80番をホストOSの80番にポートフォワーディング
  config.vm.network "forwarded_port", guest: 5432, host: 5432 #VMの5432番をホストOSの5432番にポートフォワーディング

データベースのvolumeについて

以下の設定によってデータベースの内容はVM内で永続化しています。したがってVMのイメージ自体を削除しない限り、Dockerコンテナを破棄してもデータベースの中身は保持されます。

docker-compose.yml
services:
  db:
    volumes:
      - /data/db:/var/lib/postgresql/data  #例:PostgreSQLの場合 Dockerコンテナの/var/lib/postgresql/dataをVMの/data/dbに保存します
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Railsで下書き機能を実装するために場合分けしてみた

はじめまして。
Ruby on Rails勉強中のfkdollyと申します。
現在のところ独学での勉強につき、間違っている箇所があればご指摘いただければ幸いです。

現在Railsでアプリを開発中でして、その中で投稿に下書き機能をつけたいと思い、下記の記事を始めとして色々調べてみました。
Railsで下書き機能を独自に実装した時の過程をまとめてみた

ですが、モデルをもう一つ作るとより頭がこんがらがりそうだったので(笑)、Postモデルの中で何とかする方法を考えました。

まず、搭載する下書き機能について以下のようなものを考えました。

  • 投稿一覧画面では投稿した本人がログインしている時のみ下書き一覧を表示する。他のユーザーまたは未ログインユーザーがアクセスしても公開済みの投稿のみ表示。
  • 投稿詳細画面も同様に下書きを投稿した本人がログインしている時のみアクセス可能。その他のユーザーはアクセス不可。公開済み記事には全ユーザーがアクセス可能。

そこでPostテーブルにStatusカラムを追加し、Draft/Publishのどちらかによって下書き/公開済みを切り替えることとしました。
また、アクセスしたユーザーが本人かどうかについてはセッションで判断することとしました。

users_controller.rb
def login
    @user = User.find_by!(name: params[:name], password: params[:password])
    if @user
      session[:user_id] = @user.id
      flash[:notice] = "ログインしました"
        redirect_to("/#{@user.id}")
    else
      redirect_to("/login")
    end
  end

ユーザー本人か否か/Draft記事があるか否か/Publish記事があるか否かの2×2×2=8通りなので条件分岐していきます。Userモデルのshowアクションでそのユーザーの投稿一覧を表示しており、下記のように書きました。

show.html.erb
<% posts_draft = Post.where(user_id: @user.id, status: "draft") %>
<% posts_publish = Post.where(user_id: @user.id, status: "publish") %>

# ユーザー本人かつ下書きも公開済み記事もある場合
<% if session[:user_id] == @user.id && posts_draft.present? && posts_publish.present? %>
<div class="post_status">下書きが残っています。投稿を公開しよう!</div>
<% posts_draft.each do |post| %>
    # Draft一覧
<% end %>
<div class="post_status">公開済み</div>
<% posts_publish.each do |post| %>
    # Publish一覧
<% end %>

# ユーザー本人かつ下書きのみある場合
<% elsif session[:user_id] == @user.id && posts_draft.present? && posts_publish.empty? %>
<div class="post_status">下書きが残っています。投稿を公開しよう!</div>
<% posts_draft.each do |post| %>
    # Draft一覧
<% end %>

# ユーザー本人かつ公開済み記事のみある場合
<% elsif session[:user_id] == @user.id && posts_draft.empty? && posts_publish.present? %>
<div class="post_status">公開済み</div>
<% posts_publish.each do |post| %>
    # Publish一覧
<% end %>

# ユーザー本人かつ下書きも公開済み記事も無い場合
<% elsif session[:user_id] == @user.id && posts_draft.empty? && posts_publish.empty? %>
<div class="post_not_yet">投稿はありません。投稿しよう!</div>
<% end %>

# その他のユーザーかつ下書きも公開済み記事もある場合、または公開済み記事のみある場合
<% elsif (session[:user_id] != @user.id && posts_draft.present? && posts_publish.present?) || (session[:user_id] != @user.id && posts_draft.empty? && posts_publish.present?) %>
<% posts_publish.each do |post| %>
    # Publish一覧
<% end %>

# その他のユーザーかつ下書きのみある場合、または下書きも公開済み記事も無い場合
<% else %>
    <div class="post_not_yet">投稿はありません。</div>
<% end %>
<% end %>

下2つは表示する内容が同じなのでまとめた結果6通りの場合分けになりました。
実際のコードではユーザー本人によるアクセスの場合は編集ボタンと削除ボタンも表示しています。

また、投稿詳細画面はPostモデルのshowアクションで表示しています。上記のコードで画面上は表示されませんが、URLを直接入力するとアクセスできてしまうので、コントローラで下記のようにしました。

post_controller.rb
def show
    @post = Post.find_by(id: params[:id])
    @user = User.find_by(id: @post.user_id)
    if session[:user_id] == nil && @post.status == "draft"
      redirect_to("/login")
    elsif session[:user_id] != @user.id && @post.status == "draft"
      redirect_to("/#{@current_user.id}")
    end
end

以上のような形で実装してみました。今のところは問題ないハズです。
が、要素がこれ以上増えるとこうした場合分けを使うのは現実的では無いと思うので別の方法も勉強してみたいと思います。やっぱりもっとシンプルにできる気がする...
こういう方法もあるよ!というのがあればぜひ教えてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LINEBOT + RailsでWikipediabotを作ってみました。

はじめに

Railsの勉強のためにLINEBOTを作ってみました。
内容はLineメッセージに調べたい単語を送信すれば、Wikipediaの概要とurlを返すものです。

LineBotの設定

下記のリンクを参考に設定しました。ありがとうございます。

今更ながらRails5+line-bot-sdk-ruby+HerokuでLineBot作成してみたら、色々詰まったのでまとめました。

Wikipediaの情報取得

Wikipediaの情報を取得するためにAPiがあったので、そちらを利用しました。
wikipedia-client

Gemgile
gem 'wikipedia-client'
$ bundle install

Controllerを編集

linebot_controller.rb
class LinebotController < ApplicationController
  require 'line/bot'
  require 'wikipedia'

  # callbackアクションのCSRFトークン認証を無効
  protect_from_forgery :except => [:callback]

  def client
    @client ||= Line::Bot::Client.new { |config|
      config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
      config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
    }
  end

  def callback
    body = request.body.read

    signature = request.env['HTTP_X_LINE_SIGNATURE']
    unless client.validate_signature(body, signature)
      head :bad_request
    end

    events = client.parse_events_from(body)

    events.each { |event|
      if event.message['text'] != nil
        # LINEで送られてきた文書を取得
        word = event.message['text']
        # 日本語版Wikipediaを設定
        Wikipedia.Configure {
          domain 'ja.wikipedia.org'
          path   'w/api.php'
        }
      end

      # wikipediaから情報取得
      page = Wikipedia.find(word)

      # 概要とURLを返す
      response = page.summary + "\n" + page.fullurl

      case event
      # メッセージが送信された場合
      when Line::Bot::Event::Message
        case event.type
        # メッセージが送られて来た場合
        when Line::Bot::Event::MessageType::Text
          message = {
            type: 'text',
            text: response
          }
          client.reply_message(event['replyToken'], message)
        end
      end
    }

    head :ok
  end
end

あとはherokuにデブロイして、動作確認すれば完成です。

動作イメージ

こんな感じで概要とURLを返してくれるようになります。
2019-04-11 0.53のイメージ.jpg

まとめ

簡単なまとめになりましたが以上になります。
結構簡単に実装できました。APIと組み合わせれば色々使えるんじゃないかなと思いました。
何かご指摘等がございましたらコメントよろしくお願いします。

参考サイト

今更ながらRails5+line-bot-sdk-ruby+HerokuでLineBot作成してみたら、色々詰まったのでまとめました。

Web APIとは? (LINE bot API・グルナビAPI)

wikipedia-client

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む