20201013のRailsに関する記事は15件です。

link_toメソッドのpathの引数、要る・要らない

はじめに

アプリケーションを作るときに、いつもlink_toメソッドでエラーが出ていた。しかも、そのリンク先に飛ぶときではなく、link_toメソッドがあるページを表示させたときに、
ActionController::UrlGenerationError
このエラーが出ていて、悩まされた。いつも○○_pathと、ルーティングを確認して記述するところまでは問題がなく、躓かない。ここに( )で引数を渡すとなると、何を渡したらいいのか分からなくなっていた。

引数がなくてもOKなパターン

 アプリケーションを利用する誰もが同じページに飛ぶ場合は引数がなくてもよい。ログインページやroot_pathのページがこれにあたる。

やっぱりrails routesは便利だった

ターミナルでカレントディレクトリが作成中のアプリケーションのディレクトリで

% rails routes

を入力すると、ルーティングを教えてくれる。
pathの記述はもちろん、Prefixの部分を見る。しかし、
ここで着目すべきは、URI Patternの部分。ここで/:idなど、idがURIに含まれている場合は、引数を渡す必要がある。
:idとidがURIに含まれている場合は、引数に(user.id)などとすればよい。
また、引数によっては、.idを省略できる場合もある。

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

【rails】requireメソッドとpermitメソッド

requireメソッドとpermitメソッドについて学習したため、アウトプットいたします。

この記事を読むと、require,permitメソッドの意味と使い方を理解できます。

requireメソッドとは?

ストロングパラメーターのメソッドの1つで、paramsからとってくるデータのオブジェクト名を指定する。
※ストロングパラメーターについてわからない場合は、Googleで検索してみてください。

使い方例
def user_params
 params.require(:user)
end

これによってユーザーというオブジェクトのデータを指定しています。

permitメソッドとは?

ストロングパラメーターの1つで、paramsから取ってくるデータのキーを指定する。

使い方例

def
tweet_params
params_permit(:name, :text, :image)
end

つまり2つの違いはキーを指定するかオブジェクトを指定するか。

そのため、permitメソッドと、requireメソッドを同時に使うこともある。

def user_params
 params.require(:user).permit(:name, :email, :password)
end

userオブジェクトを指定し、さらにuserオブジェクトのなかに定義されたname,email,passwordのキーを指定している。

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

ActiveRecordでカラム名のメソッドをオーバーライドしたときに、カラムを参照する方法

背景

モデルにDBのカラム名と同名のインスタンスメソッドを定義し、さらにそのメソッド内でカラムを参照したい

方法

2つ方法がある模様

class Person < ApplicationRecord
  def name
    self[:name]
  end

  def name
    read_attribute(:name)
  end
end

The Rails Style Guide では、self[:name]のほうをお勧めしている

参考

https://github.com/satour/rails-style-guide/blob/master/README-jaJA.md#read-attribute
https://stackoverrun.com/ja/q/5968719

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

[Rails]deviseの導入

はじめに

deviseとは、ユーザー管理機能を簡単に実装するためのgemです。

目次

1.deviseのインストール
2.deviseの設定ファイルを作成
3.モデルの作成
4.テーブルの作成
5.ビューの編集
6.コントローラーの編集

1. deviseのインストール

gemfileに以下を記述します。

gemfile
gem 'devise'

アプリのディレクトリで以下を実行します。

ターミナル
bundle install

gemをインストールした後はサーバーを再起動します。
サーバーを再起動することでgemが反映されます。

ターミナル
rails s

2. deviseの設定ファイルを作成

deviseを使用するためには、gemのインストールに加え、 devise専用のコマンドで設定ファイルを作成する必要があります。
以下を実行することで、追加したdeviseというgemの「設定関連に使用するファイル」を自動で生成することができます。

ターミナル
rails g devise:install

3. モデルの作成

deviseを利用する際には、アカウントを作成するためのUserモデルを新しく作成する必要があります。
作成には通常のモデルの作成方法ではなく、deviseのモデル作成用コマンドでUserモデルを作成します。

ターミナル
rails g devise user

また、rails g deviseコマンドによりモデルやマイグレーションを自動生成するだけでなく、routes.rbにルーティングも自動的に追加されます。

4. テーブルの作成

必要なカラムを追加する場合は、マイグレーションファイルに記述し以下を実行します。

ターミナル
rails db:migrate

5. ビューの編集

カラムを追加した場合は、追加したカラムを入力できるように、新規登録画面のビューを編集する必要があります。
デフォルトでは、deviseのビューファイルは隠れているので、以下を実行します。

ターミナル
rails g devise:views

6. コントローラーの編集

また、コントローラーを編集したい場合は以下を実行することでdevise管理下のコントローラーを作成することができます。

ターミナル
rails g devise:controllers users
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

既存のRailaアプリケーションにのせるフロントのフレームワークを比較した話

背景

既存のRailsのアプリケーションにフロントのフレームワークを導入する運びとなりまして、有名どころ3つをチェックポイントに沿って比較しましたのでその話を残しておきます。
今回はこういうやり方で比較したよ、という話なので、もしフレームワークを選ぶことになった際は参考程度に読んでいただき、その時々の状況やプロジェクトに応じて比較方法は変更すればよいと思います。なのでこれが正解というわけではないです。調査結果に誤りがある場合もあると思いますので、調査は自分で公式のものを見てちゃんと行った方がよいです。

比較したフレームワーク

今回比較、検討したフレームワークは以下の3つです。

  • VueJs
  • React
  • Angular

ちなみにAngularは、今回導入しようとしているアプリはそんなに大規模というわけではなかったので、ある程度のところでAngularの調査は切り上げ、VueとReactに絞りました。

チェックポイント

今回「まあこれが分かれば比較できそうかもしれない」と考えてチェックポイントをチーム内で相談し決めました。

  • フレームワークの概要(特徴)
  • 主なプログラミング言語
  • 最新バージョン
  • 学習難易度
  • パッケージサイズ
  • GUI
  • 既存機能の実装難易度
  • Heroku上の動作時における不具合等
  • コード管理のしやすさ
  • 開発手法
  • ドキュメント
  • 他フレームワークへの以降性
  • ローカル環境構築のしやすさ

