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

第4章 Rails風味のRuby

4.1 動機

■ヘルパー
view内でちょっとした処理をやりたい時に呼び出すもの。
実体はmodule。app/helpersで定義する。
使いたいとき<%= 〜%>で呼び出す。

■組み込みヘルパー
ある動作を処理する場合にメソッド化して扱えるようにRailsにあらかじめ組み込まれた機能。
helpersで定義しなくても使える。

4.2 文字列とメソッド

【演習】
1.city変数に適当な市区町村を、prefecture変数に適当な都道府県を代入してください。
city = "Yokohama"
prefecture = "Kanagawa"

2.先ほど作った変数と式展開を使って、「東京都 新宿区」のような住所の文字列を作ってみましょう。出力にはputsを使ってください。
puts prefecture+"県" + city + "市"

3.上記の文字列の間にある半角スペースをタブに置き換えてみてください。(ヒント: 改行文字と同じで、タブも特殊文字です)
やってみて

4.タブに置き換えた文字列を、ダブルクォートからシングルクォートに置き換えてみるとどうなるでしょうか?
やってみて

■オブジェクト
Rubyでは、あらゆるものがオブジェクト。
1.アイデンティティを持っている
2.メッセージを受け取る
3.内部状態を持つ
オブジェクトとは (いついかなる場合にも) メッセージに応答するものです。

文字列に質問を尋ねることができて、文字列がその質問に答えてくれる。
「empty?」という質問(メソッド)を投げかけるとその文字列が空なのか答えてくれる。

■メソッドチェーン
「nil.to_s.empty?」みたいな感じでto_sとemptyの2つをつなげる。

【演習】
1."racecar" の文字列の長さはいくつですか? lengthメソッドを使って調べてみてください。
racecar.length

2.reverseメソッドを使って、"racecar"の文字列を逆から読むとどうなるか調べてみてください。
racecar.reverse

3.変数sに "racecar" を代入してください。その後、比較演算子 (==) を使って変数sとs.reverseの値が同じであるかどうか、調べてみてください。
s=racecar
s == s.reverse

4.リスト 4.9を実行すると、どんな結果になるでしょうか? 変数sに "onomatopoeia" という文字列を代入するとどうなるでしょうか? ヒント: 上矢印 (またはCtrl-Pコマンド) を使って以前に使ったコマンドを再利用すると一からコマンドを全部打ち込む必要がなくて便利ですよ。)
puts "It's a palindrome!" if s == s.reverse
s="onomatopoeia"
puts "It's a palindrome!" if s == s.reverse

【演習】
1.リスト 4.10のFILL_INの部分を適切なコードに置き換え、回文かどうかをチェックするメソッドを定義してみてください。ヒント: リスト 4.9の比較方法を参考にしてください。
if s==s.reverse

2.上で定義したメソッドを使って “racecar” と “onomatopoeia” が回文かどうかを確かめてみてください。1つ目は回文である、2つ目は回文でない、という結果になれば成功です。
palindrome_tester(s)のsにracecar、onomatopoeia
をそれぞれ書けばOK。

3.palindrome_tester("racecar")に対してnil?メソッドを呼び出し、戻り値がnilであるかどうかを確認してみてください (つまりnil?を呼び出した結果がtrueであることを確認してください)。このメソッドチェーンは、nil?メソッドがリスト 4.10の戻り値を受け取り、その結果を返しているという意味になります。
→palindrome_tester("racecar").nil?

4.3 他のデータ構造

【演習】
1.文字列 “A man, a plan, a canal, Panama” を ", " で分割して配列にし、変数aに代入してみてください。
a = "A man, a plan, a canal, Panama".split(",")

2.今度は、変数aの要素を連結した結果 (文字列) を、変数sに代入してみてください。
s = a.join

3.変数sを半角スペースで分割した後、もう一度連結して文字列にしてください (ヒント: メソッドチェーンを使うと1行でもできます)。リスト 4.10で使った回文をチェックするメソッドを使って、(現状ではまだ) 変数sが回文ではないことを確認してください。downcaseメソッドを使って、s.downcaseは回文であることを確認してください。
s = s..split(" ")
def palindrome_tester(s)
palindrome_tester(s.split.join.downcase)

4.aからzまでの範囲オブジェクトを作成し、7番目の要素を取り出してみてください。同様にして、後ろから7番目の要素を取り出してみてください。(ヒント: 範囲オブジェクトを配列に変換するのを忘れないでください)
a=('a'..'z').to_a
a[6]
a[-7]

■mapメソッド
配列の要素の数だけブロック内の処理を繰り返し、結果として作成された配列を返す。

■doメソッド
testと一緒に使って、endまでの処理を全て実行する。

【演習】
省略

■ハッシュ
ハッシュ=連想配列

■シンボル
シンボルを使うと早い。
複合配列では
user = { "name" => "Michael Hartl", "email" => "michael@example.com" }
user = { :name => "Michael Hartl", :email => "michael@example.com" }
user = { name : "Michael Hartl", email : "michael@example.com" }
が同じになる様子。

【演習】
省略

4.4 Rubyにおけるクラス

【演習】
省略

■superclass
全てが何かしらのクラスに属している。
railsが全て属させてくれているため、自動でアプリケーションの開発が可能。

【演習】
省略

【演習】
省略

【演習】
省略

【演習】
省略

4.5 最後に

自分のモチベーションの低さに驚愕。
5章やった後に戻ってこよう。

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

array.map(&:method) を使って簡単に

array.map(&:method) を使って簡単に

下のような式があるとする。
ブロック内で各要素を大文字に変換してそれをarrayに格納する。

['a', 'b', 'c'].map{|s| s.upcase } #=> ["A", "B", "C"]

array.map(&:method) を使って簡単に

array.map(&:method)を使って上の例のしきを簡単にできる。

['a', 'b', 'c'].map(&:upcase) # => ["A", "B", "C"]

適用条件

  1. ブロックの引数が一つだけ
  2. ブロックの中で呼び出すメソッドには引数がない
  3. ブロックの中では、ブロック引数に対してメソッドを呼び出す以外の処理がない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker環境で遅いbundle installを劇的に早くする

目的

newsdict.io(docker環境上のrails)でbundle installの時間を早めたい

方法

  • あまりバージョン変更がない最も重たいgemをベースのdocker imageに入れて置きgemを流用できるようにしておく
  • nokogiriを使っていたので、[--use-system-libraries]を使う
  • bundle install を並列で実行する

実際のコード

- あまりバージョン変更がない最も重たいgemをベースのdocker imageに入れて置きgemを流用できるようにしておく

https://github.com/newsdict/docker_rails/blob/master/Dockerfile#L70

- ベースとして使ったdocker image

https://hub.docker.com/repository/docker/newsdict/rails

- nokogiriを使っていたので、[--use-system-libraries]を使う

https://github.com/yubele/newsdict.io/blob/9747c9a4d0a69f8f251f1deb9d6b776856281a2b/Dockerfile#L25

- bundle install を並列で実行する

https://github.com/yubele/newsdict.io/blob/9747c9a4d0a69f8f251f1deb9d6b776856281a2b/Dockerfile#L24

Q&A

bundle installした結果をdocker imageのすればいいのでは?

Gemfileを頻繁に書き換える可能性があるため

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

Railsにおけるsessionについて整理してみた

railsチュートリアルをもりもりとやっております。
第8章にて取り扱われている「session」について、今回は自分の理解のために整理したいと思い記事を書きました。
初心者なため、なにか間違いなどあればコメントにてご指摘いただけると嬉しいです。

そもそもsessionって何?

(答え)
"statefullな通信"を実現する"仕組み"のこと。

ステートフルとはなにか?

私たちが普段使っているHTMLは、statelessな通信を行っています。
*state (状態) less(ない)= 1回1回が独立したやりとり

この通信は、ブラウザ⇄サーバ間のやりとりは1往復ごとに独立しており、
以前の情報などは引き継ぐことはできません。

ただ、この話を聞いた時に疑問に思うかもしれません。
HTMLの通信は全て独立したやりとりならば、例えば、ショッピングサイトなどで、
カートに買いたい商品を入れていく際、情報は引き継がれないため、
ページ遷移するとカートは空になってしまうはずです。

なので、ステートレスなHTMLの通信を"ステートフル"にするのがセッションという仕組みです。
*state (状態) full(保持する)=ユーザ情報などの状態を"保持"したままやりとりを行う

この通信では、ブラウザ⇄サーバ間で通信に情報を付け加えてやりとりをすることで、
このやりとり全体を一連の動作のようにすることが可能です。

上の例でいえば、会員としてログインしたサイトで、会員情報を保持しながら様々なページに飛べたり
ショッピングサイトで、カートの中にどんどんと物を入れていくことができたりと、何かと便利です。

HTMLでの"ステートフルな通信"はセッションという仕組みで成り立っています。

参考:session通信

sessionはどうやってやりとりされるの?

(答え)
一般的には、ブラウザ側から送られる"cookie"に含まれるsession IDを
サーバ側で取得・照合し、セッション内容を取り出してやり取りをしている。
ブラウザを閉じることで、セッションは削除される。

