20200807のRubyに関する記事は11件です。

any?メソッドを使用してエラーメッセージの取得

エラーメッセージの表示の際に使用したメソッドをまとめたいと思います。

any?メソッドとは

any?メソッドはすべての要素が偽である場合に false を返します。真である要素があれば、ただちに true を返します。

書き方はこのような感じ

p [false, nil].any? # => false

例えばエラーメッセージの記述を部分テンプレートに記載してオブジェクトにエラー情報がある場合のみ表示するように設定するとします。

app/controllers/items_controller.rb
def create
    @item = Item.new(item_params)
    if @item.save
      redirect_to root_path
    else
      render :new
    end
  end

この記述でバリデーション等で保存に失敗した際に
newアクションへ戻るように設定します。

app/views/items/new.html.erb
  <%= form_with model: @item, local: true do |f| %>
      <%= render 'shared/error_messages', model: @item %> 

レンダー先にエラーの情報を持ったモデルオブジェクトを持っていきます。

app/views/shared/_error_messages.html.erb
<% if model.errors.any? %>
<div class="error-alert">
  <ul>
    <% model.errors.full_messages.each do |message| %>
    <li class='error-message'><%= message %></li>
    <% end %>
  </ul>
</div>
<% end %>

any?メソッドでerrorsの中身を確認して存在する場合はtrueとなりエラーの繰り返し処理が働きます。

またpresent?メソッドとかなり似ていますがany?メソッドは

上記例では繰り返し処理でエラーメッセージ を表示していますが

labelの場所ごとにエラーメッセージを表示したい場合はinclude?メソッドを使ってもいいなと思います。

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

継続渡しスタイル(CPS)チートシート

例によって,少なくとも約1名(自分自身)には役立ちそうだったので,各言語の継続渡しスタイル(Continuation-passing style,CPS)のサンプル記述を記事としてまとめた.

Python(Python 3):解説付き

対話モードで,xに10を代入してから,x + 20を評価させてみる.

>>> x = 10
>>> x + 20
30

このプログラム記述をファイルにしてPythonインタプリタに実行させても,何も表示されない.対話モード(REPL)では,式として評価されたものはその結果を表示するのに対し,ファイル実行(スクリプト)では,そのような表示は行わないためである.

C:\Users\hoge>type sample.py
x = 10
x + 20
C:\Users\hoge>python sample.py

C:\Users\hoge>

もし,ファイル実行で評価結果を表示させたい場合は,評価結果を得た後に,たとえばprintを用いて表示させるようにする必要がある.

C:\Users\hoge>type sample.py
x = 10
print(x + 20)

C:\Users\hoge>python sample.py
30

C:\Users\hoge>

ここで,式として記述する場合は,評価結果を処理する関数を必ず指定しなければならない仕組みを考える.次は,x + 20を評価したら,その評価結果を関数fに引数として渡して呼び出す関数cadd20を定義し,fprintを指定して実行した例である.

C:\Users\hoge>type sample.py
def cadd20(x, f): f(x + 20)
x = 10
cadd20(x, print)

C:\Users\hoge>python sample.py
30

C:\Users\hoge\python>

このように,結果を処理する手続きをあらかじめ指定する仕組みを想定したプログラミング手法を『継続渡しスタイル』(continuation-passing style,CPS)と呼ぶ.このスタイルは,そうとは意識されない形を含め,様々な実装に応用されている.

  • コールバック関数(指定する手続き)とハンドラー(呼び出す関数)
  • 例外処理のtry(呼び出す手続き)とexcept(指定する手続き)
  • CPSに自動変換して明確となった値受け渡しからの中間言語記述生成

なお,関数のCPSへの変換は容易であるが,変換した関数を使用するには工夫が必要である.ここでは,既存関数のCPSへの変換と利用例を示す(このチートシートの本体).

from operator import add, sub, mul

# func(x, y) = (x + y) * (x - y)
def func(x, y): return mul(add(x, y), sub(x, y))

print(func(10, 20))    # => -300

def cps(f):
    def cps(x, y, c): return c(f(x, y))
    return cps

cps(add)(10, 20, print)    # => 30

def cfunc(x, y, c):
    return cps(add)(x,  y,  lambda c1:    # x + y -> c1
           cps(sub)(x,  y,  lambda c2:    # x - y -> c2
           cps(mul)(c1, c2, c)))          # c1 * c2 -> c

cfunc(10, 20, print)    # => -300

Scheme(Gauche,Guile)

; func(x, y) = (x + y) * (x - y)
(define (func x y) (* (+ x y) (- x y)))
(display (func 10 20))    ; => -300

