20201118のRubyに関する記事は30件です。

非同期でいいね機能が切り替わらない時の解決方法

状態

同期状態でいいね機能を実装

そこから、非同期化

エラーもちらほらでましたが、エラーが出ないように修正。

エラーがなくなった、でも非同期でいいね機能が動かない。

って人は見てください。

同じようなパターンかもしれないです。

いいね機能を実装している方はたくさんいて
記事もたくさんありますので、自分と同じ状況を探してください。

詳しい詳細は下記を見てください。
https://teratail.com/questions/304798

非同期処理の流れをおさらい

Ajaxの流れ
①イベント発生
②非同期でリクエスト送信
③受け取った情報を処理
④処理結果をJSON形式で応答
⑤レスポンスを受けてDOMでページ更新

自分の状態を確認することで原因がどこか見つける作業が早くなる。
イベントを発生しているかの確認だったら、アラートを出すように記述して確かめるみたいな感じですね。

問題箇所

<tr class="idea_<%= @idea.id %>">
 <%= render 'like', idea: @idea %>
</tr>

ここの記述が問題でした。
記事等を参考に書いていたのですが、tableタグを使用していないのにtrタグを使っていたのが原因でした。
タグの性質を理解せずに使っていたのが駄目だったわけですね。

まさかと、思いdivタグに変更したらすんなりできました。

今回得た教訓は、タグの性質を知ること!

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

Week9


  • source ~/school/multi/my_ruby/grad_members_20f/members/evendemiaire/post/assert.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

社会人4年目SIerからのWeb系エンジニア転職(1ヶ月目)

はじめに

自分は現在SIerとして働いており、Web系エンジニアへの転職を目指しています。
転職を決意してちょうど1ヶ月が経ったので、

「転職に成功した!」の投稿に比べると「転職の途中です」の投稿は少ないと感じたので
転職活動開始1ヶ月目の活動を記録しておきます。

目次

現在のスペック
転職の動機
現在までの活動
2ヶ月目からの活動予定

現在のスペック

  • 新卒でSIer企業に入社し、入社4年目(要件定義・設計・製造・試験・運用保守を経験)
  • 職場での使用言語はCOBOLのみで、他言語の経験はなし
  • 取得資格
    • 情報処理技術者試験 基本情報技術者
    • 情報処理技術者試験 応用情報技術者
    • 情報処理技術者試験 データベーススペシャリスト
    • 情報処理技術者試験 ネットワークスペシャリスト
    • AWS クラウドプラクティショナー
    • AWS ソリューションアーキテクト アソシエイト

転職の動機

「自分の仕事が世の中の役に立っている」と自信を持って言えない

携わっているシステムが自分では使用出来ないシステムであり、
「作ったものが世に出て、役に立っている」という実感が得づらいです。

開発している機能の必要性に疑問を感じることも少なくありません。
「これ開発するのはいいけど、誰か使うのか?」とモヤモヤしながら、
長い期間の開発を行うことにモチベーションが感じられなくなってしまいました。

職場以外での勉強が仕事に活かしづらい

今の職場では以下の2点より、仕事に関わる勉強を職場外で行うことが難しいと感じています。

  • レガシーな技術を使用しており、インターネット上や書籍として教材が少ない
  • 客先常駐で勤務しており、仕事で使用している設計書やソースは持ち出しができない

「何かを勉強しないとまずい」「他の人に差をつけたい」という思いは常にあり、
会社が推奨する資格の勉強を行なっていましたが、仕事の役に立つことは少なかったです。

あまり仕事の役に立っていない資格の勉強を続けるうちに、
「折角勉強する意欲があるので、勉強が仕事の成果に直結する職場に行きたい」と感じるようになりました。

現在までの活動

  • 転職エージェントへの登録・面談
  • ポートフォリオ作成のための学習
  • Twitter、Qiita、Githubのアカウント作成

転職エージェントへの登録・面談

Web系への転職における甘い考え方を全てひっくり返されました。

  • ポートフォリオも作らずWeb系への転職を目指すとは何を考えているのか
  • あなたの持っている資格はWeb系への転職には何の役にも立たない
  • COBOLでの開発経験は開発経験がないのと同じくらいに考えて欲しい

・・・とズバズバ正直な意見をいただきました。笑

その後、調べれば調べるほどエージェントの方が仰っていたことが正しいことに気づきました。

また減収が予想されることから現職にとどまることも提案いただきましたが、
むしろ自身の市場価値の低さに焦りを感じ、一層Web系エンジニアになるぞという思いが強まりました。

自分の転職に対する考え方や転職理由にダメ出しをもらえるいい機会なので、
あまり準備ができていなくても一旦面談を受けるのはありだなと個人的には思いました。

ポートフォリオ作成のための学習

正直なところ、何から始めればいいのか全くわからずだったので、
著名な勝又健太(poly_soft)さんがおすすめされている学習順序をなぞることから始めました。

  • コンピュータサイエンス基礎(飛ばした)
  • Linux基礎(Linux標準教科書)
  • HTML/CSS基礎(Progate)
  • JavaScript基礎(Progate)
  • Ruby基礎(Progate)
  • RDBとSQL基礎(Progate)
  • GitとGitHub基礎(Udemy)
  • Ruby on Rails基礎(Progate)
  • Ruby on Railsチュートリアル

基本情報処理技術者を取得していたのでコンピュータサイエンス基礎は飛ばしてしまいましたが、
駆け足ながらも1ヶ月で一通りの学習を終えることができました。

Linux基礎の勉強そのものよりも、勉強のためにVirtualBox上でCentOSを動かすのに時間がかかった経験から、
以降の勉強では環境構築などの手間がなく、すぐに始められるProgateを選択していました。

個人的にはRuby on Railsチュートリアルはかなり難しかったので、
内容的に重複する部分はありますが、ProgateのRails基礎を先にやっておいてよかったなと感じました。

Twitter、Qiita、Githubのアカウント作成

アウトプットの場を作ることが重要と先人の方々が言っていましたので、
各種SNSの使い分けもGitが何をするものかもわからないままにアカウントを作成しました。

Twitter

2ヶ月目からの活動予定(簡単に)

ポートフォリオの作成

言わずもがな、ポートフォリオの作成に取り掛かります。
テーマは決まっているので、がしがしコードを書いていきます。

技術の勉強会に参加し、実際にWeb系エンジニアとして働いている方の話を聞く

転職するとは決めたものの、実際にどんな方が働いているのかあまりイメージがついていないところがあるので。
同じようなステップにいる転職を目指す方ともコミュニケーションをとってみたいです。

技術的アウトプットを行う(Twitter,Qiita)

現状Twitterは勉強時間を垂れ流しにして記録しているだけ、Qiitaは見る専用になってしまっています。
ポートフォリオ作成の上でつまづいたところ、何か気になったところは発信しアウトプット癖をつけます。

最後に

駄文ですが、読んでいただきありがとうございます。

SIer→Web系エンジニアの転職を目指す方は多いと思うので、
今同じステップにいる方に少しでも参考にしていただけたら嬉しいです。

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

スクールに通い始めて

はいどうも!
某プログラミングスクールに通い始めた37歳のおっさんです。
プログラミングは完全に未経験。
そんなおっさんが転職するまでの軌跡(奇跡?)をぼちぼち綴っていこうと思います。

先日、中間試験なるものを受けてみたところなんと、44点という結果・・・
学生時代に受けたテストでもこんな点数は取った記憶がなかった。
問題文を見ても何を求められているかわからないし、そもそもコードを読んでてもわからない部分が多すぎて理解ができない。
あれ?これってもしかして記憶力がだいぶ悪くなってる?
そう、気付き始めてしまったのです。
同期の人々よりも時間をかけて自分なりにわかる説明をしてくれる記述を手繰り寄せてなんとか消化する。
google先輩の力でなんとかやってきました。
自分がスペックとして他人に勝てるのは①「情熱」と②「モチベーション」この二つだと思ってます。
①他人が休んでる時にしっかり学習して
②他人が学習しているときに倍の密度で学習する
①も②も関係ないやないかーい!
というツッコミはおいておいて。

実際勝てる方法はこれしかないと思ってます。
そして、後がない。
同年代は役職に就いて、レベルアップした内容の仕事をして、家も買ってetc
人生というストーリーで見たらきっといい結末が待っているんじゃないかなと思える内容を歩んでいるわけです。
年齢という取り返せない条件で負けている以上、自分に何か付加価値がないと同じスタートラインに立った時にかなり後ろからスタートしなければいけないわけです。
人よりできないぶん、時間をかける
人より遅いぶん、密度を濃くする
この二つで対応していくのが絶対条件なんじゃないかなと思っています。
そりゃあ負けたら悔しいですよ。
だけど悔しがったってしょうがない。
もう結果として出てしまっているんだから受け止めるしかない。
考えなければいけないのは、じゃあこれからどうするか?っていうところだと思います。
負けた→もういいや→向いてなかった!goodbye!
この流れは簡単です。誰でもできる。
ここから反発してどれだけできるかだと思うんですよね。

「諦めたらそこで試合終了ですよ」
有名なバスケットボールの監督が言ってましたね(ちょうど世代)
この言葉を胸に胸に刻んで明日からも頑張ります。

ちなみに
「〜していきます」って希望的欲求感に聞こえませんか?
「〜します」って言い切ると決断なんだなって聞こえますよね。
決断すると自分がその目標に向かって行くんですよ。
○○ザップのコミットメント、と同じですよね。
言ったからにはやらなきゃいけない。
良い意味で自分を追い込んで頑張る。
その背中が他の人に勇気を与えられたらいいなって思います。

三回目に受けた試験でようやく突破したおっさんより
それではでは:wave:

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

[Rails] [ransack] link_toで検索条件を作りclassをつける

link_toにclassをつけるのに苦戦したので備忘録もかねて。

ransackで検索機能を作っており、あるdivをクリックした時にあらかじめ決めておいた検索結果にリンクさせようとlink_toを使い苦戦。

最初に書いていたコード

<%= link_to  controller: "shops", action: "index", q: { name_cont: '東京'}, class: "反映させたいクラス" do %>
 <div>このdivをクリックしたら「東京」の検索結果にとぶ</div>
<% end %>

こちらを参考に書きました。やってることはほぼ同じで、リンクテキストを踏ませるかdivを踏ませるかの違いです。
リンクテキストを指定する代わりにdo endで囲んでブロックにし、controller: ~ 東京 }までがパスで、その後にclassを書いているというイメージです。

エラーは出ずリンクは機能するものの、classが反映されず...

うまくいったコード

<%= link_to nil, controller: "shops", action: "index", q: { name_cont: '東京'}, class: "反映させたいクラス" do %>
 <div>このdivをクリックしたら「東京」の検索結果にとぶ</div>
<% end %>

リンクテキストをnilとして明記してあげたらうまく機能しました。

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

【Rails】 link_toへのpathの渡し方

はじめに

  • link_toで飛ばしたいページを指定する際、つまづいたので備忘録。
  • showページを表示させる時の、:idを指定する書き方。

結論

application.html.erb
<%= link_to '店舗詳細', company_path(:id) %>

こう書いたら表示できました。

