20190305のRubyに関する記事は23件です。

Railsでページ遷移後にJavaScriptが実行されない問題の解消法

こんにちは、とくめいチャットサービス「ネコチャ」運営者のアカネヤ(@ToshioAkaneya)です。

Railsでページ遷移後にJavaScriptが実行されない問題について解説します。

Railsでページ遷移後にJavaScriptが実行されない問題の解消法

以下の行を削除します。

application.js
//= require turbolinks

turbolinksは初心者にとっては余計な悩みを増やすだけですので、削除するのが良いでしょう。

はてなブックマーク・Pocketはこちらから

はてなブックマークに追加
Pocketに追加

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「Railsは終わった」と言われる理由

はじめに

Rubyは死んだ、Railsは時代遅れという人が最近増えてきたように思えます。
私自身RubyやRailsを書いて3年位経ちますが、「終わりつつあるな」と実感することが多いです。
そう思った経緯を記事に書いていきます。

Railsの特徴

Railsの特徴というか、流行した要因としては以下の5つが大きいと私は思っています。

  1. テンプレート、パーシャル、レイアウトをERBを使ってすばやく構築できる
  2. Active Recordによってデータベースを簡単に定義、操作ができる
  3. アセットパイプラインによってcss、jsを管理することができる
  4. チュートリアルが充実している
  5. Rubyという柔軟性の高い言語によって開発することができる

私はRailsはこの5本の柱によって支えられていると思っています。
これらの5本の柱のメリットにより、Railsは大流行しました。
すばやく簡単にプロダクトを作ることができ、チュートリアルが充実しているため、新人教育も簡単でした。

しかし時代の流れとともにこのメリットは以下のようにほぼ失われてしまいました。

  1. テンプレート、パーシャル、レイアウトをERBを使ってすばやく構築できる ReactやVue.jsによる保守性の高いフロントエンドが構築できるようになり使われなくなった
  2. Active Recordによってデータベースを簡単に定義、操作ができる これは今でも現役で使える!
  3. アセットパイプラインによってcss、jsを管理することができる webpackerなどによって置き換えられた
  4. チュートリアルが充実している 扱っている内容が段々と古くなってきており、時代に合わなくなりつつある
  5. 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>

しかし大規模になればなるほど、以下のような辛いことが待っています。

  1. jQueryなどでViewの状態を管理するので見通しが悪くなる
  2. cssの管理が大変になる。BEMなどのアプローチで頑張ってもいずれ辛くなる。
  3. パーシャルはレンダリングのみで状態を保持したり、隠蔽することができないため、使いづらい。
  4. form_for、form_withなどのRails独自の記法の挙動が意外と難しい
  5. 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.tsx
document.addEventListener('DOMContentLoaded', () => {
  const node = document.getElementById('login');
  ReactDOM.render(<LoginPage />, node);
});
index.tsx
export 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などのフロントエンドのフレームワークを導入することにより以下のようなメリットが得られました。

  1. コンポーネント化によって定数や状態を隠蔽することができ、保守性の高いフロントエンドが構築できる
  2. cssの影響範囲を任意のクラス内に留めることができる。
  3. (TypeScriptを使った場合)型によって安全にプログラミングすることができる。
  4. フロントエンドでネイティブアプリのようにモデル層、通信層などレイヤーを決めて開発できるようになった。

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プロダクトが多く生まれてしまいました。

  1. 読みにくい
  2. Rubyしか触っていないエンジニアはある一定レベルで成長が止まる
  3. 美しく書くという文化でしか質を担保する仕組みが無い
  4. 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
  end

Integer以外が入ったときには静的解析などで通知してくれると嬉しいです。
Typescriptのように複数の型を返すときは以下のようにかけるといいと思いました。
ただ、Trueクラス、Falseクラスが存在する中でBooleanクラスをすんなり導入できるかどうかで意見が分かれそうです。

  def method(x : Integer, y : Integer) : Integer | nil
    if x > y
      x
    else
      nil
    end
  end

Ruby3の開発状況は全然知らないのですが、
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やデータベースの仕組みはしばらく変わることはないので、それさえ知ってればなんとかなるんじゃないのかなー。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 y
x = 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メソットがある。
以下は例文。
・each

money = 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_i

scaffold(スキャフォールド)