(define (cps f) (lambda (x y c) (c (f x y))))
((cps +) 10 20 display)    ; => 30

(define (cfunc x y c)
  ((cps +) x y (lambda (c1)    ; x + y -> c1
  ((cps -) x y (lambda (c2)    ; x - y -> c2
  ((cps *) c1 c2 c))))))       ; c1 * c2 -> c
(cfunc 10 20 display)    ; => -300

Ruby(CRuby)

# func(x, y) = (x + y) * (x - y)
def func1(x,y) (x+y)*(x-y) end
p func1(10,20)    # => -300

add = -> x,y { x+y }
sub = -> x,y { x-y }
mul = -> x,y { x*y }
prn = -> x { p x }

cps = -> f { -> x,y,c { c.(f.(x,y)) } }
cps[add][10,20,prn]    # => 30

cfunc = -> x,y,c {
  cps.(add).(x, y, -> c1 {     # x + y -> c1
  cps.(sub).(x, y, -> c2 {     # x - y -> c2
  cps.(mul).(c1,c2,c) }) })    # c1 * c2 -> c
}
cfunc[10,20,prn]    # => -300

JavaScript(Node.js)

// func(x, y) = (x + y) * (x - y)
function func1(x,y) { return (x+y)*(x-y) }
console.log(func1(10,20))    // => -300

add = (x,y) => x+y
sub = (x,y) => x-y
mul = (x,y) => x*y

cps = f => (x,y,c) => c(f(x,y))
cps(add)(10,20,console.log)    // => 30

cfunc = function(x,y,c) {
    return cps(add)(x, y, c1 =>    // x + y -> c1
           cps(sub)(x, y, c2 =>    // x - y -> c2
           cps(mul)(c1,c2,c)))    // x * y -> c
}
cfunc(10,20,console.log)    // => -300

備考

記事に関する補足

  • そのうち『Iコンビネータ』に関する説明を追加するかもしれない(謎).

参考文献

変更履歴

  • 2020-08-09:Schemeのcall/cc利用例削除(Twitterコメントより)
  • 2020-08-07:参考文献欄を追加
  • 2020-08-07:JavaScriptの例を追加
  • 2020-08-07:初版公開(Python,Scheme,Ruby)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】form_withを完全に理解した

この記事の内容

  • form_withメソッドの使い分け
  • それに伴うストロングパラメータの取り扱い
  • フォーム内容をHTTP通信させる方法

環境

$ rails -v
Rails 6.0.3.1
$ ruby -v
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]

form_withとは

フォーム送信するためのUI部品をビルドするメソッドで、自動でサーバー側のコントローラアクションを切り替えてくれる特徴を持つ。

切り替える要因となるのは、オプションのmodelやurlに対する値によって変わり、以下のようになる。

オプション @userの中身 呼び出されるアクション 用途
model: @user User.new create ユーザー作成
model: @user User.find() update ユーザー編集
url: sessions_path create ユーザーログイン

また、オプションの違いを比較すると、以下のようになる。

オプション 入力エリアのname属性 ストロングパラメータ
model: @user name="user[email]" params.require(:user).permit(:email)
url: users_path name="email" params.permit(:email)

modelオプションを使った例

<%= form_with model: @user do |f| %>
  <%= f.label :name, "名前" %>
  <%= f.text_field :name, placeholder: "山田" %>

  <%= f.submit "登録する" %>
<% end %>

urlオプションを使った例

<%= form_with url: sessions_path do |f| %>
  <%= f.label :name, "名前" %>
  <%= f.text_field :name, placeholder: "山田" %>

  <%= f.submit "ログインする" %>
<% end %>

localオプション

実は、デフォルトでremote: trueオプションが付与されており、いわゆるAjax通信をするようにあらかじめ設定されている。
そのため、特に何も設定しないと、renderメソッドが行われないことによるflashが表示されなかったりする。

通常のHTTP通信を行うために、以下のような設定を行うことで、上記の問題を解決することができる。

<%= form_with model: @user, local: true do |form| %>

まとめ

form_withは、自動でHTTPリクエストの種類を判別してくれるので、便利。

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

RailsでWebアプリ開発の流れをざっくり。

完璧自分用です。

rvmでrubyのバージョン変更

terminal.
$ rvm install 2.5.1
$ rvm use 2.5.1

バージョン指定でRailsでアプリ作成

terminal.
$ gem install rails -v 5.2.1 -N #6系なら6.0.2.1とか適宜。
$ rails _5.2.1_ new < app name >

必要なgemをインストールしておく

Gemfile.
gem 'bootstrap', '~> 4.1.1'
gem 'jquery-rails', '~> 4.3.1'