Cookieとはなにか?

クッキーは一言でいうと、webブラウザ側で保持している情報です。

セッションが必ずしもcookieを扱うとは限りませんが、一般的に用いられるのは確かです。
Railsもcookieを標準としているため、ここではcookieを前提にしてお話します。

〜cookieのざっくりとした流れ〜

①アクセス初回
ブラウザ側が、サーバ側にアクセス。
サーバ側が、そのブラウザの識別情報(例えばログイン情報など)をHTTPヘッダに含めて送信
その情報をブラウザが保存する(Cookie情報がこれ)

②それ以降のアクセス
ブラウザ側が、保存していたcookie情報をHTTPヘッダに含めてサーバ側へ送信。
サーバ側はその情報を元に、アクセスしてきた相手を判断する。

このように、cookieはブラウザ側で保持している情報のことで、その中にsession IDも含めて送ってあげることで、サーバ側で「どんな内容やり取りしてたっけ?」がわかるようになります。
ちなみにsessionの内容自体は、webサーバ側が保持しており、cookieの中にあるsession IDはそれを呼び出すためのIDになります。

セッションはあくまで一時的なものであり、基本的にはブラウザを閉じた段階で消去されます。

Railsにおけるsessionとは?

概念の話は以上として、次からはRailsにおけるsessionの内容について整理していきます。
railsではユーザごとにセッションを設定できます。セッションは、コントローラとビューでのみ利用可能。
また、以下のストレージを選ぶことができます。

ストレージ 説明
ActionDispatch::Session::CookieStore: すべてのセッションをクライアント側のブラウザのcookieに保存する
ActionDispatch::Session::CacheStore: データをRailsのキャッシュに保存する
ActionDispatch::Session::ActiveRecordStore: Active Recordを用いてデータベースに保存する (activerecord-session_store gemが必要)
ActionDispatch::Session::MemCacheStore: データをmemcachedクラスタに保存する (この実装は古いのでCacheStoreを検討すべき)

先ほど説明したように、基本的にはsession IDはCookieに保存されサーバに渡されますが、
デフォルトで使用されているCookieStoreに関しては、sessionの情報自体をCookie側に保存します。

CookieStoreは、以下のメリットを持っています。
・非常に軽量であることと
・Webアプリでセッションを利用するため一式が準備済み
・cookieデータは改竄防止のために暗号署名が与えられており、さらにcookie自身も暗号化されているので、内容を他人に読まれない (改ざんされたcookieはRails側が拒否)

ただし以下のデメリットもあります。
・cookieの上限は4KB
・cookieはクライアント側(ブラウザ)に保存されるので、cookieの期限切れのcookieの内容が残っている可能性もあり
・クライアントのcookieが他のコンピュータにコピーされる可能性もあり
・セッションcookieはひとりでに失効することはないため、悪用目的で使い回される可能性もあり

基本的には、Railsが推奨しているCookieStoreを使うのがいいと思いますが、
状況に応じて使い分けが必要という風に捉えました。

参照:railsガイド セッション

sessionの操作

以下は全てデフォルトで使用されているCookieStoreでの操作です。

・sessionを作る

session[:user_id] = @user.id

sessionメソッドはハッシュ値で設定できます。
設定することによりこのsessionの情報を含んだ暗号化済みのcookieが生成されます。

・sessionを参照する

user = User.find(id: session[:user_id])

sessionの情報は、簡単に参照可能です。
セッションメソッドが呼び出されると内部でcookieの情報が復号され、session[:シンボル]で値を取得することができます。

・sessionを削除する

#やってることは全部一緒
session[:user_id] = nil
session[:user_id].clear
session.delete(:user_id)

nilで書き換えたり、clearやdeleteなどで情報を削除することが可能です。

終わりに

私がsessionを学んでいて一番疑問に思ったのは、sessionとcookieの関係性でした。
概念で話されている内容と、Railsのsessionメソッド(CookieStore)の挙動が違うというのが
よく理解できておらず、?がいっぱいに・・・笑

改めて、整理してみると自分が理解できていなかった点が洗い出せてすっきりしました。
引き続きRailsチュートリアル、頑張っていきたいと思います。

参考にさせていただいたサイト様

ありがとうございます!
https://ja.wikipedia.org/wiki/HTTP_cookie
https://qiita.com/hththt/items/07136ad74127999df271
https://qiita.com/hot_study_man/items/147f8b767b4135fe6fe4
https://www.justinweiss.com/articles/how-rails-sessions-work/
https://railsguides.jp/security.html

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

ジャンル別に投稿・チャットできるようなアプリ作ってみた〜アプリ概要〜

個人アプリでジャンル別に投稿・チャットができるようなアプリを作成してみました。
下記の文章はこのアプリのREADMEに載せているものです。
概要として、この記事も投稿したいと思います。

Ota-Chat

:sunny:アプリ内容

  • どんなアプリか
    趣味が同じ人同士でTwitterのような投稿をし合い、その中でグループを作って趣味が合う人同士でチャットができるようにしたアプリ
  • このアプリでできること
    • ジャンルでの投稿のみをすることもでき、また、その中でグループを作成し、決まったメンバーとチャットすることもできます
    • グループ作成では、グループにランク(ライト・ミドル・ハード)を選択でき、メンバーの趣味の度合いによって選択できるようになっています ### :sunny:URL(デプロイ済) https://ota-chat.herokuapp.com/

:sunny:開発環境

  • Ruby
  • Ruby on Rails
  • VSCode(Visual Studio Code)

:sunny:制作背景

:star: グループチャット(ランク付)

趣味が同じでも、その趣味にかける熱というのは十人十色だと私は思っています。
単にアニメが好きと言っても、ギャグアニメが好きなのか、シリアスなアニメが好きなのか、同じアニメ作品を好きと言っても、観るだけで満足なのか、グッズを買いに行くまで好きなのか、グッズも買うし、イベントも全て行きたいほど好きなのか、趣味にかける情熱というのは、人それぞれであり、それを公にするか、秘密にするかもそれぞれです。
私の友人はアニメが好きなことは職場で言えても、アプリゲームも好きで、そのゲームのあるキャラクターに関してかなりのお金を遣っていることは職場では秘密にしています。
そのような友人が同じキャラクターを好きなもの同士で話せるようなアプリを作りたいと思い、このアプリを作成しました。
このアプリのグループにランクがついているのは、友人が同じくらいのキャラクター愛を持つ人とお話ができたら楽しいだろうと考えたからです。

:star: ジャンル別投稿機能

Twitterを見ると、キーワード検索で必要ないツイートも出ているように感じて、それなら最初からジャンル分けして投稿した方が見やすいし、投稿しやすいと感じたため、このような機能を追加しました。

:sunny:機能説明

:star:ログイン登録機能

:star:アカウント作成機能

:star:ホーム画面

  1. ユーザー自身がお気に入りしているジャンルと所属しているグループを見ることができます
  2. 右上のアイコンからアカウントの変更、ログアウト、グループ希望確認(下記説明)ができます
    スクリーンショット 2020-08-01 23 46 10スクリーンショット 2020-08-02 1 42 11

:star:ジャンル作成機能

  1. 自分の好きなジャンルを作成することができます
  2. 既存ジャンルを押せば、そのジャンル投稿画面に飛ぶことができます
  3. ジャンルの検索も可能です
    スクリーンショット 2020-08-01 23 53 56

:star:ジャンル投稿画面(ジャンル内グループ検索可能)

  1. 気に入ったジャンルをお気に入りできる機能
  2. Twitterのような投稿機能(画像は5枚まで添付可能、添付された画像は横スクロールで見れます)(画面右側)
  3. 投稿物へのコメント機能(画面右側)
  4. 投稿物へのいいね機能(画面右側)
  5. いいねしたもののみ閲覧する機能(画面右側)
  6. ジャンル内のグループ検索機能(グループ名で検索、グループランクで検索、またはその両方を合わせて検索)(画面左側)

スクリーンショット 2020-08-02 0 56 12

:star:グループ作成機能

  1. グループに参加させたいユーザーをフォームに入力すれば、スクリプトサーチで検索可能です
  2. 特徴1:グループにライト・ミドル・ハードの3つのうち、どのランクかを選択します。例えば、趣味への知識が深くそのような話がしたいなら、ハードを選択して仲間を集める手段にできます。
  3. 特徴2:「どのようなグループなのか、どのようなグループにしたいのか」をコメント欄に入力できます
    スクリーンショット 2020-08-02 1 16 08スクリーンショット 2020-08-02 1 16 52

:star:グループチャット画面

  1. グループメンバーとチャットすることが可能(画面右側)
  2. 自分が所属しているグループ、そのグループタグが記載され、グループページのリンクとなっている(画面左側)

スクリーンショット 2020-08-02 14 15 28