エラーになった書き方

<%= link_to '店舗詳細', company_path %>
<%= link_to '店舗詳細', company_path(@company) %>
これではエラーになりました。

ルーティングです。
routes.rb
get 'company/:id', to: 'companies#show'

コントローラーです。
controller.rb
@company = Company.find_by(params[:id])

?Railsガイドには、

get '/patients/:id', to: 'patients#show', as: 'patient'
アプリケーションのコントローラに以下のコードがあるとします。

@patient = Patient.find(params[:id])
上記に対応するビューは以下です。

<%= link_to 'Patient Record', patient_path(@patient) %>
これで、ルーターによって/patients/17というパスが生成されます。これを利用することでビューが改修しやすくなり、コードも読みやすくなります。このルーティングヘルパーではidを指定する必要がない点にご注目ください。

同じようにasでルーティングに名前を指定しようとしたのですが、私の場合は、resourcesを使っていたので、それをするとエラーになってしまいました。
なので、

ルーティングです。
routes.rb
get 'company/:id', to: 'companies#show'

シンプルにこのように書いてあげたら普通に動きました。

感想

ルーティング一つとっても場合によって書き方も変わってくるので難しいと思いました。
Railsガイド様にはこれからもお世話になります。

参考
https://railsguides.jp/routing.html

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

開発環境別にseed ファイルを分けて管理する

はじめに

アプリの初期データ用のseed ファイルを開発環境別に管理する必要があったので備忘録です。

環境

  • macOS 10.15.6
  • Ruby 2.5.7
  • Rails 5.2.3
  • rspec-rails 4.0.1
  • capybara 3.32.2

参考URL

https://pikawaka.com/rails/env
https://prokyou.com/rails/seeds/
https://qiita.com/kimihito_/items/753390a5fe42a41b1d60
https://qiita.com/mogya/items/76430e32ffca1ef93f27

目標

  • 開発環境別でseed ファイルを管理できる
  • seed ファイルの修正後に環境別に初期データを追加/削除ができる

実装

1. seeds ディレクトリを作成

通常のRails アプリでのseed ファイルのディレクトリはdb/seeds.rb です。
これを以下のように修正します。

#修正前
db/seeds.rb
#修正後
db/seeds/development.rb #追加
db/seeds/test.rb #追加
db/seeds/production.rb #追加
db/seeds.rb
  • seeds ディレクトリの中に環境別のseed ファイルを作成
  • 元々あったseeds.rb の中身は現時点ではそのままでok

2. 環境別にseed ファイルを作成

元々あったseeds.rb の中身を元にして各環境別に初期データ用の処理を追加します。
私の場合はCSV を使った初期データをテスト環境でも使いたかったのでtest.rb に下記を追加しました。
development.rb は元々あったseeds.rb を複製しました。

test.rb
#Country CSV import
require "csv"

CSV.foreach("country.csv", headers: true) do |row|
  Country.create!(
    country_name: row["国・地域名"],
    region: row["場所"]
  )
end

3. seeds.rb に読み込み処理を追加

環境別にseed ファイルが準備できたので各ファイルの読み込み処理をseeds.rb に追加します。
初期データ追加用の処理は各環境別に分けているはずなので削除してokです。

seeds.rb
#環境別にseed ファイルを読み込む
load(Rails.root.join("db", "seeds", "#{Rails.env.downcase}.rb"))

#元の初期データ追加の処理は削除

seed ファイルの読み込みディレクトリを指定しています。

seeds.rb
#seeds.rb の読み込み処理の詳細
load(Rails.root.join("親ディレクトリ", "子ディレクトリ", "#{開発環境名.downcase}.rb))

4. 環境別にseed ファイルで初期データを導入

環境別にseed ファイルを読み込んで初期データを導入します。
初期データを削除してseed ファイルを読み込みます。

# development 環境の初期データ削除
$ rails db:migrate:reset

# development 環境の初期データ追加
$ rails db:seed

# test 環境の初期データ削除
$ rails db:migrate:reset RAILS_ENV=test

# test 環境の初期データ追加
$ rails db:seed RAILS_ENV=test

5. コンソールで導入されたデータの確認

環境別にコンソールを立ち上げて初期データの件数を確認します。
-e オプションをつける事で環境別にコンソールを立ち上げます。

#development 環境で初期データ件数の確認
$ rails console
Loading development environment (Rails 5.2.3)
> Country.count
249

#test 環境で初期データ件数の確認
$ rails console -e test
Loading test environment (Rails 5.2.3)
> Country.count
249

学び

  • 環境別にseed ファイルを用意する事で初期データの管理が楽になった
  • テスト用の初期データにseed を使うのは一部だけにして、テストに必要なデータはなるべくfixture を使う方が良さそうです
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[初心者]ブラウザでRailsのルーティングを確認する方法[小ワザ]

ブラウザでルーティングを確認するコマンド

$ rails sでサーバーを立ち上げてから、(URL)の後ろに

/rails/info/routes

とつけるだけです。

image.png
↑のような感じで、検索窓に直接打ち込めば大丈夫です。
image.png
こんなに見づらかったルーティングが、
image.png
ブラウザで確認できるようになります。

見やすくなるのに加え、毎回毎回サーバーを落として、立ち上げて、という作業からも解放されるので、作業時間短縮には非常にオススメです!

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

Railsを使ったToDoリストの作成(4.CRUDのRead機能)

概要

本記事は、初学者がRailsを使ってToDoリストを作成する過程を記したものです。
私と同じく初学者の方で、Railsのアウトプット段階でつまづいている方に向けて基礎の基礎を押さえた解説をしております。
抜け漏れや説明不足など多々あるとは思いますが、読んでくださった方にとって少しでも役に立つ記事であれば幸いです。

環境

  • Homebrew: 2.5.10 -> MacOSのパッケージ管理ツール
  • ruby: 2.6.5p114 -> Ruby
  • Rails: 6.0.3.4 -> Rails
  • node: 14.3.0 -> Node.js
  • yarn: 1.22.10 -> JSのパッケージ管理ツール
  • Bundler: 2.1.4 -> gemのバージョン管理ツール
iTerm
$ brew -v => Homebrew 2.5.10
$ ruby -v => ruby 2.6.5p114
$ rails -v => Rails 6.0.3.4
$ npm version => node: '14.3.0'
$ yarn -v => 1.22.10
$ Bundler -v => Bundler version 2.1.4

第4章 CRUDのRead(index/show)

第4章では、CRUDを使ってWebアプリケーションの基本的なデータ操作を実装します。CRUDは、Create(作成)、Read(参照)、Update(更新)、Delete(削除)を表します。今回実装したい機能は以下の通りです。

  • 一覧表示機能(indexアクション)
  • 詳細表示機能(showアクション)
  • 新規登録機能(newアクション/createアクション)
  • 編集機能(editアクション/updateアクション)
  • 削除機能(destroyアクション)

では、それぞれの機能実装について詳しく見ていきましょう。
まずはCRUDのReadについてです。

1 indexアクション

まずは、indexアクションを使って一覧表示機能を実装していきます。

1 データベースからレコードを取得する

Boardsコントローラを使ってデータベースからレコードを取ってきます。
今回は全てのレコードを取得したいのでActiveRecordのallメソッドを使い、メソッドの返り値をインスタンス変数に代入します。

app/controllers/boards_controller.rb
class BoardsController < ApplicationController
    def index
        @boards = Board.all
    end  
end
#インスタンス変数(@xxx)に代入したものはhaml内で<%=%>で囲うことで
#Viewsで自由に表示することができます。

先ほど全てのデータを取ってくるとは言ったものの、現在はデータが1つしかないためダミーデータを作成します。

ダミーデータを作成するためにはdb/seeds.rbにレコードを書きます。

db/seeds.rb
Board.create({name: 'seeds-title1', description: 'seeds-description1'})
Board.create({name: 'seeds-title2', description: 'seeds-description2'})

レコードを書くことができたら、ターミナルでseeds.rbを読み込むコマンドを実行し、ファイルを読み込みます。

iTerm
$ rails db:seed

これで新たに2つのレコードがデータベースに保存されているはずです。
試しにコンソールで確認してみましょう。

irb(main):002:0> Board.all

=> #<ActiveRecord::Relation [#<Board id: 1, name: "console-name1", description: "console-description1", created_at: "2020-11-17 05:38:06", updated_at: "2020-11-17 05:38:06">, 
#<Board id: 2, name: "seeds-title1", description: "seeds-description1", created_at: "2020-11-17 11:41:05", updated_at: "2020-11-17 11:41:05">, 
#<Board id: 3, name: "seeds-title2", description: "seeds-description2", created_at: "2020-11-17 11:41:05", updated_at: "2020-11-17 11:41:05">]>

このようにインスタンスが3つ作成されていればOKです。

2 一覧画面で全てのデータを表示する

次に、先ほど取ってきたデータをviewsで表示します。
全てのレコードを表示したいのでRubyのeachメソッドを使います。

app/views/boards/index.html.haml
.container
  - @boards.each do |board|
    .card
      .card_content
        .card_top
          %p.card_title 
            = board.name
          .dropdown
            = image_tag 'Combined Shape.png', class: 'card_dropdown dropbtn'
            .dropdown-content
              %a{:href => "#"} Edit
              %a{:href => "#"} Delete
        .card_description
          = board.description
        .card_detail
          = image_tag 'xxx.png'

ポイントは3つあります。

  • - @boards.each do |board|という繰り返し表現を使って@boardsの値全てををboardに渡しています。コンソールではBoard.allはArrayで返ってきていたため上記のような表現になっています。
  • = board.nameで繰り返し表現されたboardのnameカラムを表示しています。このようにカラム名はメソッドのように使用することができます。
  • = board.descriptionで繰り返し表現されたboardのdescriptionカラムを表示しています。

以上で、記事一覧機能は実装できました。

ダミーデータについて

先ほどはseeds.rbを使って手動でダミーデータの作成を行いましたが、ダミーデータを自動的に作成できるfakerという便利なgemがあるのでインストールして使っていきましょう。

Gemfile
gem 'faker'

Gemfileにgem 'faker'と書いたら、bundle installをします。
これでfakerのインストールは完了です。

では、実際にseeds.rbでfakerを使用してみましょう。
今回はfakerの中でも文章を作成してくれるFaker::Loremを使用しています。
また、より多くのレコードを作成したいので10回Board.createを繰り返しています。

db/seeds.rb
10.times do
    Board.create(
        name: Faker::Lorem.sentence(word_count: 3), 
        description: Faker::Lorem.sentence(word_count: 20)
    )
end

レコードを書くことができたら、ターミナルでseeds.rbを読み込むコマンドを実行し、ファイルを読み込みます。

iTerm
$ rails db:seed

これで新たに10個のダミーデータの入ったレコードがデータベースに保存されました。

2 showアクション

次に、showアクションを使って詳細表示機能を実装していきます。

1 ルーティング

まずは、詳細表示ページに遷移するURLが必要なのでroutes.rbでURLを作成します。

config/routes.rb
Rails.application.routes.draw do
  resources :boards
end

resourcesはCRUDの基本となる以下7つのアクションへのルーティングを定義する時に使います。

CRUD アクション   
Create(作成) index show
Read(参照) new create
Update(更新) edit update
Delete(削除) delete

また、以下のようにonlyオプションをつけると、ルーティングを限定することができます。

resources :boards, only: [:show, :edit, :update]
#resources :コントローラ名, only: [アクション名]

今回は、boardsコントローラにresourcesを指定したので、rails/infoで確認すると全てのアクションのルーティングが定義されていることがわかると思います。
スクリーンショット 2020-11-18 12.19.58.png

2 コントローラ

では、次にコントローラでデータを取得します。
データを取得するためにActiveRecordのfindメソッドを使用します。
findメソッドはテーブルから引数に入っているidのレコードを取ってきます。

app/controllers/boards_controller.rb
class BoardsController < ApplicationController
    def show
        @board = Board.find(params[:id])
    end
end

ポイントは2つあります。

  • ビューで表示したいので取得したデータをインスタンス変数に代入しています。今回は1つのレコードを取ってくるので単数形になっています。
  • 引数の値をparams[:id]としています。'params'とは送られてきた値を受け取るためのメソッドで[:カラム名]で値を受け取ることができます。

以上で、データの取得は完了です。

3 Views

取得したデータをViewsで表示していきます。
先ほど取得した値を@board.nameで表示しています

app/views/boards/show.html.haml
.container
  .board_title
    = @board.name
  .card
    .card_image
      = image_tag 'ToDoPhoto.png'
    .card_content
      .card_top
        %p.card_title 
          card_name
      .card_description
        card_description
      .card_detail
        = image_tag 'default-avatar.png'
        .card_detail_img
          %div
            %span 23
            %span 
              = image_tag 'Shape.png'

これでshowアクションを使った詳細表示機能の実装は完了です。

4 一覧表示画面にリンクを貼る

最後に、一覧表示ページから詳細表示ページに遷移できるようにリンクを貼っていきましょう。

app/views/boards/index.html.haml
.container
  - @boards.each do |board|
    = link_to board_path(id: board.id) do
      .card
        .card_content
          .card_top
            %p.card_title 
              = board.name
            .dropdown
              = image_tag 'Combined Shape.png', class: 'card_dropdown dropbtn'
              .dropdown-content
                %p Edit
                %p Delete
          .card_description
            = board.description
          .card_detail
            = image_tag 'default-avatar.png'

スクリーンショット 2020-11-18 17.10.28.png

ここではカード全体をリンクにしています。
では具体的にコードを見ていきましょう。

重要なのは以下の一文です。ここでリンクを作成し、ネストすることでカード全体をリンクにしています。

= link_to board_path(id: board.id) do
# = link_to 'リンクの名前' Helperのpath 
  • link_toはaタグを生み出すためのメソッドです。
  • pathの引数ではidを指定しています。idは@boardsからeachメソッドによって作成されたboardのidカラムなのでboard.idとしています。しかしRailsでは引数にインスタンスを代入するだけでidを勝手に取得してくれるためid: board:idの部分をboardと書くこともできます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アラビア数字をローマ数字に変換する

概要

アラビア数字を受け取り、ローマ数字を返す method を Ruby で作成する

ローマ数字: https://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%9E%E6%95%B0%E5%AD%97

コード

 1  $table = {1=> 'I', 4=> 'IV', 5=> 'V', 9=> 'IX', 10=> 'X', 40=> 'XL', 50=> 'L', 90=> 'XC', 100=> 'C', 400=> 'CD', 500=> 'D', 900=> 'CM', 1000=> 'M'}
 2  
 3  # reverse sort
 4  $table = Hash[ $table.sort.reverse ]
 5  
 6  
 7  def arabic2roman(arabic_num)
 8    if arabic_num.between?(1,3999)
 9      # initialize
10      i = arabic_num
11      roman_num = ''
12  
13      for s in $table.keys
14        if i == 0
15      break
16        else
17      # q: quotient, r: remainder
18      q = i / s
19      r = i % s
20      # update
21      i = r
22      roman_num += $table[s] * q
23        end
24      end
25      return roman_num
26    end
27  end

以下の行を追加して、コードをテストする

test = [1, 2, 4, 5, 6, 9, 10, 11, 14, 15, 19, 38, 42, 49, 51, 97, 99, 439, 483, 499, 732, 961, 999, 1999]

test.each do |num|
  puts "#{num} #{arabic2roman(num)}"
end

結果は以下の通り

1 I
2 II
4 IV
5 V
6 VI
9 IX
10 X
11 XI
14 XIV
15 XV
19 XIX
38 XXXVIII
42 XLII
49 XLIX
51 LI
97 XCVII
99 XCIX
439 CDXXXIX
483 CDLXXXIII
499 CDXCIX
732 DCCXXXII
961 CMLXI
999 CMXCIX
1999 MCMXCIX

Ruby初心者なので、キレイなコードではないかもしれません

これからもっとRubyについて学んでいきたいと思います!


  • source ~/doc/lecture/multi-scale/grad_members_20f/members/yukiue/docs/roman_numerals.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby AtCoder向けVSCode設定 __debug__

はじめに

以前投稿しました、Ruby AtCoder向けVSCode設定 では、msys2を使用していましたが、最近はrubyinstallerを使用しています。

その rubyinstaller でVSCodeのデバッグ環境が動作するようになりましたので、記録として残したいと思います。

構築順

手前味噌ですが、Ruby AtCoder向けVSCode設定 を合わせて参照願います
VSCode インストール
Ruby インストール
今回は、rubyinstaller-devkit-2.7.2-1-x64.exeを使用しております。
Gem インストール

Gemfile.rb
gem "ruby-debug-ide"
gem "debase"
gem "rcodetools"

VSCode settings.json 設定例

settings.json
{
    "ruby.rctComplete.commandPath": "C:\\Users\\user\\...\\rct-complete.bat"
}

rct-complete.bat のパスを通します

VSCode launch.json 設定例

launch.json
{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug Local File",
            "type": "Ruby",
            "request": "launch",
            "program": "${workspaceRoot}/main.rb",
            "pathToRDebugIDE": "C:\\Users\\user\\...\\rdebug-ide.bat",
            "pathToBundler": "C:\\Ruby27-x64\\bin\\bundle.bat",
            "useBundler": true,
            "stopOnEntry": true,
            "args": [
                "data.txt"
            ]
        },
    ]
}