gem 'bycript' #最初から用意されているので、コメントアウトを外す
terminal.
$ bundle install 

サーバー立てて、ブラウザで確認

terminal.
$ rails s -p 任意のポート番号 #-p以下はなくてもok。特に指定しないと3000になる

localhost:3000(もしくは指定したポート番号)にブラウザでアクセスして、以下のページが表示されればok
Screen Shot 2020-08-07 at 16.20.10.png

モデル作成

ログインシステムやユーザー認証機能用

terminal.
$ rails generate model User name:string email:string password_digest:string

ブログなどのコンテンツ用

terminal.
$ rails generate model Articles title:string description:text 

後から足したい(user_idをArticlesモデルに足す場合を例に)

できたmigrateファイルに以下のように追記

class AddUserIdToArticles < ActiveRecord::Migration[5.1]
  def change
    add_column :articles, :user_id, :int
  end
end

最後に

terminal.
$ rails db:migrate

コントローラとビュー作成

terminal.
$rails g controller Users index(ユーザー一覧作るなら) new edit

$rails g controller Home index

$rails g controller Sessions new #login用

ビューに書きがちなrubyコードまとめ

hoge.html.erb
#link_to
<%= link_to “トップページ”, root_path %>
<%= link_to “Yahooへ移動する”, “http://www.yahoo.co.jp/” %>
<%= link_to “削除”, member_path(params[:id]), method: :delete %>


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

DockerでRailsチュートリアルのローカル開発環境構築 - WebpackでBootstrapとFont Awesomeを導入 -

はじめに

Dockerでローカル開発環境構築を行い、Railsチュートリアルを再走しております

  • Railsチュートリアル最新版(2020.8.6現在)に対応のRails 6
  • Dockerを使用し、開発環境の再現が可能
  • なるべくローカル環境にインストールしない

今回はRailsチュートリアルの5章に相当する部分です

3章, 4章の内容は特に問題になりませんが、

5章 5.1.2 BootstrapとカスタムCSS の部分でいよいよWebpackの沼に突入して行きます

具体的にはgemを利用せず, YarnとWebpackでBootstrapやFont Awesomeを導入、管理します

加えてテストを書くことが増えるのでその辺りをRSpecで置き換えて進めていきます

5章終了時のブランチはfilling-in-layoutです
https://github.com/dev-naokit/sample_app_on_docker/tree/filling-in-layout

第一回
DockerでRailsチュートリアルのローカル開発環境構築(Rails 6 + PostgreSQL + Webpack) - Qiita

第二回
DockerでRailsチュートリアルのローカル開発環境構築 - RSpec導入 & CircleCIでHerokuデプロイ- - Qiita

個人開発アプリ
mdClip <オンラインmarkdownエディタ>

Dockerのコンテナ上で操作する場合はターミナルのコマンドを適宜

$ docker-compose run app ...

もしくは

$ docker-compose exec app ...

で置き換えてください。

BootstrapとFontawesomeの導入

Rails 6ではJavaScriptのモジュールバンドラとしてWebpackが導入されていますが、
画像やCSSに関しては従来のSprocketがアセットパイプラインとして使用され、
JavaScriptのみをWebpackでコンパイルするようになっています

application.html.erb

<head>の内容を書き換えます

app/views/layouts/application.html.erb

<head>
  <title><%= full_title(yield(:title)) %></title>
  <%= csrf_meta_tags %>
  <%= csp_meta_tag %>
  <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  # (下の行を追記)webpackがCSSを扱えるようにする
  <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  # JavaScriptはデフォルトでwebpacerが pack フォルダをコンパイルして出力している
  <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  <!--[if lt IE 9]>
      <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js">
      </script>
    <![endif]-->
</head>

YarnでBootstrapをインストール

jquerypopperはBootstrapに必要なパッケージ
Font Awesomeもあとで必要になるのでインストールします
('@fortawesome'は誤字ではありません)

yarn add bootstrap jquery popper.js @fortawesome/fontawesome-free

インストールされたモジュールは
package.jsonで確認できます

application.js

app/javascript/packs/application.js

以下を追記します

require("bootstrap");
require("@fortawesome/fontawesome-free");

require('jquery')も併記するような情報もありましたが、
Bootstrapが自動的にrequire('jquery')してくれるようでココには記述不要です

私も色々不具合を検証している過程で
require('jquery')がなくてもjqueryがロードされることに気づいたのですが
jqueryの多重起動はJavaScriptの動作不具合につながることもあるようで、
現状はこのまま進めます

(下に参考記事を貼っておきます)

environment.js

jQueryをどこからでも呼び出せるようにする

config/webpack/environment.js