:star:グループ詳細画面

  1. 左からグループ作成者ver,グループメンバーver,グループメンバー外verとなります
  2. グループ作成者はグループを解散させること、グループメンバーを変更、グループのタグ付グループコメントの記述ができます
  3. グループメンバーはグループメンバーを変更、グループのタグ付、グループコメントの記述ができます
  4. そのグループに所属してないユーザーは詳細の確認とそのグループに所属したい場合の希望(コメント可能)を送信することができます
    スクリーンショット 2020-08-02 1 03 59 スクリーンショット 2020-08-02 1 31 44 スクリーンショット 2020-08-02 1 34 12

:star:グループ希望、承認画面

  1. グループに参加希望を出した際は、右側の参加希望申請済みに希望を出したグループ名が表示される
  2. グループリーダーをしている場合は、そのグループに参加希望が出されると左側に参加希望を出したユーザー名とコメントが表示される
  3. 参加承諾のリンクと拒否のリンクがあり、グループリーダーはそのコメントなどを見て選択できるようになっている
    スクリーンショット 2020-08-02 1 18 36
    スクリーンショット 2020-08-02 1 18 36
    スクリーンショット 2020-08-02 1 18 36

:sunny:工夫したポイント

:star: ジャンル検索ができる

  • ジャンル検索ができるので、自分が投稿したいジャンルを探すことができる
  • ジャンルは左側がアルファベットから始まるジャンル、右が日本語というように分かれており、検索時もキーワードが日本語なら右側に表示されるようになっている(逆も然り)

:star: ジャンルの投稿画面でグループの表示・検索ができる

  • グループは元々、ジャンルに紐付けて作成しているので、ジャンル内のグループのみ表示、検索できるようになっている
  • 表示には、グループ名のみではなく、グループに付けられたタグも表示されるので、「グループメンバー募集中」などのタグを付けてメンバーを集めることも可能
  • 検索にはグループのランクで検索することができるようになっているので、自分がライトなオタクだと思ったら、ライトなグループを検索、逆に重めのオタクだと思えば、ハードグループの検索をすることが可能

:star: マイページに自分のお気に入りのジャンルと所属グループを表示するようにしている

  • ジャンル画面にはお気に入り登録できるボタンがあるので、それを押してお気に入り登録すれば、マイページのジャンル部分に表示される。逆に、お気に入り解除すれば表示されないようになっている
  • グループも自分が所属しているグループは表示されるようになっている

:star: 自分が所属したいグループに参加希望を出し、グループ作成者はそれに承諾・拒否することができる

  • 自分が所属していないグループの場合、参加希望と参加希望コメントをグループリーダー(グループ作成者)に送ることができる
  • グループリーダー(グループ作成者)は参加希望を受けて、承諾・拒否をすることができる

:sunny:データベース(ER図)

Untitled Diagram (1)

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

【Ruby on Rails】条件合致する時のみ機能するバリデーションの実装

Ruby on Railsにおいて、ある条件に合致する時のみ機能するバリデーションを記述したい場面があり、解決するまでに時間がかかったため、ある条件に合致する時のみ機能するバリデーションの記述方法を下記に示します。

概要

登録したいレコードのカラムXの値がaの時のみ有効になるバリデーションの記述

具体例

下記例は、products tableのカラムpublic_flag(型:integer)の値が1の場合、
products tableのカラム商品名(name)、商品説明(description)、商品価格(price)の値が空では登録できないという制約を付与している例である。

下記の場合、public_flagの値が0の場合、商品名(name)、商品説明(description)、商品価格(price)の値が空でもproducts tableへ登録が可能である。
 ※public_flag 1: 公開情報、0: 非公開情報

model(product.rb)

 with_options presence:true, if: :isProductPublicable?  do |v|
   v.validates :name
   v.validates :description
   v.validates :price
 end

 #public_flagが1の時 true
 def isProductPublicable?
  public_flag == 1
 end

 
 with_optionsは複数のバリデーションをまとめてかけることができるオプションである。
 isProductPublicable?はカラムpublic_flagの値が1の場合、trueを返却する。

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

Ruby 数値参照プログラム

自分用です
簡単な数値参照プログラムを作りました。
下記コード

qiita.rb
m = gets.chomp
n = gets.to_i


room = []

n.times do
  count = gets.chomp
  room.push(count)
end

a = 0
count = n

while n > 0

  if ! (room[a]).include?(m)
    puts room[a].to_i
  else
    count -= 1
    if count == 0
      puts "none"
    end
  end
  n = n - 1
  a = a + 1
end

最初に変数mに参照元となる数値を入力。(文字データになってますけど)
それをもとに数値入力して当てはまらない数値だけ出力しています。

全てあてはまらなかった場合"none"と返します。

今回のif文は”あてはまらない”場合なので if !という形で作りました。
又、文字列の方がinclude?で簡単に参照できると思ったので
最初に文字列で参照し、出力時に数値に変換するフローで
作ってみました。

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

Rubyのformatメソッドで名前付きのフォーマットを使う

Rubyには、いわゆるフォーマット指定と呼ばれるやつに従って文字列を整形するメソッドがいくつかあります。

※ これらの書き方をするとRuboCopに怒られてしまったので、経緯をまとめました。

# どれも "hoge = 1 + 2" と評価される
sprintf('hoge = %s + %s', 1, 2)
format('hoge = %s + %s', 1, 2)
'hoge = %s + %s' % [1, 2]

formatsprintfはどちらもKernelのメソッドで、名前が違うだけのエイリアスです。
https://docs.ruby-lang.org/ja/latest/method/Kernel/m/format.html

三番目のString#%は少し毛色が違いますが、self側をフォーマット指定の文字列とみなしてformatを呼んでいるだけで、実質的には同じことができるようです。
https://docs.ruby-lang.org/ja/latest/method/String/i/=25.html

推奨される書き方(スタイル)は?

この記事の記述時点では、RuboCopのデフォルト動作は「formatメソッドのみを認める」です。矯正させられます。

hoge.rb:3:18: C: Style/FormatString: Favor format over String#%.
'hoge = %s + %s' % [1, 2]
                 ^

さらに、伝統的な%sのようなスタイルは推奨されないという怒られ方をします。

hoge.rb:1:16: C: Style/FormatStringToken: Prefer annotated tokens (like %<foo>s) over unannotated tokens (like %s).
format('hoge = %s + %s', 1, 2)
               ^^

結局どう書けばいいのか

フォーマットの指定にアノテーション <name> を付け、はめ込みたい値はHashで渡すスタイルが推奨されるようです。

format('hoge = %<x>d + %<y>d', x: 1, y: 2)

ちなみに似た記法で %{name} もありますが、推奨されないようです。指定子は明記しろってことですね。

hoge.rb:4:16: C: Style/FormatStringToken: Prefer annotated tokens (like %<foo>s) over template tokens (like %{foo}).
format('hoge = %{x} + %{y}', x: 1, y: 2)
               ^^^^

なぜそのスタイルが推奨されるのか

RuboCopはスタイルをどれかに統一することを意図しているようで、デフォルトがformatです。
https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/FormatString

元になっているissueは↓のようですが、難解な議論の末に決まったというよりは「あるといいんじゃない?」「いいね!」みたいなラフな経緯に見えます。デフォルトは決めの問題という以上の話ではないのかもしれません?

Cop idea: Enforce preferred style for format string sequences #3438
https://github.com/rubocop-hq/rubocop/issues/3438

%<name>%{name} よりも良い、という主張については、The Ruby Style Guideを根拠にしているように見えます。

When using named format string tokens, favor %s over %{name} because it encodes information about the type of the value.
https://rubystyle.guide/#named-format-tokens

まとめ

Rubyでは、いわゆるprintf的な文字列整形メソッドが、いくつかのスタイルで提供されています。

現代的な、特にチーム開発でRubyを使用する場合には、RuboCopを導入することも多いと思います。その際に指定されるスタイルはどんなもので、どういった根拠に従っているかを、簡単にですがまとめました。

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

FakerGemで作るポケモンGo

経緯

プログラミング学習を進める過程で様々なGemを使う。Gemの理解を深めるためにGitHub上の元データを眺めていると、面白いものが見つかった。

Faker::Games::Pokemon

Faker::Games::Pokemon.name #=> "Pikachu"
Faker::Games::Pokemon.location #=> "Pallet Town"
Faker::Games::Pokemon.move #=> "Thunder Shock"

なんとポケモンの名前をランダム表示する機能がある。FakerGemを理解するなら、使ってみるのが一番!ということで、ポケモンの遭遇と捕獲を行うアプリケーションを作成してみた。

コード

application.rb
# FakerGemの読み込みと日本語設定
require "Faker"
Faker::Config.locale = :ja

# ポケモンクラスとプレイヤークラスのファイル読み込み
require "./pokemon"
require "./player"

# ポケモンを生成
pokemon_name = Faker::Games::Pokemon.name
pokemon = Pokemon.new(pokemon_name) 

# プレイヤーを生成
player = Player.new()

# ポケモンが出現!
pokemon.appear

# プレイヤーの行動
player.command(pokemon_name)
pokemon.rb
class Pokemon

  def initialize(name)
    @name = name
  end

  def appear
    puts "あっ!野生の#{@name}がとびだしてきた!"
  end

