20191224のRailsに関する記事は24件です。

ストロングパラメータとは

ストロングパラメータとは

paramsは情報を受け取れるとても便利なものだが、不正な情報を防ぐことができません。そんな時に使うのが「ストロングパラメータ」です。
「ストロングパラメータ」とは不要な情報を受け取らないようにしてくれるもの、セキュリティーを強化してくれるものになります。

ストロングパラメータの設定

ストロングパラメータはユーザーのログイン時などによく使用されるため、今回はログイン時を想定して設定を行いました。

users_controller
private
 def user_params
  params.require(:user).permit(:name, :email, :password, :password_confirmation)
 end

上記を設定することで、userはname、email、password、password_confirmationのデータしか受け取れないようになります。

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

Ha4go運用の実績と課題 2019

Ha4go の開発と運用をやっている @PharaohKJ です。この記事はチームの総意だとかそういうのじゃなく、あくまでも開発と運用を担当している私の考えていることです。いわゆるシビックテック発のプロダクトを開発・運営する現場について少しでも感じる、伝わればと思っています。

Ha4go とは

Ha4go by codeforkanazawa-org を参照してください。一応、Code for Kanazawa にリポジトリがあって開発されています。

CfKのメンバーだけではなく、 Code for Okinawa の kimihito (kimihito) さん、Code for Japan の halsk (Hal Seki) さんにもコードのコミットをいただいて共同開発しています。

大きな役割は3つだと考えています.

  • みんなで見つけた・考えた課題の投稿
  • 課題を解決するための何かを提供できる人の登録
  • 課題の欲していることからの課題と人のマッチング

実績(現状)

今年は、全国対応版としてバージョンアップすることを宣言しましたが、実質とりかかれていません。なんかまぁ下げ止まってるのか、ユーザー数にあまり変化はありません。

UU・PV

スクリーンショット 2019-12-24 23.09.24.png

登録されてる課題と増えた量

去年は5件、今年1件でした。CfK Civic Hack Nightに集まってくる人も減ってきてますし、後述する「権利的な課題」が噴出したこともあってなかなか件数が増えません。要全国版ですね!

subject created_at
ネット犯罪に巻き込まれるのを防ぎたい 2019-02-24 21:35:12
mysql> select year(created_at), month(created_at), count(id) from projects group by year(created_at), month(created_at);
+------------------+-------------------+-----------+
| year(created_at) | month(created_at) | count(id) |
+------------------+-------------------+-----------+
|             2016 |                 3 |         1 |
|             2016 |                 4 |         2 |
|             2016 |                 5 |         1 |
|             2016 |                 7 |         5 |
|             2016 |                 8 |         1 |
|             2016 |                 9 |         1 |
|             2016 |                10 |         3 |
|             2016 |                11 |         1 |
|             2016 |                12 |         1 |
|             2017 |                 5 |         2 |
|             2017 |                 6 |         1 |
|             2017 |                 8 |         1 |
|             2017 |                10 |         1 |
|             2018 |                 1 |         1 |
|             2018 |                 3 |         1 |
|             2018 |                 5 |         1 |
|             2018 |                 6 |         1 |
|             2018 |                 9 |         1 |
|             2019 |                 2 |         1 |
+------------------+-------------------+-----------+

運用

ソースコード、issue

前述のとおり、CfKのGitHubにリポジトリなど一式用意されています。 → codeforkanazawa-org/ha4go: ha4go

ウェブ上のインフラ

今年は全国版に移植する、、、と宣言したのですが、特にかわっていません。これまでは金沢の課題は kanazawa.ha4go.net に集め、他の課題は xxx.ha4go.net といったように xxx を地域名にして運用しようと当初考えていましたが、今後は全国版にしていこうと考えています。

ドメイン名はCfKにとっていただいてます。

