20200923のvue.jsに関する記事は14件です。

Vue.jsでMaterial Iconsを使えるようにする

概要

Vue.jsでMaterial Iconsを使うときに毎回使い方を忘れるので、備忘も兼ねて利用方法をまとめました。

ライブラリのインストール

まずはMaterial Icons用のライブラリをインストールします。

yarn add vue-material-design-icons

コンポーネントとしてインポート

Material Design Iconsから利用したいアイコンを探してインポートします。
ファイル名はパスカルケースで宣言。

ex) account-tie -> AccountTie.vue

hoge.vue
import KabbadiIcon from 'vue-material-design-icons/Kabaddi.vue';

components: {
  KabbadiIcon,
},

templateで利用

templateでコンポーネントとして宣言することで利用できます。
公式リポジトリに使えるpropsの説明とかがあります。

hoge.vue
<kabaddi-icon />

参考

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

Vue.js~jestを使ってのテスト~

はじめに

なかなかできなくてやっと簡単なテストならできたので
忘れないうちにメモ
間違いがあったらご指摘いただけると嬉しいです。

テスト対象

<template>
  <div>
    <p>{{num}}</p>
    <button class="increment__button" @click="increment">足す</button>
    <button class="decrement__button" @click="decrement">引く</button>
    <button class="reset__button" @click="reset">リセット</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      num: 0,
    };
  },
  methods: {
    increment() {
      return this.num++;
    },
    decrement() {
      if(this.num <= 0) return
      return this.num--;
    },
    reset() {
      return (this.num = 0);
    },
  },
};
</script>

ボタンをクリックで数字を足したり、引いたり
ただそれだけの物。

そしてこれをテストしてみる

テストコード

import Test1 from '../../src/components/test1'
import {mount} from '@vue/test-utils'

const wrapper = mount(Test1)
const vm = wrapper.vm
describe('test1', () => {
  it('初期値は0?', () => {
    expect(wrapper.html()).toContain('<p>0</p>')
  })
  it('buttonがあるか?', () => {
    expect(wrapper.contains('button')).toBe(true)
  })
})

describe('test2', () => {
  it('increment', () => {
    const button = wrapper.find('.increment__button')
    expect(vm.num).toBe(0)
    button.trigger('click')
    expect(vm.num).toBe(1)
    button.trigger('click')
    expect(vm.num).toBe(2)
  })
  it('decrement', () => {
    vm.num = 1
    const button = wrapper.find('.decrement__button')
    button.trigger('click')
    expect(vm.num).toBe(0)
    button.trigger('click')
    expect(vm.num).toBe(0)
  })
  it('reset', () => {
    vm.num = 5
    const button = wrapper.find('.reset__button')
    expect(vm.num).toBe(5)
    button.trigger('click')
    expect(vm.num).toBe(0)
  })
})

まず始めにテスト対象のコンポーネントをmount()の引数に入れて
それをwrapperとして色々するみたい

コンポーネントから取得

const button = wrapper.find('.increment__button')

これはTest1コンポーネント内のclass="increment__button"を取得
JavaScriptのdocument.getElementById()みたいな感じ?
そして今回のだとclickイベントが各ボタンにあるので、それをtrigger()を使う事で実行できる

button.trigger('click')

dataの取得

wrapper.vm.num//0
wrapper.vm.num = 10
wrapper.vm.num//10

これでコンポーネントのdatanumの値を取得
代入もできる

以上を踏まえてもう一度確認

const button = wrapper.find('.increment__button')
    expect(vm.num).toBe(0)
    button.trigger('click')
    expect(vm.num).toBe(1)
    button.trigger('click')
    expect(vm.num).toBe(2)

最初の結果はなにもしてないので(0)
次にclickイベントが実行されるのでインクリメントされた結果が(1)
となる。
残り二つのボタンもやってることはほぼ同じ
今わかってるのはここまで。

Vuex使用時とかのテストをできなきゃなぁ。。。

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

【Vue.js】Firebase独自のメソッドをまとめてみよう

firebaseモジュールのセットアップ

$ yarn add firebase --save
main.js
import firebase from "firebase";

const config = {
  apiKey: "",
  authDomain: "",
  databaseURL: "",
  projectId: "",
  storageBucket: "",
  messagingSenderId: ""
};
firebase.initializeApp(config);

コンポーネントでの準備

router/index.js
import signup from "@/components/signup";

export default new Router({
  routes: [
    {
      path: "/signup",
      component: signup
    },
  ]
})
components/signup.vue
# コンポーネント毎に、モジュール読み込み
import firebase from "firebase";

Usage ユーザー作成、認証系

サインアップ

signUp: function() {
  firebase
    .auth()
    .createUserWithEmailAndPassword(this.email, this.password)
    .then(user => {
      alert(user.email);
    })
    .catch(error => {
      alert(error.message);
    });
}

サインイン

signIn: function() {
  firebase
    .auth()
    .signInWithEmailAndPassword(this.email, this.password)
    .then(user => {
      alert("success");
      this.$router.push("/profile");
    })
    .catch(error => {
      alert(error.message);
    });
}

ログイン中ユーザーのデータ取得

data () {
  return {
    email: firebase.auth().currentUser.email
  }
}

ユーザーがログイン中なら、ユーザー情報を返すメソッド(ログインチェック)

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    //ユーザーログイン時の処理
  } else {
    //ユーザーログアウト時の処理
  }
})

Usage クラウドデータベース

firestoreの全件データを取得

firebase.firestore().collection("message").get()
  .then(function(query) {
    query.forEach(function(doc) {
      console.log(doc.data());
    });
  }).catch(function(e) {
    alert(e);
  });

firestoreの全件データの中から、条件を絞って取得

firebase.firestore().collection("message").where("name", "==", "tom").get()
  .then(function(query) {
    query.forEach(function(doc) {
      console.log(doc.data());
    });
  }).catch(function(e) {
    alert(e);
  });

firestoreにデータを追加

firebase.firestore().collection("message").add({
  name: this.nameData
})
  .then(function() {
    alert("成功しました");
  }).catch(function(e) {
    alert("失敗しました");
  });

firestoreのデータを削除