end
player.rb
class Player

  def command(pokemon)
    puts "あなたはどうする?"
    puts "[0]モンスターボールを投げる\n[1]逃げる"
    input = gets.to_i
      if input == 0
        puts"やったー!#{pokemon}をつかまえたぞ!"
      elsif input == 1
        puts "うまくにげきれた!"
      else
        puts "番号が間違っています"
      end 
  end

end

terminal

まとめ

出現するポケモンがランダムのため、「予測できない楽しさ」がある。
FakerGemはもちろんのこと、「オブジェクト指向」や「メソッドの考え方」など、少量のコードでも0から作ることは大いに学びになる。「捕獲率の変動」や「街の移動」など色々とオプションの余地もあるため、時間を見つけて遊んでいこう。

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

駆け出しエンジニア「サーバーサイド言語を勉強したら、何ができるようになるの?」

登場人物

駆け出しエンジニア(以下、後輩? と呼ぶ)
やっとの思いでHTML/CSS/JavaScriptを習得した、駆け出しエンジニア。
JavaScriptのライブラリやフレームワークも経験して、
とりあえずイメージしたWEBアプリを作成できるレベル感に達成した。
使っているブラウザはGoogle Chrome。

駆け出しエンジニアの友人(以下、先輩? と呼ぶ)
駆け出しエンジニアと同じカリキュラムで勉強をした後、
駆け出しエンジニアよりも1年早くサーバー関連の仕事を経験してきた、彼の友人。

あらすじ

後輩? :
サーバーサイド言語を学ぶべきなのはわかるけど、これを学んだら何ができるようになるんだろう?

オンラインサービスが作れるようになる、というのはわかるんだけど、それだけなのかな?
なんかパッとしないな〜。

先輩? :
おっ、後輩。
なにやら勉強のモチベーションに行き詰まっているようだね?

後輩? :
先輩!
そうなんだよ〜。サーバーサイド言語がとっても大事な知識だというのは理解してるんだけど、
JavaScriptでもいろいろ作れるようになって楽しんでるから、
今まで作れなかった、こんなものが作れるようになる!みたいなワクワク感がないと、
モチベーションがあがらないんだよ〜。

先輩? :
ちょうど、そんな時期かな〜と思って、相談にのりにきたよ。
1年前の僕も、おなじ気持ちでモヤモヤしていたから、僕が経験して得た知識を伝授してあげよう。

後輩? :
やったー!

オンラインサービス

先輩? :
サーバーサイド言語を学ぶ一番の目的がオンラインサービスを作ることだというのは、わかるよね。

後輩? :
うん、自分が入力したデータを世界中の人と共有するには、
自分のPCのかわりにデータを配信してくれるPC(=サーバー)が必要なんだよね。

先輩? :
そう。そしてデータ共有方法の発展系として、
ユーザーのひとりが情報を更新したらリアルタイムで全てのユーザーに内容を反映できる「WebSocket」や、
ユーザーからのアクションがなくともサーバーからユーザーにデータを送信できる「Push通信」などがあるね。

ログインシステム(ユーザー情報の保存)

先輩? :
オンライン化の次に大事な目的が、
ユーザーの情報を、こちらがわのPC、つまりサーバーに保存しておけることだよ。

後輩? :
JavaScriptの内容はすべてユーザーの手元に渡ってしまうものだから、
JavaScriptだけでパスワードっぽい機能を実装することは何の意味もないっていう話は聞いたことあるよ。

先輩? :
そう。どんなに難読化などの対策を頑張っても、
ユーザーに見せたくない情報をJavaScriptに持たせている時点で、仕組みとしては欠陥品だね。

後輩? :
うんうん。

ただ、ユーザーの情報を保存しておく目的っていうのはイマイチイメージできないよ。
もちろん、保存する必要なんてない!とは思わないけど、
具体的にどんなことができるようになるかは、いまいちわからない。

先輩? :
いちばんの目的は、ログインシステムを導入することだね。
IDとパスワードが一致するユーザーは同一人物だと判断することで、
たとえ使っているデバイスやブラウザが変わっても、そのユーザーの情報をひきつづき利用することができるよ。

後輩? :
うん。たしかに、それはそうだね。

先輩? :
それに、会員の情報がなければ、「お誕生日おめでとうメールを送る」みたいな、情報をもとに分岐する作業はできないよね。

後輩? :
いやいや、別にサーバーに保存してなくても、誕生日を入力してもらう画面で情報を取得してlocal storageなどに保持しておけば
ユーザーがアクセスしてきたタイミングで「お誕生日おめでとうメッセージの表示」はできるんじゃない?

先輩? :
たしかに。
後輩は、JavaScriptだけでもいろんな事ができるように、頑張って勉強しているよね。
ちなみに、JavaScriptでもメールを送る方法はあるから、「メッセージの表示」じゃなくて、ふつうに「メールを送る」こともできるよ。
気になるなら「JavaScript メール送信」でググってみてね。

後輩? :
そうなのか!
やっぱりJavaScriptって、なんでもできるんだなあ。

先輩? :
まあ、話がそれちゃったけど、
ユーザーの情報をサーバーに保存しておくとどういうメリットがあるのかは、あとで詳しく説明する事にするよ。

閑話 シェル言語について

先輩? :
ところで「シェル言語」については、既に習得済みだったかな?

後輩? :
うん。「コマンドライン」とか「シェルスクリプト」とか「Linuxコマンド」とか呼ばれているものだよね。
Windowsなら「コマンドプロンプト」や「PowerShell」、
Macなら「Terminal」を開いて入力することで、
PC内のファイルを作ったり消したりできる、PCの操作をするための言語だよね?

先輩? :
うん。
ちなみにMacやLinuxで一般的に使われているシェル言語は「bash」という名前の言語だよ。
あ、最近のMacは、これを更に便利にした「zsh」という言語を標準で使うように変更されたね。

後輩? :
「絶対やっておいた方がいい」って言われて、とりあえず勉強したんだよ。
基本は難しくなかったし、
黒い画面でカタカタする感じがプログラマー!!って感じがして楽しかったけど、
ぶっちゃけまだ有効活用できてない。

先輩? :
それもそのはず。
実はシェル言語を使う機会って、サーバーの操作をするときか、
手元のPCにサーバーに関わる設定をするときがほとんどだからね。

後輩? :
あっ、そうなの?

先輩? :
うん、理由は単純で、サーバー用のPCには基本的にデスクトップ画面やマウスがないから。
手元にあるPCからサーバーに「SSH接続」して、シェル言語を使って操作するような方法しかない。
だから必然的に必要になってくるというわけ。

後輩? :
なるほど。学習中、どういう時につかうべきなのかイマイチ想像できなかったのは、
手元のPCでは基本的に使う必要がないからなのか。

先輩? :
ちなみにサーバー用のPCを用意するとき、使われるOSはほとんどの場合「Linux」というOSだよ。なんてったって無料だからね。
「Linux」の標準のシェル言語は「bash」なので、手元のPCがMacであれば練習に使えるけど、
手元のPCがWindowsの場合、ルールやコマンドが色々違うから、「コマンドプロンプト」や「PowerShell」で練習するときは注意しながら進めたほうがいいよ。

それと、これは余計なお世話かもしれないけど、学習サービスによっては、
「パーミッション」という概念について解説されていないところがあるらしい。
シェルでファイルを操作するようになったら必ず必要になる大事な知識だから、
実際にサーバーに触りはじめる時には、勉強しておく事をオススメするよ。

後輩? :
僕が使った学習サービスでは、「パーミッション」というのはやらなかったな…。
その時が来たら、調べてみるね。

プログラミング単体で実行できる

先輩? :
ところでなんで急にシェル言語の話をしたかというと、
次の話の説明をするためなんだ。

JavaScriptは、HTMLで読み込むことで、ページを開いたときに自動で実行されるのが標準の動作だよね。

後輩? :
そうだね。

先輩? :
対して、サーバーサイド言語は基本的に「シェル」でコマンドを叩くことでも実行できるようにしてあるものなんだ。
JavaScriptをサーバーサイド言語として使えるようにした「Node.js」という仕組みも、こういうことをできるようにするためのものなんだよね。

まず、サーバー、もしくは自分のPCに好きなサーバーサイド言語をインストールする。
そうすると「シェル」でサーバーサイド言語を実行するためのコマンドが使えるようになる。

後輩? :
つまり、検証ツールのConsoleと同じようなことを、「コマンドプロンプト」や「Terminal」などの、
いわゆる「シェル」でできるのがサーバーサイド言語っていうこと?

先輩? :
うん、Consoleみたいにひとつひとつ命令を実行していくこともできるけど、
基本的には、JavaScriptと同じようにプログラム言語を書いたファイルをつくっておいて、
そのファイルを「シェル」で一気に実行する、っていう使い方が一般的かな。

後輩? :
そうなんだ。

先輩? :
つまりシェルでプログラミング言語を単体実行できることが、
JavaScriptと大きく違うポイントだっていうこと。