比較方法

期間:2週間
調査人数:エンジニア3~4名

チームメンバーで上記のチェックポイントを分担し、確認を行っていきました。その際の情報共有はGoogleドキュメントを使用し、適宜必要に応じてHangoutを行いました。

比較結果

チェックポイント VueJs React Angular
フレームワークの概要(特徴) OSSフレームワークでコミュニティにより開発・メンテ。GUIのコンポネントのみ提供。必要であれば他のライブラリをインストールする Facebook社により開発・メンテされている。GUIのコンポネントのみ提供する。必要であれば他のライブラリをインストールする Google社により開発・メンテ。フロントエンド用のMVCフレームワーク
主なプログラミング言語 EMACS6 EMACS6 Typescript
最新バージョン(調査当時) 2.6 16.13 10
学習難易度 容易:GUIのコンポネントとライフサイクルを理解すれば問題ない。CSSコードはコンポネントの中に記述してもいい 容易:GUIのコンポネントとライフサイクルを理解すれば問題ない。 難:GUIの作成方法以外、フレームワークのコンポネントを理解する必要がある
パッケージサイズ(約) 80KB 100KB 500KB
既存のサイドパーティ Vue Material Kit, Vuetify, Vue Material, Quasar, Bootstrap-Vue Raect Material Kit, Material UI, React Bootstrap, Ant Design, Semantic UI React mdbootstrap, material, ng-bootstrap
既存機能の実装難易度 APIを新たに作成する必要がある。 Vue同様
Heroku上の動作時における不具合等 deployして検証 deployして検証
コード管理のしやすさ コンポネントで分かれているためコード管理はしやすい Vue同様
開発手法 オブジェクト指向 コンポネント指向 コンポネント指向
ドキュメント 日本語あり 日本語バージョンは英語の直訳 日本語あり
他フレームワークへの以降性 独自フォーマットであるため、開発が進むと後戻りは困難 Vue同様 フルスタックなため代替は困難
ローカル環境構築のしやすさ 導入時の設定は少し困難だが、そこが完了していれば容易 Vue同様

比較結果

既存機能を実際に開発してみて検証をし、ドキュメントを読んで比較した結果今回はVue&Vuetifyになりました。チームメンバーにVueの経験がある人が多かったのも一つです。

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

既存のRailsアプリケーションにのせるフロントのフレームワークを比較した話

背景

既存のRailsのアプリケーションにフロントのフレームワークを導入する運びとなりまして、有名どころ3つをチェックポイントに沿って比較しましたのでその話を残しておきます。
今回はこういうやり方で比較したよ、という話なので、もしフレームワークを選ぶことになった際は参考程度に読んでいただき、その時々の状況やプロジェクトに応じて比較方法は変更すればよいと思います。なのでこれが正解というわけではないです。調査結果に誤りがある場合もあると思いますので、調査は自分で公式のものを見てちゃんと行った方がよいです。

比較したフレームワーク

今回比較、検討したフレームワークは以下の3つです。

  • Vue.Js
  • React
  • Angular

ちなみにAngularは、今回導入しようとしているアプリはそんなに大規模というわけではなかったので、ある程度のところでAngularの調査は切り上げ、VueとReactに絞りました。

チェックポイント

今回「まあこれが分かれば比較できそうかもしれない」と考えてチェックポイントをチーム内で相談し決めました。

  • フレームワークの概要(特徴)
  • 主なプログラミング言語
  • 最新バージョン
  • 学習難易度
  • パッケージサイズ
  • GUI
  • 既存機能の実装難易度
  • Heroku上の動作時における不具合等
  • コード管理のしやすさ
  • 開発手法
  • ドキュメント
  • 他フレームワークへの以降性
  • ローカル環境構築のしやすさ

比較方法

期間:2週間
調査人数:エンジニア3~4名

チームメンバーで上記のチェックポイントを分担し、確認を行っていきました。その際の情報共有はGoogleドキュメントを使用し、適宜必要に応じてHangoutを行いました。

比較結果

チェックポイント Vue React Angular
フレームワークの概要(特徴) OSSフレームワークでコミュニティにより開発・メンテ。GUIのコンポネントのみ提供。必要であれば他のライブラリをインストールする Facebook社により開発・メンテされている。GUIのコンポネントのみ提供する。必要であれば他のライブラリをインストールする Google社により開発・メンテ。フロントエンド用のMVCフレームワーク
主なプログラミング言語 EMACS6 EMACS6 Typescript
最新バージョン(調査当時) 2.6 16.13 10
学習難易度 容易:GUIのコンポネントとライフサイクルを理解すれば問題ない。CSSコードはコンポネントの中に記述してもいい 容易:GUIのコンポネントとライフサイクルを理解すれば問題ない。 難:GUIの作成方法以外、フレームワークのコンポネントを理解する必要がある
パッケージサイズ(約) 80KB 100KB 500KB
既存のサイドパーティ Vue Material Kit, Vuetify, Vue Material, Quasar, Bootstrap-Vue Raect Material Kit, Material UI, React Bootstrap, Ant Design, Semantic UI React mdbootstrap, material, ng-bootstrap
既存機能の実装難易度 APIを新たに作成する必要がある。 Vue同様
Heroku上の動作時における不具合等 deployして検証 deployして検証
コード管理のしやすさ コンポネントで分かれているためコード管理はしやすい Vue同様
開発手法 オブジェクト指向 コンポネント指向 コンポネント指向
ドキュメント 日本語あり 日本語バージョンは英語の直訳 日本語あり
他フレームワークへの以降性 独自フォーマットであるため、開発が進むと後戻りは困難 Vue同様 フルスタックなため代替は困難
ローカル環境構築のしやすさ 導入時の設定は少し困難だが、そこが完了していれば容易 Vue同様

比較結果

既存機能を実際に開発してみて検証をし、ドキュメントを読んで比較した結果今回はVue&Vuetifyになりました。チームメンバーにVueの経験がある人が多かったのも一つです。

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

Rails で LTI 認証を実装する