ウェブページ( http://ha4go.net )は上記リポジトリの GitHub Pages を使って運用しています。

ウェブアプリは さくらインターネット様のご厚意で、IaaSであるさくらクラウドをご提供いただきました。この上に以下のような構成とミドルウェアを使っています。

  • Ruby on Rails のウェブアプリ用インスタンス1
    • nginx
    • Let's Encrypt
    • docker (Ruby on Rails アプリ)
  • MySQL 用インスタンス1
    • docker (MySQL)
  • メンテ用踏み台1
    • munin
    • cron
      • DBデイリーバックアップ
      • Let's Encrypt 証明書更新(マンスリー)

通知メール配信はさくらのメールボックスを使わせてもらってます(これは有料でもともとCfKが契約していたものです)。

開発のコミュニケーションは 一連の業務の拠点となるデジタルワークスペース | Slack の無料プランで行っています。GitHubとの連携や、さくらインターネットさんの障害情報のRSSをここに通知させたりしています。

課題と今後

技術的課題

去年と全くかわっていません。セキュリティアラートはGitHubから飛んでくるので、把握はしやすくなりましたが、デプロイはオオゴトです。また、Railsも6がリリースされて、そちらに以降したいと考えています。幸いなことに、JavaScriptがたくさんあったりはしないので移行は簡単、、、なはずと考えています。

権利的な課題

今年増えた課題「ネット犯罪に巻き込まれるのを防ぎたい」において、プロダクト作成にまで進んでいたのですが、いろいろあって特に権利問題、、、つまり「CfKでやったものについてはCfKに帰属する」というところがご納得いただけず、プロジェクトは解散になってしまいました。

アイデアソンやハッカソンなどやるのはいいのですが、なかなかどうしてこういった権利関係は複雑になりがちで、それ故に「権利はすべて主催者側に帰属する」とざっくりと決めざるを得ないことが多いと思います。しかしながら、この方式だと人が集まってくる・やろうとする限界があるんじゃないかなあとも考え始めています。

「これが飲めないんだったら、発言も手を動かすもやめてください」ってのはさすがにキツすぎませんか?何か新しいライセンスや権利など、提案できたらなと考えています。そうじゃないと「ha4goに課題を書いたら損」「会に参加して思いついたことを話したら損」なんて状況になるのはちょっと加藤が望む状況ではありません。

今後

来年こそは

  • ha4goを全国版対応
  • Rails 6 対応
  • 発言や書き込みへの ha4go ライセンスについて提案

したいと考えています。よろしくおねがいします。

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

EC2でsassc gemがインストール アップデートできなかった

解決策

古いgccを捨てて新しいgccをインストールする

yum remove gcc libstdc++48-devel
yum install gcc72 gcc72-c++ libstdc++72

事象

lto1: fatal error: LTO_tags out of range: Range is 0 to 365, value is 51456
compilation terminated.
lto-wrapper: g++ returned 1 exit status
/usr/bin/ld: lto-wrapper failed
collect2: error: ld returned 1 exit status
make: *** [libsass.so] Error 1

make failed, exit code 2

Gem files will remain installed in /home/enechange/enechange/rails/vendor/bundle/ruby/2.6.0/gems/sassc-2.2.1 for inspection.
Results logged to /home/enechange/enechange/rails/vendor/bundle/ruby/2.6.0/extensions/x86_64-linux/2.6.0/sassc-2.2.1/gem_make.out

An error occurred while installing sassc (2.2.1), and Bundler cannot continue.
Make sure that `gem install sassc -v '2.2.1' --source 'https://rubygems.org/'` succeeds before bundling.

In Gemfile:
  activeadmin was resolved to 2.4.0, which depends on
    sassc-rails was resolved to 2.1.2, which depends on
      sassc

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

Railsアプリ、EC2の本番環境でMySQLを直接いじる方法

自分の作ったアプリを本番環境で起動させた時に、エラーが出てしまうことはあると思います。
currentフォルダ内のlogを見て、エラーを発見したとしても、それがMySQLのエラーだと直接コマンド打たないと行けないので、修正が結構めんどくさいんですよね!

そこで、よく使うMySQLのコマンドをまとめました!

ログイン

$ mysql -u root -p
Enter password: パスワードを入力(実際に文字は見えません)

データベースの確認

show databases;

以下のように出てきます

+----------------------------------+
| Database                         |
+----------------------------------+
| information_schema               |
| xxxxxxxxxxxxxxxxxx|
| mysql                            |
| performance_schema               |
+----------------------------------+

いじるデータベースの選択

上のコマンドで確認したものの中から選びます

use データベース名;

これが出ればOKです

Database changed

テーブル一覧表示

show tables;

データの検索

とりあえずテーブルの中身を全部見たい場合は

SELECT * FROM テーブル名;

データの削除

僕がよく使うのはこれ、とりあえずエラー引き起こすデータは消してしまえ精神

DELETE FROM テーブル名 WHERE id = 数字;

補足

この方法はあくまで、直接いじらないと直せないエラーなどを解決する方法です!
Railsの場合は、カラムの追加やテーブルの新規作成などは
マイグレーションファイルを使っていつものようにやれば大丈夫です。

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

ドラックアンドドロップで、投稿内容の非同期削除を実装する! [jQuery, Rails]

概要

先日クローゼットアプリ(衣服などの管理をするもの)を作成しました。
ドラックアンドドロップにて投稿内容を削除する機能を実装しました。
該当の箇所に投稿内容をドロップした際に、非同期で削除をするというものです。
備忘録も兼ねて投稿しますので、温かい目で頂けると幸いです:grinning:

こちらが実際の動画です。
demo

画像を左下のゴミ箱のアイコンにドロップした際にイベントを発火させ、非同期で削除しております。
画像がドラッグされている間にゴミ箱のアイコンに新たなクラスを付与し、アニメーションのように実装しております。

流れ

  1. jQuery UI導入
  2. viewにカスタムデータを追記
  3. jQueryにコード記載
    今回は削除機能の実装ですので、jsonなどcontrollerの追記は必要ありません。

1. jQuery UI導入

Gemfile
gem "jquery-rails"
gem "jquery-ui-rails"
application.js
//= require jquery
//= require jquery_ujs
//= require jquery-ui

jQuery UIとはjQueryをベースにしたJavaScriptのライブラリであるため、こちら2つのgemをインストールし、application.jsに追記します。
bundle installをお忘れなく!!

2.viewにカスタムデータ追記

_item.html.haml
.items
  - items.each do |item|
    .item{"data-item_id":"#{item.id}"}
      .item__image
        = image_tag(item.image.to_s) if item.image?
      .item__name
        = item.name

1つ1つのitemにdata属性を付与します。(3行目)
jQueryでイベント発火時にデータを取得出来るようにするためです。

3.jQuery

item_delete.js
$(function(){
  $(document).on("turbolinks:load", function(){
   //itemを動かすようにする ①
    $(".items").sortable({
    });
    // itemがドロップされた時にイベント発火 ②
    $(".trash-box").droppable({
      accept: ".item",
      activeClass: "move-trash",
      drop: function(e, ui){
        e.preventDefault();
        var delete_message = confirm("削除してもよろしいですか?");
        if(delete_message == true){
          //ドロップされたitem要素を取得。jQueryオブジェクトからDOM要素を取り出す
          var delete_item = ui.draggable[0]; ③
          //idを取得。
          var delete_ID = ui.draggable.data("item_id");
          var delete_url = "/items/" + delete_ID;
          $.ajax({ 
            url: delete_url,
            type: "POST",
            data: {id: delete_ID, "_method": "DELETE"},
            dataType: "json"
          })
          .done(function(data){ ⑥
            delete_item.remove();
            location.reload();
          })
          .fail(function(){
            alert("エラー");
          })
        }
      }
    })
  });
});

順を追って説明します。

①各itemを動かせるようにします。
draggableでも良いのですが、無作為に動いてしまうため、要素を並び替えるsortableの方が動きがスムーズだったためsortableを使用しています。

sortableで並び替えた要素の順番を保存する際には、gemのインストールやコントローラーの追記が必要となりますので、今回は割愛させて頂きます。

②ゴミ箱のアイコン(".trash-box")に要素がdropされた時にイベントを発火するようにします。
各オプションについては以下の表をご覧下さい。
コードに沿って説明させて頂くと、
acceptにてitemのdropを受け入れます。itemがdragされている間、trash-box"move-trash"というクラスが追加されるという流れです。

Image from Gyazo

③dropされたitemの要素を取得
jQueryオブジェクトからDOM要素を取り出すため、ui.draggable[0];とします。
delete_itemにdropされたitemのDOM要素が代入されます。

④dropされた要素のidを取得
dropされた時に先ほどviewに追記した、item_idを取得し、削除する時のパスに代入します。

⑤ajaxでcontrollerに送る情報を記入
ここで注意すべきなのは、data: {id: delete_ID, "_method": "DELETE"}です。
今回は削除機能なので、HTTPメソッドをDELETEリクエストと宣言する必要があります!

⑥非同期成功時のイベントを記載
delete_item.remove();で画像をview上から消します。
location.reload();でリロードにて、実装完了です:star:

終わりに

dropされた要素の取得に苦戦しました、、、。
jQuery UI特有のイベントやオプションがあり、リファレンスを何度も読み返し実装しました。

長くなってしまいましたが、最後まで読んで下さりありがとうございました!:relaxed:
他にこうした方が楽だよ!とかあればコメントください!!
以上、初投稿でした!笑

参考文献

https://qiita.com/tsubasaozawa/items/2f4783b47e76dc5a7ed3
http://js.studio-kingdom.com/

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

ドラックアンドドロップで、非同期の投稿削除を実装する! [JQuery, Rails]

概要

先日クローゼットアプリ(衣服などの管理をするもの)を作成しました。
ドラックアンドドロップにて投稿内容を削除する機能を実装しました。
該当の箇所に投稿内容をドロップした際に、非同期で削除をするというものです。
備忘録も兼ねて投稿しますので、温かい目で頂けると幸いです:grinning:

こちらが実際の動画です。
demo

画像を左下のゴミ箱のアイコンにドロップした際にイベントを発火させ、非同期で削除しております。
画像がドラッグされている間にゴミ箱のアイコンに新たなクラスを付与し、アニメーションのように実装しております。

流れ

  1. JQuery UI導入
  2. viewにカスタムデータを追記
  3. JQueryにコード記載
    今回は削除機能の実装ですので、jsonなどcontrollerの追記は必要ありません。

1. JQuery UI導入

Gemfile
gem "jquery-rails"
gem "jquery-ui-rails"
application.js
//= require jquery
//= require jquery_ujs
//= require jquery-ui

JQuery UIとはJQueryをベースにしたjavaScriptのライブラリであるため、こちら2つのgemをインストールし、application.jsに追記します。
bundle installをお忘れなく!!

2.viewにカスタムデータ追記

_item.html.haml
.items
  - items.each do |item|
    .item{"data-item_id":"#{item.id}"}
      .item__image
        = image_tag(item.image.to_s) if item.image?
      .item__name
        = item.name

1つ1つのitemにdata属性を付与します。(3行目)
jQueryでイベント発火時にデータを取得出来るようにするためです。

3.JQuery

item_delete.js
$(function(){
  $(document).on("turbolinks:load", function(){
   //itemを動かすようにする ①
    $(".items").sortable({
    });
    // itemがドロップされた時にイベント発火 ②
    $(".trash-box").droppable({
      accept: ".item",
      activeClass: "move-trash",
      drop: function(e, ui){
        e.preventDefault();
        var delete_message = confirm("削除してもよろしいですか?");
        if(delete_message == true){
          //ドロップされたitem要素を取得。jQueryオブジェクトからDOM要素を取り出す
          var delete_item = ui.draggable[0]; ③
          //idを取得。
          var delete_ID = ui.draggable.data("item_id");
          var delete_url = "/items/" + delete_ID;
          $.ajax({ 
            url: delete_url,
            type: "POST",
            data: {id: delete_ID, "_method": "DELETE"},
            dataType: "json"
          })
          .done(function(data){ ⑥
            delete_item.remove();
            location.reload();
          })
          .fail(function(){
            alert("エラー");
          })
        }
      }
    })
  });
});

順を追って説明します。

①各itemを動かせるようにします。
draggableでも良いのですが、無作為に動いてしまうため、要素を並び替えるsortableの方が動きがスムーズだったためsortableを使用しています。

sortableで並び替えた要素の順番を保存する際には、gemのインストールやコントローラーの追記が必要となりますので、今回は割愛させて頂きます。

②ゴミ箱のアイコン(".trash-box")に要素がdropされた時にイベントを発火するようにします。
各オプションについては以下の表をご覧下さい。
コードに沿って説明させて頂くと、
acceptにてitemのdropを受け入れます。itemがdragされている間、trash-box"move-trash"というクラスが追加されるという流れです。

Image from Gyazo

③dropされたitemの要素を取得
jQueryオブジェクトからDOM要素を取り出すため、ui.draggable[0];とします。
delete_itemにdropされたitemのDOM要素が代入されます。

④dropされた要素のidを取得
dropされた時に先ほどviewに追記した、item_idを取得し、削除する時のパスに代入します。

⑤ajaxでcontrollerに送る情報を記入
ここで注意すべきなのは、data: {id: delete_ID, "_method": "DELETE"}です。
今回は削除機能なので、HTTPメソッドをDELETEリクエストと宣言する必要があります!

⑥非同期成功時のイベントを記載
delete_item.remove();で画像をview上から消します。
location.reload();でリロードにて、実装完了です:star:

終わりに

dropされた要素の取得に苦戦しました、、、。
jQuery UI特有のイベントやオプションがあり、リファレンスを何度も読み返し実装しました。

長くなってしまいましたが、最後まで読んで下さりありがとうございました!:relaxed:
他にこうした方が楽だよ!とかあればコメントください!!
以上、初投稿でした!笑

参考文献

https://qiita.com/tsubasaozawa/items/2f4783b47e76dc5a7ed3
http://js.studio-kingdom.com/

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

ドラックアンドドロップで、投稿内容の非同期削除を実装する! [JQuery, Rails]

概要

先日クローゼットアプリ(衣服などの管理をするもの)を作成しました。
ドラックアンドドロップにて投稿内容を削除する機能を実装しました。
該当の箇所に投稿内容をドロップした際に、非同期で削除をするというものです。
備忘録も兼ねて投稿しますので、温かい目で頂けると幸いです:grinning:

こちらが実際の動画です。
demo

画像を左下のゴミ箱のアイコンにドロップした際にイベントを発火させ、非同期で削除しております。
画像がドラッグされている間にゴミ箱のアイコンに新たなクラスを付与し、アニメーションのように実装しております。

流れ

  1. JQuery UI導入
  2. viewにカスタムデータを追記
  3. JQueryにコード記載
    今回は削除機能の実装ですので、jsonなどcontrollerの追記は必要ありません。

1. JQuery UI導入

Gemfile
gem "jquery-rails"
gem "jquery-ui-rails"
application.js
//= require jquery
//= require jquery_ujs
//= require jquery-ui

JQuery UIとはJQueryをベースにしたjavaScriptのライブラリであるため、こちら2つのgemをインストールし、application.jsに追記します。
bundle installをお忘れなく!!

2.viewにカスタムデータ追記

_item.html.haml
.items
  - items.each do |item|
    .item{"data-item_id":"#{item.id}"}
      .item__image
        = image_tag(item.image.to_s) if item.image?
      .item__name
        = item.name

1つ1つのitemにdata属性を付与します。(3行目)
jQueryでイベント発火時にデータを取得出来るようにするためです。

3.JQuery

item_delete.js
$(function(){
  $(document).on("turbolinks:load", function(){
   //itemを動かすようにする ①
    $(".items").sortable({
    });
    // itemがドロップされた時にイベント発火 ②
    $(".trash-box").droppable({
      accept: ".item",
      activeClass: "move-trash",
      drop: function(e, ui){
        e.preventDefault();
        var delete_message = confirm("削除してもよろしいですか?");
        if(delete_message == true){
          //ドロップされたitem要素を取得。jQueryオブジェクトからDOM要素を取り出す
          var delete_item = ui.draggable[0]; ③
          //idを取得。
          var delete_ID = ui.draggable.data("item_id");
          var delete_url = "/items/" + delete_ID;
          $.ajax({ 
            url: delete_url,
            type: "POST",
            data: {id: delete_ID, "_method": "DELETE"},
            dataType: "json"
          })
          .done(function(data){ ⑥
            delete_item.remove();
            location.reload();
          })
          .fail(function(){
            alert("エラー");
          })
        }
      }
    })
  });
});

順を追って説明します。

①各itemを動かせるようにします。
draggableでも良いのですが、無作為に動いてしまうため、要素を並び替えるsortableの方が動きがスムーズだったためsortableを使用しています。

sortableで並び替えた要素の順番を保存する際には、gemのインストールやコントローラーの追記が必要となりますので、今回は割愛させて頂きます。

②ゴミ箱のアイコン(".trash-box")に要素がdropされた時にイベントを発火するようにします。
各オプションについては以下の表をご覧下さい。
コードに沿って説明させて頂くと、
acceptにてitemのdropを受け入れます。itemがdragされている間、trash-box"move-trash"というクラスが追加されるという流れです。

Image from Gyazo

③dropされたitemの要素を取得
jQueryオブジェクトからDOM要素を取り出すため、ui.draggable[0];とします。
delete_itemにdropされたitemのDOM要素が代入されます。

④dropされた要素のidを取得
dropされた時に先ほどviewに追記した、item_idを取得し、削除する時のパスに代入します。

⑤ajaxでcontrollerに送る情報を記入
ここで注意すべきなのは、data: {id: delete_ID, "_method": "DELETE"}です。
今回は削除機能なので、HTTPメソッドをDELETEリクエストと宣言する必要があります!

⑥非同期成功時のイベントを記載
delete_item.remove();で画像をview上から消します。
location.reload();でリロードにて、実装完了です:star:

終わりに

dropされた要素の取得に苦戦しました、、、。
jQuery UI特有のイベントやオプションがあり、リファレンスを何度も読み返し実装しました。

長くなってしまいましたが、最後まで読んで下さりありがとうございました!:relaxed:
他にこうした方が楽だよ!とかあればコメントください!!
以上、初投稿でした!笑

参考文献

https://qiita.com/tsubasaozawa/items/2f4783b47e76dc5a7ed3
http://js.studio-kingdom.com/

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

世の中の1%の人の為になるかもしれないRails tips

はじめに

本記事はZeals Advent Calendar 2019の24日目です。メリークリスマスイブ!

本記事では僕が日々開発しているなかで、どうしてもなんとかしなければいけない…という時に活用したtipsをまとめたものです。
タイトルにある通り、エッジケースすぎるのでもし同じような状況で困っている人の為になれたら幸いです。

環境

Rails 5.2.3
Ruby 2.5.5

tips

といっても今回紹介するのは2つです

  • カラムにaliasをかける
  • caller

カラムにaliasをかける

Railsではcolumnに対してaliasを貼ることができます。

モデル名 カラム名
Button name
Choice label

このようなモデルが2つあるとします。
カラム名はそれぞれ違いますが同じものとして扱いたい時があると思います。
そんな時はalias_attribute が使えます。

class Choice < ActiveRecord::Base
  alias_attribute :name, :label

alias_attributeを使うことでChoiceのlabelnameとして扱うことができるようになります。
ただnameとして扱うことができるようになるだけではなく

Choice.first.name?
=> true

といったActiveRecordの述語メソッドやゲッターやセッターを生成してくれます。
ただ同名として扱いたいだけで、述語メソッドを生成するまでもない場合は以下のようにシンプルに定義するだけで良さそうです。

class Choice < ApplicationRecord
  def name
    label
  end

カラム名を変更したい。変更したほうが良い。でもrename_columnしてる時間ないよぉ…
という時に使いました、便利ですねー

caller

モデル名 カラム名
User cliend_id
モデル名 カラム名
Answer client_id
モデル名 カラム名 カラム名
UserAnswer user_id answer_id

このような中間テーブルがあるとします。
以下はmodel

class UserAnswer < ApplicationRecord
  belongs_to :user
  belongs_to :answer

  validates :should_be_same_client

  def self.import_associations!(users, answer)
    associations = users.map do |user|
      UserAnswer.new(user_id: user.id, answer_id: answer.id)
    end
    UserAnswer.bulk_import! associations, validate: false
  end


  def should_be_same_client
    return user.client == answer.client

    errors[:base] << ' client_id of user and client_id of answer are different'
  end
end

定義されている
import_associations!は引数としてuserの配列と指定のanswer(回答)を受け取ります。

カスタムのvalidationメソッドが定義されており、UserAnswerClientに対してbelongs_toの関係で、client_idが違うものが作成されないようにしています。

should_be_same_clientはUserAnswer単体を生成する時は動いてほしいものですが、bulk importで作成したい時はvalidationをskipしたい。カスタムvalidationも動いてほしくないとします。

Railsのversionは5.2系なので(早く6に上げたい…)bulk importはactiverecord-importを利用します。

bulk_importはoptionでvalidate: falseを渡すことでmodelのvalidationをskipさせることができます。
しかし今回定義したようなカスタムvalidationはskipしてはくれません。
bulk importする件数が数件であれば都度カスタムvalidationが走ったところで問題はないのですが、件数が多くなればなるほどN+1で都度カスタムvalidationが走るにより実行時間がどんどん長くなるので避けなければなりません。

つまりbulk_importを呼び出すメソッドのときのみカスタムvalidationをskipする必要があります。

そこでcallerを使いました。
callerはバックトレースの情報を確認することができるので、どのメソッドから呼び出されたのか,どのメソッドを経由してきたのかを確認することができます

callerで呼び出されたバックトレースを正規表現を使って、メソッド名だけを見れるようにします

caller.map { |c| c[/`([^']*)'/, 1] }
=> ["eval",
 "evaluate_ruby",
 "handle_line",
 "block (2 levels) in eval",
 "catch",
 "block in eval",
 "catch",
 "eval",
 "block in repl",
 "loop",
 "repl",
 "block in start",
 "__with_ownership",
 "with_ownership",
.......

これでどのメソッドを経由してきたのかがわかります。
取得したバックトレースないにvalidateをskipしたいメソッドが含まれているかを確認するメソッドを追加し、

def call_from_import_associations?
  caller.map { |c| c[/`([^']*)'/, 1] }.include?('import_associations!')
end
validates :should_be_same_client, unless: call_from_import_associations?

カスタムvalidationの発火条件として追加し、import_associations!から呼び出されるときのみカスタムvalidationが発火しないようにすることができます。

まとめ

実際にこのユーザーに対してtagを一括でつける機能は使用頻度の高い機能だったので、早急に修正する必要があり、本当になんとかするためにひねり出した苦肉の策ではあります…w

早急に対応しなければいけない。なんとかしなければいけない。という同じような問題に詰まっている人たち、もしくは同じようなケースで困っているの助けに少しでもなっていれば幸いです!

明日はついにAdvent Calendarの最終日です!
最終日は@pannpersです!
それでは良いクリスマスイブを!

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

Youtuber向けにサービス開発したが需要が無かった話

はじめに

プログラミング始めて半年くらいで自分の好きなタイ在住日本人Youtuberに使ってもらいたくて開発した時の話です。結論から言うと上手くいかなかったです。その原因としてリサーチ不足と自己満の開発であったからです。今後サービス開発をする人にとって今回の自分の経験が役立てばいいなと思い、今回のことの経緯をまとめておきます。

結論

・競合サービスのリサーチが大切
・ユーザーにしっかりとリサーチすることも大切
・自己満のサービスになっていないか

何を作ったのか

私は1つのMAP上でYoutuberは撮影した場所にピンをさすことができ、そこをクリックすると詳細情報が見れて最終的にはYoutubeチャンネルに移動する。そして視聴者はYoutuberに撮影して欲しい場所に対してピンを挿してリクエストできる双方向コミュニケーションサービスを開発しました。

難易度

サービス自体はRuby on Railsで開発を行いProgate卒業すれば作れるような簡単なものでした。
MapにはGoogle map APIを使用しました。
そして機能自体もシンプルだったので開発自体はそれほど時間がかかりませんでした。

実際に提案してみて

この時の最大にミスとしては自分が作りたいものを作ってから、これYoutuber向けに改良すれば良いだろうと仮説を立て開発してから売り込んだことです。そのため世界的な大企業が競合となるとも知らずに自信満々にサービスの提案をYoutuberの方に行いました。

結果

・Google MapにあるMy Mapと言う似たような機能使っているが、それとは何が違うか聞かれ答えられなかった。
→正直自分でもGoogle Mapで十分だと感じました。
・Youtuberがこのサービスを「使うメリットが全くなかった。

そこからの改善

せっかく作ったので諦めたくないなと思い、Youtuberの方にヒアリングなどを行い何か改善点はないか探りました。その結果1つだけありました。

それは視聴者の目線に立つことでした。

そのYoutubeチャンネルの視聴者は日本人だけではなく、タイ人の方もおられました。
そのことに関してリサーチを行った結果、Yotuberの方はタイ人視聴者からの撮影して欲しいリクエストが多くあるみたいですが、詳細情報が聞けずに諦めていたようです。さらに視聴者としてもここを動画にして欲しいなどの思いがあったみたいです。ここから私はタイ人視聴者と日本人Yotuberがコミュニケーションできるサービスに方向転換をしました。

最終結果

最終的には良いサービスじゃん!とYoutuberの方に言って頂きました。
そしてリリースに向けて準備していたときに、Youtuberの方が解散をされてしまいました、、、
しかもやりとりをしていた方が脱退という形だったので大打撃でした。
結局その後いろいろありましたが、幻のサービスとして世に出ることはありませんでした。

学び

結果的には失敗だったのですがたくさんの学びがありました。
それはサービスを開発する上でユーザーの立場になって考えるのは大切ということです。
こんな技術を使いたい!これ面白そう!って言うのが理由で開発も素晴らしいです。しかし誰かに使ってもらう前提で作るなら、徹底的にリサーチを行い他に似たサービスがないか調査するのは必要だなと思います。

これは今後開発以外の場面であっても使えそうな考え方かなと思います。

最後に

今回かなり短い文面にはなりましたが最後までみていただきありがとうございます。
インターネットに関して全くの知識がなかった自分がサービスを作れるようになったと考えると感慨深いです。テクノロジーを使えば世の中にとって役に立つことを生み出すことができます。
しかし役に立つものしか生み出してはいけないかと言われると違うと考えてます。
意味のわからない、誰が使うかもわからないサービスであっても誰かの役に立つかもしれません。

今回リサーチは大事や自己満で作るな。どと言っておきながら、
最後に矛盾したことを言いますが楽しかったら全てokです!

メリークリスマス!!

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

Railsチュートリアルを必要コマンドだけをまとめて記しました。第1章

        Railsチュートリアル  
対象者 中級者向け(最低でもRuby, HTML/CSS, Railsの基礎)
何ができるか 本格的なアプリを開発でき、最終的にWeb上に公開できる
開発環境 AWS Cloud9 or ローカル環境

1.2.2 Railsをインストールする

リスト 1.1: バージョンを指定してRailsをインストールする
$ gem install rails -v 5.1.6
リスト 1.3: rails newを実行する (バージョン番号を指定)
$ cd ~/environment
$ rails 5.1.6 new hello_app

cdはchange directly(このディレクトリに移動する)
〜はホームディレクトリ(自分のアカウントのデフォルト)

1.3.1 Bundler

リスト 1.5: Ruby gemごとにバージョンを明示的に指定したGemfile(リスト 1.4のGemfileをリスト 1.5に置き換えます。)
$ c9 open Gemfile
source 'https://rubygems.org'

gem 'rails',        '5.1.6'
gem 'puma',         '3.9.1'
gem 'sass-rails',   '5.0.6'
gem 'uglifier',     '3.2.0'
gem 'coffee-rails', '4.2.2'
gem 'jquery-rails', '4.3.1'
gem 'turbolinks',   '5.0.1'
gem 'jbuilder',     '2.6.4'

group :development, :test do
  gem 'sqlite3',      '1.3.13'
  gem 'byebug', '9.0.6', platform: :mri
end

group :development do
  gem 'web-console',           '3.5.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.0.2'
  gem 'spring-watcher-listen', '2.0.1'
end

# Windows環境ではtzinfo-dataというgemを含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

チュートリアルではRailsを学ぶために勉強しているので、
リスト 1.4: hello_appディレクトリにあるデフォルトのGemfile をリスト 1.5のように固定します。

アプリケーションのGemfileの内容をリスト 1.5で置き換えたら、bundle installを実行してgemをインストール
$ cd hello_app/
$ bundle install

bundle installを実行すると、先程全部のVerを固定するというGemfileの内容を書き換えたので、その固定されたVerだけを取って来てくれます。


Bundler could not find complete versions for gem "activesupport":
In snapshot (Gemfile.lock):

In Gemfile:
rails (=5.1.4) was resolved to 5.1.4, which depends on railties (>= 4.0.0) was resolved to 5.1.6, which depends on activesupport(=5.1.6)
coffee-rails (= 4.2.2) was resolved to 4.2.2, which depends on railties (>= 4.0.0) was resolved to 5.1.6, which depends on activesupport(=5.1.6)

Running 'bundle update' will rebuild your snapshot from scratch, using only the gems in your Gem file, which may resolve the conflict.

ただ実行するとほとんどの場合がうまく動かず、先程あなたはrails 5.1.6を入れたのに5.1.4を入れようとすると依存関係がごちゃごちゃになるから困っちゃった、とbundleから言われます。
「直したいんだったら”bundle update”を実行してください。実行するともう一回Gemfileを一から読み直してくれるので直るんじゃないですか」というのが最後の行に書かれています。

$ bundle update

bundle updateを実行したことにより、全てのgemのVerが揃い、これでrailsが立ち上がります。ここまで実行したことはつまり、リスト1.3のrails newコマンドとリスト1.5のbundle installコマンドを実行したことにより、実際に動かすことのできるアプリケーションが作成されたということです。

1.3.2 rails server

リスト 1.6: Railsサーバーを実行する
$ cd ~/environment/hello_app/
$ rails server

railsサーバーが立ち上がったか確認するために、Preview画面をクリックして、Preview Running Applicationをクリックします。
altアプリケーションをブラウザで開く
altrails serverを実行したときのデフォルトのRailsページ
alt

c9コマンドをインストールするコマンド
$ npm install -g c9

npmはnode.jsに於ける管理ツールでc9はnpmの一環として管理されているので、npmによりc9コマンドが入れられるようになります。

リスト 1.15: クラウドIDE上でHerokuをインストールするコマンド
$ source <(curl -sL https://cdn.learnenough.com/heroku_install)
herokuが正しくインストールできていれば、バージョン番号が表示されるようになります
$ heroku --version
heroku-cli/6.15.5 (linux-x64) node-v9.2.1

1.3.3 Model-View-Controller (MVC)

alt

railsの中にはModel、View、Controllerがあり、上手く作用しています。ソースコードが煩雑にならず、3つそれぞれに切り替えられます。

1.3.4 Hello, world!

リスト 1.7: Applicationコントローラにhelloを追加する
$ c9 open app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  def hello
    render html: "hello, world!"
  end
end

renderは”描画する”という意味です。

リスト 1.9: ルートルーティングを設定する(リスト 1.8から1.9に書き換えます)
$ c9 open config/routes.rb
Rails.application.routes.draw do
  root 'application#hello'
end

routeとrootを区別するため、訳文ではrouteを基本的に「ルーティング」と訳します。「config/routes.rb」をRailsのルーティングファイルと呼びます。
「application#hello」は、Applicationコントローラファイルの中にあるhelloメソッドを呼び出してくださいという意味です。
先程の”Yay! You’re on Rails!”はrootURLで、
したがって、「root 'application#hello'」は、rootURLにアクセスが来たら、Applicationコントローラファイルのhelloメソッドを呼び出してくださいということになります。
リスト 1.9の後、rails serverを実行すると、「hello, world!」と表示されます。

1.4 Gitによるバージョン管理

次は「hello, world!」を開発環境のcloud9じゃなくて本番まで持っていきましょう。本番環境は「heroku」を使いますが、「heroku」という本番環境に自分たちの”hello_app”を載せるためにはGitというソフトウェアが必要になります。
Gitはゲームでいうセーブポイントで、ボス戦が入る前とかにセーブして負けたとしても、ロードしてやり直せる。ような感じです。普通のゲームと違うのはゲームだとセーブポイント10個くらいに収まるが、Gitの場合は1000個とかになリます。

ここからこのGitを使ってセーブやロードができる環境を作る初期化を行うコマンド
$ git init
今Gitに入っていないファイルがこれだけあるよというのが確認できる
$ git status
セーブする対象を定義する
$ git add -A
確認したらこれまで行ったものをセーブする
$ git commit

git commitを実行すると、nanoかVimが出て来ます。
nano から vim に変更するには、こちらの記事を参考に以下のコマンドを実行します。
$ git config --global core.editor 'vim -c "set fenc=utf-8"'
Vimの場合はAなどを押すと、「INSERTモード」になり、後から見て思い出せるようにコメントしておきます。
alt

この後、保存してエディタから出るには、ESCキーを押して、最初のモードに戻り、「:wq」を押します。
「:」はこの後コマンド入力する、「w」はコメントを書き込む、「」はこのエディタから出る、という意味です。

この日にコミットしたコミットlog(これまでのセーブしたプログラム)を見ることができる
$ git log

ただこれらのGitは開発環境の中でしかファイルやセーブデータが見れないため、例えば、アカウントを消してしまうと、このGitのセーブポイントのデータが消えてしまいます。ですので、万が一のために、基本的に作ったGitのレポジトリは自分たちの開発環境とは別のところに置きます。なので、何らかの理由で開発環境が使えなくなったとしても、そのサーバーに残しておけば、みんなで見れてすぐに再現できます。
そういったサーバー(レポジトリ置き場)で有名なのがGitHubです。

1.5 デプロイする

リスト 1.13: 追加や並び替えを行ったGemfile(Gemfileをリスト 1.13に書き換える)
$ c9 open Gemfile
source 'https://rubygems.org'

gem 'rails',        '5.1.6'
gem 'puma',         '3.9.1'
gem 'sass-rails',   '5.0.6'
gem 'uglifier',     '3.2.0'
gem 'coffee-rails', '4.2.2'
gem 'jquery-rails', '4.3.1'
gem 'turbolinks',   '5.0.1'
gem 'jbuilder',     '2.7.0'

group :development, :test do
  gem 'sqlite3', '1.3.13'
  gem 'byebug',  '9.0.6', platform: :mri
end

group :development do
  gem 'web-console',           '3.5.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.0.2'
  gem 'spring-watcher-listen', '2.0.1'
end

group :production do
  gem 'pg', '0.20.0'
end

# Windows環境ではtzinfo-dataというgemを含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

「sqlite3 gem」は開発環境用のDBのGemですが、「heroku」は本番用のサーバーなので、「sqlite3」だとデプロイに失敗してしまいます。なので、「development」では「sqlite3 gem」ですが、「production」では「pg gem」(PostgreSQL)という本番環境用のものを使います。

Gemfileを書き換えた後、bundle installを実行する
$ bundle install
ローカルの場合は、本番環境で使わないGemは入れないようにbundle install --without productionを実行する
$ bundle install --without production

production環境で行うGemは開発環境で使わないからwithoutで外に置いといて、インストールしないようにする

Gitを通してHeroku上にデプロイするため、Gitにコミット(セーブ)する必要があります
$ git add Gemfile Gemfile.lock

先程、bundle installを実行した結果、”$ git status”で確認すると、GemfileとGemfile.lockはmodifiedになっているため、git addで追加します。

またエディタが出てきて面倒なため、「git commit」に「-m(message)」を追加すれば一行で終わります
$ git commit -m "Add pg gem"

git commit -m "Add pg gem"を実行した後、commitしましたというエディタ画面が出ますが、「J」と「K」で上に行ったり、下に行ったりして、「Q」で終了できます。

herokuにログインする
$ heroku login --interactive
パスワード入力をスキップするための秘密鍵
$ heroku keys:add

? Would you like to upload it to Heroku? (Y/n)と出てきたら「Y」を入力します。

本番用のサーバーを作ります
$ heroku create
Creating app... done, ⬢ afternoon-sea-23202
https://afternoon-sea-23202.herokuapp.com/ | https://git.heroku.com/afternoon-sea-23202.git

heroku createを実行すると、2つのURLが出てきて、左側のURLをクリックしてOpenを選択すると開けます。

git remote -vを実行すると、先程作ったサーバーにソースコードを送るためのショートカット(エイリアス)、Herokuができます
$ git remote -v

herokuという送り先を入力すると、サーバーにソースコードが送れます。

今いるmasterというブランチのソースコードをherokuに送る(git push)
$ git push heroku master 

これで先程のURLをリロードすると「hello world!」が表示されます。

1.5.4 Herokuコマンド

アプリケーションの名前を変更
$ heroku rename rails-tutorial-kyotot 

Railsガイド

alt

この記事はYassLabさんの解説動画を参考にさせていただきました。とてもわかりやすく、Railsチュートリアルに心折れそうな方にすごくオススメです!

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

ドロップダウン機能を有効にするため、Railsのapplication.jsファイルを通して、Bootstrapに同梱されているJavaScriptライブラリとjQueryを読み込むようアセットパイプラインに指示

railsチュートリアルで、Accountをクリックしてプルダウンメニュー出すために、少し手こずったのでメモ。

実装させたい機能⬇️
fullsizeoutput_b8c.jpeg
application.jsに2つのライブラリを追加。

application.js
//= require rails-ujs
//= require jquery     ⇦追加
//= require bootstrap   ⇦追加
//= require turbolinks
//= require_tree .

fullsizeoutput_b8b.jpeg
と、Sprockets::FileNotFoundエラーが出る。

Gemfileにgemを追加。

gem 'rails', '~> 5.2.4', '>= 5.2.4.1'
gem 'bcrypt',         '3.1.12'
gem 'bootstrap-sass', '3.3.7'
gem 'jquery-rails', '~> 4.3', '>= 4.3.1'   ⇦追加

追加後bundle installとrails sでサーバー再起動。
fullsizeoutput_b8d.jpeg
無事に、機能実装完了。

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

rails 『アウトプット』

application.scssがおかしいと思い@importの順番を変えたが反映されず名前を変えてもエラーが出なかったためapplication.html.hamlを見たら配置ミスがあった。application.html.hamlはhaml,scss両方を統合しているため確かめる必要がある。

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

[rails]credentials.yml.encにdatabase設定を保存している場合のheroku デプロイ

ローカルの開発環境

データベースのパスワードなどを平文でdatabase.ymlに保存したくないので、
credentials.yml.encにdatabaseのパスワードなどを保存している。

【備忘録】credentials.yml.encにdatabase設定を保存する

heroku

この環境のままherokuにデプロイし、db:migrateするとherokuのdbに接続できず、エラーになる。

$ heroku rake db:migrate
....
rake aborted!
NoMethodError: Cannot load database configuration:
undefined method `[]' for nil:NilClass

このままheroku openすると以下のようなエラーになってApp Crashedになる。

heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=violao.herokuapp.com request_id=bf9aba32-8c1e-4b44-948b-3d302934f41f fwd="XXX.XXX.XXX.XXX" dyno= connect= service= status=503 bytes= protocol=https

解決方法

master.key をherokuの環境変数としてセットする。

$ heroku config:set RAILS_MASTER_KEY=`cat config/master.key`
Setting RAILS_MASTER_KEY and restarting ⬢ app_name... done, v13
RAILS_MASTER_KEY: xxxxxx601433892f64d2c33e69cxxxxxx

↓参考
secrets.ymlや環境変数をRails 5.2のEncrypted Credentialsに移行する

その後 heroku db:migrateでdbのマイグレーションを行うと、エラーが解消し、heroku openでアプリが起動する。

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

railsにてcreated_at の曜日も日本語で表示する方法( lメソッド)

  • 目標①:railsにて、DBからcreated_at の日時を表示(取得)したい。
変数.html.erb
<%= link_to(変数.created_at.strftime('%Y年/%m月/%d日(%a)%H:%M')," リンク先を記入") %>
  • まず試したコード
    (今回は、更にlink_toを使いたい)

    • 「 XXXX年 XX月XX日(mon) XX:XX 」とまで成功。
    • <%= 変数.created_at.strftime('%Y/%m/%d(%a)%H:%M') %> を実行し、曜日が英語標記のままのため苦戦。
    • #{%w(日 月 火 水 木 金 土… 系も試したが、上手くいかない。
    • config/initiallzers/time_formats.rb等も作成し定義を試したが、上手くいかない。

ーーーーーーーーーーーーーーーーーーーーー

<備忘録>
目標② (mon)ではなく、曜日も日本語で表示する簡単な方法

①gem 'i18n_generators' をbundleインストール

②en.ymlと同じ階層ディレクトリにconfig/locales/ja.yml を作成。以下のコマンド実行すると内容が書き換わり大変便利。

rails g i18n_locale ja

③ja.yml 内の

time:

      default: "%Y年%m月%d日(%a) %H時%M分" ←ここを取得したいように自分で変更する

④表示したいhtml.erb内へ lメソッドにて記入。

<%= link_to(l(テーブル名.created_at),"リンク先") %>

リンクではない場合、↓

<%= l テーブル名.created_at %> 

念のため、サーバーを立ち上げ直し、
曜日も (mon)→(月) 日本語になりました。

最終参考

Railsで日時をフォーマットするときはstrftimeよりも、lメソッドを使おう @Junichi Ito

*個人勉強用サイト作成のため、
曜日を表示しないで妥協するか迷いましたが、諦めないで試行錯誤することが大切だと感じています。

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

RUNTEQの講師をやってみてわかった初学者にありがちなパターン20選(後編)

前回の記事の続きです!
RUNTEQの講師をやってみてわかった初学者にありがちなパターン20選(前編)

11. 『フォームとは』が曖昧

Rails云々の以前の話で、フォームがなんなのかの理解が浅いです。Railsから入った人は特にそうなのかもしれません。

  • nameとvalueがペアになってサーバに送られる
  • nameとvalueはinputやselectやradioなどに付与される
  • action属性に指定されたURLに対して入力値が送られる

これを知っているだけでRailsでの開発が一気にスムーズになるはずです。
逆にこれを知らないとform_withなどのフォームヘルパーが何をやっているかブラックボックスすぎて魔法のようにしか感じられないでしょう。

フォームとは要はクライアントとサーバの窓口です。お互いを繋ぐものです。お互いを繋ぐというからにはインターフェース(接点)があるはずです。
ではインターフェースとはなんでしょう。具体例を書きます。

クライアント(フォーム部分)

<input type="text" name="user[email]" value="dyson@example.com">

サーバ(コントローラ)

params.require(:user).permit(:email)

インターフェースはどこでしょう?
・・・
・・

はい!
name="user[email]"user部分がrequire(:user):user部分に対応します。
name="user[email]"email部分がpermit(:email):email部分に対応します。

これがインターフェースです。
訳も分からずrequire(:user)permit(:email)を使ってませんか?

ぜひインターフェースを意識する癖をつけていただきたいです。

12. フォームヘルパーが魔法に感じる

【11. 『フォームとは』が曖昧】に関連する話です。フォームとはなんぞやがわかってないとform_withなどのフォームヘルパーが魔法に感じます。『何をやってくれてるのかよくわからないけどユーザー新規登録できた!ログインできた!』となりがちです。

フォームについて再掲します。

  • nameとvalueがペアになってサーバに送られる
  • nameとvalueはinputやselectやradioなどに付与される
  • action属性に指定されたURLに対して入力値が送られる

form_withはこれらをよしなに設定した上でフォームを生成してくれてるに過ぎません。

= form_with model: @user do |f|
  = f.label :email 
  = f.email_field :email

このようにいちいちname属性やらaction属性やらを書かなくても「よしなに」設定してくれます。
余談ですがこの「よしなに」力があり過ぎるが故にRailsは初学者向きではないという意見もあって、確かになーという感じです。

13. 実際に出力されているHTMLを見ない

これも【11. 『フォームとは』が曖昧】【12. フォームヘルパーが魔法に感じる】に繋がる話ですが、何かうまくいかないときにビューテンプレート(new.html.erbやedit.html.erbなど)とにらめっこする人が多いです。

結局ブラウザが解釈してるのはそのビューテンプレートを元に生成されたHTMLです。なので『このform_withの書き方どっかおかしいのかな?』とビューテンプレートと対峙していてもらちがあきません。まずはブラウザの開発者ツールからHTMLソースをみましょう。そしてname属性には何が設定されているか、action属性には何が設定されているか、etcを確認しましょう。

14. 何か機能を作るときにER図を書かずに実装を始める

こういう人が案外多いなと感じました。
実際には書きださなくても良いですが、少なくとも頭の中にER図が浮かんでないと実装は不可能です。

ある機能を実現したい場合、それをRDBではどう表現するかを考えるべきです。慣れないうちはER図を書き出すと良いでしょう。

15. テーブル設計

【14. 何か機能を作るときにER図を書かずに実装を始める】の問題について、テーブル設計の引き出しがまだ足りてないのでER図を書こうにも書けないのだと推測します。これは色んなパターンに出会うしかないでしょう。

これ、結構おもしろいです。テーブル設計のプロセスを学べる実践的な内容になってます。
楽々ERDレッスン

16. 何か機能を作るときにエンドポイントを意識せずに実装を始める

エンドポイントを意識せず実装を始める人が一定数いました。

例えばユーザーの新規作成・ログイン・ログアウトの機能を作るとします。
実装前にこんな表を自分で作ってみると良いでしょう。

Image from Gyazo

まず「やりたいこと」を書き出していって、それぞれどういうエンドポイントにすべきかを記入していきます。あとはそれに沿ってルーティングやコントローラを作っていけばOKです。

その際、RESTfulなルーティングを心がけましょう。基本的に「index new create edit update show destroy」の7つのアクションで仕様を満たせないか考えてみましょう。

17. レンダーとリダイレクトを混同しがち

  • レンダーは特定のビューテンプレートを利用してレスポンスをクライアントに返す
  • リダイレクトは再度サーバサイドにリクエストを投げている

英語の意味を考えるとわかりやすいかも。

render・・・描写する
redirect・・・re + direct = 再び、道を教える

このサイトの図がわかりやすいです。
render と redirect の違い

18. マイグレーションの理解が浅い

マイグレーションは、データベーススキーマの継続的な変更 (英語) を、統一的かつ簡単に行なうための便利な手法です。マイグレーションではRubyのDSLを使っているので、生のSQLを作成する必要がなく、スキーマとスキーマへの変更をデータベースの種類に依存せずに済みます。

1つ1つのマイグレーションは、データベースの新しい'version'とみなすことができます。スキーマは最初空の状態から始まり、マイグレーションによる変更が加わるたびにテーブル、カラム、エントリが追加または削除されます。Active Recordは時系列に沿ってスキーマを更新する方法を知っているので、履歴のどの時点からでも最新バージョンのスキーマに更新することができます。Active Recordはdb/schema.rbファイルを更新し、データベースの最新の構造と一致するようにします。

Railsガイド マイグレーションの概要

  • マイグレーションファイルを作ったタイミングでDBに反映される
  • 『やっぱりstring型からtext型に変更しよう!』と考えた時に、マイグレーションファイルを直接変更すればDBに反映される

という勘違いが案外多いです。

マイグレーションファイルはあくまでも「データベーススキーマの継続的な変更を、統一的かつ簡単に行なうための便利な手法」です。データベーススキーマを更新するにはSQLを発行する以外方法はありません。そのためにはrails db:migrateを実行する必要がありますよね。
ただ、一度実行したマイグレーションファイルは再度実行されないので一旦ロールバックするなどして未実行の状態に戻してあげないといけなかったりします。その辺の理解を深められると良いなーと思いました。

19. パラメータを理解してなさげ

コントローラでよく使うparams。こいつがなんなのかのイメージがついてなさげです。すごいざっくり言うとクライアントから送られてきたデータです。

<input type="text" name="email" value="abc@example.com">

という入力項目があってこれをサーバサイドに送ると

def create
  p params[:email] # => abc@example.comが取ってこれる
  # 省略...
end

こんな感じでparams[:email]でユーザーの入力値が取ってこれます。

【11. 『フォームとは』が曖昧】で書いた『インターフェース(接点)』にも密接に関わってくるところですね。

Webサービスは所詮クライアントから何か値を渡してサーバサイドはそれを受け取って何か処理してレスポンスを返すをやっているに過ぎません。送られてきたパラメータを使って何をするかがRuby(Rails)のお仕事です。このparamsの中身を入力値としてUser.create(email: params[:email])のようにcreateやnewメソッドの引数に渡しています。

paramsとはユーザーから送られてきた値が入ってるもの。これを使ってサーバサイドでごにょごにょお仕事をする
これを覚えておいてください。

20. database.ymlを意識しない人が多い。

RailsチュートリアルではSQLiteを使っているためか、database.ymlを意識しない人が多いです。

RUNTEQではMySQLを使ってローカルに環境を作ってもらってます。
こんな構成になってます。

名称未設定ファイル.png

SQLiteを使っているとデータベースサーバへのログインなんて考えませんが、実際はMySQLなりPostgresqlなりのサーバがあってそこにログインする必要があります。また、そのデータベースサーバの中でどのデータベースを使用するかを指定する必要もあります。そういった情報が書かれているのがdatabase.ymlです。

例)

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root # こことか
  password: password # こことか
  socket: /tmp/mysql.sock

development:
  <<: *default
  database: sample_app_development # こことか

こういうところを意識しないと環境構築で詰まる可能性大ですし、詰まった時に解決できないです。

まとめ

以上、ここ数ヶ月初学者に教えてきて思った『初学者ありがちパターン』でした!!
ちょっとでもRails初学者の方の助けになればと思います!

明日はとうとうアドベントカレンダーラストです!
弊社社長ひさじゅによる『なぜ今RUNTEQをやるのか』で締めます!乞うご期待!!

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

【Rails】アソシエーションを独自メソッドで定義した時の「source:」の記述注意点

この記事の対象者

  • Railsでアプリケーションを作り始めたばかりの人
  • アソシエーションを学んだばかりのプログラミング初心者

伝えたいこと

中間テーブルの外部キーを独自カラムで設定したい時、「source: ○○」を中間テーブルの「belongs_to: ○○」と一致させる必要がある。

テーブル設計

スクリーンショット 2019-12-24 11.09.39.png

多対多のテーブルを繋ぐ中間テーブルの外部キーを独自カラムに設定したい時ありますよね。

今回は、UserテーブルのID(主キー)をManagementテーブル(中間テーブル)の「participant_id」という独自カラムの外部キーで設定するパターンで考えていきます。(通常は「user_id」で定義する)

※UserとEventテーブルもアソシエーションしてますが、今回のテーマに関係ないので割愛しています。

User Management Event
id id id
name event_id(FK) title
email participant_id(FK) content

<参考>Railsで外部キー制約のついたカラムを作る時のmigrationの書き方

model.rbの記述

user.rb
has_many :managements, foreign_key: :participant_id, dependent: :destroy
management.rb
belongs_to :participant, foreign_key: :participant_id, class_name: 'User'
event.rb
has_many :managements, dependent: :destroy
has_many :participant_users, through: :managements, source: :participant

注目すべきは、「source: :participant」です。

「through:」を使い中間テーブルを経由して、Userモデルのレコードを取り出すことが可能になります。

ここで、「source:」の記載はレコード情報元であるモデル名(今回の場合はuser)だと勘違いしていましたが、実は「has_many」や「belongs_to」で定義したメソッド名が入ります。

なので、management.rb(中間テーブル)で定義した「belongs_to :participant」が「source: :participant」に繋がってくるのです。

初心者のうちは、中間テーブルの外部キーを「user_id」で定義し「belongs_to :user」と記載することが多く、「source: user」がテーブル名と一致するので勘違いしやすい部分かと思います。

最後に

Qiita初投稿 + プログラミング初心者なので、記述に誤りがある場合もあります。
ご意見があれば編集リクエスト、よろしくお願いします!

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

Railsにマークダウン記法を取り入れる方法

結論

1,2,3を導入すればいい

1. gem 'redcarpet'

2.

module MarkdownHelper
  def markdown(text)
    options = {
      filter_html:     true,
      hard_wrap:       true,
      space_after_headers: true,
      with_toc_data: true
    }

    extensions = {
      autolink:           true,
      no_intra_emphasis:  true,
      fenced_code_blocks: true,
      tables:             true
    }

    renderer = Redcarpet::Render::HTML.new(options)
    markdown = Redcarpet::Markdown.new(renderer, extensions)
    markdown.render(text).html_safe
  end

end

3 .

<%= markdown(@article.body).html_safe %>

参考
https://musicamusik.hatenablog.com/entry/2018/06/09/181439

エラーが出た場合

gemlockを確認するとgem 'redcarpet'がバージョン指定されている可能性があります。自分の場合、gemlockのバージョン指定を削除してbundle updateすることで解決しました。

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

Rails6 each文にてeachがメソッドとして扱われて困った話

目的

  • each文を使用してカラム内容を順に出力したい時にeachがメソッドとして扱われてしまって困ったときの解決法をまとめる

結論

  • each文で繰り返し処理の対象の変数に格納されている値を配列状態で格納する。

エラーの内容とエラー時のコード

  • 当該ページは@posts.contentに格納されている内容をeach文で次々渡し、表示する処理がある。
  • 当該ページへアクセスしたところ下記のエラーが発生した。

    undefined method `each' for #<Post:0x00007fac1ebd28e8>
    
  • エラーに関係するルーティングファイルの内容を記載する。

    get "posts/index/:id" => "posts#index"
    
  • エラーに関係するpostsコントローラファイルの内容を記載する。

    def index
      @posts = Post.find_by(id: params[:id])
    end
    
  • エラーに関係するindexビューファイルの内容を記載する。

    <% @posts.each do |post| %>
      <%= post.content %>
      <%= link_to("詳細", "/posts/#{post.id}") %>
    <% end %>
    

解決方法

  • 修正を行なったコードのみ下記に記載する。

    def index
      @posts = Post.where(id: params[:id])
    end
    
  • find_byメソッドは一致した最初のものを一つだけ返す。

  • whereメソッドは一致した全てのものを返す。

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

Webサイト編 - 5日ぐらいでいい感じのWebサイトとポートフォリオを作る

1 ~ 2日目 - (Webサイト) デザインの選定と作成

  • 1) サイトの名前を決める & ドメイン取得
  • 2) ディレクトリマップの作成
  • 3) ペーパープロトの作成
  • 4) CSSフレームワークの選択
  • 5) MySQLWorkBenchで簡易的なデータベース設計
  • 6) Webデザインの作成(AdobeXD)