後輩? :
プログラミング言語だけを実行するっていうのが、いまいちイメージできないな。

先輩? :
たとえばさっきの「お誕生日メール」を例にすると、
PCのどこかにユーザーの情報を保存しておけば、
その情報を取得しにいって、誕生日ならメールを送る、という処理を単体で実行できるよね。

後輩? :
ああ、そういう感じか。
JavaScriptのプログラミングは最終的にConsole.logするか、HTMLに出力するかっていうのが基本だったけど、その最終地点をメールにしちゃえば、プログラム単体で目的が達成できるもんね。

先輩? :
そういうこと。
JavaScriptが「WEBページを開いている間しかプログラムを実行できない」ことに対して、
サーバーサイド言語は「プログラム単体で実行できる」ということも、サーバーサイド言語でできるようになるひとつのメリットだよ。

好きな時間にプログラムを実行できる

先輩? :
プログラム単体で実行できることを踏まえて、
サーバーは更に「好きな時間にプログラムを実行できる」というメリットがあるよ。

サーバーに限らず基本的なPCの機能として、決まった時間に「シェル言語」を実行するための「cron」という仕組みがあるんだけど、知ってる?

後輩? :
聞いたことないよ。
あらかじめ実行するコードと、実行する時間をきめておけば、その時間に自動実行してくれるっていうこと?

先輩? :
そのとおりだよ。
だから「cron」の設定をすれば、好きな時間に好きなプログラムの実行ができるというわけ。
もちろん、PCが起動していなかったらcronも動かないから、サーバーは常につけっぱなしにする事がほとんどだよ。

ユーザーがWEBページを開いていなくても、こちらの好きな時間にユーザーに対するアクションを起こすことができる。
ユーザーに対するアクション以外にも、サーバー内のデータの整理や、外部サービスを利用するために必要な情報を送信するみたいな処理を、自動で実行することができる。
これはJavaScriptには出来ないから、サーバーサイド言語ならではのメリットだよね?

後輩? :
たしかに。なんか勉強するぞって気持ちになってきたかも!

データベース

先輩? :
ちなみにユーザーの情報を保存しておいたり、誕生日の人のメールアドレスだけ取得する、みたいな柔軟な情報操作をするために「データベース」っていう機能がよくインストールされるよ。

後輩? :
「データベース」は聞いたことある。そういう時に使うやつなんだね。
っていうか、データベースってインストールするものなの?

先輩? :
うん。サーバーだって普通のPCと同じで、最初はただのPCだからね。
サーバーサイド言語も、データベースも、WEBサーバーという機能ですらも、
ぜんぶシェル言語を使ってインストールすることで使えるようになる、アプリみたいなものなんだよ。

後輩? :
そう聞くと、なんだかサーバーというものが今までより身近に感じるかも。

先輩? :
データベース自体は、シェルから実行して単体で使うものなんだけど、
サーバーサイド言語には、その言語からデータベースにアクセスするための方法が用意されているから、
それを学べばサーバーサイドのプログラムでデータベースの情報を利用できるようになるよ。

WEBサーバー

先輩? :
そういえば、さっきチラっと触れたけど
そもそもWEBサーバーってどういう機能か知ってる?

後輩? :
たぶん、わかってると思う。
HTMLやCSSやJavaScriptのファイルを、ネットを介してユーザーに配信できるんだよね?

先輩? :
まあ、そのとおりだね。
そして配信する際に、特定のIPアドレスからのアクセスをブロックしたり、
アクセスされたアドレスによって表示するファイルを決めるのも、WEBサーバーの機能だよ。

後輩? :
表示するファイルを決めるって…
ユーザーがアクセスするアドレスは、サーバーのディレクトリ構成やファイル名そのままじゃなくても大丈夫ってこと?

先輩? :
うん。◯◯◯というアドレスにアクセスされたら、△△△というファイルを表示するっていうことができる。
仮にサーバー内のディレクトリ構成やファイル名がごちゃごちゃしちゃっても、
ユーザーがアクセスしてくるアドレスは単純なものに変更できてしまうので、これもメリットのひとつかもね。

サーバーサイドレンダリング

先輩? :
WEBサーバーの機能とサーバーサイド言語を組み合わせて利用した、重要な機能があるんだ。
サーバーでは、配信するファイル(主にHTML)の内容を書き換えて配信することができるんだよ。

後輩? :
JavaScriptでもHTMLの内容を書き換えることはできるけど、
サーバーでそれをするメリットは何なの?

先輩? :
JavaScriptでHTMLを書き換えるということは、書き換え前のHTMLの内容も、書き換え後のHTMLの内容も、すべてユーザーの手元にある状態だよね?

後輩? :
えーっと…
書き換え前のHTMLの内容は、ブラウザの「ソースを表示」機能で見ることができるし、
JavaScriptの内容は、検証ツールの「Sources」タブから見ることができるから、
それを読み解けば把握できちゃう状況ではあるね。

先輩? :
うん。それに対して、サーバーでファイルの内容を書き換えてから配信するということは、
ブラウザの「ソースを表示」機能で見れるHTMLが、既に書き換えられた状態になってるということ。
なので、どのように書き換えたかがユーザーに全く伝わらず、よりセキュリティ的に良い(=セキュアな)仕組みになるよ。

それと、配信するファイルの容量も軽くなるから、通信量的にもお得になるね。

後輩? :
なるほどなあ。たしかに、HTMLとJavaScriptの内容を削減して配信できるわけだから、その分データは軽くなりそうだね。

先輩? :
うん。だから、ユーザーがページ内で行った行動(入力やクリックなど)によって表示内容を判断しなきゃいけない部分はJavaScript、
ページ内での行動にかかわらず、ページを表示する時点でどのような表示内容にするか判断できる場面なら、サーバーサイド言語で書き換えるのが合理的だよ。
もちろん、必ずそうしろという事ではないけどね。

このようにサーバーで予めファイルの情報を書き換えて配信することを「サーバーサイドレンダリング」と呼ぶよ。

まとめ

先輩? :
どうかな?
サーバーサイドを学ぶことで、

  1. オンラインサービスを作成できる!
  2. ログインシステムを実装できる!
  3. プログラム単体で実行できる!
  4. 好きな時間にプログラムを実行できる!
  5. データベースを利用したシステムを作れる!
  6. WEBサーバーを作れる!
  7. サーバーサイドレンダリングができる!

少なくともこれだけの事ができるようになるんだから、今までよりもっといろんなシステムが作れるようになる。
学習のモチベーションは、上がったかな?

後輩? :
うん!もちろん!
何のために勉強するのか、具体的に想像することができて、すっごく助かったよ!
ありがとう!!

先輩? :
もちろん、僕の知識以外にも、出来るようになることはもっともっといっぱいあるとおもう。
もしかしたら、コメント欄で教えてもらえるかもしれないから、チェックしてみてね。

後輩? :
みなさま、コメント欄へのコメントお待ちしております!

先輩? :
後輩くん以外の駆け出しエンジニアのみんなにとっても、
この記事が学習のモチベーションを上げるきっかけになったら、とってもうれしいな!
長い話になりましたが、最後まで読んでくれて、どうもありがとうございました!

後輩? :
ありがとうございました!!

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

動的計画法について(トップダウン方式、メモ化、一次元)

paizaとかAtCoderとかやっていると、プログラムとして書けているはずなのに処理時間が遅くてアウト、というのが結構あって、この動的計画法について学んで、処理速度の向上を図ろうと思っているのですが、なかなかに理解しづらいので、少しずつ言語化していきます。

動的計画法の定義

  • 帰納的な関係の利用
  • 計算結果の記録

を満たすアルゴリズムの総称だそうです(Wikipedia)。とりあえずは適用条件等は省略します。
もう少しわかりやすくいうならば、漸化式の形で帰納的な関係を記述し、ここの計算結果について記録しておいて、その結果を参照しながら計算することによって、計算量を減らす手法ですね。

うん、全くもってわかりやすく言えてない。具体例(ベタなフィボナッチ数列)いきましょう。
フィボナッチ数列とは、a[0]=a[1]=1,a[n]=a[n-1]+a[n-2]で表される漸化式の一般解ですね。
(前二つの和によってその項の値が決まる)

普通に書いた時

fib.rb
def fib(n)
  if n <=1
    return 1
  else
    fib(n-1)+fib(n-2)
  end
end

n=gets.chomp.to_i
puts fib(n)

こんな感じ。これだと計算量はO(2^n)となり、nに代入する値が小さければ問題ないが40くらいからしんどくなる。

動的計画法(トップダウン)を用いた時

fib2.rb
@dp=[]
def fib2(n)
  if n <= 1
    return 1
  else
    if @dp[n]
      return @dp[n]
    else
      @dp[n] = fib2(n-1)+fib2(n-2)
    end
  end
end
n=gets.chomp.to_i
puts fib2(n)

@dpという配列(nがキーのハッシュのようなイメージで使う)を用意して、計算結果をこの配列に格納していきます。
計算時にはこの配列から値を参照して計算することで計算量がO(n)となり、計算量がガクッと減ります。
これならnに10000とかを代入しても一瞬です。