LTI 認証とは

LTI認証という規格があります。
LTIとはLearning Tools Interoperability という単語の頭文字で、要するに学習支援ツール間で認証情報をやり取りする際の規格という意味です。

認証技術の中でも分野が特定されているかなりニッチな技術なので、知らない方も多いんではないでしょうか。僕も教育工学を専門にする前はLTIという単語すら知りませんでした。

以下にIMS GLOBAL から公式に出されているLTI認証の説明図をのっけて置きます。

個人的には、結局OAuthの一種と理解しているのですが、我々が開発するアプリケーションを Learning Tools, 認証の基盤となる大本のツールを Learning Platform として、Learning Tools は Learning Platform の拡張機能を提供する代わりに、認証情報を Learning Platform からもらいます。これによってユーザーは様々なアプリをユーザーを切り替えることなしに、使い分けることができるという利点があるわけです。このとき、Learning Toolsのほうを LTI Tool Provider, Learning Platformのほうを LTI Consumer と呼んだりもします。

ただ、問題はこれをどう実装するかです。実際、ニッチな技術ゆえにドキュメンテーションに乏しく、ネット上を探しても実装例はあまり公開されていません。そこで今回、この記事では Ruby on Rails を使った LTI 認証の実際について紹介したいと思います。

実装例

ライブラリのインストール

まずは、必要なライブラリをインストールします。

$ bundle add devise
$ bundle add ims-lti
$ bundle add oauth

Rails を使うメリットは lti 認証のためのライブラリがあるところです。今回は ims-lti を使って実装します。

認証キーの登録

config配下にlti_settings.yml ファイルを作成します。
内容は以下の内容を記載してください(コンシューマーキー、LTIシークレットは適宜安全な文字列を使用してください)。

config/lti_settings.yml
production:
  __consumer_key__: '__lti_secret__'

development:
  __consumer_key__: '__lti_secret__'

作成したら、これを読み込むための設定をconfig/application.rbに追加します。

config/application.rb
config.lti_settings = Rails.application.config_for(:lti_settings)

コントローラーの作成

ltis_controllerを作って以下の内容を記載しましょう

require 'oauth/request_proxy/action_controller_request'

class LtisController < ApplicationController
  skip_before_action :verify_authenticity_token, only: :launch

  def lti_launch
    # 受け取った consumer key が config/lti_settings.yml の中にあるかどうかを確認
    if not Rails.configuration.lti_settings[params[:oauth_consumer_key]]
      render :launch_error, status: 401
      return
    end 
    shared_secret = Rails.configuration.lti_settings[params[:oauth_consumer_key]]
    authenticator = IMS::LTI::Services::MessageAuthenticator.new(request.url, request.request_parameters, shared_secret)

    #Check if the signature is valid
    if not authenticator.valid_signature?
      render :launch_error, status: 401
      return
    end
    #check if the message is too old
    if DateTime.strptime(request.request_parameters['oauth_timestamp'],'%s') < 5.minutes.ago
      render :launch_error, status: 401
      return
    end

    # LTI 情報をsessionに保存
    session_data = {
      "fullname" => authenticator&.message&.lis_person_name_full,
      "email" => authenticator&.message&.lis_person_contact_email_primary,
      "user_id" => authenticator&.message&.user_id,
      "context_id" => authenticator&.message&.context_id,
      "context_title" => authenticator&.message&.context_title,
      "tool_consumer_instance_name" => authenticator&.message&.tool_consumer_instance_name
    }
    print(session_data)
    session['lti-authenticator'] = session_data
    sign_in_and_redirect(User.first)
  end
end

MessageAuthenticator にわたってきたリクエストと事前にこちらで保持しているキーの情報を渡すことで簡単に認証ができます。認証が成功したら、devise ライブラリを使ってsign_inすることで、アプリ側での認証を実装しています。

最後にlti_launchを起動するためのエンドポイントを定義しておきましょう。

config/routes.rb
  match 'lti/launch' => 'ltis#lti_launch', via: [:get, :post], as: :lti_launch

Learning Platform 側での設定

ここまでできたら、あとは Learning Platform 側で事前に lti_settings.yml に記載した認証情報を書いて指定したエンドポイントに遷移するように設定すれば完成です。このやり方は個々のLearning Platformによって異なるので割愛します。

(補足)LTI 遷移時に Rails で cross origin error が出たら

config/application.rb に以下の内容を追記してください

config/application.rb
    config.lti_settings = Rails.application.config_for(:lti_settings)
    config.action_dispatch.default_headers['Referrer-Policy'] = 'unsafe-url'
    config.action_controller.forgery_protection_origin_check = false
    config.action_controller.allow_forgery_protection = false

参考文献

Rails LTI Tool Provider
IMS LTI

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

デプロイ後に背景画像が適用されない場合の対処法

この記事を書いた背景

デプロイしたら背景画像が表示されない!
調べたら結構そういう人いたため。

実施環境

macOS Catalina 10.15.7
VS Code 1.50.0
Ruby 2.6.5
Rails 6.0.0
サーバー:AWS EC2
WEBサーバー:Nginx
アプリケーションサーバー:unicorn 5.4.1

対処法

僕はこれでいけました。
デプロイ後、本番に上がったアプリ等で背景画像が表示されます。

①背景画像は『/app/assets/images』フォルダに格納する

②背景画像を指定するCSSの種類を『SCSS』にする

③背景画像を指定するCSSの記述『background-image: image-url("画像名");』

scss
/*例*/
body { 
  background-image: image-url("wallpaper.jpg");
}

※この記述だと逆にローカル環境で背景画像が表示されない...笑

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

アプリをもっと良くしよう

コメント機能の追加

Commentのscaffoldをする

Commentの著者名、コメント本文、Ideaテーブルへの関係(reference)をscaffoldします。
rails generate scaffold comment user_name:string body:text idea:reference

マイグレーションをします。
rails db:migrate

モデルに関係(relation)を追加する

Ideaとcommentオブジェクト間の接続をRailsに認識させます。

app/models/idea.rb
class Idea < ApplicationRecord
has_many :comments
app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :idea