(1) サイトの名前を決める & ドメイン取得

ドメイン取得を視野に入れたサイトの名前は決めは結構難しいです!

とりあえず助けてくれそうなWebサイト達を使います。

今回はストレージに関するWebサイトなのでStorageは入れたいです。

一つの単語だけだと99%既に使われていたり、高額で売りつけられるのでもう一つぐらい単語を付け足したいのでGB(ギガバイト)をsurffixに付けたStorageGBにしました!正直5日前後で作るレベルのサイトなのでその程度で大丈夫かと思われます。ではとりあえず、namech_kで調べます。ドメイン名は大丈夫ですがTwitterで既に使われていました。試しにプロフィールに飛んだら凍結だったので少し不安材料が残ります。※今回はサイト名のTwitterアカウントは作りません。個人で代用

ではドメインを取得しましょう。お馴染みの お名前.comで買います。

他の候補:

今回は 出来るだけ低予算でサイトを作ると言うのも含まれているので .com ドメインは取得しない方針です。100円以下で取得できるドメインを探します。ポートフォリオの分も合わせてなので二つ取得します。

ヒットしました!StorageGBの方はxyzで良いかなと考えていましたが .spaceドメインが100円以下であったのでそちらにしました。ダイコンの方は一つの単語だけだったので空きと料金が心配でしたが、Webサイトのドメイン(storagegb.space)を購入した後に、メールにクーポンが届いてたので無料でした。