補足

配列を定義するときはインスタンス変数で。

最後に

なかなか理解が追いついてないのでおいおい学習しながら小出しにアウトプットしようと思います。

参考記事

https://ja.wikipedia.org/wiki/%E5%8B%95%E7%9A%84%E8%A8%88%E7%94%BB%E6%B3%95
https://www.jabba.cloud/20161020172918/

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

PAIZAのDランクで使ったメソッド一覧

はじめに

大前提として答えを掲載することは禁止となっている為、あくまでも参考程度でお願い致します。配列やハッシュは使わなくてもクリアできる為、詳しい説明は致しません。なお投稿者は駆け出しバリバリの実務未経験で、現在Bランクでありまだまだ知識不足が否めません。詳しいメソッドの説明は省くのでご了承をお願いします。

入力値の取得

入力例1(input)
  10

## 10を数値として取得
input = gets.chomp.to_i
=> 10

## 10を文字列として取得
input = gets.chomp.to_s
=> "10"

## 10を分割して数値として取得
input = gets.chomp.split("").map(&:to_i)
=> [1, 0]

## 10を分割して数値として取得
input = gets.chomp.split("").map(&:to_s)
=> ["1", "0"]


入力例2(input)
  10 20

## 10, 20を数値として取得
input = gets.chomp.split.map(&:to_i)
=> [10, 20]

## 10, 20を文字列として取得
input = gets.chomp.split.map(&:to_s)
=> ["10", "20"]


入力例3(input)
1
2
3

## 1,2,3を数字として取得
input = readlines.map(&:chomp).map(&:to_i)
=> [1, 2, 3]

## 1,2,3を文字列として取得
input = readlines.map(&:chomp)
=> ["1", "2", "3"]

readlinesメソッドは、B,Cランクになると使いづらくなるのであまりおすすめしない。

値の出力

出力例1
input = 1

## 1を数値として出力
puts input
=> 1


出力例2
input = ["1", "2", "3"]

## 1,2,3を数値として出力
puts input.map(&:to_i)
=> [1, 2, 3]

## 1だけ文字列として出力
puts input[0]
=> "1"

## 3だけ数値として出力
puts input[2].to_i
=> 3


出力例3
input1, input2, input3 = ["1", "2", "3"]

## 2だけ文字列として出力
puts input2
=> "2"

## 1だけ数値として出力
puts input1.to_i
=> 1

## "1 2 3"という文字列で出力
puts input1 + " " + input2 + " " + input3
若しくは
puts "#{input1} #{input2} #{input3}"
=> "1 2 3"

数値と文字列を意識しないと思い通りの出力ができないので気をつけましょう。

メソッド

mapメソッド

各要素に対してブロックを評価した結果を新しい配列にして返します。

input = [1, 2, 3]
puts input.map { |n| n * 2 }
=> [2, 4, 6]

input = ["1", "2", "3"]
puts input.map { |n| n.to_i }
若しくは
puts input.map(&:to_i)
=> [1, 2, 3]

joinメソッド

各要素を連結した文字列を返します。

input = ["a", "b", "c"]
puts input.join
=> "abc"

puts input.join(",")
=> "a,b,c"

puts input.join(" or ")
=> "a or b or c"

sliceメソッド

指定された自身の要素を返します。

input = ["a", "b", "c"]
puts input.slice(1)
=> "b"

## 1番目から2つ取得
puts input.slice(1, 2)
=> ["b", "c"]

## 破壊的メソッド
input.slice!(0)
puts input
=> ["b", "c"]

input.slice!(1, 2)
puts input
=> "a"

indexメソッド

文字列のインデックスから右に向かってを検索し、最初に見つかった部分文字列の左端のインデックスを返します。見つからなければ nil を返します。

input = [1, 2, 3, 4, 5]

puts input.index(2)
=> 1

puts input.index("2")
=> nil

input = "111111"
## "1"を文字列3番目から検索
puts input.index("1", 3)
=> 3

upcase, downcaseメソッド

全ての小文字を対応する大文字に置き換えた文字列を返します。(upcaseメソッド)
全ての大文字を対応する小文字に置き換えた文字列を返します。(downcaseメソッド)

input = "abcd 1234 GHJK !!"
puts input.upcase
=> "ABCD 1234 GHJK !!"

input = "abcd 1234 GHJK !!"
puts input.downcase
=> "abcd 1234 ghjk !!"

sub, gsubメソッド

文字列中でマッチした最初の部分を置き換えた文字列を生成して返します。
gsubメソッドはマッチした全ての部分を置き換えた文字列を生成して返します。

input = "aaaabbbb11112222"
puts input.sub("a", "A")
=> "Aaaabbbb11112222"

puts input.gsub("a", "A")
=> "AAAAbbbb11112222"

puts input.gsub("a", "A").gsub("1", "3")
=> "AAAAbbbb33332222"

lengthメソッド

文字列の文字数を返します。

input = "aaaabbbb11112222"
puts input.length
=> 16

input = "aaaa bbbb 1111 2222 !"
puts input.length
=> 21

countメソッド

レシーバの要素数を返します。

input = [1, 2, 3, 4, 4]
puts input.count
=> 5

puts input.count(4)
=> 2

## 各要素で2で割ってあまりが0のみ数える
puts input.count {|x| x % 2 == 0}
=> 3

reverseメソッド

文字列を文字単位で左右逆転した文字列を返します。

input = "paiza Drank 12"
puts input.reverse
=> "21 knarD aziap"

input = ["1", 2, "3", "true"]
puts input.reverse
=> ["true", "3", 2, "1"]

shiftメソッド

配列の先頭の要素を取り除いてそれを返します。引数を指定した場合はその個数だけ取り除き、それを配列で返します。

input = [1, 2, 3, 4, 5]
puts input.shift
=> 1
puts input
=> [2, 3, 4, 5]

input = ["a", "b", "c", "d"]
puts input.shift(3)
=> ["a", "b", "c"]
puts input
=> ["d"]

sumメソッド

要素の合計を返します。

input = [1, 2, 3, 4, 5]
puts input.sum
=> 15

input = ["12", "45"]
p input.sum
=> エラー

max, minメソッド

最大の要素を返します。(maxメソッド)
最小の要素を返します。(minメソッド)

input = [1, 2, 3, 4, 5]
puts input.max
=> 5

input = [1, 2, 3, 4, 5]
puts input.min
=> 1

input = ["abcde", "123", "ABCD"]
puts input.max
=> "abcde"

input = ["abcde", "123", "ABCD"]
puts input.min
=> "123"

odd?, even?メソッド

自身が奇数であれば真を返します。そうでない場合は偽を返します。(odd?メソッド)
自身が偶数であれば真を返します。そうでない場合は偽を返します。(even?メソッド)

puts 5.odd?
=> true

puts 2.odd?
=> false

puts 2.even?
=> true

puts 5.even?
=> false

absメソッド

値の絶対値を返します。

puts 100.abs
=> 100

puts -100.abs
=> 100

roundメソッド

自身ともっとも近い整数もしくは実数を返します。いわゆる四捨五入ですが、偶数丸めではありません。

input = 3.141592
puts input.round
=> 3

## 小数点第3位を四捨五入し値を返す
puts input.round(2)
=> 3.14

## 小数点第4位を四捨五入し値を返す
puts input.round(3)
=> 3.142

ceilメソッド

小数点切り上げで値を返す

input = 3.141592
puts input.ceil
=> 4

## 小数点第3位を切り上げて値を返す
puts input.ceil(2)
=> 3.15

## 小数点第4位を切り上げて値を返す
puts input.ceil(3)
=> 3.142

floorメソッド

小数点切り捨てで値を返す

input = 3.141592
puts input.floor
=> 3

## 小数点第3位を切り捨てて値を返す
puts input.floor(2)
=> 3.14

## 小数点第4位を切り捨てて値を返す
puts input.floor(3)
=> 3.141

終わりに

少しでも役に立てて頂ければ光栄です。間違い等がありましたらコメント等でお願い致します。時間に余裕があったらCランクで使ったメソッド等まとめる予定です!

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

クリックイベントでタブの切り替えをするときにハマったこと

背景

クリックしたときにタブの切り替えはできたが、肝心な内容がもともと両方とも表示されてしまっている
タブの切り替えだけ反映されても意味がないのだ...。?

仮説

jqueryの記述に問題があると仮説を立てた。

実装開始

調べてみると、show、hide、toggleというものを見つけた。

でも、よくよく考えてみるとこれらはクリックしたときに起こるイベントだから意味ないような...。

