Rails APIモードのテンプレートリポジトリを作った【解説編】
概要
前回の記事の解説です。
learning-mind.hatenablog.com
テンプレートリポジトリ
github.com
Gemの選定について
「Rails Gem おすすめ」で検索してヒットした記事をいくつか読みつつ、普段仕事で使っているGemと合わせて選びました。
どんなプロダクトでも必要になると思われるものを選んだつもりです。
Gemの中にはコードの保守性を高めたり脆弱性を検知するものをいくつか含めています。
- rubycritic
- rubocop
- bundler-audit
- bullet
- rails_best_practices
- brakeman
これらは後から導入すると既存のコードの大部分を変更しなければならないケースが多いです。
初めから入れておいて型にはまったコーディングができれば、長期的に見たときに開発効率が高まると考えて選びました。
もしチーム開発を前提とするならやはりこういうGemは先に入れてメンバー間の認識を早期に固めておいた方が気持ちよく開発作業が進むと思います。
Docker
前回の記事で
rails newで作成したファイルに極力変更を加えない
と書きましたがDockerfileだけはそのままでは動かせず、シンプルなものをゼロベースで作成しました。
参考としてRailsが自動で作成するものを以下に記載します。
# syntax = docker/dockerfile:1 # Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile ARG RUBY_VERSION=3.2.2 FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base # Rails app lives here WORKDIR /rails # Set production environment ENV RAILS_ENV="production" \ BUNDLE_DEPLOYMENT="1" \ BUNDLE_PATH="/usr/local/bundle" \ BUNDLE_WITHOUT="development" # Throw-away build stage to reduce size of final image FROM base as build # Install packages needed to build gems RUN apt-get update -qq && \ apt-get install --no-install-recommends -y build-essential git libpq-dev libvips pkg-config # Install application gems COPY Gemfile Gemfile.lock ./ RUN bundle install && \ rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ bundle exec bootsnap precompile --gemfile # Copy application code COPY . . # Precompile bootsnap code for faster boot times RUN bundle exec bootsnap precompile app/ lib/ # Final stage for app image FROM base # Install packages needed for deployment RUN apt-get update -qq && \ apt-get install --no-install-recommends -y curl libvips postgresql-client && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives # Copy built artifacts: gems, application COPY --from=build /usr/local/bundle /usr/local/bundle COPY --from=build /rails /rails # Run and own only the runtime files as a non-root user for security RUN useradd rails --create-home --shell /bin/bash && \ chown -R rails:rails db log storage tmp USER rails:rails # Entrypoint prepares the database. ENTRYPOINT ["/rails/bin/docker-entrypoint"] # Start the server by default, this can be overwritten at runtime EXPOSE 3000 CMD ["./bin/rails", "server"]
参考記事
作業手順
テンプレートリポジトリを作成したときに使ったコマンドを雑に列挙します。
概略
コマンド
brew update & brew upgrade rbenv install 3.2.2 rbenv rehash rbenv global 3.2.2 rbenv versions ruby -v gem update --system gem search -l rails gem search rails | grep "^rails (" gem install rails -N rails -v brew install postgresql gem install pg -v 0.18.4 rails new rails-api-template --api -d postgresql # 以下Gem初期設定 docker compose run web rails generate rspec:install docker compose run web bundle exec rails g rswag:api:install docker compose run web bundle exec rails g rswag:ui:install docker compose run web bundle exec rails g rswag:specs:install docker compose run web bundle exec rails g bullet:install docker compose run web bundle exec rubocop --auto-gen-config
今回はローカルPCでrails newを実行しましたが、dockerで実行してプロジェクトを作成することもできます。
感想
テンプレートができたので今後新しいプロダクトを作り始めるときの心理的ハードルが下がりました。
今回触ったGemに対して解像度が高まったように思います。
Dockerに関して、可能な限り自動で作成されたものを使用したかったのでかなり時間を使ってマルチステージビルド等の設定を調べましたがもう少し早めに見限って今の形にした方が良かったかもしれません。
どの道Dockerfileはプロダクトの環境に応じて書き直すことになりますしね。
この記事はkb advent calendar24日目の記事です。
Rails APIモードのテンプレートリポジトリを作った(2023年12月版)
完成したリポジトリ
なぜやるのか
新規プロジェクトを立ち上げるときに初動で躓きたくなかったからです。
早く作って試せる状態にしておいた方がRailsメリットを活かせると考えています。
方針
- Rails APIモード
- cloneしてから以下の手順でRailsのデフォルトページが表示されるリポジトリ
- コンテナをビルド
- マイグレーション
- コンテナ起動
- 開発環境のみで本番環境は想定しない
- rails newで作成したファイルに極力変更を加えない
- credentialsを.gitignoreに記載
- 作業するときにうっかり上げないようにしたかったからです
- READMEに再発行が必要な旨を記載しました
- どんなプロダクトでも必要になるGemをインストールし初期設定を行う
Ridgepoleは賛否両論あるようなので未インストール
作成手順や詰まったところは別記事で
あとがき
この記事はkb advent calendar17日目の記事です。
Flutter APIキー等を記載した秘匿ファイルを読み込むflutter_dotenv
やりたいこと
Flutterアプリで使うAPIキーをソースファイルに書かずに使用したい。
やり方
.env
ファイルを作成する.env
を.gitignoreに追記する- flutter_dotenvをインストールする
main()
で.env
ファイルを読み込む- 必要な箇所で環境変数を読む
.env
ファイルを作成する
プロジェクトのルートディレクトリに作成します。
.env
HOGEHOGE=hogehoge
.env
を.gitignoreに追記する
.gitignore
.env
flutter_dotenvをインストールする
下記を記載してUpgradeを実行。
pubspec.yaml
dependencies: flutter_dotenv: ^3.1.0
ソースファイルでimport。
main.dart
import 'package:flutter_dotenv/flutter_dotenv.dart' as DotEnv;
main()
で.env
ファイルを読み込む
main.dart
Future main() async { await DotEnv.load(); runApp(MyApp()); }
ライブラリは非同期で動作するようなのでasync
await
が必要。
必要な箇所で環境変数を読む
main.dart
DotEnv.env['HOGEHOGE']
HOGEHOGE
は.env
に記載した環境変数名です。
備考
flutter_dotenvの詳細は下記ページを参照。
pub.dev
flutter_dotenvのバージョン3系と2系では書き方が異なるようです。
Flutterアプリの動作確認用テンプレート
備忘録として。
import 'package:flutter/material.dart'; void main() => runApp(RootWidget()); class RootWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: TestWidget(), ); } } class TestWidget extends StatefulWidget { @override TestWidgetState createState() => new TestWidgetState(); } class TestWidgetState extends State<TestWidget> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Startup Name Generator'), ), body: Center( child: Text('Text'), ), ); } }
実行結果
intellij使うときに.gitignoreに書くこと
# Ignore intellij .idea/* .generators **/dgph # Flutterの場合
Javascript cookie内の特定の値のみを取り出す正規表現
hogehoge_cookieの値だけを取り出す
document.cookie.replace(/(?:(?:^|.*;\s*)hogehoge_cookie\s*\=\s*([^;]*).*$)|^.*$/, "$1")
こういうノウハウって運良く知る機会があったので助かりましたが、知らないままだと自力で正規表現作ってたんだろうなって思うと九死に一生を得た気分になりますね。
この記事はkb Advent Calendar 2020 15日目の記事です。
追記(2020/12/16)
もっといいやり方ありました。
developer.mozilla.org
備忘録としてサンプルアプリを書いておきます。
<!DOCTYPE html> <html> <head> <title>Test</title> <script> document.cookie = "test1=Hello"; document.cookie = "test2=World"; const cookieValue = document.cookie .split('; ') .find(row => row.startsWith('test2')) .split('=')[1]; function alertCookieValue() { alert(cookieValue); } </script> </head> <body> <button onclick="alertCookieValue()">Show cookie value</button> </body> </html>
Rails APIモード + Vue.jsでアプリを作成する手順
やること
フロントエンドアプリケーションはVue.jsで実装し、バックエンドアプリケーションはAPIサーバーとしてのみ機能するシステムを作ること。
ルーティングはVue Routerで行い、Railsのテンプレートエンジンは使用しない。
環境構築
OS
Virtualbox + Vagrantを使います。
Downloads – Oracle VM VirtualBox
Downloads | Vagrant by HashiCorp
アプリのファイルを変更しやすくしたいので、ホストマシンで見られるように共有ファイルの設定もしました。
qiita.com
Rails
Rails6でやります。
インストールは以前やったので省略。
learning-mind.hatenablog.com
※バージョンが更新されたせいか、node.jsとかyarnのインストールでエラーになることがありましたのでご注意。
qiita.com
Rails APIモードでアプリを作成する
rails new
のオプションに--api
を加えます。
rails new sample --api
Vue.js
Vue.js 2.6.12でやります。
Vue CLIのインストール
nodeバージョン確認
$ node --version v15.3.0
node.jsがインストールされていない場合は下記ページを参照してインストールします。
prog-8.com
インストール
npm install -g @vue/cli
※Macで実行したところ失敗したので下記記事の手順で解決しました。
qiita.com
バージョン確認
$ vue -V @vue/cli 4.5.9 $ npm list -g vue /.nvm/versions/node/v15.3.0/lib └─┬ @vue/cli@4.5.9 ├─┬ vue-codemod@0.0.4 │ ├─┬ @vue/compiler-sfc@3.0.4 │ │ └── vue@3.0.4 deduped │ └── vue@3.0.4 └── vue@2.6.12
プロジェクトを作成
vue create projectname
利用するプリセット(Vue.jsのバージョン)とパッケージマネージャを訊かれます。
- プリセットは
default vue2
を選択 - パッケージマネージャは
yarn
を選択
※Macで実行したときのエラーは下記記事の手順で解決しました。
blog.kimizuka.org
アプリを起動
cd projectname npm run serve
コンソールに表示されるURLをブラウザで表示
プロジェクトをビルド
npm run build
プロジェクトルートディレクトリの配下にdist
というディレクトリが作成されます。
このディレクトリにあるファイルをHTTPサーバーに置くとかなんとか。(今回は使いません)
Vue Routerをインストール
vue add router
コーディング
Rails
render json: {[JSONオブジェクト]}
これをやればいいだけです。
モデルの作成
下記記事を参照しました。
qiita.com
rails g model post title:string rails g controller posts rails db:create rails db:migrate
コントローラにJSONを返す処理を追加
app/controllers/posts_controller.rb
class PostsController < ApplicationController def index posts = Post.all render json: posts end end
ルートを追加
config/routes.rb
Rails.application.routes.draw do resources :posts end
テスト用のレコードを用意
rails c
を実行して
irb(main):003:0> Post.create(title: "テスト用のタイトル")
APIの動作確認
X
はサーバーのIPアドレス。3000
はRailsのデフォルトのポート番号。
$ curl http://XXX.XXX.XXX.XXX:3000/posts [{"id":1,"title":"テスト用のタイトル","created_at":"2020-12-13T22:20:41.884Z","updated_at":"2020-12-13T22:20:41.884Z"}]
クロスドメイン制約対策
APIサーバー側で設定が必要です!
qiita.com
config/application.rb
config.action_dispatch.default_headers = { 'Access-Control-Allow-Credentials' => 'true', 'Access-Control-Allow-Origin' => 'http://[フロントエンドアプリケーションのIPアドレスとポート番号]', 'Access-Control-Request-Method' => '*' }
(これに気付かず、フロント側でどうにかしようとしててめちゃくちゃ時間を使いました…)
Rails側はこれでOKです。
Vue.js
ルーティング
生成されたVue Routerのファイル
src/router/index.js
import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') } // ★遷移先が増える場合はここに追加していく ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
ルーター経由でページを遷移させる場合は、<router-link>
要素を使う。
src/App.vue
<template> <div id="app"> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> </div> <router-view/> </div> </template>
遷移先のパスはv-bind:to=
属性でも指定できる
<router-link v-bind:to="'/about'">About</router-link>
nameプロパティでも指定でき、これを名前付きルート
という。
<router-link v-bind:to="'{ name: 'about' }">About</router-link>
ルーター経由で呼び出されたコンポーネントは、<router-view/>で確保された領域に反映される。
プログラムでルーター経由のページ移動
$router.push([パス名])
ルーター経由では以下のコンポーネントにパラメータを渡す
- ルートを追加する
- ルートパラメータを受け取る
リンク文字列を生成する
ルートを追加する 前述の★の部分に新規のルートの情報を追加します。
src/router/index.js
import Article from '../views/Article.vue' .. { path: '/article/:aid', name: 'Article', component: Article }
:aid
はプレースホルダです。
- ルートパラメータを受け取る
$route.params.aid
aid
はプレースホルダです。
今回は受け取り先として新規ビューファイルを作成します。
src/views/Article.vue
<template> <span> 記事コード:{{ $route.params.aid }} </span> </template>
- リンク文字列を生成する src/App.vue
<router-link to="/article/108">記事:No.108</router-link>
axiosのインストール
npm install axios
※CDN版を使おうとしたのですが、単一ファイルコンポーネントで使用する方法がわかりませんでした……。
axiosでAPIリクエスト
Aboutページに表示させてみます。
src/views/About.vue
<template> <div class="about"> <h1>This is an about page</h1> {{ info }} </div> </template> <script> import axios from 'axios' export default { data () { return { info: null, } }, created () { axios .get('http://[APIサーバーのIPアドレス:3000]/posts') .then(response => (this.info = response)) } } </script>
Vue.jsもこれでOK。
動作確認
rails s -b 0.0.0.0
Vue.js
npm run serve
Vue.jsアプリのIPアドレスとポート番号をブラウザに打ち込んで、「About」のリンクをクリックします。
ページ下部に先程確認したJSONのデータが表示されていれば成功です。
この後、JSONを解釈して表示すべき状態に開発を進めることができます。
参考書籍
この記事はkb Advent Calendar 2020 6日目の記事です。