※ 初期費用が安いドメインは2年目に更新する際に値段が高くなるので注意
※ 買った時に気づいたんですが、上記の価格は税抜きの価格なので 買った時の値段は 98円しました。

お支払いと共にドメイン登録が完了いたしますが、ICANNのWHOIS情報正確性確認方針に基づき、登録完了と共にご登録いただいたメールアドレスの有効性を確認するメールが送信されます。送信後2週間以内にメールに記載されたURLからの認証が確認できない場合、当該ドメインを利用したホームページの閲覧や、メール送受信ができなくなりますので、必ずお手続きをお願いいたします。※jpドメイン等のccTLDは対象外となります。

との事なので、きちんと認証手続きをメールから済ませられれば完了です。
後は、自動契約更新がONになっているので留意しておいてください。
このタイミングとかでTwitter等のSNSアカウントも作ってしまうと良いかもしれません。

(2) ディレクトリマップ の作成

ペーパープロトを書く前にディレクトリマップを作成して、それからペーパープロトに取り組んだ方が捗る気がするので
簡易的なディレクトリマップを作成していきます。

テキストエディターで適当に作成してみます。

TOP
|__ Pages
| |__ About (このサイトについて)
| |__ Contact (お問い合わせフォーム)
|__ Error Pages
| |__ 404 (無効なURLが入力されたら)
| |__ 501 (サーバーダウン)
|
|__ Increase/Decrease (増やす/減らす)
|  |__ Item (Dropbox(Increase), ImageOptim(Decrease) etc...)
|    |__ SNSシェア (Twitter etc...)
| 
|__ Platform (Mac, Windows, Linux)
| |__ Category (Web, Tool, iPhone, Android etc...)
|
|__ Admin Pages (管理画面)

最初はこんな感じと予想しましたが、最終的には2~3倍ぐらいになってしまいました。

もっと視覚的に作成したい時には

上記のツールを個人的には使用してます。

(3) ペーパープロトの作成

先ほど作成したディレクトリマップを元に「無地のA4(100均で買った)」に大雑把に書いて行きます。
画像を載せたいのですが、Qittaのサーバーが圧迫してしまうので、今回は割愛して行きます。

(4) CSSフレームワークを選択

今回は、GitHubのトピックから人気順に調べて行きます。

条件

  • いい意味で、Bootstrapっぽくない (個人的な美的感覚に左右される)
  • ドキュメントが完備されている
  • テンプレートファイルやスニペットがある (コピペ要素があるか)

新しいやつを使おうか迷いましたが、結局はBootstrapの親戚の使い慣れている

Bulmaに決定しました。(結局は)

(5) MySQLWorkBenchで簡易的なデータベース設計

ER図が欲しいだけなので紙に書いても他のツールでも良いのですが、とりあえずこやつが無料で使えるのでそうします。

ダウンロード先: MySQLWorkBench

Brew Caskなら以下のコマンドでダウンロードできます。

$ > brew cask install mysqlworkbench

(6) Webデザインの作成

今回は使い慣れているAdobe XDを使用します。

似たアプリ:

と思いきや、時間がないし個人開発なので割愛します。
ペーパープロトとBulma / Documentationを見比べながらコーティングして行きましょう。
ちなみに、ロゴはAdobe XDで30秒ぐらいで作りました。(四角形二つです)

2 ~ 5日目 - (Webサイト) コーティング

ここでは基本的なCRUD/MVC操作については省いております。

とりあえず要点だけを書き出してみます。

  • 1) デプロイ先を決める (Heroku, AWS 等)
  • 2) Rails new
  • 3) Gemfileの編集 / 設定
  • 4) 怒涛の yarn add
  • 5) application.rb の設定
  • 6) Modelの作成
  • 7) Viewの作成
  • 8) Controllerの作成 - ここは割愛

(6) ~ (8) を完成するまで繰り返して、それが終わったら、最終調整に入ります。

(1) デプロイ先を決める (Heroku, AWS 等)

今回は時間がないので使い慣れている、Heroku に決定。

他の候補:

(2) Rails new StorageGB

今回は自前で作ったTemplateがあるのですが、それは使わずにやりたいと思います。

Templateを使わないときにはいつも下記の二大問題が発生します。

  • Turbolinksを使うか使わないか
  • MinitestかRspecどっちを使うんだ
  • データベース(Mysql, Postgresql)をdevelopment環境から入れるか入れないか問題
  • その他色々

今回は

  • Turbolinks使います!
  • Rspec使います!
  • 今回はHerokuにデプロイ予定なので、dev/testはsqliteでproductionはpsqlを使います!
> $ \> rails new StorageGB -T -B

-T → testディレクトリを生成しない
-B → bundle install をしない

(3) Gemfileの編集 / 設定

個人的にいつも使うGemを入れます!

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.3'

