- 投稿日:2020-11-18T23:13:50+09:00
非同期でいいね機能が切り替わらない時の解決方法
状態
同期状態でいいね機能を実装
そこから、非同期化
エラーもちらほらでましたが、エラーが出ないように修正。
エラーがなくなった、でも非同期でいいね機能が動かない。
って人は見てください。
同じようなパターンかもしれないです。
いいね機能を実装している方はたくさんいて
記事もたくさんありますので、自分と同じ状況を探してください。詳しい詳細は下記を見てください。
https://teratail.com/questions/304798非同期処理の流れをおさらい
Ajaxの流れ
①イベント発生
②非同期でリクエスト送信
③受け取った情報を処理
④処理結果をJSON形式で応答
⑤レスポンスを受けてDOMでページ更新自分の状態を確認することで原因がどこか見つける作業が早くなる。
イベントを発生しているかの確認だったら、アラートを出すように記述して確かめるみたいな感じですね。問題箇所
<tr class="idea_<%= @idea.id %>"> <%= render 'like', idea: @idea %> </tr>ここの記述が問題でした。
記事等を参考に書いていたのですが、tableタグを使用していないのにtrタグを使っていたのが原因でした。
タグの性質を理解せずに使っていたのが駄目だったわけですね。まさかと、思いdivタグに変更したらすんなりできました。
今回得た教訓は、タグの性質を知ること!
- 投稿日:2020-11-18T23:09:00+09:00
Week9
- source ~/school/multi/my_ruby/grad_members_20f/members/evendemiaire/post/assert.org
- 投稿日:2020-11-18T23:07:38+09:00
社会人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が何をするものかもわからないままにアカウントを作成しました。2ヶ月目からの活動予定(簡単に)
ポートフォリオの作成
言わずもがな、ポートフォリオの作成に取り掛かります。
テーマは決まっているので、がしがしコードを書いていきます。技術の勉強会に参加し、実際にWeb系エンジニアとして働いている方の話を聞く
転職するとは決めたものの、実際にどんな方が働いているのかあまりイメージがついていないところがあるので。
同じようなステップにいる転職を目指す方ともコミュニケーションをとってみたいです。技術的アウトプットを行う(Twitter,Qiita)
現状Twitterは勉強時間を垂れ流しにして記録しているだけ、Qiitaは見る専用になってしまっています。
ポートフォリオ作成の上でつまづいたところ、何か気になったところは発信しアウトプット癖をつけます。最後に
駄文ですが、読んでいただきありがとうございます。
SIer→Web系エンジニアの転職を目指す方は多いと思うので、
今同じステップにいる方に少しでも参考にしていただけたら嬉しいです。
- 投稿日:2020-11-18T22:30:42+09:00
スクールに通い始めて
はいどうも!
某プログラミングスクールに通い始めた37歳のおっさんです。
プログラミングは完全に未経験。
そんなおっさんが転職するまでの軌跡(奇跡?)をぼちぼち綴っていこうと思います。先日、中間試験なるものを受けてみたところなんと、44点という結果・・・
学生時代に受けたテストでもこんな点数は取った記憶がなかった。
問題文を見ても何を求められているかわからないし、そもそもコードを読んでてもわからない部分が多すぎて理解ができない。
あれ?これってもしかして記憶力がだいぶ悪くなってる?
そう、気付き始めてしまったのです。
同期の人々よりも時間をかけて自分なりにわかる説明をしてくれる記述を手繰り寄せてなんとか消化する。
google先輩の力でなんとかやってきました。
自分がスペックとして他人に勝てるのは①「情熱」と②「モチベーション」この二つだと思ってます。
①他人が休んでる時にしっかり学習して
②他人が学習しているときに倍の密度で学習する
①も②も関係ないやないかーい!
というツッコミはおいておいて。実際勝てる方法はこれしかないと思ってます。
そして、後がない。
同年代は役職に就いて、レベルアップした内容の仕事をして、家も買ってetc
人生というストーリーで見たらきっといい結末が待っているんじゃないかなと思える内容を歩んでいるわけです。
年齢という取り返せない条件で負けている以上、自分に何か付加価値がないと同じスタートラインに立った時にかなり後ろからスタートしなければいけないわけです。
人よりできないぶん、時間をかける
人より遅いぶん、密度を濃くする
この二つで対応していくのが絶対条件なんじゃないかなと思っています。
そりゃあ負けたら悔しいですよ。
だけど悔しがったってしょうがない。
もう結果として出てしまっているんだから受け止めるしかない。
考えなければいけないのは、じゃあこれからどうするか?っていうところだと思います。
負けた→もういいや→向いてなかった!goodbye!
この流れは簡単です。誰でもできる。
ここから反発してどれだけできるかだと思うんですよね。「諦めたらそこで試合終了ですよ」
有名なバスケットボールの監督が言ってましたね(ちょうど世代)
この言葉を胸に胸に刻んで明日からも頑張ります。ちなみに
「〜していきます」って希望的欲求感に聞こえませんか?
「〜します」って言い切ると決断なんだなって聞こえますよね。
決断すると自分がその目標に向かって行くんですよ。
○○ザップのコミットメント、と同じですよね。
言ったからにはやらなきゃいけない。
良い意味で自分を追い込んで頑張る。
その背中が他の人に勇気を与えられたらいいなって思います。三回目に受けた試験でようやく突破したおっさんより
それではでは
- 投稿日:2020-11-18T22:16:43+09:00
[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として明記してあげたらうまく機能しました。
- 投稿日:2020-11-18T22:12:28+09:00
【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ガイド様にはこれからもお世話になります。
- 投稿日:2020-11-18T21:59:40+09:00
開発環境別に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["場所"] ) end3. 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=test5. コンソールで導入されたデータの確認
環境別にコンソールを立ち上げて初期データの件数を確認します。
-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 を使う方が良さそうです
- 投稿日:2020-11-18T21:09:43+09:00
[初心者]ブラウザでRailsのルーティングを確認する方法[小ワザ]
- 投稿日:2020-11-18T20:37:17+09:00
Railsを使ったToDoリストの作成(4.CRUDのRead機能)
概要
本記事は、初学者がRailsを使ってToDoリストを作成する過程を記したものです。
私と同じく初学者の方で、Railsのアウトプット段階でつまづいている方に向けて基礎の基礎を押さえた解説をしております。
抜け漏れや説明不足など多々あるとは思いますが、読んでくださった方にとって少しでも役に立つ記事であれば幸いです。環境
Homebrew: 2.5.10
-> MacOSのパッケージ管理ツールruby: 2.6.5p114
-> RubyRails: 6.0.3.4
-> Railsnode: 14.3.0
-> Node.jsyarn: 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.rbclass BoardsController < ApplicationController def index @boards = Board.all end end #インスタンス変数(@xxx)に代入したものはhaml内で<%=%>で囲うことで #Viewsで自由に表示することができます。先ほど全てのデータを取ってくるとは言ったものの、現在はデータが1つしかないためダミーデータを作成します。
ダミーデータを作成するためには
db/seeds.rb
にレコードを書きます。db/seeds.rbBoard.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があるのでインストールして使っていきましょう。
Gemfilegem 'faker'Gemfileに
gem 'faker'
と書いたら、bundle install
をします。
これでfakerのインストールは完了です。では、実際にseeds.rbでfakerを使用してみましょう。
今回はfakerの中でも文章を作成してくれるFaker::Lorem
を使用しています。
また、より多くのレコードを作成したいので10回Board.createを繰り返しています。db/seeds.rb10.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.rbRails.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で確認すると全てのアクションのルーティングが定義されていることがわかると思います。
2 コントローラ
では、次にコントローラでデータを取得します。
データを取得するためにActiveRecordのfindメソッドを使用します。
findメソッドはテーブルから引数に入っているidのレコードを取ってきます。app/controllers/boards_controller.rbclass 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'ここではカード全体をリンクにしています。
では具体的にコードを見ていきましょう。重要なのは以下の一文です。ここでリンクを作成し、ネストすることでカード全体をリンクにしています。
= 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
と書くこともできます。
- 投稿日:2020-11-18T19:46:23+09:00
アラビア数字をローマ数字に変換する
概要
アラビア数字を受け取り、ローマ数字を返す 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 MCMXCIXRuby初心者なので、キレイなコードではないかもしれません
これからもっとRubyについて学んでいきたいと思います!
- source ~/doc/lecture/multi-scale/grad_members_20f/members/yukiue/docs/roman_numerals.org
- 投稿日:2020-11-18T18:45:06+09:00
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.rbgem "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.txt2 3
main.rb
と同じディレクトリにdata.txt
を置きます。まとめ
- Windows10 の VSCode の Ruby でデバッグができるようになった
参照したサイト
VSCodeでrbenv環境のrubyのデバッグ、コードコンプリートができない
Ruby AtCoder向けVSCode設定
- 投稿日:2020-11-18T17:41:15+09:00
Sessionとcookieをがよくわからないので、できるだけかみ砕いてまとめたよ
SessionとCookie
Railsチュートリアルでログイン機能の実装まで学習したが、いかんせん【わからない】
本当に分からない。自分が今何をやっているか理解できない。
こういう「分からない事が分からない」のは基礎的な知識が抜け落ちているパターンが多い。どの基礎を抑えれば理解できるのかわからないが、まずSessionとCookieというこの2つのキーワードがよくわからないので、自分なりにまとめていく。
私は現職が医療従事者であり、専門知識の理解は対象(自分 or 他人)が理解できる内容までかみ砕く(対象によって流動食、ペースト食とレベルを変える)必要があると考えている。
このページでの内容はプログラミング学習前の自分レベルでも理解できるように、出来る限りかみ砕いた。内容を簡素にしていくと、認識違いやそもそも間違ってるでという事も起きる。
その場合はご指摘、ご助言いただけるとうれしいです。
※個人的ではあるが、感覚的に理解しやすいようにwebサーバーを「webページ」・ブラウザを「ユーザー」と表現している。
webの仕組みから始まる。
webの仕組みは
- ユーザーがwebページにアクセスする(リクエストする)
アクセスしたページが表示される。(レスポンスが返ってくる)
次のページにアクセスすると、①で送られてきたユーザーのリクエストは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の流れはこうなる。
ユーザーがwebページにアクセスする
リクエストされた画面を返す。この時①でユーザーから送られてきた情報を元にwebページはプログラミングで指示された処理をしてユーザーに画面を返す
(例:「あなたはこのサイトに○○回訪問したよ」とか「指示された色に画面の色を変えたよー」とか。この時にcookieというメモ書きも一緒につけている例でいうと「○○回」「○○色」など)webページからついてきたメモをユーザーが保存します。次のページにアクセスする時にこのメモ書きの情報も一緒につけて、webページに要求します。
本来のHTTP通信であれば情報は引き継がれないが、メモ情報がついているのでそのメモ情報を元にユーザーに適した画面が表示される。
このcookieはユーザー(ブラウザ)が保存しているのでデベロッパーツールで編集や削除ができる。つまりメモの内容を見れるし、書き換えられるし、捨てる事もできる。
そのため、大事な情報はcookieで扱ってはいけない(ログイン情報とか)Sessionとは
Sessionは情報をwebページ側で保存するのでデベロッパーツールで触る事はできない。
つまり安全である。ただしwebサーバー上で保存しているので、アクセスしてきた人がAさんなのか、Bさんなのか、Cさんなのか識別しないといけない。(これはブラウザが変わっても同様の事が言える)
ユーザー(ブラウザ)とwebページで保存されているsessionを紐づけてあげる必要がある
sessionではそのためだけにcookieを使う。sessionの流れは
- ユーザーがwebページにアクセスする
- リクエストされた画面を返す。①でユーザーから送られてきた情報を元にwebページはプログラミングで指示された処理をしてユーザーに画面を返す。そして情報をサーバー側で保持&Session情報をcookieとしてセットするよう送ってくる。
ユーザーはsession情報をセットする。次のページにアクセスする時に、このセッション情報を送信してwebページに要求します。
本来の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
- 投稿日:2020-11-18T16:56:51+09:00
Rails エラーメッセージの日本語化
はじめに
オリジナルアプリを制作しています。完成後は知人に使ってもらい、感想をフィードバックする予定です。その知人は日本人なので、エラー文は日本語にした方がより親切だと思いました。そのため、エラーメッセージの日本語化を行いました。
目次
1.日本語の言語設定
2.Gemfileの追加
3.日本語化ファイルの作成
4.追加で日本語翻訳文言の設定1.日本語の言語設定
config.i18n.default_locale = :jaを追加する。この「ja」は日本語を表す。
config/application.rbmodule Pictweet class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 6.0 # 日本語の言語設定 config.i18n.default_locale = :ja # 省略 end end2.Gemfileの追加
Gemfileに追加後、忘れずにターミナルでbundle installを実行、サーバーを再起動する。Gemfilegem 'rails-i18n'3.日本語化ファイルの作成
様々な言語に対応する言語ファイルをlocale(ロケール)ディレクトリに作成する。ここでは日本語化ファイルを作成する。ファイルの中身は下記リンク先の内容をコピペする。これにより、コピペした内容に該当するエラー文は日本語化される。
config/locales/devise.ja.ymlja: activerecord: attributes: user: confirmation_sent_at: パスワード確認送信時刻 confirmation_token: パスワード確認用トークン #------------------省略------------------------------ not_saved: one: エラーが発生したため %{resource} は保存されませんでした。 other: "%{count} 件のエラーが発生したため %{resource} は保存されませんでした。"4.追加で日本語翻訳文言の設定
最後に日本語文の追加設定を行う。同じくlocalesディレクトリにja.ymlファイルを作成する。追加したい日本語とその英語を記述することで、カスタマイズできる。
config/locales/ja.ymlja: activerecord: attributes: user: nickname: ニックネーム desk: title: 画像タイトル image: 投稿画像 suggestion: place: 掃除場所 period_cleaning: 清掃期間 last_cleaned_date: 最後に清掃した日以下参考画面
- 投稿日:2020-11-18T16:42:09+09:00
スコープの概念
1.スコープの概念 ~初心者編~
定義した変数が使える範囲をスコープと表現します。メソッドによって若干異なるので代表的なものをまとめます。
def, time, each メソッド
def, time, each メソッドの中で定義した変数はメソッドの外で使うことはできない。
一方で、メソッドの外で定義された変数はtime, eachメソッドは条件なく使えます。しかし。def メソッドに関しては引数が必要となります。
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.さいごに
拙い文章で分かりにくい点があるかもしれませんが、何か誤りなどご指摘ありましたらコメントお願いします。
- 投稿日:2020-11-18T16:16:27+09:00
マルチスケールシミュレーション特論:第 9 回をまとめてみた
assert_equal について
今後の授業の方針として、テスト駆動開発を行っていく。assert equal とはequal かどうかを確かめる(assert)関数であり、この関数を使って授業を進める
colorize
- 始める時に colorize というライブラリがないと怒られたなら以下のコマンドを行う
sudo gem install colorize
この際に関学のプロキシーでライブラリのインストールができない人は以下のコマンドを使用する
sudo gem install colorize -r -p http://proxy.ksc.kwansei.ac.jp:8080gem とは?
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
- 投稿日:2020-11-18T16:10:04+09:00
【Rails】お気に入り機能の実装手順を公開!!【簡単にできます】
あおい(https://twitter.com/aoi_programming )です。
Ruby on Railsでお気に入り機能を実装したので手順を公開します。実装したい機能
- お気に入り登録・解除できること
- 未ログイン時にはお気に入りできない
- 未ログイン時にはお気に入り一覧ページにアクセスできない
- お気に入りボタンをクリックするとAjax通信でデータ保存ができる
1. ブランチ作成
git checkout -b favorites2. Favoriteモデルの作成
% rails g model Favorite user_id:integer post_id:integer3. 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:migrate4. 関連付け
favorite.rbclass Favorite < ApplicationRecord belongs_to :user belongs_to :post enduser.rbhas_many :favorites, dependent: :destroypost.rbhas_many :favorites, dependent: :destroy5. バリデーションの追加
favorite.rbbelongs_to :user belongs_to :post validates :user_id, presence: true # 追記 validates :post_id, presence: true # 追記6. コントローラー作成
% rails generate controller Favorites7. コントローラー編集
favorites_controller.rbclass 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 end8. ルート編集
route.rbpost "favorites/:post_id/create" => "favorites#create" delete "favorites/:post_id/destroy" => "favorites#destroy" end9. お気に入り登録のメソッドを作成
user.rbdef 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? end10. 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/~.scsslike { color: gray; } unlike { color: red; }14. 表示させたいページに追加
~.erb<%= render 'users/favorite_form' %>以上です!
参考になった方はLGTMお願いします?♂️
- 投稿日:2020-11-18T16:01:02+09:00
マルチスケールシミュレーション特論 第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[0mref: 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
- 投稿日:2020-11-18T15:53:25+09:00
再帰関数【配列のとりうる組み合わせについて】
取り組んだ経緯
とある会社との面談の際に、再帰関数について質問されて、
まともに答えられず悔しかったので
タイトルに表記したアルゴリズム問題に挑戦して取り組んでみることにしました。問題
- 数字の配列から数値を0〜n(配列数)個取った組み合わせを求める。
- 組み合わせの数字を合計し、その結果を重複なく出力する。
問題の具体例
- 数字の配列[1,2,3,4,5,6]がある
- 数字の配列を取りうるパターン(数式でnCr)を求める。
- nの最大値は6(配列の中身の数である)
- 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.rbclass Array def combination_array(pick_num(nCrのrに相当)) # 配列からゼロ個取る場合ゼロを返す 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感想
再帰関数について、なんとなくわかったような状態になった気がします。
今後は、アルゴリズム問題に挑みつつ再帰関数の理解を深めたいと思います。また、課題としては、負荷の少ない記述方法を考えて、
大規模演算の処理ができるようにすることだと考えております。参考
- 投稿日:2020-11-18T15:14:21+09:00
herokuで環境変数を設定する際の注意点
この投稿について
ポートフォリを作成の際にGoogle認証を取り入れたいと思い、ユーザー登録や、ログインを行ってくれる"Omniauth"というGemを導入しました。
ところが、環境変数に設定しようとした際に、うまく設定できなかったため、どんな理由でエラーが起きて、どのように解消できたのかを記事にしています。herokuに環境変数を設定するとき
heroku config:set
というコマンドを使用します
実際には、
heroku config:set GOOGLE_CLIENT_ID="~~~~~"
このようなコマンドで追加します。
このとき、 『=』と『""』の間にスペースを入れるとエラーが発生してしまいます。heroku config:set GOOGLE_CLIENT_ID = "~~~"
このようにしてしまうとエラーが発生してしまうので、設定できませんでした。
なので、まとめると環境変数に設定するときは、無駄なスペース入れず、コピペして貼ってしまうことをお勧めします。
- 投稿日:2020-11-18T13:56:54+09:00
タダ飲みコーヒー
[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になるまで繰り返しているのでしょう。繰り返し文が苦手だという事が分かりました。
以上!悔しい
- 投稿日:2020-11-18T13:51:04+09:00
Rubyのお勉強<変数とMethod>
変数
受けとった引数ARGV[0]を適当な変数に代入して出力せよ.
前回もそれっぽいことした気が…
name = ARGV[0] puts name #puts "#{name}"でも可で
$ ruby name.rb Dora DoraRubyでは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
- 投稿日:2020-11-18T13:49:58+09:00
Rubyのお勉強<入出力編>
Rubyのお勉強<入出力編>
出力編
とりあえず,出力させますか.
まぁ,おなじみ"print("Hello World")"ですか?
print("Hello World")$ ruby print.rb Hello World$改行されない...
Rubyの出力方法はいろいろある.
method 引数に指定した値を出力 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でも可能).
締め
基礎の入出力についてお勉強.
- source ~/school/multi/my_ruby/grad_members_20f/members/evendemiaire/post/ruby_put.org
- 投稿日:2020-11-18T13:48:40+09:00
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) endOK.
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てな感じにもできるみたい.
締め
今回は条件処理について学んだ.
- source ~/school/multi/my_ruby/grad_members_20f/members/evendemiaire/post/if_case.org
- 投稿日:2020-11-18T13:46:04+09:00
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 NAMErakeって打つと,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
- 投稿日:2020-11-18T11:56:52+09:00
【Ruby on Rails5】フラッシュメッセージをいい感じに表示させる。
環境
Ruby 2.5.7
Rails 5.2.4前提
形を問わずフラッシュメッセージの表示が確認できる状態。
turbolinksは切っています。オンの状態で動作確認はしていません。gem
gem 'devise'
経緯
タイトルに「いい感じ」と表現しましたが、他に言葉が見つかりませんでした・・・笑
ユーザーが何かアクションを起こした時に、それをお知らせする方法としてページ内へのフラッシュメッセージの埋め込みがありますが、それをただ表示させるのは物足りないと感じて今回の形を実装してみました。(AbemaTVのアプリから発想を得ました笑)目標
ログイン・ログアウトをメインに、アクションを起こすごとに下の方からニュッっとお知らせが一定時間出てくる。
手順
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.scssmain { .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.jsaddEventListener('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.rbclass 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
- 投稿日:2020-11-18T10:17:19+09:00
ファイルで定義したメソッドの一覧を取得する
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
- 投稿日:2020-11-18T09:04:31+09:00
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名
- 投稿日:2020-11-18T08:43:53+09:00
【Rails非同期処理】Sidekiq vs Resque vs Delayed Job 〜Active Jobとは〜
1. Active Jobとは
バックグラウンドで実行するジョブをRailsアプリケーションで動かすための共通インターフェイスです。
例えばメール送信やCSVアップロード等の時間がかかる重い処理はバックグラウンドで実行することが多いです。Rails 4.2で導入された機能で、非同期処理を実装する際にまず検討する事が多いかと思います。
2. 共通インターフェイスって?
Active Jobが導入される以前のRailsバージョンの場合、非同期処理を実装するGemを使用していました。
代表的なものではDelayed JobやSidekiq、Resqueがあります。当然それぞれの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.rbclass 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.rbclass SampleJob < ApplicationJob queue_as :default def perform(*args) puts '--------------------------------' puts '------------ Test ------------' puts '--------------------------------' end endrailsコンソール上で下記のようにJobを実行できます。
# 「キューイングシステムが空いたらジョブを実行する」とキューに登録する > SampleJob.perform_later # 明日正午に実行したいジョブをキューに登録する > SampleJob.set(wait_until: Date.tomorrow.noon).perform_later # 5秒後に実行するジョブをキューに登録する > SampleJob.set(wait: 5.second).perform_laterperformメソッドには実際には非同期で実行する重い処理が実装されることになります。
かんたんに実装できましたが、一つ問題があります。Active JobはキューをRailsのメモリ内に保持するため、Railsを再起動するとジョブは失われてしまいます。
Railsガイドには以下のような記述があります。
デフォルトのRailsは非同期キューを実装します。これは、インプロセスのスレッドプールでジョブを実行します。ジョブは非同期に実行されますが、再起動するとすべてのジョブは失われます。
ーー
Rails自身が提供するのは、ジョブをメモリに保持するインプロセスのキューイングシステムだけです。 プロセスがクラッシュしたりコンピュータをリセットしたりすると、デフォルトの非同期バックエンドの振る舞いによって主要なジョブが失われてしまいます。アプリケーションが停止、または再起動した際に、ジョブが失われないように、Railsで使うべきサードパーティのキューイングライブラリ、 Active Job のアダプタを決める必要があります。
3. アダプタはどれがいいのか?
代表的なものは、Delayed JobやSidekiq、Resqueがあります。
プロダクトの規模や要件に応じて技術選定することになると思いますが、かんたんに比較してみます。
ストレージ プロセス/スレッド 処理速度 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
パフォーマンスや大量のジョブが発生する場合 ----> SidekiqActive Jobか純粋なGemを使用するかは、大きなアプリケーションで複雑な仕様の場合、純粋なGemを使うほうが選択肢が広がりそう。
ただし、最新版のRails + Sidekiq の環境であれば、Active Jobも検討できる。小規模 or 個人開発レベルであれば、Active Jobに沿って実装すれば良いのでは?といった感じ。
参考サイト
やはり原典をあたるのが一番良く、この辺りは情報が充実していました(英語ですが)。
Qiita記事も大変参考になり、ありがとうございました。
- 投稿日:2020-11-18T08:07:23+09:00
【Rails】コールバックについて
基本的な事項ですが忘れやすいので備忘録かつ、まとめとして記事にすることにしました。
【参考記事】
Railsのコールバックコールバックとは?
モデルを保存、更新、削除後や削除前のタイミングで実行されるメソッドのこと指す。
例) 記事を投稿し保存後には〜をする。before_save => #保存前 after_save => #保存後 after_update => #更新後 before_destroy => #削除前 after_destroy => #削除後コールバック使用例
before_save(保存前)コールバック後にメソッド実行する例を挙げる。
user.rbclass << self before_save :downcase_email def downcase_email self.email = email.downcase end上記コードはemail保存前に、大文字のローマ字がある場合、小文字に変換するメソッド。
他の分かりやすい例としては、posts_controller.rbclass ShopsController < ApplicationController before_action :logged_in_user上記はpostsコントローラーの各アクション前にログインユーザーであることを指定している。
通販で何か購入する時、ログインしていない場合はログインまたは会員登録を促されると思われるが、ログインユーザーでないため、弾かれる(before_action :logged_in_user)のもコールバックによりログインを要求しているものと言える。学び
普段何気なく上記のメソッド(before_action)等は使用していると思われるが、改めて「Railsにおけるコールバックを端的に説明せよ」と言われるとビシッと答えられない初学者の方が意外に多いと思います(自分もそうです)。意味のないコードなどなく、それぞれが意味を持っているので、普段から自分が書いたコードについて自信を持って説明できるように学習を励んでいきたいと思いました。Railsはとっつきやすいと言われていますが、本当に理解して使いこなそうとするとかなり奥が深いものであることを学びました。