ruby on rails でアプリケションを作る際の便利ツール。本来、railsでは、モデル、コントローラー、ビュー、ルーティングを作る必要があるが、scaffoldを使えば、これらの作業をまとめて行って、簡単にアプリケーションの雛形を作ってくれる。

今日の授業の感想

今日はRubyの基礎を午前に行い、午後はscaffoldを用いて、フォームを作成した。午前は問題に出されたコードが、どのような処理を行うのかを学び、色んな発見があって理解が深まった。しかし、午後の授業に追いついて行けなかった。railsの環境構築も行ったが、〜をして、次に〜をしてくださいと言われても、どこで実行すればいいのかもわからず、(macでとか、mysql、railsとか)、専門用語も多いので、それが何の意味を指していて、なんでそれを実行する意味があるのかもわからず、自分の非力さを感じた。現在何をしているのかが全く分からなかった。事前課題はprogateで指定されたことを一通り行ったが、それだけではついていけなかった。授業の復習を行い、少しずつ理解を深めて行きたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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
end
output
1
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.

output
1
2
3
4
5

Refactoring used of map

This is bad smell, you should replace each with map.

each
hoge = []
[1, 2, 3, 4, 5].each_with_index do |e, i|
  hoge[i] = e * 100
end

This is code replaced with map.
It be elegant dosen't it?

