- 投稿日: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-05T22:55:12+09:00
Refactoring used of map in Ruby
Refactoring used of map in Ruby
Overview
This is topic for practice in English, so Please don’t expect too much.
I used to write a each, when I just started ruby programming.
but I recently write map to refactor my source.
I write for that matter.Version
ruby 2.3.1
comparison each with map
Let's comparison each with map.
This is the most pupuler way of writing which use each.each[1, 2, 3, 4, 5].each do |e| puts e endoutput1 2 3 4 5
This is the most pupuler way of writing which use map.
map[1, 2, 3, 4, 5].map{ |m| puts m }map's output is same as each.
output1 2 3 4 5
Refactoring used of map
This is bad smell, you should replace each with map.
eachhoge = [] [1, 2, 3, 4, 5].each_with_index do |e, i| hoge[i] = e * 100 endThis is code replaced with map.
It be elegant dosen't it?maphoge = [1, 2, 3, 4, 5].map{ |m| m * 100 }I blieve if you use map, it will lead to be readable and reduce amount of your sorce.
- 投稿日: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-05T22:09:16+09:00
python, ruby, php, node のループの速さ
今日も楽しいマイクロベンチマーク。
某所で python の for ループが遅いという話を聞いたので、そうなの? と思って他の言語と比べてみた。
ソース
python3
python3import sys r=0 for i in range(int(sys.argv[1])): r+=1 print(r)PHP
<?php $len = (int)$argv[1]; $r=0; for( $i=0 ; $i<$len ; ++$i ){ ++$r; } echo($r);node.js
node.jsconst len = process.argv[2]|0; let r=0; for( let i=0 ; i<len ; ++i ){ ++r; } console.log(r);ruby
ruby2.6n=ARGV[0].to_i r=0 n.times do r+=1 end p r測る人
測る人はわりとやる気ない感じで、
Benchmark.realtime
を使っている。bench.rbrequire "benchmark" require "pp" COMMANDS = [ [ "php for.php", "php" ], [ "node for_pp.js", "node" ], [ "python3 for_range.py", "python3" ], [ "ruby times.rb", "ruby" ], ] COUNTS = (10..27).map{ |e| 2**e } File.open( "result.csv", "w" ) do |f| f.puts( (["tick"]+COMMANDS.map{ |cmd| cmd[1] }).join(",") ) COUNTS.each do |count| s=([count]+COMMANDS.map{ |cmd| Benchmark.realtime{ %x(#{cmd[0]} #{count}) } }).join(",") f.puts(s) puts(s) end end各言語のファイル名がいい加減なのがバレるね。
結果
結果は下記グラフの通り。
両対数グラフ注意。測る人のソースコードを見ると分かる通り、プログラムの起動時間を含んでいる。
10万回ぐらい回しても、起動時間の影に隠れて殆ど見えないということがわかる。1.34億回回すのに要する時間を、node.js を 1.00 として表にすると:
php node python3 ruby Benchmark.realtime そのまま 6.13 1.00 61.72 28.83 起動時間らしきものを減算 10.56 1.00 108.11 50.13 「起動時間らしきものを減算」は、1.34億回の結果から 1024回の結果を減じたもの。
こんな感じ。node 速いね。
そして噂のとおり、python3 は遅いのであった。あと。PHP だけ起動が速いらしい。そういうものか。
- 投稿日: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-05T19:51:30+09:00
半年間自動テストを利用して開発してみた感想
はじめに
以前自動テストの記事(自動テストの意義について考える)を投稿しました。
それから半年以上、自動テストを利用した開発を進めてきたため、今、どう思っているのかについて書いてみようと思います。自動テストは必要か否か
自信を持って必要だと言い切ることができると感じています。
失うことより得ることが圧倒的に多いと感じているため、特にプロジェクトのスタートアップ時期において自動テストを利用しないという選択肢を取ることはほぼないのではないかなと。
ただ、既に運用・保守を開始しているようなプロジェクトなど、一部のケースではそこまで有用ではない可能性もあると感じています。スタートアップ時期において有用と考える理由
- 積極的にロジックの変更を行うことができる
- 言語バージョンアップによる影響の検証を簡単に行うことができる
スタートアップ時期においては、特にプログラムや言語バージョンアップに対する変更リスクと戦っていくことになると考えています。
いかに柔軟に変更可能であるか、言語バージョンアップによる影響を簡単に把握できるか、という部分が最終的な開発スピードに大きく影響を及ぼしてくる。
自動テストを書いておくだけで、これらの影響の把握が簡単に行えるということは、非常に大きなメリットであると考えています。運用・保守を開始しているサービスにおいて有用でない可能性がある理由
- 変更が入る可能性が低い部分に対してかけるコスト
運用・保守を開始しているサービスにおいては、上記のコストをどうみるか?が非常に重要になってくると思います。
サービスの8割の機能に変更が入らないものについて、すべての機能に対する自動テストを書く必要があるのか?という点においては、今後のサービスの展開などを考えて慎重に判断したほうが良いと思います。
変更が多い箇所のみに絞ってテストを記載する
という方法もあると思うので、まずはなんのために自動テストを書くのか?
という部分をしっかり意識しながら行うと良いかなと。
もちろん、時間をかけて全機能に対するテストを書く価値があると判断できるのであれば、書くに越したことはないと考えてます。おわりに
思っていた以上に、自動テストによる恩恵は大きく、個人的にはもう自動テストを書かないという選択肢はないぐらいになっています。
どのレベルまでテストを書くのか?など、まだまだ課題は多くあるので、そのあたりに関しては今後も模索していければと思っています。
- 投稿日:2019-03-05T19:24:04+09:00
rubyでのハッシュと配列の違いについて
ハッシュと配列の違いは確実に理解しよう!
ハッシュと配列は非常に形が似ていて、なおかつよく使うものなのでしっかりと覚えておきましょう!
ハッシュとは
ハッシュはキーとバリューがセットで格納できるのが特徴です。
また複数オブジェクト格納することができます。hash{} ・hashの表記 Items={"キー":"バリュー",....} ・呼び出し方 puts Items --Itemsに格納されているキーとバリューの両方がセットで出力される。 puts Items[:キー] --指定したキーとセットのバリューが出力される配列とは
ハッシュと似ていて、複数のオブジェクトを格納することができる。
しかし、ハッシュのようにキーとバリューといった関係性はないので理解はしやすい。hairetu = [] ・配列の表記 hairetu=["a""b""c""d"] ・呼び出し方 hairetu[0]-"a"が出力される。 hairetu[1]-"b"が出力される。 配列を呼び出すときはインデックスという番号を使って呼び出します。 ちなみに、始まりは必ず0スタート。
- 投稿日: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-05T14:25:05+09:00
シンボルはハッシュのキーのためだけに存在している
Rubyにはシンボルがありますが、ほぼ文字列ど同じだし、なんのためにあるの???ってずっと思ってました。
そこで色々と勉強していくうちにシンボルの存在理由がわかったので、ここにまとめてみようと思います。
シンボルは文字列より処理が早く、数字よりも読みやすい
結論から言ってしまうと、
ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいからです。ハッシュのキーは、文字列でもいいしハッシュでもいい。なんなら数字でもいい
そもそもですが、ハッシュのキーはシンボル以外でも全く問題なく動きます。
文字列でもいいですし、数字でもいいです。もちろんシンボルでもいいです。以下ではハッシュのキーとして、
文字列、数字、シンボルを比較して説明していきます。シンボルの方が文字列よりも処理が早い理由
簡単にいうと、
シンボルはメモリ番地が変わらないのに対し、
文字列はメモリ番地が毎回変わるからです。文字列はメモリ番地が変わるので、文字列を探すためのコストがかかります。
それに対しシンボルはメモリ番地は変わりません。シンボルでは探すコストがかからないので、文字列よりも早く処理ができます。
"メモリ番地が変わらない"という点で言えば、数字とシンボルは似ています。
シンボルは数字と文字列のいいとこ取りをしているとも言えますね。
シンボルの方が数字よりも読みやすい理由
流れ的に章を分けましたが、これは自明ですね。
キーに数字を使うなら配列でいいじゃんって話です。
まとめ
ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいから。シンボルは数字のようにメモリ番地が変わらないので、文字列よりも処理が早い
シンボルは数字と文字列のいいとこ取り
以上の3つを抑えてからはシンボルの存在意義を問うことはくなりました。
シンボルで悩める人の参考になれば幸いです。
- 投稿日:2019-03-05T14:25:05+09:00
最近シンボルについて結構わかってきたのでまとめてみる
Rubyにはシンボルがありますが、ほぼ文字列ど同じだし、なんのためにあるの???ってずっと思ってました。
そこで色々と勉強していくうちにシンボルの存在理由がわかったので、ここにまとめてみようと思います。
シンボルは文字列より処理が早く、数字よりも読みやすい
結論から言ってしまうと、
ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいからです。ハッシュのキーは、文字列でもいいしハッシュでもいい。なんなら数字でもいい
そもそもですが、ハッシュのキーはシンボル以外でも全く問題なく動きます。
文字列でもいいですし、数字でもいいです。もちろんシンボルでもいいです。以下ではハッシュのキーとして、
文字列、数字、シンボルを比較して説明していきます。シンボルの方が文字列よりも処理が早い理由
簡単にいうと、
シンボルはメモリ番地が変わらないのに対し、
文字列はメモリ番地が毎回変わるからです。文字列はメモリ番地が変わるので、文字列を探すためのコストがかかります。
それに対しシンボルはメモリ番地は変わりません。シンボルでは探すコストがかからないので、文字列よりも早く処理ができます。
"メモリ番地が変わらない"という点で言えば、数字とシンボルは似ています。
シンボルは数字と文字列のいいとこ取りをしているとも言えますね。
シンボルの方が数字よりも読みやすい理由
流れ的に章を分けましたが、これは自明ですね。
キーに数字を使うなら配列でいいじゃんって話です。
まとめ
ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいから。シンボルは数字のようにメモリ番地が変わらないので、文字列よりも処理が早い
シンボルは数字と文字列のいいとこ取り
以上の3つを抑えてからはシンボルの存在意義を問うことはくなりました。
シンボルで悩める人の参考になれば幸いです。
- 投稿日:2019-03-05T14:25:05+09:00
【シンボルの存在意義】シンボルはハッシュのキーのためだけに存在していると言っても過言ではない。なぜならシンボルは文字列より処理が早く、数字よりも読みやすいから
Rubyにはシンボルがありますが、ほぼ文字列ど同じだし、なんのためにあるの???ってずっと思ってました。
そこで色々と勉強していくうちにシンボルの存在理由がわかったので、ここにまとめてみようと思います。
シンボルは文字列より処理が早く、数字よりも読みやすい
結論から言ってしまうと、
ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいからです。ハッシュのキーは、文字列でもいいしハッシュでもいい。なんなら数字でもいい
そもそもですが、ハッシュのキーはシンボル以外でも全く問題なく動きます。
文字列でもいいですし、数字でもいいです。もちろんシンボルでもいいです。以下ではハッシュのキーとして、
文字列、数字、シンボルを比較して説明していきます。シンボルの方が文字列よりも処理が早い理由
簡単にいうと、
シンボルはメモリ番地が変わらないのに対し、
文字列はメモリ番地が毎回変わるからです。文字列はメモリ番地が変わるので、文字列を探すためのコストがかかります。
それに対しシンボルはメモリ番地は変わりません。シンボルでは探すコストがかからないので、文字列よりも早く処理ができます。
"メモリ番地が変わらない"という点で言えば、数字とシンボルは似ています。
シンボルは数字と文字列のいいとこ取りをしているとも言えますね。
シンボルの方が数字よりも読みやすい理由
流れ的に章を分けましたが、これは自明ですね。
キーに数字を使うなら配列でいいじゃんって話です。
まとめ
ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいから。シンボルは数字のようにメモリ番地が変わらないので、文字列よりも処理が早い
シンボルは数字と文字列のいいとこ取り
以上の3つを抑えてからはシンボルの存在意義を問うことはくなりました。
シンボルで悩める人の参考になれば幸いです。
- 投稿日: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:02:03+09:00
Rubyのブロックがあまり理解できてないので自分なりにまとめてみる
Rubyのブロックが毎回よくわからなくなります。強いエンジニアになりたひ。。。
あと、範囲オブジェクトもよくわかってないような気がします。雑魚です。
でも、Rubyにおいてブロックという概念はめっちゃ大事らしいので逃げることはできなさそうです。
仕方ないので勉強します。
勉強がてらここにアウトプットしてみます。
よかったらいいねしてください。喜びます。
ブロックは範囲オブジェクトや配列に対して使える
具体例をみながら理解していきましょう。
例
(1..5).each { |i| puts 2*i }
出力結果
2
4
6
8
10波カッコの部分がブロックです。
出力結果をみながらだと理解しやすいですね。
(1..5).each { |i| puts 2*i }
を無理やり和訳すると、
「(1..5)という範囲オブジェクトに対して、eachメソッドを実行する。eachメソッドの引数は|i| puts 2*i
」
という感じでしょうか。「1から5までのそれぞれの要素に対して、2倍した結果を出力する」
というとより自然言語っぽくていいですかね。ちなみにこのコード
(1..5).each { |i| puts 2*i }
はこのように書くこともできます。
(1..5).each do |i|
puts 2*i
end他の言語やってた人だったらこっちの方がわかりやすいかもですね。
しかし、今回のようにRubyのコードが一行の場合は前者の書き方が一般的なので早く慣れる必要がありそうです。
複数行のときは後者の書き方でオッケーです。
(1..5).each { |i| puts 2*i }
は和訳すると、
「(1..5)という範囲オブジェクトに対して、eachメソッドを実行する。eachメソッドの引数は|i| puts 2*i
」
と言えると言いましたが、より汎用的な言い方をすると、「範囲オブジェクトで指定された要素に対して、波カッコで書かれたRubyのコードを実行する」
とも言えますね。
ブロックを一言でいうと???
ブロックを一言でいうと、
「引数としてRubyのコードを渡せる機能」
と言えます。今までは引数には数字や文字列、trueやfalseなどを渡していましたが、ブロックでは引数にRubyコードを渡すことができます。
とりあえずはこれくらいの理解でいいですかね。
あとはコードみたり書いたりして慣れていこうと思います。mapもよくわかってないので、次はmapの記事を書こうかな。
- 投稿日:2019-03-05T10:41:30+09:00
Ruby と Python は似ているというけれど、設計思想レベルで見るとめちゃめちゃ違う
RubyもPythonもスクリプト言語ですし、確かに似ている部分はあります。
しかし、"設計思想"レベルで見てみると大きく違っています。
その違いがとてもおもしろいので紹介したいと思います。
Rubyは自由を好み、Pythonは統一を好む
簡単に言ってしまえば、Rubyは自由を好み、Pythonは統一を好みます。
Pythonはインデントによるdefなどの範囲指定が特徴的ですが、Rubyにはそのような制約はありません。
というのも、
Rubyは「書きたいように書く」というような自由な設計思想があるからです。それに対し、
Pythonは「誰が書いても同じようなコードになる」というような、統一感を好む設計思想になっています。
このような設計思想を知っていれば各言語の細々とした違いも理解しやすくなります。
他の言語も調べてみると面白いかもしれないですね。
- 投稿日: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-05T02:31:46+09:00
Ruby 2.6のArray#differenceはArray#-と同じ振る舞いだけど、なにか違いはあるのかと実装やパフォーマンスを調べた
はじめに
Ruby 2.6には配列の差集合を返す
Array#difference
メソッドが追加されました。
もともとあったArray#-
演算子と同じ振る舞いをしますが、複数の引数を取れるというメリットがあります。参考: サンプルコードでわかる!Ruby 2.6の主な新機能と変更点 の 和集合と差集合を返すunion/differenceメソッド
Array#difference
とArray#-
の実装は同じなのか、パフォーマンス的に違いはないのか、など気になったので調べてみました。実装の比較
Githubにあるruby 2.6の、C言語で書かれたArrayクラスの実装 array.c をみてみます。
rb_define_xxx
rb_define_method(rb_cArray, "difference", rb_ary_difference_multi, -1);rb_define_method(rb_cArray, "-", rb_ary_diff, 1);どちらも
rb_define_alias
ではなくrb_define_method
と書かれているので、Array#difference
とArray#-
は別の実装のようです。ただ、メソッドの実装の中で一方がもう一方を呼び出している、という可能性が残っていますね。メソッドの実装
rb_ary_difference_multi (
Array#difference
の実装)static VALUE rb_ary_difference_multi(int argc, VALUE *argv, VALUE ary) { VALUE ary_diff; long i, length; volatile VALUE t0; bool *is_hash = ALLOCV_N(bool, t0, argc); ary_diff = rb_ary_new(); length = RARRAY_LEN(ary); for (i = 0; i < argc; i++) { argv[i] = to_ary(argv[i]); is_hash[i] = (length > SMALL_ARRAY_LEN && RARRAY_LEN(argv[i]) > SMALL_ARRAY_LEN); if (is_hash[i]) argv[i] = ary_make_hash(argv[i]); } for (i = 0; i < RARRAY_LEN(ary); i++) { int j; VALUE elt = rb_ary_elt(ary, i); for (j = 0; j < argc; j++){ if (is_hash[j]) { if (rb_hash_stlike_lookup(argv[j], RARRAY_AREF(ary, i), NULL)) break; } else { if (rb_ary_includes_by_eql(argv[j], elt)) break; } } if (j == argc) rb_ary_push(ary_diff, elt); } ALLOCV_END(t0); return ary_diff; }static VALUE rb_ary_diff(VALUE ary1, VALUE ary2) { VALUE ary3; VALUE hash; long i; ary2 = to_ary(ary2); ary3 = rb_ary_new(); if (RARRAY_LEN(ary1) <= SMALL_ARRAY_LEN || RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) { for (i=0; i<RARRAY_LEN(ary1); i++) { VALUE elt = rb_ary_elt(ary1, i); if (rb_ary_includes_by_eql(ary2, elt)) continue; rb_ary_push(ary3, elt); } return ary3; } hash = ary_make_hash(ary2); for (i=0; i<RARRAY_LEN(ary1); i++) { if (rb_hash_stlike_lookup(hash, RARRAY_AREF(ary1, i), NULL)) continue; rb_ary_push(ary3, rb_ary_elt(ary1, i)); } ary_recycle_hash(hash); return ary3; }2つのメソッドの実装を見ると、似てはいるけど、それぞれ別の実装ですね。もしかして、複数の引数を取れる
rb_ary_difference_multi
側に寄せてもいいんじゃないのかな、と思いました。そうしていない理由を知っている方がいたら教えてください。おまけ: Setクラス
Arrayとは違ってSetはC言語ではなくrubyで実装されていますね。
def -(enum) dup.subtract(enum) end alias difference -そしてArrayとは異なって、
Set#difference
はSet#-
のaliasです。
なぜArrayとSetはこんなに違うのか、私にはわかりませんでした。こちらも知っている方がいたら教えてください。パフォーマンスの比較
パフォーマンスの比較もしてみましょう。
こんなスクリプトを書いてみました。https://gist.github.com/Akiyah/c55e0f218e342cbed8f8b23ffa74a0e6#file-benchmark_-_difference-rb
require 'benchmark' M = 1000 Benchmark.bm 30 do |r| [1, 10, 100, 1000].each do |n| a = (0...(n*100)).to_a b0 = ((0*n)...(1*n)).to_a b1 = ((1*n)...(2*n)).to_a b2 = ((2*n)...(3*n)).to_a b3 = ((3*n)...(4*n)).to_a b4 = ((4*n)...(5*n)).to_a b5 = ((5*n)...(6*n)).to_a b6 = ((6*n)...(7*n)).to_a b7 = ((7*n)...(8*n)).to_a b8 = ((8*n)...(9*n)).to_a b9 = ((9*n)...(10*n)).to_a r.report("#{n}, Array#-, 1,") { M.times { a - b0 } } r.report("#{n}, Array#-, 2,") { M.times { a - b0 - b1 } } r.report("#{n}, Array#-, 3,") { M.times { a - b0 - b1 - b2 } } r.report("#{n}, Array#-, 4,") { M.times { a - b0 - b1 - b2 - b3 } } r.report("#{n}, Array#-, 5,") { M.times { a - b0 - b1 - b2 - b3 - b4 } } r.report("#{n}, Array#-, 6,") { M.times { a - b0 - b1 - b2 - b3 - b4 - b5 } } r.report("#{n}, Array#-, 7,") { M.times { a - b0 - b1 - b2 - b3 - b4 - b5 - b6 } } r.report("#{n}, Array#-, 8,") { M.times { a - b0 - b1 - b2 - b3 - b4 - b5 - b6 - b7 } } r.report("#{n}, Array#-, 9,") { M.times { a - b0 - b1 - b2 - b3 - b4 - b5 - b6 - b7 - b8 } } r.report("#{n}, Array#-, 10,") { M.times { a - b0 - b1 - b2 - b3 - b4 - b5 - b6 - b7 - b8 - b9 } } r.report("#{n}, Array#difference, 1,") { M.times { a.difference(b0) } } r.report("#{n}, Array#difference, 2,") { M.times { a.difference(b0, b1) } } r.report("#{n}, Array#difference, 3,") { M.times { a.difference(b0, b1, b2) } } r.report("#{n}, Array#difference, 4,") { M.times { a.difference(b0, b1, b2, b3) } } r.report("#{n}, Array#difference, 5,") { M.times { a.difference(b0, b1, b2, b3, b4) } } r.report("#{n}, Array#difference, 6,") { M.times { a.difference(b0, b1, b2, b3, b4, b5) } } r.report("#{n}, Array#difference, 7,") { M.times { a.difference(b0, b1, b2, b3, b4, b5, b6) } } r.report("#{n}, Array#difference, 8,") { M.times { a.difference(b0, b1, b2, b3, b4, b5, b6, b7) } } r.report("#{n}, Array#difference, 9,") { M.times { a.difference(b0, b1, b2, b3, b4, b5, b6, b7, b8) } } r.report("#{n}, Array#difference, 10,") { M.times { a.difference(b0, b1, b2, b3, b4, b5, b6, b7, b8, b9) } } end end1,10,100,1000の100倍の大きさの配列aに対して、aの1/100の大きさの別々の配列を1回〜10回引いています。
パフォーマンスに影響を与えないように、出来るだけベタに書きました。(が、後の結果を見るとここはあまり神経質にならなくて良さそうです)このスクリプトは、上に書いたようなごく限られたパターンのパフォーマンスを計測していることに注意してください。
上記のスクリプトの実行結果を少し加工してcsvファイルにして、Tableau Publicに入れてグラフにしてみました。
Array#difference
の方がArray#-
よりも少し早いようですね。n=1000の場合の傾きを比べると、Array#difference
はArray#-
の80%程度のようです。ただしそれは配列を引く回数が多い場合で、1つしか引かない場合はほぼ同じ実行時間ですね。「複数の引数を取れる
Array#difference
はメソッドコールの回数を節約できるのでArray#-
よりも高速」と、どこかで聞いた気がしたのですが、配列が大きくなるにつれて実行時間の差が開いていくので、厳密にはメソッドコールの回数が差を生んでいるというわけではなさそうです。メソッドコールというより、一回ごとに配列オブジェクトを作るからArray#-
が不利という感じでしょうか。呼び出しごとに配列オブジェクトを作ることもメソッドコールのうちと思えば、メソッドコールの回数が影響していると言っても間違いではなさそうです。まあ、とにかく、この場合には
Array#difference
の方がArray#-
よりも速いことがわかりました。ただし実行時間が80%になる程度なので、通常の使い方の場合は速いか遅いかは気にせずに、読みやすい方を採用すれば良さそうですね。おまけ: 逆転する場合もある
普通の場合には
Array#difference
の方が速そうですが、特別な場合にはArray#-
が速くなる場合もあります。require 'benchmark' a = (0...100000).to_a Benchmark.bm do |r| r.report("-"){a-a-a} r.report("d"){a.difference(a,a)} end #=> # user system total real # - 0.009689 0.000928 0.010617 ( 0.010765) # d 0.016169 0.001048 0.017217 ( 0.018013)配列aから配列aを引いて、さらに配列aを引くような場合、1回目の引き算で空っぽになってしまうので、2回目の実行時間が極端に短くなって、
Array#-
がArray#difference
よりも2倍近く速くなることがあります。まとめ
Array#difference
とArray#-
は別実装Array#difference
の方がArray#-
よりもちょっと速い(限定された状況でのパフォーマンス計測で)- 速さの差は小さいので、通常は読みやすい方を使えばよい
- どうしてこういう実装なのかはわからないので、詳しい方教えてください
- 投稿日:2019-03-05T01:30:00+09:00
今日のスクレイピングの復習
Lv.0 大前提
①class = ○○(HTML)→.○○ { }(CSS) え?↓
<div class = "search">外国人の彼氏</div>「外国人の彼氏」を装飾したい。
↓
classをつけて適当に"search"と名前をつける。.search{ 文字の大きさ変えたり 背景色変えたり }class部分は
.search{ }と書けば、
CSS(装飾)があたる。
(そういうものです。)②inner_textって何?
<p>季節のコーヒー</p>「季節のコーヒー」部分がinner_text
「p」の部分はinner_textではない。Lv.1 検証画面→コピペ←私は何をやっていたの?
contents = documents.css(".searchResult_itemTitle") ④ ① ② ③画面を指差し確認しながらがいいかもです。
①ドキュメント(文章)の中の
②CSSで、(Lv.0)③class名が
"searchResult_itemTitle"
(検索結果の項目のタイトル)
となっている文章を
④「contents」という箱(変数)に入れてあげる。
※contentsという名前は作った人が勝手に命名した。
englishでもSeasonCoffeeでも何でもいい。Lv.2 each文の登場
eachからendまでが1つの塊です。
contents.each do |content| ① ② ③ ④ puts content.inner_text ⑤ ⑥ end ⑦----------1行目----------------------------
①contentsという箱(変数)に入った文章を
②まず1個箱から…
③出して…
④「content」という箱に入れてください。
※このcontentも勝手に命名。
----------2行目-----------------------------
⑤「content」に入った文章の
⑥inner_textの部分をputsしてください。(Lv.0)
----------end------------------------------
⑦putsしたら、
contetsの箱の中身が無くなるまで
①から⑥を繰り返してください。上と同じコードです。見づらかったらどうぞ。 contents.each do |content| ① ② ③ ④ puts content.inner_text ⑤ ⑥ end ⑦Lv.3 これ学ぶ意味あるの? 社会のどこで使うの?
すごい使えます。すごい使ってます。
例:Twitt○r
みんなのツイートを一覧で見る例の画面みんなのtweet○.each do |twe○t| puts twe○t end他にもF○cebookやi○stagramでも
このように実装されています。SNSの根幹をなしています。
- 投稿日:2019-03-05T00:32:00+09:00
【備忘録】Railsを使って開発を始めたいと思った時の読み物
これは何か
- Railsで開発を始めようとした時の環境構築あたりのtipsをまとめたもの
参考情報
Rails開発環境の構築(複数バージョン共存可能)(Homebrew編)
- Homebrewを使って、Railsをローカルインストールで抑えることで、複数バージョンを管理することができるようになる良記事
- 若干、環境構築の話も出てきているが、どちらかというとRailsプロジェクトを始める時の進め方が書いてあってめちゃ助かる記事
- 投稿日:2019-03-05T00:12:17+09:00
メソッド名の変更(Rename Method)
1つずつリファクタリング技法まとめ
個人的に簡単かつ取り入れ易いと思うものから目的
すぐ引き出せるようにする
基本作業サイクル
- システムを動かして仕様を精査
- テストメソッドを作成
- テストの失敗を確認
- テストの成功を確認
- 小さい変更、随時テスト実行(パターン追加失敗確認->成功確認)
- 最後テスト実行
- 最後動作確認
メソッド名の変更(Rename Method)とは
メソッド名からメソッドの目的が分からないときにメソッド名を変更すること
ポイント
- メソッド名が略されすぎてよく分からない
- 微妙なニュアンスを追加したい(品詞を追加した方が分かりやすい)
例
- メソッド名が略されすぎてよく分からない
def creca_pt puts @point end↓
def credit_card_point puts @point end
- 微妙なニュアンスを追加したい(品詞を追加した方が分かりやすい)
def telephone_number puts @office_number end↓
def office_telephone_number puts @office_number end書籍情報
Jay Fields (著), Shane Harvie (著), Martin Fowler (著), Kent Beck (著),
長尾 高弘(訳), リファクタリング:Rubyエディション
https://amzn.to/2VlyWML雑感
Rubyは提供されているメソッドが略されすぎて、微妙に分かりにくいものが結構ある気もする