vueを使っている時のディレクトリ構成
みなさん、ディレクトリ構成は気になったりしませんか?
自分は割とディレクトリ構成を気にします。
どこにどういうコードが書いてあるのかすぐに探すことができるからです。
自分は普段railsでコードを書いているので、railsのレールに則って書くとディレクトリ構成で悩むことは少ないです。
そして、vueを使っている場合は、レールがないので、自分で考えなければいけません。
今回は初期構成から現在に至る構成を伝えます。
これがベストプラクティスかどうかは自分もわかりません。
現在の構成はgitlabhqを見ていて、納得することが多かったので、それに従っています。
余談ですが、大規模なプロダクションレベルのコードを見るのはオススメです!
初期構成
javascripts ├── application.js ├── components │ ├── comment │ │ │── form.vue │ │ └── icon.vue │ └── common │ └── form │ └── modal.vue ├── comment.js ├── mixins │ └── icon.js └── store └── index.js
こんな感じです。
内容
- 起点となるディレクトリ(javascripts)にファイルを置いていく
- compoments単位・store単位に区切る
- 共通のcomponentsはcommonを利用する
vuexなどのサンプルにあるやり方です。
一番最後だけは付け足しました。
小規模のサイトならいいのでしょうが、大規模のサイトになってくるとちょっとcomponents内が膨らんでいきます。
また、起点となるファイルもディレクトリを作成することになるでしょう。
現在の構成
javascripts ├── application.js ├── icon │ ├── components │ │ ├── form.vue │ │ └── icon.vue │ ├── icon_bundle.js │ └── store │ └── index.js ├── initializers │ └── axios.js └── vue_shared └── components └── form └── modal.vue
内容
- 起点となるのはディレクトリ内の
**_bundle.js
で管理します。 - 起点となるディクレトリ単位で
components
・store
を作成 - 共通処理は
vue_shared
に移す - 初期設定は
initializers
で設定する
一番最後のはWeb アプリの JavaScript の初期化処理をどうまとめるかを見て取り入れました。
普段railsと一緒に使用しているので、ディレクトリ単位の管理だと、位置を合わせることができるので、便利です。
components
・store
もどこで一緒に利用されているのかがわかります。
このディレクトリ構成が何かしっくりきました。
もっとこうしたほうがいい!などの意見があれば教えてください。
以上です。
参考
Deviseのログイン後のページ先をユーザーフレンドリーにする
せっかくログインしたのに、トップページに戻ると、なんだこれ?ってなってしまいます。
それを回避する方法です。
class ApplicationController < ActionController::Base before_action :store_user_location!, if: :storable_location? private def storable_location? request.get? && is_navigational_format? && !devise_controller? && !request.xhr? end def store_user_location! # :user is the scope we are authenticating store_location_for(:user, request.fullpath) end
storable_action
でGET/Devise/Ajaxじゃないなどの判定をします。
その場合のみ、sessionでurlを保存しています。
class Users::SessionsController < Devise::SessionsController include Cookie protected def after_sign_in_path_for(resource_or_scope) stored_location_for(resource_or_scope) || super end end
あとはsession後にそれがあるかどうかを判定します。
参考
wikiが充実していて、本当に助かる。
ここから何かをカスタムする場合は、storable_location?
をいじるか、after_sign_in_path_for
で条件分岐でいじるかになると思います。
railsで今月・先月の期間を取得する方法
all_month
を使いましょう。
今月の場合
[1] pry(main)> Time.current.all_month => Fri, 01 Dec 2017 00:00:00 JST +09:00..Sun, 31 Dec 2017 23:59:59 JST +09:00
先月の場合
[2] pry(main)> Time.current.last_month.all_month => Wed, 01 Nov 2017 00:00:00 JST +09:00..Thu, 30 Nov 2017 23:59:59 JST +09:00
これは便利すぎる
参考
railsの本番サイトのデータベースを別サーバーで行うための方法
今回やりたいことです。
- app/dbサーバーを分けて運用する
そんなの当たり前だろ!って感じですが、デプロイするときに詰まったので、忘れないために書いておきます。
なお、今回のお話はmysqlでの運用にしておりますが、他のデータベースでも同じ感じでいけると思います。
mysqlでリモートの接続を許可する
こんな設定あるとは知りませんでした。
言われてみれば、どこからでもデフォルトでアクセスできるのは、危険としか言いようがありません。
/etc/mysql/mysql.conf.d/mysqld.cnf
ファイルから、mysqlの設定を変更します。
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html [mysqld] pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock datadir = /var/lib/mysql log-error = /var/log/mysql/error.log # By default we only accept connections from localhost # bind-address = 127.0.0.1 bind-address = xxx ←ここをmysqlがあるサーバーのIPアドレスにする # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0
これでmysql -h IPアドレス -u hoge -p
でアクセスできるようになります。
補足
ただし、この場合は全世界に解放されている状態なので、最低でもDBサーバーにアクセスする場所からしか入れないようにしましょう。
AWSの場合はセキュリティグループがあるので、port:3306
へのアクセスはIPアドレスで制限しましょう。
ファイアーウォールでもそういう設定はできるでしょう。
database.ymlの設定
production: <<: *default host: xxx ←IPアドレスを環境変数で設定しておきましょう ...
mysql userの作成
deployするとmysqlのユーザーはhoge@localhost
ではなく、hoge@IPアドレス
でアクセスします。
なので、それに沿ってユーザーを作成しましょう。
- ユーザー作成
- 権限設定
create user {user名}@IPアドレス identified by {パスワード} -- ユーザー作成 grant all on foo_db.* to {user名}@IPアドレス identified by {パスワード};
この辺を注意して作成します。
以上です。
やってる内容は普通なのですが、意外に詰まってしまった。
chromeで簡易的にパフォーマンスチェックする方法
「推測するな、計測せよ!」ってよく言われるけど、どうやって計測すればいいんだよ!ってパターンはあります。
自分はサーバーサイドをメインでやることが多いのですが、最近話題のフロント側のパフォーマンスについては、どうやって計測するのかわかっておりません。
サーバーサイドだと、New relicで計測することが多いでしょう。
では、サーバーからレスポンス後の速度はどのようにチェックすれば良いのでしょうか?
その時のために、google先生がパフォーマンス計測する方法を用意してくれていました!
Developer toolsのAuditsを利用する
まずはchromeを用意しましょう。
chromeでページを表示した状態ででF12キー(Windowsの場合。Macの場合はCommand+Option+Iキー)を押しましょう。
chromeのdeveloper toolが開きます。
タブのAuditsを選択しましょう。
上記のような画面になります。
ここから単純にボタンを押して、実行をします。
パフォーマンス測定をしてくれます。
これはひどいですね・・・
内容は今風のことが多いです。
- Service Workerを利用しろ!
- 画像は遅延読み込みしてくれ
- ファイルは圧縮して配信してくれ(オフラインなので、勘弁してくれ・・・)
とりあえず、google先生のいうところから、少しづつ改善していきましょう。
追記
画像はwebp使えばこれだけ軽くなるぞって教えてくれます。
google先生優しすぎだろ・・・
最小限の機能で作成していく方法
よく、最小限の機能でリリースしろ!なんて巷で言われますよね。
けど、気がついたらガッツリ思い込みでガチガチに仕様を固めています。
自分はよくそうなるので、どうすればそうならないかを考えました。
というか、最小限の機能で作成する能力って技術の一つじゃないの?とすら、思っています。
だって、難しいから。。。
よく考えてみると後から追加できる形で作成するすればいいなとふと、気づきました。
よくある悩み
例えば、読書管理のサイトを作成しようとします。
読書管理するために、読書している状態を取得したいです。
- 読んだ
- 読みたい
という2つの状態で作成する方法。
- 読んだ
- 読書中
- 読みたい
という3つの状態で作成する方法があるとします。
作成者本人は読書中は微妙じゃない?と思いつつ、後者を選ぼうとしています。
なぜなら、選択肢が多い方が細かいデータを取得できて、ユーザーにとって便利だ!という悪魔の声があります。
3つのステータスがある場合
雑にやりましたが、セレクト形式になると思います。
この欠点は、二回クリックしないといけません。
「セレクトボックス」選択→「ステータス」選択
この後に待っているのは、状態をもっと細かく取った方がいいんじゃないの?という話になるでしょう。
2つのステータスの場合
では、最初から、「読んだ」「読みたい」の二つしかない状態にします。
デザインが雑なんで、あれですが、こちらの方は1クリックでできます。
「読んだ」を押すとチェックマークが入るアニメーションをつければ終わります。
ユーザーとしても、迷いが減ります。
そして、何よりも後から「読書中」は必要なら追加することができます!
一方、最初から3つのステータスで作成していた場合は、なかなか消すことができません。
なぜなら、ユーザーさんが使用している可能性があり、簡単に消すことはサイトの信頼性が失われます。
後から追加できる形で作成する
おそらく、自分の中では最小限の実装で作成するんだ・・・!と思いつつも実際はガッツリと組み込んで作成してしまいます。
自分がそうなので・・・
そして、ガッツリ組み込んで作業していく場合は、作業に没頭できるので、やった気になります。
後から追加できる形で作成すれば、反応を見ながら作成していくことができます。
なので、自分は後から追加できるから、今は作らなくてもいいやと思って、割り切って最小限で作成していきたいと思います。
railsのroutesのid以外にする方法
railsはdefaultのresourcesは:id
になっています。
config/routes.rb
resources :users
URIのパターンです。
Prefix Verb URI Pattern Controller#Action new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy
ただ、id以外にしたい場合があります。
routesにparamをつけてid以外に変更する
その場合はparams
を付け足せばいいです。
config/routes.rb
resources :users, param: :name
users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:name/edit(.:format) users#edit user GET /users/:name(.:format) users#show PATCH /users/:name(.:format) users#update PUT /users/:name(.:format) users#update DELETE /users/:name(.:format) users#destroy
linkを貼る場合に楽をする
そして、この時のlinkを貼る場合にdefaultでnameにしたい場合があります。
@user = User.first user_path(@user)
通常だと、この場合はidになるので、/users/1
になります。
これをnameに変更します。
model/User.rb
def to_param name end
これで/users/foo
になります。
注意点
ただし、これをやる場合はnameがuniqにしておけないとバグの温床になります。 例えば、nameにfooが二人いた場合です。 だいたいcontrollerに書くのは下記のようなコードだと思います。
def show @user = User.find_by(params[:name]) ... end
find_byは最初の一つしか取得しないので、二つ以上あると予期せぬ挙動になります。
def find_by(arg, *args) where(arg, *args).take rescue ::RangeError nil end
そのため、DB/モデルにuniq制約
をつけるのを忘れないようにしましょう。
以上です。
railsでssl設定をした場合にしておいた方がいい設定
ssl対応をやってみました。
はてなブログもssl対応を行っているし、今となっては当たり前になりましたね。
なぜ、そんなことが起こっているかというと、chromeで安全なサイトではないという警告が出るからですね。
そんなsslですが、let's encryptで行うことが増えているのかなとは思っています。
それについて、後日まとめることができたらまとめます。
今回はsslをした後のrailsの設定を見ていきたいと思います。
cookieにsecure属性をつける
cookieにsecure属性をつけることで、cookieも暗号化されます。 平文だと、万が一盗まれるようなことがあるとまずいので、secure属性をつけるのがいいでしょう。
config/initializers/session_store.rb
Rails.application.config.session_store(secure: Rails.env.production?)
nginxのリバースプロキシにssl通信のheaderをつけてあげる
railsはforce_ssl
を利用しても、httpsと認識してくれません。
その原因は、https
と認証するheaderがないからです。
https://github.com/rack/rack/blob/rack-1.5/lib/rack/request.rb#L70
def scheme if @env['HTTPS'] == 'on' 'https' elsif @env['HTTP_X_FORWARDED_SSL'] == 'on' 'https' elsif @env['HTTP_X_FORWARDED_SCHEME'] @env['HTTP_X_FORWARDED_SCHEME'] elsif @env['HTTP_X_FORWARDED_PROTO'] @env['HTTP_X_FORWARDED_PROTO'].split(',')[0] else @env["rack.url_scheme"] end end def ssl? scheme == 'https' end
こうなっております。
scheme
のどれでもいいので、headerとしてリバースプロキシから渡してあげましょう。
location @app { ... proxy_set_header X-Forwarded-Proto $scheme;←追加 ... }
ここまで来たらリダイレクト処理をnginx側で行います。 わざわざアプリケーション側でリダイレクト処理をするのは無駄な作業です。
server { listen 80; rewrite ^ https://$server_name$request_uri? permanent; }
個人的には、rails側の設定もforce_sslで合わせました。 これで誰が見ても、設定がわかると思うので。
config/environments/production.rb
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true
設定にもcookieにsecure属性をつけた方がいいよって書いてますね。
以上です。
参考
railsのARに対するpresent?とexists?のパフォーマンスの差
exists?
の方がいいですという指摘を受けた。
なので、ここで確認する。
[4] pry(main)> Work.where(id: [*1..100]).exists? Work Exists (0.5ms) SELECT 1 AS one FROM `works` WHERE `works`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100) LIMIT 1 => true [5] pry(main)> Work.where(id: [*1..100]).present? Work Load (0.8ms) SELECT `works`.* FROM `works` WHERE `works`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100) => true
exists?
の方はLIMIT 1
がついてある。
ということは、ARの結果が複数ある場合はexists?
の方が高速ですね。
where
の後で存在を確認する場合はexists?
を利用すべきだな。
これからは気をつけよう。
後日談
この後の展開が続くなら、present?
の方がいいです
present?
- if @work.episodes.present? - @work.episodes.each.with_index(1) do |episode, i|
Episode Load (0.4ms) SELECT `episodes`.* FROM `episodes` WHERE `episodes`.`work_id` = 1
こういう場合ですね。 cacheした値がそのまま使用されます。
exists?
の場合
- if @work.episodes.exists? - @work.episodes.each.with_index(1) do |episode, i|
Episode Exists (0.3ms) SELECT 1 AS one FROM `episodes` WHERE `episodes`.`work_id` = 1 LIMIT 1 Episode Load (0.4ms) SELECT `episodes`.* FROM `episodes` WHERE `episodes`.`work_id` = 1
ということは、ARの存在を確認するだけで、そこから先は展開しない場合ですね。
考えられる場所は、モデル層とかなのかな。
Viewで確認してよかった。
ということで、使用する場所はきちんと考えないとダメですね。
nginxのmoduleを追加する
nginxにはmoduleという概念があります。
これは、各機能がmodule単位で実装されており、moduleを組み合わせた構成になっています。
moduleには「静的module」と「動的module」の二種類があります。
「静的module」はビルドした時にしか組み込みができません。
「動的module」はビルドし直すことなく、追加することができます。
この概念自体はApacheからあります。
そして、nginxは昔は「動的module」をサポートしていませんでした。
https://heartbeats.jp/hbblog/2016/02/nginx-dynamic-modules.html
1.9.11
で対応したようです。
ただし、一部だけになります。
下記は動的に組み込みができるものです。
Certified Dynamic Modules - NGINX
moduleは、上記以外には、サードパーティー製のものがあります(OSSに感謝)
https://www.nginx.com/resources/wiki/modules/
動的に組み込めれるものもあれば、静的に組み込むしかないものもあります。
ということは、きちんと最初で使用するであろうものは、用意しておくほうがいいですね。
ソースコードからビルドする
僕はmoduleを追加するためにソースコードからビルドしています。
ソースコードのダウンロードからやります。
deployer@ik1-324-22232:wget http://nginx.org/download/nginx-1.13.5.tar.gz deployer@ik1-324-22232:tar xvf nginx-1.13.5.tar.gz deployer@ik1-324-22232:cd nginx-1.13.5
まずは現状確認
deployer@ik1-324-22232:~$ sudo nginx -V nginx version: nginx/1.13.5 built by gcc 6.3.0 20170516 (Debian 6.3.0-18)
何も入っていません。
なんでこうなっているのかというと、自分はModuleを簡単に追加できると思っていたからです。
ここからhttpsを使おうとすると、nginx_http_ssl_module
がほしいです。
最初から入れておけばよかった。
ということで追加します。
ついでにwith-http_stub_status_module
も追加します。
deployer@ik1-324-22232:/tmp/nginx-1.13.5$ sudo ./configure --with-http_ssl_module --with-http_stub_status_module --prefix=/usr/local/nginx --sbin- path=/usr/sbin/nginx deployer@ik1-324-22232:make deployer@ik1-324-22232:make install
これでパッケージをもう一度ビルドできました。
deployer@ik1-324-22232:/tmp/nginx-1.13.5$ sudo nginx -V nginx version: nginx/1.13.5 built by gcc 6.3.0 20170516 (Debian 6.3.0-18) built with OpenSSL 1.1.0f 25 May 2017 TLS SNI support enabled configure arguments: --with-http_ssl_module --with-http_stub_status_module --prefix=/usr/local/nginx --sbin-path=/usr/sbin/nginx
結論
原則動的に追加できないなど、nginxの特徴を押さえておくべきだった・・・orz