map
hoge = [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.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】form_withしたけど、redirectされないエラー

エラーについて

ユーザー登録できたけど、画面推移しないエラーにぶつかりました。

つまり、Usersコントローラーのcreateアクション内で、User.newして、@user.saveできてユーザー登録完了しているけど、redirect_to("/users/#{@user.id}")できないので、画面推移がされないエラーが起きました。

users_controller.rb
def 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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

form_withしたけど、redirectされない原因と解決策

エラーについて

ユーザー登録できたけど、画面推移しないエラーにぶつかりました。

つまり、Usersコントローラーのcreateアクション内で、User.newして、@user.saveできてユーザー登録完了しているけど、redirect_to("/users/#{@user.id}")できないので、画面推移がされないエラーが起きました。

users_controller.rb
def 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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

python, ruby, php, node のループの速さ

今日も楽しいマイクロベンチマーク。

某所で python の for ループが遅いという話を聞いたので、そうなの? と思って他の言語と比べてみた。

ソース

python3

python3
import 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.js
const len = process.argv[2]|0;
let r=0;
for( let i=0 ; i<len ; ++i ){
  ++r;
}
console.log(r);

ruby

ruby2.6
n=ARGV[0].to_i
r=0
n.times do
  r+=1
end
p r

測る人

測る人はわりとやる気ない感じで、Benchmark.realtime を使っている。

bench.rb
require "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

各言語のファイル名がいい加減なのがバレるね。

結果

結果は下記グラフの通り。
両対数グラフ注意。

image.png

測る人のソースコードを見ると分かる通り、プログラムの起動時間を含んでいる。
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 だけ起動が速いらしい。そういうものか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

半年間自動テストを利用して開発してみた感想

はじめに

以前自動テストの記事(自動テストの意義について考える)を投稿しました。
それから半年以上、自動テストを利用した開発を進めてきたため、今、どう思っているのかについて書いてみようと思います。

自動テストは必要か否か

自信を持って必要だと言い切ることができると感じています。
失うことより得ることが圧倒的に多いと感じているため、特にプロジェクトのスタートアップ時期において自動テストを利用しないという選択肢を取ることはほぼないのではないかなと。
ただ、既に運用・保守を開始しているようなプロジェクトなど、一部のケースではそこまで有用ではない可能性もあると感じています。

スタートアップ時期において有用と考える理由

  1. 積極的にロジックの変更を行うことができる
  2. 言語バージョンアップによる影響の検証を簡単に行うことができる

スタートアップ時期においては、特にプログラムや言語バージョンアップに対する変更リスクと戦っていくことになると考えています。
いかに柔軟に変更可能であるか、言語バージョンアップによる影響を簡単に把握できるか、という部分が最終的な開発スピードに大きく影響を及ぼしてくる。
自動テストを書いておくだけで、これらの影響の把握が簡単に行えるということは、非常に大きなメリットであると考えています。

運用・保守を開始しているサービスにおいて有用でない可能性がある理由

  1. 変更が入る可能性が低い部分に対してかけるコスト

運用・保守を開始しているサービスにおいては、上記のコストをどうみるか?が非常に重要になってくると思います。
サービスの8割の機能に変更が入らないものについて、すべての機能に対する自動テストを書く必要があるのか?という点においては、今後のサービスの展開などを考えて慎重に判断したほうが良いと思います。
変更が多い箇所のみに絞ってテストを記載するという方法もあると思うので、まずはなんのために自動テストを書くのか?という部分をしっかり意識しながら行うと良いかなと。
もちろん、時間をかけて全機能に対するテストを書く価値があると判断できるのであれば、書くに越したことはないと考えてます。

おわりに

思っていた以上に、自動テストによる恩恵は大きく、個人的にはもう自動テストを書かないという選択肢はないぐらいになっています。
どのレベルまでテストを書くのか?など、まだまだ課題は多くあるので、そのあたりに関しては今後も模索していければと思っています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyでのハッシュと配列の違いについて

ハッシュと配列の違いは確実に理解しよう!

ハッシュと配列は非常に形が似ていて、なおかつよく使うものなのでしっかりと覚えておきましょう!

ハッシュとは

ハッシュはキーとバリューがセットで格納できるのが特徴です。
また複数オブジェクト格納することができます。

hash{}
・hashの表記
Items={"キー":"バリュー",....}
・呼び出し方
puts Items
--Itemsに格納されているキーとバリューの両方がセットで出力される。
puts Items[:キー]
--指定したキーとセットのバリューが出力される

配列とは

ハッシュと似ていて、複数のオブジェクトを格納することができる。
しかし、ハッシュのようにキーとバリューといった関係性はないので理解はしやすい。

hairetu = []
・配列の表記
hairetu=["a""b""c""d"]
・呼び出し方
hairetu[0]-"a"が出力される。
hairetu[1]-"b"が出力される。

配列を呼び出すときはインデックスという番号を使って呼び出します。
ちなみに、始まりは必ず0スタート。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

文字列を表として使う場合(例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

もっと良い方法があったり、こういう考え方もあると思った人はコメントしていただけると助かります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

シンボルはハッシュのキーのためだけに存在している

Rubyにはシンボルがありますが、ほぼ文字列ど同じだし、なんのためにあるの???ってずっと思ってました。

そこで色々と勉強していくうちにシンボルの存在理由がわかったので、ここにまとめてみようと思います。

シンボルは文字列より処理が早く、数字よりも読みやすい

結論から言ってしまうと、
ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいから
です。

ハッシュのキーは、文字列でもいいしハッシュでもいい。なんなら数字でもいい

そもそもですが、ハッシュのキーはシンボル以外でも全く問題なく動きます。
文字列でもいいですし、数字でもいいです。もちろんシンボルでもいいです。

以下ではハッシュのキーとして、
文字列、数字、シンボルを比較して説明していきます。

シンボルの方が文字列よりも処理が早い理由

簡単にいうと、
シンボルはメモリ番地が変わらないのに対し、
文字列はメモリ番地が毎回変わるからです。

文字列はメモリ番地が変わるので、文字列を探すためのコストがかかります。
それに対しシンボルはメモリ番地は変わりません。

シンボルでは探すコストがかからないので、文字列よりも早く処理ができます。

"メモリ番地が変わらない"という点で言えば、数字とシンボルは似ています。

シンボルは数字と文字列のいいとこ取りをしているとも言えますね。

シンボルの方が数字よりも読みやすい理由

流れ的に章を分けましたが、これは自明ですね。

キーに数字を使うなら配列でいいじゃんって話です。

まとめ

ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいから。

シンボルは数字のようにメモリ番地が変わらないので、文字列よりも処理が早い

シンボルは数字と文字列のいいとこ取り

以上の3つを抑えてからはシンボルの存在意義を問うことはくなりました。

シンボルで悩める人の参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

最近シンボルについて結構わかってきたのでまとめてみる

Rubyにはシンボルがありますが、ほぼ文字列ど同じだし、なんのためにあるの???ってずっと思ってました。

そこで色々と勉強していくうちにシンボルの存在理由がわかったので、ここにまとめてみようと思います。

シンボルは文字列より処理が早く、数字よりも読みやすい

結論から言ってしまうと、
ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいから
です。

ハッシュのキーは、文字列でもいいしハッシュでもいい。なんなら数字でもいい

そもそもですが、ハッシュのキーはシンボル以外でも全く問題なく動きます。
文字列でもいいですし、数字でもいいです。もちろんシンボルでもいいです。

以下ではハッシュのキーとして、
文字列、数字、シンボルを比較して説明していきます。

シンボルの方が文字列よりも処理が早い理由

簡単にいうと、
シンボルはメモリ番地が変わらないのに対し、
文字列はメモリ番地が毎回変わるからです。

文字列はメモリ番地が変わるので、文字列を探すためのコストがかかります。
それに対しシンボルはメモリ番地は変わりません。

シンボルでは探すコストがかからないので、文字列よりも早く処理ができます。

"メモリ番地が変わらない"という点で言えば、数字とシンボルは似ています。

シンボルは数字と文字列のいいとこ取りをしているとも言えますね。

シンボルの方が数字よりも読みやすい理由

流れ的に章を分けましたが、これは自明ですね。

キーに数字を使うなら配列でいいじゃんって話です。

まとめ

ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいから。

シンボルは数字のようにメモリ番地が変わらないので、文字列よりも処理が早い

シンボルは数字と文字列のいいとこ取り

以上の3つを抑えてからはシンボルの存在意義を問うことはくなりました。

シンボルで悩める人の参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【シンボルの存在意義】シンボルはハッシュのキーのためだけに存在していると言っても過言ではない。なぜならシンボルは文字列より処理が早く、数字よりも読みやすいから

Rubyにはシンボルがありますが、ほぼ文字列ど同じだし、なんのためにあるの???ってずっと思ってました。

そこで色々と勉強していくうちにシンボルの存在理由がわかったので、ここにまとめてみようと思います。

シンボルは文字列より処理が早く、数字よりも読みやすい

結論から言ってしまうと、
ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいから
です。

ハッシュのキーは、文字列でもいいしハッシュでもいい。なんなら数字でもいい

そもそもですが、ハッシュのキーはシンボル以外でも全く問題なく動きます。
文字列でもいいですし、数字でもいいです。もちろんシンボルでもいいです。

以下ではハッシュのキーとして、
文字列、数字、シンボルを比較して説明していきます。

シンボルの方が文字列よりも処理が早い理由

簡単にいうと、
シンボルはメモリ番地が変わらないのに対し、
文字列はメモリ番地が毎回変わるからです。

文字列はメモリ番地が変わるので、文字列を探すためのコストがかかります。
それに対しシンボルはメモリ番地は変わりません。

シンボルでは探すコストがかからないので、文字列よりも早く処理ができます。

"メモリ番地が変わらない"という点で言えば、数字とシンボルは似ています。

シンボルは数字と文字列のいいとこ取りをしているとも言えますね。

シンボルの方が数字よりも読みやすい理由

流れ的に章を分けましたが、これは自明ですね。

キーに数字を使うなら配列でいいじゃんって話です。

まとめ

ハッシュのキーとしてシンボルを使う理由は、
文字列より処理が早く、数字よりも読みやすいから。

シンボルは数字のようにメモリ番地が変わらないので、文字列よりも処理が早い

シンボルは数字と文字列のいいとこ取り

以上の3つを抑えてからはシンボルの存在意義を問うことはくなりました。

シンボルで悩める人の参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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の記事を書こうかな。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby と Python は似ているというけれど、設計思想レベルで見るとめちゃめちゃ違う

RubyもPythonもスクリプト言語ですし、確かに似ている部分はあります。

しかし、"設計思想"レベルで見てみると大きく違っています。

その違いがとてもおもしろいので紹介したいと思います。

Rubyは自由を好み、Pythonは統一を好む

簡単に言ってしまえば、Rubyは自由を好み、Pythonは統一を好みます。

Pythonはインデントによるdefなどの範囲指定が特徴的ですが、Rubyにはそのような制約はありません。

というのも、
Rubyは「書きたいように書く」というような自由な設計思想があるからです。

それに対し、
Pythonは「誰が書いても同じようなコードになる」というような、統一感を好む設計思想になっています。


このような設計思想を知っていれば各言語の細々とした違いも理解しやすくなります。

他の言語も調べてみると面白いかもしれないですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby クラス、インスタンス、継承

動作環境はMacです。

クラス

クラスとは車でいえば設計図のこと。
インスタンスは、設計図を元に作られる自動車そのもの。
ゆえにクラスの要素はもつが、個々のインスタンスでの特徴は違う。

オリジナルクラスの作成

構文

class クラス名
end

**実践**

car.rb
class Car #Carクラスを作成。頭文字は大文字
  def hello #メソッドの定義
    puts "Hello!"
  end
end

car = Car.new  #newメソッドでインスタンスを生成し、変数carに格納
car.hello #インスタンス変数の呼び出し
cae.rb
class 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.hello
car.rb
初期化されました #newされたタイミングで出力される
Hello! I am Kitt
初期化されました
Hello! I am Kitt

クラスに所有されるインスタンスであればいくらでも呼び出すことができる。

アクセサ(インスタンス)メソッド

ゲッター、セッターとも呼ばれる。
インスタンス変数はクラスの外部から呼び出すことができないため、
呼び出せるように設定するメソッド。

accessor.rb
class 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.rb
class 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.rb
class 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.rb
def name
  @name
end

def name=(value)
  @name = value
end

#この記述を簡単に書き換える

accessor.rb
class 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.rb
class 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.name
Kitt
Kitt

書き込み専用のメソッド

attr_accessor.rb
class 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.rb
class 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.rb
class 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.rb
class 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.rb
class 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.rb
class 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.rb
iwamano = 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.rb
class 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が返る
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby 2.6のArray#differenceはArray#-と同じ振る舞いだけど、なにか違いはあるのかと実装やパフォーマンスを調べた

はじめに

Ruby 2.6には配列の差集合を返す Array#difference メソッドが追加されました。
もともとあった Array#- 演算子と同じ振る舞いをしますが、複数の引数を取れるというメリットがあります。

参考: サンプルコードでわかる!Ruby 2.6の主な新機能と変更点和集合と差集合を返すunion/differenceメソッド

Array#differenceArray#- の実装は同じなのか、パフォーマンス的に違いはないのか、など気になったので調べてみました。

実装の比較

Githubにあるruby 2.6の、C言語で書かれたArrayクラスの実装 array.c をみてみます。

rb_define_xxx

Array#difference

    rb_define_method(rb_cArray, "difference", rb_ary_difference_multi, -1);

Array#-

    rb_define_method(rb_cArray, "-", rb_ary_diff, 1);

どちらも rb_define_alias ではなく rb_define_method と書かれているので、 Array#differenceArray#- は別の実装のようです。ただ、メソッドの実装の中で一方がもう一方を呼び出している、という可能性が残っていますね。

メソッドの実装

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;
}

rb_ary_diff (Array#-の実装)

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で実装されていますね。

Set#-の実装

   def -(enum)
     dup.subtract(enum)
   end
   alias difference -

そしてArrayとは異なって、 Set#differenceSet#- の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
end

1,10,100,1000の100倍の大きさの配列aに対して、aの1/100の大きさの別々の配列を1回〜10回引いています。
パフォーマンスに影響を与えないように、出来るだけベタに書きました。(が、後の結果を見るとここはあまり神経質にならなくて良さそうです)

このスクリプトは、上に書いたようなごく限られたパターンのパフォーマンスを計測していることに注意してください。

上記のスクリプトの実行結果を少し加工してcsvファイルにして、Tableau Publicに入れてグラフにしてみました。

スクリーンショット 2019-03-03 11.26.37.png

Array#difference の方が Array#- よりも少し早いようですね。n=1000の場合の傾きを比べると、Array#differenceArray#- の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#differenceArray#- は別実装
  • Array#difference の方が Array#- よりもちょっと速い(限定された状況でのパフォーマンス計測で)
  • 速さの差は小さいので、通常は読みやすい方を使えばよい
  • どうしてこういう実装なのかはわからないので、詳しい方教えてください
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

今日のスクレイピングの復習

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の根幹をなしています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】Railsを使って開発を始めたいと思った時の読み物

これは何か

  • Railsで開発を始めようとした時の環境構築あたりのtipsをまとめたもの

参考情報

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メソッド名の変更(Rename Method)

image.png

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は提供されているメソッドが略されすぎて、微妙に分かりにくいものが結構ある気もする

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む