コメントフォームの表示と編集

app/views/ideas/show.html.erb
<p>
  <strong>Picture:</strong>
  <%= image_tag(@idea.picture_url, width: 600) if @idea.picture.present? %>
</p>

<h3>Comments</h3>
<% @comments.each do |comment| %>
  <div>
    <strong><%= comment.user_name %></strong>
    <br />
    <p><%= comment.body %></p>
    <p><%= link_to 'Delete', comment_path(comment), method: :delete, data: { confirm: '削除してもよろしいですか?' } %></p>
  </div>
<% end %>
<%= render 'comments/form', comment: @comment %>
app/controllers/ideas_controller.rb
def show
  @comments = @idea.comments.all
  @comment = @idea.comments.build
end
app/views/comments/_form.html.erb
<div class="field">
  <%= form.label :body %>
  <%= form.text_area :body %>
</div>

<%= form.hidden_field :idea_id %>

最後に次の行を削除します。

app/views/comments/_form.html.erb
<div class="field">
  <%= form.label :idea_id %>
  <%= form.number_field :idea_id %>
</div>

HTML&CSSを使ってデザインしましょう

アプリケーションのレイアウトを適用する

app/assets/stylesheets/application.css
body { padding-top: 100px; }

上の行を以下のように書き換えます。

app/assets/stylesheets/application.css
body { padding-top: 60px; }

最後にapp/assets/stylesheets/scaffolds.scssを削除します。

ナビゲーションを良くしよう

"New Idea"ボタンをナビゲーションに常に表示します。

app/views/layouts/application.html.erb
<li class="active"><a href="/ideas">Ideas</a></li>
<li><%= link_to 'New Idea', new_idea_path %></li>

アイデアリストのデザイン

以下のように書き換えます。

app/views/ideas/index.html.erb
<h1>Listing ideas</h1>

<% @ideas.in_groups_of(3) do |group| %>
  <div class="row">
    <% group.compact.each do |idea| %>
      <div class="col-md-4">
        <%= image_tag idea.picture_url, width: '100%' if idea.picture.present? %>
        <h4><%= link_to idea.name, idea %></h4>
        <%= idea.description %>
      </div>
    <% end %>
  </div>
<% end %>

<h2>Ideaの詳細ページをデザイン</h2>

以下のように書き換えます。

```app/views/ideas/show.html.erb
<p id="notice"><%= notice %></p>

<div class="row">
  <div class="col-md-9">
    <%= image_tag(@idea.picture_url, width: '100%') if @idea.picture.present? %>
  </div>

  <div class="col-md-3">
    <p><b>Name: </b><%= @idea.name %></p>
    <p><b>Description: </b><%= @idea.description %></p>
    <p>
      <%= link_to 'Edit', edit_idea_path(@idea) %> |
      <%= link_to 'Destroy', @idea, data: { confirm: 'Are you sure?' }, method: :delete %> |
      <%= link_to 'Back', ideas_path %>
    </p>
  </div>
</div>



```

Carrierrwaveを使ったサムネイルの表示

ImageMagickのインストール

brew install imagemagickを実行します、

gem 'carrierwave'

の下に、

gem 'mini_magick'

を追加します。その後、以下のコマンドを実行します。
bundle

画像をアップロードしたときにサムネイルを作成する

app/uploaders/picture_uploader.rb
 # include CarrierWave::MiniMagick

version :thumb do
  process :resize_to_fill => [50, 50]
end

上の#を削除します。

サムネイルの作成

app/views/ideas/index.html.erb
<%= image_tag idea.picture_url, width: '100%' if idea.picture.present? %>

を以下のように変更します。

app/views/ideas/index.html.erb
<%= image_tag idea.picture_url(:thumb) if idea.picture.present? %>

Deciceで認証機能を追加

devise gemを追加

gem 'devise'

を追加します。次に以下のコマンドをターミナルで実行します。
bundle

アプリにdeviseをセットアップ

以下のコマンドを実行します。

rails generate devise:install

Deviceの環境設定

environmentファイルにデフォルトのurlオプションを追加します。

config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost:3000' }

endの前に追加します。

app/views/layouts/application.html.erb
<% if notice %>
  <p class="alert alert-success"><%= notice %></p>
<% end %>
<% if alert %>
  <p class="alert alert-danger"><%= alert %></p>
<% end %>
<%= yield %>

さらに上の行を追加します。

また、以下を削除します。

app/views/ideas/show.html.erb
<p id="notice"><%= notice %></p>

同じようにapp/views/comments/show.html.erbでも削除します。
なぜなら、app/views/layouts/application.html.erbに同じ行を追加したためです。

Userモデルのセットアップ

User modelを作るためにbundled generator script を使います。

rails generate devise User
rails db:migrate

サインアップとログインリンクの追加

ユーザーがログインできる適切なリンク、または案内をナビゲーションバー右上のコーナーに追加する。

app/views/layouts/application.html.erb
<p class="navbar-text pull-right">
  <% if user_signed_in? %>
    Logged in as <strong><%= current_user.email %></strong>.
    <%= link_to 'Edit profile', edit_user_registration_path, class: 'navbar-link' %> |
    <%= link_to "Logout", destroy_user_session_path, method: :delete, class: 'navbar-link'  %>
  <% else %>
    <%= link_to "Sign up", new_user_registration_path, class: 'navbar-link'  %> |
    <%= link_to "Login", new_user_session_path, class: 'navbar-link'  %>
  <% end %>
</p>
<ul class="nav navbar-nav">
  <li class="active"><a href="/ideas">Ideas</a></li>
</ul>

最後にログインしていない時に登録した内容を確認できないようにする。

app/controllers/application_controller.rb
  before_action :authenticate_user!

endの前に追加します。

Gravatarでプロフィール写真を追加

Gravtastic gemを追加

gem 'gravtastic'

これをdeviseの下に追加します。

Terminalで、次のコマンドを実行します。

bundle

Gravatarをセットアップ

最後の行の下に次を追加します。

app/models/user.rb
include Gravtastic
gravtastic

Gravatarを設定する。