rdebug-ide.bat のパスを通します。
bundlerを使用しています。

data.txt

data.txt
2 3

main.rbと同じディレクトリにdata.txtを置きます。

では、F5キーを押下
20201118a.png
20201118b.png
変数表示もバッチリ

まとめ

  • Windows10 の VSCode の Ruby でデバッグができるようになった

参照したサイト
VSCodeでrbenv環境のrubyのデバッグ、コードコンプリートができない
Ruby AtCoder向けVSCode設定

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

Sessionとcookieをがよくわからないので、できるだけかみ砕いてまとめたよ

SessionとCookie

Railsチュートリアルでログイン機能の実装まで学習したが、いかんせん【わからない】
本当に分からない。自分が今何をやっているか理解できない。
こういう「分からない事が分からない」のは基礎的な知識が抜け落ちているパターンが多い。

どの基礎を抑えれば理解できるのかわからないが、まずSessionとCookieというこの2つのキーワードがよくわからないので、自分なりにまとめていく。


私は現職が医療従事者であり、専門知識の理解は対象(自分 or 他人)が理解できる内容までかみ砕く(対象によって流動食、ペースト食とレベルを変える)必要があると考えている。
このページでの内容はプログラミング学習前の自分レベルでも理解できるように、出来る限りかみ砕いた。内容を簡素にしていくと、認識違いやそもそも間違ってるでという事も起きる。
その場合はご指摘、ご助言いただけるとうれしいです。


※個人的ではあるが、感覚的に理解しやすいようにwebサーバーを「webページ」・ブラウザを「ユーザー」と表現している。

webの仕組みから始まる。

webの仕組みは

  1. ユーザーがwebページにアクセスする(リクエストする)
  2. アクセスしたページが表示される。(レスポンスが返ってくる)

  3. 次のページにアクセスすると、①で送られてきたユーザーのリクエストは2つ目に引き継がれない

といった仕組みになっている

HTTP通信は情報の引継ぎはできないようになっている。
※HTTPの通信には「HTTPリクエスト」と「HTTPレスポンス」の2つに分けられる。
※①がHTTPリクエスト②がHTTPレスポンス
※HTTPリクエストには「httpリクエストライン」「HTTPリクエストヘッダ」「HTTPメッセージボディ」の3つに分かれてる。
※HTTPリクエストヘッダには「ユーザーエージェント名」や「cookie」情報が含まれている

情報が引き継がれないなら、webページでよくある、ログインする仕組みとかはどうなっているのか???
ここでsessionとcookieという仕組みが登場する。

Sessionとcookieとは、それらの違い

Sessionとcookieは似ている。一見、動作としては同じように感じる。

違いとしては
cokkieはユーザー側(ブラウザ)に保存、覚えられる情報に制限あり(サイズ、文字列のみ)、簡単に盗み見られる

sessionはサーバー側に保存、覚えられる情報に制限はない、簡単に盗み見られない

cookieとは

cookieはユーザー側(ブラウザ)にメモ書きレベルの情報を保存するための仕組み

cokkieを使ったwebの流れはこうなる。

  1. ユーザーがwebページにアクセスする

  2. リクエストされた画面を返す。この時①でユーザーから送られてきた情報を元にwebページはプログラミングで指示された処理をしてユーザーに画面を返す
    (例:「あなたはこのサイトに○○回訪問したよ」とか「指示された色に画面の色を変えたよー」とか。この時にcookieというメモ書きも一緒につけている例でいうと「○○回」「○○色」など)

  3. webページからついてきたメモをユーザーが保存します。次のページにアクセスする時にこのメモ書きの情報も一緒につけて、webページに要求します。

  4. 本来のHTTP通信であれば情報は引き継がれないが、メモ情報がついているのでそのメモ情報を元にユーザーに適した画面が表示される。

このcookieはユーザー(ブラウザ)が保存しているのでデベロッパーツールで編集や削除ができる。つまりメモの内容を見れるし、書き換えられるし、捨てる事もできる。
そのため、大事な情報はcookieで扱ってはいけない(ログイン情報とか)

Sessionとは

Sessionは情報をwebページ側で保存するのでデベロッパーツールで触る事はできない。
つまり安全である。

ただしwebサーバー上で保存しているので、アクセスしてきた人がAさんなのか、Bさんなのか、Cさんなのか識別しないといけない。(これはブラウザが変わっても同様の事が言える)

ユーザー(ブラウザ)とwebページで保存されているsessionを紐づけてあげる必要がある
sessionではそのためだけにcookieを使う。

sessionの流れは

  1. ユーザーがwebページにアクセスする
  2. リクエストされた画面を返す。①でユーザーから送られてきた情報を元にwebページはプログラミングで指示された処理をしてユーザーに画面を返す。そして情報をサーバー側で保持&Session情報をcookieとしてセットするよう送ってくる。
  3. ユーザーはsession情報をセットする。次のページにアクセスする時に、このセッション情報を送信してwebページに要求します。

  4. 本来のHTTP通信であれば情報は引き継がれないが、session情報がついているのでその情報を元にユーザーに適した画面が表示される。

