- 投稿日:2019-03-05T23:51:01+09:00
Railsでページ遷移後にJavaScriptが実行されない問題の解消法
こんにちは、とくめいチャットサービス「ネコチャ」運営者のアカネヤ(@ToshioAkaneya)です。
Railsでページ遷移後にJavaScriptが実行されない問題について解説します。
Railsでページ遷移後にJavaScriptが実行されない問題の解消法
以下の行を削除します。
application.js//= require turbolinks
turbolinksは初心者にとっては余計な悩みを増やすだけですので、削除するのが良いでしょう。
はてなブックマーク・Pocketはこちらから
- 投稿日:2019-03-05T23:25:11+09:00
「Railsは終わった」と言われる理由
はじめに
Rubyは死んだ、Railsは時代遅れという人が最近増えてきたように思えます。
私自身RubyやRailsを書いて3年位経ちますが、「終わりつつあるな」と実感することが多いです。
そう思った経緯を記事に書いていきます。Railsの特徴
Railsの特徴というか、流行した要因としては以下の5つが大きいと私は思っています。
- テンプレート、パーシャル、レイアウトをERBを使ってすばやく構築できる
- Active Recordによってデータベースを簡単に定義、操作ができる
- アセットパイプラインによってcss、jsを管理することができる
- チュートリアルが充実している
- Rubyという柔軟性の高い言語によって開発することができる
私はRailsはこの5本の柱によって支えられていると思っています。
これらの5本の柱のメリットにより、Railsは大流行しました。
すばやく簡単にプロダクトを作ることができ、チュートリアルが充実しているため、新人教育も簡単でした。しかし時代の流れとともにこのメリットは以下のようにほぼ失われてしまいました。
テンプレート、パーシャル、レイアウトをERBを使ってすばやく構築できるReactやVue.jsによる保守性の高いフロントエンドが構築できるようになり使われなくなった- Active Recordによってデータベースを簡単に定義、操作ができる これは今でも現役で使える!
アセットパイプラインによってcss、jsを管理することができるwebpackerなどによって置き換えられたチュートリアルが充実している扱っている内容が段々と古くなってきており、時代に合わなくなりつつあるRubyという柔軟性の高い言語によって開発することができる柔軟性が高いゆえに保守が大変。今現在5本の柱のうち4本はすでに形骸化しつつあります。Railsを使うメリットはほぼActiveRecordの利便性のみです。
過去に比べてRailsの利便性が失われてしまった、だから「Railsは死んだ」と言われているのだと私は思ってます。
これらに要素について順番に解説していきます。
1. テンプレート、パーシャル、レイアウトをERBを使ってすばやく構築できる
RailsではErbをつかって以下のように書くことができます。
<% provide(:title, "ログイン") %> <h1>Log in</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_for(:session, url: login_path) do |f| %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.submit "ログイン", class: "btn btn-primary" %> <% end %> <p>New user? <%= link_to "Sign up now!", signup_path %></p> </div> </div>しかし大規模になればなるほど、以下のような辛いことが待っています。
- jQueryなどでViewの状態を管理するので見通しが悪くなる
- cssの管理が大変になる。BEMなどのアプローチで頑張ってもいずれ辛くなる。
- パーシャルはレンダリングのみで状態を保持したり、隠蔽することができないため、使いづらい。
- form_for、form_withなどのRails独自の記法の挙動が意外と難しい
- View側にモデルを持つことができないため、helperやdecoratorなどの関数に依存してしまい、さらに保守が難しくなる。
上記のような課題点によってRails単体ではフロントエンドで負債が積み重なることが多いので、最近ではReactやVue.jsをつかうのが一般的です。
下記はReact一例ですが、コンポーネント化により、状態やcssの影響範囲をクラス範囲内だけに留めることができます。login.html.erb<% provide(:title, "ログイン") %> <%= javascript_pack_tag 'login', 'data-turbolinks-track': 'reload' %> <%= stylesheet_pack_tag 'login', 'data-turbolinks-track': 'reload' %> <div id="login"></div>login.tsxdocument.addEventListener('DOMContentLoaded', () => { const node = document.getElementById('login'); ReactDOM.render(<LoginPage />, node); });index.tsxexport default class LoginPage extends React.Component<Props, State> { constructor(props) { super(props); } private onSubmit = event => { event.preventDefault(); const formData = new FormData(event.target); // ログイン処理 }; render() { return ( <div className={styles.container}> <form method="POST" className={styles.content} onSubmit={this.onSubmit}> <div className={styles.title}> ログイン </div> <InputTextField label="メールアドレス" name="email" inputType="email" /> <InputTextField label="パスワード" name="password" inputType="password" /> <button className={styles.button} type="submit"> ログイン </button> </form> </div> ); } }Reactなどのフロントエンドのフレームワークを導入することにより以下のようなメリットが得られました。
- コンポーネント化によって定数や状態を隠蔽することができ、保守性の高いフロントエンドが構築できる
- cssの影響範囲を任意のクラス内に留めることができる。
- (TypeScriptを使った場合)型によって安全にプログラミングすることができる。
- フロントエンドでネイティブアプリのようにモデル層、通信層などレイヤーを決めて開発できるようになった。
Railsのerbやhelperを殆ど使わずに書くことが一般的な今、ViewにおけるRailsの優位性はなくなりつつあります。
Railsあるあるですが、インスタンス変数地獄、ERBでモデルやクエリを呼び出したりなど、行儀の悪いコーディングも防ぐことができるというだけでもメリットです。2. Active Recordによってデータベースを簡単に定義、操作ができる
RailsのDB周りは未だに優秀だと感じています。
- データベースをORMにより簡単に操作が行える
- マイグレーションによってデータベース定義をバージョン管理できる
- バリデーションを簡単に定義できる
- リレーションが簡単に定義できる
欠点を上げるとするならば
- 難しい処理の場合実際に発行されるクエリがわかりにくい
- ActiveRecordから逸脱したことをやろうとすると面倒
というぐらいで、普通に使うのであれば、非常に使いやすいです。
他言語のマイグレーションツールとしてはFlywayなどがありますが、
Railsのほうが圧倒的に使いやすいです。しかし、Railsのようにマイグレーション周りだけ記述できるridgepoleというものがあり、それだけを使うという選択肢もあります。
3.アセットパイプラインによってcss、jsを管理することができる
cssやjsを連結したり圧縮してくれる仕組みです。
これによって複数ファイルでcss、jsを管理したり、出し分けたりするなどのことが簡単に行えました。ですがこちらもReactやVueを使う場合殆ど使わない機能になります。
それに加えて、Rails向けのフロントエンド用のライブラリの保守が行われなくなってきているのもあり、
フロントエンドのコードをRailsで管理すること自体が難しくなってきています。4.チュートリアルが充実している
Railsを一通り学ぶことができる良い教材だと思います。
しかし、ReactやVueによるフロントエンド構築が一般的になりつつある今に、
form_withやimage_tagなどのRails独自のマークアップを覚えることはメリットではなくなりつつあります。
とくにform_for、form_tag、form_withは挙動を完全に把握するにはかなりの学習コストが必要です。しかし学んだところでRails以外のフレームワークでは全く必要のない知識ですし、erbによるHtml記述は斜陽なのでこの部分はReactなどjsxを使った方法で学ぶほうが良いのかなと思います。TDDやデータベースの設計手法などは未だに参考になると思います。
数年前はRailsチュートリアルをやれば誰でも一線級のエンジニアになれる!という感じでしたが、
今では一部の知識は役に立つが、その他の知識はあまり役に立たなくなってきています。5. Rubyという柔軟性の高い言語によって開発することができる
Railsの記事なのにRubyに言及するのは許してください。
Rubyは確かに良い言語でした。純粋なオブジェクト指向言語で、書きやすい言語だと思っています。
Ruby2系はチームの幸福度を最大化できなかった - Qiita
過去にこのような記事を書きましたが、Rubyは以下のような弱点があり、コードに問題のあるRailsプロダクトが多く生まれてしまいました。
- 読みにくい
- Rubyしか触っていないエンジニアはある一定レベルで成長が止まる
- 美しく書くという文化でしか質を担保する仕組みが無い
- Rubyの将来性
1. 読みにくい
Rubyのコードは引数や返り値の定義がないため、実際に読んでみないと何が帰ってくるのかわからないです。
なのでコードリーディングに時間がかかりますし、バグの混入確率も高いです。そのためにRailsは「設定より規約」を取り入れて、このようにしたら、このように返ってくるというルールみたいなものを決めています。
このような規約は非常にありがたい半面、Railsはこう書いたら動くみたいな覚えゲーとなってしまった感がします。(その覚えたこともRailsでしか役に立たない)2. Rubyしか触っていないエンジニアはある一定レベルで成長が止まる
これは社内外のエンジニアでRubyしか触ったことがないエンジニアを見て思った感想に近く、具体的に検証していませんが、述べさせていただきます。
Rubyは型やクラスを意識することなく記述することができてしまいます。
そのため、なんとなくな設計指針やなんとなくなクラス設計というのができてしまいますし、業務でも多く見てきました。
(よくあるケースとしてなんとなく、クラスメソッドにしたり、なんとなくインスタンス生成したりなど)逆にJava、KotlinやGoなどの型定義を行うプログラミング言語の経験があるエンジニアが書くこRubyのコードは比較的美しいです。
Rubyという言語は型を全く定義しなくても動きますが、型を意識して書かなければ簡単に崩壊してしまいます。
しかし、型を意識する書き方というのはRubyでは学ぶことはできません。(一応学べるかもしれませんが、Rubyの型は他言語に比べて挙動の把握が難しいです)
このパラドックスにより、質の悪いRailsプロジェクトが生まれやすくなっています。
そのためRubyしか触っていないエンジニアは一定のレベルで成長が止まると考えています。その他、クラスの継承、インターフェース、ビルダーパターン、リアクティブプログラミング、ジェネリクス、ラムダなどのパラダイムはRubyでは学ぶことはできません。
私自身Rubyを触ったあとにJava,Kotlinを触ったことによりRubyに対する見方が変わったという経験があります。
3.美しく書くという文化でしか質を担保する仕組みが無い
Rubyは他人が書いたコードを読むのにエネルギーが要る言語だと思います。
型定義がある言語では入力と出力の形式がある程度定まっているので、比較的読みやすいです。
Rubyは返り値がどのような型になるかわからないので、メソッド名が妥当であるか?であったり、美しくわかりやすく書かれているかもしくは、テストを書いているか?が重要になってきます。熟練者のコードは非常に読みやすいかもしれませんが、そうでない場合、コードリーディングが大変です。
こうしたコールドリーディングを簡単にするために、美しく書く文化やテストを書くという文化があるのですが、
どこまで美しく書くか、どの程度テストを書くかは属人的であるので、質が担保しにくいです。
追い打ちをかけるようにRubyは様々な書き方ができてしまうので、よりコードリーディングが難しくなります。
ダメなプログラミング言語の代名詞としてPHPが挙げられますが、それがRubyに置き換わる時が来るかもしれませんTypeScript、KotlinやGoなどのように、型宣言があり、最低限の書き方が担保されている方が読みやすいのではないかと私は思います。
上記のようにRubyは簡単にかけるが、チーム開発をするには上級者向けの言語なので、Rubyによる開発が辛くなってくるのは必然だと思います。4. Rubyの将来性
これは個人的な愚痴に近いのですが、Rubyの開発の方向性と現場で必要なものがだんだんと違ってきているのではないかと最近は感じています。
Ruby3では速く動作することが頻繁に取り上げられますが、実際にそれでいまエンジニアが抱えている問題が解決するのか・・・?と思ってしまいます。
Rubyでチーム開発しやすいように、型宣言などの開発者を支援するような機能もほしいなという声もあるのではないでしょうか。
そういった背景もあり、型推論言語であったり、並列処理が速い言語などにフォーカスがあたってきているのだと思います。
今後のRailsとRuby界隈
終わったといっているだけでは生産性がないので、自分なりのアイデアや予想も記述しておきます。
Railsの将来性について
全盛期に比べて価値はかなり落ちてしまったように感じますが、
- 優秀なマイグレーションツール
- 扱いやすいORM
によって、APIサーバーとしてはまだまだ活用されるのではないかと思います。
バックエンドはRailsで書いて、フロントエンドはReactもしくはVueなどのフレームワークを用いて設計というのがRailsの今後の道かもしれません。
Railsを使うのであれば、以下のような構成が主流になるのではないかと思います。
- Rails
- Fast JSON API
- Webpack
- React
- TypeScript
- 一部の処理をGoなどでマイクロサービス化
とはいえ、Railsが管理しているのはAPIサーバーとデータベースのみなので、このためだけにRailsが選択されるかというとちょっと微妙な気もします。
Rails代替のフレームワーク、言語について
Go
Goへの乗り換えが最近HOTですが、個人的にはGoはきついのではないかなと思います。
Goroutineなど並列処理は優秀ですが、それ以外のことは苦手だと思っています。
理由としては
- オブジェクト指向的な設計がやりにくい
- C言語チックなのでポインタなど意識しなければならないことが多い
- Rubyなどの他言語に比べて不親切
マイグレーション、データベース、APIをすべてGoで実装しようとする人をたまに見かけますが、それって本当にGoでやるべきなのか?と思います。
なので、私の予想としてはRailsプロジェクトが数年後100%Goのプロジェクトに置き換わることはないかなと思います。
バッチ処理などの重い処理がGoに置き換わっていくというのが現実的かなと思います。Kotlin
筆者としてはKotlinはかなりありなのかなと思っています
- Null安全、型推論のモダンな言語
- Ktor(けいとーる)がKotlin/Nativeに対応した
- Kotlin/JSによってフロントエンドも書くことができる
Kotlinの世界観として、すべてのプラットフォームをKotlinで書けるという物を目指しているので、
実現した暁には、フロントエンド、バックエンド、アプリをすべてKotlinで書くことができます。しかし、期待値は高いですが、Railsに代替するほどの決め手はまだなくこれからって感じだと思います。
Node.js
私はあまり触ったことがないのでわからないですが、フロントエンドでTypeScript、JavaScriptを書くので、
サーバーサイドもJSのほうが学習コストが少なくて済むかもしれないので流行るかもしれません。また、フロントエンドとバックエンドの一部モデルの共通化などNode.jsでできるかもしれません。
その他
Elixirなどありますが、私は触ったことがないのでよくわからないです。
Rubyの型システム予想
ここは完全に妄想なので、興味ない人は飛ばしてください。
メソッド
Ruby3系以降では以下のような感じになってほしいなと思います。
def add(x : Integer, y : Integer) : Integer x + y endInteger以外が入ったときには静的解析などで通知してくれると嬉しいです。
Typescriptのように複数の型を返すときは以下のようにかけるといいと思いました。
ただ、Trueクラス、Falseクラスが存在する中でBooleanクラスをすんなり導入できるかどうかで意見が分かれそうです。def method(x : Integer, y : Integer) : Integer | nil if x > y x else nil end endRuby3の開発状況は全然知らないのですが、
Rubyに型の秩序を取り入れるという選択肢をユーザーに与えてみてもいいのではないかと思います。クラス
抽象クラスやメソッドのスコープがわかりやすく定義できると良いなと思いました。
Rubyの場合オブジェクト指向的なプログラミングへのサポートが弱いです。
継承を行うだけで見通しの悪いコードになってしまいがちなので、言語的にサポートがほしいです。abstract class ApplicationRecord < ActiveRecord::Base open def method(x : Integer) : Boolean x > 10 end private def save #..... end end class User < ApplicationRecord override def method(x : Integer) : Boolean x > 20 end endおわりに
数年前はRails学べばなんでもできると言われていましたが、今ではちょっとつらいかなと言う印象です。
主にフロントエンドでは、ネイティブアプリのようにAPIだけ問い合わせてレンダリング、状態管理、キャッシュするというプロダクトが主流になりつつあります。さらにRubyはチーム開発にはあまり向いていない言語だと思うので、コードレビューや設計方針にあまりエネルギーを割くことがないプログラミング言語が人気になると思います。
という感じでRailsは終わっていないかもしれないが、積極的に学んでも、Railsの特徴の殆どはこれから活躍する見込みが無いので、あまり得られるものはない。と思ってます。
ぶっちゃけRailsがすごいのはActiveRecordなので、それさえ現役である限り終わっていないとも言えます。次の言語何が来るかってのはいろいろあると思いますが、どんな環境になってもHTTPやデータベースの仕組みはしばらく変わることはないので、それさえ知ってればなんとかなるんじゃないのかなー。
- 投稿日:2019-03-05T23:12:50+09:00
1日目 ruby基礎,rails_scaffold(スキャフォールド)
文字コードの設定
ソースコードの文字コードをUS-ASCIIに設定する時は以下の3つのどの書きかたでも合ってる。
# coding: us-ascii
# encoding: us-ascii
# CODING: US-ASCII
変数と定数
rubyの場合、変数を定義する時は、小文字、定数を定義する時は、最初の文字が、大文字。以下に例を示す。
komozi=1
Oomozi=3.14
例外処理(begin rescue ensure)
beginの処理がエラーだった場合に、rescueの処理が実行される。ensureには、beginの処理がエラーでも、エラーじゃなくても処理される。
三項演算子
以下の2つの実行結果は同じである。
x = 10 y = x < 10 ? "A" : "B" puts yx = 10 y = nil if x < 10 y = "A" else y = "B" end puts y引数(*a)
以下のようにコードを書くと、実行結果が配列として出てくる。
def foo(*a) p a end foo(1,2,3)繰り返し
繰り返しはeach,step,uptoメソットがある。
以下は例文。
・eachmoney = 1000 (1..10).each do (1..10).each do money = money + 1 end end puts money・step
money = 1000 rate = 1.1 (2001..2010).step(2) do |i| money = money * rate end puts money.to_i・upto
money = 1000 rate = 1.1 2001.upto(2004) do money = money * rate end puts money.to_iscaffold(スキャフォールド)
ruby on rails でアプリケションを作る際の便利ツール。本来、railsでは、モデル、コントローラー、ビュー、ルーティングを作る必要があるが、scaffoldを使えば、これらの作業をまとめて行って、簡単にアプリケーションの雛形を作ってくれる。
今日の授業の感想
今日はRubyの基礎を午前に行い、午後はscaffoldを用いて、フォームを作成した。午前は問題に出されたコードが、どのような処理を行うのかを学び、色んな発見があって理解が深まった。しかし、午後の授業に追いついて行けなかった。railsの環境構築も行ったが、〜をして、次に〜をしてくださいと言われても、どこで実行すればいいのかもわからず、(macでとか、mysql、railsとか)、専門用語も多いので、それが何の意味を指していて、なんでそれを実行する意味があるのかもわからず、自分の非力さを感じた。現在何をしているのかが全く分からなかった。事前課題はprogateで指定されたことを一通り行ったが、それだけではついていけなかった。授業の復習を行い、少しずつ理解を深めて行きたい。
- 投稿日:2019-03-05T23:12:50+09:00
2日目 ruby基礎,rails_scaffold(スキャフォールド)
文字コードの設定
ソースコードの文字コードをUS-ASCIIに設定する時は以下の3つのどの書きかたでも合ってる。
# coding: us-ascii
# encoding: us-ascii
# CODING: US-ASCII
変数と定数
rubyの場合、変数を定義する時は、小文字、定数を定義する時は、最初の文字が、大文字。以下に例を示す。
komozi=1
Oomozi=3.14
例外処理(begin rescue ensure)
beginの処理がエラーだった場合に、rescueの処理が実行される。ensureには、beginの処理がエラーでも、エラーじゃなくても処理される。
三項演算子
以下の2つの実行結果は同じである。
x = 10 y = x < 10 ? "A" : "B" puts yx = 10 y = nil if x < 10 y = "A" else y = "B" end puts y引数(*a)
以下のようにコードを書くと、実行結果が配列として出てくる。
def foo(*a) p a end foo(1,2,3)繰り返し
繰り返しはeach,step,uptoメソットがある。
以下は例文。
・eachmoney = 1000 (1..10).each do (1..10).each do money = money + 1 end end puts money・step
money = 1000 rate = 1.1 (2001..2010).step(2) do |i| money = money * rate end puts money.to_i・upto
money = 1000 rate = 1.1 2001.upto(2004) do money = money * rate end puts money.to_iscaffold(スキャフォールド)
ruby on rails でアプリケションを作る際の便利ツール。本来、railsでは、モデル、コントローラー、ビュー、ルーティングを作る必要があるが、scaffoldを使えば、これらの作業をまとめて行って、簡単にアプリケーションの雛形を作ってくれる。
今日の授業の感想
今日はRubyの基礎を午前に行い、午後はscaffoldを用いて、フォームを作成した。午前は問題に出されたコードが、どのような処理を行うのかを学び、色んな発見があって理解が深まった。しかし、午後の授業に追いついて行けなかった。railsの環境構築も行ったが、〜をして、次に〜をしてくださいと言われても、どこで実行すればいいのかもわからず、(macでとか、mysql、railsとか)、専門用語も多いので、それが何の意味を指していて、なんでそれを実行する意味があるのかもわからず、自分の非力さを感じた。現在何をしているのかが全く分からなかった。事前課題はprogateで指定されたことを一通り行ったが、それだけではついていけなかった。授業の復習を行い、少しずつ理解を深めて行きたい。
- 投稿日:2019-03-05T22:27:45+09:00
【備忘録】form_withしたけど、redirectされないエラー
エラーについて
ユーザー登録できたけど、画面推移しないエラーにぶつかりました。
つまり、Usersコントローラーのcreateアクション内で、User.newして、@user.saveできてユーザー登録完了しているけど、redirect_to("/users/#{@user.id}")できないので、画面推移がされないエラーが起きました。
users_controller.rbdef create @user = User.new( name: params[:name], email: params[:email], image_name:"default_user.jpg", password: params[:password] ) if @user.save session[:user_id] = @user.id flash[:notice] = "ユーザー登録が完了しました。" @current_user = @user redirect_to("/users/#{@user.id}") else render("users/new") end end原因
form_withのurl先にlocal:trueがついていないことが原因でした。
users/new.html.erb<%= form_with url: users_path do |form| %>参照記事:「Rails 5.1のform_withを使ってうまくredirectできないあなたへ」https://kimuraysp.hatenablog.com/entry/2017/07/08/233754
解決策
local:trueをつけてフォームの送信ボタンを押したら、redirectされて、無事、画面推移されました。
users/new.html.erb<%= form_with url: users_path, local: true do |form| %>メモ:なぜform_with url: users_path, local: true do |form|だとredirect(画面推移)されるのか
結論:送信先がlocalであるべきところを、リモートにフォームの送信先が指定されていた点に原因があったのかもしれません。
localで開発していたので、フォームの送信先もlocalであるべきだったのかもしれません。ですが、local:trueをつけていないことから、デフォルトのフォームになっていたため、リモート + unobtrusive XHRが有効になっており、フォームの送信先がリモートになっていた可能性が高いです。
つまり、送信先がlocalであるべきところを、リモートにフォームの送信先が指定されていた点に原因があったのかもしれません。
local: trueを指定するとフォームのリモート + unobtrusive XHR送信が無効になります(デフォルトのフォームではリモート + unobtrusive XHRが有効になります)。
参照記事:https://techracho.bpsinc.jp/hachi8833/2017_05_01/39502メモ
今回のエラーの原因を深掘りしていくには、Javascriptの知識が必要みたいなので、まずはprogateからやっていって、後ほど今回のエラーを振り返りたいと思います。
「unobtrusive XHR」とは、
unobtrusive = 「控えめな」 + XHR =「XMLHttpRequest」で、
「必控えめなXMLHttpRequest」という意味らしい。・参照記事:
XMLHttpRequest についてのメモ https://qiita.com/sirone/items/412b2a171dccb11e1bb6
- 投稿日:2019-03-05T22:27:45+09:00
form_withしたけど、redirectされない原因と解決策
エラーについて
ユーザー登録できたけど、画面推移しないエラーにぶつかりました。
つまり、Usersコントローラーのcreateアクション内で、User.newして、@user.saveできてユーザー登録完了しているけど、redirect_to("/users/#{@user.id}")できないので、画面推移がされないエラーが起きました。
users_controller.rbdef create @user = User.new( name: params[:name], email: params[:email], image_name:"default_user.jpg", password: params[:password] ) if @user.save session[:user_id] = @user.id flash[:notice] = "ユーザー登録が完了しました。" @current_user = @user redirect_to("/users/#{@user.id}") else render("users/new") end end原因
form_withのurl先にlocal:trueがついていないことが、@user.saveできてユーザー登録完了しているけど、redirect_to("/users/#{@user.id}")できない原因でした。
users/new.html.erb<%= form_with url: users_path do |form| %>参照記事:「Rails 5.1のform_withを使ってうまくredirectできないあなたへ」https://kimuraysp.hatenablog.com/entry/2017/07/08/233754
解決策
local:trueをつけてフォームの送信ボタンを押したら、redirectされて、無事、画面推移されました。
users/new.html.erb<%= form_with url: users_path, local: true do |form| %>メモ:なぜform_with url: users_path, local: true do |form|だとredirect(画面推移)されるのか
結論:form_withの送信先がlocalであるべきところを、リモートに指定されていたからかもしれません。
自分はlocalで開発していたので、フォームの送信先もlocalであるべきだったのかもしれません。ですが、local:trueをつけていないことから、デフォルトのフォームになっていたため、リモート + unobtrusive XHRが有効になっており、フォームの送信先がリモートになっていた可能性が高いです。
つまり、送信先がlocalであるべきところを、リモートにフォームの送信先が指定されていた点に原因があったのかもしれません。
local: trueを指定するとフォームのリモート + unobtrusive XHR送信が無効になります(デフォルトのフォームではリモート + unobtrusive XHRが有効になります)。
参照記事:https://techracho.bpsinc.jp/hachi8833/2017_05_01/39502メモ
今回のエラーの原因を深掘りしていくには、Javascriptの知識が必要みたいです。なので、まずはprogateである一定理解した後に、今回のエラーを振り返りたいと思います。
「unobtrusive XHR」とは、
unobtrusive = 「控えめな」 + XHR =「XMLHttpRequest」で、
「必控えめなXMLHttpRequest」という意味らしい。・参照記事:
XMLHttpRequest についてのメモ https://qiita.com/sirone/items/412b2a171dccb11e1bb6
- 投稿日:2019-03-05T21:03:36+09:00
rails Basic認証
作成しているアプリケーションでbasic認証を使ってたので、備忘録として記載
gemのinstall
gemfileに下記を追加
gem 'dotenv-rails'追加したら、bundle installを行う
環境変数の追加
gemfileやappなどがあるディレクトリに.envファイルを作成
BASIC_AUTH_USER='user名' BASIC_AUTH_PASSWORD='password'controllerの設定
controller.rbbefore_action :basic_auth private def basic_auth authenticate_or_request_with_http_basic do |username, password| username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"] end end確認
- 投稿日:2019-03-05T20:54:34+09:00
Pumaって結局何?
きっかけ
RailsでWEBアプリケーションを開発してリリースする際、PumaやUnicornを使ってNginxやらApachと組み合わせればいいということは調べてわかったけど、結局こういったコンポーネントはどういった役割を持っているのかはっきりわかっていなかったので、調べてみることにした
ウェブサーバー/アプリケーションサーバー
PumaやNginxは結局何なのか
まず、PumaとNginxが具体的にどういったものかという点について。Pumaはアプリケーションサーバー(app server)、Nginxはウェブサーバーといわれます。
アプリケーションサーバー
アプリケーションサーバーは実際に開発したアプリケーションを実行するソフトウェアです。基本的にはウェブサーバーからのリクエストを受け取りそれに対しレスポンスを返す働きをします。このとき、アプリケーションサーバーのみによってアプリケーションを動かすことも原理的には可能です。development環境で実行するときにはこのアプリケーションサーバーのみによって実行していることになります。
ウェブサーバー
ウェブサーバーはウェブサイトに対するリクエストに対する何らかの処理を行う働きをします。Railsを運用するときは、リクエストをアプリケーションサーバーに伝え、そのレスポンスをリクエストを送ってきたユーザーに返します。ただしウェブサーバーもそれ自身で動かないということはなく、静的なファイルを表示するだけであればウェブサーバーだけでも可能ですし、その方が表示速度が速くなります。また、SSL通信(httpsというプロトコルを用いて通信するとき)やリバースプロキシを用いる際にも必要になってくるソフトウェアです。
全体の流れ
最終的にどういうことかというと、ユーザからウェブサイトにリクエストが送られたとき、まずウェブサーバーがそのリクエストに応じてアプリケーションサーバーに対してこういうリクエストが来たよーと伝えます。このときに様々な処理をすることによってリクエストに対する処理を最適化することができるわけです。そしてアプリケーションサーバーが実際に処理を行ったものをウェブサーバーに伝え、それをウェブサーバーがユーザーに返すことによってユーザーの画面にウェブサイトが表示されるのでした。
参考リンク
https://www.justinweiss.com/articles/a-web-server-vs-an-app-server/
- 投稿日:2019-03-05T20:17:20+09:00
[スーパー小ネタ] railsでサーバー接続時にエラーが発生する
完全に小ネタです!
ただ、経験したことある方もいるかと思います。
結論から言うとパスには気を付けましょうということですね。はい....今回起こった事象
cloud9でGithubからクローンしたリポジトリで開発を行おうとしていました。
とりあえずクローンしてサーバーに接続してみようと思うと下記エラーが発生しましたaws-test:~/clone_article $ rails server -b 0.0.0.0 Usage: rails new APP_PATH [options] Options: [--skip-namespace], [--no-skip-namespace] # Skip namespace (affects only isolated applications) -r, [--ruby=PATH] # Path to the Ruby binary of your choice # Default: /usr/local/rvm/rubies/ruby-2.4.1/bin/ruby -m, [--template=TEMPLATE] # Path to some application template (can be a filesystem path or URL) -d, [--database=DATABASE] # Preconfigure for selected database (options: mysql/postgresql/sqlite3/oracle/frontbase/ibm_db/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc) # Default: sqlite3 [--skip-yarn], [--no-skip-yarn] # Don't use Yarn for managing JavaScript dependencies [--skip-gemfile], [--no-skip-gemfile] # Don't create a Gemfile -G, [--skip-git], [--no-skip-git] # Skip .gitignore file [--skip-keeps], [--no-skip-keeps] # Skip source control .keep files -M, [--skip-action-mailer], [--no-skip-action-mailer] # Skip Action Mailer files -O, [--skip-active-record], [--no-skip-active-record] # Skip Active Record files [--skip-active-storage], [--no-skip-active-storage] # Skip Active Storage files -P, [--skip-puma], [--no-skip-puma] # Skip Puma related files -C, [--skip-action-cable], [--no-skip-action-cable] # Skip Action Cable files.....rails new APP_PATH [options]を初めて見たときは何だこれと思い、少し焦りました。
2、3回試し、同じエラーが発生したので、やり方が悪いのかと思い、少し調べました。原因
今いるディレクトリーが Rails アプリのルートディレクトリーでないことが原因でした。
clone_article配下にrails-articleRecord/ があり、そこがRails アプリのルートディレクトリーでした。
ディレクトリを移動させるとうまくいきました。aws-test:~/clone_article $ cd rails-record/ aws-test:~/clone_article/rails-record (master) $ rails server -b 0.0.0.0 => Booting Puma => Rails 5.2.2 application starting in development => Run `rails server -h` for more startup options Puma starting in single mode... * Version 3.12.0 (ruby 2.4.1-p111), codename: Llamas in Pajamas * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://0.0.0.0:8080 Use Ctrl-C to stopパス間違いはRailsに限らず、起こりえることなので、よく確認してみると、エラーを確認する時間を短縮できるかもしれません。
- 投稿日:2019-03-05T19:51:30+09:00
半年間自動テストを利用して開発してみた感想
はじめに
以前自動テストの記事(自動テストの意義について考える)を投稿しました。
それから半年以上、自動テストを利用した開発を進めてきたため、今、どう思っているのかについて書いてみようと思います。自動テストは必要か否か
自信を持って必要だと言い切ることができると感じています。
失うことより得ることが圧倒的に多いと感じているため、特にプロジェクトのスタートアップ時期において自動テストを利用しないという選択肢を取ることはほぼないのではないかなと。
ただ、既に運用・保守を開始しているようなプロジェクトなど、一部のケースではそこまで有用ではない可能性もあると感じています。スタートアップ時期において有用と考える理由
- 積極的にロジックの変更を行うことができる
- 言語バージョンアップによる影響の検証を簡単に行うことができる
スタートアップ時期においては、特にプログラムや言語バージョンアップに対する変更リスクと戦っていくことになると考えています。
いかに柔軟に変更可能であるか、言語バージョンアップによる影響を簡単に把握できるか、という部分が最終的な開発スピードに大きく影響を及ぼしてくる。
自動テストを書いておくだけで、これらの影響の把握が簡単に行えるということは、非常に大きなメリットであると考えています。運用・保守を開始しているサービスにおいて有用でない可能性がある理由
- 変更が入る可能性が低い部分に対してかけるコスト
運用・保守を開始しているサービスにおいては、上記のコストをどうみるか?が非常に重要になってくると思います。
サービスの8割の機能に変更が入らないものについて、すべての機能に対する自動テストを書く必要があるのか?という点においては、今後のサービスの展開などを考えて慎重に判断したほうが良いと思います。
変更が多い箇所のみに絞ってテストを記載する
という方法もあると思うので、まずはなんのために自動テストを書くのか?
という部分をしっかり意識しながら行うと良いかなと。
もちろん、時間をかけて全機能に対するテストを書く価値があると判断できるのであれば、書くに越したことはないと考えてます。おわりに
思っていた以上に、自動テストによる恩恵は大きく、個人的にはもう自動テストを書かないという選択肢はないぐらいになっています。
どのレベルまでテストを書くのか?など、まだまだ課題は多くあるので、そのあたりに関しては今後も模索していければと思っています。
- 投稿日:2019-03-05T17:55:23+09:00
Rails 5.1.6 で Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord). (Gem::LoadError) というエラーが出たら
TL;DR
sqlite3
gem のバージョンが新しすぎる可能性が高いです。バージョンを落とします。Gemfilegem 'sqlite3'↓
Gemfilegem 'sqlite3', '~> 1.3.0'に書き換えて、
bundle install
を実行具体的な症状と解決方法
Rails 5.1.6 で新しいアプリケーションを作成、つまり
rails _5.1.6_ new test_app
した後に、 Scaffold で CRUD な処理を作ろうとすると、/Users/i.norifumi.homma/RailsApps/test_app% bin/rails g scaffold User email:string last_name:string first_name:string password:string /Users/i.norifumi.homma/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.1.6.1/lib/active_record/connection_adapters/connection_specification.rb:188:in `rescue in spec': Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord). (Gem::LoadError)のようなエラーが出ることがあります。
bundle install
時にはエラーは出ませんし、ちゃんと Gemfile にも Gemfile.lock にもsqlite3
が記載されています。そんな時は、 Gemfile を開いて、
sqlite3
のバージョンを1.3
系に落として、再度bundle install
します。
そうすると、今度は正しく実行できます。/Users/i.norifumi.homma/RailsApps/test_app% bin/rails g scaffold User email:string last_name:string first_name:string password:string Running via Spring preloader in process 17420 invoke active_record create db/migrate/20190305084938_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml invoke resource_route route resources :users invoke scaffold_controller create app/controllers/users_controller.rb invoke erb create app/views/users create app/views/users/index.html.erb create app/views/users/edit.html.erb create app/views/users/show.html.erb create app/views/users/new.html.erb create app/views/users/_form.html.erb invoke test_unit create test/controllers/users_controller_test.rb invoke helper create app/helpers/users_helper.rb invoke test_unit invoke jbuilder create app/views/users/index.json.jbuilder create app/views/users/show.json.jbuilder create app/views/users/_user.json.jbuilder invoke test_unit create test/system/users_test.rb invoke assets invoke coffee create app/assets/javascripts/users.coffee invoke scss create app/assets/stylesheets/users.scss invoke scss create app/assets/stylesheets/scaffolds.scssRails 5.1.6 では
sqlite3
のバージョン 1.4 系に対応していないようです。
- 投稿日:2019-03-05T17:40:37+09:00
Mac(Mojave)でRails起動すると Sorry, you can't use byebug without Readline エラー
背景
ローカルMac(Mojave)でRails4.6系。rubyを2.3系 -> 2.6系にあげて起動したところ以下のエラー発生。
Sorry, you can't use byebug without Readline. To solve this, you need to rebuild Ruby with Readline support. If using Ubuntu, try `sudo apt-get install libreadline-dev` and then reinstall your Ruby.やったこと
brewで入れたreadlineのバージョンが8.0になっているのがだめみたいだった。7.05にスイッチしたら動いた。
参考: https://github.com/deivid-rodriguez/byebug/issues/289#issuecomment-455963200
brewのバージョンの下げ方
この記事めっちゃわかりやすかった↓
Homebrewで旧バージョンのパッケージをインストールしたい
- 投稿日:2019-03-05T17:31:56+09:00
RailsでBootstrapを使うときにやることリスト
よく忘れるのでメモ。
gemを追加
gem 'bootstrap-sass', '3.3.7'
(最新バージョンはここで確認できる↓
https://rubygems.org/gems/bootstrap-sass/ )
bundle install
touch app/assets/stylesheets/custom.scss
(CSSを正しい順序で表示させるため)custom.scssに以下を追加
@import "bootstrap-sprockets";
@import "bootstrap";
- 投稿日:2019-03-05T16:50:15+09:00
文字列を表として使う場合(例10×2など)のcount及びmapの使い方
下のように「a」が6つ「nil」が4つ文字列があった時
3番目から9番目(m[2..8])にaがいくつあるか。m = [ "a" , "a" , "a" , "a" , "a" , "a" , nil , nil , nil, nil ]m[2..8].count("a") => 4文字列の中に文字列がある場合は
m = [[ "a" , "a" , "a" , "a" , "a" , "a" , nil , nil , nil , nil ], [ nil , nil , nil , nil , "a" , "a" , "a" , "a" , "a" , "a" ]]#上段 m[0][2..8].count("a") => 4 #下段 m[1][2..8].count("a") => 5*縦に調べたい場合、同じようにすると間違います。(失敗例)
(m[0][0]とm[0][1]でaの数を調べる)
#左から1番目の列 m[0..1][0].count("a") => 6同じようにするとなぜか6になります。(上段のaの数になる)
*理由を知っている方がいたら教えてください。mapを使って一段ずつ呼び出して、今回は一番左の列の情報が欲しいので、i[0]としています。
m.map{|i|i[0]}.count("a") => 1もっと良い方法があったり、こういう考え方もあると思った人はコメントしていただけると助かります。
- 投稿日:2019-03-05T16:25:46+09:00
Railsでcsv出力したファイルをexcelで開いても文字化けしない方法
発生事象
RailsアプリケーションでCSV出力をしたファイルをExcelで開くと文字化ける。
理由はExcelがUTF-8のファイルをSJISで開こうとしてしまうため。
文字a化け.rbdef to_csv csv_column_name = %w(id 名前 メール) CSV.generate do |csv| csv << csv_column_name all.each do |user| csv << user.csv_column_values end end end方法1 BOM付きUTF-8でcsv出力する
bom付きにするとexcelがUTF-8で開く。
UTF-8のファイルをUTF-8として開くから文字化けない。bom+utf8の方法.rbdef to_csv bom = "\uFEFF" csv_column_name = %w(id 名前 メール) CSV.generate(bom) do |csv| csv << csv_column_name all.each do |user| csv << user.csv_column_values end end endこの部分.rbbom = "\uFEFF"この部分.rbCSV.generate(bom) do |csv|方法2 SJISにエンコードして出力する
sjisの方法.rbdef to_csv csv_column_name = %w(id 名前 メール) CSV.generate(encoding: Encoding::SJIS, row_sep: "\r\n", force_quotes: true) do |csv| csv << csv_column_name all.each do |user| csv << user.csv_column_values end end endこの部分.rbCSV.generate(encoding: Encoding::SJIS, row_sep: "\r\n", force_quotes: true) do |csv|参考
https://qiita.com/wada811/items/8b73f633f77c0466e9da
https://qiita.com/ngron/items/60915083cb2bc3622c79
- 投稿日:2019-03-05T16:25:46+09:00
Railsで出力したcsvファイルをexcelで開いても文字化けしない方法
発生事象
RailsアプリケーションでCSV出力をしたファイルをExcelで開くと文字化ける。
理由はExcelがUTF-8のファイルをSJISで開こうとしてしまうため。
文字a化け.rbdef to_csv csv_column_name = %w(id 名前 メール) CSV.generate do |csv| csv << csv_column_name all.each do |user| csv << user.csv_column_values end end end方法1 BOM付きUTF-8でcsv出力する
bom付きにするとexcelがUTF-8で開く。
UTF-8のファイルをUTF-8として開くから文字化けない。bom+utf8の方法.rbdef to_csv bom = "\uFEFF" csv_column_name = %w(id 名前 メール) CSV.generate(bom) do |csv| csv << csv_column_name all.each do |user| csv << user.csv_column_values end end endこの部分.rbbom = "\uFEFF"この部分.rbCSV.generate(bom) do |csv|方法2 SJISにエンコードして出力する
sjisの方法.rbdef to_csv csv_column_name = %w(id 名前 メール) CSV.generate(encoding: Encoding::SJIS, row_sep: "\r\n", force_quotes: true) do |csv| csv << csv_column_name all.each do |user| csv << user.csv_column_values end end endこの部分.rbCSV.generate(encoding: Encoding::SJIS, row_sep: "\r\n", force_quotes: true) do |csv|参考
https://qiita.com/wada811/items/8b73f633f77c0466e9da
https://qiita.com/ngron/items/60915083cb2bc3622c79
- 投稿日:2019-03-05T14:16:02+09:00
MySQLのGenerated Columnsまとめ with Rails
Geneterated Columnとは
- 実カラムの値を計算した結果 を格納する専⽤のカラムを作成できる
関数indexをシミュレートできる(生成列にindexを貼れるし、生成列で分割もできる)
- MySQL5.7から使える。
- Oracleの仮想列に近い。
CREATE TABLE
またはALTER TABLE
文の中で使用できる。[GENERATED ALWAYS] as (expression)
の構文で作成する。作成
CREATE TABLE triangle ( sidea DOUBLE, sideb DOUBLE, sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb)) );データの挿入
INSERT INTO triangle (sidea, sideb) VALUES(1,1),(3,4),(6,8);確認
mysql> select * from triangle; +-------+-------+--------------------+ | sidea | sideb | sidec | +-------+-------+--------------------+ | 1 | 1 | 1.4142135623730951 | | 3 | 4 | 5 | | 6 | 8 | 10 | +-------+-------+--------------------+ 3 rows in set (0.01 sec)何故 Geneterated Column を使うのか
データの保存用途ではなく、仮想生成した列は、
照会を単純化し統一するための方法
として使用できる。
複雑な条件は、生成された列として定義することで、
確実に同じ条件を使用するようにできる。格納された生成列は、その場で計算するのにコストがかかる
複雑な条件のためのキャッシュとして使用できる。Geneterated Columnは
関数インデックスをシミュレートできる。
Geneterated Columnを使用して関数式を定義し、
それにインデックスを付けることもできる。
- 生成された列に
NOT NULL制約・UNIQUE制約をかけられる。
- 生成された列で
分割(partitioning)ができる。
- 生成された列にインデックスが付けられている場合、クエリがその列を直接名前で参照していなくても、
オプティマイザは列の定義と一致するクエリ式を認識し、
クエリ実行中にその列のインデックスを適切に使用する
https://dev.mysql.com/doc/refman/5.7/en/generated-column-index-optimizations.htmlデータの持ち方
- Geneterated Columnは
VIRTUAL
またはSTORED
を指定することができる。
デフォルトはVIRTUAL
である。テーブル内に VIRTUAL と STORED は混在できる
どちらのタイプもセカンダリーインデックスを作成できる。
セカンダリーインデックスはどちらのタイプでもDiskに固定できる。
STORED
列の値は、行が挿入または更新されたときに評価されて保管される。
- ストレージを必要とする。
- indexを貼れる。
VIRTUAL
SELECT時に都度計算される。
- ストレージを必要としない。
- MySQL 5.7.8から、計算後のデータに対してインデックスが貼れる
注意点
生成された列式は、
以下の規則に従う必要があり、
式に許可されていない構成が含まれていると、エラーが発生する。
- ストアドファンクション・ユーザー定義関数は不許可
- ストアドプロシージャと関数パラメータは不許可
- 変数(システム変数、ユーザー定義変数、およびストアドプログラムローカル変数)は不許可
サブクエリは許可されない
- AUTO_INCREMENT属性は列の定義で使用できない
カラムとしては定義されているので、 SELECT * とかはできるが、
Insert のように直接値を投入することはできない。式が宣言された列型とは異なるデータ型に評価された場合、
宣言された型への暗黙的な強制は通常のMySQLの型変換規則に従って発生する。外部キー制約は仮想生成した列を参照できない。
Railsで実装する場合
テーブル作成時に追加する方法
class CreateTriangle < ActiveRecord::Migration[5.1] def change create_table :triangle do |t| t.integer :sidea t.integer :sideb t.virtual :sidec, type: :float, as: "SQRT(sidea * sidea + sideb * sideb)" t.virtual :sided, type: :float, as: "SQRT(sidea * sidea + sideb * sideb)", stored: true end end endテーブルの確認
mysql> show create table triangle; +----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | triangle | CREATE TABLE `triangle` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `sidea` int(11) DEFAULT NULL, `sideb` int(11) DEFAULT NULL, `sidec` float GENERATED ALWAYS AS (sqrt(((`sidea` * `sidea`) + (`sideb` * `sideb`)))) VIRTUAL, `sided` float GENERATED ALWAYS AS (sqrt(((`sidea` * `sidea`) + (`sideb` * `sideb`)))) STORED, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 | +----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.01 sec)データの挿入
INSERT INTO triangle (sidea, sideb) VALUES(1,1),(3,4),(6,8);確認
mysql> select * from triangle; +----+-------+-------+---------+---------+ | id | sidea | sideb | sidec | sided | +----+-------+-------+---------+---------+ | 1 | 1 | 1 | 1.41421 | 1.41421 | | 2 | 3 | 4 | 5 | 5 | | 3 | 6 | 8 | 10 | 10 | +----+-------+-------+---------+---------+ 3 rows in set (0.00 sec)カラムとして追加
class AddColumnToTriangle < ActiveRecord::Migration[5.1] def up execute "ALTER TABLE triangle ADD COLUMN sidee float AS ( CASE WHEN sidea > 2 THEN SQRT(sidea * sidea + sideb * sideb) ELSE NULL END )" add_index :triangle, :sidee, unique: true end def down remove_column :triangle, :sidee end endテーブルの確認
mysql> show create table triangle; +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | triangle | CREATE TABLE `triangle` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `sidea` int(11) DEFAULT NULL, `sideb` int(11) DEFAULT NULL, `sidec` float GENERATED ALWAYS AS (sqrt(((`sidea` * `sidea`) + (`sideb` * `sideb`)))) VIRTUAL, `sided` float GENERATED ALWAYS AS (sqrt(((`sidea` * `sidea`) + (`sideb` * `sideb`)))) STORED, `sidee` float GENERATED ALWAYS AS ((case when (`sidea` > 2) then sqrt(((`sidea` * `sidea`) + (`sideb` * `sideb`))) else NULL end)) VIRTUAL, PRIMARY KEY (`id`), UNIQUE KEY `index_triangle_on_sidee` (`sidee`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 | +----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)確認
mysql> select * from triangle; +----+-------+-------+---------+---------+-------+ | id | sidea | sideb | sidec | sided | sidee | +----+-------+-------+---------+---------+-------+ | 1 | 1 | 1 | 1.41421 | 1.41421 | NULL | | 2 | 3 | 4 | 5 | 5 | 5 | | 3 | 6 | 8 | 10 | 10 | 10 | +----+-------+-------+---------+---------+-------+ 3 rows in set (0.00 sec)参考
MySQL 5.7にやられないためにおぼえておいてほしいこと
https://www.slideshare.net/yoku0825/mysql-57-5344973413.1.18.8 CREATE TABLE and Generated Columns
https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html
- 投稿日:2019-03-05T14:10:40+09:00
2019年3月7日以降はGoogle+ APIが使えない / rails + omniauth-google-oauth2でgoogleログインができなくなったら
Google+ API のサービス終了について
2019 年 3 月 7 日をもって、すべての Google+ API はサービスが終了します。
とのことです。参考URL) https://developers.google.com/+/api-shutdown
これにより、Googleアカウントでログインしているサービスにも影響が出るようです。
対応方法
利用するAPIをこちらに差し替えると良いという記述がありました。
https://www.googleapis.com/oauth2/v2/userinfo
参考URL) https://github.com/zquestz/omniauth-google-oauth2/issues/340
ruby gemの
omniauth-google-oauth2
のversion 0.6.0 では既に対応済みとのことです。railsで既に導入している場合は、以下のようにgemをアップデートすると解消するかと思います。
bundle update 'omniauth-google-oauth2'
確認方法
Google APIsのダッシュボードから、Google+ APIを無効化した状態でもGoogleアカウントでログインできれば成功です。
omniauth-google-oauth2
の旧バージョンでは、Google+ API用のendpointを参照しているようですので、これで確認OKだと思います。Google APIs) https://console.developers.google.com/apis/dashboard
- 投稿日:2019-03-05T14:10:40+09:00
2019年3月7日以降にrails+omniauth-google-oauth2でgoogleログインができなくなったら
Google+ API のサービス終了について
2019 年 3 月 7 日をもって、すべての Google+ API はサービスが終了します。
とのことです。参考URL) https://developers.google.com/+/api-shutdown
これにより、Googleアカウントでログインしているサービスにも影響が出るようです。
対応方法
利用するAPIをこちらに差し替えると良いという記述がありました。
https://www.googleapis.com/oauth2/v2/userinfo
参考URL) https://github.com/zquestz/omniauth-google-oauth2/issues/340
ruby gemの
omniauth-google-oauth2
のversion 0.6.0 では既に対応済みとのことです。railsで既に導入している場合は、以下のようにgemをアップデートすると解消するかと思います。
bundle update 'omniauth-google-oauth2'
確認方法
Google APIsのダッシュボードから、Google+ APIを無効化した状態でもGoogleアカウントでログインできれば成功です。
omniauth-google-oauth2
の旧バージョンでは、Google+ API用のendpointを参照しているようですので、これで確認OKだと思います。Google APIs) https://console.developers.google.com/apis/dashboard
- 投稿日:2019-03-05T14:05:39+09:00
deviseでパスワードの最低文字数を変更する方法。
パスワードを変更するには2種類の方法があります。
1 railsアプリのルートディレクトリ直下の~/configディレクトリにあるファイルを編集する方法です。
~/config/initializers/devise.rbにアクセスしてください。
config.password_length = 6..128上記の部分がファイルの真ん中らへんにあるので、数値の部分を変更してください。
2 deviseを使用するモデルに直接記述する方法です。
例えば、user.rbに
devise :validatable, password_length: 10..128このようにpassword_length: で指定してあげましょう。
最後に
自分でモデルにvalidates :password, length: { maximum: 8 }みたいに指定するとエラーの元になるのでやめましょう。僕はこれで1時間くらい溶かしました。
参考文献
https://github.com/plataformatec/devise/wiki/Customize-minimum-password-length
- 投稿日:2019-03-05T12:41:39+09:00
Google+API終了!devise✕omniauth-google-oauth2でGoogle+APIを利用せずにrailsのgoogleログインできるようにする
背景
自分が開発していたrailsアプリケーションで
omniauth-google-oauth2
のgemでgoogleログインを実装していたのですが
他の記事を参考に、Google+APIを有効にしてgoogle側の認証の設定をしていました。
参考:爆速ッ!! gem omniauth-google-oauth2 で認証させる
[Rails] Facebook/Twitter/Googleでのユーザー登録をDevise & Omniauthを使って爆速で実装する
など。Google+APIの終了
ところが、Google+APIが終了ということでこれはまずいんじゃないか?
と思い調べてみたものの、なかなか解決策がみつからず。
Google+APIを無効化したらInvalid Credential
と認証が無効であると怒られてしまう...解決方法
どうしたものかと、
omniauth-google-oauth2
のgemのバージョンを上げてみたところ、gem update 'omniauth-google-oauth2' bundle install 'omniauth-google-oauth2'無事Google+APIを無効にしてもログインできるようになりました。
定期的なgemの更新は大事ですね。
- 投稿日:2019-03-05T12:26:00+09:00
Rails~ルーティングのネストとform_forの関係~
form_tagだと...
form_tag ('/tweets', method: :post) do form_tag ('tweets_path', method: :post) do form_tag ({controller: :tweets, action: :create}, {method: :post}) doform_forだと...
form_for (モデルクラスのインスタンス) do |f| 例えば。。。 form_for (@tweets) do |f|form_tagではルーティングを示すものとHTTPリクエストが必要だが、form_forではモデルクラスのインスタンスを記述するだけで良い
ルーティングのネストとform_forの記述の関係
routes.rbresources :users do resources :products do resources :reviews end endURLはこんな感じになる
/users/:id/product/:id/reviewshtml.erbform_for ([@users, @product, @review]) do |f|
- 投稿日:2019-03-05T07:35:30+09:00
Ruby クラス、インスタンス、継承
動作環境はMacです。
クラス
クラスとは車でいえば設計図のこと。
インスタンスは、設計図を元に作られる自動車そのもの。
ゆえにクラスの要素はもつが、個々のインスタンスでの特徴は違う。オリジナルクラスの作成
構文
class クラス名
end**実践**
car.rbclass Car #Carクラスを作成。頭文字は大文字 def hello #メソッドの定義 puts "Hello!" end end car = Car.new #newメソッドでインスタンスを生成し、変数carに格納 car.hello #インスタンス変数の呼び出しcae.rbclass Car def initialize(name) #インスタンスが作成(new)されると同時に実行 puts "初期化されました" @name = name #インスタンス変数に引数nameの値を格納 end def hello puts "Hello! I am #{@name}" #インスタンス変数はインスタンスの中であればどこからでも参照可能 end end car = Car.new("Kitt") #initializeされ、引数”Kitt”がnameに渡される car.hello karr = Car.new("Karr") car.hellocar.rb初期化されました #newされたタイミングで出力される Hello! I am Kitt 初期化されました Hello! I am Kittクラスに所有されるインスタンスであればいくらでも呼び出すことができる。
アクセサ(インスタンス)メソッド
ゲッター、セッターとも呼ばれる。
インスタンス変数はクラスの外部から呼び出すことができないため、
呼び出せるように設定するメソッド。accessor.rbclass Car def initialize(name) puts "初期化されました" @name = name end def hello puts "Hello! I am #{@name}" end end car = Car.new("Kitt") car.hello car.@name #クラス外部からインスタンス変を呼び出す出力結果
ruby accessor.rb accessor.rb:15: syntax error, unexpected tIVAR, expecting '(' car.@name ^エラーになる。
accessor.rbclass Car def initialize(name) puts "初期化されました" @name = name end def hello puts "Hello! I am #{@name}" end def name #外部から読み取り可能なメソッドを定義 @name end end car = Car.new("Kitt") car.hello puts car.name #呼び出し出力結果
ruby accessor.rb 初期化されました Hello! I am Kitt Kitt #呼び出されたaccessor.rbclass Car def initialize(name) puts "初期化されました" @name = name end def hello puts "Hello! I am #{@name}" end def name @name end def name=(value) #nameのを書き換えるメソッド。=も含まれる。スペースは開けない @name = value #インスタンス変数にvalueを代入 end end car = Car.new("Kitt") car.hello puts car.name car.name = "sisido" #nameを書き換え puts car.name出力結果
初期化されました Hello! I am Kitt Kitt sisido #書き換わった!アクセサメソッド2
attr_accessor
インスタンスメソッドを簡単に書き込みや読み込みができるようにするためのメソッド
accessor.rbdef name @name end def name=(value) @name = value end #この記述を簡単に書き換える↓
accessor.rbclass Car attr_accessor :name #この箇所にアクセサメソッドを記述 def initialize(name) puts "初期化されました" @name = name end def hello puts "Hello! I am #{@name}" end #def name #@name #end #def name=(value) #@name = value #end end car = Car.new("Kitt") car.hello puts car.name car.name = "sisido" puts car.name出力結果
初期化されました Hello! I am Kitt Kitt sisido
読み取り専用メソッド
accessor.rbclass Car #attr_accessor :name attr_reader :name #読み取り専用のメソッド def initialize(name) #puts "初期化されました" @name = name end def hello puts "Hello! I am #{@name}" end #def name #@name #end #def name=(value) #@name = value #end end car = Car.new("Kitt") #car.hello #car.@name NG puts car.name #car.@name = "sisido" NG #car.name = "sisido" puts car.nameKitt Kitt
書き込み専用のメソッド
attr_accessor.rbclass Car #attr_accessor :name #attr_reader :name attr_writer :name #書き込み専用メソッドの記述 def initialize(name) #puts "初期化されました" @name = name end def hello puts "Hello! I am #{@name}" end #def name #@name #end #def name=(value) #@name = value #end end car = Car.new("Kitt") #car.hello #car.@name NG #puts car.name #car.@name = "sisido" NG car.name = "sisido" #puts car.nameクラス変数
クラス自体に値を保持することができる変数
実践
class_val.rbclass Car @@counter = 0 #クラス変数はここに記述 def initialize(name) @name = name @@counter += 1 #インスタンスが作成されるごとにインクリメントされる end def hello puts "Hello! I am #{@name}.#{@@counter} instanse(s)."#この中にインスタンスの作成回数を記録 end end kitt = Car.new("Kitt") kitt.hello karr = Car.new("Karr") karr.hello imawano = Car.new("imawano") imawano.hello出力結果
Hello! I am Kitt.1 instanse(s). Hello! I am Karr.2 instanse(s). Hello! I am imawano.3 instanse(s).クラスメソッド
クラスから直接呼び出すことができるメソッド
実践
class_val.rbclass Car @@counter = 0 def initialize(name) @name = name @@counter += 1 end def hello puts "Hello! I am #{@name}.#{@@counter} instanse(s)." end def self.info #クラスメソッドの作成 selfをつける puts "#{@@counter} instance(s)" end end #kitt = Car.new("Kitt") #kitt.hello #karr = Car.new("Karr") #karr.hello #imawano = Car.new("imawano") #imawano.hello Car.info #クラスメソッドの呼び出し出力結果
0 instance(s) #インスタンスは作成していないため0インスタンスが作成された直後にクラスメソッドを呼び出す
class_val.rbclass Car @@counter = 0 def initialize(name) @name = name @@counter += 1 end def hello puts "Hello! I am #{@name}.#{@@counter} instanse(s)." end def self.info puts "#{@@counter} instance(s)" end end kitt = Car.new("Kitt") #kitt.hello Car.info karr = Car.new("Karr") #karr.hello Car.info imawano = Car.new("imawano") #imawano.hello Car.info出力結果
1 instance(s) 2 instance(s) 3 instance(s)1つのクラスをインスタンスが共通して使用していることがわかる。
クラスと定数
class_const.rbclass Car REGION = "USA" #定数の定義 @@counter = 0 def initialize(name) @name = name @@counter += 1 end def hello puts "Hello! I am #{@name}.#{@@counter} instanse(s)." end def self.info puts "#{@@counter} instance(s). REGION: #{REGION}" ##クラスメソッド内で定数を使用 end end kitt = Car.new("Kitt") #kitt.hello Car.info karr = Car.new("Karr") #karr.hello Car.info imawano = Car.new("imawano") #imawano.hello Car.info puts Car::REGION #定数の呼び出し定数の作成や呼び出しはクラス変数やインスタンス変数と違うので注意
**クラスの継承**
スーパークラス(親クラス)の振る舞いをサブクラス(子クラス)でも使用することができる
**実践**
sub_class.rbclass User def initialize(name) @name = name end def hello puts "Hello! I am #{@name}." end end class AdminUser < User #Userクラスの継承 def admin_hello #AdminUserクラス内でメソッドを定義 puts "Hello! I am #{@name} from AdminUser." end end iwamano = User.new("iwamano") iwamano.hello sansiro = AdminUser.new("sansiro") sansiro.hello #AdminUserクラスのでUserクラスのメソッドを使用 sansiro.admin_hello #AdminUserクラス内のメソッドを使用出力結果
Hello! I am iwamano. Hello! I am sansiro. Hello! I am sansiro from AdminUser.Userクラス(親クラス)からAdminUserクラスのメソッドを呼び出すことはできない
sub_class.rbiwamano = User.new("iwamano") iwamano.hello iwawano.admin_hello出力結果
Hello! I am iwamano. sub_class.rb:19:in `<main>': undefined method `admin_hello' for #<User:0x007fbc6a19f4c8 @name="iwamano"> (NoMethodError) #エラーが返るオーバーライド
親クラスの持つ振る舞いを子クラスで上書きや追加をすることができる機能
実践
sub_class.rbclass User def initialize(name) @name = name end def hello puts "Hello! I am #{@name}." end end class AdminUser < User def admin_hello puts "Hello! I am #{@name} from AdminUser." end def hello puts "Admin!" #親クラスのhelloメソッドに上書き end end iwamano = User.new("iwamano") iwamano.hello #子クラスのhelloメソッドの呼び出し #iwamano.admin_hello sansiro = AdminUser.new("sansiro") sansiro.hello sansiro.admin_hello出力結果
Hello! I am iwamano. Admin! #親クラスのhelloメソッドは呼び出されず、子クラスのhelloメソッドが呼び出されている Hello! I am sansiro from AdminUser.オーバーライドは親クラスの修正を加えることなく子クラスに同名のメソッドを記述することで、上書きや追加をすることができる。
クラスの確認
クラス名.superclassで確認することができる。
例)Integerクラス
irb(main):002:0> Integer.superclass => Numeric irb(main):003:0> Numeric.superclass => Object irb(main):004:0> Object.superclass => BasicObject irb(main):006:0> BasicObject.superclass => nil #BasicObjectクラスが大元の親クラスとなるため、nilが返る
- 投稿日:2019-03-05T01:59:25+09:00
rails create、updateアクションでの小ネタ(エラーハンドリング�について)
はじめに、、、
そもそもエラーハンドリングとは、、、
エラーハンドリングとは、プログラムの処理中に処理が妨げられる事象が発生した際、その処理をエラーとして対処する処理のことである。例外処理とも呼ばれる。
とのことです。
今回はこれについて話していきます。railsでエラーハンドリングを行う
railsでエラーハンドリングを行わなければならなくもっとも使用頻度で高いものでいうとmodelのcreateアクションだと思います。
article_controller.rbdef create @article = Article.create(article_params) redirect_to articles_path end上のような書き方をしていると、もし仮にarticleがバリデーションの設定等の原因で新しいレコードをcreate出来なかった場合、エラーがでてそこで処理が止まってしまいます。
ローカル開発なら問題ないですが、実務環境で処理が止まってしまうと数分で何百万もの損失を出してしまう可能性があります。
そこで、エラーハンドリングを行います。そしてエラーハンドリングをやった時のcreateアクションが↓です。
article_controller.rbdef create #@articleと言う変数に値を入れる @article = Article.new(article_params) if @article.save #保存できるか? #保存に成功した時はarticleのindexアクションにリダイレクト redirect_to articles_path else #保存に失敗したときはarticleのnewアクションにリダイレクト render :new end endこのように、値をseveする時にseveできているかの条件式(if文)を挟む事で、
値が作成されずエラーが出て処理が停止してしまう事は無くなります。最後に
railsではscaffoldなどのrails g コマンドを使ってビューファイルを生成した時は、自動でエラーハンドリングは行われていますが、
自分でsave処理を行う時などは、気をつけてコーディングしましょう!
- 投稿日:2019-03-05T00:57:56+09:00
【Rails5】DBからデータが削除されたことをテスト自動化するときの観点たち
Introduction
Railsでデータを削除する機能をRSpecのSystem Testでテストしたときのメモ。
「削除対象のレコードが削除されていること」を確認するためにいくつかの観点から検証を実施してみました。【観点1】対象を削除すると検索してもNotFound
[id=1のレコードを削除処理] expect{Model.find(1)}.to raise_exception(ActiveRecord::RecordNotFound)まずは素直に削除した対象を検索するとNotFoundになる検証。
find_by
を使っても同じように検証できます。
あんまり考えにくいですが、id
がupdate
された場合でも検証は通ってしまいます。ちなみに今回の記事で一番メモしておきたかったのはこの
raise_exception
の書き方。
expect{}
とブロックで記述しないといけないのがかなりつまずきポイントでした。【観点2】対象を削除するとwhere検索しても0件
expect(Model.where(id: 1).count).to eq 1 [id=1のレコードを削除処理] expect(Model.where(id: 1).count).to eq 0次はwhereを使ってひっかかった数が0になることでレコードが削除されたことを検証する方法です。この場合でも、
id
がupdate
されたケースは検証は通ってしまいます。
where
なので1件に絞る必要はなく、例えばアソシエーションの全ての子要素を消す時の検証などでは使いやすい気がします。「User1に紐づく全Itemを削除」の場合はexpect(Item.where(user_id: 1).count).to eq 0
とすれば検証ができます。
一方で、where
だとデータ量が多いとテスト実行の性能が悪くなったりするのかな。【観点3】対象を削除するとレコード数が減る
count = Model.all.count [いずれか1レコードを削除処理] expect(Model.all.count).to eq (count - 1)1レコード削除されれば総数が1減る、という検証です。これ単体では何が消えたのか定かではありませんが、観点1・2と合わせることで「
id
がupdate
された」可能性をなきものとできる気がします。Conclusion
【観点1】or【観点2】でおおよそやりたいことはできました。【観点3】も合わせ技として複合すれば、間違ってほかのレコードも消していないことなども検証できるので安心安全です。
- 投稿日:2019-03-05T00:52:34+09:00
Rails〜環境構築〜
Command Line Tools
gccというコンパイラが入っていてRuby内部でこのコンパイラを使用するHomebrew
MacOS上でソフトウェアの管理を行うもの パッケージ管理とは、追加、更新、削除を行うことrbenv
Homebrew経由でインストールする 必要に応じてRubyのバージョンを切り替えることを可能にするターミナルrbenv rehash gemはRubyのバージョンごとに管理されている gemをインストールした時にそのgemのコマンドをそのRubyのバージョンで使えるようにするためのコマンドruby-build
Rubyのバージョンをインストールすることを可能にするRubyGems
Rubyのプラクイン管理機能みたいなものGem
Rubyを便利に扱うためのアプリケーション Rubyのバージョンごとに管理されている ターミナルでそれぞれのgemが持つコマンドを実行するという形で使用するBundler
gemの1つなのでRubyGems経由でインストールする プロジェクトごとに使用するgemを決めたり、バージョンを決めたりする RubyGemsより柔軟なイメージ
- 投稿日:2019-03-05T00:34:17+09:00
Rails〜ユーザー管理機能の実装〜
deviseの導入
gemfilegemfileに記述する gem 'devise'ターミナルbundle install rails s deviseの設定ファイル rails g devise:install deviseに基づいたUserモデル rails g devise user rake db:migrate deviseによるデフォルトのview rails g devise:views usersコントローラは普通に rails g controller usersニックネームを登録項目の追加
ターミナルnicknameカラムの追加 rails g migration AddNicknameToUsers nickname:string rake db:migrate ※間違えた場合は rake db:rollback追加したカラムをストロングパラメータとして受け取る
application_controller.rbbefore_action :configure_permitted_parameters, if: :devise_controller? def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname]) end ※sign_upはアクション名他には
ログインしていないユーザーに投稿させないようにする
ヘッダー部分にログイン・新規登録ボタンを作る
登録画面にニックネームを登録するフォームを追加する
- 投稿日:2019-03-05T00:32:00+09:00
【備忘録】Railsを使って開発を始めたいと思った時の読み物
これは何か
- Railsで開発を始めようとした時の環境構築あたりのtipsをまとめたもの
参考情報
Rails開発環境の構築(複数バージョン共存可能)(Homebrew編)
- Homebrewを使って、Railsをローカルインストールで抑えることで、複数バージョンを管理することができるようになる良記事
- 若干、環境構築の話も出てきているが、どちらかというとRailsプロジェクトを始める時の進め方が書いてあってめちゃ助かる記事