gem 'rails', '5.2.3'
gem 'turbolinks', '~> 5'
gem 'sass-rails', '~> 5.0'
gem 'coffee-rails', '~> 4.2'
gem 'uglifier', '>= 1.3.0'
gem 'puma', '~> 3.11'
gem 'jbuilder', '~> 2.5'
gem 'bootsnap', '>= 1.1.0', require: false
# SEO
gem 'meta-tags'
# HTML
gem 'hamlit'
gem 'hamlit-rails'
# 環境変数
gem 'figaro'
# URL
gem 'friendly_id', '~> 5.2.4'
# Pagination
gem 'pagy', '~> 3.5'
# I18n
gem 'rails-i18n', '~> 5.1'
# 通貨
gem 'money-rails', '~>1.12'
# Form
gem 'simple_form'
# パフォーマンス測定
gem 'flamegraph'
gem 'stackprof'
# Counter Cache の上位互換
gem 'counter_culture', '~> 2.0'
# Image
gem 'carrierwave'
gem "mini_magick"
# Storage Service
gem 'cloudinary'
# 管理画面
gem 'devise'
gem 'activeadmin'
# テストデータのシード
gem 'seed-fu', '~> 2.3'

group :development, :test do
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  # コマンドライン
  gem 'awesome_print', require: 'ap'
  gem 'pry-rails'
  # N+1 対策
  gem 'bullet'
end

group :development do
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  # 自動リローダー (Web, テスト)
  gem 'guard'
  gem 'guard-livereload', require: false
  # エラーページ
  gem 'better_errors'
  gem 'binding_of_caller'
  # モデルにスキームを追加
  gem 'annotate'
  # Heroku
  gem 'sqlite3'
end

group :production do
  # Heroku
  gem 'pg'
end

group :test do
  # Pry Rails
  gem 'pry-byebug'
end

# NOTE:
# Be sure to require rack_mini_profiler below
# the pg and mysql gems in your Gemfile. rack_mini_profiler will
# identify these gems if they are loaded to insert instrumentation.
# If included too early no SQL will show up.

# Profiling
gem 'rack-mini-profiler', require: false

gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

# でコメントされてるところがカスタムしているところです。
今回は、テストを書かないでWebサイトを作るため
いつも使っているRspec関連のGemは入れていません。
今回、作成し終わった時にテストを書いといた方が良かった場面がいくつかあったので後悔しました。。。

とりあえず箇条書きで、上から順に設定コマンドを書いておきます!

Hamlit

テンプレートをerbからhaml拡張子に変更するgemです。

初期のviewsはerbなのでそれをまずコンバートしていきたいのでコマンドをターミナルに打ちます。

$ > bundle exec rake hamlit:erb2haml

html2haml gem is not part of the bundle.
`rake hamlit:erb2haml` requires html2haml gem to convert erb files.
Please add html2haml gem temporarily and run `rake hamlit:erb2haml` again.
(You can remove html2haml gem after the conversion.)

と怒られるのでGemfileに gem 'html2haml' を入れてから

$ > bundle exec rake hamlit:erb2haml

そしたら gem 'html2haml' はいらないので消しましょう。

後は、hamlテンプレートを使用する事を宣言しておきます。

config/application.rb
config.generators do |g|
  g.template_engine :haml
end

html to haml にしたい時には他にも色々手段はありますが、下記のWebサイトで手動変換してやります。

Figaro

環境変数設定にはFigaroを使います。

$ > bundle exec figaro install

config/application.yml が生成されるので、そこで色々設定します。

config/application.yml
WEBSITE_URL: 'https://storagegb.space'
$ > rails console
[1] pry(main) > ENV['WEBSITE_URL']
"https://storagegb.space"

ENV['WEBSITE_URL'] みたいな形で参照します。

Pry / AwesomePrint

irb兄貴ことpryの基本的な設定です。

$ > touch .pryrc
require "awesome_print"
AwesomePrint.pry!

Bullet

N+1 の警告を出力してくれるGemです。

config/development.rb
config.after_initialize do
  Bullet.enable = true
  Bullet.alert = true
  Bullet.bullet_logger = true
  Bullet.console = true
  Bullet.rails_logger = true
end

Guard / Livereload

html css js ファイルの更新を感知して自動でブラウザを更新してくれるやつ。

$ > guard init

Annotate

モデルファイルの上部か下部にスキームの情報を書き込んでくれます。

$ > rails g annotate:install

Simple Form

フォームの扱いを簡単にする。Bulma と互換性がある。設定は割愛。

$ > rails g simple_form:install --bulma

Friendly ID

フレンドリーなURLを生成してくれる。

サンプルコード

user.rb
class User < ApplicationRecord
  extend FriendlyId
  friendly_id :name, use: :slugged
end
users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.friendly.find(params[:id])
  end
end

slugの再生成

$ > User.find_each(&:save) 

Friendly ID

複数の通貨を使用するため使用。ドルを円に、円をドルにする力を持つ超能力者

$ > rails g money_rails:initializer

デフォルトで、日本円を使用するのに設定

config/initializers/money.rb
MoneyRails.configure do |config|

  # To set the default currency
  #
  config.default_currency = :jpy
app/models/xxx.rb
class XXX < ApplicationRecord
  monetize :price_cents, with_model_currency: :currency, as: 'price'
  ...
db/init_db_xxxxx.rb
create_table :xxx do |t|
  t.integer :price_cents, null: false, default: 0
  t.string  :currency,    null: false, defualt: '', limit: 20

price_cents には最小限の単位(日本なら1円単位、ドルならセント)で入れなくてはいけないので
to_cents とかを作って、「\$5 / USD」が入力されたら、「500 / USD」で保存するようにします。

db/fixrues/xx_xxx.rb
def to_cents(price_cents)
  (100 * price_cents.to_r).to_i
end

def normalize_price_cents(price_cents, currency)
  if %w(USD EUR).include? currency
    to_cents(price_cents)
  else
    price_cents
  end
end

CSV.foreach(file, options) do |row|
  @row = row
  ...
  price_cents = normalize_price_cents(@row[:price_cents], currency)

  ItemPlanPrice.seed(
    price_cents:  price_cents,
    currency:     currency,
    ...

app/views/xxx.haml
= humanized_money_with_symbol(obj.price)

(例)
-> $7.99
-> $74.62
-> 10,000
-> $4,499.25

こんな感じで使用します。

CarrierWave & Minimagick

今回は主に、Cloudinaryと一緒に使います。
Development環境では MiniMagick
Production環境では Cloudinary という風に使い分けます。
Cloudinaryの設定は yarn で lazyload を追加する時に設定します。

$ > rails generate uploader cloudinary_logo

production と development で分けずに
include Cloudinary::CarrierWave を使用し続けてると
Cloudinary の無料枠を使い切ってしまうので、オススメできません。(テストデータを何回も上げ直したり)

app/uploaders/cloudinary_logo_uploader.rb
class CloudinaryLogoUploader < CarrierWave::Uploader::Base
  STANDARD_SIZE  = [128, 128]
  THUMBNAIL_SIZE = [64, 64]

  if Rails.env.production?
    include Cloudinary::CarrierWave
  else
    include CarrierWave::MiniMagick
  end

  ...

end

(5) application.rb の設定

次にconfig/application.rbを設定したいと思います!開発がスムーズになるように初期のうちから色々設定します。

  • /lib ディレクトリをオートロードする
  • I18nとTimeZoneの設定
  • generatorsの設定
module StorageGB
  class Application < Rails::Application
  ...
  # /lib ディレクトリー内のファイルをオートロードする
  config.autoload_paths += %W(#{config.root}/lib/)
  config.paths.add 'lib', eager_load: true
  # I18n & Timezone
  config.i18n.available_locales = %i(ja en)
  config.time_zone = 'Tokyo'
  config.active_record.default_timezone = :local
  config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
  config.i18n.default_locale = :ja

  config.generators do |g|
    g.assets false             # rails g controller の時に js/scss を生成しない 
    g.helper false             # rails g controller の時に helper を生成しない 
    g.skip_routes true         # rails g controller の時に routes.rb に追加しない 
    g.stylesheet_engine :scss  # css は scss を使用する
    g.template_engine  :haml   # html は haml を使用する
  end

(6) Modelの作成

$ > rails g model モデル名 カラム名

を開発段階でしてると、途中でデータベースを変更したくなる時にファイルが乱立してしまうのでmigrationファイルを最初に作成してそこに全部まとめたいと思います。ファイル名はなんでもいいです。

$ > rails g migration init_db

MysqlBenchWorkのER図(データベース)を作ったはずなので、それに倣って作成したいと思います。

モデルファイルを手動でapp/models/ 配下に作成したら
以下のコマンドででデータベースを再生成して反映させます。

$ > rails db:migrate:reset

基本的には init_db ファイルを編集して
db:migrate:reset でデータベースに反映させて、の繰り返しをして開発して行きます。

(7) Viewの作成

全部書く必要はないと思うので箇条書きで流れだけ書いておきます。

  • CSS フレームワークをダウンロード
  • application.css の編集
  • application.html の編集
  • application.htmlで使うpartialの作成(header, footer etc...)
  • ディレクトリマップに基づいてconfig/routes.rbを編集
  • 色々なGemを設定(Pagination 等...)

CSS フレームワークをダウンロード

$ > yarn add bulma
$ > mkdir -p vendor/assets/stylesheets/bulma
$ > mv node_modules/bulma/bulma.sass vendor/assets/stylesheets/bulma/base.sass
$ > mv node_modules/bulma/sass  vendor/assets/stylesheets/bulma/sass

$ > which sass-convert
/Users/xxx/.rbenv/shims/sass-convert

$ (1) > sass-convert -R vendor/assets/stylesheets -F sass -T scss 
...
convert xxx.sass
create xxx.scss 

$ (2) > find vendor/assets/stylesheets -type f -name '*.sass' -delete

$ (3) > grep -lr '.sass' vendor/assets/stylesheets | xargs sed -i '' -e 's/.sass//g'

Railsでは「scss」を使用しているけど
Bulmaでは「sass」ファイルしかないので、sass を scss に変換する必要があります。

(1) vendor/assets/stylesheets 内にある sass ファイルを scss に変換して
(2) xxx.sass ファイルがまだ残っているので、一括で消去しています。
(3) ファイルの中身に @import '***.sass' みたいな形で .sass が邪魔をしているので
それを、一括置換する形で消去すればエラーを起こさずにページが表示されるかと思います。

Bulmaをカスタムしたいときは下記のリンクを参考にすると良いかもしれないです。

application.css の編集

Bulma のセッティング

$ > mv app/assets/application.css app/assets/application.scss 
app/assets/application.scss
@import 'bulma/base';

TOPページの作成

とりあえずページを確認したいので

$ > rails g controller pages index
config/routes.rb
root 'pages#index'

を追加して、サーバーを立ち上げます。

$ > rails server

Developer Tool(Chrome)を開いて

Network > CSS

でBulmaのcssファイルが適切に読み込まれているかを確認できたら、次に進みます。

application.htmlの編集

初期のHTMLはスマホ対応のコードすら入っていないのでとりあえずカスタムします。

最低限Bulmaを動かすにはスマホ対応のコードだけ入れればいいのでそれを入れます。

app/views/application.haml
%head
  %meta{content: "width=device-width, initial-scale=1", name: "viewport"}

Metaタグとかは「最終調整」の時に調節したいと思います。

Bulmaで使うアイコンを選ぶ

Bulmaは以下のリストのアイコンと互換性があるのでその中から一つ選びます。(好きなやつ)

今回はIoniconsを使います!

Bulmaのドキュメンテーション:

基本的にDevelopment環境ではIoniconsやFontawesomeから提供されるCDN(もしくはGem)を使用して開発をして
本番環境では使う分だけのsvg画像をダウンロードしていきます!(今回は3つぐらいしか使いませんでした)

Ioniconsファイルの読み込み

app/views/application.haml
%body
  = yield
  %script{:src => "https://unpkg.com/ionicons@4.5.10-0/dist/ionicons.js"}

それかこう

%body
  = yield
  - if Rails.env.development?
    %script{:src => "https://unpkg.com/ionicons@4.5.10-0/dist/ionicons.js"}

application.hamlで使うpartialの作成(header, footer etc...)

$ > mkdir app/views/shared
$ > touch app/views/shared/_header.haml
$ > touch app/views/shared/_footer.haml
$ > touch app/views/shared/_flash.haml

詳しい内容は割愛しますが _flash.haml には

[Documentation] Notification

app/views/application.haml
= render partial: 'shared/flash' if flash.present?
app/views/shared/_flash.haml
#notification= notification_block(flash)
$ > rails g helper notification
app/helpers/notification_helper.rb
def notification_block(flash_obj)
  # 左に、xxx_controllerで指定した flash[:xxx] が入り
  # (例) flash[:contact] = 'text'
  # 右で、色を指定している。
  colors = {
    info:    :info,
    contact: :info,
    report:  :info,
    success: :primary,
    warning: :warning,
  }
  flash = flash_obj.to_h
  color = colors[flash.keys.first.to_sym]
  text  = flash.values.first

  content_tag(:div, class: "notification is-#{color}") do
    concat button_tag class: 'delete'
    concat text
  end
end

Notificationのボタンを押したら消すようにしたいのでJSコードを追加します。

$ > touch app/assets/javascripts/initialize/bulma.js
app/assets/javascripts/initialize/bulma.js
document.addEventListener('turbolinks:load', () => {

  // Notification Delete Button
  (document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
    $notification = $delete.closest('#notification');
    $delete.addEventListener('click', () => {
      $notification.remove();
    });
  });

});

ファイルのパスを追加したら完了です。

app/assets/javascripts/application.js
//= require initialize/bulma

(4) 怒涛の yarn add

今回は文章の読みやすさを踏まえてGemを設定し終わった後に、この項目を入れました。(本当は開発中なので、もうちょい先)

とりあえず、今回使うJavascriptのライブラリーをyarn addで追加します。

怒涛と言っても二つだけです。

Lazyload の設定

$ > yarn add lazyload
$ > mkdir -p vendor/assets/javascripts/lazyload
$ > cp node_modules/lazyload/lazyload.js $_
$ > touch app/assets/javascripts/initialize/lazyload.js

app/assets/javascripts/application.js

...
//= require initialize/lazyload

app/assets/javascripts/initialize/lazyload.js

//= require lazyload/lazyload

document.addEventListener('turbolinks:load', function(){
  lazyload();
});

production環境でのみ Cloudinary を使いたいので、imageヘルパーを生成してそこに適当に処理を書いてます。

$ > rails g helper image

app/models/logo.rb

class Logo < ApplicationRecord
  mount_uploader :image, CloudinaryLogoUploader
  ...

Logoモデルにimageカラムでマウントをしてあるので、objにはlogoが入る予定です。

app/helpers/image_helper.rb

module ImageHelper
  ...

  def lazy_cl_image_tag(obj, version: :thumbnail)
    placeholder = asset_path('logo/storagegb.svg')

    if Rails.env.production?
      # Cloudinary && Lazyload
      width, height = *CloudinaryLogoUploader.const_get("#{version.to_s.upcase}_SIZE")
      options = [
        { width: width, height: height, crop: 'fit'},
        { quality: 'auto:low', fetch_format: :auto}
      ]
      image_tag placeholder, data: {
          src: cl_image_path(obj.image.public_id, transformation: options)
        }, class: 'lazyload'
    else
      # CarrierWave & Lazyload
      image_tag placeholder, data: {
        src: obj.image_url(version)
      }, class: 'lazyload'
    end
  rescue
    # Fallback
    image_tag placeholder, data: { src: placeholder }, class: 'lazyload'
  end

app/uploaders/cloudinary_logo_uploader.rb

class CloudinaryLogoUploader < CarrierWave::Uploader::Base
  STANDARD_SIZE  = [128, 128]
  THUMBNAIL_SIZE = [64, 64]
  ...

これでdevelopment環境とproduction環境でlazyloadが動くと思います。

参考リンク:

Tablesortの設定

$ > yarn add tablesort
$ > mkdir -p vendor/assets/javascripts/tablesort
$ > cp node_modules/tablesort/src/tablesort.js node_modules/tablesort/src/tablesort/sorts/tablesort.number.js $_
$ > touch app/assets/javascripts/initialize/tablesort.js
//= require tablesort/tablesort
//= require tablesort/sorts/tablesort.number

document.addEventListener('turbolinks:load', function(){
  new Tablesort(document.getElementById('tablesort'));
});

app/views/tables/online_storage.haml

.table-container
  %table.table.is-striped.is-hoverable.is-fullwidth#tablesort
    %thead
      %tr
        %th 料金
        ...
    %tbody
      - @items.each do |item|
        %tr
          %td{data: sort: { item.price_cents } }= item.price_cents
          ... 

これで、数字を基準としたソートをしてくれます。

ディレクトリマップに基づいてconfig/routes.rbを編集

ここは書く必要性が薄いのでコードを大幅に省きます。(固定ページだけ)

$ > touch app/views/pages/{about,contact}.haml
config/routes.rb
Rails.application.routes.draw do

  # ここに色々追加

  get 'about',      to: 'pages#about'
  get 'contact',    to: 'pages#contact'
  post '/contacts', to: 'pages#create_contact'

  root 'pages#index'
end

development/production環境で使うデータの作成/収集

情報収集する必要性のあるモデルのカラムをCSVに当てはめながら

Google Spreadsheet を使いデータを収集していきます。

終わったら

ファイル > ダウンロード > カンマ区切りの値 でcsvファイルをダウンロードできます。

※ 今回は合計740件前後集めて終わりました。(5時間前後) APIが見つからないため使わずに手作業で地道に(もしかしたら自分のサイトで公開するかもしれない)

データのシード

データのシードのやり方は色々ありますが
今回は、SeedFuのgemを使ってデータを登録したいと思います。

gem 'seed-fu', '~> 2.3'
$ > bundle install

セットアップが必要なので

$ > mkdir db/fixtures
$ > touch db/fixtures/{10_platform,20_item}.rb # ファイルは適当

Seed Fuは名前順にseedしていくので
番号を割り当てることによってデータの整合性を保っています。

Google Spread Sheet でダウンロードしたcsvファイルを
platform.csv, item.csv に名前を変更して db/fixturesに置きます。

10_platform.rb
20_item.rb
item.csv 
platform.csv

になっているはずです。

コードはこんな感じです。20_item.rb は長いので 10_platform.rbを例に出しておきます。

db/fixtures/platform.csv
category_name,name
Mobile,iOS
Mobile,Android
...
db/fixtures/10_platform.rb
require 'csv'

file = 'db/fixtures/platform.csv'

options = {
  encoding: "UTF-8",
  headers: true,
  header_converters: :symbol,
  converters: :all,
  nil_value: ""
}

begin
  CSV.foreach(file, options) do |row|
    @row          = row
    category_name = @row[:category_name]
    name          = @row[:name]
    category      = PlatformCategory.find_by_name(category_name)
    Platform.seed(
      name: name,
      category_id: category.id
    )
  end
  Platform.find_each(&:save)
rescue => e
  binding.pry
end

CSVライブラリーの使い方は省きますが代わりに使い方を説明しているリンクを載せておきます。

FriendlyID を使用しているので find_each(&:save) をして slug を生成しています。(SeedFuはActiveRecordを通してシードをしないため)

Platform.find_each(&:save) 

begin ~ rescue は CSVとモデル間でのバグを捕捉するもので、SeedFuではエラーが発生する原因はテメェの元のデータファイルが悪いんだこのタコ野郎みたいなポリシーがあるので地道にbinding.pryと@rowで探っていきます。(何かもっと他にいい方法はないのだろうか)

シードする際にモデルファイルにアソシエーションがない場合は以下のように簡潔に書ける方法もあります。

CSV.foreach(file, options) do |row|
  Model.seed(row.to_hash)
end

色々な書き方がありますが、今回は簡潔にseedしています。
適当にSeed Fuの使い方を説明している記事のリンクを並べておきます。

参考リンク:

これでテストデータをシードする準備が出来たと思うのでシードして行きます。

$ > rails db:seed_fu

本番用のテストデータが出来たので後はBulmaでデザインを作っていくだけです!

(Gem) Paginationの設定

gem 'pagy', '~> 3.5'
$ > bundle install 
$ > curl -o config/initializers/pagy.rb https://raw.githubusercontent.com/ddnexus/pagy/master/lib/config/pagy.rb

pagyの設定ファイルをconfig/initializers/pagy.rbにダウンロードして設定を変更していきます。

ファイルリンク:

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include Pagy::Backend
  ...
app/helpers/application_helper.rb
module ApplicationHelper
  include Pagy::Frontend
  ...

Pagination with Bulma

CSSフレームワークのテンプレート(erb, haml, slim)が既にあるので
基本的にはそれをコピーして改変して作っていきます。

pagyのhelperを使ってbulmaのpaginationのレイアウトを使用することも出来ますが
デザインを少し変更したかったので、hamlファイルをコピペして
shared/pagy/_bulma_nav.hamlを作りrender partialでページネーションを実現します。

とりあえずファイルをダウンロードします。

$ > curl -o app/views/shared/pagy/_bulma_nav.haml https://raw.githubusercontent.com/ddnexus/pagy/master/lib/templates/bulma_nav.html.haml
# サンプルコード / Controller
def index
  @pagy, @items = pagy(Item.all)
end
# サンプルコード / View
.columns.is-centered
  .column.is-half!= render partial: 'shared/pagy/bulma_nav', locals: {pagy: @pagy}

(Gem) MetaTagsの設定

MetaTags(SEO)を設定しておきます。(最終調整の時でもいい)

  • デフォルトの設定
  • Favicon の設定
  • twitter og の設定

参考リンク:

MetaTags - デフォルトの設定

app/helpers/application_helper.rb
module ApplicationHelper
  ...

  def default_meta_tags
    {
      charset: 'UTF-8',
      site: 'StorageGB',
      reverse: true,
      separator: '|',
      title: 'GBを増やす/減らす',
      description: 'GBを増やす/減らす WEBサイトとアプリの情報を集めたStorageGB!',
      canonical: request.original_url,

      icon:    default_icon,
      og:      default_og,
      twitter: default_twitter
    }
  end

defaultを上書きしたい場合は、show.hamlページ等で下記のように設定してください。

- title @item.name
- description @item.description
- twitter: { image: @item.image_url }

参考リンク:

MetaTags - Favicon の設定

とりあえず、Faviconを作成したいと思います。

AdobeXD ~色々あって~ 100x100 ぐらいでFavicon用の画像を作成して

Generate favicon from an image に画像をアップロードして、zipファイルをダウンロードして画像を

app/assets/imagesにファイルを配置したら完了です。

MetaTagsではかんな感じです。

app/helpers/application_helper.rb
private
...

def default_icon
  [
    {
      href: asset_path('favicon_io/favicon.ico'),
      sizes: '16x16 32x32',
      type: 'image/png'
    },
    {
      href: asset_path('favicon_io/apple-touch-icon.png'),
      rel: 'apple-touch-icon',
      type: 'image/png'
    }
  ]
end

上記の設定は、必要最低限なので(ブラウザー, iOS, Android)

もっと(+Macbook safari manifest)設定をしたい場合は

RealFaviconGenerator で画像を生成する事をお勧めします。

上記のサイトでは、Faviconを機種毎(Android/Chrome, iOS/Safari, Windows 8 10, ブラウザ)に

チェック出来るので、デプロイ後にする事をお勧めします。

Metatags - OGの設定

Twitter OGで使用する画像はここで設定されています。

app/helpers/application_helper.rb
private
...

def default_og
  {
    title: :title,
    description: :description,
    url: request.original_url,
    image: asset_url('meta_tags/twitter/summary-logo-black.jpg')
  }
end

参考リンク:

Metatags - Twitter OGの設定

[Twitter] Card validator を使えばプレビューを確認できます。Herokuにデプロイした後にすぐ確認できます。

今回は下記の設定で作成しております。

  • summary(1:1 144x144)
    • 今回は width:150 x height:150 の画像を用意
  • summary_large_image(2:1 300x157)
    • 今回は width:400 x height:200 の画像を用意
private
...

def default_twitter
  {
    card: 'summary',
    site: '@yowai_daicon',
  }
end

参考リンク:

5日目 - (Webサイト) 最終調整

コーティングが終わりデプロイ前に色々な設定をしたいので、それを行いたいと思います。
コーティングの最初の方でMetaTagsの設定をしましたが、ここでしても大丈夫です。主に本番環境で使うツールの導入です。

  • 1) public/robots.txt の設定
  • 2) Google Analytics の導入
  • 3) 管理画面の導入 (ActiveAdmin)
  • 4) エラーページの導入
  • メトリクスの導入(今回は使用しないため割愛)
  • チャットボットの導入(同上)