firebase.firestore().collection("message").doc("directMessage").delete(
  .then(function() {
    alert("成功しました");
  }).catch(function(e) {
    alert("失敗しました");
  });
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(´ω`) CREATE TABLEパースできて楽になって余った時間でコーヒー

なし水を飲みながら書いてるわ。つーわけで書く。Qiitaの記事を書き始めてからこの時点でフォロワーは1人増えたぐらいで全く効果がなかった。まったく俺の書く記事が幼稚でそもそも読み手の意識していないちゃらんぽらんの野郎だって思われているかもしれない。実際その通りだ。かつては丁寧に書いていた時期もあったが今では一日の30分をQiitaに放り込んでいる。まるでゴミのようにだ。読者はその事に絶句しているかもしれない。嘲笑しているかもしれない。それが俺だ。丁寧に書けばいけるんじゃね?...と考えたがこれは質の問題のように思える。今日も最低限の記事を書いて眠ろう。ソースコードはわいの手元にある。完璧に動作する状態だがこの分だと陽の目を見ることもないだろう。終わりだ。そこで今まで通りだらだら書くスタイルにした。文字通りダラダラ書く。帰宅したおっさんが寝ながら書いているのか?それより酷い。みたいに書くぞ。

何作ったの

昔、私はある会社にいた。ウェブ系の案件を任された時に作ったユーティリティだ。これはCREATE TABLE文をパース出来る。JSON形式で。この案件ではエクセルに全てのテーブルとそのテーブルのスキーマが書かれていた。列に対する名前・型・入力値検証・制約・備考などだ。プロジェクトを作りこむ内に整合性が合わなくなってくる。ソースコード上はこちらに変更したが「そういやエクセル更新忘れた」と言っても担当は俺一人だったから何も問題はなかった。引継時は大問題だが。特にここらへんはもうCREATE TABLEのコメント文として真横に記載しておいた方がいいんじゃないか。ぐらいのレベルに思えた。でもそれじゃいけない。やっぱりエクセルに纏めておかないと。ってことで作った。とにかく列名をコピペするのが嫌だったし手入力だと遅いで一発でエクセルにコピーするために作った。

image.png

何組み合わせたの

単純だ。エディタはCODE-MIRRORを利用している。UIはいつも通りFOMANTIC-UIだ。テーブルのCSV化とテーブルを画像化したいこともあると思ってPNG化もできる。CHROMEの拡張機能だ。html2Canvasを使っても出来るが全て自作するのはよろしくない。本末点灯だ。エラーも出るようにしてある。

image.png

どうやってパースしてるの?

NEARLEYだ。NEARLEYというコンパイラを使えばJSの解析器を作れる。NEARLEYに.NEというファイル(BNFベースのNEARLEY構文)をぶち込めばパーサ(.JS)を出力してくれる。俺はNEファイルを書いてない。実際はGITHUBで楽に作れるライブラリはないかと探してたんだ。解析 mysql create table 语句,用于通过建表语句生成 model 代码文件。を持ってきた。

5ファイルあって

image.png

構文はこんな感じ

image.png

ソースコード

sample.html
<!doctype html>
 <html>
  <head>

   <title></title>

   <meta charset='utf-8'>
   <meta content='' name='author'>
   <meta content='' name='description'>
   <meta content='' name='application-name'>
   <meta content='notranslate' name='google'>
   <!-- GOOGLE:https://support.google.com/webmasters/answer/79812 -->
   <meta content='telephone=no,address=no,email=no,date=no,url=no' name='format-detection'>
   <meta content='noimageindex,notranslate,nosnippet,noarchive,nofollow,noindex' name='robots'>
   <meta content='width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no' name='viewport'>

   <link href='css/fomantic-ui/2.7.8.min.css' rel='stylesheet'>
   <link href='asset/manifest.json' rel='manifest'>
   <link href='asset/favicon.ico' rel='icon'>

   <style name='editor'>
     @import url('css/code-mirror/5.49.1.min.css');
     @import url('css/code-mirror/addon-5.49.1.min.css');
     @import url('css/code-mirror/mode-5.49.1.min.css');
     @import url('css/code-mirror/5.49.1.min.css');
  </style>
   <style name='font'>
     @font-face{
       font-family:'M+2VM+IPAG circle';
       src:url('asset/m+2vm+ipag-circle.ttf');
     }
  </style>
   <style>
     html,
     body{
       font:12px 'M+2VM+IPAG circle';
     }
     /*
      * 配置(中央)
      */
     body{
       display:flex;
       align-items:center;
       justify-content:center;
     }
     main{
       width:999px;
       height:100%;
     }
     .ui.grid{
       height:100%;
     }
     /*
      * 見栄(凹凸)
      */
     .ui.label,
     .ui.table,
     .ui.segment{
       border-bottom:2px solid rgba(34,36,38,.15) !important;
       box-shadow:rgba(16, 36, 94, 0.4) 0 2px 6px 0 !important;
     }
     /*
      * 調整(位置)
      */
     .ui.table,
     .ui.segment{
       margin-top:0;
     }
     /*
      * 調整(全体)
      */
     .ui.table>thead>tr>th,
     .ui.table>tbody>tr>td{
       padding:.2em;
     }
     /*
      * 調整(個々)
      */
     .ui.table>thead>tr>th:nth-child(2){
       width:5em;
     }
     .ui.table>thead>tr>th:nth-child(3),
     .ui.table>tbody>tr>td:nth-child(3),
     .ui.table>thead>tr>th:nth-child(4),
     .ui.table>tbody>tr>td:nth-child(4),
     .ui.table>thead>tr>th:nth-child(5),
     .ui.table>tbody>tr>td:nth-child(5){
       text-align:center;
       width:3em;
     }
     .ui.table>thead>tr>th{
       text-align:center;
     }
     .ui.table>tbody>tr:hover{
       cursor:pointer;
     }
     /*
      * 構造(エディタ)
      */
     #editor{
       display:flex;
       flex-direction:column;
     }
     #eidotr #editor-view{
       flex:1;
     }
     #editor #editor-controller{

     }
     /*
      * 設定(スクロールとフォント)
      */
     #editor,
     #editor .ui.segment,
     #editor .ui.segment .vue-codemirror,
     #editor .ui.segment .vue-codemirror .CodeMirror{
       height:100%;
     }
     #editor .ui.segment .vue-codemirror .CodeMirror{
       font:12px 'M+2VM+IPAG circle';
     }
     #viewer{
       height:100%;
       overflow-y:scroll;
     }
     /*
      * FOMANTIC-UI(が既に指定してる)
      * CREATE TABLE->(sql2json):<TABLE>群は
      * スクロールバーがあるとみっともないので非表示
      * #viewer::webkit-scrollbarは効かないので上書き
      */
     body ::-webkit-scrollbar {
       display:none;
     }
     .ui.label>i.icon{
       margin-right:0;
     }
     /*
      * FOMANTIC-UI
      * 2.7.8は古いFONTAWESOMEを利用中
      * ので新しいフォントを入れ直して個々に定義する
      * path:css/fomantic-ui/themes/default/assets/fonts
      */
      i.icon.file-csv:before    {content:'\f6dd';}
      i.icon.file-image:before  {content:'\f1c5';}
      /*
       * MODEL
       * CREATE-TABLEをTABLE化したモノ
       * 画像化した際(クロームの拡張機能による要素指定キャプチャ)影を残す
       * https://chrome.google.com/webstore/search/element%20capture?_category=extensions
       */
      #viewer div.model{
        padding-bottom:.7em;
        padding-right:.7em;
        padding-left:.7em;
        padding-top:0;
      }
      /*
       * sql2json
       * エラー時の色合わせ
       */
      .ui.label.error-message,
      .CodeMirror .error-line{
        background:#fff0f0 !important;
        color:#ff7373 !important;
      }
  </style>
 </head>
  <body>
   <main>

     <div class='ui grid internally celled'>
       <div class='row'>
         <div class='eleven wide column' id='editor'>
            <div class='ui labels blue' id='editor-controller'>
              <div class='ui label pointing below'>
                全テーブル
             </div>
              <a class='ui label' target='_blank' href='https://chrome.google.com/webstore/detail/html-elements-screenshot/mckfdaahjhmnchjihljdiakamamondld?hl=ja'>
                <i class='icon file-image'>
               </i><div class='ui detail'>PNG</div>
             </a>
              <a class='ui label' target='_blank' href='https://chrome.google.com/webstore/detail/download-table-as-csv/jgeonblahchgiadgojdjilffklaihalj?hl=ja'>
                <i class='icon file-csv'>
               </i><div class='ui detail'>CSV</div>
             </a>
              <div class='ui label error-message' v-if='hasErrSql2json'>
                {{err.message}}
             </div>
          </div>
           <div class='ui segment fitted' id='editor-view'>
             <codemirror ref='myCm' v-model='content'>  
            </codemirror>
          </div>
        </div>
         <div class='five wide column' id='viewer'>
           <div ref='models' v-for='(obj,n) in json'>
             <model v-model='json[n]'>
            </model>
          </div>
        </div>  
      </div>
    </div>
  </main>
 </body>
</html>

 <template id='model' type='text/x-template'>
   <div v-ui>
     <div class='ui divider horizontal'>
       {{table.name}}
    </div>
     <table class='ui table attached top single line unstackable celled fixed green'>
       <thead>
         <th></th>
         <th></th>
         <th></th>
         <th></th>
         <th></th>
      </thead>
       <tbody>
         <template ref='models' v-for='(obj,i) in table.columns'>
           <!-- ここ修正 -->
           <tr v-if='hasName(obj)' :class="[{'positive':highlight[obj.name]}]" @dblclick='highlightRow(obj)'>
             <td><span>{{getName(obj)}}</span></td>
             <td><span>{{getDataType(obj)}}</span></td>
             <td><span>{{getDataTypeParam(obj)}}</span></td>
             <td>
               <span>
                 <template v-if='getNullType(obj)'>
                   <i class='icon check'>
                  </i>
                </template>
              </span>
            </td>
             <td>
               <span>
                 <template v-if='getAutoIncrementType(obj)'>
                   <i class='icon check'>
                  </i>
                </template>
              </span>
            </td>
          </tr>
        </template>
      </tbody>
    </table>
  </div>
</template>

 <!-- native:code-mirror -->
 <script src='js/native/code-mirror/5.49.1.min.js'></script>
 <script src='js/native/code-mirror/addon-5.49.1.min.js'></script>
 <script src='js/native/code-mirror/mode-5.49.1.min.js'></script>

 <!-- native:sql2json -->
 <script src='js/native/lodash-4.17.15.min.js'></script>
 <script src='js/native/nearley/2.19.0.min.js'></script>
 <script src='js/native/nearley/grammar.js'></script>

 <!-- vue -->
 <script src='js/vue/2.6.10.js'></script>
 <script src='js/vue/code-mirror-4.0.6.min.js'></script>
 <script>
   Vue.directive('ui',function(el,binding,vnode){
     el.classList.add(vnode.context.$options.name)
   })

   Vue.component('model',{
     template:'#model',
     model:{
       prop:'table',
       event:'input'
     },
     props:{
       table:{
         required:true,
         type:Object,
         default:{}
       }
     },
     data:function(){
       return{
         highlight:{
         }
       }
     },
     methods:{
       /*
        * 行色
        */
       highlightRow:function(obj){
         if(this.hasName(obj)){
           var name = obj.name
           var toggle = (name in this.highlight) ? !this.highlight[name] : true
           this.$set(this.highlight,name,toggle)
         }
       },
       /*
        * 取得
        */
       getName:function(column){
         return column.name
       },
       getNullType:function(column){
         if(this.hasNullType(column)){
           return column.allow_null
         }
       },
       getDataType:function(column){
         if(this.hasDataType(column)){
           return column.data_type.type
         }
       },
       getDataTypeParam:function(column){
         if(this.hasDataTypeParams(column)){
           return column.data_type.params[0]
         }
       },
       getAutoIncrementType:function(column){
         if(this.hasAutoIncrementType(column)){
           return column.auto_increment
         }
       },
       /*
        * 検証
        */
       hasAutoIncrementType:function(obj){
         return 'auto_increment' in obj
       },
       hasDataTypeParams:function(obj){
         return this.hasDataType(obj) ? 'params' in obj.data_type : false
       },
       hasDataType:function(obj){
         return 'data_type' in obj
       },
       hasNullType:function(obj){
         return 'allow_null' in obj
       },
       hasName:function(obj){
         return 'name' in obj
       }
     }
   })

   /*
    * view-source:https://codemirror.net/mode/sql/
    */
   Vue.use(VueCodeMirror,{
     options:{
       scrollbarStyle  : 'simple',
       theme           : 'default',
       mode            : 'text/x-mysql',
       firstLineNumber : 1,
       tabSize         : 2,
       indentUnit      : 2,
       matchTags       : {bothTags: true},
       htmlMode        : true,
       autoCloseTags   : true,
       foldGutter      : true,
       lineNumbers     : true,
       lineWrapping    : true,
       styleActiveLine     : true,
       styleActiveSelected : true,
       extraKeys: {
         'Ctrl-A': 'autocomplete',
         'Ctrl-Q': function(cm){
           cm.foldCode(cm.getCursor())
         },
         'F11': function(cm){
           cm.setOption("fullScreen", !cm.getOption("fullScreen"))
         }
       }
     }
   })

   new Vue({
     el:'main',
     data:{
       err:{
         message:'',
         line:0,
         col:0
       },
       json:[],
       content:`
CREATE TABLE action_protocol (
  id int(11) NOT NULL AUTO_INCREMENT,
  createdbyuserkey varchar(40) NOT NULL DEFAULT '',
  userkey varchar(40) NOT NULL DEFAULT '',
  dt_created datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  servicekey varchar(40) NOT NULL DEFAULT '',
  information text NOT NULL,
  objectkey varchar(40) NOT NULL DEFAULT '',
  action varchar(50) DEFAULT '',
  PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE crm_reports (
  id int(11) NOT NULL AUTO_INCREMENT,
  entrykey varchar(40) NOT NULL,
  userkey varchar(40) NOT NULL,
  reportname varchar(255) NOT NULL,
  description varchar(255) NOT NULL,
  tablekey varchar(40) NOT NULL,
  dt_created datetime NOT NULL,
  dt_start datetime NOT NULL,
  dt_end datetime NOT NULL,
  date_field varchar(100),
  displayfields text NOT NULL,
  specials text NOT NULL,
  interval varchar(255) NOT NULL,
  filter text NOT NULL,
  defaultreport int(11) NOT NULL DEFAULT '0',
  basedonaddressbook int(11) NOT NULL,
  allow_select_fields int(11) NOT NULL DEFAULT '1',
  PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE adminactions (
  id int(11) NOT NULL AUTO_INCREMENT,
  dt_created datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  userkey varchar(50) NOT NULL DEFAULT '',
  urlvariables text NOT NULL,
  formvariables longtext NOT NULL,
  resellerkey varchar(50) NOT NULL DEFAULT '',
  companykey varchar(50) NOT NULL DEFAULT '',
  href text NOT NULL,
  PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

       ` 
     },
     watch:{
       content:{
         /*
          * 監視(入力)
          */
         deep:true,
         handler:function(newSQL,oldSQL){
          /*
            * 保存
            */
           localStorage.setItem('cache',this.content)

          /*
           * 解析
           */
          var self=this
          var editor=self.$refs.myCm.codemirror

          /*
           * 保存間隔は850ms
           */
          var processing = _.throttle(function(){

            // 消去:前回のエラー
            editor.removeLineClass(self.err.line,'wrap','error-line')

            try{
              // 変換
              self.sql2json()
              // 変換:正常
              self.err.message = ''
            }catch(exception){
              // 取得:エラーの情報と内容
              var errInfo = exception.message.split('\n')[0]
              var errContent = exception.message.split('\n')[2]
              // 整形:エラーとして表示するメッセージ
              self.err.message = errInfo + ':' + errContent
              // 取得:エラーの発生した行と列(https://www.regextester.com/97589 "Syntax error at line 111 col 1:: test")
              self.err.line = parseInt(errInfo.match(/line\s[0-9]+/g)[0].replace('line ','')) - 1
              self.err.col = parseInt(errInfo.match(/col\s[0-9]+/g)[0].replace('col ',''))
              // 追加:今回のエラー(https://codemirror.net/doc/manual.html#removeLineClass)
              self.$refs.myCm.codemirror.addLineClass(self.err.line,'wrap','error-line')
            }
          },850)

          /*
           * 処理
           */
          processing()
          /*
           * 表示
           */
          console.dir(
            this.json
          )
         }
       }
     },
     computed:{
       hasErrSql2json:function(){
         return this.err.message.length > 0
       }
     },
     methods:{
       init:function(){
         if('cache' in localStorage){
           this.content = localStorage.getItem('cache')
         }
       },
       sql2json:function(){
         this.json = new nearley.Parser(grammar).feed(this.content).results[0]
       }
     },
     created:function(){
       this.init()
       this.sql2json()
     }
   })
</script>

備考

CODE-MIRRORはmode/, addon/ とか別個に読み込まずにモジュール事に全ての*.jsをuglifyjs-folder使って1ファイルに纏めてる。CSSもcssclean *.css使って1ファイル化している。

さて

SQL以外にも何かできたらええよな。というお話でしたとさ。

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

[初心者]Rails6 Vue Postgres開発環境をDocker-Composeを使って構築する方法

はじめに

Dockerを用いてRails,Vue,Posgre環境下で開発したいなと思った時に、
Rails,Vueの環境作りに手こずりましたので自分のメモとして保存します。

この記事で分かること

・Rails6とVueとPostgresのDocker環境が構築できる
・RailsとVueの連動ができる
・PryにてインスタンスとDBの操作ができる

環境

MacOS Mojave
Ruby 2.6.4
Rails 6.0.3.3
Vue 2.6.12
Webpack 4.44.2
yarn 1.22.5
Docker 2.3.0.5
VScode

Githubのリンク

僕のGithubのDocker-Start-Kitです。よろしければご活用ください。
Whiro0501/Docker_Rails6_Vue

Docker環境をMacに構築

ターミナルを開く
mkdirで任意の名前のディレクトリを作成
cdで作成したディレクトリに移動
code.でVScodeを開く

terminal
mkdir qiita
cd qiita
code .

VScodeが起動
VScode上のターミナルで作業していくので
ターミナルが起動していなければ⬆︎+Control+@でターミナルを開く

VScodeのターミナル上で以下コマンドを入力してリモートリポジトリのファイルをローカルにコピー

VScode
git clone https://github.com/Whiro0501/Docker_Rails6_Vue.git

cdでDocker_Rails6_Vue/ディレクトリに移動

VScode
cd Docker_Rails6_Vue/

以下コマンドを実行してDockerイメージをビルド

VScode
docker-compose build

以下を実行して、必要なノードモジュールを取得

VScode
docker-compose run web yarn install --check-files

以下を実行して DB(Postgres)を作成

VScode
docker-compose run web rake db:create

別のターミナルを開く
以下を実行してレールズアプリケーションを起動

VScode
#別ターミナルが億劫なら、docker-compose up -dでも良い 
docker-compose up

さらに別のターミナルを開く
Viewを更新するたび毎回コンパイルが発生し、時間がかかるため以下を実行

VScode
docker-compose exec web ./bin/webpack-dev-server

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Railsアプリケーションが起動することを確認

スクリーンショット 2020-09-23 20.42.47.png

VueとRailsの連動ってどうやるの?

初期構築時点ではRailsとVueが連動されておりませんので連動させていく。

以下でhomeコントローラを作成する(コントローラー名はなんでも良い)。

VScode
docker-compose exec web rails g controller home index

index.html.erbが作成

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

以下ファイルに"root to: 'home#index'"を追加

routes.rb
Rails.application.routes.draw do
  root to:  'home#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

まずはindex.html.erbに記述されている内容が表示

スクリーンショット 2020-09-23 21.07.31.png

Rails側で下地ができたので、次にVueとの連動の設定
app.vueの初期設定

app.vue
<template>
  <div id="app">
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      message: "Hello Vue!"
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

hello_vue.jsの初期設定

hello_vue.js
import Vue from 'vue'
import App from '../app.vue'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)

  console.log(app)
})

app.vueが単一ファイルコンポーネントであり、
hello_vue.jsにオブジェクトとして渡される。
それをindex.html.erbに表示させるよう設定する。

index.html.erbを以下の通り設定

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<%= javascript_pack_tag 'hello_vue.js' %>
<%= stylesheet_pack_tag 'hello_vue.js' %>

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Vueとの連動が完了!!

スクリーンショット 2020-09-23 21.23.06.png

Githubののファイルの説明

Whiro0501/Docker_Rails6_Vue
まず上記リンクのGithubの状態から説明する
端的に説明すると以下2つを設定した後の状態となる
従ってRails6とVueとPostgresが使用できる環境を整えることができるという理屈である。

VScode
docker-compose run web rails new . --force --database=postgresql --webpack=vue

加えて上記の状態だとPostgresがうまくRailsと連動できないため以下のように設定する。
こちらも公式ドキュメントを参考とした。

database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development


test:
  <<: *default
  database: myapp_test


production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>

Dockerfile

公式ドキュメントのベストプラクティスを参照にした。
DockerはFROM, RUN, COPY毎にレイヤーが作成されるため
RUNやCOPYはできるだけまとめると少ないレイヤーで収めることができるとのこと。

Dockerfile
FROM ruby:2.6

# `apt-get install yarn`とするとエラーになる
# プロジェクトに必要なツールをインストール
# &&で繋げてコマンドを実行することによりレイヤーを1つとする
#apt-get update と apt-get installは同一RUN上で行う(分けると最新版を使用できない)
#RUNはイメージの作成次に実行(CMDはコンテナ起動時に実行)
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update -qq && apt-get install -y nodejs postgresql-client vim && \
    apt-get install -y yarn

# ディレクトリの作成
RUN mkdir /myapp

# 作業ディレクトリの指定
#RUN ,  COPY,  ADD 命令のみレイヤを作成するためWORKDIRは気にしなくて良い
# 絶対パスとする
WORKDIR /myapp

# Gemfileが更新された時のみ、レイヤを再構築
#先にプロジェクト全体をコピーしないのはレイヤーを分けるため
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
# ライブラリの依存関係をインストール
RUN bundle install

# プロジェクト全体をコピー(Gemfile/Gemfile.lockはコピーされない)
COPY . /myapp

#コンテナを起動する毎に実行されるスクリプトを追加
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

#コンテナの公開ポート番号の指定
EXPOSE 3000

#指定しなければコンテナ起動時にデフォルトで実行する処理
#Dockerfile では CMD 命令を 1 つしか記述できない
#ENTRYPOINT 命令に対するデフォルト引数としてCMDを使用可能
CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose

docker-compose.ymlもDocker公式ドキュメントを参考にした。

docker-compose.yml
version: '3'
services:
    db:
        # DBにpostgresを使用
        image: postgres
        # ホストの./tmp/dbと/var/lib/postgresql/dataを同期させる
        volumes:
            - ./tmp/db:/var/lib/postgresql/data
        # 環境変数の指定
        environment:
            POSTGRES_PASSWORD: password
    web:
        build: .
        # コンテナ起動時にserver.pidを削除し、rails sを実行する
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        # ホストのカレントディレクトリをコンテナの/myappと同期させる
        volumes:
            - .:/myapp
        # ホストとコンテナ間をポートフォワードする
        ports:
            - '3000:3000'
        # サービス間の依存関係
        depends_on:
            - db
        # Docker環境でByebugを使用
        stdin_open: true
        tty: true

Gemfile

Gemfileに関してはデフォルトで入っているもの主となる。
追加したパッケージはコメントしているため不要であれば削除して構わない

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


ruby '2.6.6'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.3'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  #デバックツールの導入
  gem 'pry-rails'
  gem 'pry-byebug'
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  #自動補完用ツールの導入
  gem 'solargraph'
  #静的コード解析ツールの導入
  gem 'rubocop'
  gem 'rubocop-rails'
  #erbのフォーマットツールの導入
  gem 'htmlbeautifier'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

package.json

package.jsonについてはvue-routerとvuexを使いたいため追加している
Vuetify等、UIフレームワークを使用したいようであれば追加する。

package.json
{
    "name": "myapp",
    "private": true,
    "dependencies": {
        "@rails/actioncable": "^6.0.0",
        "@rails/activestorage": "^6.0.0",
        "@rails/ujs": "^6.0.0",
        "@rails/webpacker": "4.3.0",
        "turbolinks": "^5.2.0",
        "vue": "^2.6.12",
        "vue-loader": "^15.9.3",
        "vue-template-compiler": "^2.6.12",
        "vue-router": "^3.0.1",
        "vuex": "^3.0.1"
    },
    "version": "0.1.0",
    "devDependencies": {
        "webpack-dev-server": "^3.11.0"
    }
}

Railsでデバッグする

VScode上のターミナルを開く
以下をターミナルで実行

VScode
docker-compose exec web rails console

pry が起動

VScode
[1] pry(main)> 

postコントローラーを作成

docker-compose exec web rails g controller post index

post_controller.rbを修正

post_controller.rb
class PostController < ApplicationController
  def index
    @post = Post.all
  end
end

postのモデルを作成

VScode
docker-compose exec web rails g model post name:string age:integer

DBにモデルを反映させる

VScode
docker-compose exec web rails db:migrate

index.html.erbを以下に書き換え

index.html.erb
<%= @post.name %>
<%= @post.age %>

routes.rbを以下に修正

routes.rb
Rails.application.routes.draw do
  root to:  'post#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

pryでPostモデルのインスタンスを作成

VScode
@post = Post.new
=> #<Post:0x00005589bc4beb78 id: nil, name: nil, age: nil, created_at: nil, updated_at: nil>

#まだnameやageにはデータを入れていない
#DBに保存もされていない
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[]

#DBにインスタンスを保存する
@post.save
   (0.7ms)  BEGIN
  Post Create (5.9ms)  INSERT INTO "posts" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2020-09-23 13:06:47.962085"], ["updated_at", "2020-09-23 13:06:47.962085"]]
   (3.1ms)  COMMIT
=> true

#もう一度Post.allをしてデータをDBから取得すると保存したインスタンスが呼び出される
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00005589bceec5f0
  id: 1,
  name: nil,
  age: nil,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00>]

#@postインスタンスにデータを入れてみる
@post.name = "Hiro"
=> "Hiro"

@post.age = "29"
=> "29"

#再び保存
@post.save
Post Update (4.0ms)  UPDATE "posts" SET "name" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["name", "Hiro"], ["updated_at", "2020-09-23 13:10:26.486888"], ["id", 1]]
Post Update (2.0ms)  UPDATE "posts" SET "age" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["age", 29], ["updated_at", "2020-09-23 13:10:56.785029"], ["id", 1]]
   (1.0ms)  COMMIT
=> true

#データが保存されている
Post.all
=>   Post Load (1.4ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00007f1ddc7a77a8
  id: 1,
  name: "Hiro",
  age: 29,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:10:56 UTC +00:00>]

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下のようにDBからデータを取得できればOK

スクリーンショット 2020-09-23 22.27.01.png

Binding.pryを試してみる

post_controller.rbを修正

class PostController < ApplicationController
  def index
    binding.pry
    @post = Post.all
  end
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下の画面でフリーズする(正しい挙動)
スクリーンショット 2020-09-23 22.30.19.png

pryのターミナルに戻る

pry(main)> 

以上で、pryでデバッグする環境が整った

参考

以下のサイトを参考にさせていただきました。
Docker ドキュメント日本語化プロジェクト
クィックスタート: Compose と Rails
Dockerfile のベストプラクティス
Webpacker の基本的な仕組み
Dockerを使って「Rails / PostgreSQL」の開発環境を作ろう!

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

Rails6 Vueの連動方法(環境構築から)

はじめに

Dockerを用いてRails,Vue,Posgre環境下で開発したいなと思った時に、
Rails,Vueの環境作りに手こずりましたので自分のメモとして保存します。

この記事で分かること

・Rails6とVueとPostgresのDocker環境が構築できる
・RailsとVueの連動ができる
・PryにてインスタンスとDBの操作ができる

環境

MacOS Mojave
Ruby 2.6.4
Rails 6.0.3.3
Vue 2.6.12
Webpack 4.44.2
yarn 1.22.5
Docker 2.3.0.5
VScode

Githubのリンク

僕のGithubのDocker-Start-Kitです。よろしければご活用ください。
Whiro0501/Docker_Rails6_Vue

Docker環境をMacに構築

ターミナルを開く
mkdirで任意の名前のディレクトリを作成
cdで作成したディレクトリに移動
code.でVScodeを開く

terminal
mkdir qiita
cd qiita
code .

VScodeが起動
VScode上のターミナルで作業していくので
ターミナルが起動していなければ⬆︎+Control+@でターミナルを開く

VScodeのターミナル上で以下コマンドを入力してリモートリポジトリのファイルをローカルにコピー

VScode
git clone https://github.com/Whiro0501/Docker_Rails6_Vue.git

cdでDocker_Rails6_Vue/ディレクトリに移動

VScode
cd Docker_Rails6_Vue/

以下コマンドを実行してDockerイメージをビルド

VScode
docker-compose build

以下を実行して、必要なノードモジュールを取得

VScode
docker-compose run web yarn install --check-files

以下を実行して DB(Postgres)を作成

VScode
docker-compose run web rake db:create

別のターミナルを開く
以下を実行してレールズアプリケーションを起動

VScode
#別ターミナルが億劫なら、docker-compose up -dでも良い 
docker-compose up

さらに別のターミナルを開く
Viewを更新するたび毎回コンパイルが発生し、時間がかかるため以下を実行

VScode
docker-compose exec web ./bin/webpack-dev-server

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Railsアプリケーションが起動することを確認

スクリーンショット 2020-09-23 20.42.47.png

VueとRailsの連動ってどうやるの?

初期構築時点ではRailsとVueが連動されておりませんので連動させていく。

以下でhomeコントローラを作成する(コントローラー名はなんでも良い)。

VScode
docker-compose exec web rails g controller home index

index.html.erbが作成

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

以下ファイルに"root to: 'home#index'"を追加

routes.rb
Rails.application.routes.draw do
  root to:  'home#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

まずはindex.html.erbに記述されている内容が表示

スクリーンショット 2020-09-23 21.07.31.png

Rails側で下地ができたので、次にVueとの連動の設定
app.vueの初期設定

app.vue
<template>
  <div id="app">
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      message: "Hello Vue!"
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

hello_vue.jsの初期設定

hello_vue.js
import Vue from 'vue'
import App from '../app.vue'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)

  console.log(app)
})

app.vueが単一ファイルコンポーネントであり、
hello_vue.jsにオブジェクトとして渡される。
それをindex.html.erbに表示させるよう設定する。

index.html.erbを以下の通り設定

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<%= javascript_pack_tag 'hello_vue.js' %>
<%= stylesheet_pack_tag 'hello_vue.js' %>

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Vueとの連動が完了!!

スクリーンショット 2020-09-23 21.23.06.png

Githubののファイルの説明

Whiro0501/Docker_Rails6_Vue
まず上記リンクのGithubの状態から説明する
端的に説明すると以下2つを設定した後の状態となる
従ってRails6とVueとPostgresが使用できる環境を整えることができるという理屈である。

VScode
docker-compose run web rails new . --force --database=postgresql --webpack=vue

加えて上記の状態だとPostgresがうまくRailsと連動できないため以下のように設定する。
こちらも公式ドキュメントを参考とした。

database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development


test:
  <<: *default
  database: myapp_test


production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>

Dockerfile

公式ドキュメントのベストプラクティスを参照にした。
DockerはFROM, RUN, COPY毎にレイヤーが作成されるため
RUNやCOPYはできるだけまとめると少ないレイヤーで収めることができるとのこと。

Dockerfile
FROM ruby:2.6

# `apt-get install yarn`とするとエラーになる
# プロジェクトに必要なツールをインストール
# &&で繋げてコマンドを実行することによりレイヤーを1つとする
#apt-get update と apt-get installは同一RUN上で行う(分けると最新版を使用できない)
#RUNはイメージの作成次に実行(CMDはコンテナ起動時に実行)
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update -qq && apt-get install -y nodejs postgresql-client vim && \
    apt-get install -y yarn

# ディレクトリの作成
RUN mkdir /myapp

# 作業ディレクトリの指定
#RUN ,  COPY,  ADD 命令のみレイヤを作成するためWORKDIRは気にしなくて良い
# 絶対パスとする
WORKDIR /myapp

# Gemfileが更新された時のみ、レイヤを再構築
#先にプロジェクト全体をコピーしないのはレイヤーを分けるため
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
# ライブラリの依存関係をインストール
RUN bundle install

# プロジェクト全体をコピー(Gemfile/Gemfile.lockはコピーされない)
COPY . /myapp

#コンテナを起動する毎に実行されるスクリプトを追加
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

#コンテナの公開ポート番号の指定
EXPOSE 3000

#指定しなければコンテナ起動時にデフォルトで実行する処理
#Dockerfile では CMD 命令を 1 つしか記述できない
#ENTRYPOINT 命令に対するデフォルト引数としてCMDを使用可能
CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose

docker-compose.ymlもDocker公式ドキュメントを参考にした。

docker-compose.yml
version: '3'
services:
    db:
        # DBにpostgresを使用
        image: postgres
        # ホストの./tmp/dbと/var/lib/postgresql/dataを同期させる
        volumes:
            - ./tmp/db:/var/lib/postgresql/data
        # 環境変数の指定
        environment:
            POSTGRES_PASSWORD: password
    web:
        build: .
        # コンテナ起動時にserver.pidを削除し、rails sを実行する
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        # ホストのカレントディレクトリをコンテナの/myappと同期させる
        volumes:
            - .:/myapp
        # ホストとコンテナ間をポートフォワードする
        ports:
            - '3000:3000'
        # サービス間の依存関係
        depends_on:
            - db
        # Docker環境でByebugを使用
        stdin_open: true
        tty: true

Gemfile

Gemfileに関してはデフォルトで入っているもの主となる。
追加したパッケージはコメントしているため不要であれば削除して構わない

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


ruby '2.6.6'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.3'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  #デバックツールの導入
  gem 'pry-rails'
  gem 'pry-byebug'
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  #自動補完用ツールの導入
  gem 'solargraph'
  #静的コード解析ツールの導入
  gem 'rubocop'
  gem 'rubocop-rails'
  #erbのフォーマットツールの導入
  gem 'htmlbeautifier'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

package.json

package.jsonについてはvue-routerとvuexを使いたいため追加している
Vuetify等、UIフレームワークを使用したいようであれば追加する。

package.json
{
    "name": "myapp",
    "private": true,
    "dependencies": {
        "@rails/actioncable": "^6.0.0",
        "@rails/activestorage": "^6.0.0",
        "@rails/ujs": "^6.0.0",
        "@rails/webpacker": "4.3.0",
        "turbolinks": "^5.2.0",
        "vue": "^2.6.12",
        "vue-loader": "^15.9.3",
        "vue-template-compiler": "^2.6.12",
        "vue-router": "^3.0.1",
        "vuex": "^3.0.1"
    },
    "version": "0.1.0",
    "devDependencies": {
        "webpack-dev-server": "^3.11.0"
    }
}

Railsでデバッグする

VScode上のターミナルを開く
以下をターミナルで実行

VScode
docker-compose exec web rails console

pry が起動

VScode
[1] pry(main)> 

postコントローラーを作成

docker-compose exec web rails g controller post index

post_controller.rbを修正

post_controller.rb
class PostController < ApplicationController
  def index
    @post = Post.all
  end
end

postのモデルを作成

VScode
docker-compose exec web rails g model post name:string age:integer

DBにモデルを反映させる

VScode
docker-compose exec web rails db:migrate

index.html.erbを以下に書き換え

index.html.erb
<%= @post.name %>
<%= @post.age %>

routes.rbを以下に修正

routes.rb
Rails.application.routes.draw do
  root to:  'post#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

pryでPostモデルのインスタンスを作成

VScode
@post = Post.new
=> #<Post:0x00005589bc4beb78 id: nil, name: nil, age: nil, created_at: nil, updated_at: nil>

#まだnameやageにはデータを入れていない
#DBに保存もされていない
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[]

#DBにインスタンスを保存する
@post.save
   (0.7ms)  BEGIN
  Post Create (5.9ms)  INSERT INTO "posts" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2020-09-23 13:06:47.962085"], ["updated_at", "2020-09-23 13:06:47.962085"]]
   (3.1ms)  COMMIT
=> true

#もう一度Post.allをしてデータをDBから取得すると保存したインスタンスが呼び出される
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00005589bceec5f0
  id: 1,
  name: nil,
  age: nil,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00>]

#@postインスタンスにデータを入れてみる
@post.name = "Hiro"
=> "Hiro"

@post.age = "29"
=> "29"

#再び保存
@post.save
Post Update (4.0ms)  UPDATE "posts" SET "name" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["name", "Hiro"], ["updated_at", "2020-09-23 13:10:26.486888"], ["id", 1]]
Post Update (2.0ms)  UPDATE "posts" SET "age" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["age", 29], ["updated_at", "2020-09-23 13:10:56.785029"], ["id", 1]]
   (1.0ms)  COMMIT
=> true

#データが保存されている
Post.all
=>   Post Load (1.4ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00007f1ddc7a77a8
  id: 1,
  name: "Hiro",
  age: 29,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:10:56 UTC +00:00>]

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下のようにDBからデータを取得できればOK

スクリーンショット 2020-09-23 22.27.01.png

Binding.pryを試してみる

post_controller.rbを修正

class PostController < ApplicationController
  def index
    binding.pry
    @post = Post.all
  end
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下の画面でフリーズする(正しい挙動)
スクリーンショット 2020-09-23 22.30.19.png

pryのターミナルに戻る

pry(main)> 

以上で、pryでデバッグする環境が整った

参考

以下のサイトを参考にさせていただきました。
Docker ドキュメント日本語化プロジェクト
クィックスタート: Compose と Rails
Dockerfile のベストプラクティス
Webpacker の基本的な仕組み
Dockerを使って「Rails / PostgreSQL」の開発環境を作ろう!

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

Rails6 Vue Postgres開発環境をDocker-Composeを使って構築する方法

はじめに

Dockerを用いてRails,Vue,Posgre環境下で開発したいなと思った時に、
Rails,Vueの環境作りに手こずりましたので自分のメモとして保存します。

この記事で分かること

・Rails6とVueとPostgresのDocker環境が構築できる
・RailsとVueの連動ができる
・PryにてインスタンスとDBの操作ができる

環境

MacOS Mojave
Ruby 2.6.4
Rails 6.0.3.3
Vue 2.6.12
Webpack 4.44.2
yarn 1.22.5
Docker 2.3.0.5
VScode

Githubのリンク

僕のGithubのDocker-Start-Kitです。よろしければご活用ください。
Whiro0501/Docker_Rails6_Vue

Docker環境をMacに構築

ターミナルを開く
mkdirで任意の名前のディレクトリを作成
cdで作成したディレクトリに移動
code.でVScodeを開く

terminal
mkdir qiita
cd qiita
code .

VScodeが起動
VScode上のターミナルで作業していくので
ターミナルが起動していなければ⬆︎+Control+@でターミナルを開く

VScodeのターミナル上で以下コマンドを入力してリモートリポジトリのファイルをローカルにコピー

VScode
git clone https://github.com/Whiro0501/Docker_Rails6_Vue.git

cdでDocker_Rails6_Vue/ディレクトリに移動

VScode
cd Docker_Rails6_Vue/

以下コマンドを実行してDockerイメージをビルド

VScode
docker-compose build

以下を実行して、必要なノードモジュールを取得

VScode
docker-compose run web yarn install --check-files

以下を実行して DB(Postgres)を作成

VScode
docker-compose run web rake db:create

別のターミナルを開く
以下を実行してレールズアプリケーションを起動

VScode
#別ターミナルが億劫なら、docker-compose up -dでも良い 
docker-compose up

さらに別のターミナルを開く
Viewを更新するたび毎回コンパイルが発生し、時間がかかるため以下を実行

VScode
docker-compose exec web ./bin/webpack-dev-server

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Railsアプリケーションが起動することを確認

スクリーンショット 2020-09-23 20.42.47.png

VueとRailsの連動ってどうやるの?

初期構築時点ではRailsとVueが連動されておりませんので連動させていく。

以下でhomeコントローラを作成する(コントローラー名はなんでも良い)。

VScode
docker-compose exec web rails g controller home index

index.html.erbが作成

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

以下ファイルに"root to: 'home#index'"を追加

routes.rb
Rails.application.routes.draw do
  root to:  'home#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

まずはindex.html.erbに記述されている内容が表示

スクリーンショット 2020-09-23 21.07.31.png

Rails側で下地ができたので、次にVueとの連動の設定
app.vueの初期設定

app.vue
<template>
  <div id="app">
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      message: "Hello Vue!"
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

hello_vue.jsの初期設定

hello_vue.js
import Vue from 'vue'
import App from '../app.vue'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)

  console.log(app)
})

app.vueが単一ファイルコンポーネントであり、
hello_vue.jsにオブジェクトとして渡される。
それをindex.html.erbに表示させるよう設定する。

index.html.erbを以下の通り設定

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<%= javascript_pack_tag 'hello_vue.js' %>
<%= stylesheet_pack_tag 'hello_vue.js' %>

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Vueとの連動が完了!!

スクリーンショット 2020-09-23 21.23.06.png

Githubののファイルの説明

Whiro0501/Docker_Rails6_Vue
まず上記リンクのGithubの状態から説明する
端的に説明すると以下2つを設定した後の状態となる
従ってRails6とVueとPostgresが使用できる環境を整えることができるという理屈である。

VScode
docker-compose run web rails new . --force --database=postgresql --webpack=vue

加えて上記の状態だとPostgresがうまくRailsと連動できないため以下のように設定する。
こちらも公式ドキュメントを参考とした。

database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development


test:
  <<: *default
  database: myapp_test


production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>

Dockerfile

公式ドキュメントのベストプラクティスを参照にした。
DockerはFROM, RUN, COPY毎にレイヤーが作成されるため
RUNやCOPYはできるだけまとめると少ないレイヤーで収めることができるとのこと。

Dockerfile
FROM ruby:2.6

# `apt-get install yarn`とするとエラーになる
# プロジェクトに必要なツールをインストール
# &&で繋げてコマンドを実行することによりレイヤーを1つとする
#apt-get update と apt-get installは同一RUN上で行う(分けると最新版を使用できない)
#RUNはイメージの作成次に実行(CMDはコンテナ起動時に実行)
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update -qq && apt-get install -y nodejs postgresql-client vim && \
    apt-get install -y yarn

# ディレクトリの作成
RUN mkdir /myapp

# 作業ディレクトリの指定
#RUN ,  COPY,  ADD 命令のみレイヤを作成するためWORKDIRは気にしなくて良い
# 絶対パスとする
WORKDIR /myapp

# Gemfileが更新された時のみ、レイヤを再構築
#先にプロジェクト全体をコピーしないのはレイヤーを分けるため
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
# ライブラリの依存関係をインストール
RUN bundle install

# プロジェクト全体をコピー(Gemfile/Gemfile.lockはコピーされない)
COPY . /myapp

#コンテナを起動する毎に実行されるスクリプトを追加
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

#コンテナの公開ポート番号の指定
EXPOSE 3000

#指定しなければコンテナ起動時にデフォルトで実行する処理
#Dockerfile では CMD 命令を 1 つしか記述できない
#ENTRYPOINT 命令に対するデフォルト引数としてCMDを使用可能
CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose

docker-compose.ymlもDocker公式ドキュメントを参考にした。

docker-compose.yml
version: '3'
services:
    db:
        # DBにpostgresを使用
        image: postgres
        # ホストの./tmp/dbと/var/lib/postgresql/dataを同期させる
        volumes:
            - ./tmp/db:/var/lib/postgresql/data
        # 環境変数の指定
        environment:
            POSTGRES_PASSWORD: password
    web:
        build: .
        # コンテナ起動時にserver.pidを削除し、rails sを実行する
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        # ホストのカレントディレクトリをコンテナの/myappと同期させる
        volumes:
            - .:/myapp
        # ホストとコンテナ間をポートフォワードする
        ports:
            - '3000:3000'
        # サービス間の依存関係
        depends_on:
            - db
        # Docker環境でByebugを使用
        stdin_open: true
        tty: true

Gemfile

Gemfileに関してはデフォルトで入っているもの主となる。
追加したパッケージはコメントしているため不要であれば削除して構わない

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


ruby '2.6.6'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.3'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  #デバックツールの導入
  gem 'pry-rails'
  gem 'pry-byebug'
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  #自動補完用ツールの導入
  gem 'solargraph'
  #静的コード解析ツールの導入
  gem 'rubocop'
  gem 'rubocop-rails'
  #erbのフォーマットツールの導入
  gem 'htmlbeautifier'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

package.json

package.jsonについてはvue-routerとvuexを使いたいため追加している
Vuetify等、UIフレームワークを使用したいようであれば追加する。

package.json
{
    "name": "myapp",
    "private": true,
    "dependencies": {
        "@rails/actioncable": "^6.0.0",
        "@rails/activestorage": "^6.0.0",
        "@rails/ujs": "^6.0.0",
        "@rails/webpacker": "4.3.0",
        "turbolinks": "^5.2.0",
        "vue": "^2.6.12",
        "vue-loader": "^15.9.3",
        "vue-template-compiler": "^2.6.12",
        "vue-router": "^3.0.1",
        "vuex": "^3.0.1"
    },
    "version": "0.1.0",
    "devDependencies": {
        "webpack-dev-server": "^3.11.0"
    }
}

Railsでデバッグする

VScode上のターミナルを開く
以下をターミナルで実行

VScode
docker-compose exec web rails console

pry が起動

VScode
[1] pry(main)> 

postコントローラーを作成

docker-compose exec web rails g controller post index

post_controller.rbを修正

post_controller.rb
class PostController < ApplicationController
  def index
    @post = Post.all
  end
end

postのモデルを作成

VScode
docker-compose exec web rails g model post name:string age:integer

DBにモデルを反映させる

VScode
docker-compose exec web rails db:migrate

index.html.erbを以下に書き換え

index.html.erb
<%= @post.name %>
<%= @post.age %>

routes.rbを以下に修正

routes.rb
Rails.application.routes.draw do
  root to:  'post#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

pryでPostモデルのインスタンスを作成

VScode
@post = Post.new
=> #<Post:0x00005589bc4beb78 id: nil, name: nil, age: nil, created_at: nil, updated_at: nil>

#まだnameやageにはデータを入れていない
#DBに保存もされていない
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[]

#DBにインスタンスを保存する
@post.save
   (0.7ms)  BEGIN
  Post Create (5.9ms)  INSERT INTO "posts" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2020-09-23 13:06:47.962085"], ["updated_at", "2020-09-23 13:06:47.962085"]]
   (3.1ms)  COMMIT
=> true

#もう一度Post.allをしてデータをDBから取得すると保存したインスタンスが呼び出される
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00005589bceec5f0
  id: 1,
  name: nil,
  age: nil,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00>]

#@postインスタンスにデータを入れてみる
@post.name = "Hiro"
=> "Hiro"

@post.age = "29"
=> "29"

#再び保存
@post.save
Post Update (4.0ms)  UPDATE "posts" SET "name" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["name", "Hiro"], ["updated_at", "2020-09-23 13:10:26.486888"], ["id", 1]]
Post Update (2.0ms)  UPDATE "posts" SET "age" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["age", 29], ["updated_at", "2020-09-23 13:10:56.785029"], ["id", 1]]
   (1.0ms)  COMMIT
=> true

#データが保存されている
Post.all
=>   Post Load (1.4ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00007f1ddc7a77a8
  id: 1,
  name: "Hiro",
  age: 29,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:10:56 UTC +00:00>]

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下のようにDBからデータを取得できればOK

スクリーンショット 2020-09-23 22.27.01.png

Binding.pryを試してみる

post_controller.rbを修正

class PostController < ApplicationController
  def index
    binding.pry
    @post = Post.all
  end
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下の画面でフリーズする(正しい挙動)
スクリーンショット 2020-09-23 22.30.19.png

pryのターミナルに戻る

pry(main)> 

以上で、pryでデバッグする環境が整った

参考

以下のサイトを参考にさせていただきました。
Docker ドキュメント日本語化プロジェクト
クィックスタート: Compose と Rails
Dockerfile のベストプラクティス
Webpacker の基本的な仕組み
Dockerを使って「Rails / PostgreSQL」の開発環境を作ろう!

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

Rails6 Vueの開発環境の構築

はじめに

Dockerを用いてRails,Vue,Posgre環境下で開発したいなと思った時に、
Rails,Vueの環境作りに手こずりましたので自分のメモとして保存します。

この記事で分かること

・Rails6とVueとPostgresのDocker環境が構築できる
・RailsとVueの連動ができる
・PryにてインスタンスとDBの操作ができる

環境

MacOS Mojave
Ruby 2.6.4
Rails 6.0.3.3
Vue 2.6.12
Webpack 4.44.2
yarn 1.22.5
Docker 2.3.0.5
VScode

Githubのリンク

僕のGithubのDocker-Start-Kitです。よろしければご活用ください。
Whiro0501/Docker_Rails6_Vue

Docker環境をMacに構築

ターミナルを開く
mkdirで任意の名前のディレクトリを作成
cdで作成したディレクトリに移動
code.でVScodeを開く

terminal
mkdir qiita
cd qiita
code .

VScodeが起動
VScode上のターミナルで作業していくので
ターミナルが起動していなければ⬆︎+Control+@でターミナルを開く

VScodeのターミナル上で以下コマンドを入力してリモートリポジトリのファイルをローカルにコピー

VScode
git clone https://github.com/Whiro0501/Docker_Rails6_Vue.git

cdでDocker_Rails6_Vue/ディレクトリに移動

VScode
cd Docker_Rails6_Vue/

以下コマンドを実行してDockerイメージをビルド

VScode
docker-compose build

以下を実行して、必要なノードモジュールを取得

VScode
docker-compose run web yarn install --check-files

以下を実行して DB(Postgres)を作成

VScode
docker-compose run web rake db:create

別のターミナルを開く
以下を実行してレールズアプリケーションを起動

VScode
#別ターミナルが億劫なら、docker-compose up -dでも良い 
docker-compose up

さらに別のターミナルを開く
Viewを更新するたび毎回コンパイルが発生し、時間がかかるため以下を実行

VScode
docker-compose exec web ./bin/webpack-dev-server

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Railsアプリケーションが起動することを確認

スクリーンショット 2020-09-23 20.42.47.png

VueとRailsの連動ってどうやるの?

初期構築時点ではRailsとVueが連動されておりませんので連動させていく。

以下でhomeコントローラを作成する(コントローラー名はなんでも良い)。

VScode
docker-compose exec web rails g controller home index

index.html.erbが作成

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

以下ファイルに"root to: 'home#index'"を追加

routes.rb
Rails.application.routes.draw do
  root to:  'home#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

まずはindex.html.erbに記述されている内容が表示

スクリーンショット 2020-09-23 21.07.31.png

Rails側で下地ができたので、次にVueとの連動の設定
app.vueの初期設定

app.vue
<template>
  <div id="app">
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      message: "Hello Vue!"
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

hello_vue.jsの初期設定

hello_vue.js
import Vue from 'vue'
import App from '../app.vue'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)

  console.log(app)
})

app.vueが単一ファイルコンポーネントであり、
hello_vue.jsにオブジェクトとして渡される。
それをindex.html.erbに表示させるよう設定する。

index.html.erbを以下の通り設定

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<%= javascript_pack_tag 'hello_vue.js' %>
<%= stylesheet_pack_tag 'hello_vue.js' %>

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Vueとの連動が完了!!

スクリーンショット 2020-09-23 21.23.06.png

Githubののファイルの説明

Whiro0501/Docker_Rails6_Vue
まず上記リンクのGithubの状態から説明する
端的に説明すると以下2つを設定した後の状態となる
従ってRails6とVueとPostgresが使用できる環境を整えることができるという理屈である。

VScode
docker-compose run web rails new . --force --database=postgresql --webpack=vue

加えて上記の状態だとPostgresがうまくRailsと連動できないため以下のように設定する。
こちらも公式ドキュメントを参考とした。

database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development


test:
  <<: *default
  database: myapp_test


production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>

Dockerfile

公式ドキュメントのベストプラクティスを参照にした。
DockerはFROM, RUN, COPY毎にレイヤーが作成されるため
RUNやCOPYはできるだけまとめると少ないレイヤーで収めることができるとのこと。

Dockerfile
FROM ruby:2.6

# `apt-get install yarn`とするとエラーになる
# プロジェクトに必要なツールをインストール
# &&で繋げてコマンドを実行することによりレイヤーを1つとする
#apt-get update と apt-get installは同一RUN上で行う(分けると最新版を使用できない)
#RUNはイメージの作成次に実行(CMDはコンテナ起動時に実行)
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update -qq && apt-get install -y nodejs postgresql-client vim && \
    apt-get install -y yarn

# ディレクトリの作成
RUN mkdir /myapp

# 作業ディレクトリの指定
#RUN ,  COPY,  ADD 命令のみレイヤを作成するためWORKDIRは気にしなくて良い
# 絶対パスとする
WORKDIR /myapp

# Gemfileが更新された時のみ、レイヤを再構築
#先にプロジェクト全体をコピーしないのはレイヤーを分けるため
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
# ライブラリの依存関係をインストール
RUN bundle install

# プロジェクト全体をコピー(Gemfile/Gemfile.lockはコピーされない)
COPY . /myapp

#コンテナを起動する毎に実行されるスクリプトを追加
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

#コンテナの公開ポート番号の指定
EXPOSE 3000

#指定しなければコンテナ起動時にデフォルトで実行する処理
#Dockerfile では CMD 命令を 1 つしか記述できない
#ENTRYPOINT 命令に対するデフォルト引数としてCMDを使用可能
CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose

docker-compose.ymlもDocker公式ドキュメントを参考にした。

docker-compose.yml
version: '3'
services:
    db:
        # DBにpostgresを使用
        image: postgres
        # ホストの./tmp/dbと/var/lib/postgresql/dataを同期させる
        volumes:
            - ./tmp/db:/var/lib/postgresql/data
        # 環境変数の指定
        environment:
            POSTGRES_PASSWORD: password
    web:
        build: .
        # コンテナ起動時にserver.pidを削除し、rails sを実行する
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        # ホストのカレントディレクトリをコンテナの/myappと同期させる
        volumes:
            - .:/myapp
        # ホストとコンテナ間をポートフォワードする
        ports:
            - '3000:3000'
        # サービス間の依存関係
        depends_on:
            - db
        # Docker環境でByebugを使用
        stdin_open: true
        tty: true

Gemfile

Gemfileに関してはデフォルトで入っているもの主となる。
追加したパッケージはコメントしているため不要であれば削除して構わない

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


ruby '2.6.6'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.3'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  #デバックツールの導入
  gem 'pry-rails'
  gem 'pry-byebug'
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  #自動補完用ツールの導入
  gem 'solargraph'
  #静的コード解析ツールの導入
  gem 'rubocop'
  gem 'rubocop-rails'
  #erbのフォーマットツールの導入
  gem 'htmlbeautifier'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

package.json

package.jsonについてはvue-routerとvuexを使いたいため追加している
Vuetify等、UIフレームワークを使用したいようであれば追加する。

package.json
{
    "name": "myapp",
    "private": true,
    "dependencies": {
        "@rails/actioncable": "^6.0.0",
        "@rails/activestorage": "^6.0.0",
        "@rails/ujs": "^6.0.0",
        "@rails/webpacker": "4.3.0",
        "turbolinks": "^5.2.0",
        "vue": "^2.6.12",
        "vue-loader": "^15.9.3",
        "vue-template-compiler": "^2.6.12",
        "vue-router": "^3.0.1",
        "vuex": "^3.0.1"
    },
    "version": "0.1.0",
    "devDependencies": {
        "webpack-dev-server": "^3.11.0"
    }
}

Railsでデバッグする

VScode上のターミナルを開く
以下をターミナルで実行

VScode
docker-compose exec web rails console

pry が起動

VScode
[1] pry(main)> 

postコントローラーを作成

docker-compose exec web rails g controller post index

post_controller.rbを修正

post_controller.rb
class PostController < ApplicationController
  def index
    @post = Post.all
  end
end

postのモデルを作成

VScode
docker-compose exec web rails g model post name:string age:integer

DBにモデルを反映させる

VScode
docker-compose exec web rails db:migrate

index.html.erbを以下に書き換え

index.html.erb
<%= @post.name %>
<%= @post.age %>

routes.rbを以下に修正

routes.rb
Rails.application.routes.draw do
  root to:  'post#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

pryでPostモデルのインスタンスを作成

VScode
@post = Post.new
=> #<Post:0x00005589bc4beb78 id: nil, name: nil, age: nil, created_at: nil, updated_at: nil>

#まだnameやageにはデータを入れていない
#DBに保存もされていない
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[]

#DBにインスタンスを保存する
@post.save
   (0.7ms)  BEGIN
  Post Create (5.9ms)  INSERT INTO "posts" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2020-09-23 13:06:47.962085"], ["updated_at", "2020-09-23 13:06:47.962085"]]
   (3.1ms)  COMMIT
=> true

#もう一度Post.allをしてデータをDBから取得すると保存したインスタンスが呼び出される
Post.all
=>   Post Load (2.2ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00005589bceec5f0
  id: 1,
  name: nil,
  age: nil,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00>]

#@postインスタンスにデータを入れてみる
@post.name = "Hiro"
=> "Hiro"

@post.age = "29"
=> "29"

#再び保存
@post.save
Post Update (4.0ms)  UPDATE "posts" SET "name" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["name", "Hiro"], ["updated_at", "2020-09-23 13:10:26.486888"], ["id", 1]]
Post Update (2.0ms)  UPDATE "posts" SET "age" = $1, "updated_at" = $2 WHERE "posts"."id" = $3  [["age", 29], ["updated_at", "2020-09-23 13:10:56.785029"], ["id", 1]]
   (1.0ms)  COMMIT
=> true

#データが保存されている
Post.all
=>   Post Load (1.4ms)  SELECT "posts".* FROM "posts"
[#<Post:0x00007f1ddc7a77a8
  id: 1,
  name: "Hiro",
  age: 29,
  created_at: Wed, 23 Sep 2020 13:06:47 UTC +00:00,
  updated_at: Wed, 23 Sep 2020 13:10:56 UTC +00:00>]

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下のようにDBからデータを取得できればOK

スクリーンショット 2020-09-23 22.27.01.png

Binding.pryを試してみる

post_controller.rbを修正

class PostController < ApplicationController
  def index
    binding.pry
    @post = Post.all
  end
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

以下の画面でフリーズする(正しい挙動)
スクリーンショット 2020-09-23 22.30.19.png

pryのターミナルに戻る

pry(main)> 

以上で、pryでデバッグする環境が整った

参考

以下のサイトを参考にさせていただきました。
Docker ドキュメント日本語化プロジェクト
クィックスタート: Compose と Rails
Dockerfile のベストプラクティス
Webpacker の基本的な仕組み
Dockerを使って「Rails / PostgreSQL」の開発環境を作ろう!

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

[初心者]Rails6-Vue-Postgres開発環境をDocker-Composeを使って構築する方法

はじめに

Dockerを用いてRails,Vue,Posgre環境下で開発したいなと思った時に、
Rails,Vueの環境作りに手こずりましたので自分のメモとして保存します。

環境

MacOS Mojave
Ruby 2.6.4
Rails 6.0.3.3
Vue 2.6.12
Webpack 4.44.2
yarn 1.22.5
Docker 2.3.0.5
VScode

Githubのリンク

僕のGithubのDocker-Start-Kitです。よろしければご活用ください。
Whiro0501/Docker_Rails6_Vue

Docker環境をMacに構築

ターミナルを開く
mkdirで任意の名前のディレクトリを作成
cdで作成したディレクトリに移動
code.でVScodeを開く

terminal
mkdir qiita
cd qiita
code .

VScodeが起動
VScode上のターミナルで作業していくので
ターミナルが起動していなければ⬆︎+Control+@でターミナルを開く

VScodeのターミナル上で以下コマンドを入力してリモートリポジトリのファイルをローカルにコピー

VScode
git clone https://github.com/Whiro0501/Docker_Rails6_Vue.git

cdでDocker_Rails6_Vue/ディレクトリに移動

VScode
cd Docker_Rails6_Vue/

以下コマンドを実行してDockerイメージをビルド

VScode
docker-compose build

以下を実行して、必要なノードモジュールを取得

VScode
docker-compose run web yarn install --check-files

以下を実行して DB(Postgres)を作成

VScode
docker-compose run web rake db:create

別のターミナルを開く
以下を実行してレールズアプリケーションを起動

VScode
#別ターミナルが億劫なら、docker-compose up -dでも良い 
docker-compose up

さらに別のターミナルを開く
Viewを更新するたび毎回コンパイルが発生し、時間がかかるため以下を実行

VScode
docker-compose exec web ./bin/webpack-dev-server

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Railsアプリケーションが起動することを確認

スクリーンショット 2020-09-23 20.42.47.png

VueとRailsの連動ってどうやるの?

初期構築時点ではRailsとVueが連動されておりませんので連動させていく。

以下でhomeコントローラを作成する(コントローラー名はなんでも良い)。

VScode
docker-compose exec web rails g controller home index

index.html.erbが作成

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

以下ファイルに"root to: 'home#index'"を追加

routes.rb
Rails.application.routes.draw do
  root to:  'home#index'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

まずはindex.html.erbに記述されている内容が表示

スクリーンショット 2020-09-23 21.07.31.png

Rails側で下地ができたので、次にVueとの連動の設定
app.vueの初期設定

app.vue
<template>
  <div id="app">
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      message: "Hello Vue!"
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

hello_vue.jsの初期設定

hello_vue.js
import Vue from 'vue'
import App from '../app.vue'

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)

  console.log(app)
})

app.vueが単一ファイルコンポーネントであり、
hello_vue.jsにオブジェクトとして渡される。
それをindex.html.erbに表示させるよう設定する。

index.html.erbを以下の通り設定

index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<%= javascript_pack_tag 'hello_vue.js' %>
<%= stylesheet_pack_tag 'hello_vue.js' %>

Webブラウザで以下にアクセス

Webブラウザ
http://localhost:3000

Vueとの連動が完了!!

スクリーンショット 2020-09-23 21.23.06.png

Githubののファイルの説明

Whiro0501/Docker_Rails6_Vue
まず上記リンクのGithubの状態から説明する
端的に説明すると以下2つを設定した後の状態となる
従ってRails6とVueとPostgresが使用できる環境を整えることができるという理屈である。

VScode
docker-compose run web rails new . --force --database=postgresql --webpack=vue

加えて上記の状態だとPostgresがうまくRailsと連動できないため以下のように設定する。
こちらも公式ドキュメントを参考とした。

database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development


test:
  <<: *default
  database: myapp_test


production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>

Dockerfile

公式ドキュメントのベストプラクティスを参照にした。
DockerはFROM, RUN, COPY毎にレイヤーが作成されるため
RUNやCOPYはできるだけまとめると少ないレイヤーで収めることができるとのこと。

Dockerfile
FROM ruby:2.6

# `apt-get install yarn`とするとエラーになる
# プロジェクトに必要なツールをインストール
# &&で繋げてコマンドを実行することによりレイヤーを1つとする
#apt-get update と apt-get installは同一RUN上で行う(分けると最新版を使用できない)
#RUNはイメージの作成次に実行(CMDはコンテナ起動時に実行)
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update -qq && apt-get install -y nodejs postgresql-client vim && \
    apt-get install -y yarn

# ディレクトリの作成
RUN mkdir /myapp

# 作業ディレクトリの指定
#RUN ,  COPY,  ADD 命令のみレイヤを作成するためWORKDIRは気にしなくて良い
# 絶対パスとする
WORKDIR /myapp

# Gemfileが更新された時のみ、レイヤを再構築
#先にプロジェクト全体をコピーしないのはレイヤーを分けるため
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
# ライブラリの依存関係をインストール
RUN bundle install

# プロジェクト全体をコピー(Gemfile/Gemfile.lockはコピーされない)
COPY . /myapp

#コンテナを起動する毎に実行されるスクリプトを追加
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

#コンテナの公開ポート番号の指定
EXPOSE 3000

#指定しなければコンテナ起動時にデフォルトで実行する処理
#Dockerfile では CMD 命令を 1 つしか記述できない
#ENTRYPOINT 命令に対するデフォルト引数としてCMDを使用可能
CMD ["rails", "server", "-b", "0.0.0.0"]

docker-compose

docker-compose.ymlもDocker公式ドキュメントを参考にした。

docker-compose.yml
version: '3'
services:
    db:
        # DBにpostgresを使用
        image: postgres
        # ホストの./tmp/dbと/var/lib/postgresql/dataを同期させる
        volumes:
            - ./tmp/db:/var/lib/postgresql/data
        # 環境変数の指定
        environment:
            POSTGRES_PASSWORD: password
    web:
        build: .
        # コンテナ起動時にserver.pidを削除し、rails sを実行する
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        # ホストのカレントディレクトリをコンテナの/myappと同期させる
        volumes:
            - .:/myapp
        # ホストとコンテナ間をポートフォワードする
        ports:
            - '3000:3000'
        # サービス間の依存関係
        depends_on:
            - db
        # Docker環境でByebugを使用
        stdin_open: true
        tty: true

Gemfile

Gemfileに関してはデフォルトで入っているもの主となる。
追加したパッケージはコメントしているため不要であれば削除して構わない

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


ruby '2.6.6'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.3'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  #デバックツールの導入
  gem 'pry-rails'
  gem 'pry-byebug'
end

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  #自動補完用ツールの導入
  gem 'solargraph'
  #静的コード解析ツールの導入
  gem 'rubocop'
  gem 'rubocop-rails'
  #erbのフォーマットツールの導入
  gem 'htmlbeautifier'
end

group :test do
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
  # Easy installation and use of web drivers to run system tests with browsers
  gem 'webdrivers'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

package.json

package.jsonについてはvue-routerとvuexを使いたいため追加している
Vuetify等、UIフレームワークを使用したいようであれば追加する。

package.json
{
    "name": "myapp",
    "private": true,
    "dependencies": {
        "@rails/actioncable": "^6.0.0",
        "@rails/activestorage": "^6.0.0",
        "@rails/ujs": "^6.0.0",
        "@rails/webpacker": "4.3.0",
        "turbolinks": "^5.2.0",
        "vue": "^2.6.12",
        "vue-loader": "^15.9.3",
        "vue-template-compiler": "^2.6.12",
        "vue-router": "^3.0.1",
        "vuex": "^3.0.1"
    },
    "version": "0.1.0",
    "devDependencies": {
        "webpack-dev-server": "^3.11.0"
    }
}

参考

以下のサイトを参考にさせていただきました。
Docker ドキュメント日本語化プロジェクト
クィックスタート: Compose と Rails
Dockerfile のベストプラクティス
Webpacker の基本的な仕組み
Dockerを使って「Rails / PostgreSQL」の開発環境を作ろう!

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

Vueで著作権ページを表示したいときどうするか

前提

  • 以下記載のコードはそのままコピペしても動作は保証していません

環境

  • macOS 10.15.6
  • OnsenUI

検討

  • nodeライブラリを使用しているということで、すべての著作権はnode_modules配下にある
  • そこからライセンスを抜き出し表示すれば良い

手順

jsonファイルを作成する

  • このシェルを使用し、jsonファイルを作成する
    • ※生成されたjsonファイルは完璧ではございません
    • 生成されたjsonファイルから以下を行う
      • 一番下の,を削除する
      • 外枠の {}を追加する
      • 改行を全て削除する
generateJson.sh
#!/bin/bash
for i in `ls node_modules/*/LICENSE node_modules/*/LICENCE`;
do
    license_name=$(echo $i|sed 's/node_modules\///g'|sed 's/\/LICENSE//g'|sed 's/\/LICENCE//g');
    license_text=$(cat ${i}|sed -e 's/\"/\\\"/g');
    json=$(printf '
    "%s": {
        "license_name": "%s",
        "license_text": "%s"
        },' "${license_name}" "${license_name}" "${license_text}")
    echo ${json}
done > ./licenses.json

著作権をリストで表示するページを作成する

  • 上記で作成したjsonファイルをimportし、ライセンスの名前をリストで表示する
Copyright.vue
<template>
  <v-ons-page>
    <v-ons-list class="scroll_erea">
      <v-ons-list-item
        v-for="(license, key) in licenses"
        :key="key"
        class="copyright__item"
        @click="onLicenseClick(license, key)">
        <div class="copyright__item__title">
          {{ key }}
        </div>
      </v-ons-list-item>
    </v-ons-list>
  </v-ons-page>
</template><script>
import CopyrightDetail from '@/components/pages/CopyrightDetail'
import licensesJson from '@licenses'

export default {
  name: 'Copyright',
  data () {
    return {
     licenses: licensesJson
  {
  methods: {
    onLicenseClick (license, key) {
      const props = {
        licenseName: license.license_name,
        licenseText: license.license_text
      }
      // ここにCopyrightDetailページにpropsを渡して遷移する処理を入れてください
    }
  }
}
</script>
<style lang="scss" scoped>
.scroll_erea {
  position: absolute;
  margin: 0 auto;
  width: 100%;
  height: 100%;
  overflow-y: scroll;
}
</style>

著作権の詳細を表示するページを作成する

  • propsで受け取った著作権の詳細テキストを表示する
CopyrightDetail.vue
<template>
  <v-ons-page>
    {{ licenseText }}
  </v-ons-page>
</template><script>
import { mapMutations } from 'vuex'

export default {
  name: 'CopyrightDetail',
  props: {
    licenseName: {
      type: String,
      required: true
    },
    licenseText: {
      type: String,
      required: true
    }
  }
}
</script>

こうなったらいいのにと思うこと

  • シェル一髪で著作権のjsonファイルを作成できる
  • nodeのライブラリが多すぎるとDOMレンダリングのforループでかなり時間がかかるため、そこを短縮する処理を入れる

おわりに

  • こうすればnodeライブラリの著作権情報をVueで簡単に表示できるよ、などあれば教えてください
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】Vue.jsの基礎知識

Vue.js とは

  • javascriptのフレームワークの一つ。
  • JQueryよりも記述が簡単。
  • ディレクティブ機能により、HTMLとjavascriptの間でデータ連携を行う役割を果たす。
  • Raectが単方向のデータバインディングであるのに対し、Vue.jsは双方向のデータバインディングが可能(※)。
  • React、Angularに並び人気があるフレームワーク。
  • ECMAScript 5 準拠のブラウザに対応。IE8以前のバージョンはサポートされていないため注意。 (詳しくはこちらを参照)

双方向データバインディング
UI、データのいずれか一方が更新されれば、もう一方も更新される仕組みのこと。(=データとUIが常に同期される)

Vue.jsの読み込み(インストール)

事前にお使いのブラウザにVue Devtoolsをインストールしておくことをお勧めします。
筆者はchromeを使うので拡張機能vuejs-devtoolsを入れました。

Vue.jsの読み込み方法

  1. CDN ←プロトタイピングや学習を目的とする場合はこちらがオススメ
  2. NPM
  3. 直接組み込み

今回は学習用なので、CDNを読み込みました。

下記コードをbody終了タグの直前に挿入すれば完了。

html
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
or
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

(例1)html内にスクリプトを記述する場合

html
<body>
  <!-- html -->
  <div>
     <span>TEST</span>
  </div>

  <!-- js -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
      // js記述部分
  </script>
</body>

(例2)jsファイルを別途作成する場合

html
<body>
  <!-- html -->
  <div>
     <span>TEST</span>
  </div>

  <!-- js -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="js/test.js"></script>  // vue.jsの下で読み込むこと
</body>

Vue.jsの基礎

目次

1.マスタッシュ構文(単方向データバインディング)
2.ディレクティブ
  2-1.v-bind
  2-2.v-if
  2-3.v-show
  2-4.v-for
  2-5.v-on
  2-6.v-model(双方向データバインディング)
3.コンポーネント

1.マスタッシュ構文(単方向データバインディング)

マスタッシュ構文とは?

{{ }}で囲った構文のこと。({{ }}がマスタッシュ=口ひげに見えるからこのような名前になったとか)
マスタッシュ構文を使用することで、Vueインスタンス内に作ったdataオブジェクトのプロパティを反映させることができます。

まずは「Hello World」

次のようなディレクトリ構造で作成していきます。
image.png

まずはhtml内に次のように書いてみましょう。

main.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="css/common.css">
        <link rel="stylesheet" href="css/main.css">
        <title>Main</title>

    </head>
    <body>
        <div id="app">
            {{ message }}
        </div>

        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="js/main.js"></script>
    </body>
</html>
  • <div>タグにappというidを付与し、その中にマスタッシュ構文で{{ message }}と記述しました。
  • この時点でhtmlを開いても、{{ message }}がテキストとして表示されるのみです。

次にjsファイルに次のように記述。

main.js
var app = new Vue({

    el: '#app',
    data: {
        message: 'Hello World!'

    }
})

  • var 変数名 = new Vue({ ... }) で新規のVueインスタンスが作成できます。
  • el:(elementの略)に#appと指定することで、<div id="app">内にのみ有効なVueインスタンスとすることが出来ます。 (id名の先頭に#を付けるところはCSSでの指定方式と同じです。)
  • data: {}内にmessage: 'Hello World!'と記述します。

結果

image.png
js側で宣言したmessageの内容がDOMに反映されていることが分かります。

2.ディレクティブ

ディレクティブとは?

v-から始まるデータバインディング(HTMLとjavascriptを紐づける)を行うための機能。

2-1.v-bind

v-bindで要素属性のバインディングを行うことが出来ます。

main.html
        <div id="app-2">
            <span v-bind:title="message">
                今何時?
            </span>
        </div>
main.js
var app2 = new Vue({

    el: '#app-2',
    data: {
        message: '現在時刻は ' + new Date().toLocaleString()
    }

})

【html】

  • v-bind:[属性名]=""で要素属性にdataオブジェクト内のプロパティをバインディングします。

【js】

  • マスタッシュ構文の時と同様、Vueインスタンスを作成し、el:<div>タグのidを記述します。
  • dataオブジェクト内にはmessage: '現在時刻は ' + new Date().toLocaleString()と記述します。 (new Date().toLocaleString()で、ローカル言語に合わせた書式の日時を取得できます。)

結果

image.png
「今何時?」にカーソルを合わせると、title属性にバインディングされた現在日時が表示されました。

注意点

タグの属性内にマスタッシュ構文を使うことは出来ません。
属性に適用したい場合は上記の手順でv-bindを使用した書き方にしてください。

main.html
        <div id="app-2">
            <span title={{ message }}>  ←このような記述は出来ません!
                今何時?
            </span>
        </div>

2-2.v-if

v-ifで要素の表示/非表示を切り替えることが出来ます。

main.html
        <div id="app-3">
            <span v-if="seen">
                見えるかな?
            </span>
        </div>
main.js
var app3 = new Vue({

    el: '#app-3',
    data: {
        seen: true
    }

})
  • js側でseenプロパティを作成します。
  • html側で表示/非表示を切り替えたい要素内にv-if="seen"と記述します。

結果

image.png
seen: trueのため、「見えるかな?」というテキストが表示されました。

試しに[F12]でデベロッパーツールを起動し、コンソールからapp3.seen = falseと打ってみましょう。
image.png

「見えるかな?」が非表示になりました。
image.png

DOMを見るとコメントアウトになっています。
image.png

2-3.v-show

v-showでもv-ifと同じように、要素の表示/非表示を切り替えることが出来ます。

main.html
        <div id="app-3">
            <span v-show="seen">
                見えるかな?
            </span>
        </div>
main.js
var app3 = new Vue({

    el: '#app-3',
    data: {
        seen: true
    }

})

v-ifとv-showの違い

v-ifでfalse指定した際には要素内がコメントアウトになったのに対し、
v-showの場合はdisplay: none;が付与されます。(cssのdisplayプロパティと同じはたらき)
v-ifより軽いため、頻繁に表示/非表示を変える場合はv-showがおすすめです。
image.png

2-4.v-for

v-forを使って、配列内のデータを反映させることが出来ます。

main.html
        <div id="app-4">
            <ol>
                <li v-for="todo in todos">
                    {{ todo.text }}
                </li>
            </ol>
        </div>
main.js
var app4 = new Vue({

    el: '#app-4',
    data: {
        todos: [
            { text: 'りんご' },
            { text: 'みかん' },
            { text: 'ぶどう' }

        ]
    }

})
  • dataオブジェクト内にtodosという配列を作成します。
  • v-for="[変数] in [配列名]"で配列の内容を取得するループ処理を作成出来ます。
  • マスタッシュ構文内で[変数]を使用することが出来ます。

結果

image.png
dataオブジェクト内に作った配列をDOMに反映させることができました。

2-5.v-on

v-onでmethods内の処理を実行させることが出来ます。

main.html
        <div id="app-5">
            <p>{{ message }}</p>
            <button v-on:click="reverseMessage">メッセージをひっくり返すよ</button>
        </div>
main.js
var app5 = new Vue ({

    el: '#app-5',
    data: {
        message: 'stressed'
    },
    methods: {
        reverseMessage: function() {
            this.message = this.message.split('').reverse().join('')
        }
    }
})
  • dataオブジェクトに加えてmothodsオブジェクトを作成します。
  • methodsオブジェクト内にreverseMessageというfunctionを作成します。
  • function内にmessageの文字列を逆にする処理を記述します。(thisはVueインスタンス(app5)自身を指しています。)
  • html側には、messageのマスタッシュ構文とその下に<button>タグを記述します。
  • <button>タグ内にv-on:[イベント]="[処理名]"の形で記載すると、Vueインスタンスのmethods内の処理が呼び出されます。
  • 今回はクリック時にreverseMessageを実行したいので、v-on:click="reverseMessage"と記述します。

結果

ボタンクリック前
image.png
ボタンクリック後
image.png

2-6.v-model(双方向データバインディング)

【双方向データバインディングの特徴】
 ・jsのdata側とテンプレート側の両方で変更可能。
 ・テンプレートの値を変更すると、dataオブジェクトのプロパティも変更される。

main.html
        <div id="app-6">
            <p>{{ message }}</p>
            <input v-model="message">
        </div>
main.js
var app6 = new Vue({

    el: '#app-6',
    data: {
        message: 'こんにちは!'
    }
})
  • <p>タグの中にマスタッシュ構文を使いmessageの内容(こんにちは!)を出力するようにしました。
  • 直後の<input>タグでv-model="[オブジェクト名]"と指定することで、<p>タグ同様messageの内容を適用しています。

結果

image.png
テキスト、テキストボックスの両方にmessageの内容が出力されました。

image.png
試しに下のテキストボックスの文字列を変更すると、上のテキストもそれに連動して書き換えられます。

3.コンポーネント

コンポーネントとは?

必要なパーツ(template(HTMLタグ)、methods等)を「コンポーネント」としてパッケージ化できる機能。
一度コンポーネントとして登録すれば再利用が可能なため、同じ組み合わせのタグを繰り返し使う時などに便利。

本項はコチラの動画の記述を参考にいたしました。
Vue.js入門 #08:コンポーネントで再利用可能なパーツを作ろう

main.html
        <div id="app-7">
            <alert-box>IDが未入力です。</alert-box>
        </div>
main.js
Vue.component('alert-box',{
    template: `
    <div class="alert">
        <strong>Error!</strong>
        <slot></slot>
    </div>
    `
})
var app7 = new Vue({
    el: '#app-7'
})
main.css
.alert{
    background-color: #fcc;
    padding: 10px;
    border: 1px solid #f33;
}
  • Vue.component('[コンポーネント名]',{template: [HTMLタグ]})で、コンポーネントを登録します。
  • template:内には、コンポーネント化したいHTMLタグを記述します。
  • <slot>タグは、コンポーネントを使用時にタグ中に記述したテキスト(「IDが未入力です。」)を反映させるための独自タグです。
  • あわせて、Vueインスタンスの作成も忘れず行ってください。
  • コンポーネント化したタグにalertというクラスを付与し、cssにてスタイル編集を行っています。
  • <[コンポーネント名]>で、登録したコンポーネントを使用することが出来ます。

結果

image.png
image.png

  • <alert-box>タグで囲った箇所が、コンポーネントとして登録したHTMLタグ(<div class="alert">...)に置き換わっています。
  • <alert-box>タグ内に記述したテキスト(「IDが未入力です。」)が、<slot>タグによって文字列として適用されています。

コンポーネントの再利用

次のように、<alert-box>を繰り返し使うと・・・

main.html
        <div id="app-7">
            <alert-box>IDが未入力です。</alert-box>
            <alert-box>IDが未入力です。</alert-box>
            <alert-box>IDが未入力です。</alert-box>
            <alert-box>IDが未入力です。</alert-box>
        </div>

その分だけコンポーネントが適用されます。
このように、一度登録したコンポーネントは再利用可能なことが分かります。
image.png

mothodsの追加

コンポーネント内にはHTMLタグだけでなく、処理も記述することが出来ます。

main.js
Vue.component('alert-box',{
    template: `
    <div class="alert" v-on:click="caution">
        <strong>Error!</strong>
        <slot></slot>
    </div>
    `,
    methods: {
        caution: function(){
            alert('クリックされました。');
        }
    }
})
var app7 = new Vue({
    el: '#app-7'
})
  • template:内の<div>タグに、v-on:click="caution"を追記しました。
  • methods:を追記し、画面上に「クリックされました。」というアラートを表示させるcaution処理を追記しました。

image.png

[Error!]をクリックすると、アラートが表示されました。

さいごに

本記事で紹介したものは基礎知識となります。
ディレクティブ機能などの独自の記述が多いですが、慣れればシンプルで非常に書きやすいフレームワークだと感じました。
学習目的であればCDNで読み込みも簡単なので、ご興味のある方は是非お試しいただければと思います。

Vue.js学習に役に立ったサイトまとめ

公式サイト(ガイド)
公式サイト(Vueオプションオブジェクト一覧)
基礎から学ぶVue.js
【Vue.js】vue.jsの基礎(yuta-38さん)
【Vue.js】Vue.jsの基礎(smkhkcさん)
Vue.js入門(動画)

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

V-forでsliceを 使ってlist array を制限する

comment.vue
上に
<v-for="comment in comments" :key"comment.id">
{{id}} {{name}}
</div>
<button :disabled="currentPage == 1" @click="prevPage">少なく表示する</button>
      <button :disabled="currentPage == totalPages" @click="nextPage">
        もっと見る
      </button>

Scriptのところに下記を入れる

comment.vue
 currentPage: number = 1
//コメントをsliceを使ってページごとに5個ずつ出す
  get comments() {
    return this.visaCommentResponse.slice(0, this.currentPage * 5)
  }
//ページの数をわかる
  get totalPages() {
    return Math.ceil(this.commentCount / 5)
  }
//もっと見るボタン
  nextPage() {
    if (this.currentPage < this.totalPages) this.currentPage++
  }
//少なく表示するボタン
  prevPage() {
    this.currentPage = this.currentPage - 1 || 1
  }

結果はこれです。
Screen Shot 2020-09-23 at 16.15.19.png

EDIT* もう少し簡単にできた。

script のところに

comment.vue
commentsShown?: number = 10

get comments() {
    return this.visaCommentResponse?.slice(0, this.commentsShown)
  }

  loadMore() {
    this.commentsShown += 10
  }

  loadLess() {
    this.commentsShown -= 10
  }

ボタンは

comment.vue
<button
        v-if="
          visaCommentResponse.length > 10 &&
            commentsShown < visaCommentResponse.length
        "
        @click="loadMore"
      >
        Load More
      </button>
      <button v-if="commentsShown > 10" @click="loadLess">
        Load less
      </button>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでSassを使用する方法

Vue.jsでScssを使用したらエラーが出たので解決方法をまとめておきます。

複数の方法があるらしいですが、以下が簡単と感じました。
参考:プロジェクト作成時に設定する方法プロジェクト作成後に反映する方法(今回)

以下のようにstyleへScssの指定を追加して下さい。

<style lang="scss"> //この部分に追記
</style>

これだけだと次のようなエラーが出ますので、
Failed to resolve loader: sass-loader You may need to install it.
ターミナルで以下を実行します。

npm install sass-loader node-sass --save-dev

あとはいつものようにScssを記述していけば、自動でコンパイルしてくれます。

<style lang="scss">
  .nav {
    ul {
      li {
          list-style-type: none;
      }
    }
  }
</style>

簡単ですね!

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

Vue3で公開された実験的機能の紹介

はじめに

9/18 に v3.0.0 One Piece が公開されました。
その中に Experimental Features として以下の 2 つが挙げられていたため、簡単に紹介してみようと思います。
※既に Vue v3.0.0 で利用できます。
※記事作成時の仕様となります。今後変わる場合もあります。

(原文)

We have proposed two new features for Singe-File Components (SFC, aka .vue files):

  • <script setup>: syntactic sugar for using Composition API inside SFCs
  • <style vars>: state-driven CSS variables inside SFCs

These features are already implemented and available in Vue 3.0, but are provided only for the purpose of gathering feedback. They will remain experimental until the RFCs are merged.

1. <script setup>: syntactic sugar for using Composition API inside SFCs

概要

詳細はこちらを参照してください。
Composition API の setup 関数のシンタックスシュガーになります。
具体的には、

<script>
import { ref } from "vue";

export default {
  setup() {
    const count = ref(0);
    const inc = () => count.value++;

    return {
      count,
      inc,
    };
  },
};
</script>

これが、

<script setup>
import { ref } from "vue";

export const count = ref(0);
export const inc = () => count.value++;
</script>

これになります。
setup 関数の定型文が大幅に省略できることが分かります。
また、逐次exportできるため、最後にまとめてreturnする必要がなくなります。

Component の export

こちらもシンプルに書けます。

<template>
  <Foo />
  <Bar />
  <component :is="ok ? Foo : Bar" />
</template>

<script setup>
export { default as Foo } from "./Foo.vue";
export { default as Bar } from "./Bar.vue";
export const ok = Math.random();
</script>

props の宣言と他のオプションの使い方

setup 関数以外の option を利用するためには、export default を使用します。
また、setup 関数の中で props を使用する場合は、 <script setup="props"> のように記述します。

<script setup="props">
import { computed } from "vue";

export default {
  props: {
    msg: String,
  },
  inheritAttrs: false,
};

export const computedMsg = computed(() => props.msg + "!!!");
</script>

※Compile の都合上、export default内でこの外で宣言された値(ex. computedMsg)を使用できません。

Typescript のサポート

<script setup>のほとんどが Typescript で動作します。
propsemit等の setup 関数の引数を使用する場合はdeclareで宣言します。

<script setup="props, context" lang="ts">
import { computed, SetupContext } from "vue";

// declare props using TypeScript syntax
// this will be auto compiled into runtime equivalent!
declare const props: {
  msg: string;
};
declare const context: SetupContext;

export const computedMsg = computed(() => props.msg + "!!!");
export const onClick = () => context.emit("click", "sample");
</script>

※しかし、この方法で props の型を定義する場合、propsdefaultrequired を指定できませんでした。
export defaultで定義しても、declare の設定で上書きしてしまうようです。
もし指定方法を知っている方がいらっしゃいましたら、コメントいただけると嬉しいです。

Typescript 使用時、Component を import(re-exporting) する際に気をつけること

通常、<script setup>を使用する際はexport defaultは記述する必要はありません。
しかし、その Component を import する側が Typescript を使用している場合、export defaultを使っていないと Vetur がエラーを吐きます。
そのため、export default {}と空の Object を return しましょう。

<!--子-->
<script setup></script>

<!--親-->
<script setup>
// Module '"../components/Child.vue"' has no exported member 'default'.Vetur(2305)
export { default as Child } from "@/components/Child.vue";
</script>

通常の<script>との併用

<script setup>を使用した場合、内部の Script は全て setup 関数でラップされてしまいます。
そのため、他のファイルから import できる値の宣言や、一度しか実行したくない関数の実行をしたい場合は、<script><script setup>を併用します。

<script>
performGlobalSideEffect();

// this can be imported as `import { named } from './*.vue'`
export const named = 1;
</script>

<script setup>
import { ref } from "vue";

export const count = ref(0);
</script>

2. <style vars>: state-driven CSS variables inside SFCs

後ほど追記します。
詳細はこちらを参照してください。

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