Rails 5.2のCredential管理方法
背景
RailsからS3にファイルをアップロードするときに、認証情報を環境変数に持たせようとしていたが、どうやらそれをする必要はないということがわかった。
今回はそれをまとめます。
環境情報は以下。
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04.4 LTS" $ ruby --version ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux] $ rails --version Rails 5.2.3
何をしようとしていたのか
ActiveStorageを使ってアップロード機能を実装しようとしていましたが、storage.ymlの編集時に以下の記載がありました。
amazon: service: S3 access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> region: ${region} bucket: ${your_own_bucket}
Rails.application.credentials.dig
とは。
AWS Access Keyとかはてっきり環境変数に保持して、ENV[AWS_ACCESS_KEY]
で参照するつもりでいたので、プチ想定外。
もちろんもともとの想定で実装を進めることも可能ですが、せっかくなのでちゃんと調べてみることに。
結局何なのか
Rails 5.2から追加されたCredentialの管理方法。
普通にRails Guideのリリースノートを見た。
config/credentials.yml.enc
とconfig/master.key
の2つが登場人物。
平文の認証情報をconfig/master.key
で暗号化し、その内容がconfig/credentials.yml.enc
に保存される。
Rails.application.credentials.dig
でconfig/credentials.yml.enc
にアクセスする。
gitとかに上げるときは、config/credentials.yml.enc
を上げて、config/master.key
は上げない。
こうすることで一元的に管理することができる。
いちいち環境変数に保存したり、環境変数から参照する手間が省ける。
どうすればいいのか
コンソールで以下のコマンドを実行する。
EDITOR=vim bin/rails credentials:edit
secret_key_baseはそのままにして、awsの情報を追記。
${access_key_id}と${secret_access_key}には各々の情報を記載する。
aws: access_key_id: ${access_key_id} secret_access_key: ${secret_access_key} # Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. secret_key_base: ${secret_key_base}
config/credentials.yml.enc
の中を見てみると内容が変わっている。
以上
ファイルをS3にアップロードするときはサーバー?クライアント?
背景
クライアントサイドとサーバーサイドを分離して、JSONでデータのやり取りをするのが昨今のアプリケーションのトレンド。
クライアントサイドからファイルをアップロードするのがいいのか、サーバーサイドからファイルをアップロードするのがいいのか、どっちがいいのかとふと思いました。
最近はS3に画像データ保存することが多いので、S3にデータを保存するとしたらどうするかを考えます。
サーバーサイドからアップロードする場合
ファイル送信を行う際に取りうる選択肢
ファイル送信を行う際には、multipart/form-data
かBase64 Encode
のどちらかが使われるのが一般的
multipart/form-data
HTTPリクエストのヘッダに付与されるContent-Type。
HTTPでファイル送信を行う際の王道。
大体のWebアプリケーションのフレームワークでは、この方法で送られてきたファイルをデコードする仕組みを持っている。
複合データ型であることを意味していて、1回のHTTP通信で複数の種類のデータ(text, file etc...)を扱うことができる。
バウンダリ文字列という境界を定義して、その境界を使ってリクエストボディを複数のパートに区切ってデータを送信する。
良いところ
バイナリを扱える
王道良くないところ
JSONベースの通信で使えない
Base64 Encode
Base64でエンコードして文字列にし、JSONでサーバーへ送る方法。
Base64ってのは、全てのデータを英数字と記号(+
と/
)で表現したもの。
なんでBase64なの?みたいな話は「base64ってなんぞ??理解のために実装してみた」って記事がいい感じでした。
multi-part/form-data
ほど王道というわけではないが、GitHubやTumblrなどのAPIで利用実績があり、それなりに安心して採用できる手法。
良いところ
文字列として扱えるのでJSONベースの通信と相性バッチリ
それなりに安心して使える良くないところ
データ容量が大きい
エンコードとデコードのコストが高い(場合によっては自前で処理を書くかも)
取りうる選択肢
multi-part/form-data
を使うなら、画像をアップロードするエンドポイントを切り出す。
そもそもJSONで使えないし。
その場合は、アップロードしたレスポンスでIDを返してもらって、そのIDを保存したいリソースに紐づけて再度サーバーにリクエスト投げる、って感じになるのかな。Base64 Encode
を使う。
JSONで使えるけど、処理書くのめんどくさそう。- そのリソースに関する部分だけそもそもJSONの通信やめちゃう?(← 割と一番実装楽そうだったりする)
そのリソースに関する部分はmulti-part/form-dataでやり取りするということ。
クライアントサイドからアップロードする場合
処理は軽いだろうけど
やっぱりセキュリティのリスクあるよなぁ。
特にS3に保存するってなるとAWS Access Keyとかの取得処理をクライアントに持たないといけなくてかなりリスクあると思う。
と思ったらなんかいけそうなのある
ググったら色々出てきた。
「Amazon S3 Browser Upload」とか「Vue.jsからS3に直接ファイルをアップロードする」とか。
Amazonが提供しているSDKの中に、S3への画像アップロードをサポートしてくれるやつがあるんだと。
ここからはちょっとRubyの話。
PresignedPost
というのがあるらしい
AWSが提供しているSDKのクラスリストを見てみるとS3にアクセスするためのキーを発行できそうな感じ。
Pre
signed
なので仮なんだろうなという感じはするが・・・
ただ、PresignedPost
を直接使うんじゃなくて、Bucket::presigned_postを使った方がいいとも書かれている。
そちらを見てみるとBucket::presigned_post
はPresignedPost
のインスタンスを簡単に作ってくれるらしい。
expiresオプションをつけることで有効期限をつけることができるようで、要はワンタイムURLみたいなのを生成してくれるみたい。
これ使えばクライアントサイドからS3に直接アップロードでもさっきの懸念はなさそう。
クライアントサイドからアップロードする場合の処理のイメージ
サーバーでワンタイムURLを作り、クライアントに返す。
クライアントはそれを受け取って、Fileオブジェクトを乗せてS3にリクエスト。
S3からレスポンスとしてアップロード先のkeyなどの情報が入ってるので、保存したいリソースに紐づけて次はサーバーにリクエスト。
・・・
ってこれmulti-part/form-dataの処理と変わらない。
むしろS3との通信は成功したけど、サーバーとの通信失敗しましたってなったときにS3のデータとサーバーのデータで若干不整合が生じている気がする。
それが許されるならそれでいいけど。
結論
クライアントでアップロードするっていう選択肢はなさそう。
サーバーでアップロードする場合は
multi-part/form-data
を使う。画像をアップロードするエンドポイントを切り出す。
これはクライアントからアップロードする場合と同じ問題を孕んでいる気がする。っていうかそう。
画像はアップロードできたけど、リソースの保存リクエスト失敗しましたって、S3がサーバーに変わっただけで構造的には同じ。Base64 Encode
を使う。
最初はめんどくさいけどエンコード/デコードの処理は一度作れば使いまわせるはず。
そしたら何も考えずJSONでデータのやり取りができるので幸せになれそう。- そのリソースに関する部分だけそもそもJSONの通信やめちゃう。
最初楽できそう。だけど同じあとで苦労しそう。
悩むなら2と3。
時間との相談な気がする。
なんか妙な結論になりましたが、本日はここまで。 それぞれのパターンで実装してみようかな。
同じモデルに対する外部参照キーを別名で複数持つ方法
同じモデルに対する外部参照キーを複数持つ方法
今週に入って、だいたい10度前後を推移していた(高い時は20度近かった)のに、この日の最高気温2度。
寒すぎ。
どうでもいいですね。
さて、今回は同じモデルに対する外部参照キーを複数持つ方法、そして外部参照キーの名前をデフォルトから変更する方法を一気にやります。
言葉だけだとわかりにくいのでイメージを説明します。
何がしたいのか
あるユーザが複数のPostを持っているようなモデルを想像する。
誰から誰に投稿したのか、どのグループに投稿したのかを保持したくて
- from_user_id
- to_user_id
- posted_group_id
を持たせた。
from_user_id
と to_user_id
がともにUserを参照してほしい。
という感じです。
HOW TO
テーブル作成時にカラム名を変更しておく
外部参照をするときは、
class CreatePosts < ActiveRecord::Migration[5.1] def change create_table :posts do |t| t.string :text t.references :user, foreign_key: true t.timestamps end end end
のように書くのがオーソドックス。
そして実際に作成されるテーブルには user_id
でカラムが作成される。
ただ今回みたいに from_user_id
と to_user_id
という別々のカラムを作成し、それぞれでUserを参照したい場合、同じカラムがPostsテーブル内に2つ存在することになるので困る。
なので user_id
というカラムを作るのをやめてもらう。
class CreatePosts < ActiveRecord::Migration[5.1] def change create_table :posts do |t| t.string :text t.references :from_user, foreign_key: { to_table: :users } t.references :to_user, foreign_Key: { to_table: :users } t.timestamps end end end
このように書くことで、Postsテーブルには from_user_id
と to_user_id
というカラムができる。
これでOK。マイグレートする。
Modelにアソシエーションを追加
もちろんこれだけではダメで、Modelにアソシエーションを記載しないといけない。
class Post < ApplicationRecord belongs_to :from_user, class_name: 'User', :foreign_key => 'from_user_id' belongs_to :to_user, class_name: 'User', :foreign_key => 'to_user_id' has_many :praise_replies end
アソシエーションを記述する際は、 belongs_to :${table_name}
がオーソドックスだが、今回はUserを参照しているカラムが2つあるのでそれぞれに対してアソシエーションの定義が必要。
これで例えば
posts = Post.all.includes(:from_user) posts.each do |post| puts post.from_user.name puts post.from_user.email end
とするとPostに紐づくfrom_userのUser情報をワンクエリで取ってくることができる。
以上です。
いつもこれくらいサバサバした文章を書きたいな。。。
ありがとうございました。
Ruby on RailsのAPIモードでログイン機能の実装をする
概要
タイトルのとおりです。
Rails APIを使ってログイン機能の実装を行います。
今回はサーバーサイドオンリーです。
Googleの拡張機能でデータをPOSTして、テーブルに格納できていることを確認できればゴールとします。
そのうち、フロントサイドも含めた記事を書く予定ですが、その時の参照先となる記事です。
前提
すでにRailsのプロジェクトがある。
手順概要
実装するための手順の概要です。
やること
devise_token_authのgemのインストール
では早速いきましょう。
まずはGemfileに追加です。
devise_token_authはTwitterやFacebookでの認証にも対応することができ、omniauthというGemをインストールすることで使用可能になるのですが、今回はこれらの認証方法は使わないので、omniauthはインストールしません。
devise_token_authを利用する上でインストールすべきGemは devise
と devise_token_auth
なのですが、
ユーザのデータはJSONでやりとりしたいので jbuilder
を、
またクロスドメイン対策のために rack-cors
もインストールします。
source 'https://rubygems.org' git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") "https://github.com/#{repo_name}.git" end # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.1.5' gem 'rake', '~>12.3.2' # Use mysql as the database for Active Record gem 'mysql2', '>= 0.3.18', '< 0.5' # Use Puma as the app server gem 'puma', '~> 3.7' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder gem 'jbuilder', '~> 2.5' <- JSONでデータのやりとりを行う想定のためコメントアウトを外す # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 4.0' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible gem 'rack-cors' <- クロスドメイン対策のために利用するため、コメントアウトを外す # This is for authentication based on token gem 'devise' <- 追加 gem 'devise_token_auth' <- 追加 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] end group :development do gem 'listen', '>= 3.0.5', '< 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' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
Gemfileの編集が終わったらこれらのGemをインストールします。
$ bundle install --path ./vendor/bundle
(プロジェクトごとに管理するgemを変えたいのでローカル(./vendor/bundle)にインストールします。
ここまでエラーなく終了すれば、1st ステップ終了です。
テーブルの作成・マイグレーション
では次はユーザ認証のために使用するクラスや、それに対応するテーブルを作っていきます。
devise_token_authのコントローラ等の作成
$ bundle exec rails g devise_token_auth:install User auth
(bundle exec は必要に応じてつける)
フォーマットは
$ rails g devise_token_auth:install [USER_CLASS] [MOUNT_PATH]
です。
[USER_CLASS] はユーザ認証に使用するクラスの名前です。何も指定しなければUserとなります。
[MOUNT_PATH] は認証のルーティングをマウントするパスです。何も指定しなければauthとなります。
認証のルーティングをマウントとは、要はURLのことで、authだと例えばサインインのURLはGET、POSTともに /auth/sing_in
のようになります。
$ bundle exec rails g devise_token_auth:install User auth create config/initializers/devise_token_auth.rb create db/migrate/xxxxxxxxxxxxxx_devise_token_auth_create_users.rb create app/models/user.rb insert app/controllers/application_controller.rb gsub config/routes.rb
となればOK!!
user.rbとマイグレーションファイルの修正
そうすると、 user.rb
とマイグレーションファイルが作成されるのでそちらを修正していきます。
class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable include DeviseTokenAuth::Concerns::User end
もともとconfirmableやomniauthableがコメントアウトされていましたが、もしコメントアウトされていなければコメントアウトしておきましょう。
confirmableはメールを送信して確認する機能を実装するためのもの、omniauthableは先程のTwitterやFacebookでの認証を指しています。
今回は使いません。
次はマイグレーションファイルを修正します。
confirmableがコメントアウトされていなかったので、コメントアウトします。
また、このコメントアウトに伴って confirmation_token
に対して張られているindexの記述もコメントアウトします。
今回は、
- 名前
- ニックネーム
- メールアドレス
- 画像
がデフォルトで記述されていたので、それをそのまま使用します。
class DeviseTokenAuthCreateUsers < ActiveRecord::Migration[5.1] def change create_table(:users) do |t| ## Required t.string :provider, :null => false, :default => "email" t.string :uid, :null => false, :default => "" ## Database authenticatable t.string :encrypted_password, :null => false, :default => "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at t.boolean :allow_password_change, :default => false ## Rememberable t.datetime :remember_created_at ## Trackable t.integer :sign_in_count, :default => 0, :null => false t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip # ## Confirmable ========= コメントアウトここから # t.string :confirmation_token # t.datetime :confirmed_at # t.datetime :confirmation_sent_at # t.string :unconfirmed_email # Only if using reconfirmable ========= コメントアウトここまで ## Lockable # t.integer :failed_attempts, :default => 0, :null => false # Only if lock strategy is :failed_attempts # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at ## User Info t.string :name t.string :nickname t.string :image t.string :email ## Tokens t.text :tokens t.timestamps end add_index :users, :email, unique: true add_index :users, [:uid, :provider], unique: true add_index :users, :reset_password_token, unique: true # add_index :users, :confirmation_token, unique: true <- コメントアウト # add_index :users, :unlock_token, unique: true end end
完了したらマイグレーションファイルを適用しましょう。
$bundle exec rake db:migrate == 20190127155844 DeviseTokenAuthCreateUsers: migrating ======================= -- create_table(:users) -> 0.0580s -- add_index(:users, :email, {:unique=>true}) -> 0.0552s -- add_index(:users, [:uid, :provider], {:unique=>true}) -> 0.0501s -- add_index(:users, :reset_password_token, {:unique=>true}) -> 0.0500s == 20190127155844 DeviseTokenAuthCreateUsers: migrated (0.2139s) ==============
となっていればOKです!!
ここまででテーブルの作成・マイグレーションが完了しました。
devise_token_authを使ったトークン認証の実装
さぁ、いよいよ実装に入ります。
コードって書いてる時間より紙上とかエクセルとかで考えてる時間のほうが長いですよね。
どうでもいいですけど。
ちなみに今の状態で bundle exec rake routes
をすると、
new_user_registration GET /auth/sign_up(.:format) devise_token_auth/registrations#new
のようなパスが表示されています。
コントローラの作成
さて、 bundle exec rails g controller api/v1/auth/registrations
を実行します。
$bundle exec rails g controller api/v1/auth/registrations Running via Spring preloader in process 6670 create app/controllers/api/v1/auth/registrations_controller.rb invoke test_unit create test/controllers/api/v1/auth/registrations_controller_test.rb
と表示されればOK。
作成されたコントローラを編集して以下のようにします。
# registrations_controller.rb module Api module V1 module Auth class RegistrationsController < DeviseTokenAuth::RegistrationsController private def sign_up_params params.permit(:name, :nickname, :email, :img, :password, :password_confirmation) end def account_update_params params.permit(:name, :nickname, :email, :img) end end end end end
ルートの編集
次はルートを編集します。デフォルトのコントローラではなく、DeviseTokenAuth::RegistrationsControllerを継承したコントローラを使うようにします。
# routes.rb Rails.application.routes.draw do namespace :api do scope :v1 do mount_devise_token_auth_for 'User', at: 'auth', controllers: { registrations: 'api/v1/auth/registrations' } end end end
さて、ルートが確認しましょう。 $ bundle exec rake routes
を実行します。
new_api_user_registration GET /api/v1/auth/sign_up(.:format) api/v1/auth/registrations#new
URIがちゃんと変わってますね!
実装としては以上です。
Initializerの編集
もろもろの設定を触ります。
config.change_headers_on_each_request = true
にするとリクエストごとにトークンを新しくする必要があります。
なのでコメントアウトを外してfalseにしたいです。
そして、ずっと同じトークンを使い続けるわけにもいかないので config.token_lifespan = 2.weeks
のコメントアウトも外します。
以上で、サーバー側の実装完了です。
試しに適当にデータを投入してテストしてみましょう。
Restlet Clientを使ってテスト
まずは rails s
でrailsをローカルで起動します。
ユーザ作成
http://localhost:3000/api/v1/auth
に対して、POSTメソッドでリクエストを投げます。
Request Body
{ "name": "kenny takimura", "nickname": "kenny", "email": "kennytakimura@example.com", "password": "password", "password_confirmation": "password" }
200ステータスが返ってきてますね。
Response見てみましょう。
Response
**idは人によって違うかもしれないです。
僕はすでにいくつかユーザ登録していたので新規登録でidが4になっています。
{ "status": "success", "data":{ "id": 4, "provider": "email", "uid": "kennytakimura@example.com", "allow_password_change": false, "name": "kenny takimura", "nickname": "kenny", "image": null, "email": "kennytakimura@example.com", "created_at": "2019-01-28T16:00:20.000Z", "updated_at": "2019-01-28T16:00:21.000Z" } }
テーブルも確認しましょう。
ちゃんとデータが入ってますね。
ログイン
ついでにログインも確認しておきましょう。
ログインするためには http://localhost:300/api/v1/auth/sign_in
に対してPOSTメソッドでリクエストを投げます。
Request Body
{ "email": "kennytakimura@example.com", "password": "password" }
こちらも200ステータスが返ってきてます。
Responseは
Response
{ "data":{ "id": 4, "email": "kennytakimura@example.com", "provider": "email", "uid": "kennytakimura@example.com", "allow_password_change": false, "name": "kenny takimura", "nickname": "kenny", "image": null } }
先ほど登録したデータが返ってきます。
idも4です。
いい感じですね。
おわりに
想像してたより簡単に実装できました。
他にもいろんなサイトで実装方法が紹介されているので、見てみるといいかもしれないです。
私は基本的には公式サイト
Installation - devise-token-auth
をベースに、
web帳 | Rails5 + devise token authで作る 認証API
や
[Rails5] devise_token_auth でAPIを作成する / 新規登録・ログイン | tackeyy.com
を参考に実装していきました。
次はフロントとのつなぎも書いていこうと思います。
それでは!
4日でポートフォリオサイトを作った話
ポートフォリオサイトを作りました
最近全然アウトプットできていなかったんですが、久しぶりの投稿です。
今まで学んできたVue.jsを使ってポートフォリサイトを作りました。
動機
細かいものを出していくといろいろあるんですが、大きいところでいうと、
- 自分が持っているスキルとかやっていることとかをまとめたかった
- このサイトを見てもらうことで、ちょっとでも興味持ってくれた人と繋がれたりしたら嬉しいなー
とかですかね。
使用言語etc
- HTML
- CSS(Buefy)
- JavaScript(Vue.js)
HTMLは当然、Vue.jsも最初に述べた通り。
Buefyは今回初めて使った、Vue用に作られたコンポーネント群。
Buefy?
今回初めてなので少しだけBuefyとはについて書こうと思います。
使った感想とかはまた後で。
公式サイト(https://buefy.github.io/)を見ると、
Lightweight UI components for Vue.js based on Bulma
と書いています。
Bulmaをベースに作られたVue.jsのための軽量なコンポーネント群ってことですね。
で、Bulmaって何よ?となるわけで、さらにBulmaの公式ページ(https://bulma.io/)ヘ...
Bulma is a free, open source CSS framework based on Flexbox (以下略)
と記載されていて、要はFlexboxベースで作られたOSSのCSSフレームワークで、Bootstrapみたいなものかなぁと理解します。
なので、BuefyはこのBulmaというCSSフレームワークを使って作られたコンポーネント群ですね。
作る前に考えてたこと
- コンテンツ
- UI
考えてたことは正直この2つだけです。
どういうコンテンツにするかを決めないと作れないし、ダサいUIは嫌だし・・・
(結局UIは個人的には好きな感じだけど、そこはほんとに人によりそう 笑)
コンテンツ
まず初めに考えた。
あまり情報が多すぎでも見る気失せるし、少なくても自分のことわかってもらえないし。
自分のテクニカルな側面を知ってもらうために、過不足ない情報はどんなもんなんだろうというところから考えた。
逆に、自分は人のことを知りたい時、何がわかればいいか。
結果、コンテンツは
Top
Top画面。ベタに。
だけどこのサイトを見てもらえるかどうかが決まる画面とも思ったのでかなり真剣に考えた。Me
自分の基本的な情報を書く画面。Skills
自分のスキルセットを書く画面。
たぶんこのサイトを訪れた人が一番気になること。Outputs
今までの成果物を挙げる画面。
Skillsページよりこっちの方が気になる人もいてるか。Contacts
僕への連絡手段を掲載する画面。
おまけみたいな感じ。
で構成することにした。
UI
とりあえずExcelで大雑把なイメージを書き出してみた。
これを上の各コンテンツに対して行い、自分の中でのイメージを書き出してより明確なものにした。
ちなみに、自分の考えていることを書き出すということはよくやっている。
頭の中が整理されてちょっとすっきりするんですよね。
実装へ
実装するにあたって要件として以下を考えていました。
- SPA
- レスポンシブにすること
- Git hub pagesでホスティングすること
Vue.js使うならSPAは大前提ですが、前提でも要件は要件なので一応考えてた。
レスポンシブもWebでアプリ作るなら必須だと思うので要件にしています。
GitHub pagesは実は今回初めて知ったんですよね。
GitHubのリポジトリから直接Webサイトをホスティングできるサービスです。
簡単にリリースできるとのことなので、友人からの勧めもあり使うことにしました。
ハマったポイント
ここではポイントの紹介だけ。
コーディング自体はまぁまぁスムーズにできたんですよね。
Vuexとかも使う必要なかったですし、ルーティングをネストさせることもなかったので、
そもそもハマるポイントがそれほどなかったです。
GitHub pagesに上げるところで一番時間使ってましたw
成果物をビルドして、専用のリポジトリにpushしたんですが、URLにアクセスしても真っ白でした。
このポイントはまた別の機会に書こうと思います。
感想
Buefyを今回初めて使ったんですが、かなり良かったです。
こういうTileを使ったUIとか
NavbarとHamburgerの切り替えとか
を一瞬で実装できた。
実際はTileはCSSクラスを利用することで実装できるのだが、それらをラップしてTileコンポーネントにして、
ラベルと値組み合わせ(誕生日: YYYY/MM/DD)を渡すようにし、再利用できるようにするとともに、
項目が追加された時も簡単に追加できるようにした。
にしてもやっぱりUI考えるのって難しいですね。
今回は割とスピード感持って完成させましたが、本音を言うならもっとゆっくり時間使ってやりたいです。
(これからも当然ブラッシュアップしていくんですが)
今後もう少しUI考えてできればもっと改良したいのと、
あとは多言語対応もしたいです。
i18n使えば実装自体はすぐできるんですが、翻訳をする時間が無い。というか優先度低い。
今回このようにまとめたことで、自分のスキルセットを整理して、今後どうしようかとか考えるいい機会になりました。
みなさんもぜひ自分でサイト作りましょうよ、めちゃくちゃ楽しいですよ。
最後まで読んでいただきありがとうございました!
Udemy修了しました
早いものでもう11月
スターバックスなんかは店員さんが赤と緑のスタイルですっかりクリスマスモード
なんか年々1年が短くなっているような気がする・・・
さて、タイトルのとおり、前回の投稿で伝えていたUdemyのカリキュラムを修了しました。
分量が多かったかつ平日仕事だったのでなかなか時間かかりました。
実施コンテンツ(前回投稿以降)
- 実践演習:Bitcoin価格表示アプリの開発 - API連携
- テンプレート構文
- 算出プロパティ
- 監視プロパティ(ウォッチャ)
- 実践演習:APIを用いたリアルタイムサーチ - 監視プロパティの理解を深める
- クラスとスタイルのバインディング
- 条件付きレンダリング
- イベントハンドリング
- フォーム入力バインディング
- コンポーネント
- トランジション
内容的には以前からの復習が割と多かった
それぞれ紹介したいんだが、長くなりそう・・・なので省略
実践演習:Bitcoin価格表示アプリの開発 - API連携
やっぱりGETメソッドしか実装しなかった。
証券コードみたいなのを入力すれば該当のBitcoinの価格を表示させる・・・みたいなことができると尚良かったかも。
まあでも、POSTメソッドとかまでやってしまうとaxiosの話にもなるし、入門編という趣旨からも外れるんだろうな。
自分でやった方が勉強にもなりそうだし、ここは追々自分でやっていこうかと思う。
とはいえエラーハンドリングの仕方とかローディング中の表示とか基本的なことではあるが、ちゃんと学べたのは良かった。
テンプレート構文
種類はいろいろある。
- v-once
- v-pre
- v-html
- v-cloak
- v-text
などかな。
v-preディレクティブなんかはその要素とその要素の子要素のコンパイルを全てスキップするから、XSS(クロスサイトスクリプティング)対策になるらしい。
コンパイルされないということは悪意のあるJavaScriptも実行されないということだから、まぁそういうことなんだろうな。
v-cloakなんかは、インスタンスが生成されるまでは生きているが、インスタンスが生成されると取り除かれるディレクティブ。
なので、vueのインスタンスを生成する過程で見える可能性のあるマスタッシュ構文とかをユーザーに見せないようにすることができる。
かゆいところに手が届くディレクティブ。
算出プロパティ
computedプロパティを使う。
methodsプロパティを使った場合と比較されがちだが、
computedは算出結果をキャッシュしてくれるので、同じ処理を繰り返す場合はcomputedを使用する方が効率的。
また、computedにはgetterとsetterという概念がある一方でmethodsにはgetterしかないので、computedの方がよりリッチ。
監視プロパティ(ウォッチャ)
watchプロパティを使う。
computedプロパティと比較されがちなんだが、
どっちでも同じことができるなら、computedを使うことを推奨する。
watchに比べて、記述が簡単でコードが短くなるから。
watchプロパティを使えば、例えばテキストボックス内に金額を入力する必要があったとして、
onFocusで『¥1,200,000』->『1200000』に、onBlurで『1200000』->『¥1,200,000』にいった具合に切り替えることが容易になりそう。
あとはwatchプロパティのオプションとして、ネストされたオブジェクトまで監視するdeepオプションや、初期表示時にも処理を実行するimmediateオプションがあったりもする。
実践演習:APIを用いたリアルタイムサーチ - 監視プロパティの理解を深める
QiitaのAPIを使って記事を取得する演習。
watchを使うメリットはあまり感じなかった・・・v-on:keyupディレクティブでいいのでは・・・?という疑問が今なおある 笑
watchを使う練習だから別にいいんだけど。
クラスとスタイルのバインディング
v-modelやv-bindを使うことでvalue属性へのデータバインディングができるのと同様に、クラス属性やスタイル属性にもバインティングできるんだということを伝えるためのセクション。
v-bind:classを使うことでclassに、v-bind:styleを使うことでstyleにバインディングを行うことが可能だった。
プレーンなJavaScriptでDOM操作するよりはるかに簡単。
ここでも、「やっぱすごいな」と思っていた。
プレーンなJavaScriptで同じことをやろうとすると、
- 1 要素を取得して、
- 2 その要素が自分が追加したい属性を持っているかどうかを確認して、
- 3 その要素に属性を追加して・・・
と割と手順が多い。
条件付きレンダリング
v-if、v-else-if、v-elseやv-showがあるよ。
v-ifは切り替えコスト高い一方でv-showは初期描画コストが高いよという話で、
内容的には前回投稿のv-ifとはv-showとはという話と重複してた。
イベントハンドリング
イベントハンドリングもv-onを使うという話で、基本的には復習。
ただし、イベントの修飾子があるよということで以下が紹介されていた。
- .stop
- .prevent
- .capture
- .self
- .once
- .passive
それぞれの内容を全ては覚えていないけど、.once修飾子はv-onceと似てる。
イベントに対して一度しか処理を実行しないとするのか(.once)、データに対して一度しかレンダリングしないとするのか(v-once)の違い。
.prevent修飾子は詳しくレクチャーされなかったけど、おそらくいわゆるprevenDefaultと同じ挙動になるのではないかと。
なので非同期で通信するときに使うかもしれない。
フォーム入力バインディング
これも以前からの復習で、v-modelを使って演習する感じ。
v-modelを使用するとvalue属性は無効化されるとか、textareaではマスタッシュ記法が使えないからv-model使いましょうとか。
あとはラジオボタンやセレクトボックスのときにどのようにデータバインディングするかを、選択肢が単体・複数に応じて勉強した。
コンポーネント
Vue.componentを使うことでグローバルにコンポーネントを定義できて、
ローカル変数に対してコンポーネントを宣言して代入し、ローカルのvueインスタンスにtemplateの記述をすることで、ローカルでしか適用されないコンポーネントを定義できるという話。
グローバルなコンポーネントは、ヘッダとかサイドバーとかで使えそうだなぁって考えながら聞いてた。
スコープが広がると影響範囲も大きくなるので基本的にはローカルな方がいいんでしょうけどね。
トランジション
最後がトランジションで、要するにアニメーションをvueを使ってどうやるかという話。
transtionタグで囲むと、アニメーションであることが認識されて、
非表示->表示に遷移する際にはv-enter-active, v-enter、
表示->非表示に遷移する際にはv-enter-leave, v-leave-to
というクラスが一時的に割り当てられ、そのクラスに対してCSSを記述していくことでフェードインさせたりフェードアウトさせたりできる。
まとめ
結局全てのセクションに対してコメントしてしまった・・・。
こんな自己満足な投稿をここまで読んでいただきありがとうございます。
個人的な感想ですが、やっぱり最初は体系的に、こういう形で動画とかで勉強するほうが良かったです。
多少お金を払ってでもこういう勉強の仕方をした方が、今後何かわからないことや詰まったことが出てきた時に、
この話は全体の中のどこに位置づけられるのかといったことが意識しやすくなる。
全量を全く知らず、付け焼き刃の状態では『わかる』ことはできても、『できる』ようにはならない。
『できる』ようにならないと正しい実戦経験が積めないし、結果として『気付き』も得にくいんじゃないのかな。
今後
ペンディングしていたRuby on Railsのプロジェクトを再開させようと思います。
並行して、来年4月に応用情報技術者試験も受けたいので、直近はその勉強ですかね。(実は3週間くらい前から着手してる)
わからないこととか、詰まったことがあれば今後もツラツラと書いて投稿すると思います。
Udemy始めてみました
背景
前回の記事でRuby(Ruby on Rails)を利用したプロジェクトにVue.jsを導入したという話を書きました。
で、実際にVue.jsを使い始めたわけですが、ところがどっこい凄まじく手詰まり・・・
Componentて?ルーティングどうなってんのこれ・・・笑?もはやどうやってサーバーと繋げればいいのか・・・?
あらゆることに疑問が出てきて、基本的・体系的な知識が不足していると感じたので、友人に勧められたこともあり先週くらいからUdemyでVue.jsのレクチャーを受講することにした。
通常7,200円が1,500円だったので、お買い得
『Vue JS入門決定版!jQueryを使わないWeb開発 - 導入からアプリケーション開発まで体系的に動画で学ぶ』(上に張ったリンク)を使ったが、
ある程度進んだので、ここで少しだけ自分にfeedbackしようかなぁと。
内容をざっくり
速習編
基礎を叩き込むtodoアプリ作成編
叩き込んだ基礎でtodoアプリを作るビットコイン価格取得&表示
axiosを使ってBitCoinの価格を取得するするAPIを叩き、JSON形式のレスポンスを画面に描画するより深く
テンプレート構文や算出プロパティなど、より深く知識を得て使いこなすためのレクチャー
やったこと
速習編・todoアプリの作成編の学習
期間は1週間くらい
感想を一言で
レガシーなjsに比べて、こんなに楽だと思ってなかった。
学んだこと
- ディレクティブ
簡単に言うなら、HTML側である指示を行うためのプロパティ。自分が学んだディレクティブと簡単な説明
v-if
""(ダブルクウォテーション)で書いた変数の値がtrueの時に、DOMを表示する。falseの時はDOMごと消されるので、処理的には比較的重い。v-show
基本的な処理はv-ifと同じ。ただし、falseのときにはDOMそのものが削除されるわけではなく、display:noneになるだけなので、v-ifに比べて処理が軽い。v-on
イベントリスナーとして使う。v-on:click="doSomething" みたいに使うと、ある要素がclickされたときにjsで定義しているdoSomethingという処理が呼ばれる感じ。v-bind
単方向のデータバインディング。Vue->HTMLに対してデータバインドするので、HTML側での入力はVueに影響することはない。v-model
双方向のデータバインディング。v-bindと違ってHTML側での入力がVueにも影響する。v-for
繰り返し処理。配列やオブジェクトをループしてくれる。前だとjsの中にテンプレート書いて、ループ回してappendしながらHTML作って、ようやく描画してたのに対して、HTML側で1行で済むので非常に楽。
こんなもんかな?もしかしたらヌケモレあるかも。
- DOM操作
上で述べたディレクティブはこれに強く関係している。 todoアプリ作成の中でちゃんと学べたと思う。
各ディレクティブでDOMがどのように操作されるのか、使い所はどこかを学んだ。
v-onを起点に、v-bind使って表示非表示を切り替えたりするのなんかめちゃくちゃ楽。
中でも強く印象に残っているのが、双方向のデータバインディングがこんなに簡単にできるのかということ。
todoアプリは、inputフォームを使ってタスクをリストに追加、doneになったら打ち消し線を表示する、削除するとリストからも消える。くらいの基本的な仕様だったが、JQuery使ってたときよりはるかに簡単に実装できる。
dataプロパティの中に、任意のプロパティを定義して、v-bindでそのプロパティを指定するだけであら不思議、データバインドできましたー
くらいの感覚。(実際はもう少しやることある(CSSとかとか)。要は簡単って話。)
感じたこと・感じていること
基礎って大事。
体系的に学ぶって大事。
どんどんいろんな技術が出てきて、進化してるな。
取り残されたくないな。
勉強し続けないと、(エンジニア市場で見た時に)ただでさえ高くない市場価値が下がる一方だな。
これから
ビットコイン価格取得&表示を進めていく。
API使ってレスポンスをどうやって描画するかを学習できるといいかな。
イメージGETメソッドしか使わなそうだけど、POSTメソッドも使う内容なんだろうか。
より深くの部分もやります。 おそらく次の投稿は全てやりきったあとなのではないかと。
外に出るねくらの成長を楽しみしててもらえるとありがたい。