(1) public/robots.txt の設定

今回はオーソドックスに下記のような設定にしました。詳しい説明は参考リンク等を参考にしてください。

public/robots.txt
User-agent: *
Disallow: /admin

参考リンク:

(2) Google Analytics の導入

Gemを使う方法がありますが3年以上更新されていなく、不安材料があるので利用しないで頑張りたいと思います。(Turbolinksも使っている事だし)

とりあえず、js.erb と haml の二つを用意します。

$ > touch app/assets/javascripts/initialize/google_analytics.js.erb

$ > touch app/views/shared/_google_analytics.haml
app/assets/javascripts/application.js
...
//= require initialize/google_analytics
app/assets/javascripts/initialize/google_analytics.js.erb
document.addEventListener('turbolinks:load', function(event) {
  if (typeof gtag === 'function') {
    gtag('config', "<%= ENV['google_analytics_ua'] %>", {
      'page_location': event.data.url
    });
  }
});
app/viwes/application.haml
%head
  ...
  = render 'shared/google_analytics' if Rails.env.production?

app/views/shared/\_google\_analytics.haml
%script{async: '', src: "https://www.googletagmanager.com/gtag/js?id=#{ENV['google_analytics_ua']}"}

:javascript
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', "#{ENV['google_analytics_ua']}");
config/application.yml
google_analytics_ua: 'UA-xxxxxx-x'

本番環境でHerokuを使用しているので必ず環境変数をコマンドライン上で反映させてください。本番環境で環境変数が空文字だった場合は、登録をし忘れている可能性が大です。figaroを使用しているので、以下のコマンドで一括でファイルに書いてある変数達を登録します。

$ > figaro heroku:set -e production

参考リンク:

(3) 管理画面の導入 (ActiveAdmin)

最初にSeedFuとFigaroでログインするユーザーとパスワードを設定しておきます。

$ > touch db/fixtures/00_admin_user.rb
AdminUser.seed(
  email:    ENV['admin_user_email'],
  password: ENV['admin_user_password'],
  password_confirmation: ENV['admin_user_password']
)

ActiveAdminをインストールしたら
AdminUserモデル(Devise)が生成されるので、データベースに反映して、サーバーを再起動させたら

$ > rails generate active_admin:install
$ > rake db:migrate
$ > rails server

routes.rb を編集して

config/routes.rb
Rails.application.routes.draw do
  devise_for :admin_users, ActiveAdmin::Devise.config
  ActiveAdmin.routes(self) rescue ActiveAdmin::DatabaseHitDuringLoad
  ...

localhost:3000/admin にアクセスして、ログインすればOKです。

後は、以下のコマンドで管理画面に表示したいモデルを生成すれば最低限の管理画面は作成できるかと思います。

$ > rails generate active_admin:resource [MyModelName]

下記のコードはFriendlyIDを反映させる方法ですが
ネストした際にエラーになるので、その時は個別のモデルでオーバーライドしてください。

config/initializers/active_admin.rb
ActiveAdmin.setup do |config|

  # == Friendly Id addon
  ActiveAdmin::ResourceController.class_eval do
    def find_resource
      if resource_class.is_a?(FriendlyId)
        scoped_collection.friendly.find(params[:id])
      else
        scoped_collection.find(params[:id])
      end
    end
  end

  ...

end

他のコードを載せるとQiitaサーバーを圧迫する可能性があるため、割愛させていただきます。

参考リンク:

5日目 - (Webサイト) デプロイ

  • 1) Heroku にデプロイ
  • 2) Heroku x Cloudflare の設定
  • 3) Heroku を叩き起こす
  • 4) (番外編) Webページのスピードを測定する
  • (今回、SEO関係は割愛) (番外編) Sitemap.xml の設定

基本的には[公式] Rails Asset Pipeline on Herokuに書いてある通りにすれば大丈夫です。

ですが、Gemの読み込み順が悪かったり(sqliteとpgとか)groupの指定方法が悪かったりするとエラーが良く起きるので、そこは留意しておいてください。

今回は、SeedFuでデータをシードする際に binding.pry を使用していて

...
rescue
  binding.pry
end

production環境では、pry-rails はインストールされていないので assets:precompile 中にエラーが発生します。

group :development, :test do
  gem 'pry-rails'
end

pry-rails をグループから外してproductionで使う用に設定する事でエラーを回避する事も可能ですが

binding.pry のためだけに、production環境でも使えるようにbundle installはしたくないので

コマンドラインで grep | xargs sed を用いて一時的に binding.pry をコメントアウトする事によってエラーを回避します。