const { environment } = require('@rails/webpacker')
var webpack = require('webpack');

environment.plugins.append(
    'Provide',
    new webpack.ProvidePlugin({
        $: 'jquery/src/jquery',
        jQuery: 'jquery/src/jquery',
        Popper: ['popper.js', 'default']
    })
)

module.exports = environment

こうすることで毎回import $ from 'jquery';と書かなくて良くなるとのことです

CSS

app/javascript/stylesheets/application.scss(新規作成)

以下追記

@import '@fortawesome/fontawesome-free/scss/fontawesome';
@import 'bootstrap/scss/bootstrap';

application.js

app/javascript/packs/application.js

以下を追記します

import '@fortawesome/fontawesome-free/js/all';
import "../stylesheets/application.scss";

参考

Rails 6: Webpacker+Yarn+Sprocketsを十分理解してJavaScriptを書く: 前編(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社

Rails 6+Webpacker開発環境をJS強者ががっつりセットアップしてみた(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社

CSSの扱いについて

JavaScriptだけでなく画像やCSS、全てをWebpackでコンパイルすることも可能なようです

現状はSprocketによるアセットコンパイルと、Webpackerが共存している状況に変わりはなく

app/javascript/stylesheets/...をコンパイルした内容が<head>内の

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>

で出力され

app/assets/stylesheets/...をコンパイルした内容が同じく<head>内の

  <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>

に出力されている状況です

つまりチュートリアル通りassets内のCSS, SCSSを編集しても、
packs内のCSS, SCSSを編集してもビューに反映される状況が出来上がっています

BootstrapやFont Awesomeのstyleは後者によってimportされ、コンパイル、ビューに反映されています

今後チュートリアルをすすめる上でどちらのStylesheetを編集しても問題ないと思いますが
<head>内での呼び出しの順序くらいは気に留めて置いたほうがいいかもしれません

ちなみに、packs内でCSSを編集する場合、webpack-dev-serverのHot reloadの恩恵を受けられます
具体的には、変更保存した場合に自動的にブラウザがリロードされ、即座に(やや遅延あり...)変更内容を確認できます

Railsチュートリアルの流れでcustom.scssを作成する場合
app/javascript/packs/application.jsに以下を記述すれば大丈夫です

import "../stylesheets/custom.scss";

(私はこの仕様で進めてみます)

Troubleshoot

Bootstrapのスタイルがおかしい

おそらくヘッダー周りが上手く表示されないと思います
Bootstrapのバージョンの違いによるものでこちらの記事の通りインストールすると
Bootstrapの最新版ver. 4.5(2020.8.7現在)が導入されます
(Railsチュートリアルでは3.4.1)

navbar-inverseというタグがBootstrap 4では使用できなくなっていますので

  <header class="navbar navbar-expand-md bg-dark navbar-dark bg-dark">

で置き換えるなど細かな修正が必要です
修正を加えたこの章終了時のBranchを公開する予定ですが

yarn addの際にバージョンを指定するといった対策も可能です

テストRSpec関連

assert_...をどう書き換えるか?

そのまま使えます

「RSpecでも assert xxx って書いてテストしたい」=> すぐできます! - Qiita

ApplicationHelperをテストでも使えるように...

include ApplicationHelperを個別の_spec.rbファイルに記述

もしくは、spec/rails_helper.rbに記述
_spec.rbでrequireされているので、個々に記述する必要はないはずです

おわりに

この部分はRails 6環境で個人アプリ開発にあたり、最も苦労しました

身の丈に合わない内容で誤りを含むかもしれませんが

Railsチュートリアルの環境構築だけでなく、Rails 6環境でアプリ開発を行う方の一助になれば幸いです

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

編集、削除の権限を投稿者だけにしたい

バージョン

・ruby 2.5.7
・Rails 5.2.4.3

編集、削除の権限を投稿者だけにしたい

CRUD処理は出来た!
けど、このままだと全ての投稿を編集や削除が出来てしまう状態。
編集、削除の権限を投稿者だけの機能にしたい。
スクリーンショット 2020-08-07 11 24 11

ユーザーの投稿を守る為に下記のメソッドを使う

unlessはもし〜でなかったらと言う意味。下記でいうと、
もし受け取ったユーザーのIDが、ログインしているユーザー(current_user)のIDと一致しなければ、処理を実行せずリダイレクトで戻しますよっていう意味です。
スクリーンショット 2020-08-07 11 33 46

before_actionでメソッドを呼び出して、完成!!!

before_actionはコントローラーの全てのアクションが実行される前に実行されるものです。
今回は編集と削除のみの場合で行いたいので、editとupdateとdestroyのみにしています。
スクリーンショット 2020-08-07 11 33 58

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

Ruby 例外処理について

例外処理とは

例外処理を行うことで、エラー原因やエラー箇所の特定や、発生したエラーに対する適切な処理を行うことができます。

本記事では、以下について紹介していきます。

  • rescue
  • ensure
  • 例外処理の戻り値
  • return
  • rescue修飾子
  • 特殊変数 $! $@
  • メソッド全体に例外処理を設ける(begin/endの省略)
  • raise
  • カスタム例外を作成

rescue

以下のようにbeginの下に、通常実行したいプログラムを記述します。
そのプログラムの実行中に例外が発生した場合に、呼び出したいコードをrescueの下に記述します。

begin
  p "実行するコード"
rescue
  p "例外が発生した場合に実行されるコード"
end

結果

"実行するコード"

else

rescueに、elseを付け加えることによって
例外が発生しなかった場合に呼び出したいコードを追加できます。

begin
  p "実行するコード"
rescue
  p "例外が発生した場合に実行されるコード"
else
  p "例外が発生しなかった場合に実行されるコード"
end

結果

"実行するコード"
"例外が発生しなかった場合に実行されるコード"

rescueの使用例

例外が発生したら、「エラーが発生しました!」というメッセージを表示するブログラムを作成します。

文字列を""(ダブルクォーテーション)で囲っていないので例外となり、rescueの下にあるコードが実行されます。

begin
  p 文字列
rescue
  p "例外が発生しました"
end

結果

"例外が発生しました"

ensure

例外処理にensureを使用することで、例外の有無に関わらず実行される処理を記述することができます。

begin
  p "実行するコード"
rescue
  p "例外が発生した場合に実行されるコード"
else
  p "例外が発生しなかった場合に実行されるコード"
ensure
  p "例外の有無にかかわらず 最後に実行されるコード"
end

結果

"実行するコード"
"例外が発生しなかった場合に実行されるコード"
"例外の有無にかかわらず 最後に実行されるコード"

使用用途としては、何らかの処理を行っている時にエラーが発生した場合でも、確実に実行したい処理がある場合に使います。
(例)ファイルを開いて作業するプログラムの場合、「実行中の例外発生に関わらず、最後に必ずファイルを閉じておく」といったことができます。

例外処理の戻り値

  • 例外発生せず正常に処理が終了した場合、beginの最後の式が戻り値となります。
  • 例外が発生した場合、rescueの最後の式が戻り値となります。

(例) 処理が正常に終了した場合

class Return
  def test(n)
    begin
      1 / n
      "Begin"
    ensure
      "Ensure"
    end
  end
end

obj = Return.new
p obj.test(1)
"Begin"

(例) 例外処理が発生した場合

class Return
  def test(n)
    begin
      1 / n
      "Begin"
    rescue
      'Rescue'
    ensure
      "Ensure"
    end
  end
end

obj = Return.new
p obj.test(0)
"Rescue"

return

ensure内にreturn文を記述すると、

  • 例外発生が取り消され、正常終了してしまう
  • 例外発生の有無に関わらず、ensureの値がメソッドの戻り値となってしまう

このことから、本来期待した処理が実行されません。

例外発生が取り消され、正常終了してしまう

本来は下記のようにrescueをなくし、ensureのみ記述した場合は、例外処理が発生すると異常終了となります。

class Return
  def test(n)
    begin
      1 / n
      "Begin"
    ensure
      "Ensure"
    end
  end
end

obj = Return.new
p obj.test(1)
p obj.test(0)

結果

(ZeroDivisionError)

しかし、ensure内にreturn文を記述すると、下記のように正常終了してしまいます。

class Return
  def test(n)
    begin
      1 / n
      "Begin"
    ensure
      "Ensure"
      return "return ensure"
    end
  end
end

obj = Return.new
p obj.test(1)
p obj.test(0)

結果

"return ensure"
"return ensure"

例外発生の有無に関わらず、ensureの値がメソッドの戻り値となってしまう

class Return
  def test(n)
    begin
      1 / n
      "Begin"
    rescue
      'Rescue'
    ensure
      "Ensure"
      return "return ensure"
    end
  end
end

obj = Return.new
p obj.test(1) # 本来は"Begin"が返ってくる
p obj.test(0) # 本来は"Rescue"が返ってくる

結果

"return ensure"
"return ensure"

rescue修飾子

rescueは修飾子のように使うこともできます。
以下のように、普通に記述すると5行は書かないといけない処理がrescue修飾子を使うと1行でスッキリと記述することができます。

class Return
  def test
    begin
      文字列
    rescue
      "error!!!"
    end
  end
end

obj = Return.new
p obj.test

rescue修飾子を使用すると1行で書けます!

class Return
  def test
    文字列 rescue "error!!!"
  end
end

obj = Return.new
p obj.test

結果

"error!!!"

特殊変数

特殊変数とは、rubyに用意された特別な意味を持つ変数のことです。
rescueで例外オブジェクトを代入する変数は、指定の有無に関わらず値が代入されます。

特殊変数$!

例外が起こった時にその例外オブジェクト(Exception)が代入されます。もしも複数の例外があった場合は一番最後の例外オブジェクトが代入されることになります。例外が発生しなかった場合にアクセスすると nil が得られます。

特殊変数$@

例外が起こったバックトレースを配列にして代入してくれます。バックトレースとは、例外を発生させたプログラム全体の流れを追って、原因となったコードの情報を示すものです。

引用参考文献

使用例

コンソール(irbやpry)で例外が発生しても、バックトレースが表示されないことにより、原因がわからない場合があります。
コンソールで例外時のバックトレースを表示するには、特殊変数$@を使用します。

irb(main):002:0> puts $@

メソッド全体に例外処理を設ける(begin/endの省略)

メソッド全体に例外処理を設ける場合は、begin/endを省略することができます。

begin/endを記述した例

def hoge
  begin
    p 1/0
  rescue
    p "エラー!!!"
  end
end

begin/endを省略した例

def hoge
  p 1/0
rescue
  p "エラー!!!"
end

raise

raiseメソッドは意図的に例外を引き起こすためのメソッドです。

下記のように記述すると、raiseメソッドにより例外が引き起こされ、rescueメソッドが実行され、「エラー!!!」と出力されます。

begin
  raise
  rescue
    p "エラー!!!"
end
"エラー!!!"

このメソッドはrescue内でも使用することができます。
使用用途としては、例外発生時にプログラムを異常終了させることに加えて、例外情報をログに記録したり、メール送信したい場合に使うことができます。

class Return
  def test(n)
    begin
      1 / n
      "Begin"
    rescue
      p "エラー!!!(log)"
      raise
    end
  end
end

obj = Return.new
obj.test(0)
"エラー!!!(log)"
Traceback (most recent call last):
        2: from ruby.rb:14:in `<main>'
        1: from ruby.rb:4:in `test'
ruby.rb:4:in `/': divided by 0 (ZeroDivisionError)

カスタム例外を作成

例外クラスはRubyのクラスと同様です。
StandardErrorを継承する独自の例外クラスを作成します。

(例)

class Error < StandardError
end

全てのrubyの例外オブジェクトはmessage属性を持っています。
以下のように独自の例外にデフォルトのメッセージを定義してみます。

class Error < StandardError
  def initialize(msg="Error Message")
  super
  end
end

raise Error #=> Error Message

独自のデータを例外に付け加えることができます。

class Error < StandardError
  attr_reader :attr

  def initialize(msg="default Error message", attr="value")
    @attr = attr
    super(msg)
  end
end

begin
  raise Error.new("Error message", "value")
rescue => e
  puts e.attr #=> value
end

これで、独自の例外を作成できました。

参考記事
https://qiita.com/k-penguin-sato/items/3d8ce4e71520da2d68c4

https://qiita.com/ngron/items/4c319ae7340b72c7e566

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

[頭の体操] 逆ポーランド記法の列挙とその計算

列挙

[1, 2, 3, 4]を与えられた時に四則演算ですべてのパターンを列挙する

require "set"
NUMBERS = [1, 2, 3, 4]
SIGNS = %i[+ - / *]

results =
  NUMBERS.permutation(4).uniq.each.with_object(Set.new) do |numbers, results|
    (SIGNS * 3).permutation(3).uniq.each do |signs|
      a, b, c, d = numbers
      x, y, z = signs

      [c, d, x, y].permutation(4).uniq.each do |combination|
        next if combination.join.match(/\A([^\d]{2})/)

        results << ([a, b, *combination, z]).join(" ")
      end
    end
  end

# results.size => 7680

その計算

1 2 3 4 + - /を与えられた場合、-0.2を返す

require 'active_support/core_ext/object/blank'

class Rpn
  SIGNS = %i[+ - / *]

  class << self
    def calc(rpn_text)
      rpn = new(rpn_text)
      rpn.calc
    end
  end

  def initialize(rpn_text)
    @stack = notation_to_a(rpn_text)
  end

  def calc(this_stack = stack)
    return this_stack.first if this_stack.size == 1

    operator_at = this_stack.index{|x| SIGNS.include? x }
    operator = this_stack[operator_at]
    value = this_stack[operator_at - 2].send(operator, this_stack[operator_at - 1])
    this_stack[(operator_at - 2)..operator_at] = value

    return calc(this_stack)
  end

  private

  attr_reader :stack

  def notation_to_a(rpn_text)
    rpn_text.chars.select(&:present?).collect do |x|
      SIGNS.include?(x.to_sym) ? x.to_sym : x.to_f
    end
  end
end

Rpn.calc("1 2 3 4 + - /") # => -0.2
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Railsのシード機能についてのメモ

はじめに

初心者です。
RubyとRuby on Railsを使ってアプリケーションを作っています。
備忘録も兼ねておりますので、間違いなどあればご指摘ください。

シード機能とは

データベース作成後に、初期データを簡単に流し込むことができる機能。

どのファイルを使うのか

db/seeds.rbに初期データとして流し込みたいコードを記述して、
ターミナルでrails db:seedすればOK。
実行してもターミナルに何か表示されるわけではないが、データに問題なければ流し込まれているはず。

ファイルにはどう記述するのか

例)productsテーブルのnameカラムとdescriptionカラムにデータを5つ流し込む

5.times do |i|
  Product.create(name: "Product ##{i}", description: "A product.")
end

timesメソッドを使っているが、1行ずつ書いていっても問題なし。

まとめ

  • シード機能はデータベース作成後に初期データを流し込むことができる機能
  • db/seeds.rbに流し込みたいデータを記述
  • ターミナルでrails db:seedすれば流し込める

参考

Railsガイドv6.0
https://railsguides.jp/active_record_migrations.html

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

[ruby]特定条件のみに呼応するプログラムの作成

内容

今日の曜日を表示するコードをDateクラスを使用して記述してください。
ただし、金曜日だった場合だけ以下のように表示の内容を変えてください。

(出力内容)
「今日は月曜日」
「今日は金曜日だ !!!」

実装

image.png
image.png

解説

今回はRubyで日付を扱うため、ライブラリであるDateクラスを使用します。
1行目でライブラリからDateクラスをよびだします。
3行目でwdayメソッドを使用し、0(日曜日)〜6(土曜日)の整数にした時の今日の値を取得することができます。
※値なので取得できるのはあくまでも整数です。
なので、4行目でdaysに配列で曜日を記入します。
そして、6~10行目のif文で今日の日付が5(金曜日)だったら
「今日は金曜日だ!!!」と出力される式、違った場合は
「今日は○曜日」と出力される

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

DockerでRailsチュートリアルのローカル開発環境構築 - RSpec導入 & CircleCIでHerokuデプロイ-

はじめに

前回の記事
DockerでRailsチュートリアルのローカル開発環境構築(Rails 6 + PostgreSQL + Webpack) - Qiita

個人開発アプリ
mdClip <オンラインmarkdownエディタ>

前回の記事に続いて、Railsチュートリアルのローカル開発環境構築を行っていきます。

  • Railsチュートリアル最新版(2020.8.6現在)に対応のRails 6

  • Dockerを使用し、開発環境の再現が可能

  • なるべくローカル環境にインストールしない

Docker環境で操作する場合はターミナルのコマンドを適宜

$ docker-compose run app ...

もしくは

$ docker-compose exec app ...

で置き換えてください。

Rspec導入

minitestでも問題ないです
後述のCircleCIでもminitestを走らせる事もできました。

Gemfile

必要に応じて以下のgemを追加
(不要ならminitest関連のgemを削除する)

Gemfile

  # Test enviroment: Rspec
  gem 'rspec-rails'
  gem 'spring-commands-rspec'
  gem 'guard'
  gem 'guard-rspec', require: false

  # Test enviroment: Fake date generator
  gem 'factory_bot_rails'
  gem 'faker'
  gem 'forgery_ja'

bundle install

Gemfileを編集したのでdocker-compose buildが必要です

$ docker-compose build

初期ファイル生成

$ rails g rspec:install

レポートフォーマット

.rspec

--require spec_helper
--format documentation

Guard初期化

$ bundle exec guard init

spring boot対応

$ bundle exec spring binstub --all

Guardの自動テスト時にもspringが有効になるように
Guardfile

$ guard :rspec, cmd: "bundle exec spring rspec" do

CircleCI設定

事前準備

ここでは説明しませんが事前に以下の手順が必要だと思います

  • GitHub & CircleCI & Herokuのアカウント登録
  • SSH接続設定(GitHub - CircleCI)
  • CircleCIへのproject登録および環境変数定義(Heroku APIキー, app名)

設定概要

  • git push -> 自動test (RSpec) -> 自動デプロイ(Heroku)
  • Orbを使うとconfig.ymlの内容を簡略化できる(キャッシュ利用のための記述が不要)
  • Orbを使うためにversion: 2.1指定
  • Docker imageのRuby versionはRailsチュートリアルに合わせて2.6.3
  • Node.jsが必要なので-nodeのついたimageを指定

メモ: Node.js バリアントの Docker イメージ (-node で終わるタグ) に対しては、Node.js の LTS リリースがプリインストールされています。 独自に特定のバージョンの Node.js/NPM を使用する場合は、.circleci/config.yml 内の run ステップで設定できます。 Ruby イメージと共に特定のバージョンの Node.js をインストールする例については、以下を参照してください。

公式サンプルを踏襲しています

公式do - Ruby
Language Guide: Ruby - CircleCI

公式doc - Heroku Deploy
デプロイの構成 - CircleCI

現時点で最新のHeroku-orbは1.2.0ですがバグがあるようで
pull requestされています

deploy-via-git bash script error · Issue #13 · CircleCI-Public/heroku-orb

config.yml

.circleci/config.yml

version: 2.1

orbs:
  ruby: circleci/ruby@1.0
  node: circleci/node@2
  heroku: circleci/heroku@1.1.0

# setupでまとめる
commands:
  setup:
    steps:
      - checkout
      - ruby/install-deps
      - node/install-packages:
          pkg-manager: yarn
          cache-key: "yarn.lock"

jobs:
  build:
    docker:
      - image: circleci/ruby:2.6.3-node
    steps:
      - setup
  test:
    docker:
      - image: circleci/ruby:2.6.3-node
      - image: circleci/postgres:11.6-alpine
        environment:
          POSTGRES_USER: postgres
          POSTGRES_DB: myapp_test
          POSTGRES_PASSWORD: ""
    environment:
      BUNDLE_JOBS: "4"
      BUNDLE_RETRY: "3"
      PGHOST: 127.0.0.1
      PGUSER: postgres
      PGPASSWORD: ""
      RAILS_ENV: test
    steps:
      - setup
      - run:
          name: Wait for DB
          command: dockerize -wait tcp://localhost:5432 -timeout 1m
      - run:
          name: Database setup
          command: bundle exec rails db:schema:load --trace
      - run:
          name: Rspec
          command: bundle exec rspec

  deploy:
    executor: heroku/default
    steps:
      - checkout
      - heroku/install
      - heroku/deploy-via-git:
          maintenance-mode: true
      - run:
          name: DB migrate
          command: heroku run rails db:migrate --app $HEROKU_APP_NAME

workflows:
  version: 2
  test_and_deploy:
    jobs:
      - build
      - test:
          requires:
            - build
      - deploy:
          requires:
            - build
            - test
          filters:
            branches:
              only: master

動作確認

HTMLを書き換える

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  def hello
    render html: "Bye, world!"
  end
end

変更をgit pushして、CircleCIのpipelineがsuccessに変われば
ブラウザから見た内容が上記を反映するはずです

簡単なテスト

仮のrequest_specを作成

rails g rspec:request test

spec/requests/tests_spec.rb

require 'rails_helper'

RSpec.describe "Tests", type: :request do
  describe "GET /" do
    it "Bye, world!が表示されること" do
      get root_path
      expect(response.body).to include "Bye, world!"
    end
  end
end

CircleCI上でも上記テストがpassするはずです

Troubleshoot

CircleCIのtestが以下のエラーで失敗する

rails aborted!
PG::ConnectionBad: could not translate host name "db" to address: Name or service not known

database.ymlの記述が原因です

test環境ではdefaultを引き継いでhost: dbが適応されているはずです

config/database.yml
以下のように書き換えて環境変数がある場合はそちらを優先するようにしました
(ymlのなかでもerbが使えるという学びでした)

test:
  <<: *default
  host: <%= ENV['PGHOST'] || 'db' %>
  database: myapp_test

ERROR IN CONFIG FILE:

インデント、フォーマットの間違いが多いと思います

最後に

  • 意地でローカル環境をきれいに保つことに注力した結果、デプロイやdb:migrateを自動化することで、Heroku CLIすらインストールすることを回避しましたが、素直にHeroku CLIくらいは入れておいた方が今後のエラー解決に役立つ気もします。
  • 難易度は上がりますが、ローカル環境でDocker, CICDを取り入れてのRailsチュートリアル、学べることも多いと思いますので腰を据えて取り組むことのできる方はぜひトライしてみてください。
  • 今後も未知のエラーに遭遇することが予想されますが、おおよそココまでが大変なところかとおもっていますので、この記事がお役に立てれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む