app/views/layouts/application.html.erb
<% if user_signed_in? %>

のなかに,以下のようになるように書き換えます。

app/views/layouts/application.html.erb
<% else %>
<%= image_tag current_user.gravatar_url %>

さらにデザインしよう

headerのデザイン

app/assets/stylesheets/application.css
nav.navbar {
  min-height: 38px;
  background-color: #f55e55;
  background-image: none;
}

.navbar a.brand { font-size: 18px; }
.navbar a.brand:hover {
  color: #fff;
  background-color: transparent;
  text-decoration: none;
}

tableのデザイン

以下のように書き換えます。

app/views/ideas/index.html.erb
<table class="table">

以下のコードを使って、画像のサイズを調整します。

<%= image_tag(idea.picture_url, width: 600) if idea.picture.present? %>

app/assets/stylesheets/ideas.scssの最後に以下を追加します。

app/assets/stylesheets/ideas.scss
.container a:hover {
  color: #f55e55;
  text-decoration: none;
  background-color: rgba(255, 255, 255, 0);
}

footerにスタイルを追加

app/assets/stylesheets/application.css
footer {
  background-color: #ebebeb;
  padding: 30px 0;
}

buttonにスタイルを追加

app/assets/stylesheets/ideas.scs
.container input[type="submit"] {
   height: 30px;
   font-size: 13px;
   background-color: #f55e55;
   border: none;
   color: #fff;
 }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsでコントローラーの単体テストコード時に「302」のエラーが出た時の対処法

はじめに

前回の記事の続きってわけでも無いのですが、似たような内容にはなっています。
それと「302」のエラーの中のあくまでも1例ですのでよろしく御願い致します。

エラー内容

それではまずはコントローラーの単体テストコード実装時に下記のようなエラーが出ました。
ご覧ください

ターミナル
 % bundle exec rspec spec/requests/orders_spec.rb

OrdersController
  GET /index
    indexアクションにリクエストすると正常にレスポンスが返ってくる (FAILED - 1)
    indexアクションにリクエストするとレスポンスに出品済みの商品の説明文が存在する (FAILED - 2)


Failures:

  1) OrdersController GET /index indexアクションにリクエストすると正常にレスポンスが返ってくる
     Failure/Error: expect(response.status).to eq 200

       expected: 200
            got: 302

       (compared using ==)
     # ./spec/requests/orders_spec.rb:24:in `block (3 levels) in <top (required)>'

  2) OrdersController GET /index indexアクションにリクエストするとレスポンスに出品済みの商品の説明文が存在する
     Failure/Error: expect(response.body).to include @item.details
       expected "<html><body>You are being <a href=\"http://www.example.com/users/sign_in\">redirected</a>.</body></html>" to include "商品の説明"
     # ./spec/requests/orders_spec.rb:29:in `block (3 levels) in <top (required)>'

〜中略〜

エラー内容の検証

それではエラー内容を検証していきたいと思います。
今回のエラーの原因となる該当箇所は2つあります。
まず1つ目は下記の部分です。

ターミナル
  1)   Failure/Error: expect(response.status).to eq 200

       expected: 200
            got: 302

次に2つ目は下記の部分です。

ターミナル
  2)   Failure/Error: expect(response.body).to include @item.details

        expected "<html><body>You are being <a href=\"http://www.example.com/users/sign_in\">redirected</a>.</body></html>" to include "商品の説明"

エラー内容の検証

それではエラー内容の検証をしていきます。

  • まず1つ目のエラーはexpectedで「200」(成功)を期待していますが実際に返ってきたレスポンスは「302」(Found)となっています。 ここで「302」(Found)についての詳しい説明を載せておきます。

302 (Found) エラーとは

「The HyperText Transfer Protocol (HTTP) の 302 Found リダイレクトステータスレスポンスコードは、リクエストされたリソースが一時的に Location で示された URL へ移動したことを示します。ブラウザーはこのページにリダイレクトしますが、検索エンジンはリソースへのリンクを更新しません 。」

上記を簡単に説明しますと「本来遷移したいページではなく違うページにリダイレクトされましたよ」という内容です。

(引用させて頂いた参考サイト)
HTTP レスポンスステータスコードについての参考サイト


  • それでは次に2つ目のエラーですが自分が着目したポイントは下記の部分です。
<a href=\"http://www.example.com/users/sign_in\">redirected</a>.

まず「users/sign_in」とあるのでログイン画面が関わっていると推測出来ます。そしてその次に「redirected」とあるのでここでもリダイレクトされたということが推測出来ます。

以上の検証内容から推測すると「リダイレクトでログイン画面に飛ばされたのかな?」と仮定することが出来ます。

ではそのような処理をしている箇所はと言うと「authenticate_user!」という部分が該当すると分かりました。ここで少し「authenticate_user!」について説明します。

authenticate_user!とは

処理が実行された時にユーザーがログインしていなければ、そのユーザーをログイン画面に遷移させます。という処理をしてくれるdevise用のメソッドです。

以上の点から自分はテストコード実行時は該当の「authenticate_user!」の箇所をコメントアウトすることにしました。

orders_controller.rb
class OrdersController < ApplicationController
  before_action :authenticate_user! (この行をコメントアウトする)

再度単体テストコードを実行

ターミナル
% bundle exec rspec spec/requests/orders_spec.rb

OrdersController
  GET /index
    indexアクションにリクエストすると正常にレスポンスが返ってくる
    indexアクションにリクエストするとレスポンスに出品済みの商品の説明文が存在する
    indexアクションにリクエストするとレスポンスに出品済みの商品の画像が存在する
    indexアクションにリクエストするとレスポンスに出品済みの商品の販売価格が存在する
    indexアクションにリクエストするとレスポンスに購入内容の確認の文言が存在する

Finished in 5.79 seconds (files took 10.02 seconds to load)
5 examples, 0 failures

上記の通り無事にテストコードを実行することが出来ました。

おわりに

今回のエラー(302)はコントローラーのテストコード実行時に「authenticate_user!」でログイン画面にリダイレクトされていたために起きたエラーでした。結合テストコードだとログインする処理は簡単なのですがコントローラーの単体テストコードの際にログイン出来るのか少し調べた限りではよくわかりませんでしたので今回はコメントアウトをすることで対処しました。