$ > grep -lr 'binding.pry' db/fixtures/* | xargs sed -i '' -e 's/binding.pry/# binding.pry/g'

簡単に説明しますと

grep で db/fixtures 内にある 'binding.pry' の文字を含んだファイルのみを対象として

xargs sed を発動して 's/対象の文字/変更後の文字/g' で文字を置換する。でございます。

それから、Herokuにデプロイしたいと思います。(Gitコメントは適当 / masterブランチでやっている事を想定)

$ master > RAILS_ENV=production bundle exec rake assets:precompile 
$ master > git add .
$ master > git commit -m '
- assets:precompile のために db/fixtures 内で使われている binding.pry を一時的にコメントアウト
- Heroku にデプロイするための assets:precompile の実行が完了した
'
$ master > git push
$ master > git push heroku master

後は、環境変数をHerokuに反映させて

$ > figaro heroku:set -e production

これでHerokuのアプリにローカルで開発したコードが反映されているはずです。

$ > heroku open

でページを開いてデプロイが成功したかを確認してください。

(2) Heroku x Cloudflare の設定

引用: [Blog] http://vdeep.net/cloudflare-rootdomain-ssl-heroku

(2-1) Heroku にカスタムドメインを登録

(2-1-1) Webから設定

https://dashboard.heroku.com にログインします。

アプリ名 > Settings の

Domains にある Add Domain で先ほど購入した

storagegb.space を入力して登録します。

(2-1-2) Command Line から設定
$ > heroku domains:add storagegb.space

で登録をして

$ > heroku domains

で確認できます。

引用: [公式] https://devcenter.heroku.com/articles/custom-domains

(2-2) Cloudflare にカスタムドメインを登録

https://dash.cloudflare.com にログインして

上方のメニューバーにある "+ Add site"をクリックします。

Enter your site (example.com) に 先程購入した

storagegb.space を入力して "Add site" をクリックして20秒前後待ちます。

(2-3) Freeプランを選択する

Select a Plan > "Free" を選択して "Confirm Plan" をクリックします。

(2-4) CNAME で ドメインを登録する

(2-4-1) Herokuに行き、アプリ名をコピーしてくる

Heroku のアプリ名が CNAME の VALUE で 必要になるので

Heroku.com > Personal > アプリ名 > Settings

の Name から コピーするか、そのまま アプリ名 の所をコピーしてください。

(2-4-2) Cloudflare に戻り設定する

デフォルトの設定で DNS が30前後登録されていますが

必要がないので全部を削除してください。

削除が完了したら

CNAME | NAME: storagegb.space | VALUE: 4-1でコピーしたアプリ名.herokuapp.com

CNAME | NAME: www | VALUE: 同上.herokuapp.com

の2つを "Add Record" をクリックして作成します。

storagegb.space の CNAMEの左横に警告マークがついていますが大丈夫です。

(2-5) SSL の設定をする

(2-5-1) Cloudflare側でSSLの設定

Cloudflare > storagegb.space > SSL / TLS をクリックします。

※ もし上にアイコンメニューが表示されていなかったら、メール認証を確定させるか再読み込みをしてください。

... > ... > SSL / TLS > Overview

Your SSL/TLS encryption mode is ~~~ の下にある

セレクトボックスで "Full (strict)" を選択します。

(2-5-1) Rails側でSSLの設定

ここで一旦 rails に戻り 本番環境で強制的にHTTPSを使うように設定します。

conifg/production.rb
config.force_ssl = true

(2-6) お名前.com でネームサーバーの情報を変更する

お名前.com > ドメイン に行きます。

storagegb.space(取得したドメイン) をクリックします。

下を少しスクロールすると

ネームサーバー情報

ネームサーバー1 : dns1.onamae.com

ネームサーバー2 : dns2.onamae.com

があるのを確認して "ネームサーバーの変更" のリンクがあるのでクリックします。

1.ドメインの選択 でドメインが(自動)選択されているのを確認し

2.ネームサーバーの選択"その他" をクリックします。

"その他のネームサーバーを使う" を選択する。

(2-7) Cloudflareでネームサーバーの情報を取得する

cloudflare.com > storagegb.space(取得したドメイン) > Overview に行き

Complete your nameserver setup の

2. Replace with Cloudflare's nameservers に書いてある

Nameserver 1Nameserver 2 をコピーします。

(2-8) (2-6) に戻り、Cloudflare (2-7) でコピーしたものをペーストする。

さっきコピペしたのを 先程のお名前.comのページ に戻り下記のようにコピペします。

(例)

ネームサーバー1 (必須) : Nameserver 1 をコピペ

ネームサーバー2 (必須) : Nameserver 2 をコピペ

"確認" ボタンを押し、下記のようなモーダルが出てくるので "OK" ボタンをクリックする。


ご確認

以下の内容でネームサーバーの設定をします。よろしいですか?

対象ドメイン

storagegb.space

ネームサーバー情報

  1. xxx
  2. xxx

※ インターネットの環境により、反映完了まで24時間から72時間かかる場合があります。


(2-9) お名前.com の設定が Cloudflare に反映されるのを待つ

今回は、約13分で完了しました。

メールを確認すると以下のような件名でメールが届いているのが確認できます。

[Cloudflare]: storagegb.space has been added to a Cloudflare Free Plan

HTTPS化はまだです。

お名前.com の設定が Cloudflare に反映されただけです。

お名前.com の設定が Cloudflare に反映されたら

Universal SSL が自動で生成されて

Universal SSLの反映がサーバーに完了したら https://storagegb.space が使えるようになります。

引用: [公式] End-to-end HTTPS with Cloudflare - Part 1: conceptual overview

If SSL was not working for your domain (e.g. your SSL certificate has not yet been issued), you would see a 525 or 526 HTTP response after the redirect.

Please note that the issuing of a Universal SSL certificate typically takes up to 24 hours. Our paid SSL certificates issue within 10-15 minutes.

との事なので、とりあえず半日~1日ぐらいは待ちましょう。


* 4-2

CNAME の VALUE を Heroku に ドメインを登録した際に発行される

DNS Target で登録していたので SSL 525 Handshake Error が起きて

少しだけハマってしまったのですが

アプリ名.herokuapp.com に書き換える事により

https://storagegb.space が使えるようになったので

もしかしたら、待ち時間はもっと短かったかもしれません。(数時間?)

今回はこまめに確認していなかったので、最短の時間はわかりませんでしたが

約18時間ぐらいで反映されているのを確認できました。

(2-10) CURL コマンドを叩いて反映されているかを確認する (お名前.com編)

反映前: (2-9) お名前.com の設定が Cloudflare に反映されるのを待つ

$ > curl -I storagegb.space
HTTP/1.1 530
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
...

反映完了後: 約13分前後

$ > curl -I storagegb.space
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Connection: keep-alive
...

反映完了後で

HTTP Status Code が 200 OK を返してるのが確認できます。

引用: [公式] Configure Cloudflare and Heroku over HTTPS

(2-11) 反映の確認が出来たので、Cloudflare で設定を追加する。

cloudflare.com > storagegb.space(取得したドメイン) > Edge Certificates に行き

  1. Universal SSLActive になっている事を確認する。
  2. Always Use HTTPS のラジオボックスを "OFF" から "ON" に変更する。

(2-12) 第二回 CURL コマンドを叩いて反映されているかを確認する (SSL編)

Universal SSL が反映されていないと

https://storagegb.space にアクセスしても

525 Error のページが返ってくると思います。

$ > curl -I -L storagegb.space
HTTP/1.1 301 Moved Permanently
Connection: keep-alive
Location: [https://storagegb.space/](https://storagegb.space/)
Server: cloudflare

リダイレクトされて...

HTTP/2 525
content-type: text/html; charset=UTF-8
server: cloudflare

Universal SSL が 反映された後は

$ > curl -I -L storagegb.space
HTTP/1.1 301 Moved Permanently
Location: [https://storagegb.space/](https://storagegb.space/)
Server: cloudflare

リダイレクトされて...

HTTP/2 200
content-type: text/html; charset=utf-8
server: cloudflare

となっていて無事、URLのhttps化に成功できたことが確認できます。

(2-13) Cloudflare の Page Ruler を使う。

www のサブドメイン付きアクセスが来たら

$ > curl -I -L www.storagegb.space
HTTP/1.1 301 Moved Permanently
Location: [https://www.storagegb.space/](https://www.storagegb.space/)
Server: cloudflare

リダイレクトされて...

HTTP/2 404
content-type: text/html; charset=utf-8
server: cloudflare

と、残念ながら 404 Heroku ページに飛ばされてしまうので

200 OKコードが返ってくるようにリダイレクトの設定をしたいと思います。

cloudflare.com > Page Rules をクリックします。

Page Rules の 横にある "Create Page Rule" ボタンをクリックします。

Create a Page Rule for storagegb.space(ドメイン名)

If the URL matches の フォーム に以下の値を入力します。

www.storagegb.space/*

Then the settings are: には

Forwarding URL - 301 - Permanet Redirect

https://storagegb.space/$1

と入力して "Save and Deploy" ボタンをクリックしたら完了です。

$ > curl -I -L www.storagegb.space
HTTP/1.1 301 Moved Permanently
Connection: keep-alive
Location: [https://storagegb.space/](https://storagegb.space/)
Server: cloudflare

リダイレクトされて...

HTTP/2 200
content-type: text/html; charset=utf-8
server: cloudflare

となり、無事 www サブドメイン付きのアクセスが来ても

絶対URL https://storagegb.space にリダイレクトさせることにより

404コード問題 に対処する事が出来ました。

以上で Heroku x Cloudflare 編 は 完了です。

(3) Heroku を叩き起こす

Herokuの無料プランだと

30分アクセスしないとスリープして、レスポンスタイムが遅くなり使い勝手が悪くなるので、監視するツールを導入します。

この二つが主流かと思われます。(自分調べ)

Heroku Scheduler だと あまり安定性が保証されてなく

起動する際に dyno を少しだけ消費しないといけないらしいので

今回は、無料で50個まで監視先を登録できる UptimeRobot でポチポチ設定したいと思います。

(3-1) UptimeRobot でアカウントを作成

UptimeRobot でアカウントを作成してください。

(3-2) UptimeRobot で監視先の追加

Add New Monitor ボタンをクリックします。

Monitor Type で HTTP(s) を選択して

Friendly Name には分かりやすい名前を設定して (例) Heroku - サイトの名前

URL には 監視先のURL https://storagegb.space

Monitoring Interval には 5 minutes (デフォルト) に設定して

Create Monitor ボタンを押せば登録完了です。

参考リンク:

(4) (番外編) Webページのスピードを測定する。

作成したWebサイトのパフォーマンスを測定したいと思います。

色々ツールはありますが *1 今回は主に

URLを入力して、Webサイトの性能を測定するものに絞りたいと思います。(早い・安い・うまい)

PageSpeed Insights 以外はオプションでどうぞ ぐらいの感じです。

GTmetrix はPageSpeed Insights をもっとテクニカルに分析した感じです。

DareBoost も GTmetrix 的要素を兼ね備えています。(ただし、月に5回まで無料)

Web Page Test は測定するサーバーを国別から選べるので、日本とアメリカとかいろいろな場所から測定すればリアルなユーザー体験に基づいた結果が得られるはずです。

余談:
*1 本当は、色々書きすぎて中々記事が出せない状況が続くため、色々省く(永遠と書いていられる)

(5) (番外編) Sitemap.xml の設定

これも重要度が低いのか高いのか難しい所ですが、設定が少し面倒なので省かせていただきます...

この記事では、あまりSEO関係の設定は行っていないので、そこだけは留意しといてください。。。

もしかしたら後日、時間があったときに書くかもしれません。。。


以上で、この記事は終了です。
やけに文章が冗長で読みにくいかな〜と普通に自分でも思います。
所々、クオリティがないのは力尽きた証拠です。
誤字脱字 + バグが酷いかと思われます。訂正されたら速攻修正させていただきます。ありがとうございました。。

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

5日ぐらいでいい感じのWebサイトとポートフォリオを作る

5日ぐらいでいい感じのWebサイトとポートフォリオを作る

長文を書きましたが、結局は「記事を作って公開して最終的にどこからか案件もらえないかな〜チラチラ」という一行を希薄化しただけです。
タイトルはもしかしたら「Railsでプロトタイプを素早く作る」とかでも良かったかもしれないです。
ちなみに作ったWebサイトが跳ねる確率は限りなく0です。

何を作ったのか

StorageGB (https://storagegb.space)

オンラインストレージの比較サイト

ポートフォリオ (https://daicon.xyz)

自分が作ったWebサイトを実績として載せたり、仕事の受注等に使う(予定)

なぜ作ったのか

理由は二つあります。

国内にオンラインストレージを体系的にまとめたサイトがなかったため(記事はちらほら見るけども)。どうせなら自分がついでに作っておこう。と言う軽い気持ち

もう一つは

RubyとRailsを学んでいる初学者ぐらいの人を対象に(Rails Tutorial が終わったぐらい)
少しずつ色々なツールを紹介しつつ開発している様子を発信したかった。と言う謎の気持ち

後者に関しては
初学者の方達が Rails Tutorial を終えた時に

はい!じゃあ何かWebサイトをRoRで開発しよう!

って時に、そのまま Rails Tutorial で使った

  • Bootstrap
  • DB Browser for SQLite

などを用いた、Web開発を阻止する!のが私の目的です!

そこで今回は違うツールを使ったりを提案したりします。

(イメージ)

  • こんなフレームワークとかあるんだよ!!
  • こういうツールを使えば楽に開発できるよ!!

※ 偉そうな事を言いましたが、記事を見返したら、あまり提案してませんでした。

初学者とか独学者だと知らないという事を知らない状態で開発を進めてしまって、無駄に労力を消費しながらWebサイト開発をしてしまう事が往々にあります。(自分がそうだったし、未来永劫そうです)

例えば、CSSの微調整をする際に手作業

  1. CSSファイルを更新
  2. ブラウザの更新ボタンをマウスでクリック
  3. 反映を確認する
  4. 何か違う
  5. 1に戻る

という王道コンボをしがちです。なのでそこで私が

CSSの微調整ならChromeのDeveloper Toolを開いてやって調整すれば楽だよ!!

とか

HTMLファイルとかCSSファイルを更新したら自動で読み込めるGuard Livereloadを使うと楽だよ!(これは Rails Tutorialで矯正される)

みたいな感じの事を書き残しておきたいと思います。

Rails Tutorial にもそのような事が書いてある時もありますが、カバーしきれないのでそれの補助的な意味合いもあります。

とりあえず引用を多めに備忘録とかも兼ねて残したいです。

あと一つ、この記事を投稿するのには理由があります。

一応、現在フリーランスでプログラマーをしているので

「お金 と プログラミング」を題材とした記事とかも書きたいのでそれのためにです。

例えば

  • Qiitaに記事(個人開発系 等)を書いて案件をGETできるのか?
  • クラウドネットワークソーシングで案件をGETしてお金を稼ぐ

みたいな感じでございます。

最終的には、初学者やネットで独学している人達が

仕事を受注できるようにするための道のりとかの、情報を蓄積できればいいかな〜と言う所存です。

使用しているアプリ (生産性を上げるための)

Time Scduling / 時間測定

作業時間を主に計測する

Note App / ノートアプリ

ブログを書いたり、マークダウン形式でドキュメントやコメントを書くときに使う

TODO LIST / やることリスト

やるべき事のタスクを管理する。

Browsing / ブラウジング

Session Buddy: 開いている全ページを記録する。

OneTab: 開きすぎたぺージをとりあえずまとめたい時に使用する。

使用している技術 (Webサイト)

Web Design Tool / Webデザインツール

Webデザインをするために使用。

HTML

毎度お馴染み、Hamlの進化版 Hamlit先生

CSS Framework / CSS フレームワーク

今回は使い慣れたBulmaを使用することによって、開発時間の短縮を狙う。

Javascript

動的なサイトだと色々不具合が起きる悪名高いTurbolinks

今回は、静的なサイトなので採用。

Tablesortは比較表に使用している。Lazyloadは定番のやつを

Autoload Browser / 自動ブラウザリロード

HTML/CSS/Javascript/Rspec 等 のファイルを更新した際に自動でブラウザリロードを行う

Database / データベース

Heroku の王道セッティング

ユーザーからのデータ作成をあまり受け付けていないため

この設定で耐えられると確信

Server / サーバー

開発時間の短縮を狙うため、使い慣れているHerokuを使用する。

Storage / ストレージ

AWS S3 は使わず Cloudinaryを今回初採用。

無料でそこそこ使えるのでありがたい

Image Compressor / 画像圧縮

jpg, png, svg 等の画像を圧縮する

作業日数

流れは下記のような形ですが、「ポートフォリオ編」と「Webサイト編」に分けています。

  • 1日目 - 何を作るか決める (この記事)
  • 1日目 - (ポートフォリオ) デザインの選定と作成
  • 1日目 - (ポートフォリオ) コーティング
  • 1 ~ 2日目 - (Webサイト) デザインの選定と作成
  • 2 ~ 5日目 - (Webサイト) コーティング
  • 5日目 - (Webサイト) 最終調整
  • 5日目 - (Webサイト) デプロイ
  • 5日目 - (ポートフォリオ) デプロイ
  • 6 ~ 18日目 - Qittaを書く (途中で書くのに飽きて、書かない日のほうが多かった)

1日でポートフォリオを作って、4日ぐらいでWebサイトを作るといった感じです。
正直、Webサイトは3時間で作れる時もあれば、1ヶ月かかるやつもあるので
「5日ぐらいでいい感じのWebサイトとポートフォリオを作る」とは言いましたが
あくまで、今回のテーマでございます。

1日目 - 何を作るか決める

  • ポートフォリオ
  • Webサイト

今回は、上記の二つのサイトを作る必要があります。

ポートフォリオの要件/機能定義

簡易的に大雑把に決めてしまいます。

  • ランニングコスト0の完全無料で運用したい
  • 自分が作成したWebサイトを載せたい (他のクライアント様から受注した活動実績も載せるかも...???)
  • 活動実績とブログ機能とコンタクトフォームがあると有難い
  • 活動実績はタイトルと写真、クリックしたら詳細ページに飛んで色々な情報を載せたい
  • ブログ機能はシンプルなやつでいい
  • 時間がないから、静的サイトジェネレーターとテーマで作成する
  • コンタクトフォームは活動実績等を見た将来のクライアント様から仕事を受注したい

今回は上記なような感じで行きます。

Webサイトの要件/機能定義

  • 5日前後で作れるレベルのサイト(ほぼ静的なサイトになるかも)
  • それでもThe Bootstrap的なサイトデザインは避けたい

個人開発ということもあり、ここからは完全独裁主義でババっと具体的なアイデアを決めたいと思います。

  • 一元的に情報を集めたやつを作りたい
  • オンラインストレージ(Dropbox, Box, Mega) のようなサイトを集めて、無料で使えるGBを表示したりとかどうかしら...???
  • オンラインストレージだけじゃなくてGBを主軸として「GBを増やす(オンラインストレージ等)」と「減らす(圧縮ツール等)」の紹介のサイトとかだったら2~5日で出来そう

ここまで決めれば、後は作るだけです。

恐らく Google で調べると似たようなサイトがヒットしたりとか、全く同じ事を考えてた人が五年前とかに似たようなサイトを作ってたり。まぁ、車輪の再発明感がありますが今回のテーマは「5日前後でポートフォリオとWebサイトを作成」なので、涙を堪えてWebサイト作成に取り組みます。

ここで競合調査を入れようかと思いましたが今回は割愛します。

終わりに

一つの記事に、ポートフォリオとWebサイトを作成する過程を残すのは
結果的に読みづらくなると判断したので、二つに分割します。

基本的な流れは

  • デザインを決める / 作成
  • コーティングする (セットアップが中心)
  • 最終調整(Google Analyticsの設定 等)
  • デプロイ

このようになっています。

Ruby on Rails チュートリアル が 100までの事を、1ずつ説明しているとすると
今回は、6~10 ずつぐらいの感じで書いています。
コーティングの部分は、ほとんどGem/JSライブラリーのセットアップと使い方ぐらいです。

リンク:
(1日) ポートフォリオ編 - 5日ぐらいでいい感じのWebサイトとポートフォリオを作る
(1 ~ 5日) Webサイト編 - 5日ぐらいでいい感じのWebサイトとポートフォリオを作る

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

rails/gem "ancestry"を使って多階層構造をつくる

"ancestry"・・・ 親・子・孫みたいな多階層構造のテーブルを作れるgem。

例)食べログのラーメンカテゴリ

親   子  孫     
親:ラーメン   子1:ラーメン NULL
子2:汁なしラーメン 子2の孫: 油そば
子2の孫:台湾まぜそば
子2の孫: 汁なし担々麺
子3:つけ麺 NULL

ラーメンは3つ子要素がある。
この場合、汁なしラーメンは孫が3つある。
ラーメンとつけ麺は子のみ。

つまり、親 → 子2:”汁なしラーメン” → 孫 を作成できる。


Gemfile
 gem 'ancestry'
$ bundle install
$ rails g migration add_ancestry_to_table ancestry:string

マイグレーションファイル

category.rb
class AddAncestryToCategory < ActiveRecord::Migration[5.2]
  def change
    add_column :categories, :ancestry, :string
    add_index :categories, :ancestry
  end
end
$ rake db:migrate

 Model

category.rb
class Category < ApplicationRecord
  has_many :products_categories
  has_many :products, through: :products_categories
  has_ancestry
end

データを入れる

seeds.rb に記述するとデータを流入できます。
まずは、categoriesにデータを格納する。

db/seeds.rb
categories = [{layer1:"レディース",layer1_chaild:[
                  {layer2:"トップス",layer2_chaild:["Tシャツ/カットソー(半袖/袖なし)","Tシャツ/カットソー(七分/長袖)","シャツ/ブラウス(半袖/袖なし)","シャツ/ブラウス(七分/長袖)","ポロシャツ","キャミソール","タンクトップ","ホルターネック","ニット/セーター","チュニック","カーディガン/ボレロ)","アンサンブル","ベスト/ジレ","パーカー","トレーナー/スウェット","ベアトップ/チューブトップ","ジャージ","その他"]},
                  {layer2:"ジャケット/アウター",layer2_chaild:["テーラードジャケット","テーラードジャケット","ノーカラージャケット","Gジャン/デニムジャケット","レザージャケット","ダウンジャケット","ライダースジャケット","ミリタリージャケット","ダウンベスト","ジャンパー/ブルゾン","ポンチョ","ロングコート","トレンチコート","ダッフルコート","ピーコート","チェスターコート","モッズコート","スタジャン","毛皮/ファーコート","スプリングコート","スカジャン","その他</option></select>ノーカラージャケット","Gジャン/デニムジャケット","レザージャケット","ダウンジャケット","ライダースジャケット","ミリタリージャケット","ダウンベスト","ジャンパー/ブルゾン","ポンチョ","ロングコート","トレンチコート","ダッフルコート","ピーコート","チェスターコート","モッズコート","スタジャン","毛皮/ファーコート","スプリングコート","スカジャン","その他"]},
                  {layer2:"パンツ",layer2_chaild:["デニム/ジーンズ","ショートパンツ","カジュアルパンツ","ハーフパンツ","チノパン","ワークパンツ/カーゴパンツ","クロップドパンツ","サロペット/オーバーオール","オールインワン","サルエルパンツ","ガウチョパンツ","その他"]},
                  ]},
             {layer1:"インテリア・住まい・小物",layer1_chaild:[
                  {layer2:"キッチン/食器",layer2_chaild:["食器","調理器具","収納/キッチン雑貨","弁当用品","カトラリー(スプーン等)","テーブル用品","容器","エプロン","アルコールグッズ","浄水機","その他"]},
                  {layer2:"ベッド/マットレス",layer2_chaild:["セミシングルベッド","シングルベッド","セミダブルベッド","ダブルベッド","ワイドダブルベッド","クイーンベッド","キングベッド","脚付きマットレスベッド","マットレス","すのこベッド","ロフトベッド/システムベッド","簡易ベッド/折りたたみベッド","収納付き","その他"]},
                  ]},
             {layer1:"本・音楽・ゲーム",layer1_chaild:[
                  {layer2:"本",layer2_chaild:["文学/小説","人文/社会","ノンフィクション/教養","地図/旅行ガイド","ビジネス/経済","健康/医学","コンピュータ/IT","趣味/スポーツ/実用","住まい/暮らし/子育て","アート/エンタメ","洋書","絵本","参考書","その他"]},
                  {layer2:"漫画",layer2_chaild:["全巻セット","少年漫画","少女漫画","青年漫画","女性漫画","同人誌","その他"]},
                  {layer2:"雑誌",layer2_chaild:["アート/エンタメ/ホビー","ファッション","ニュース/総合","趣味/スポーツ","その他"]},
                  {layer2:"CD",layer2_chaild:["邦楽","洋楽","アニメ","クラシック","K-POP/アジア","キッズ/ファミリー","その他"]},
                  ]},
                ]

親要素がlayer1

{layer1:"親"

layer1_chaild:[{layer2:"子要素"                  
layer2_chaild:["孫1","孫2","孫3", ...]

categories保存する。

db/seeds.rb
categories.each do |category|
  @layer1 = Category.create(layer:"#{category[:layer1]}")  #・・・親を保存
    category[:layer1_chaild].each do |layer1_chaild|
      @layer2 = @layer1.children.create(layer:layer1_chaild[:layer2])  #・・・子を保存
        layer1_chaild[:layer2_chaild].each do |layer2_chaild|
          @layer2.children.create(layer:layer2_chaild)    #・・・孫を保存
          end
      end
    end

完成!

db/seeds.rb
categories = [{layer1:"レディース",layer1_chaild:[
                  {layer2:"トップス",layer2_chaild:["Tシャツ/カットソー(半袖/袖なし)","Tシャツ/カットソー(七分/長袖)","シャツ/ブラウス(半袖/袖なし)","シャツ/ブラウス(七分/長袖)","ポロシャツ","キャミソール","タンクトップ","ホルターネック","ニット/セーター","チュニック","カーディガン/ボレロ)","アンサンブル","ベスト/ジレ","パーカー","トレーナー/スウェット","ベアトップ/チューブトップ","ジャージ","その他"]},
                  {layer2:"ジャケット/アウター",layer2_chaild:["テーラードジャケット","テーラードジャケット","ノーカラージャケット","Gジャン/デニムジャケット","レザージャケット","ダウンジャケット","ライダースジャケット","ミリタリージャケット","ダウンベスト","ジャンパー/ブルゾン","ポンチョ","ロングコート","トレンチコート","ダッフルコート","ピーコート","チェスターコート","モッズコート","スタジャン","毛皮/ファーコート","スプリングコート","スカジャン","その他</option></select>ノーカラージャケット","Gジャン/デニムジャケット","レザージャケット","ダウンジャケット","ライダースジャケット","ミリタリージャケット","ダウンベスト","ジャンパー/ブルゾン","ポンチョ","ロングコート","トレンチコート","ダッフルコート","ピーコート","チェスターコート","モッズコート","スタジャン","毛皮/ファーコート","スプリングコート","スカジャン","その他"]},
                  {layer2:"パンツ",layer2_chaild:["デニム/ジーンズ","ショートパンツ","カジュアルパンツ","ハーフパンツ","チノパン","ワークパンツ/カーゴパンツ","クロップドパンツ","サロペット/オーバーオール","オールインワン","サルエルパンツ","ガウチョパンツ","その他"]},
                  ]},
             {layer1:"インテリア・住まい・小物",layer1_chaild:[
                  {layer2:"キッチン/食器",layer2_chaild:["食器","調理器具","収納/キッチン雑貨","弁当用品","カトラリー(スプーン等)","テーブル用品","容器","エプロン","アルコールグッズ","浄水機","その他"]},
                  {layer2:"ベッド/マットレス",layer2_chaild:["セミシングルベッド","シングルベッド","セミダブルベッド","ダブルベッド","ワイドダブルベッド","クイーンベッド","キングベッド","脚付きマットレスベッド","マットレス","すのこベッド","ロフトベッド/システムベッド","簡易ベッド/折りたたみベッド","収納付き","その他"]},
                  ]},
             {layer1:"本・音楽・ゲーム",layer1_chaild:[
                  {layer2:"本",layer2_chaild:["文学/小説","人文/社会","ノンフィクション/教養","地図/旅行ガイド","ビジネス/経済","健康/医学","コンピュータ/IT","趣味/スポーツ/実用","住まい/暮らし/子育て","アート/エンタメ","洋書","絵本","参考書","その他"]},
                  {layer2:"漫画",layer2_chaild:["全巻セット","少年漫画","少女漫画","青年漫画","女性漫画","同人誌","その他"]},
                  {layer2:"雑誌",layer2_chaild:["アート/エンタメ/ホビー","ファッション","ニュース/総合","趣味/スポーツ","その他"]},
                  {layer2:"CD",layer2_chaild:["邦楽","洋楽","アニメ","クラシック","K-POP/アジア","キッズ/ファミリー","その他"]},
                  ]},
                ]
categories.each do |category|
  @layer1 = Category.create(layer:"#{category[:layer1]}")  #・・・親を保存
    category[:layer1_chaild].each do |layer1_chaild|
      @layer2 = @layer1.children.create(layer:layer1_chaild[:layer2])  #・・・子を保存
        layer1_chaild[:layer2_chaild].each do |layer2_chaild|
          @layer2.children.create(layer:layer2_chaild)    #・・・孫を保存
          end
      end
    end

終わったら下記を打って、tableにデータがあったら成功!

$ rake db:seed

ちなみに1

孫要素がない時には一応 "layer2_chaild:[""]"と書かないと私の場合うまくいいかなかった。

ちなみに2

やっぱり、めんどくさい!孫が多ければそりゃめんどくさいです。
いい方法が思いつかなかったので教えて欲しい!
ちなみに、私は下記のように検証ツールを加工しながら作りました。
この方法も最前ではないです。めんどくさかったので。
ancestry'使用時,seedsに多階層カテゴリを書くのめんどくさいから、一気に置換してみる

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

form_tag,form_withと空のnewアクション

はじめに

今スクールでpictweetという、写真とツイートを一緒に投稿できる、簡単な練習用のwebサービスをつくっているのですが、ふと気になったことがあったので記事にしてみました。

form_tag,form_withとnewアクション

MVCモデル学習し、ツイートが投稿されたときの流れも理解していたつもりでした。
投稿されたデータがデータベースに保存されるためにはnewアクションでインスタンス変数を定義しなければいけないと思っていたのですが、、、
サンプルコードを見ると

qiita.rb
def new
end

え???newアクションの中身なにもないやん!(◎◎;)
でもよく似たwebサービスのサンプルコードではpostsコントローラに「@post = Post.new」としっかりインスタンス変数が定義されていました。
何が違うの?
と思って数時間かけて調べた結果,HTMLでform
tagを使用していた場合は、newアクションでインスタンス変数は定義しなくていいみたいです。
逆にform_withを使用する場合は@モデル名で定義しなければいけないみたいです。

もう少し調べるとform_forというのも出てきて、そもそもform_forとform_tagは投稿されたデータをデータベースに保存するかしないかで使い分けるんですね。
ただ新しく作られたform_withは両方の用途で使用し、またセキュリティ面でも有能のようです。

結果、これからは投稿フォーム系にはform_withを使うのがベストというのが今回の学びです。

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

Rails の scope 便利手帳

Ruby on Rails で、使いやすいScopeをまとめました。
Model の設計によって差異があるものの、視点は使えるのでは?と思います。

Scopeとは

Model に scope を記載することで、Controllerなどから、複雑なWhere条件を書かずにデータを取りだせるようになります。

app/model/article.rb
class Article < ApplicationRecord
  scope :newer, ->{order("updated_at DESC") }
end

これによって、Controllerなどで

app/controllers/articles_controller.rb
@articles = Article.newer

で、更新日が新しい順に取得することができます。

app/controllers/article.rb
@articles = Article.newer.limit(5)

とすると、更新日が新しい順に5件を取得できます。

公開されているか

  scope :published, -> { where(published: true) }

ある時間より前か

  scope :created_before, ->(dt) { where("created_at < ?", dt) }

呼び出し元では、引数に時間をいれる

@articles = Article.created_before(Time.now)

終了状態であるか

終了日の有無で判定する場合

  scope :finished, ->{where("finished_at IS NOT NULL") }

その他追記予定

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

【チーム開発】【GitHub】プログラミング未経験の営業が、GitHubでチーム開発を経験してみたお話

~はじめに~

この記事なに?

社会人で営業の経験しかしてこなかった26歳が、プログラミング独学で勉強して早半年。
MENTAというサービスで現役Railsエンジニアの方に教えて頂き、ちょっとだけチーム開発の経験をさせて頂きました。
『アウトプットが大切』ということを周りから聞いてますので頑張って書いてます。
~未経験の方に向けてチーム開発の大まかな流れが分かっていただければと思います~


自分のレベル感

  • 営業歴8年…お話は得意です。特にテキト〜な話は大好物。
  • Railsチュートリアル2周ほど
  • GitとGithub使って2ヶ月ほど
  • 基本情報技術者試験ぎりぎり合格。。。
  • チーム開発は、もちろん未経験です。

で、なにするの?

https://github.com/Matsushin/qiitan
こちらQiitaを模倣したサイト『Qiitan』
過去MENTA生徒の方々と、Railsエンジニアの先生がコツコツ作ってきた汗と涙の結晶!

こちらのサイトへ『追加機能を実装』していきます!
~現場によってやり方は様々ですので今回はあくまで一例になります~

0: 前提条件

gitのインストールとgithubへのアカウント登録は済んでいること。

1: GithubからCloneして、ローカルリポジトリを作成

1112hiroki_action_text_sample.png
1:上記画像参照頂き、緑のボタンを押下
2:URLをそのまま全てコピー(一番右にあるボタンでもコピー可能)
3:ターミナルでコピペして下記を実行 ↓

$ git clone https://github.com/xxx/xxx.git

2: 作業用のブランチを作成する

そのまま作業をするとmasterブランチに直接コードを書いてしまう為、自分の実装内容にあったブランチ名をつけて作成を行う。

$ git checkout -b [ブランチ名]

  checkout → ブランチを移動する時に使う。
  -b     → checkoutに-b (-branchの略)をつけることで、ブランチの作成+移動を行う。

3:Githubで『実装進捗の見える化』

Githubのの進捗管理として[Project]を設定でき、その中で自分の割り当てられたタスクをドラックして指定の位置へ。
この様にすることで『誰が今なんの機能を実装しているか』を把握することが出来ます。

Projects_·_Matsushin_qiitan.png
1-2.png

4: 作業用のブランチで実装スタート

先程作成した作業用ブランチで機能実装開始!

●実装に一区切りがついたら → addしてcommitをしよう!

$ git add .
 → 作業したファイルを全てステージングに乗せる
$ git commit -m [メッセージの入力]
 → 上記の[]内に、終了したタスクのコメントを簡単に入力

●与えられた機能の実装が全て終了したら → pushをしてGithubに上げよう!

$ git push origin [ブランチ名]
 → Github上に自分のブランチをpushで上げる事ができる。

※今回の『Qiitan』では[CircleCi]を使用してますので、GithubにPushしたらCircleCiのテストが作動します。
URLにアクセスして、テストが通ることを確認してみましょう↓↓↓
7.png

5: Githubにpushをしたら、プルリクエストを

8.png
9.png

6: プルリクエスト後、[Project]内のタスク移動を忘れずに!

[Project]を開き、再度タスクをドラックして[Review]へ
あとは、リードプログラマのコードレビューを待ちます。
2-2.png

7: コードレビューをもらったら一つずつ修正を行っていく

image.png

~コードを全て修正後、再度push⇔コードレビューを繰り返します~

4-2.png


こんな感じです

8: コードレビューが全て完了したら、masterにマージする

image.png

9: 最後に忘れずに、[Project]内のタスクを移動して終了

6-2.png

こうすることで自分が実装した機能が、本流へ追加されます!
お疲れさまでした!

〜最後に:チーム開発体験を終えて〜

チーム開発の大枠の流れを把握することが出来ました。
実際に既存のコードをさわる際に、既存コードの流用や真似をして記載するようにしていたが、
周りのコードに影響が出ないか。また必要であれば既存のコードへの加筆をしてもいいか、判断に迷いました。

現在は個人開発しかしておりませんので、ブランチ名もクラス名もテキトーに付けてしまっている部分もありますが、
チーム開発ではそれは出来ません。

日頃からそちらも意識して開発して参りますm(_ _)m

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