ここまでのまとめ

・通常HTTPではページ毎に情報を引き継げない
・そのためcookieやsessionを使って情報を継続しないといけない
・sessionとcookieは違いがある
・ログイン機能を実装するにはsessionとcookieを使う必要がある

参考リンク

https://wa3.i-3-i.info/word1841.html
https://wa3.i-3-i.info/word1791.html

Webpicks
ドットインストール
railsチュートリアル

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

Rails エラーメッセージの日本語化

はじめに

オリジナルアプリを制作しています。完成後は知人に使ってもらい、感想をフィードバックする予定です。その知人は日本人なので、エラー文は日本語にした方がより親切だと思いました。そのため、エラーメッセージの日本語化を行いました。

目次

1.日本語の言語設定
2.Gemfileの追加
3.日本語化ファイルの作成
4.追加で日本語翻訳文言の設定

1.日本語の言語設定

config.i18n.default_locale = :jaを追加する。この「ja」は日本語を表す。

config/application.rb
module Pictweet
 class Application < Rails::Application
   # Initialize configuration defaults for originally generated Rails version.
   config.load_defaults 6.0

   # 日本語の言語設定
   config.i18n.default_locale = :ja
  # 省略
 end
end

2.Gemfileの追加
Gemfileに追加後、忘れずにターミナルでbundle installを実行、サーバーを再起動する。

公式 GitHub

Gemfile
gem 'rails-i18n'

3.日本語化ファイルの作成

様々な言語に対応する言語ファイルをlocale(ロケール)ディレクトリに作成する。ここでは日本語化ファイルを作成する。ファイルの中身は下記リンク先の内容をコピペする。これにより、コピペした内容に該当するエラー文は日本語化される。

公式 devise-i18n

config/locales/devise.ja.yml
ja:
  activerecord:
    attributes:
      user:
        confirmation_sent_at: パスワード確認送信時刻
        confirmation_token: パスワード確認用トークン
#------------------省略------------------------------
      not_saved:
        one: エラーが発生したため %{resource} は保存されませんでした。
        other: "%{count} 件のエラーが発生したため %{resource} は保存されませんでした。"

4.追加で日本語翻訳文言の設定

最後に日本語文の追加設定を行う。同じくlocalesディレクトリにja.ymlファイルを作成する。追加したい日本語とその英語を記述することで、カスタマイズできる。

config/locales/ja.yml
ja:
 activerecord:
   attributes:
     user:
       nickname: ニックネーム
     desk:
       title: 画像タイトル
       image: 投稿画像
     suggestion:
       place: 掃除場所
       period_cleaning: 清掃期間
       last_cleaned_date: 最後に清掃した日

以下参考画面

image.png

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

スコープの概念

1.スコープの概念 ~初心者編~

 定義した変数が使える範囲をスコープと表現します。メソッドによって若干異なるので代表的なものをまとめます。

 def, time, each メソッド

def, time, each メソッドの中で定義した変数はメソッドの外で使うことはできない。
一方で、メソッドの外で定義された変数はtime, eachメソッドは条件なく使えます。しかし。def メソッドに関しては引数が必要となります。
  メソッド.png

2.メソッドの中で定義した変数を外で使うには?

#defメソッドの中で定義した変数はそのままではメソッドの外では使えない....
1   def rename(name)
2     name = "{name}さん"
3   end
4     name = "yamada"
5     rename(name)
6   puts name                
 => yamada   #yamadaさんにしたい...
#2行目の変数nameはメソッドの中にあるため6行目のputsで出力されずに4行目のnameが出力される。
#defメソッドの中で定義した変数をメソッドの外の変数に代入して使う!
1   def rename(name)
2     name = "{name}さん"
3   end
4     name = "yamada"
5     name = rename(name)
6   puts name                
 => yamadaさん
#defメソッドを5行目で引数を用いて呼び出して、6行目のputsで出力する。

3.さいごに

拙い文章で分かりにくい点があるかもしれませんが、何か誤りなどご指摘ありましたらコメントお願いします。

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

マルチスケールシミュレーション特論:第 9 回をまとめてみた

ruby-2.5.5p157

assert_equal について

今後の授業の方針として、テスト駆動開発を行っていく。assert equal とはequal かどうかを確かめる(assert)関数であり、この関数を使って授業を進める

colorize

  • 始める時に colorize というライブラリがないと怒られたなら以下のコマンドを行う
sudo gem install colorize 
  • この際に関学のプロキシーでライブラリのインストールができない人は以下のコマンドを使用する

    sudo gem install colorize -r -p http://proxy.ksc.kwansei.ac.jp:8080
    

gem とは?

gem は ruby の library を管理するシステムの事

richer output

assert equal という関数の出力をより rich な物にしてみた。具体的には、以下の改良を行っている。

  • どうなったかを記述
  • 引数でとってきた,expected, result の値をそのまま出力
require 'colorize'

def assert_equal(expected, result)
  if expected == result
    print "#{expected} " + "#{result} " +"succeeded in assert_equal.\n".green
  else
    print "#{expected} " + "#{result} " +"failed in assert_equal.\n".red
  end
end

assert_equal(1, 1)
assert_equal(1, 2)

正規表現について

正規表現(regular expression)

正規表現は文字情報を取り出す便利ツールである。しかしいきなり使用するのはハードルが高い。Rubular で練習を行うのが良い。

Rubular

使い方

  • Your regular expression

    • 正規表現 を書く。その結果として、test string から正規表現を適応した結果が出力される
  • Your test string

    • テスト用の文章を書く

Regex quick reference

簡単な正規表現の使い方が示されている


  • source ~/Downloads/git/grad_members_20f/members/taiseiyo/memos/class9.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】お気に入り機能の実装手順を公開!!【簡単にできます】

