9/7 ~ ## 2021年9月7日(火) 現場Rails Chapter7-5-4 ファイルをアップロードしてモデルに添付する ## Active Storageとは何? Railsガイド([Active Storageの概要](https://railsguides.jp/active_storage_overview.html]))には以下のように書かれています。 > Active StorageとはAmazon S3、Google Cloud Storage、Microsoft Azure Storageなどの クラウドストレージサービスへのファイルのアップロードや、ファイルをActive Recordオブジェクトにアタッチする機能を提供します。development環境とtest環境向けのローカルディスクベースのサービスを利用できるようになっており、ファイルを下位のサービスにミラーリングしてバックアップや移行に用いることもできます。 アプリケーションでActive Storageを用いることで、ImageMagickで画像のアップロードを変換したり、 PDFやビデオなどの非画像アップロードの画像表現を生成したり、任意のファイルからメタデータを抽出したりできます。 今までは、CarrierWaveを使って画像ファイルをアップロード・管理するのが主流でした。しかし、既存のテーブルにカラムを追加するなど、導入するのに手間が掛かりました。そのため、**Rails 5.2からActive Storageがデフォルトで導入**されました。 ## Active Storageが提供するメソッド **ActiveStorage**を用いることで、さまざまなメソッドが使えるようになります。 **attach**メソッド・・・IOオブジェクトを渡すことで、対象のオブジェクトに対してファイルを直接紐づけることができます。 ```ruby= @message.image.attach(io: File.open('/path/to/file'), filename: 'file.pdf') ``` **attached?** メソッド・・・レシーバーのオブジェクトに画像が紐づいていれば、trueを返し、紐づいていなければfalseを返します。 ```ruby= @message.images.attached? ``` **purege**メソッド・・・オブジェクトと添付ファイルの紐付けを解除する。実行すると、対象のblobファイルとファイルデータがDBから削除されます。 ```ruby= user.avatar.purge ``` **variant**メソッド・・・blobから画像を呼び出す際に変換処理を行うことができます。このメソッドを用いるには、gem 'image_processing' を導入する必要があります。 ```ruby= <%= image_tag user.avatar.variant(resize: "100x100") %> ``` `image_tag` を使う場合は画像が添付されていないとエラーになってしまうため、`if @message.images.attached?` で判定メソッドを使って画像が添付されているときだけ`image_tag` を表示するように使用できます。(画像は添付必須ではない事が多いのではないかと思います) ```ruby= if @task.image.attached? image_tag @task.image else image_tag 'default' end ``` ※ストロングパラメータにActiveStorageで追加したパラメータを許可することを忘れずに行うとよいでしょう。 ## CSVファイルとは何? [CSVとは](https://www.wakarutodekiru.com/blog/tips/578.html)、Comma-Separated Valuesの略で、カンマ区切りのテキストデータという意味です。CSVファイルは、CSV形式で記述されたファイルのことを指します。CSVは各ソフトウェアで共通している形式であり、CSVを使うことで、異なるソフトウェアでのデータ移行が簡単に行える。 ## CSVの出力 app/models/task.rb ```ruby= # どの属性をどの順番で出力するかを定義してcsv_attributesメソッドで要素に属性名の文字列を持つ配列を作成します。 def.csv_attributes ["name", "description", "created_at", "updated_at"] end # CSV形式でインスタンスの中身を出力できるようにします。 def self.generate_csv CSV.generate(headers: true) do |csv| # headers: trueはデータベースの一番上の行をCSVのレコードのタイトルを指定します。 # csvに上記で定義したcsv_attributesを一行目として代入します。 csv << csv_attributes all.each do |task| # eachでtaskを一つ一つ取り出してtaskの値を代入していきます。 csv << csv_attributes.map{ |attr| task.send(attr) } # mapはcsv_attributesの配列の中身全てに対して処理を行います。 # task.sendは引数に指定したメソッドを文字列で返します。 # 文字列として返った属性名の中身をCSVに追加します。 end end end ``` コントローラ内でレスポンスの形式をcsvで指定してあげることで、そのアクションにリクエストが飛んできた時の、レスポンスをcsv形式で返します。 ```ruby= def index ~ 略 ~ respond_to do |format| format.html format.csv { send_data @tasks.generate_csv, filename: "sample.csv"} end end ``` generate_csvで生成されたcsv形式のデータをsend_dataメソッドでリクエスト先に返しています(ダウンロード)。 **strftimeメソッド**は日時データを指定したフォーマットで文字列に変換することができるメソッドです。 ```ruby= blog = Blog.find(1) # strftimeメソッドを使い指定した形で文字列として出力 blog.created_at.strftime("%Y年%m月%d日") => "2019年07月26日" ``` ____ ## 参照 Railsガイド(Active Storageの概要) https://railsguides.jp/active_storage_overview.html CSVファイルとは何?何のために使うの? https://www.wakarutodekiru.com/blog/tips/578.html ## 2021年9月9日 現場Rails Chapter7 ## CSVデータをインポートする際に使われるメソッド #### [Csv.foreach](https://docs.ruby-lang.org/ja/latest/method/CSV/s/foreach.html) 1行ずつ処理をするため、行数が多いCSVファイルを扱う場合でもメモリ使用量を気にしないで使えます。 #### [slice](https://docs.ruby-lang.org/ja/latest/method/Hash/i/slice.html) 引数で指定されたキーとその値だけを含む Hash を返します。 #### [to_hash](https://docs.ruby-lang.org/ja/latest/method/CSV=3a=3aRow/i/to_hash.html) to_hashメソッドを呼ぶことで、次のようなハッシュの形に変換されます。 ```ruby= {"name"=>"たすくのなまえ", "description"=>"", "created_at"=>2021-09-07 02:30:00 UTC", "updated_at"=>"2021-09-09 09:30:00 UTC" ``` ## CSVデータを入力(インポート)する ```ruby= # app/models/task.rb def self.import(file) # headers: trueで1行目をヘッダーとして扱うことができる。 # headers: trueとすることで、DBに登録する際に1行目をレコードとして登録せずに、2行目以降からレコードしてDBに登録するようにしている。 CSV.foreach(file.path, headers: true) do |row| task = new task.attributes = row.to_hash.slice(*csv_attributes) task.save! end end ``` attributes = は、変数ではなく特定のattributeを変更するメソッドです。 オブジェクトの属性を変更しただけで、DBには保存されていないため、saveします。 #### 可変長引数 引数に* をつけると、個数に制限なく、引数を渡せる可変長引数というものになります。可変長引数を使うことで、引数を配列として受け取ることが出来ます。 ```ruby= a = [1, 2] b = [1, 2, 3] p a.push(b) #=> [1, 2, [1, 2, 3]] p a.push(*b) #=> [1, 2, [1, 2, 3], 1, 2, 3] ``` ## 参照 【Ruby】よく使う、CSVライブラリを使ったCSV操作 https://qiita.com/mogulla3/items/2d2053f0e4c13ba3b6dc Ruby 3.0.0 リファレンスマニュアル ライブラリ一覧 csvライブラリ CSV::Rowクラス https://docs.ruby-lang.org/ja/latest/class/CSV=3a=3aRow.html#I_ROW instance method Hash#slice https://docs.ruby-lang.org/ja/latest/method/Hash/i/slice.html Active Recordのattributesの更新メソッド https://morizyun.github.io/ruby/active-record-attributes-save-update.html Ruby 3.0.0 リファレンスマニュアル ライブラリ一覧 組み込みライブラリ Hashクラス https://docs.ruby-lang.org/ja/latest/method/Hash/i/slice.html ## 2021年9月10日 現場Rails Chapter7 ## [9/9朝会の続き](https://morning-6.hatenablog.com/entry/2021/09/09/092852) 前回にモデルファイルで実装したimportメソッドを使用して CSVデータをインポート出来るボタンを実装していきます。 ```ruby= # app/controllers/tasks_controller.rb def import current_user.tasks.import(params[:file]) redirect_to tasks_url, notice: "タスクを追加しました" end ``` ```ruby= # routes.rb resources :tasks do post :confirm, action: :cofirm_new, on: :new post :import, on: :collection end ``` ```ruby= # tasks/index.html.slim = link_to 'エクスポート', tasks_path(format: :csv), class: 'btn btn-primary mb-3' = form_tag import_tasks_path, multipart: true, class: 'mb-5' do = file_field_tag :file = submit_tag "インポート", class: 'btn btn-primary' ``` **form_withのmultipartオプション** モデルと紐づかない**form_with**使用時にファイルや画像データを送信する場合に指定するオプションです。 この場合の**form_with**で、**multipart:true**と指定しないと、ファイル名を記述した際に、**文字列**として認識されてしまいます。 現場Railsではモデルと紐づかない場合にのみ使用する旧form_withのforn_tagを使用しているため、ファイルや画像データを送信するのであればmultipart: trueの設定が必要です。 ```ruby= form_with #=> form_tagとform_forが合体したもの。 form_for #=> フォームのデータの送り先に、モデルを指定する。 form_tag #=> フォームのデータの送り先に、モデルを指定しない。 ``` これでTaskをインポートする機能の実装が完了しました。 ## 7-7 ページネーション ### ページネーションとは? 表示したいレコードがとても膨大にある時に、そのレコードを一度に全て表示するのではなく、**複数のページに分けて表示させる機能**です。そのようにすることで、見つけたいレコードを見つけやすくするとともに、表示するデータがそれほど多くないので、処理速度も落ちることはありません。Railsでは、**kaminari**というgemが**ページネーション**を提供しています。 まず**kaminari**のgemをインストールします。 pageというスコープを使えるようになり簡単に実装できます。 ↓現場で使えるRuby on Railsのコード例 ``` ruby= # app/controllers/tasks_controller.rb # params[:page]にページ番号が入ります。 def index @q = current_user.tasks.ransack(params[:q]) @tasks = @q.result(distinct: true).page(params[:page]) end ``` ### 7-7-3 ビューにページネーションのための情報を表示する #### paginateヘルパー - 現在どのページを表示しているのかの情報 - 他のページに移動するためのリンク #### page_entries_infoヘルパー - 全データが何件なのかといった情報 ```ruby= #app/views/tasks/index.html.slim .mb-3 =paginate @tasks =page_entries_info @tasks ``` 動作を確認するためにはそれなりにデータが必要になるため コンソールで以下のように実行します。また、初期データを入れる際に、コンソールではなく**seed-fu**を使うやり方もあります。[seed-fuの使い方](https://qiita.com/ko2ic/items/be96e450a33d631e0059) ```shell= > user = User.find(1) > 100.times {|i|user.tasks.create!(name: "サンプルタスク#{i}")} ``` (※)kaminariが内部で用意してくれている翻訳ファイルは英語なので、日本語にしたい場合は翻訳する必要があります。 ### 7-7-5 デザインの調整 **paginateヘルパー**が表示に利用するパーシャルテンプレートをアプリケーション内に用意し、自分でカスタマイズすることでデザインを好きなように調整することができます。 kaminariの提供するジェネレータで生成しますが、テーマ(Thema)と呼ばれるデザインの種類を指定できます。 利用できるテーマの一覧は[github](https://github.com/amatsuda/kaminari_themes)から確認できます。 ``` ruby= $ bin/rails g kaminari:views 指定したいファイル名 ``` ### 一度に表示する件数を変更する kaminariでは、1ページに25件までのデータを表示するという設定がデフォルトで指定されていますが、その表示件数を変更する方法もあります。その方法は、以下の3つです。 #### 1. perスコープを使用する方法 **perスコープ** kaminariで提供されているスコープです。1ページあたりに表示する件数を指定することが出来ます。perスコープに引数として渡した数字が、1ページあたりに表示する件数として反映されます。 ```ruby= # タスク一覧機能だけ、1ページあたり50件のデータを取得したい # indexアクション内にperスコープを用いて、表示件数を変更する def index # @tasks = @q.result(distinct: true).page(params[:page])部分を変更 @tasks = @q.result(distinct: true).page(params[:page]).per(50) end ``` #### 2. paginates_perを使って、モデルごとの表示件数を指定する方法 ```ruby= # taskモデルの1ページのデフォルト表示件数を50件に指定します # app/models/tasks.rb内に記述 class Task < ApplicationRecord paginates_per 50 ~省略~ end ``` #### 3. configファイルを設定する方法 kaminari全体としての表示件数のデフォルトを変更します。 下記のコマンドで、Kaminariの設定ファイルが生成されます。 > $ bin/rails g kaminari:config この設定ファイル内に、表示件数の設定を記述します。 ```ruby= #config/initializers/kaminari_config.rb Kaminari.configure do |config| # ここではデフォルトの表示件数を10件に設定しています。 config.default_per_page = 10 ~省略~ end ``` ## 参照 Action View Form Helpers https://guides.rubyonrails.org/form_helpers.html#uploading-files GitHub kaminari_themes https://github.com/amatsuda/kaminari_themes GitHub kaminari https://github.com/kaminari/kaminari railsで初期データを入れる(seed-fuの使い方) https://qiita.com/ko2ic/items/be96e450a33d631e0059 ## 2021年9月13日 Railsガイド(RailsでJavaScriptを使用する) ### CoffeeScriptとは JavaScriptのコードを生成するためのスクリプト言語です(高級言語)。 Rubyの文法に似ているのが特徴です。 RailsではAsset Pipelineを利用して、CoffeeScriptを自動的にコンパイルしてくれますので、特にコンパイル作業を意識する必要はありません。拡張子を「〇〇.js.coffee」とし、/app/assets/javascriptsフォルダに配置するだけで利用できます。 JavaScript に変換されることを前提としています。CoffeeScript を書き、それをコンパイル(変換)したら、JavaScript ファイルができます。 ### 高級言語とは 機械語に近い言語で記述を行う低級言語に対し、高級言語は言語の理解がしやすく、汎用性が高いという利点があります。 ## rails-ujsとは Ajaxの送信の部分を JavaScript で実装せずに、よしなにやってくれるJavaScriptのライブラリです。 Rails 5.0 までは jquery-rails を使ってフォームやリンクの Ajax 通信を可能にしていましたが、Rails 5.1 からは rails-ujs として切り出され、jQueryに依存しなくなりました。 Rails 5.1 ではデフォルトで rails-ujs が入っているので、require するだけです。 ```ruby= # app/assets/javascripts/application.js //= require rails-ujs ``` これで読み込みが完了します。 ## form_with form_with のデフォルトは remote: true の状態であるため、何もオプションを指定しなくても Ajax 通信となりますのでform_with でフォームを作って submit した時に何も動作しないことがあります。 一般的なページ遷移するフォーム送信をしたい場合には local: true を明示する必要があります。 ## お腹すいた。。ラーメン食べたい。。煮干し ## 2021年9月14日 現場Rails Chapter7 非同期処理や定期実行を行う(Jobスケジューリング) ### 非同期処理とは? 非同期処理とは、あるタスクが実行している際に同時にバックグラウンドで他のタスクを実行できる方式です。 例えば、処理が重く時間が掛かるタスクを非同期処理として裏で動作させ、ユーザーに見える処理では処理の受付だけを行わせることで、ユーザーは受付をしながら非同期処理の結果を待つことが出来ます。結果として、ユーザーがただ何もしないで待つ時間を減らすことが出来ます。 [例:非同期処理を用いたメールの送信](https://serip39.hatenablog.com/entry/2020/12/28/235800#:~:text=%E9%96%A2%E9%80%A3%E8%A8%98%E4%BA%8B-,%E3%82%B8%E3%83%A7%E3%83%96%E7%AE%A1%E7%90%86,-Rails%20%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A7) ### 対して、同期処理とは? 1つずつ順番にタスクが実行される処理のことです。同時に2つ以上の処理を行うことはできず、1つの処理が終わるまで、もう1つの処理は待つ必要があります。 ### Active Jobとは? バックグラウンドでさまざまな処理を非同期に行うための共通フレームワークのことです。   **Active Jobを使うメリット**は、非同期処理を行うための様々なGem(sidekiq, Resque, Delayedjob)の記述方法や機能の違いを気にすることなく、非同期処理が行えることです。つまり、Active Jobを使うことで、どのGemを使用しても同じように**非同期通信**が行えます。 詳しくは[Railsガイド](https://railsguides.jp/active_job_basics.html#:~:text=%E3%82%82%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82-,2%20Active%20Job%E3%81%AE%E7%9B%AE%E7%9A%84,-Active%20Job%E3%81%AE) ### Redisとは > Redisはオープンソースの永続化可能な[インメモリデータベース(In-memory database)](https://aws.amazon.com/jp/nosql/in-memory/)で、[BSDライセンス](https://ja.wikipedia.org/wiki/BSD%E3%83%A9%E3%82%A4%E3%82%BB%E3%83%B3%E3%82%B9)で公開されています。 特徴はインメモリ型データ構造ストアであること、そしてデータの永続化ができることといえるでしょう。 メモリ上で動作する[キーバリューストア型のデータベース](https://www.rworks.jp/cloud/azure/azure-column/azure-entry/21517/)です。 ### Sidekiqとは wikipediaで詳しく説明されているので、以下に引用します。 >SidekiqはRubyで書かれたオープンソースのジョブスケジューラです。デフォルトではスケジューリングは行わず、ジョブの実行のみを行います。エンタープライズ版では、スケジューリング機能が搭載されています。 Sidekiqを使用するためには、sidekiqというgemをインストールします。 ### sidekiqのインストール redisをbrew経由でインストール、Gemの'sidekiq'のインストールを行います。 > $ brew install redis ```ruby gem 'sidekiq' # bundle install ``` インストールしたらRailsとSidekiqを連携するために以下のように追記を行います。 ```ruby # config/environments/development.rb (略) config.active_job.queue_adapter = :sidekiq end ``` 下記のコマンドでジョブの雛形を生成します。 ```shell $ bin/rails g job sample ``` performメソッド内部に、非同期で実行したい処理を書きます。 ```ruby # sample_job.rb class SampleJob < ApplicationJob queue_as :default def perform(*args) Sidekiq::Logging.logger.info "ジョブを実行しました。" end end ``` Railsアプリケーションからperformメソッドの処理を呼び出すと、ジョブを登録できます。 ```ruby def create @task = current_user.tasks.new(task_params) ... if @task.save TaskMailer.created_email(@task).deliver_now # 以下の一文で、performメソッドを呼び出せます。 Sample.Job.perform_later redirect_to @task, notice: "タスク「#{@task.name}」を登録しました。" else render :new end end ``` ジョブは実行する時間を予約することも可能です。 ```ruby # 明日の正午に実行 SampleJob.set(wait_until: Date.tomorrow.noon).perform_later # 1時間後に実行 SampleJob.set(wait: 1.hour).perform_later ``` <h4 style="color: red;">雑学🤘🏽</h4> スケーラビリティとは拡張性という意味です。 スケーラビリティ考慮してこ!!🙆🏽‍♂️ ### スタックとキューについて **Sidekiq**では、複数のジョブを並列して実行することができるのですが、**キュー**(**queue**)をいう仕組みを使ってそのジョブをどのような優先順位で実行するのか決めています。 **スタック**も**キュー**も、「データの持ち方 (データ構造)」のことで、どのようにpop(データを取り出すか)に違いがあります。 #### キューとは? データ構造に入っている要素のうち、最初に push した要素から順に取り出します。(popする) #### スタックとは? データ構造に入っている要素のうち、最後に push した要素から順に取り出します。(popする) ## 参照 [BSDライセンス wikipedia](https://ja.wikipedia.org/wiki/BSD%E3%83%A9%E3%82%A4%E3%82%BB%E3%83%B3%E3%82%B9) [インメモリデータベース](https://aws.amazon.com/jp/nosql/in-memory/) [【Active Job】Sidekiq vs Resque vs Delayed Job](https://zenn.dev/shima_zu/articles/rails_active_job) [Railsガイド Active Job](https://railsguides.jp/active_job_basics.html) [Rails:Sidekiqを用いて非同期処理を実行する](https://serip39.hatenablog.com/entry/2020/12/28/235800) [スタックとキューを極める! 〜 考え方と使い所を特集 〜](https://qiita.com/drken/items/6a95b57d2e374a3d3292#2-2-%E3%82%AD%E3%83%A5%E3%83%BC%E3%81%AE%E8%A9%B3%E7%B4%B0) [Railsで非同期処理を行える「Sidekiq」](https://qiita.com/yumiyon/items/6835d90e621e73268021) ## 2021年9月15日 現場Rails Chapter8-1 JavaScriptでページに変化をつける ### ページが読み込まれてからJavaScriptを実行する方法 ページが読み込まれてからJavaScriptを実行する方法は、2種類存在します。Turbolinks機能を有効にしていない場合と、有効にしている場合です。以下に、Turbolinks機能を有効にしていない場合のコードを記載します。 assets/javascripts/task.js ```javascript= window.onload = function() { document.querySelectorAll('td').forEach(function(td) { td.addEventListener('mouseover', function(e) # function(e)の引数eは、eventの「e」です。 # mouseoverはマウスポインタが乗ったときの処理です。 e.currentTarget.style.backgroundColor = '#eff'; }); td.addEventListener('mouseout', function(e) { e.currentTarget.style.backgroundColor = ''; }); }); }); ``` Turbolinks機能を有効にしている場合、 一番上の行を下記に書き換える必要があります。 ```javascript= document.addEventListener('turbolinks:load', function() { ``` いずれも「ページの読み込みが完了したタイミング」で処理を開始するという意味です。これがない場合、ページの読み込みよりも早い「JavaScriptが読み込まれたタイミング」で処理が実行されますが、ページの読み込みが完了するタイミングでないと イベントハンドラの定義先となる要素がDOM上に存在せず、期待した挙動が実現しない場合があります。 ### addEventListenerメソッド **addEventListenerメソッド**は、特定のイベントが対象に配信されるたびに呼び出される関数を設定します。 下記はaddEventListenerの一般的な構文です。 ```javascript= 対象要素.addEventListener(種類, function() { //ここに処理を記述する }, false); ``` 上記のjavascriptは**DOM**を使って、HTMLを操作しています。具体的には、タスクのtd要素にmouseover(マウスを乗せている間)というイベントに対応する**イベントハンドラ**を登録し、その中で、背景色を変更する処理を記述しています。 Javascriptでイベントハンドラを用いることで、ページの遷移なしに画面を動的に変更することが出来るのです。(**非同期通信**) ### イベントハンドラとは? イベントハンドラとは、特定の処理(イベント)が発生した時に実行する処理です。 ### DOMとは? >DOMは「Document Object Model」の略。 「ドキュメントを物として扱うモデル」。プログラムからHTMLやXMLを自由に操作するための仕組み。 >例えばブラウザに表示される文字の色を変更したり、大きくしたりと、Webページの見た目をプログラムで処理をしたい場合があるだろう、しかし何もしていない状態のHTMLファイルではJavaScriptから手を出す事が出来ない。そこでファイルの特定の部分に目印を付けて「この部分」に「こういう事をしたい」という処理を可能にするための取り決めがDOMである。 DOMの特徴 - ツリー構造とも呼ばれる階層構造を取る - それぞれノードという言葉で説明される - WEBページとJavaScriptなどのプログラミング言語とを繋ぐ ![](https://i.imgur.com/vXdBqUc.png) [画像引用] ### CoffeeScriptとは JavaScriptのコードを生成するためのスクリプト言語です(高級言語)。 Rubyの文法に似ているのが特徴です。 RailsではAsset Pipelineを利用して、CoffeeScriptを自動的にコンパイルしてくれますので、特にコンパイル作業を意識する必要はありません。拡張子を「〇〇.js.coffee」とし、/app/assets/javascriptsフォルダに配置するだけで利用できます。JavaScript に変換されることを前提としています。CoffeeScript を書き、それをコンパイル(変換)したら、JavaScript ファイルができます。 ECMAScript2015(ES2015)という仕様以降、Javascriptでもクラス構文やアロー関数が導入されました。そのため、CoffeeScriptを導入するメリットは以前よりも少なくなりました。 ### 高級言語とは 機械語に近い言語で記述を行う低級言語に対し、高級言語は言語の理解がしやすく、汎用性が高いという利点があります。 ### Ajaxとは Asynchronous JavaScript AndXMLの頭文字をとった言葉で、Webブラウザ上で非同期通信を行い、ページの再読み込みなしでページを更新するためのJavaScriptのプログラミング手法です。 ### rails-ujsとは Ajaxの送信の部分を JavaScript で実装せずに、よしなにやってくれるJavaScriptのライブラリです。 Rails 5.0 までは jquery-rails を使ってフォームやリンクの Ajax 通信を可能にしていましたが、Rails 5.1 からは rails-ujs として切り出され、jQueryに依存しなくなりました。 Rails 5.1 ではデフォルトで rails-ujs が入っているので、require するだけです。 ```ruby= # app/assets/javascripts/application.js //= require rails-ujs ``` これで読み込みが完了します。 [pikawaka 引用] ![](https://i.imgur.com/5UcFseb.png) ↑ 上記は`remote:true`を指定しているかしていないかの違いです。`remote:true`と記述することで、Ajax通信のリクエストをサーバーに送ることができます。 つまり、`remote:true`オプションをつけることで、サーバーとのやりとりを非同期で行うAjax通信が実行できます。ただし、更新された画面を表示するのはjsで更新する必要があるので、別途記述が必要です。 更新された画面を表示する方法は2通りあり、1つはコントローラーで、`head :no_content`と記述することで、HTTPレスポンスを返すとともに、削除成功の`ajax:success`イベント呼び出し、そのイベント内で、DBで削除済みのタスクを表示上でも削除が反映されるような処理を記述します。 もう1つは、ビューファイルのjs.erbファイル内で、更新された画面を表示する処理を記述しており、そのファイルに処理が移ることで、新しいビューが表示されます。 ## 参照 [EventTarget.addEventListener()-WEB API|MDN]("https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener") [【Rails】 remote: trueでフォーム送信をAjax実装する方法とは?](https://pikawaka.com/rails/remote-true) ## 2021年9月16日 現場Rails Chapter8-2-2 前回はクライアント側でイベントハンドラとして用意してあったJavaScriptコードを実行してタスクを非表示にしていましたが、今回はAjax通信をした後にAjaxのリクエストボディにJavaScriptコードを返却してそれをブラウザ側で実行するという方法で削除機能の実装を行います。 #### Viewファイル ```ruby # app/views/tasks/index.html.slim table.table.table-hover thead.thead-default (略) tbody - @tasks.each do |task| tr id= "task-#{task.id}" # tr要素にタスクのid情報を与える td= link_to task.name, task ``` 前回での方法との違う点で、サーバ側は、**DOM要素としてどの削除ボタンがクリックされたことによってAjax通信が発生したのかが判別出来ない**ため、あらかじめ**DOM要素側にタスクのid情報を与えておく**必要があります。   #### Controllerファイル ```ruby # app/controllers/tasks_controller.rb def destroy @task.destroy end ``` #### JavaScriptファイル ```jsx # app/views/tasks/destroy.js.erb document.querySelector("#task-<%= @task.id %>").style.display = 'none'; var message = document.createElement('p'); message.innerText = 'タスク「<%= @task.name %>」を削除しました。残りのタスクは<%= Task.count %>件です。'; document.querySelector('table').insertAdjacentElement('beforebegin', message); ``` 一行目のdestroyアクションに対応するコードに加えて、削除と同時に「残りのタスクは〇〇件です」といったメッセージを表示する記述を行っています。 Railsでは上記のようにサーバーサイドで生成したJavaScriptからなるレスポンス(またはこのレスポンスからなる画面更新までのプロセス)のことをServer-generated JavaScript Responses(SJR)といいます。   手軽にテンプレートやヘルパー、モデルなどのサーバーサイドの資産を簡単に使えるメリットがあることに対して、複雑なJavaScriptで**SJR**を行ってしまうと一つの画面に関するコードがあちこちに散らばってしまって共通化など管理がしづらい点があります。 ### rails-ujsとは?  **remote: true**オプションを加えると**Ajax通信**が送信されますが、それは、**rails-ujs**というActionView添付のjavaScriptライブラリによって処理されています。また、rails-ujsによって、Ajaxリクエストを送信時に、関連するイベントを発行する役割も持ちます。 ### Gitについても少し復習しました ![](https://i.imgur.com/0L4jeLP.png) [画像引用] - 「**master**」**ブランチ**…ローカルの中心となる統合ブランチで、他のローカルの作業ブランチと繋がったもの。 - 「**origin/master**」**ブランチ**…ローカルにある、リモートのmasterブランチを追跡するリモート追跡ブランチ。 ①masterブランチから作業ブランチを切ります。その作業ブランチにて、実際の作業などを行うコードを書いていきます。 ②fooブランチで変更した内容をリモートリポジトリに反映するまでの流れは、 fooリポジトリをadd→commitで、ファイルの追加・変更をローカルリポジトリに反映します。最後にpushをすることで、リモートリポジトリに反映できます。ただし、ここで反映されるのは画像のリモートリポジトリfooの部分であり、masterではないところに注意が必要です。 ③リモートリポジトリのmasterに反映させるには、プルリクを出して、確認後マージボタンを最終的に押すことで、masterリモートリポジトリに反映されます。マージボタンを押した後に、fooリモートリポジトリを削除するのをオススメします。 ④リモートリポジトリのmasterからローカルリポジトリのmasterブランチにpull(git fetch + git merge)すると、リモートリポジトリに反映した内容をブランチにも反映させることができます。 pullすると、origin/masterブランチ(追跡ブランチ)を超えて、直接masterブランチに反映することが出来ます。 ※Gitを操作する時は自分がどこにいるのか確認するのが大切です。 ## 参照 [【超入門】初心者のためのGitとGitHubの使い方](https://tech-blog.rakus.co.jp/entry/20200529/git) [【初心者向け】git fetch、git merge、git pullの違いについて ](https://qiita.com/wann/items/688bc17460a457104d7d) ## 2021年9月17日 GitとGitHub ### Gitとは? >ソースコードをはじめとしたファイルの変更履歴(バージョン)を管理することを「バージョン管理」と呼びます。 ファイルの追加や変更の履歴情報を管理することで、過去の変更箇所を確認する、特定時点の内容に戻す、などの「バージョン管理」という作業が可能となります。 そのバージョン管理を行うのがGitです。 ### GitHubとは? >複数人のエンジニアが**リモートリポジトリ**として活用する他、チーム開発を行うための機能を提供するWEBサービスがGitHubです。 #### git status >git status コマンドは、このワークツリーという領域で変更・追加した状態のファイルや、インデックスという領域に反映されている状態のファイルを確認することができます。 つまり、現在のインデックスの状態を見ることができるのです。 ![](https://i.imgur.com/skSULy7.png) [画像引用](https://26gram.com/git-status) ```bash= # 現在の状況を確認する $ git status #ディレクトリの状態を確認する $ git status [ディレクトリ] ``` コマンド実行後、以下の表示結果では、項目ごとに該当するファイルを確認できます。 - Changes to be committed = コミットする予定の変更したファイル(インデックスに反映されているファイル) - Changes not staged for commit = まだインデックスに追加されていない、変更したファイル - Untracked files = Gitでまだ管理されていないファイル(新しく追加したファイル) ![](https://i.imgur.com/gaWoKiD.png) **直前のコミットからの変更があったファイルの前についている** ・modified:修正したファイル ・deleted:削除したファイル ・Untracked files:新規作成したファイル #### git add git add コマンドを打つと、**インデックス**にaddで指定したファイルが入ります。(コミットする前の準備段階) **インデックス**とは、コミット前に変更内容を一時的に保存する領域を指し、インデックスに追加されたファイルのみがコミットの対象となります。 ```bash= $ git add 変更したい(コミット)ファイル名 $ git add . # git add .は変更したファイルを全て、インデックスに追加してしまうので、色んなファイルの変更したコミットする時は、色んな変更をしたコミットとして登録されます。 # 色んな変更をまとめてaddとcommitはしないほうが良い。 ``` あどちょんの"ちょん"はカレントディレクトリを指しています。 カレントディレクトリ配下の全てのディレクトリやファイルを指定していることになります。 #### git commit ファイルの追加・変更をローカルリポジトリに反映する操作を意味します。 以下のコマンドを入力すると、インデックスに存在するファイルがローカルリポジトリへ追加されます。 ※コミットは編集した機能や修正内容ごとにすることが大切です。1修正ごとにコミットするのをオススメします。 ※コミット時点で正常に機能しているか確認しましょう。 ```bash= # Add:の部分はprefix名 $ git commit -m "Add:ファイル名" ``` ここで -m はコミットメッセージを入力するためのオプションです。 コミットメッセージは任意ですがAddやFixなどを先頭につけたり、作業内容などを分かりやすく書いておくと良いです。 #### push ローカルのコードをリモートリポジトリに送ります。 ![](https://i.imgur.com/dCEDjwC.png) [画像引用](https://tech-blog.rakus.co.jp/entry/20200529/git) #### origin リモートリポジトリの保管場所の名前です。 名前は実は変更することも出来ますが慣習的にoriginとされることが多いです。 #### GitHubのフォークとクローンの違い - clone リポジトリの複製をローカル環境に作成することを指します。 ローカルにクローンしたリポジトリに対しては、自由にコミット、プッシュ、マージをおこなうことができます。 - fork リモート上で公開されているリポジトリを自分のGithubにコピー出来ます。フォークすると、オリジナルのリポジトリの所有者にforkしたことが通知されます。 単に公開されているリポジトリをベースにして何かを作ろう、とか、ビルドして動きを見てみようという場合ならforkを使います。 ![](https://i.imgur.com/VVACUjz.png) [画像引用](https://style.potepan.com/articles/31067.html) <h4 style="color: red;">じゃあ、ナイフは?git knife??🔪🔪🔪</h4> ## 参照 [【超入門】初心者のためのGitとGitHubの使い方](https://tech-blog.rakus.co.jp/entry/20200529/git) [GitHubのforkはリポジトリの複製 フォークの手順と、クローン、ブランチとの違い](https://style.potepan.com/articles/31067.html) [GitHub初心者はForkしない方のPull Requestから入門しよう](https://blog.qnyp.com/2013/05/28/pull-request-for-github-beginners/) [リポジトリのフォークポリシーを管理する](https://docs.github.com/ja/github/administering-a-repository/managing-repository-settings/managing-the-forking-policy-for-your-repository) [【Git入門】git status の使い方(ファイルの状態を確認する)](https://26gram.com/git-status) ## 2021年9月18日 GitとGitHub ### git resetコマンド 間違えて"git add"、"git commit"をした場合、"git reset"を行うことでそれらの操作をなかったことにできます。コミットidを指定すると、指定したコミットまで戻ります。 resetはコミットIDを指定したコミット時点まで戻るが指定したコミットより新しい履歴が消えてしまうので リモートにプッシュしてからresetを使うと最終的に整合性が取れずコンフリクトを起こす場合があります。 #### **reset --hard** :add、commit、ワーキングツリーの取り消し 「ステージングエリアにも作業ディレクトリにも残らなくていいから、コミットをまるごと消したい」というときに使えるコマンドです。 ```bash= # ひとつ前のコミットまでまるごと消したい $ git reset --hard HEAD^ ``` ![画像引用](https://i.imgur.com/dhDswGN.png) [画像引用](https://www.r-staffing.co.jp/engineer/entry/20191129_1) #### **reset --mixed** :commitとaddの取り消し ```bash= # ステージしたものを取り消したい $ git reset --mixed HEAD # オプション無しでも--mixedを実行したときと同じ結果が得られます $ git reset HEAD ``` ![画像引用](https://i.imgur.com/QPQDQTh.png) [画像引用](https://www.r-staffing.co.jp/engineer/entry/20191129_1) #### **reset --soft** :commitのみ取り消し ```bash= # 直前のコミット内容を修正したい $ git reset --soft HEAD^ ``` ![画像引用](https://i.imgur.com/VCMWHsG.png) [画像引用](https://www.r-staffing.co.jp/engineer/entry/20191129_1) ※HEADというのは、「自分が今いる位置」のことです。たいていはブランチの先頭のコミットを指していることが多いですが、それを過去のコミットに移動させることで、ブランチを過去に巻き戻すわけだが、そのときにステージも道連れにするのがmixedで、ステージも作業ディレクトリも道連れにするのがhardです。 ※HEADの代わりにコミットIDを指定することもできます。コミットIDを使うと、指定した時点まで「HEAD・ステージングエリア・作業ディレクトリ」のすべてが巻き戻されます。 ※git reset --hardを使う場合は、プッシュ前のコミットに対してのみ、使うようにする。 ```bash= # 指定したコミットID時点までのHEAD・ステージングエリア・作業ディレクトリの状態が元に戻る $ git reset --hard 02f11b7 ``` #### git reflogコマンド HEADでの動きが履歴として一覧表示することが出来ます。 ・新規コミット(コミット、マージ、プル、リバートなど) ・ブランチの切り替え(チェックアウト) ・履歴の書き換え(リセット、リベースなど) このコマンドを使うことで、間違えてリセットしてしまっても指定した時点に戻すことが出来ます。 ```bash= # 操作履歴を見る $ git reflog # 以下操作一覧 02f11b7 HEAD@{0}: reset: moving to 02f11b7 0488e28 HEAD@{1}: merge develop: Merge made by the 'recursive' strategy. 6978c20 HEAD@{2}: checkout: moving from develop to master#=> 間違えてresetしてしまった時点 32275f0 (develop) HEAD@{3}: commit: 制作事例を追加 0e95561 HEAD@{4}: checkout: moving from master to develop 6978c20 HEAD@{5}: commit: 3回目のコミット 0e95561 HEAD@{6}: commit: 2回目のコミット 02f11b7 HEAD@{7}: commit (initial): はじめてのコミット # resetの操作をする前(1つ前)に戻したい場合 $ git reset --hard HEAD@{1} #=> 間違えて実行してしまったコマンドの1つ前まで戻る ``` #### git revert revertはコミットの履歴自体は消さずに、指定したコミットidを作る前の状態に戻れます(結果としてコミットを消した状態になります)。 <span style="color: red;">**revertで指定したコミットを打ち消すようなコミットを作成します。そのコミットがあるおかげで、指定したコミットがなかったことになります。**</span> 間違えた時点で、新たにコミットをしてその作成したコミットを**revert**することで、間違える前の状態に戻れます。(ただし、コミットした履歴は残っています。) ``` $ git revert 消したいコミットid ``` ![](https://i.imgur.com/mFLJpob.png) [画像引用](https://www-creators.com/archives/1116) <h3 style="color: red;">ポケモンに例えると</h2> ジムリーダーに初挑戦する前にコミットして、負けたときに挑戦前に戻りたい場合 負けた後に新しくコミットする→負けた後の最新のコミットIDを`git revert コミットID` にして実行する→ジムリーダーに初挑戦時に戻ります。 ## 参照 [第5話 プッシュ済みのコミットを取り消したい!リバートの使い方【連載】マンガでわかるGit ~コマンド編~](https://www.r-staffing.co.jp/engineer/entry/20191025_1) [第6話 git reset 3種類をどこよりもわかりやすい図解で解説!【連載】マンガでわかるGit ~コマンド編~](https://www.r-staffing.co.jp/engineer/entry/20191129_1) [第7話 間違えて reset しちゃった?git reflogで元どおり【連載】マンガでわかるGit ~コマンド編~](https://www.r-staffing.co.jp/engineer/entry/20191227_1) [git commit を取り消して元に戻す方法、徹底まとめ](https://www-creators.com/archives/1116)