当記事内で参考にさせて頂いたサイト

HTTP レスポンスステータスコードについての参考サイト

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

DockerでRailsアプリの開発環境構築【Docker, Rails, Puma, Nginx, MySQL】

こんにちは.
今回もRailsアプリの開発環境をDockerで構築する手順をまとめてみました.
前回はRails用とデータベース用の2つのコンテナを起動しましたが,今回は,WebサーバーとしてNginxのコンテナも起動してみました.

事前準備

環境

Ruby: 2.5.8
Rails: 5.2.4.4
MySQL: 5.7.31
Nginx: 1.19.2
Docker: 19.03.12
Docker Compose: 1.26.2

手順

1. ディレクトリ,ファイルの作成

全体の構成は以下の通りです.
それではこの構成図の通り,ディレクトリとファイルを作成していきます.

全体の構成
/test-app
├── Dockerfile
├── Dockerfile.nginx
├── docker-compose.yml
├── nginx.conf
├── Gemfile
└── Gemfile.lock

まずは, プロジェクトのルートディレクトリを作成します.

terminal
$ mkdir test-app

そして, ルートディレクトリの直下に

  • Dockerfile
  • Dockerfile.nginx
  • docker-compose.yml
  • nginx.conf
  • Gemfile
  • Gemfile.lock

これらを作成します.

terminal
$ cd test-app
$ touch Dockerfile Dockerfile.nginx docker-compose.yml nginx.conf Gemfile Gemfile.lock 

2. ファイルの編集

上記で作成したそれぞれのファイルの中身は以下のようになります.
(Gemfile.lockは空のままにします.)

Dockerfile
FROM ruby:2.5
RUN apt-get update && apt-get install -y \
    build-essential \
    node.js