ということで、もっと調べてみると、cssのdisplay:none;と言うものを発見。

      %ul.container-body
        %li.container-body__item.show
          %p お知らせはありません
        %li.container-body__item
          %p やることはありません
      .container-body {
        width: 100%;
        &__item {
          display: none;
        }
        .show {
          display: block;
        }

こんな感じで、showになっている箇所はdisplay: block;で表示させ、隠したい箇所(showではない所)はdisplay: none;を付与してあげることで実現することができた。

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

【Ruby/Rails】スレッドエラー(ThreadError)をリトライする機構

概要

Thread.new()を使いマルチスレッド処理してたところ、Herokuにてリソース不足エラーが発生。

can't create Thread: Resource temporarily unavailable (ThreadError)

スレッドエラーのハンドリングと、ベストエフォートでスレッドを使うリトライ機構を作成した。


ちなみにHerokuで実行できるプロセス・スレッド数はかなり限られているので、ローカル環境との差異に注意。

https://devcenter.heroku.com/articles/limits#processes-threads

2020/08現在

free, hobby and standard-1x dynos support no more than 256
standard-2x and private-s dynos support no more than 512
performance-m and private-m dynos support no more than 16384
performance-l and private-l dynos support no more than 32768


ローカル環境がMacOSであれば、sysctl kern.num_taskthreadsというコマンドで1プロセスあたりの最大スレッド数が調べられる。

$ sysctl kern.num_taskthreads
kern.num_taskthreads: 4096

注意

通常より少し負荷がかかったらスレッドが不足するような処理は、まず設計ミスしている可能性が高い。

別サーバーに処理を委譲できないか、APIの仕様的に少ないリクエスト数で済ませる方法はないかなど、立てなければいけないスレッドの数を減らす方法を先に考える。

コード

def retry_threads(times: 3)
  try = 0
  begin
    try += 1
    Thread.new { yield }
  rescue ThreadError
    sleep(1 * try)
    retry if try < times
    raise
  end
end

使えるスレッドがない場合、数秒待ちリトライする。

1回目のリトライでは1秒、2回目のリトライでは2秒...と待ちの秒数を可変にし、タイムロスを防ぐ。

待ち時間をマイクロ秒にして、細かくリトライを管理するやり方もあり。


実際の使用方法

def heavy_task(url)
  # 重い処理
end

# urls = ["...","...",...]
threads = []
urls.all each do |url|
  threads << retry_threads{ heavy_task(url) }
end
threads.each(&:join)

ベンチマーク

このリトライ機構で、スレッドを立てる許容度が実際どれぐらい上がったのか調査した。

条件

  • 10秒かかる処理がリトライ対象
  • 1秒づつリトライ待ち時間を増やす
  • リトライは3回まで
  • ローカル環境で実行(MacOS Catalina)
    • 最大スレッド数: 4096
  • 処理できたタスク数をベンチマークの値にする

計測コード

def heavy_task
  sleep(10)
end

def retry_threads(times: 3)
  try = 0
  begin
    try += 1
    Thread.new { yield }
  rescue ThreadError
    sleep(1 * try)
    retry if try < times
    p $count
    raise
  end
end

def no_retry_threads()
  begin
    Thread.new { yield }
  rescue ThreadError
    p $count
    raise
  end
end

$count = 0

# retryなし
loop do
  no_retry_threads{ heavy_task }
  $count += 1
end

# retryあり
loop do
  retry_threads{ heavy_task }
  $count += 1
end

結果

ローカル環境(最大スレッド数: 4096)で、10秒かかるタスクを処理した数の値。

リトライなし リトライあり
4094 212888

参考

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

paramsを設定するときの自分用メモ

プログラミングを学び始めた初学者です。自分用のメモと、とりあえずQiitaを使ってみるという試みです。

controllerのコード

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end

  def new
  end

  def create
    Post.create(memo: params[:memo])
  end
end

createメソッド:モデル.create(カラム名:保存する値)

viewのコード

<h1>トップページ</h1>
<%= link_to "投稿ページ", '/posts/new'%>
<% @posts.each do |post|%>
  <div>
    <%= post.memo %>
    <%= post.created_at %>
  </div>
<% end %>

フォームの入力内容をデータベースから引っ張ってきて表示する部分<%= post.memo %>

memoと記載がある部分が連動していないと、フォームに入力された値が保存され、表示されないので注意が必要。Railsの基礎の流れがなんとなくわかってきたけど、paramsでちょっとつまずいたのでメモ。

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

Ruby 得点計算プログラム

簡易得点計算のプログラムを書いたので自分用備忘録

まずは下記コード

qiita.rb
k,n = gets.chomp.split(" ").map{|i| i.to_i}
a = []
d = []

k.times do
  d_1,a_1 = gets.chomp.split(' ').map{|i| i.to_i}
  d.push(d_1)
  a.push(a_1)
end

num = 0
check = []


while k > 0
  if d[num].between?(1,9)
    check.push(((a[num].fdiv(n) * 100) * 0.8).floor)
  elsif d[num] >= 10
    check.push(0)
  else
    check.push((a[num].fdiv(n) * 100).floor)
  end


  if check[num] >= 80
    puts "A"
  elsif check[num] >= 70
    puts "B"
  elsif check[num] >= 60
    puts "C"
  else
    puts "D"
  end
  num = num + 1
  k = k - 1
end

1つめのif文は提出期限に対する点数の減点の可否をチェックしてます。
提出期限(d_1)を想定して期限ないであれば原点なし
1~9日遅れたら二割減点
10日以上は0点にしてます。

2つめのif文で最終的な点数に対する評価を4段階で返しています。

今回新しく使ったメソッドは fdiv です。
一言で言うとto_fとほぼ同じです。

x / y.to_f
ってなるのを
x.fdiv(y)
って直しているだけ

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

【Ruby基礎】ブロックを使う頻度の高いメソッド

ブロック構文を使う頻度の高いメソッドを使用例と一緒にまとめます。

map(エイリアスメソッドはcollect)

各要素に対してブロックを評価した結果を新しい配列にして返す
空の配列を用意して、他の配列をループ処理した結果を空の配列に詰め込んでいくような処理の
大半はmapメソッドに置き換えられる。

コンソール
irb(main):026:0> numbers
=> [1, 2, 3, 4, 5]
irb(main):027:0> new_numbers = numbers.map {|n| n * 10}
irb(main):028:0> new_numbers
=> [10, 20, 30, 40, 50]
irb(main):029:0> numbers
=> [1, 2, 3, 4, 5]

select/find_all/reject

selectメソッドは各要素に対してブロックを評価し、その戻り値が真の要素を集めた配列を返すメソッドです。

コンソール
irb(main):003:0> numbers = [1,2,3,4,5,6]
irb(main):004:0> even_numbers = numbers.select {|n| n.even?}
rb(main):006:0> even_numbers
=> [2, 4, 6]

rejectメソッドはselectメソッドの反対で、ブロックの戻り値が真になった要素を除外した配列を返す

コンソール
irb(main):007:0> numbers = [1,2,3,4,5,6]
irb(main):008:0> non_multiples_of_three = numbers.reject {|n| n % 3 == 0}
irb(main):010:0> non_multiples_of_three
=> [1, 2, 4, 5]

&とシンボルを使って簡潔に書く

コンソール
irb(main):012:0> ['ruby', 'java', 'perl'].map(&:upcase)
=> ["RUBY", "JAVA", "PERL"]

参考文献

「プロを目指す人のためのRuby入門」

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

rescueとrescue_from

はじめに

プログラミングにおいてエラーは無いにこしたことはありませんが、開発者も人間なのでミスすることもあるし、ネットワークのエラーで開発者がどうしようもなくエラーが発生することもあります。
エラーと言っても色々な種類がありますが、「こういうエラーが起きた場合はこうする」という処理を書いておくべき場面は多々ありますが、そんな時に使えるrescueとrescue_fromについての基礎的な使い方です。

rescue

rescueを使うと、エラーが発生した場合それを拾って次に行う処理を書くことができます。
以下はrescueがない場合とある場合の違いです。

Userクラスはnameカラムしか持たないようにして、あるuserを作ります。

> user = User.new('山田')
=> #<User:0x00007ff0fa896758 @name="山田">

> user.name
=> "山田"

Userクラスにputs_infoメソッドを作り、実行します。

class UsersController < ActionController::Base

  def puts_info
    "名前は" + name
  end
end

> user.puts_info
=> 名前は山田

puts_infoメソッドに存在しないageメソッドを加え、実行します。

class UsersController < ActionController::Base

  def puts_info
    "名前は" + name
    "年齢は" + age
  end
end

> user.name
=> "山田"
> user.puts_info
=> NameError (undefined local variable or method `age' for #<User:0x00007fdeb98f3ed8 @name="山田">)

NameErrorになりました。
rescueを使って、エラーの場合の処理を書いてあげます。

class UsersController < ActionController::Base

  def puts_info
    "名前は" + name
    "年齢は" + age
  rescue => e
    "エラーです"
  end
end

> user.puts_info
=> "エラーです"

rescue_from

rescue_fromは、特定の種類または複数の種類の例外を1つのコントローラ全体およびそのサブクラスで扱えるようになります。

例:404エラー(そんなページありませんよ)の場合にrecord_not_foundというメソッドを実行させる。

class UsersController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, with: :record_not_found

  private

    def record_not_found
      render plain: "404 Not Found", status: 404
    end
end

例:adminユーザー以外はeditページに入れない、という仕様を実装する

1, Clientコントローラーで、editアクション実行前にbefore_actionで権限チェックの処理を行います。
check_authorizationメソッド内でcurrent_user.admin?を行い、管理者ではない場合は、raiseを使ってUser::NotAuthorizedという例外を発生させます。

class ClientsController < ApplicationController
  before_action :check_authorization

  def edit
    @client = Client.find(params[:id])
  end

  private

    def check_authorization
      raise User::NotAuthorized unless current_user.admin?
    end
end

2, ApplicationControllerに、User::NotAuthorizedというエラーの場合にuser_not_authorizedメソッドを実行させる処理を書きます。

class ApplicationController < ActionController::Base
  rescue_from User::NotAuthorized, with: :user_not_authorized

  private

    def user_not_authorized
      flash[:error] = "あなたはこのページにアクセスする権限がありません。"
      redirect_back(fallback_location: root_path)
    end
end

こうすることで、権限エラーの場合には「権限がありません」という表示をしてあげることができますし
、editアクション内では権限に関して心配する必要がなくなります。
edit以外にも管理者権限を必要とするメソッドがある場合には、before_actionを使ってcheck_authorizationを実行してあげればいいですね。

参考

https://railsguides.jp/action_controller_overview.html#rescue

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

rails tutorial 第5章

はじめに

独学でrails tutorialを進めていく過程を投稿していきます。

進めていく上でわからなかった単語、詰まったエラーなどに触れています。

個人の学習のアウトプットなので間違いなどあればご指摘ください。

初めての投稿なので読みにくいところも多々あるかと思いますがご容赦ください。

第5章 レイアウトを作成する

5.1.2 BootstrapとカスタムCSS

余談です。

リスト5.6でBootstrap CSSフレームワークを導入した後、ブラウザで確認をしてみました。
すると先の演習(5.1.1の演習3)により猫の画像が表示されたままだったのでCTRL+/でコメントアウトを試みましたところ

# <%= image_tag("kitten.jpg", alt: "cat image") %>

となりブラウザで確認をすると猫の画像は表示されたままでした。

どうやらerbをコメントアウトする場合は

<%#= image_tag("kitten.jpg", alt: "cat image") %>

とし、<%の後に#をつけるようです。
(次の演習でこちらの内容についてフォローされていました)

5.4.2 ユーザー登録用URL

演習1
もしまだ5.4.1.1の演習に取り掛かっていなければ、まずはリスト 5.41のように変更し、名前付きルートsignup_pathを使えるようにしてください。また、リスト 5.43で名前付きルートが使えるようになったので、現時点でテストが green になっていることを確認してください。(rails tutorial第4章より引用)

問題発生!!
成功するはずのテストでエラーが出ました。

ERROR["test_should_get_root", #<Minitest::Reporters::Suite:0x00000000091feba0 @name="StaticPagesControllerTest">, 0.5797893999842927]
 test_should_get_root#StaticPagesControllerTest (0.58s)
ActionView::Template::Error:         ActionView::Template::Error: Permission denied @ rb_file_s_rename - (C:/environment/sample_app/tmp/cache/assets/sprockets/v4.0.0/99/99m8UCKl4j8IpsVOK8ltLHyNh8Ae0nHw3GBkC34V_co.cache.47870560.18376.12839, C:/environment/sample_app/tmp/cache/assets/sprockets/v4.0.0/99/99m8UCKl4j8IpsVOK8ltLHyNh8Ae0nHw3GBkC34V_co.cache)

あれ?似たようなエラー3章で遭遇したような、、、
3章で参考にした記事
https://qiita.com/yasumichi/items/ccf1f7f57b6627034226

ということで改めて

> rails tmp:cache:clear
> rails assets:precompile

こちらのコマンドを入力し、テストを実行したら無事成功しました。

終わりに

今回の章はそこまで躓かずに終えることが出来ました。

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

【Ruby基礎】モジュールを学習してみた

1. モジュールの概要

モジュールは様々な用途で使用されます。

具体的には

  1. 継承を使わずにクラスにインスタンスメソッドを追加する。もしくは上書きする(ミックスイン)
  2. 複数のクラスに対して共通の特異メソッド(クラスメソッド)を追加する(ミックスイン)
  3. 関数的メソッドを定義する
  4. シングルトンオブジェクトのように扱って設定値などを保持する

上の定義だけでは分かりにくいので、実際にモジュールを作成しながら学んで行きましょう。

1. 2. モジュールの定義

モジュールの作り方

module モジュール名
# モジュールの定義(メソッドや定数など)
end

(例)

module.rb
# helloメソッドを持つGreeterモジュールを定義
module Greeter
 def hello
   'hello'
 end
end

一見クラスの定義と似ているがモジュールはクラスとは大きく違う

* モジュールからインスタンスを作成することはできない
* 他のモジュールやクラスを継承することはできない

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

【Ruby基礎】モジュールを学習してみた(一章)

1. モジュールの概要

モジュールは様々な用途で使用されます。

具体的には

  1. 継承を使わずにクラスにインスタンスメソッドを追加する。もしくは上書きする(ミックスイン)
  2. 複数のクラスに対して共通の特異メソッド(クラスメソッド)を追加する(ミックスイン)
  3. 関数的メソッドを定義する
  4. シングルトンオブジェクトのように扱って設定値などを保持する

上の定義だけでは分かりにくいので、実際にモジュールを作成しながら学んで行きましょう。

1. 2. モジュールの定義

モジュールの作り方

module モジュール名
# モジュールの定義(メソッドや定数など)
end

(例)

module.rb
# helloメソッドを持つGreeterモジュールを定義
module Greeter
 def hello
   'hello'
 end
end

一見クラスの定義と似ているがモジュールはクラスとは大きく違う

* モジュールからインスタンスを作成することはできない
* 他のモジュールやクラスを継承することはできない

参考文献

「プロを目指す人のためのRuby入門」

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

【Ruby】文字列の中で奇数番目の文字のみ出力したい

文字列の中で奇数番目の文字のみ出力したい

例えば、アルファベットの中で、奇数番目の文字のみを出力したい場合。

str = "abcdefghijklmnopqrstuvwxyz"

str.each_char.with_index{|c, index|
   print c if index % 2 == 0
}
# => acegikmoqsuwy

それぞれ解説していきます。
何か間違った事を記載してたらご指摘して頂けると嬉しいです。

each_char

String クラスの chars メソッド(別名 each_char)を使用すると、文字列内の文字を1文字ずつ取り出しながら処理することができます。

x = "TOKYO"
y = x.chars 
# => ["T", "O", "K", "Y", "O"]

each.with_index

eachで回しつつ、それぞれのデータに番号を付けたい場合に使う。
今回の場合だと、まず取り出された1文字1文字は変数であるcに入っている。
そして、each.with_indexにより番号が付けられた変数indexが2で割り切れてたら、変数cは出力するという形になっている。
2で割り切れたら偶数になるのだが、プログラミングの数字は0も含むので、逆転してしまっている。

str = "abcdefghijklmnopqrstuvwxyz"

str.each_char.with_index{|c, index|
   print c if index % 2 == 0
}
# => acegikmoqsuwy

参考文献

instance method String#each_char
https://docs.ruby-lang.org/ja/latest/method/String/i/each_char.html
大文字にして奇数番目のみ出力するプログラム
http://sinyt.hateblo.jp/entry/2013/12/22/183217
each_with_indexの使い方
https://qiita.com/tsuchinoko_run/items/5cef7dd9d8baf48ffde7

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

Nil location provided. Can't build URI.の解決

前提・実現したいこと

転職活動用ポートフォリオとしてダイエットアプリを開発中。
画像一覧ページに画像を表示させたい。

発生している問題・エラーメッセージ

Nil location provided. Can't build URI.

画像一覧ページでcurrent_userが投稿したpostの画像を表示しようとしたら、
このエラーが出た。

「画像がnil(ない)なので、画像のURLが作れないよ」と言ってる。

スクリーンショット 2020-08-23 午前0.02.27.png
確かに指摘されたとおり、データベースには画像なしの投稿が複数保存されている。

どうやらこれが原因らしい。

該当のソースコード

posts_controller.rb
posts_controller.rb
  def image
    @posts = Post.where(user_id: current_user.id)
  end

  private
  def post_params
    params.require(:post).permit(:food, :calorie, :protein, :fat, :carbo, :text, :image, :weight).merge(user_id: current_user.id)
  end
image.html.haml
image.html.haml
.content
  .images
  - @posts.each do |post|
    = image_tag post.image.url, class: "my-images"

試したこと(これで解決した)

画像ありのpostだけ取得する。

posts_controller.rb
posts_controller.rb
  def image
    @posts = Post.where(user_id: current_user.id).where.not(image: nil)
  end

これでimageカラムがnilのpostは取ってこないように指定したら解決した。

できあがった画像一覧ページ

スクリーンショット 2020-08-23 午前0.09.37.png

参考記事:[Rails]Where句にNOT NULLを指定する

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