あおい(https://twitter.com/aoi_programming )です。
Ruby on Railsでお気に入り機能を実装したので手順を公開します。

実装したい機能

  • お気に入り登録・解除できること
  • 未ログイン時にはお気に入りできない
  • 未ログイン時にはお気に入り一覧ページにアクセスできない
  • お気に入りボタンをクリックするとAjax通信でデータ保存ができる

1. ブランチ作成

git checkout -b favorites

2. Favoriteモデルの作成

% rails g model Favorite user_id:integer post_id:integer

3. migrationファイルの編集

def change
    create_table :favorites do |t|
      t.integer :user_id
      t.integer :post_id
      t.timestamps
    end
    add_index :favorites, [:user_id, :post_id], unique: true  # 追記
  end

データベースに反映させます。

% rails db:migrate

4. 関連付け

favorite.rb
class Favorite < ApplicationRecord
  belongs_to :user
  belongs_to :post
end
user.rb
has_many :favorites, dependent: :destroy
post.rb
has_many :favorites, dependent: :destroy

5. バリデーションの追加

favorite.rb
belongs_to :user
belongs_to :post
validates :user_id, presence: true  # 追記
validates :post_id, presence: true  # 追記

6. コントローラー作成

% rails generate controller Favorites

7. コントローラー編集

favorites_controller.rb
class FavoritesController < ApplicationController
 before_action :authenticate_user!

 def create
  @post = Post.find(params[:post_id])
    @user = @post.user
    current_user.favorite(@post)
    respond_to do |format|
      format.html { redirect_to request.referrer || root_url }
      format.js
    end
 end

 def destroy
  @post = Post.find(params[:post_id])
    current_user.favorites.find_by(post_id: @post.id).destroy
    respond_to do |format|
      format.html { redirect_to request.referrer || root_url }
      format.js
    end
 end

end

8. ルート編集

route.rb
 post   "favorites/:post_id/create"  => "favorites#create"
 delete "favorites/:post_id/destroy" => "favorites#destroy"
end

9. お気に入り登録のメソッドを作成

user.rb
  def favorite(post)
    Favorite.create!(user_id: id, post_id: post.id)
  end

  # 投稿のお気に入り解除する
  def unfavorite(post)
    Favorite.find_by(user_id: id, post_id: post.id).destroy
  end

  # 現在のユーザーがお気に入り登録してたらtrueを返す
  def favorite?(post)
    !Favorite.find_by(user_id: id, post_id: psot.id).nil?
  end

10. Ajaxの実装

  • app/views/favorites/create.js.erb (新規)
  • app/views/favorites/destroy.js.erb (新規)
create.js.erb
$("#favorite-<%= @post.id %>").html("<%= escape_javascript(render('users/unfavorite')) %>");
destroy.js.erb
$("#favorite-<%= @post.id %>").html("<%= escape_javascript(render('users/favorite')) %>");

11. お気に入りフォームの作成

app/views/users/_favorite_form.html.erb (新規)

app/views/users/_favorite_form.html.erb
<div id="favorite-<%= @post.id %>">
  <% if !current_user.nil? && current_user.favorite?(@post) %>
    <%= render 'users/unfavorite' %>
  <% else %>
    <%= render 'users/favorite' %>
  <% end %>
</div>

12. それぞれのボタンを作成

app/views/users/_favorite.html.erb (新規)

app/views/users/_favorite.html.erb
<%= link_to "/favorites/#{@post.id}/create", method: :post, class: 'like', remote: true do %>
  <div class="favorite">お気に入りに登録</div>
<% end %>

app/views/users/_unfavorite.html.erb (新規)

app/views/users/_unfavorite.html.erb
<%= link_to "/favorites/#{@post.id}/destroy", method: :post, class: 'like', remote: true do %>
  <div class="unfavorite">お気に入り登録済み</div>
<% end %>

13. ボタンによって色を変更

app/assets/stylesheets/~.scss
like {
 color: gray;
}

unlike {
 color: red;
}

14. 表示させたいページに追加

~.erb
<%= render 'users/favorite_form' %>

以上です!
参考になった方はLGTMお願いします?‍♂️

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

マルチスケールシミュレーション特論 第9回

colorize

gem install colorize を実行して、colorizeを使うとターミナルの出力に色付けできる

例えば、以下を実行すると、'hello'が青色で表示される

require 'colorize'

puts 'hello'.blue

ターミナルの標準出力はどうなっているだろうか?

以下を実行して、 xsel でクリップボードにコピーしてみた

require 'colorize'

def assert_equal(expected, result)
  if expected == result
    puts 'true'.green
  else
    puts 'false'.red
  end
end

assert_equal(1, 1)
assert_equal(1, 0)

クリップボードから貼り付けると、以下のようになりました

[0;32;49mtrue[0m
[0;31;49mfalse[0m

ref: https://github.com/fazibear/colorize

Interactive Ruby

irb を実行すると、Interactive Rubyが立ち上がる

ref: https://docs.ruby-lang.org/ja/latest/library/irb.html

正規表現

ref: http://rubular.com/

#+qiita_id: daddygongon

から"daddygongon"を取り出す

ここでは以下のようにしていた

:\s*(.+)

こんなのでもいけるかな

#\+qiita_id:\s(.*)

やっぱり便利!!


  • source ~/doc/lecture/multi-scale/grad_members_20f/members/yukiue/docs/09.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

再帰関数【配列のとりうる組み合わせについて】

取り組んだ経緯

とある会社との面談の際に、再帰関数について質問されて、
まともに答えられず悔しかったので
タイトルに表記したアルゴリズム問題に挑戦して取り組んでみることにしました。

問題

  1. 数字の配列から数値を0〜n(配列数)個取った組み合わせを求める。
  2. 組み合わせの数字を合計し、その結果を重複なく出力する。

問題の具体例

  1. 数字の配列[1,2,3,4,5,6]がある
  2. 数字の配列を取りうるパターン(数式でnCr)を求める。
  3. nの最大値は6(配列の中身の数である)
  4. rの取り得る範囲は0~6

具体例の解き方

今回は、配列[1,2,3,4,5,6]から数値を0〜6個取った場合の合計数値の求め方を記載する

◆配列から数値を0個とる場合

_6 C_0                                                                \\

数値は取らないので、合計値としては「0」

◆配列から数値を1個とる場合の合計値

_6 C_1                                                                \\

結果は、[1,2,3,4,5,6]

◆配列から数値を2個とる場合の合計値

{_6 C_2} = (1 +  {_5 C_1}) + (2 + {_4 C_1}) + (3 + {_3 C_1}) + (4 + {_2 C_1}) + (5 + {_1 C_1})              \\

配列から2つ取得すれば良いので、
考え方として、まず1つ取る数値を固定すると
上記数式のように取得数を減らした数式が入れ子形式できる。

取得方法についてイメージがしにくいので下記に記載する。

(1 +  {_5 C_1}) の場合                                    

[1,2],[1,3],[1,4],[1,5],[1,6]という配列の組み合わせができる。
今回は合計した数値を求めたいので、結果は下記のようになる
[3,4,5,6,7]

これを1〜5を先約して取得するパターンを実施した場合の結果は、
[3,4,5,6,7,5,6,7,8,7,8,9,9,10,11]という風になる
最終的に重複を削除して
[3,4,5,6,6,8,9,10,11]となる。

◆配列から数値を3個以上n未満取得する場合の合計値

{_6 C_r} = (1 +  {_5 C_{r-1}}) + (2 + {_4 C_{r-1}}) + (3 + {_3 C_{r-1}}) + (4 + {_2 C_{r-1}})             \\

上記式でn=rとなった時点で、「切り出し数字+数式」が終了する。
r=3だと上記数式
r=4だと下記のようになり

{_6 C_4} = (1 +  {_5 C_{3}}) + (2 + {_4 C_{3}}) + (3 + {_3 C_{3}}) 
                                                \\

r=5だと下記のようになる。

{_6 C_5} = (1 +  {_5 C_{4}}) + (2 + {_4 C_{4}})                                                             
                                                \\

基本的に、入れ子構造となり、
r=5の場合で(1 + 5C4)の部分は下記となり、

(1 +  {_5 C_{4}}) = (1 + (2 + {_4 C_{3}})) + (1 + (3 + {_3 C_{3}}))                             \\

新規に出現した(1 + (2+4C3))の部分は下記となり、

(1 + (2 + {_4 C_{3}})) =  (1 + (2 + (3 + {_3 C_{2}}))) +  (1 + (2 + (4 + {_2 C_{2}})))                        \\

続いて、出現した(1+(2+(3+3C2)))の部分は下記となる。

(1 + (2 + (3 + {_3 C_{2}}))) = (1 + (2 + (3 + (4 + {_2 C_{1}})))) + (1 + (2 + (3 + (5 + {_1 C_{1}}))))                       \\

◆配列から数値をn個取得する場合の合計値
配列の合計値を取得すれば良いので
結果は、1 + 2 + 3 + 4 + 5 + 6 = 21

問題を解くプログラム

入れ子構造を取るようにすれば良いので、
例題での考え方を反映してプログラムをかけば下記の通りになる

combination.rb
class Array
  def combination_array(pick_numnCrrに相当))
   # 配列からゼロ個取る場合ゼロを返す
    return 0 if pick_num == 0
  # 配列から1個取る場合、配列の中身を1つずつ返す
    return map { |e| e } if pick_num == 1
    ret = []
  # 先行して取得する数値の取るインデックス番号の範囲を0〜(n(配列数)-r(ピック数))繰り返す
    (0..size(配列数) - pick_num).each do |i|
    #切り出す数値
      picked = self[i]
    #[1,2,3]の配列があったとして、self[0]で1を抜き出し、rest = [2,3]の残りの配列を求める式
      rest = self[i + 1..-1]
    #残りの配列に対して、再度combination_arrayメソッドを実行する。nCr= n-1Cr-1の形式
      rest.combination_array(pick_num - 1).each do |c|
        ret << picked + c
      end
    end
  #3C2 [1,2,3]があった場合、下記で[3,4,5]の最終結果を返す
    ret
  end
end

#今回用意する数値の配列をXと表記する
numbers = Array(X)
#結果を格納する配列を新規作成
answers = Array.new()

#配列を取得する範囲を0個〜n個未満まで上記作成のコンビネーションメソッドを繰り返す。
for pick in 0..n-1
  #上記メソッドの最終結果をanswers配列に格納
    answers << numbers.combination_array(pick)
end

#配列を全て取得した場合=配列の中身の合計値を求める
answers << numbers.inject(:+)

#配列の結果は[[1,2,3],[3,5,6,7],[7,9,10,32,44]]のようになっているのを
#flattenメソッドで[1,2,3,5,6,7,7,9,10,32,44]に修正
answers.flatten!

#uniqメソッドで重複している数値をなくす。
answers.uniq!

#答えの出力。
puts answers

感想

再帰関数について、なんとなくわかったような状態になった気がします。
今後は、アルゴリズム問題に挑みつつ再帰関数の理解を深めたいと思います。

また、課題としては、負荷の少ない記述方法を考えて、
大規模演算の処理ができるようにすることだと考えております。

参考

https://qiita.com/kotaroooo0/items/8c9adaa6a4b72f8ef8f3

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

herokuで環境変数を設定する際の注意点

この投稿について

ポートフォリを作成の際にGoogle認証を取り入れたいと思い、ユーザー登録や、ログインを行ってくれる"Omniauth"というGemを導入しました。
ところが、環境変数に設定しようとした際に、うまく設定できなかったため、どんな理由でエラーが起きて、どのように解消できたのかを記事にしています。

herokuに環境変数を設定するとき

heroku config:set

というコマンドを使用します

実際には、

heroku config:set GOOGLE_CLIENT_ID="~~~~~"

このようなコマンドで追加します。
このとき、 『=』と『""』の間にスペースを入れるとエラーが発生してしまいます。

heroku config:set GOOGLE_CLIENT_ID = "~~~"

このようにしてしまうとエラーが発生してしまうので、設定できませんでした。

なので、まとめると環境変数に設定するときは、無駄なスペース入れず、コピペして貼ってしまうことをお勧めします。

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

タダ飲みコーヒー

[Ruby] タダ飲みコーヒー

問題

・コーヒーをお買い上げした際に、次のお買い上げの値段を更に P% off!
・毎回の値下げにおいて小数点以下切り捨て

あなたは値下げが累積する事に目をつけました。
コーヒーを何回も飲んでいれば、タダでコーヒーを飲めるようになるのです。
タダで頼みたいあなたは、何円払えば以後タダで注文できるのか計算したくなりました。
実際にプログラムを書いて計算してみましょう。

入力される値

入力は以下のフォーマットで与えられます。

X P
・コーヒーの価格を示す整数 X と 割引き率を示す整数 P が、この順に半角スペース区切りで与えられます。
・入力は 1 行となり、末尾に改行が 1 つ入ります。

期待する出力

以後タダで注文するのに必要な金額を出力してください。

入力例1

300 50

出力例1

596

入力例2

1000 99

出力例2

1010

私の答え

a,b = gets.split(' ').map(&:to_i)
s = 100 - b
int = a
while a.floor > 0 
    a = (a*s/100).floor
    int += a
end

puts int

このコードはほぼ丸パクリしたのですがなるほどという感じです。while文の中身は説明できませんが、a変数(floor)が0になるまで繰り返しているのでしょう。繰り返し文が苦手だという事が分かりました。

以上!悔しい

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

Rubyのお勉強<変数とMethod>

変数

受けとった引数ARGV[0]を適当な変数に代入して出力せよ.

前回もそれっぽいことした気が…

name = ARGV[0]
puts name       #puts "#{name}"でも可

$ ruby name.rb Dora
Dora

RubyではPython同様,変数の型宣言は必要なくて,文脈に合わせて適当に定められる(楽〜).

数字の計算がしたいなら,

num = ARGV[0].to_i
sum = num + num
puts sum
$ ruby name.rb 1
2

てなこともできるみたい.

Method

function(関数)やprocedure(手順)などのまとまりはmethodを定義することになる.

例えば,

def name(moji1,moji2)
    puts "#{moji1} #{moji2}"
end

hoo1 = "hello"
hoo2 = "world"
name(hoo1,hoo2)
$ ruby def.rb
hello world

キーボードから文字を受け取りたかったら,

def name(moji1,moji2)
    puts "#{moji1} #{moji2}."
end

hoo1 = ARGV[0]
hoo2 = ARGV[1]
name(hoo1,hoo2)
$ ruby def.rb hello world
hello world.

詳しくは前回の記事を見るのじゃ〜.Rubyのお勉強<入出力編>

締め

今回は変数とmethodについて学んだ.

次回,条件処理<Ifとか>


  • source ~/school/multi/my_ruby/grad_members_20f/members/evendemiaire/post/var_met.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyのお勉強<入出力編>

Rubyのお勉強<入出力編>

出力編

とりあえず,出力させますか.

まぁ,おなじみ"print("Hello World")"ですか?

print("Hello World")
$ ruby print.rb
Hello World$

改行されない...

Rubyの出力方法はいろいろある.

method
print 引数に指定した値を出力
puts 引数に指定した値を出力(自動改行)
p 値と共に型情報を出力(debugに使える)
printf 書式を指定して出力(Cと同じ)

他にも,"pp"とか"sprintf"とかあるみたい.

"puts"を試してみる.

puts("Hello World")
$ ruby puts.rb
Hello World
$ 

改行されている.

調べていると,

print "Hello World"
puts "Hello World"

と全員書いている...,python2系の書き方でも大丈夫みたい(むしろそっちが主流?).

入力編

次は入力編です.

入力方法を調べてみる.

method
ARGV[引数番号] ruby code.rb 引数
gets() キーボードから取得(文字列)
gets.to_i() キーボードから取得(整数)
gets.to_f() キーボードから取得(浮動小数点)

"gets()"は丸括弧あってもなくても大丈夫

実際に使ってみる.

  • ARGV[]
puts "Hello #{ARGV[0]}"
print "#{ARGV[1]}#{ARGV[2]}\n"
$ ruby ARGV.rb World 今日は 水曜日
Hello World
今日は水曜日 
  • gets()
moji = gets()
puts "moji:#{moji}"
$ ruby gets.rb
Hello World
moji:Hello World

とまあ,いろいろ方法はある.

ちなみに,"ARGV[]"と"gets()"両方では駄目みたい.

puts "Hello #{ARGV[0]}"
moji = gets()
puts "moji:#{moji}"
$ ruby code.rb world
Hello world
Traceback (most recent call last):
    2: from print.rb:2:in `<main>'
    1: from print.rb:2:in `gets'
print.rb:2:in `gets': No such file or directory @ rb_sysopen - world (Errno::ENOENT)

とかなんとか.

出力をtxtなどに保存したい場合

$ ruby gets.rb > out.txt
Hello World
$ cat code.txt
Hello World

出力を別ファイルに保存できている(当然ARGVでも可能).

締め

基礎の入出力についてお勉強.

次回,変数とかmethodについて


  • source ~/school/multi/my_ruby/grad_members_20f/members/evendemiaire/post/ruby_put.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyのお勉強<制御構造_Ifとか>

bundler

Rubyの制御構造に入る前に...

講義資料にもあるが,bundlerがなんとかかんとか...

説明できるほどはわからないので,詳しくは講義資料を見てください.

if-elsif-else-end

Rubyリファレンスマニュアルでif文の文法は,

if  [then]
   ...
[elsif  [then]
   ... ]
...
[else
   ... ]
end

と記述されている.

閏年判定(単年)

もし閏年なら"true"を返すようにするには,

p year = ARGV[0].to_i

if year % 4 == 0
  p true
end

ほいで,

$ ruby check_leap.rb 2004
2004
true

となる.

では,閏年以外なら"false"を返すようにするなら,

p year = ARGV[0].to_i
if year%4 == 0
  p true
else
  p false
end

ほいで,

$ ruby check_leap.rb 1999
1999
false

となる.

閏年の条件は4年に一度だけじゃないみたい,

  • 西暦年号が4で割り切れる年をうるう年とする.
  • 西暦年号が100で割り切れて400で割り切れない年は平年とする.

では,その条件も含めると,

p year = ARGV[0].to_i
if year%400 == 0
  p true
elsif year%100 == 0
  p false
elsif year%4 == 0
  p true
else
  p false
end

ほいで,

$ ruby check_leap.rb 1900
1900
false
$ ruby check_leap.rb 2000
2000
true

とな.

閏年判定(複数年)

複数年を判定するときに何度も打つのが面倒なら,配列にしてloop回すだけ.

[2004,1999,1900,2000].each do |year|
  p year
  if year%400 == 0
    p true
  elsif year%100 == 0
    p false
  elsif year % 4 == 0
    p true
  else
    p false
  end
end

ほいで,

$ ruby check_leap_year.rb
2004
true
1999
false
1900
false
2000
true

となる.

判定部分をmethodにしますか,

def leap?(year)                                                                 
  if year % 400 ==0                                                             
    p true                                                                      
  elsif year % 100 ==0                                                          
    p false                                                                     
  elsif year % 4 == 0                                                           
    p true                                                                      
  else                                                                          
    p false                                                                     
  end                                                                           
end                                                                             

[2004,1999,1900,2000].each do |year|                                            
  p year                                                                        
  leap?(year)                                                                   
end 

OK.

case

Rubyリファレンスマニュアルのif文の下の方に,caseについて載っている.

caseを使って,より美しくするなら,

def leap?(year)
  case
  when year % 400 ==0 ; p true
  when year % 100 ==0 ; p false
  when year % 4 ==0 ;   p true
  else ;                p false
  end
end

[2004,1999,1900,2000].each do |year|                                            
  p year                                                                        
  leap?(year)                                                                   
end 

てな感じにもできるみたい.

締め

今回は条件処理について学んだ.

次回,Rakeで自動化&Export


  • source ~/school/multi/my_ruby/grad_members_20f/members/evendemiaire/post/if_case.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rakeで自動化&Export

Rake

Rakeってなんぞや

わからないので,とりあえず調べて,有志の力を借りる.

  • RakeはMakeによく似た機能を持つRubyで書かれたシンプルなビルドツール
  • makeコマンドのRubyでの代替,Rakefileにtaskを記述していく

とかなんとか.

詳しくは,

では実際にさわってみる.

講義資料を見ると,

前回のbundlerを少し思い出してください.

$ rake install:local

とすると自動的にhogehogeがinstallされましたよね.

と記述されている...私は上手くいかなかったが,まあ,とりあえず進めよう.

上述したが,Rakefileにtaskを記入していくと,

$ emacs -nw Rakefile

で,Rakefile内に

task :default do
  system 'rake -T'
  exit
end

desc 'hello NAME'
task :hello do
  name = ARGV[1]
  puts "Hello #{name}!"
  exit
end

を記述.

$ rake
rake hello  # hello NAME

rakeって打つと,Rakefileに記述したtaskを教えてくれる.

なぜかって言うと,

task :default do
  system 'rake -T'
  exit
end

で,rakeが引数なしで入力されたときのtaskを書いてるから.すなわち,rake -Tが走ってる.

じゃあ,他のtaskを動かすには?

$ rake hello World
Hello World!

これだけ.

こんな感じで,taskをこなしてくれる.

では早速taskを記述!,とその前に,

desc 'hello NAME'
task :hello do
  name = ARGV[1]
  puts "Hello #{name}!"
  exit
end

この部分,"ARGV[1]"になってる.

でもこれが正しい.*注意

System Call

  • Rakefileに記述することの多い関数はsystem (systemコマンドを起動する関数).

コマンドもtaskにできるみたい.以前の記事でシェルスクリプトの紹介をしたが,Rubyでもこんなのがあるとは...

例えば,gitに共有するときの流れで,

  • git pull origin main
  • git add -A
  • git commit -m "hogehoge"
  • git push origin main

って一個ずつ打ってたわけだけど,

task :default do
  system 'rake -T'
  exit
end

desc 'hello NAME'
task :hello do
  name = ARGV[1]
  puts "Hello #{name}!"
  exit
end

desc 'git pull push'
task :push do
  p comm = "git pull origin main"
  system comm
  p comm = "git add -A"
  system comm
  p comm = "git commit -m #{ARGV[1]}"
  system comm
  p comm = "git push origin main"
  system comm
  exit
end

と記述することで,

$ rake push hogehoge
git pull origin main   
   ・
   ・
   ・

ってな感じで,pullからpushまでやってくれる.便利ですなぁ...

"p comm"しておく(putsでもいい)と,コマンドを出力するからわかりやすいよ.

desc 'git pull push2'
task :push2 do
  ["git add -A",
   "git commit -m \'hoge\'",
   "git pull origin main",
   "git push origin main"].each do |comm|
    p comm
    system comm
  end
  exit
end

とも記述できる.

コマンドをcolorizeしたいなら,

require 'colorize'
desc 'git pull push2'
task :push2_color do
  ["git add -A",
   "git commit -m \'hoge\'",
   "git pull origin main",
   "git push origin main"].each do |comm|
    puts comm.green
    system comm
  end
  exit
end

で,コマンドがgreenにcolorizeされる.

Command_Line

systemで使える外部のコマンドは動作が遅いので,より高速で動かせるようにrubyの組み込み関数に用意されているとのこと.

例えば,皆のREADME.orgがあるかを見たいとき,

require 'command_line/global'                                                                                                                         
desc 'make list'                                                                                                                                      
task :mk_list do                                                                                                                                      
  #system 'ls -1 ../**/README.org'                                                                                                                    
  target = "../**/README.org"                                                                                                                         
  Dir.glob(target).each do |file|                                                                                                                     
    p file                                                                                                                                            
  end                                                                                                                                                 
  exit                                                                                                                                                
end   

てな感じで,配列に入った情報を列挙してるみたい.

コマンドを組み込んで,qiita_idを取り出すなら,

require 'command_line/global'                                                                                                                         
desc 'make list'                                                                                                                                      
task :mk_list do                                                                                                                                      
  #system 'ls -1 ../**/README.org'                                                                                                                    
  target = "../**/README.org"                                                                                                                         
  Dir.glob(target).each do |file|                                                                                                                     
    p file                                                                                                                                            
    res = command_line "grep qiita_id #{file}"                                                                                                        
    p res.stdout                                                                                                                                      
  end                                                                                                                                                 
  exit                                                                                                                                                
end   

標準出力(stdout)などを取り出すのはcommand_lineというgemを使うらしいので,

$ sudo gem install command_line

をしましょう.

他にもいろいろできるみたいだけど,それは次回以降.

Export

Org-Modeで記述した記事はHtmlとかにもexportできる.

$ emacs -nw hoge.org
c-c c-e ho # export as [h]tml, [o]ut
c-c c-e ll # export [l]atex, [l]atex 

これでhtmlやlatexにexportできる.

便利ですなぁ...

締め

今回はrakeを使ったtask管理について学んだ.

次回,執筆時には知らない.


  • source ~/school/multi/my_ruby/grad_members_20f/members/evendemiaire/post/rake.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

test

test

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

【Ruby on Rails5】フラッシュメッセージをいい感じに表示させる。

環境

Ruby 2.5.7
Rails 5.2.4

前提

形を問わずフラッシュメッセージの表示が確認できる状態。
turbolinksは切っています。オンの状態で動作確認はしていません。

gem

gem 'devise'

経緯

タイトルに「いい感じ」と表現しましたが、他に言葉が見つかりませんでした・・・笑
ユーザーが何かアクションを起こした時に、それをお知らせする方法としてページ内へのフラッシュメッセージの埋め込みがありますが、それをただ表示させるのは物足りないと感じて今回の形を実装してみました。(AbemaTVのアプリから発想を得ました笑)

目標

ログイン・ログアウトをメインに、アクションを起こすごとに下の方からニュッっとお知らせが一定時間出てくる。
ezgif.com-gif-maker (2).gif

手順

1.お知らせ窓layouts/_flash_window.html.erbを作成する
2.cssでお知らせ窓をいい感じにする
3.コントローラーでflash[:notice]を作成する
4.お知らせ窓専用のjsファイルassets/javascripts/flash_window.jsを作成する

1.お知らせ窓layouts/_flash_window.html.erbを作成する

お知らせ窓views/layouts/_flash_window.html.erbを部分テンプレートとして作成します。
今回は簡単なのでapplication.html.erbに直接書いてもいいのですが、窓の中身が今後複雑になったり、基本的に見えていない部分なので最初から分けておいてもいいかと思います。

layouts/_flash_window.html.erb
<div class="flash-window hidden">
  <p><%= notice %><span class="flash-window--delete">X</span></p>
</div>

CSSクラス名にhidddenを付けていますが、これがついているときは非表示、これがjs(toggle)で外れると表示される仕組みです。
2行目はnoticeメッセージ(フラッシュメッセージ)の後ろにX(バツボタン)を表示させることでお知らせ窓が自動で閉じる前に任意で閉じることができるようにしていきます。

ここで作成した部分テンプレートをapplication.html.erbに反映させていきます。

application.html.erb
...

<main>
  <div class="main">
    <%= yield %>
    <%= render partial: 'layouts/flash_window' %>
  </div>
</main>

...

<main>タグの下に<div>を挟んでいる理由としては、お知らせ窓の位置を<main>直下の<div>に基点にするためです。
<main>タグを起点にしても良かったのですが、後述するcssにmainを指定してしまうと思わぬところで依存関係ができてしまいそうでしたので、今回はお知らせ窓用に<div>を作った形です。
また、<main>タグの中に書くことで、どこのページでもお知らせ窓が表示されるようになります。

2.cssでお知らせ窓をいい感じにする

作成したお知らせ窓をcssでいい感じにデザインします。
これは一例なので、お好きにカスタマイズしてください。
(scssで記述していきます。)

assets/stylesheets/application.scss
main {
  .main {
    .flash-window  {
      // 窓の形、色、文字の形、色
      height: 50px;
      padding: 10px;
      color: white;
      background: black;
      border-radius: 10px;

      // 中のp要素(メッセージ)を上下左右中央揃え
      display: flex;
      justify-content: center;
      align-items: center;

      // 表示・非表示にかける時間
      transition: 0.5s 0s ease;

      // 窓の位置調整
      position: fixed;
      bottom: 40px;
      left: 0;
      right: 0;
      margin: auto;

      p {
        color: white;

        span {
          margin-left: 1vw;
          color: white;

          &:hover {
            cursor: pointer;
          }
        }
      }

      // 窓が非表示の時の状態
      &.hidden {
        opacity: 0;
        bottom: -50px;
      }
    }
  }
}

@media (min-width: PC, タブレット共通) {
  main {
    .main {
      .flash-window  {
        // 大画面の時は窓の横幅は固定
        width: 500px;
      }
    }
  }
}
@media (min-width: スマホ) {
  main {
    .main {
      .flash-window  {
        // 小画面の時は画面サイズの90%で可変
        width: 90%;
        p {
          font-size: 4vw;
        }
      }
    }
  }
}

CSSセレクタのhiddenが付くと"非表示"と言いましたが、実際はbottom: -50px;で要素が画面外に隠れているだけです。
さらにopacity: 0;も指定しているので、hiddenの時は完全に透明になっている状態です。
なので画面内に現れる時は透明の状態から具現化し、画面外にいく(非表示になる)時は段々と透明になっていきます。

3.コントローラーでflash[:notice]を作成する

今回はredirect後のフラッシュメッセージ(ログイン後TOPページにリダイレクトと同時にお知らせが出現など)を想定しています。
render後のフラッシュメッセージ(バリデーションエラーによるrender&エラーメッセージ)については最後に触れます。

users_controller.rb
...

def update
  if current_user.update(user_params)
    flash[:notice] = '会員情報を更新しました。'
    redirect_to user_path(current_user)
  else
    render :edit
  end
end

...

ここでは会員情報の更新時に会員詳細にリダイレクト&フラッシュメッセージの表示をする記述をしましたが、必要なところがあれば適宜行ってください。
基本的にはflash[:notice] = '内容'でnoticeキーにフラッシュメッセージを格納したあとに、redirect_toで任意のページに遷移することで、遷移先で先ほど格納したフラッシュメッセージが表示されるようになります。
ちなみに、deviseを使った新規登録・ログイン・ログアウトの処理はdevise内部で自動的にflash[:notice] = 'ログインしました。'等が格納されるので、自分で記述する必要はありません。

4.お知らせ窓専用のjsファイルassets/javascripts/flash_window.jsを作成する

最後に専用のjsファイルを作成していきます。(既存のapplication.jsから切り分ける理由は後述します。)

flash_window.js
addEventListener('load', ()=> {
  const flashWindow = document.getElementsByClassName("flash-window")[0];
  flashWindowToggle();
  setTimeout(flashWindowToggle, 3000)
  document.getElementsByClassName("flash-window--delete")[0].addEventListener('click', flashWindowToggle);
  function flashWindowToggle() {
    flashWindow.classList.toggle("hidden");
  }
});

addEventListenerのイベントハンドラにloadを指定しています。
これはページの読み込みが全て完了した時点でイベントを発生させないと、ページが読み込み中(白紙)の時に先にお知らせウィンドウだけが表示されてしまうからです。
toggleメソッドを使ったCSSクラスのhiddenの追加・削除は関数化しています。
この関数が呼び出されると、hiddenが削除されお知らせ窓が表示されます。
その後、この関数が3000ms(3秒)後に自動的に呼び出されるか、X(バツボタン)がクリックされると、hiddenが追加されます。

そして、このjsファイルはflash[:notice]に値が入っている時だけ発火させたいので、設定を少し変えていきます。

application.js
...

//= stub flash_window #ここを追加
//= require_tree .

...

application.jsの冒頭の読み込み設定で//= stub flash_windowを書くことで、flash_window.jsがプリコンパイルで結合されるjsファイルから除外されます。
しかし、この状態ではflash_window.js自体がプリコンパイルされないことになってしまうので、config/initializers/assets.rbに以下を追記します。

config/initializers/assets.rb
...

# 以下を追加
Rails.application.config.assets.precompile += %w( flash_window.js )

...

これにより、flash_window.jsのプリコンパイルを明示できます。

しかし、このままではプリコンパイルはされてもapplication.jsには結合されていないので読み込まれません。
そこでapplication.html.erb<head>タグ内にflash_window.jsの読み込みタグを直接追加する必要があります。

layouts/application.html.erb
<head>

...

<%= javascript_include_tag 'application', defer: true %>

# 以下を追加
<% if flash[:notice] %>
  <%= javascript_include_tag 'flash_window' %>
<% end %>

...

</head>

このようにapplication.jsからflash_window.jsを切り離すことでif文でflash[:notice]がある時だけflash_window.jsが読み込まれるという動作を作ることができました。
defer: trueオプションを付けていない理由は、今回はflash_window.js内のイベントがページ読み込み完了後であることをaddEventListener('load', ()=>{...});という形で直接書いているためです。
このファイルに他のイベントを書く場合はdefer: trueが必要になるかも知れませんので、そこは適宜お願いします。

以上で、目的の動作が達成されます。

ここからは番外編です!

番外編

番外編その1.不要なフラッシュメッセージが表示される場合

deviseのcreate, update, destroyアクションにはほとんどの場合フラッシュメッセージflash[:notice]が格納されているため、不要な場面でも窓が出てきてしまうことがあります。
その場合の対処法を書いていきます。

application_controller.rb
...

protected

...

# deviseで作成されたflashのリセット
def delete_devise_flash_messages
  flash[:notice] = nil
end

application_controller.rbでこのメソッドを定義し、不要なフラッシュメッセージが表示されるコントローラー内でbefore_action :delete_devise_flash_messagesまたはafter_action :delete_devise_flash_messagesを書くとフラッシュメッセージflash[:notice]が表示されなくなります。(:notice以外のキーは残ります。)

私の場合は新規登録の場面で利用しています。

入力内容の確認画面から認証メールの送信アクション、そして"メールを確認してください。"というページに遷移するのですが、そのページ内でメールについて詳しい説明を書いているのに、改めてフラッシュメッセージで"メールを確認してください"は重複するので不要です。
そこで下記のような記述をしています。

registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController

  before_action :delete_devise_flash_messages, only: %i[email_notice]

  # 入力された内容を確認するページ
  def confirm
  end

  # 認証メールが送信されたことをお知らせするページ
  def email_notice
  end

  # 登録が完了したことをお知らせするページ
  def complete
  end

end

これで認証メールの送信をお知らせするページemail_noticeアクションのみflash[:notice]のリセットが働いてフラッシュメッセージが表示されなくなります。

番外編その2.エラーのフラッシュメッセージに対応する方法

ここまではflash[:notice]に格納されたメッセージのみを取り扱ってきましたが、バリデーションで発生したエラーなども表示したい場合はflash[:alert]を追加していきます。
さらにフラッシュメッセージごとに挙動を変更したい場合はflash[:error]flash[:success]なんかも自作されてみるといいでしょう。
変更箇所は主に2カ所です。

layouts/application.html.erb
...

<%= javascript_include_tag 'application', defer: true %>

<%# if文に || flash[:alert]を追加 %>
<% if flash[:notice] || flash[:alert] %>
  <%= javascript_include_tag 'flash_window' %>
<% end %>

...

if文に|| flash[:alert]キーを追加します。
これにより:noticeキーもしくは:alertキーに値があるとflash_window.jsが発火するようになります。

次に部分テンプレート内にもalertを追加をしていきます。

layouts/_flash_window.html.erb
<div class="flash-window hidden">
  <% if flash[:notice] %>
    <p><%= notice %><span class="flash-window--delete">X</span></p>
  <% end %>
  <% if flash[:alert] %>
    <p><%= alert %><span class="flash-window--delete">X</span></p>
  <% end %>
</div>

部分テンプレートにもif文を書くことで表示や動作などの細かい部分を分けることができるようになります。

まとめ

今回ご紹介した物はベースとなると考えていますので、動作などはお好きなようにカスタムしていただければと思います^^
みなさまの参考になれば幸いです。

また、質問や解釈の違い、記述方法に違和感ありましたら、コメント等でご指摘いただけると幸いです。

私のTwitterでも毎日このようなテクニックや感想・考察を発信していますので、もしご興味があれば一度覗いてみてください(´ー`)
Twitter - @Masao_Sasaki_ae

最後まで読んでいただきありがとうございました!

参考サイト

より実用的な使い方については、私のGitHubに実際に使っているファイルを公開しているのでこちらも参考にしていただければと思います!
GitHub - MasaoSasaki/matchi

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

ファイルで定義したメソッドの一覧を取得する

methods = []
File.foreach("foo.rb") { |line|
  methods << line.chomp.gsub(/^\s*def\s/, '').gsub(/\(.+\)/, '') if /^\s*def\s/ =~ line
}

クラスメソッドとインスタンスメソッドを分けるのは以下でできそう。(未検証)

instance_methods = Foo.instance_methods(false) + Foo.private_instance_mathods(false)
class_methods = methods.map(&:to_sym) - instance_methods
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsのViewの共通化について

本投稿の目的

・Rails学習の議事録です。


学習に使った教材

Udemyの "はじめてのRuby on Rails入門-RubyとRailsを基礎から学びWebアプリケーションをネットに公開しよう" を教材として使用しました。


①viewの共通化

・全てのviewの共通箇所を1箇所に集約する方法
・集約ファイル (app/views/layouts/application.html.erb)

application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <%= csrf_meta_tags %>

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

  <body>
    <div class="container">
      <%= yield %>
    </div>
  </body>
</html>

【解説】
・html.erbファイル共通フォーマットがここに記載
・大抵は,htmlのheadのcss,js読み込みテンプレ文を記載する
・<%= yield %>の部分には,他のhtml.erbが読み込まれる

②partialについて

・共通部分を記載したファイルをpartialファイルという
・例えば,new と edit など共通のviewなどが該当する

【やり方①】
・パーシャルファイルを partial名.html.erb として保存
・(* " _
" から始めるのに注意)
・viewファイルの共通部分を削除

・下記を記載
・(*名前に" _ "を外すことに注意)

partialを呼び出す側のファイル名.html.erb
<% render partial '名前' %>

【やり方②】
・①のやり方はメンテナンス性が悪い
・(controllerのインスタンス変数に気付き辛い)

記載を下記に修正

partialを呼び出す側のファイル名.html.erb
<% render '名前', locals: {ローカル変数: インスタンス変数} %>

・これによりパーシャルのhtmlにはインスタンス変数ではなくローカル変数が渡される

_partialファイル名.html.erb
<%= form_for ローカル変数 do |f| %>
  共通の内容をここに記載
<% end %>

【やり方③】
・記載を下記に修正

partial導入側のファイル名.html.erb
<% render '名前', locals: {object: インスタンス変数} %>

・objectとすることで,パーシャル名と同様のローカル変数を作成
・これにインスタンス変数を渡す

【やり方④】
・記載を下記に修正

partial導入側のファイル名.html.erb
<% render インスタンス変数 %>

・これは partial名 = ローカル変数 時に使用可能
・インスタンス変数の@を除去した変数=ローカル変数=partial名

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

【Rails非同期処理】Sidekiq vs Resque vs Delayed Job 〜Active Jobとは〜

1. Active Jobとは

バックグラウンドで実行するジョブをRailsアプリケーションで動かすための共通インターフェイスです。
例えばメール送信やCSVアップロード等の時間がかかる重い処理はバックグラウンドで実行することが多いです。

Rails 4.2で導入された機能で、非同期処理を実装する際にまず検討する事が多いかと思います。

2. 共通インターフェイスって?

Active Jobが導入される以前のRailsバージョンの場合、非同期処理を実装するGemを使用していました。
代表的なものではDelayed JobSidekiqResqueがあります。

当然それぞれのGemで記述や機能が異なっていましたが、Active Jobはそうした違いを気にせずジョブを扱うことができるインフラ機能です。

Railsガイド Active Jobの目的によると、Active Jobには下記のようなメリットがあります。

  • Delayed JobとResqueなどのように、さまざまなジョブ実行機能のAPIの違いを気にせずにジョブフレームワーク機能やその他のgemを搭載することができる
  • バックエンドでのキューイング作業では、操作方法以外のことを気にせずに済む
  • ジョブ管理フレームワークを切り替える際にジョブを書き直さずに済む

かんたんなJobを実装

  • ruby 2.7.2
  • rails 6.0.3

この環境でとりあえずJobを実装してみます。

$ rails g job sample
      create  app/jobs/sample_job.rb
      create  app/jobs/application_job.rb

2つのファイルがapp/job/配下に生成されます。
application_job.rbはActiveJob::Baseを継承する親クラスで、処理を実装するジョブファイルでは必ずApplicationJobを継承する必要があります。

app/jobs/application_job.rb
class ApplicationJob < ActiveJob::Base
  # Automatically retry jobs that encountered a deadlock
  # retry_on ActiveRecord::Deadlocked

  # Most jobs are safe to ignore if the underlying records are no longer available
  # discard_on ActiveJob::DeserializationError
end

そしてApplicationJobを継承したSampleJobを実装していきます。
Jobが実行される際には、performメソッドが呼ばれるので、このメソッドに処理を記述します。

app/jobs/sample_job.rb
class SampleJob < ApplicationJob
  queue_as :default

  def perform(*args)
    puts '--------------------------------'
    puts '------------  Test  ------------'
    puts '--------------------------------'
  end
end

railsコンソール上で下記のようにJobを実行できます。

# 「キューイングシステムが空いたらジョブを実行する」とキューに登録する
> SampleJob.perform_later

# 明日正午に実行したいジョブをキューに登録する
> SampleJob.set(wait_until: Date.tomorrow.noon).perform_later

# 5秒後に実行するジョブをキューに登録する
> SampleJob.set(wait: 5.second).perform_later

performメソッドには実際には非同期で実行する重い処理が実装されることになります。
かんたんに実装できましたが、一つ問題があります。

Active JobはキューをRailsのメモリ内に保持するため、Railsを再起動するとジョブは失われてしまいます。

Railsガイドには以下のような記述があります。

デフォルトのRailsは非同期キューを実装します。これは、インプロセスのスレッドプールでジョブを実行します。ジョブは非同期に実行されますが、再起動するとすべてのジョブは失われます。
ーー
Rails自身が提供するのは、ジョブをメモリに保持するインプロセスのキューイングシステムだけです。 プロセスがクラッシュしたりコンピュータをリセットしたりすると、デフォルトの非同期バックエンドの振る舞いによって主要なジョブが失われてしまいます。

アプリケーションが停止、または再起動した際に、ジョブが失われないように、Railsで使うべきサードパーティのキューイングライブラリ、 Active Job のアダプタを決める必要があります。

3. アダプタはどれがいいのか?

代表的なものは、Delayed JobSidekiqResqueがあります。
プロダクトの規模や要件に応じて技術選定することになると思いますが、かんたんに比較してみます。

ストレージ プロセス/スレッド 処理速度
Sidekiq Redis マルチスレッドプロセス
Resque Redis シングルスレッドプロセス
Delayed Job SQL DB シングルスレッドプロセス

3-1. Sidekiq

利点

  • ストレージとしてRedisを使用し、マルチスレッドプロセスで動くので処理速度が速い
  • マルチスレッドであるがゆえに、他の2つと比べて、使用メモリに対するパフォーマンスが良い
  • ダッシュボードがいい感じ

懸念点

  • マルチスレッドなので、スレッドセーフであるように実装しなければならない
  • これもマルチスレッドがゆえに、 メモリが肥大化することがある
  • sidekiqは起動時にソースコードを読み込むため、(workker or jobの)コードを修正した場合に再起動が必要

※Rails、sidekiq、Redisはそれぞれ独立したプロセスでの起動

最初の問題については、下記に詳しく書きましたが、通常考慮する必要はなさそうです。
後ろ2つの問題を解消するためには、本番環境デプロイ時にcapistranoを使用していれば、同時にsidekiqも再起動してあげたら良さそうです。(ジョブ実行中の考慮は必要かも)

→結論:懸念点に大きな問題はないのでは?


(↓以下は、コアな部分に興味のない人は読み飛ばしてください。)

マルチスレッドとは、ひとつのプロセス内で複数のスレッドが動作していることを指します。

まずプロセスとは実行中のプログラムのことで、ひとつのプロセスには1つのメモリ領域(正確にはOSのメモリ空間)が割り当てられます。

そしてスレッドとはプロセス内で作られる並列動作が可能な処理の単位です。1つのスレッドにつき、1つのCPUコアに命令を出し処理を行います。プロセス内のスレッドは、プロセスに割り当てられたメモリ空間を共有できます。

そのため、並列で実行しても互いに影響を与えない実装、つまりスレッドセーフな実装を行う必要があります。

※Rubyは言語仕様上、OSレベルではメモリに対するアクセスが複数並行することはなく、スレッドセーフであることが担保されているようです。この辺までは詳しくないです。。参考記事


Active Jobのアダプタよりも、純粋にSidekiqを導入したほうが良い場合

Rails 6.0.1以前のActive Jobは、Sidekiqのリトライ機能を完全にサポートしていませんでした。
具体的には純粋なSidekiqで使用できるsidekiq_optionsメソッドがあります。sidekiq_optionsを使うとジョブを登録するキューや、ジョブが失敗したときのリトライ処理の有無が設定できます。

sidekiq_optionsでリトライ等の詳細に設定を行い場合、Active Jobではなく、純粋なSidekiqを使うほうが良いです。

Sidekiq 6.0.1 + Rails 6.0.2の組み合わせ以上のバージョンで、オプションが完全に使用できるようになります。

3-2. Resque

利点

  • ストレージとしてRedisを使用し、シングルスレッド・マルチプロセスで動く
  • delayed jobよりも高速
  • ジョブごとにフォークされてメモリ初期化されるからスッキリ(メモリリーク/肥大化の心配が基本的にない)
  • ダッシュボードがある

懸念点

  • 大量にジョブを処理するとフォークオーバーヘッドが大きく、速度面やメモリ面でやや不利
  • 単体でリトライ処理ができない (Railsのメソッド使えば、Active Jobではできるかも?)

3-3. Delayed Job

利点

  • ストレージにDBを使用するので、既存のRailsアプリに導入が容易
  • 登録されたキューもActiveRecordと同じように扱えるので、色々とやりやすい
  • (単体)キューへの登録が簡単 ※Active Jobを使う場合はインターフェイスは統一されている

懸念点

  • シングルスレッドプロセスがゆえの速度面

4. Active Jobを使ったほうが良いの?

利点

  • インターフェイスが統一され可読性が上がった (アダプタ間の移行はそんなに発生しなさそう)
  • Gem依存を減らせる (Railsのレールに沿ったほうがバージョンアップ時などに楽?)

懸念点

  • リトライ制御が弱い
  • 複雑な要件にどこまで耐えられるか

5. 結論

小規模なアプリや、スケーラビリティを考慮しない場合 ----> Delayed Job
パフォーマンスや大量のジョブが発生する場合 ----> Sidekiq

Active Jobか純粋なGemを使用するかは、大きなアプリケーションで複雑な仕様の場合、純粋なGemを使うほうが選択肢が広がりそう。
ただし、最新版のRails + Sidekiq の環境であれば、Active Jobも検討できる。

小規模 or 個人開発レベルであれば、Active Jobに沿って実装すれば良いのでは?といった感じ。

参考サイト

やはり原典をあたるのが一番良く、この辺りは情報が充実していました(英語ですが)。
Qiita記事も大変参考になり、ありがとうございました。

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

【Rails】コールバックについて

基本的な事項ですが忘れやすいので備忘録かつ、まとめとして記事にすることにしました。

【参考記事】
Railsのコールバック

コールバックとは?

モデルを保存、更新、削除後や削除前のタイミングで実行されるメソッドのこと指す。
例) 記事を投稿し保存後には〜をする。

before_save => #保存前
after_save => #保存後
after_update => #更新後
before_destroy => #削除前
after_destroy => #削除後

コールバック使用例

before_save(保存前)コールバック後にメソッド実行する例を挙げる。

user.rb
class << self
  before_save :downcase_email
  def downcase_email
     self.email = email.downcase
  end

上記コードはemail保存前に、大文字のローマ字がある場合、小文字に変換するメソッド。
他の分かりやすい例としては、

posts_controller.rb
class ShopsController < ApplicationController
  before_action :logged_in_user

上記はpostsコントローラーの各アクション前にログインユーザーであることを指定している。
通販で何か購入する時、ログインしていない場合はログインまたは会員登録を促されると思われるが、ログインユーザーでないため、弾かれる(before_action :logged_in_user)のもコールバックによりログインを要求しているものと言える。

学び

普段何気なく上記のメソッド(before_action)等は使用していると思われるが、改めて「Railsにおけるコールバックを端的に説明せよ」と言われるとビシッと答えられない初学者の方が意外に多いと思います(自分もそうです)。意味のないコードなどなく、それぞれが意味を持っているので、普段から自分が書いたコードについて自信を持って説明できるように学習を励んでいきたいと思いました。Railsはとっつきやすいと言われていますが、本当に理解して使いこなそうとするとかなり奥が深いものであることを学びました。

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