WORKDIR /test-app
COPY . /test-app
RUN bundle install
Dockerfile.nginx
FROM nginx
RUN rm -f /etc/nginx/conf.d/*
COPY nginx.conf /etc/nginx/conf.d/test-app.conf
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
containers/nginx/nginx.conf
# プロキシ先の指定
# Nginxが受け取ったリクエストをバックエンドのpumaに送信
upstream test-app {
  # ソケット通信したいのでpuma.sockを指定
  server unix:///test-app/tmp/sockets/puma.sock;
}

server {
  listen 80;
  # ドメインもしくはIPを指定
  server_name _;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  # ドキュメントルートの指定
  root /test-app/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @test-app;
  keepalive_timeout 5;

  # リバースプロキシ関連の設定
  location @test-app {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://test-app;
  }

  location /favicon {
    empty_gif;
    access_log    off;
    log_not_found off;
  }
}
Gemfile
source 'https://rubygems.org'
gem 'rails', '~>5.2'
docker-compose.yml
version: '3'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    command: bundle exec puma -C config/puma.rb
    volumes:
      - .:/test-app
    tty: true
    stdin_open: true
    depends_on:
      - db

  db:
    image: mysql:5.7
    environment:
      - 'MYSQL_ROOT_PASSWORD=password'
    volumes:
      - 'db-data:/var/lib/mysql'

  web:
    build:
      context: .
      dockerfile: Dockerfile.nginx
    volumes:
      - ./public:/test-app/public
      - ./tmp:/test-app/tmp
    ports:
      - 80:80
    depends_on:
      - app

volumes:
  db-data:

3. Appのコンテナ内にRailsのセットアップを行う

terminal
$ docker-compose run --rm app rails new . --force --database=mysql --skip-bundle

4. tmp/socketsフォルダ作成,作成されたファイルを編集

まず,tmpフォルダ内にsocketsフォルダを作成します.

そして,Railsのセットアップにより作成されたファイルのうち以下の3つを編集します.
- config/database.yml
- config/puma.rb
- config/environments/production.rb

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password   #docker-compose.ymlのMYSQL_ROOT_PASSWORDの値を設定する
  host: db   #docker-compose.ymlのservice名と合わせる

development:
  <<: *default
  database: test-app_development
config/puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count

port        ENV.fetch("PORT") { 3000 }

environment ENV.fetch("RAILS_ENV") { "development" }

pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }

plugin :tmp_restart

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true
config/environments/production.rb
# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = true   #デフォルトではfalseなので,trueにかえる

このconfig/environments/production.rbの変更はしなくても開発環境の構築については問題はないが,あとあと本番環境にデプロイした際にアセットプリコンパイルのエラーが出たので,ここで変更しています.

5. コンテナの起動, DBの作成

terminal
$ docker-compose up -d --build
$ docker-compose exec app rails db:create

これで http://localhost にアクセスすると, Railsのホーム画面が表示されるはずです.

参考

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

【Rails】Gem gretelを用いたパンくずリスト作成

そもそもパンくずリストとは

パンくずリストとは下記の写真のようにページを階層順に配列し、ユーザーが操作時に現在どの位置にいるかを可視化したリストです。このようなリストを位置型パンくずリストと呼びます。

スクリーンショット 2020-10-12 17.09.55.png

他にも属性型パンくずリストといいページを階層的に示したものでなく、そのページがどの種類のカテゴリに属しているかを示すリストもあります。属性型パンくずリストの例としてはECサイトが挙げられます。

パンくずリストを導入することによって主に下記の3点のメリットがあります。
・操作性の向上
→ユーザーがサイト内での位置情報を常に確認できる。

滞在時間の向上
→パンくずリストにより階層毎の移動が容易になり回遊性が高まる。

SEO効果
→パンくずリストによりクローラーの巡回をサポートする。

gretelのインストール

【ドキュメント】
https://www.rubydoc.info/gems/gretel

【GitHub】
https://github.com/lassebunk/gretel

Gemfileにて

gem 'gretel'

記載後は下記を実行

$ bundle install

これでインストールは完了です。

設定

下記のコマンドでパンくずリストを設定するためのファイルを作成します。

$ rails generate gretel:install

するとこのようなファイルが作成されます。

config/breadcrumbs.rb
crumb :root do
  link "Home", root_path
end

# crumb :projects do
#   link "Projects", projects_path
# end

# crumb :project do |project|
#   link project.name, project_path(project)
#   parent :projects
# end

# crumb :project_issues do |project|
#   link "Issues", project_issues_path(project)
#   parent :project, project
# end

# crumb :issue do |issue|
#   link issue.title, issue_path(issue)
#   parent :project_issues, issue.project
# end

# If you want to split your breadcrumbs configuration over multiple files, you
# can create a folder named `config/breadcrumbs` and put your configuration
# files there. All *.rb files (e.g. `frontend.rb` or `products.rb`) in that
# folder are loaded and reloaded automatically when you change them, just like
# this file (`config/breadcrumbs.rb`).

今回ブログアプリの管理画面にパンくずリストを実装するので下記のように記載します。

config/breadcrumbs.rb
#管理画面
crumb :root do
  link "Home", admin_dashboard_path
end

#記事(一覧)
crumb :admin_articles do
  link "記事", admin_articles_path
end

コードの説明

config/breadcrumbs.rb
crumb :(設定ファイル) do
  link "(パンくずリストに表示される名前)", (呼び出し元のパス)
end

ビューの設定

ここでbreadcrumbs.rbに記載したcrumb :admin_articles doが繋がります。

admin/articles/index.html.slim
- breadcrumb :admin_articles

下記を記載した箇所にパンくずリストが表示されます。
layouts/admin.html.slim
== breadcrumbs

親の設定

最後に下記のようにさらに階層を加えた表示方法を記載します。

スクリーンショット 2020-10-12 14.48.17.png

config/breadcrumb.rb
crumb :root do
  link "Home", admin_dashboard_path
end

crumb :admin_articles do
  link "記事", admin_articles_path
end

crumb :edit_admin_article do
  link "記事編集", admin_articles_path
  parent :admin_articles
end

先ほど「記事」のパンくず追加に加えて今回は「記事編集」というパンくずを追加しました。
前回と異なる点は記事と記事編集を紐付けるために記事編集の欄にparent :admin_articlesを記載しております。

最後にadmin/articles/edit.html.slimでもビューの設定を終えればパンくずの実装が可能になります。

参照したページの一覧

【ドキュメント】
https://www.rubydoc.info/gems/gretel

【Flexible Ruby on Rails breadcrumbs plugin.(GitHub)】
https://github.com/lassebunk/gretel

【gretelでパンくずリストを作成】
https://doruby.jp/users/kisuzuki/entries/gretel%E3%81%A7%E3%83%91%E3%83%B3%E3%81%8F%E3%81%9A%E3%83%AA%E3%82%B9%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90

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

【Rails】 あいまい検索機能の付け方

経緯

現在絶賛作成中のポートフォリオで、フリマアプリのラクマの仕上げに検索機能をの実装したいと思い作成。さほど難しくはなかったけれども、色々記載しておこうと思います。

環境

ruby 2.6.5
Rails 6.0.3.2
haml使用

イメージ図

ファイル名

ファイル名

手順

1、ルーティングの設定
2、検索フォームを作成
3、モデルに定義
4、コントローラーに設定
5、ビューの作成

の5つの手順になります。
簡単なので、サックと出来ると思います。

ルーティングの設定

今回は、7つの基本アクション以外のアクションを定義します。
7つの基本アクションとは、index new create 等のアクションの事ですね。
先ずは、コードから

routes.rb
resources :items do
  collection do
    get 'search'
  end
end

説明ですが、
今回は先程言った通り7つの基本アクション以外で定義します。要は新しいアクションを作るみたいなイメージです。
その為のコードが、collection ~ end の所です。
get の横のsearch が新しいアクション名です。

そして、collection ですが、ここはURLの指定先にidの必要の可否で変わります。
idの可とは、showアクションのような個々に登録されている商品の詳細ページなどに見にいく時にふられるものです(まぁ、ふられると言うと言葉のニュアンス的に違うのですが・・・)
なので、idなしで有ればcollection
idありで有ればmember となります。

今回は、特定のページにいく必要がないため、collectionを使用してルーティングを設定してます。



検索フォームを作成

先ずは、コードから

index.html.haml
= form_with(url: search_items_path, local: true, method: :get, class: "search-form") do |f|
  = f.text_field :keyword, placeholder: "検索する", class: "search-input"
  %label{for: "search-icon", class: "search-label"}
    %i.fas.fa-search
  .search-icon
    = f.submit "検索", class: "search-btn", id: "search-icon"

必要そうな所だけ説明します。

先ずは、urlの設定部分ですが、先程設定したsearchのルーティングで設定されるplefixを書きます。plefixをパスとして書く時は文字の最後に_pathをつけるような書き方をします。
調べ方は、ご存知だと思いますが、
ターミナルで rails routes です。

次に、まぁこれは無理に行う必要はないですが、上記の画像のように、ムシメガネ?みたいなアイコンとsubmit(送信ボタン)の紐付けについてです。
つまり、ムシメガネ?みたいなアイコンを押したら送信ボタンが押されるように設定していると言う事です。

やり方は、label:for と id で紐付けます。
上記のコードで言うと、
これと %label{for: "search-icon", class: "search-label"}
これで = f.submit "検索", class: "search-btn", id: "search-icon" です。
search-icon って言う同じ名前がありますよね。これで紐付けされています。

そして、今回は検索フォームにf.text_field :keywordと記述したので、このフォームに入力した値はコントローラーでparams[:keyword]と書くことで取得する事ができます。



モデルに定義

ここも、コードから

item.rb
def self.search(search)
  return Item.all unless search
  Item.where('name LIKE(?)', "%#{search}%")
end

言い忘れましたが、今回はitemって言う名前でモデル等を作ってます。

説明します。

検索したキーワードが含まれている投稿を取得するために、whereメソッドLIKE句を利用します。
whereメソッドは、モデル名.where('検索対象となるカラムを含む条件式')とする事でテーブル内の「条件に一致したレコードのインスタンス」を配列の形で取得できます。

LIKE句は、曖昧(あいまい)な文字列の検索をするときに使用するもので、whereメソッドと一緒に使います。
詳細は、省きますが今回は、searchが含まれるnameカラムのデータを検索するものです。
詳しく知りたい方は、下記のリンク先が分かりやすいと思います。
【SQL】LIKE句の基本的な使い方~複数検索する場合の方法まで解説

そもそも、searchって何?っと思われた方の向けに説明します。
要は引数の話です。
引数のsearch(1行目の(search))は、検索フォームから送信されたパラメーターが入ります。イメージしやすく言うと、検索フォームに みかん と入力して送信ボタンを押すとこの引数に みかん が入ると言う事ですね。
後は、文法の話です。
この引数にみかんというデータが入れば定義の中のsearchもみかんになるって訳です。つまり、このまま、みかんネタで言えば、itemテーブルのnameカラムにみかんと言う文字が入っているものを検索するって言う事です。
なので、言ってしまえば、1行目の(search)はどんな単語でも良いって事ですね。

次に、return Item.all unless search ですが、これは読んで字のごとくですが、
search(さっきので言えばみかん)と言う文字がなかったら他全てを出力すると言う意味です。

そして、最後に1行目のself.searchの説明です。
現時点だと、.searchというメソッドは使うことができません(この後コントローラーで定義するのに使います)
が、モデルで設定することにより、コントローラーで使用することができるようになります。そして、モデルの中でメソッドを定義する際には、メソッド名の頭にself.を付けると事で、コントローラーで使えるクラスメソッドになります。

以上で、モデルに定義は終了です。



コントローラーに設定

はたまた、ここもコードから

item_controller.rb
def search
  @items = Item.search(params[:keyword])
end

@items = Item.search(params[:keyword])のコードは、
上記で述べてきた、今迄の説明で事足りるかと思うので省略します。



ビューの作成

検索結果画面のビューの作成ですが、
ここは、search.html.hamlと言うファイルを作成して、
eachメソッドを使って出力すればオッケーかと思います。

search.html.haml
.contents__box
  - @items.each do |item|
  ここより下は今自分が作っているものに照らし合わせて作成お願いします。

これで、全ての実装が完了です!
実装出来なかったなどの不備が有ればご連絡ください!

ありがとうございました。

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

【超初心者向け】autofocus: trueの使用法

autofocus属性

「autofocus: true」とはページを読み込んだらすぐにautofocus属性を記述している部分にカーソルが移動して入力状態になる、というものです。

具体例

一覧ページからマイページへ移動し名前を変更しようとする場合。
 
一覧ページからマイページボタンをクリックしてマイページに行くと自ら名前のフォーム部分を選択しなくてもカーソルが移動した状態でマイページが表示されます。そのためすぐに編集をすることができます!
カーソルが移動して入力状態になる


⚠️autofocus:trueは1つのhtmlファイルに1つまでということを押さえておきましょう。

記述の仕方

ここでは上記画像のコードをそのままご紹介します。
全体の中で「autofocus: ture」がどのように使用されているのかを把握しましょう。
環境:rails 6.0.0、VScode 1.46、macOS Catalina 10.15

app/views/users/show.html.erb
<div class="account-page">
  <div class="account-page-title">
    <h1>マイページ</h1>
  </div>
  <div class="account-info">
    <h2>ユーザー情報</h2>
  </div>
  <div class="account-page-form">
    <%= form_for(current_user) do |f| %>
      <div class="form-field">
        <div class="form-field-name">
          <%= f.label :name %>
        </div>
        <div class="form-field-name">
          <%= f.text_field :name, autofocus: true %>
        </div>
      </div> 
      <div class="form-field">   
        <div class="form-field-email">
          <%= f.label :email %>
        </div>
        <div class="form-field-email">
          <%= f.email_field :email %>
        </div>
        <br>
      </div>
      <%= f.submit "更新する" %>
      <br>
      <%= link_to "ログアウト", destroy_user_session_path, method: :delete%>
    <% end %>
  </div>
</div>

おわりに

参考サイトURL→Uhyohyoweb
「autofocus: trueの使用法」に関する記事はいかがでしたか?
autofocus自体はとっても簡単ですが知らないと「何それ?」ってなりますよね。
簡単なことすぎてピックアップされないので今回記事にしてみました!
少しでも理解のお役に立てたら幸いです。
また、いたらない点がございましたらご指摘ください。(初投稿かつ初学者なので皆さんのご指摘がとっても学びにつながります!)
皆さん、最後までご覧いただきありがとうございます。それではまた別の記事でお会いしましょう〜〜!

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

RailsでCSVインポートする時、nkfコマンドを使ったらめっちゃ楽だった

RailsでCSVファイルをインポートするとき、こんな感じに実装してた。

def read_csv
  begin
    rows = CSV.read(file, encoding: 'UTF-8')
  rescue CSV::MalformedCSVError
    begin
      # csvファイルがUTF-8でない場合はShift_JISで読む
      rows = CSV.read(file, encoding: 'Shift_JIS')
    rescue CSV::MalformedCSVError, Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError
        # csvファイルがUTF-8・Shift_JISでもない場合
        @error = 'ファイルのエンコードはUTF-8かShift_JISではありません。'
    end
  end
  rows&.shift # ヘッダーを削除する
  rows
end

問題点

例外処理が多くて面倒だし、そもそもインポートできる文字コード2つしかないとか…

解決策

どうやらnkfとかいうモジュールを使うとなんでも変換できそうだ…
https://docs.ruby-lang.org/ja/latest/class/NKF.html

  • nkfをPCにインストール
brew install nkf
nkf -w --overwrite 変換したいファイルのpath

このコマンドを使えばいい感じに変換できそう。

  • Railsの中にこのコマンドを組み込んでみる。
def read_csv(file)
  system("nkf -w --overwrite #{file.path}")
  rows = CSV.read(file)
  rows&.shift # 1行目のヘッダーを削除する
  rows
end

すっきりとしたコードになりました。

注意点

あらかじめbrew install nkfコマンド等でnkfをインストールしておかないと、nkfコマンドが使えないので気をつけてください。

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