20200119のRubyに関する記事は25件です。

【初心者向け】Rubyを使用して、正規表現を実装するアプリケーションを作成

正規表現を実装するため、ミニアプリを作ります。

条件
 ・住所を入力させる(都道府県と市区町村のみ)
 ・都道府県を正規表現で取得
 ・市区町村を正規表現で取得
 ・それぞれ取得したものを配列にいれていく
 ・リスト表示させて、配列にいれこんだ、データを取り出せるメソッドを作成する

コーディングしていく

def enter_address(personal_informations)
  puts "住所を入力してください"
  address = gets.to_s
  prefecture_address = address.scan(/.*[都道府県]/)
  #改行を除く任意の一文字で、直前のパターンの文字が0文字以上あり、都道府県が含まれるやつ
  puts "都道府県名は#{prefecture_address}"
  puts "---------------------------------------"
  municipality_address = address.scan(/.+?郡.+?[町村]|.+?市.+?区|.+?[市区町村].*/)
  municipality_address = address.gsub!(/.*[都道府県]/,'').chomp
  #任意の文字が一文字以上、またはなし、そして市区町村のいずれかの文字に該当し、任意の文字で一文字以上で終わるやつ
  puts "市区町村名は#{municipality_address}"
  puts "---------------------------------------"
  puts "電話番号を入力してください(携帯、市外局番両方可)"
  number = gets.to_s
  regular_number = number.scan(/0[1-9]\d{0,3}[-(]\d{1,4}[-)]\d{4}/)
  puts "#{regular_number}"
  puts "---------------------------------------"


  personal_information = { prefecture_address: prefecture_address, municipality_address: municipality_address, regular_number: regular_number }
  personal_informations << personal_information
end

def show(personal_informations)
  puts "番号を入力してください"

  index = 1
  personal_informations.each do |personal_information|
    puts "#{index}: #{personal_information[:prefecture_address]} #{personal_information[:municipality_address]} #{personal_information[:regular_number]}"
    index += 1
  end  
  input = gets.to_i
  input -= 1
  puts "都道府県は#{personal_informations[input][:prefecture_address]}です"
  puts "市町村名は#{personal_informations[input][:municipality_address]}です"
  puts "電話番号は#{personal_informations[input][:regular_number]}です"
end

personal_informations = []

while true do

puts "下記に該当する番号を選んでください"
puts "---------------------------------------"
puts "1:都道府県名と電話番号を入力"
puts "2:リストをみる"
puts "3:終了する"
puts "---------------------------------------"
  case gets.to_i
    when 1
     enter_address(personal_informations)
    when 2
      show(personal_informations)
    when 3
      exit
    else
      puts "無効な値です"
      puts "---------------------------------------"
  end
end

動作確認

下記に該当する番号を選んでください
---------------------------------------
1:都道府県名と電話番号を入力
2:リストをみる
3:終了する
---------------------------------------
1
住所を入力してください
千葉県浦安市
都道府県名は["千葉県"]
---------------------------------------
市区町村名は浦安市
---------------------------------------
電話番号を入力してください(携帯、市外局番両方可)
02-2222-2222 
["02-2222-2222"]
---------------------------------------
下記に該当する番号を選んでください
---------------------------------------
1:都道府県名と電話番号を入力
2:リストをみる
3:終了する
---------------------------------------
1
住所を入力してください
東京都練馬区
都道府県名は["東京都"]
---------------------------------------
市区町村名は練馬区
---------------------------------------
電話番号を入力してください(携帯、市外局番両方可)
090-3333-3333
["090-3333-3333"]
---------------------------------------
下記に該当する番号を選んでください
---------------------------------------
1:都道府県名と電話番号を入力
2:リストをみる
3:終了する
---------------------------------------
2
番号を入力してください
1: ["千葉県"] 浦安市 ["02-2222-2222"]
2: ["東京都"] 練馬区 ["090-3333-3333"]

苦労した点

・そもそも正規表現を一から考えるのってむずいなぁと感じました。
・市区町村を正規表現だけで取り出すのは断念しました・・・結果として二行つかいました。

 municipality_address = address.scan(/.+?郡.+?[町村]|.+?市.+?区|.+?[市区町村].*/)
 municipality_address = address.gsub!(/.*[都道府県]/,'').chomp

ここの部分二行になってるんですが、どうしても都道府県も出てきてしまうので、
無理やりですが、都道府県は変換して削除して出力するようにしました。
たぶんこれはナンセンスなやり方だと思いますが・・・・これも正規表現で取得できるはずなので、時間をかけて理解を深めていこうと思います。

諸々間違ってたらすいません。。。。
ここが違う、ここはこうしたほうがセンスが良い等々ございましたら
ご指摘いただけますと幸いです。

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

[Ruby]Leet文字列への変換

Leetとは

たとえば、「Warez」という語を leet で表記すると、「W@rez」や「W4r3z」などとなるように、一部のアルファベットを形の似た数字や記号などに変化させる。

引用: Leet - Wikipedia
英語圏のネット掲示板などで使用されるネットスラングの一種らしいです。

今回は、Rubyで以下のように変換します。

アルファベット⇄記号変換表
アルファベット 記号
A 4
B 8
C
D T)
E 3

条件

入力された文字列に対してLeetで文字を変換する。その後、文字列を出力する。

コード

word = gets.chomp.split('')
word.each {|w|
  case w
  when 'A'
    print '4'
  when 'B'
    print '8'
  when 'C'
    print '['
  when 'D'
    print 'T)'
  when 'E'
    print '3'
  else
    print w
  end
}

1行めでは入力された文字を一文字ずつ区切って配列にしています。
2行め以降はcaseによって一致判定・文字の入れ替えを一文字ずつ行っています。

入力例
CD ABSOLUTE
出力例
[T) 48SOLUT3

(ABCDEでやったので微妙です笑)

間違いやこう書けるよ!などありましたら教えてください!

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

取り出した情報(インスタンス変数)をhaml記法を用いてビューで表示させたいです。

初投稿です。初心者なので理解不足点、お見苦しい文面お許し下さい。

コントローラーからモデル経由で取り出したDBのデータを表示させたいのですがうまくいきません。
やりたい事はhaml記法でビューに取り出したデータを一覧表示させたいです。
試したことはデバックを行いデータ@pruducts@imagesは中身があることはわかりました。
hamlの記法においてendがないのでdo抜けやインデントミスが原因だとは思うのですが見つけることができませんでした。回答いただけましたら幸いです。よろしくお願いします。

products_controller.rb

class ProductsController < ApplicationController

  def index
    @products = Product.includes(:images)
    @images = Image.includes(:product)
  end

  def show
  end

  def new
  end

end

content.html.haml

.header-kari
  仮です!!!!!!
.slider
  .slider__content
    = image_tag 'GuitarWolf2019_a.jpg',width:"300px"
  .slider__content
    = image_tag 'gahag-0097732117.jpg',width:"300px"
  .slider__content
    = image_tag 'J19y54-P_400x400.jpg',width:"300px"

.categoryname-sapce
  .categoryname-sapce__title
    人気のカテゴリ
  .categoryname-sapce__center-space
    .categoryname-sapce__center-space__name1
      レディース
    .categoryname-sapce__center-space__name2
      メンズ
    .categoryname-sapce__center-space__name3
      おもちゃ・ホビー・グッズ
    .categoryname-sapce__center-space__name4
      家電・スマホ・カメラ
.center
  .center__space-a
    .center__space-a__new-item1
      .center__space-a__new-item1__title1
        レディース新着アイテム
      .center__space-a__new-item1__link1
        = link_to "もっとみる"
      .center__space-a__new-item1__image-space
        %ul.space
          %li.space__box
            - @product.each do |product|
              - product.images.each do |image|
            -# = image_tag @products.url, class:'space__box__size'
          -# %li.space__box
          -#   = image_tag @products.url, class:'space__box__size'
          -# %li.space__box
          -#   = image_tag @image.url, class:'space__box__size'
          -# %li.space__box
          -#   = image_tag @image.url, class:'space__box__size'
          -# %li.space__box
          -#   5
          -# %li.space__box
          -#   6
          -# %li.space__box
          -#   7
          -# %li.space__box
          -#   8
          -# %li.space__box
          -#   9
          -# %li.space__box
          -#   10

  .center__space-b
    .center__space-b__new-item2
      .center__space-b__new-item2__title2
        メンズ新着アイテム
      .center__space-b__new-item2__link2
        = link_to "もっとみる" , class:'links'
      .center__space-b__new-item2__image-space
        .center__space-b__new-item2__image-space__image
          ここは写真です
        .center__space-b__new-item2__image-space__imagetest
          testimage

  .center__space-c
    .center__space-c__new-item3
      .center__space-c__new-item3__title3
        メンズ新着アイテム
      .center__space-c__new-item3__link3
        = link_to "もっとみる" , class:'links'
      .center__space-c__new-item3__image-space
        .center__space-c__new-item3__image-space__image
          ここは写真です
        .center__space-c__new-item3__image-space__imagetest
          testimage
  .center__space-d
    .center__space-d__new-item4
      .center__space-d__new-item4__title4
        家電・スマホ・カメラ新着アイテム
      .center__space-d__new-item4__link4
        = link_to "もっとみる" , class:'links'
      .center__space-d__new-item4__image-space
        .center__space-d__new-item4__image-space__image
          ここは写真です
        .center__space-d__new-item4__image-space__imagetest
          testimage

  .center__space-e
    .center__space-e__new-item5
      .center__space-e__new-item5__title5
        おもちゃ・ホビー・グッズ新着アイテム
      .center__space-e__new-item5__link5
        = link_to "もっとみる" , class:'links'
      .center__space-e__new-item5__image-space
        .center__space-e__new-item5__image-space__image
          ここは写真です
        .center__space-e__new-item5__image-space__imagetest
          testimage

.categoryname-sapce
  .categoryname-sapce__title
    人気のブランド
  .categoryname-sapce__center-space
    .categoryname-sapce__center-space__name5
      シャネル
    .categoryname-sapce__center-space__name6
      ルイビトン
    .categoryname-sapce__center-space__name7
      シュプリーム
    .categoryname-sapce__center-space__name8
      ナイキ
.center
  .center__space-a
    .center__space-a__new-item1
      .center__space-a__new-item1__title1
        シャネル新着アイテム
      .center__space-a__new-item1__link1
        = link_to "もっとみる"
      .center__space-a__new-item1__image-space
        .center__space-a__new-item1__image-space__image
          ここは写真です
        .center__space-a__new-item1__image-space__imagetest
          testimage
  .center__space-b
    .center__space-b__new-item2
      .center__space-b__new-item2__title2
        ルイビトン新着アイテム
      .center__space-b__new-item2__link2
        = link_to "もっとみる" , class:'links'
      .center__space-b__new-item2__image-space
        .center__space-b__new-item2__image-space__image
          ここは写真です
        .center__space-b__new-item2__image-space__imagetest
          testimage

  .center__space-c
    .center__space-c__new-item3
      .center__space-c__new-item3__title3
        シュプリーム新着アイテム
      .center__space-c__new-item3__link3
        = link_to "もっとみる" , class:'links'
      .center__space-c__new-item3__image-space
        .center__space-c__new-item3__image-space__image
          ここは写真です
        .center__space-c__new-item3__image-space__imagetest
          testimage
  .center__space-d
    .center__space-d__new-item4
      .center__space-d__new-item4__title4
        ナイキ新着アイテム
      .center__space-d__new-item4__link4
        = link_to "もっとみる" , class:'links'
      .center__space-d__new-item4__image-space
        .center__space-d__new-item4__image-space__image
          ここは写真です
        .center__space-d__new-item4__image-space__imagetest
          testimage

  .center__space-e
    .center__space-e__new-item5
      .center__space-e__new-item5__title5
        おもちゃ・ホビー・グッズ新着アイテム
      .center__space-e__new-item5__link5
        = link_to "もっとみる" , class:'links'
      .center__space-e__new-item5__image-space
        .center__space-e__new-item5__image-space__image
          ここは写真です
        .center__space-e__new-item5__image-space__imagetest
          testimage

脂肪.png

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

You don't have bcrypt installed in your application. と言われた

railsチュートリアル第6章の最後の最後のrails testでエラってハマりまくったので備忘録。

環境

  • Rails 5.1.6
  • ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin18]

問題発生

ユーザー作成も認証もできたし一区切り!と、ローカルでのrails testはok
しかしマージしてpushしたあと再びrails testしたところ怒られました

You don't have bcrypt installed in your application. Please add it to your Gemfile and run bundle install:??

bcryptはinstallしてるしGemfileにもGemfile.lockにもあるのになんでだ・・・?
ググると同じようなエラーでハマったという方がいらっしゃって
rails server再起動とかuninstallしてから再インストールやってみたけど解決せず・・

なんとか解決

  • Gemfileのbcryptの部分をコメントアウトする
  • bundle installする
  • コメントアウト外す
  • 再びbundle install

たまたま見つけたこちらの方の手順でいけました:sob:
rails consoleでコマンド実行時のbcryptに関するエラーとその対処法

根本的な原因がまじでわかんないのでもちょい調べる。

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

【Rails】Colorpickersliderの使い方

開発環境

Rails 5.2.4.1
ruby 2.4.0

前提

devise導入済み
userにcolorカラムを追加
Screen Shot 0002-01-19 at 22.05.41.png

参考URL

色の指定が自由自在!カラーピッカーを出現させるjQueryプラグイン「Bootstrap Color Picker Sliders」
Bootstrap Color Picker Sliders

まとめた経緯

自社内サービスでガントチャートを作成するに当たり、
自分の色をつけることでわかりやすくしようとしました。

使い方

ソースをダウンロードする↓

Githubから
ファイルをダウンロードする

ライブラリを読み込む

jQuery, Bootstrap, tinyColorに依存するので、それらを先に読み込む
(ここは省略します)

表示のしかた

index.haml
...
= f.text_field :color, id: 'hsvflat', autofocus: true, autocomplete: 'color'

jsの初期化

index.haml
<script>
  $("#hsvflat").ColorPickerSliders({
    color: "#F44336",
    placement: 'right',
    sliders: false,
    swatches: ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFEB3B', '#FFC107', '#FF9800', '#FF5722', '#795548', '#9E9E9E', '#607D8B', '#000000', '#FFFFFF'],
    hsvpanel: false
  });
</script>

こんな感じになるよ

Screen Shot 0002-01-19 at 22.17.06.png

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

[Ruby]文字列の指定範囲を大文字に変える

条件

スペース区切りの2つの整数と文字列を入力する。2つの整数の範囲の部分文字列を大文字にして出力する。
例)
入力

3 7
I have a pen

出力

I hAVE A Pen

コード

num = gets.chomp.split(' ')
str = gets.chomp

first = num[0].to_i - 1
second = num[1].to_i - 1

(0...str.length).each {|n|
    if n >= first && n <= second
        print str[n].upcase
    else
        print str[n]
    end
}

動作環境

Ruby 2.6.5
macOS Catalina 10.15.2

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

configure IIS with ruby on rails app on windows server

As the title looks strange, RubyOnRails application deploy on Windows Server??? really???

Yes, today I would like to share my experience, how I managed to deploy a Ruby application on the window server(IIS).

Most of the Ruby applications are deploying on a Linux system(cents, ubuntu, AMI) & as the performance perspective, which is also very good and the developer also prefers to develop on Linux systems, because all gems are easily installed on Linux machine in compare to install on the Windows machine.

Let's back to the topic,
In the year 2018, we released Ruby application on the Windows server and It's almost 1.5 years, it's running perfectly without having any big issue.
Regarding the issue, I will discuss it later.

Sharing the System architecture with you all. If you find helpful then please share and leave your comments.
Screen Shot 2020-01-17 at 21.46.10.png

At 1st we started development on the Linux machine, but suddenly we get to knew that we have to release it on the Windows server(IIS). At that time we discussed all the pros and cons with the client and within a team, but it's a God wish to get in a new ride. We accepted this challenge.

Before going deep into it,
I researched how much it is practical to deploy the RubyonRails app on production env(Window Server). I found some articles over the internet but looks like deploy on development env.

Sharing the application details.
・Language: Ruby
・Framework: Rails
・Database: SQL Server 2014
・Platform: Windows Server(2012)
・Application Server: Puma

Now it's time to get it into the windows server.

Step 1: Installation of the Ruby and Rails framework.
This step is quite easy because I already did the same in the past. so 1st step is clear.

Step 2: Run our application on Windows.
As you all know that rails provide the puma server as default, so I used puma and there are some dependency issues but resolved it successfully. And finally, our application is on running mode.

Step 3: IIS Installation.
You can find many articles to Install IIS. As we need to follow some steps to install IIS. So this step is also clear without having any problem.

Step 4: Deploy app on IIS.
Open IIS Manager, then Go to the Site, Right-click on Default Web Site and Add New Application.
As the below image described.
2019-10-09 11_02_17-API-SVR57 - TeamViewer.jpg

Step 5: Configration web.config file.
This step was the most difficult. WHY???
Because 1st you have to create a web.config file with all the basic settings for the Ruby app.
2nd the web.config file location(where should I keep this file, inside the ruby application folders or outside).
3rd is, how to run a puma server in IIS.

I tried many settings, did many changes in the IIS and configuration files.
but no luck.

and finally, the day has come, when I got successfully run a Ruby application in IIS.
yeah....cheers.

1st below is the web.config file for your reference.
2nd, please copy your web.config file under your mail directory. (please check the below image.)
3rd, please check the web.config file below.

Setup and configuration have been completed.
2019-10-09 11_00_54-API-SVR57 - TeamViewer.jpg

Step 6: How to do set up an environment(run an app on development or production mode?).
In apache or Nginx, it is easy to define the environment. but in IIS it was not an easy task.
So the next hurdler was: run our application on development and as well as production env.
here is the web.config for the development & production env.
■ Development Env:

    <?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <handlers>
            <add name="httpplatformhandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />            
        </handlers>
        <httpPlatform stdoutLogEnabled="true" processesPerApplication="1" stdoutLogFile="log\rails.log" startupTimeLimit="20" processPath="C:\Ruby24-x64\bin\ruby.exe" arguments="-S puma --env development --dir C:\inetpub\wwwroot\live\<your-project-folder-name> -p %HTTP_PLATFORM_PORT% ">
            <environmentVariables>              
              <environmentVariable name="RAILS_ENV" value="development" />        
            </environmentVariables>            
        </httpPlatform>        
    </system.webServer>    
</configuration>

■ Production Env:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <handlers>
            <add name="httpplatformhandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified" />            
        </handlers>
        <httpPlatform stdoutLogEnabled="true" processesPerApplication="1" stdoutLogFile="log\rails.log" startupTimeLimit="20" processPath="C:\Ruby24-x64\bin\ruby.exe" arguments="-S puma --env production --dir C:\inetpub\wwwroot\live\<your-project-folder-name> -p %HTTP_PLATFORM_PORT% ">
            <environmentVariables>              
              <environmentVariable name="RAILS_ENV" value="production" />  
              <environmentVariable name="SECRET_KEY_BASE" value="<generate-your-secret-key>" />         
            </environmentVariables>            
        </httpPlatform>        
    </system.webServer>    
</configuration>

Till date, it is running perfectly.
But this was a very interesting and new combination(ruby on rails on IIS).

If you have the same scenario then please go through this article. I hope it will be helpful.

■Problem
In the last, I want to share one problem with you guys,
sometimes the IIS server stopped suddenly, so at that time we need to restart it. Looking for the solution...

If you find anything wrong within this article then please let me know.

Thank you for your valuable​ time.

Enjoy Coding.:smiley::smiley:

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

Rails 5.2 (with --api) + ActiveAdmin + devise

はじめに

Rails (with --api)でActiveAdminを使う際に必要となる手順についてまとめてあります。
他にも記事がたくさんありますが、deviseを使う使わないなどの選択肢で、記事通りで動かないことがあったので、ここではdeviseを使う前提で書いてあります。

環境

  • Rails 5.2.4
  • ActiveAdmin 2.6.0
  • devise 4.7.1
  • mini_racer 0.2.9

参考

https://blog.heroku.com/a-rock-solid-modern-web-stack
(ほぼリンクの手順ですが、config/application.rbの内容を補足)

Gem インストール

Gemfile
gem 'activeadmin', '2.6.0'
gem 'devise', '4.7.1'
gem 'mini_racer', '0.2.9'
$ bin/bundle install

ファイルの修正

app/controller/application_controller.rb
class ApplicationController < ActionController::Base
    protect_from_forgery with: :exception
end

次のファイルを継承してコントローラを実装する。

app/controller/api_controller.rb
class ApiController < ActionController::API
end
config/application.rb
require "sprockets/railtie"   # uncommented
...
  class Application < Rails::Application

  # Middleware for ActiveAdmin
    config.middleware.use Rack::MethodOverride
    config.middleware.use ActionDispatch::Flash
    config.middleware.use ActionDispatch::Cookies
    config.middleware.use ActionDispatch::Session::CookieStore

    config.generators do |g|
        g.scaffold_controller = :scaffold_controller
    end
  end
end
$ mkdir -p app/assets/config && echo '{}' > app/assets/config/manifest.js

インストール

$ bin/rails g active_admin:install
$ bin/rails db:migrate db:seed

動作確認

$ bin/rails s

http://localhost:3000/adminにアクセスしてログイン画面が表示されれば完了です。

モデルの追加方法

rails g active_admin:resource some_model
app/admin/some_model.rb
ActiveAdmin.register User do
  permit_params :xxx, :yyy, :zzz
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(Ruby)文字列を16進数に変換する

文字列を16進数で確認したい時に使う。

動作環境

ruby 2.6.5

16進数への変換

String#unpackString#bytesを利用できる。

unpack

パラメータに("H*")を指定することで、16進数の値が返される。

text = "吾輩は猫である。"
text.unpack("H*")
# => ["e590bee8bca9e381afe78cabe381a7e38182e3828be38082"]

bytes

文字列の各バイトが数値の配列で返される。
bytesを使うと10進数で表示される1ので、16進数に変換する。

text = "吾輩は猫である。"
text.bytes
# => [229, 144, 190, 232, 188, 169, 227, 129, 175, 231, 140, 171, 227, 129, 167, 227, 129, 130, 227, 130, 139, 227, 128, 130]

text.bytes.map{ |b| "%02X" % b }
# => ["E5", "90", "BE", "E8", "BC", "A9", "E3", "81", "AF", "E7", "8C", "AB", "E3", "81", "A7", "E3", "81", "82", "E3", "82", "8B", "E3", "80", "82"]

# Ruby 2.7以降は以下の書き方も出来る
text.bytes.map{ "%02X" % _1 }

UTF-8の文字コード表と照らし合わせれば何の文字が入っているか分かる。
UTF-8の文字コード表 - 備忘帳 - オレンジ工房


  1. bytesは10進数に変換しているわけではない。返り値のInteger オブジェクトを表示するときに、10進法に基づく文字列化を行っている。 

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

Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #17.5 MySQL導入編

こんな人におすすめ

  • プログラミング初心者でポートフォリオの作り方が分からない
  • Rails Tutorialをやってみたが理解することが難しい

前回:#17 VPC環境構築編
次回:準備中

今回の流れ

  1. SQLite3からMySQLに変更する

この記事は、動画を観た時間を記録するアプリのポートフォリオです。
今回はRailsアプリのDBをSQLite3からAWSによるRDSのMySQLに変更します。
RDSはすでにAWSで作成しているものとします(詳しくは#17をご覧ください)。

SQLite3からRDSのMySQLに変更する

RailsアプリのDBをSQLite3からRDSのMySQLに変更します。

  • Gemfileを編集する
  • エンドポイントを確認する
  • database.ymlを編集する
  • RDS(MySQL)に接続する
  • DBを更新する

Gemfileを編集する

Gemfileから使っているDBを削除し、MySQLを入れます。

Gemfile
+ gem 'mysql2'

group :development, :test do
- gem 'sqlite3'
end

group :production do
- gem 'pg'
end
shell
$ bundle install

エンドポイントを確認する

RDSのエンドポイントはDBを設定する際に必要なので、確認します。

AWSにログイン → AWSマネジメントコンソール『RDS』
画面左のダッシュボード『データベース』 → 『自分のアプリ名』 → 下部タブ『接続とセキュリティ』のエンドポイントをコピー

database.ymlを編集する

DBの設定を編集します。

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: lantern(自分のアプリ名)
  pool: 5
  username: ①任意の名前
  password: ②任意のパスワード
  socket: /var/lib/mysql/mysql.sock
  host: コピーしたエンドポイント

development:
  <<: *default
  database: lantern_devlopment(自分のアプリ名)

test:
  <<: *default
  database: lantern_test(自分のアプリ名)

production:
  <<: *default
  database: lantern_production(自分のアプリ名)
$ sudo yum install mysql-devel
$ bundle install
$ sudo /etc/init.d/mysqld restart

RDS(MySQL)に接続する

RDS(MySQL)に接続します。

shell
$ mysql -h コピーしたエンドポイント -P 3306 -u ①任意の名前 -p
Enter password:②任意のパスワード

その後はユーザーを作成し、ルート権限を与えます。

mysql
mysql> create user '①任意の名前'@'コピーしたエンドポイント' identified by '②任意のパスワード';
mysql> grant all on `%`.* to '①任意の名前'@'コピーしたエンドポイント' identified by '②任意のパスワード';

参考になりました↓
MySqlのソケットエラーを解決する
RailsのDBを(初めから| |後から)MySQLに変更する
RDSのMySQLでGRANT文が通らない

DBを更新する

後はいつも通り、DBを更新します。

shell
$ rails db:reset
$ rails db:migrate
$ rails db:seed

以上でMySQLの移行が完了しました。

補足:MySQLが繋がらない場合

VPC内にRDSを設置した場合、外からRDSにアクセスができません。
そのため、EC2からではなくcloud9から接続するには、一時的にセキュリティを切っておくなどの工夫が必要です。

AWSマネジメントコンソール『EC2』 → 画面左のダッシュボード『セキュリティグループ』 → 作成したセキュリティグループを選択 → 下部タブ『インバウンド』 → 『編集』

各ルールを一時的に削除する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

high_voltageで利用規約等の静的ページを作る

high_voltageで静的ページ(プライバシーポリシー、利用規約など)作成

routesとcontrollerの記述不要
headerやfooterを適用したい場合

gem high_voltage

routes.rb
/pages/*id 

を自動生成してくれる。rails routesで確認しよう
routes.rbには記述しないでいい

つまづいたところ

https://github.com/thoughtbot/high_voltage

Disabling routes
The default routes can be completely removed by setting the routes to false:

# config/initializers/high_voltage.rb
HighVoltage.configure do |config|
  config.routes = false
end

※high_voltageのgithub参照

config.routes = false
はhigh_voltageのルートを使えなくするという意味。ここを読まずにコントローラーを作成したりルーティングを記述したりして複雑にしてしまっていた。

high_voltageのgithubやREAD.MEを参考にしてよく読む。

レイアウト指定

initializer.rb
config.layout = 'customer' 
#customer.html.slimがheader footerが書かれているlayout配下のfile

で適用したいレイアウトを指定

views/pages内に

confirm.html.slim
=link_to 'プライバシーポリシー', page_path('privacypolicy')
confirm.html.slim
=link_to '利用規約', page_path('terms')

と記述する

リンクごとにpages/privacyやpages/termsが表示されるようになります。

コピペじゃなくてなぜ動いているかを考えよう(自戒)

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

RailsにおけるAjaxの実装(JavaScriptとjQueryのコード比較)

1. はじめに

RailsでAjaxを実装するには、jQueryを使うのが便利です。
しかし、ブラックボックスが多すぎて、何をやっているのか、いまいちよく分かりません。

そこで、jQueryで記述したAjax処理を、改めてJavaScriptのみを用いて記述し直してみました。
以下、jQuery及びJavaScriptのコードを比較しつつ、使用したメソッドなどについて記録しておきます。

対象とするHTTPメソッドは、POSTメソッドとDELETEメソッドです。
なお、turbolinksは無効にしています。

誤りなどあれば、ご指摘をいただけると幸いです。

実行した環境

  • Rails 5.2.4.1
  • Ruby 2.5.1
  • jQuery 1.12.4
  • jquery-rails 4.3.5
  • (※ turbolinksは無効にしています)

コードの記載にあたっては、次のような簡易ツールを作って、挙動を確認しています。
スクリーンショット 2020-01-18 15.43.03.png
ビューファイルは、次のように記載しています。

index.html.erb
<h4>メモアプリ(サンプル)</h4>
<div class="form">
  <% if user_signed_in? %>
    <%= form_with(model: @note, class: "note_form", id: "note_input", local: true) do |form| %>
      <%= form.text_area :body, class: "note_form-text" %>
      <%= form.submit "メモを登録", class: "note_form-btn" %>
    <% end %>
  <% end %>
</div>
<div class="notes">
  <% @notes.each do |note| %>
    <div class="note" id="note<%= note.id %>">
      <span class="note_name">
        投稿者:<%= note.user.name %>
      </span>
      <% if user_signed_in? && note.user_id == current_user.id %>
        <%= link_to "削除", note_path(note.id) ,class: "note_delete", method: :delete %>
      <% end %>
      <%= simple_format note.body, class: "note_body"%>
    </div>
  <% end %>
</div>

2. 投稿(POSTメソッド)についてのAjaxのコード

まずは、POSTメソッド(投稿)についてのAjax処理です。

2-1. jQueryでAjaxを記載(POSTメソッド)

まずは、一般的な、jQueryを使用したサンプルコードです。
データの受け渡しの状況を確認するため、FormDataは使用していません。

note.js
$(function() {
  // 追加するHTMLデータを生成する関数
  function createHTML(note) {
    let html = `<div class="note" id="note${note.id}">
                  <span class="note_name">投稿者:${note.user_name}</span>
                  <a class="note_delete" rel="nofollow" data-method="delete" href="/notes/${note.id}">削除</a>
                  <p class="note_body">${note.body}</p>
                </div>`
    return html;
  }
  // メモ投稿(POSTメソッド)の処理
  $("#note_input").on("submit", function(e) {
    e.preventDefault();  // デフォルトのイベント(HTMLデータ送信など)を無効にする
    let inputText = $(".note_form-text").val();  // textareaの入力値を取得
    let url = $(this).attr("action");  // action属性のurlを抽出
    $.ajax({
      url: url,  // リクエストを送信するURLを指定
      type: "POST",  // HTTPメソッドを指定(デフォルトはGET)
      data: {  // 送信するデータをハッシュ形式で指定
        "note[body]": inputText
      },
      dataType: "json"  // レスポンスデータをjson形式と指定する
    })
    .done(function(data) {
      let html = createHTML(data);  // 受信したデータ(data)を元に追加するURLを生成(createHTML関数は冒頭で定義)
      $(".notes").append(html);  // 生成したHTMLをappendメソッドでドキュメントに追加
      $(".note_form-text").val("");  // textareaを空にする
    })
    .fail(function() {
      alert("error!");  // 通信に失敗した場合はアラートを表示
    })
    .always(function() {
      $(".note_form-btn").prop("disabled", false);  // submitボタンのdisableを解除
      $(".note_form-btn").removeAttr("data-disable-with");  // submitボタンのdisableを解除(Rails5.0以降はこちらも必要)
    });
  });
});

コントローラーは、次のように記載しています。

notes_controller.rb
class NotesController < ApplicationController
  def index
    @note = Note.new
    @notes = Note.includes(:user)
  end

  def create
    @note = Note.new(note_params)
    if @note.save
      respond_to do |format|
        format.html { redirect_to root_path }
        format.json { render json: {
            body: @note.body,
            user_name: @note.user.name,
            user_id: @note.user_id,
            id: @note.id
          }
        }
      end
    end
  end

  def destroy
    @note = Note.find(params[:id])
    if @note.destroy
      respond_to do |format|
        format.html { redirect_to root_path }
        format.json { render json: { id: params[:id] } }
      end
    end
  end

  private
  def note_params
    params.require(:note).permit(:body).merge(user_id: current_user.id)
  end
end

2-1-1. jQueryにおけるAjax通信の基本的な記載項目

必要ないかもしれませんが、備忘としてjQueryのAjaxについても、ざっと概要のみ紹介しておきます。
不要な方は読み飛ばしてください。

2-1-1-1. $.ajax({})

jQueryでAjax通信をする場合に、送信データとして記載する内容は次のとおりです。

項目 内容
url リクエストを送信するURLを指定(formタグのaction属性にあるURLを指定します)
type HTTPメソッドを指定(デフォルトはGET)
data 送信するデータをハッシュ形式で指定
dataType サーバから返信されるデータの形式を指定

●参考サイト(本記事の全般で参考にさせていただいています)
jQuery逆引きリファレンス:一般的なAjax通信を実装するには?
JavaScript 日本語リファレンス jQuery $.ajax()

2-1-1-2. .done(function(data){})

.doneの部分には、通信が成功した場合の処理を記載します。
引数のdataには、受信したデータが格納されています。

2-1-1-3. .fail(function(){})

.failの部分には、通信が失敗した場合の処理を記載します。

2-1-1-4. .always(function(){})

.alwaysの部分には、通信の成功、失敗に関わらず行う処理を記載します。

ここには、「submitボタンが2回目以降押せなくなる」というRailsの仕様を解除するためのコードを記載しています。

sample.js
.always(function() {
  $(".note_form-btn").prop("disabled", false);  // submitボタンのdisableを解除
  $(".note_form-btn").removeAttr("data-disable-with");  // submitボタンのdisableを解除(Rails5.0以降はこちらも必要)
})

submitボタンのdisableを解除するためには、上記の1つ目の処理のように$(セレクタ名).prop("disabled", false);と記載します。

ただし、Railsの新しいバージョン(Rails5.0以降)では、上記のコードを記載しただけでは、submitボタンの無効が解除されません。
その理由は、ボタンの2度押しを回避するために、"data-disable-with"という属性が自動で追加される仕様となっているためです。

これについては、上記の2つ目のように、"data-disable-with"属性自体を消去するコードを追加することで(参照記事:[Rails5] submitタグでAjaxを使うと2回目以降に無効になる)submitボタンがロックされなくなります。

●参考サイト
Rails で JavaScript を使用する / 3.4 入力を自動で無効にする

2-1-2. セキュリティトークンについて

jQueryでAjaxを記述する場合は、本来、セキュリティトークンは意識しなくとも良いのですが、JavaScriptでコードを記載する場合に必要になりますので先に触れておきます。

Railsにおいて、POSTメソッド等でサーバにリクエストを送信する場合は、サーバからクライアントあてに発行されているセキュリティトークン(ワンタイムパスワードのようなもの)を送信データと共に送らなければ、リクエストは受け付けられない仕様になっています。

しかし、今回のサンプルコードでは、特にセキュリティトークンの送信について記述していません。

下のようにログを確認しても、パラメータとしては、Parameters: {"note"=>{"body"=>"こんにちは!"}}というように、1つのみのデータしか送られていません。

log/development.log
Started POST "/notes" for ::1 at 2020-01-19 12:30:52 +0900
Processing by NotesController#create as JSON
  Parameters: {"note"=>{"body"=>"こんにちは!"}}

このように、明示的にセキュリティートークンの送信を指定しなくても、リクエストは正常に処理されています。

なぜこのようにリクエストが正常に成立するかと言うと、jQueryで処理する場合は、サーバにリクエストを送信する際のリクエストヘッダに、自動的にセキュリティトークンが付加されているからでした。
実際のリクエストヘッダは、次のようになっています。上から7行目のX-CSRF-Token:のところがセキュリティトークンに当たります。

RequestHeaders
POST /notes HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 69
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:3000
X-CSRF-Token: gHVpLnJtIFjVzl5VsSESArnw7+sNU67AenEoa29eALi3s9EPl+O5VbM8TnE1QgrA1PbS4Avhdg9atdz2rDcJhg==
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
(以下略)

リクエストヘッダの確認方法は、「HTTPレスポンスヘッダ・リクエストヘッダ情報をウェブブラウザで表示・確認する方法」でご確認ください。

●参考サイト
Rails で JavaScript を使用する / 6 AjaxのCSRF(Cross-Site Request Forgery)トークン

2-1-3. (参考)FormDataを使用した場合のコード

参考のため、FormDataを使用した場合の記載も掲載しておきます。
省略した部分は、先に記載している「FormDataを使用しない場合」と同様です。

note.js
$(function() {
  // (省略)
  $("#note_input").on("submit", function(e) {
    e.preventDefault();
    let formData = new FormData(this);  // FormDataを作成
    let url = $(this).attr("action");
    $.ajax({
      url: url,
      type: "POST",
      data: formData,  //FormDataをそのまま渡せば良い(必要な"note[body]"と"authenticity_token"を含む)
      dataType: "json",
      processData: false,  //FormDataを使用した場合に必要となる
      contentType: false  //FormDataを使用した場合に必要となる
    })
    .done(function(data) {
      // (以下省略)
    });
  });
});

FormDataを使用した場合に、どのようなデータが送信されているかを確認しておきます。

下記のログに表示されているパラメータには、3つのデータが含まれています。
これは、formタグの中に記載された内容を、そのまま送信しているようです。

2つ目にある"authenticity_token"は、formタグ内に含まれているセキュリティトークンです。
中身の文字列はこの次に掲載しているリクエストヘッダと全く同じです。

log/development.log
Started POST "/notes" for ::1 at 2020-01-19 13:27:54 +0900
Processing by NotesController#create as JSON
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"FcqVGUaR69OzydUk9hwJRleLmX0z6auwkgd+NSy1RXwiDC04ox9y3tU7xQByfxGEOo2kdjVbc3+yw4qo79xMQg==", "note"=>{"body"=>"こんばんは!"}}

リクエストヘッダでも、7行目のところにX-CSRF-Token:とあり、セキュリティトークンが付加されています。

RequestHeaders
POST /notes HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 446
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:3000
X-CSRF-Token: FcqVGUaR69OzydUk9hwJRleLmX0z6auwkgd+NSy1RXwiDC04ox9y3tU7xQByfxGEOo2kdjVbc3+yw4qo79xMQg==
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryPiYVoZWTHjJdIdbK
(以下略)

FormDataでは、以上のようにパラメータ及びリクエストヘッダの両方にセキュリティトークンが付加されています。
実際の挙動を確認しましたところ、どちらか一方にセキュリティトークンがあれば、リクエストは成功するようです。

2-2. JavaScriptでAjaxを記載(POSTメソッド)

前置きが長すぎましたが、ここからが本題です。
JavaScriptでPOSTメソッドを実装すると次のようなコードとなります。変に長いですが。

note.js
window.addEventListener("load", function() {
  let token = document.getElementsByName("csrf-token")[0].content; //セキュリティトークンの取得
  // 追加するHTMLデータを生成する関数
  function createHTML(note) {
    // 必要となるタグ及びテキストノードを生成
    let divElm = document.createElement("div"); 
    let spanElm = document.createElement("span");
    let aElm = document.createElement("a");
    let pElm = document.createElement("p");
    let nameText = document.createTextNode("投稿者:" + note.user_name);
    let deleteText = document.createTextNode(" 削除");
    let bodyText = document.createTextNode(note.body);
    // 各タグに属性・属性値を付加
    divElm.setAttribute("class", "note");
    divElm.setAttribute("id", "note" + note.id);
    spanElm.setAttribute("class", "note_name");
    aElm.setAttribute("class", "note_delete");
    aElm.setAttribute("rel", "nofollow");
    aElm.setAttribute("data-method", "delete");
    aElm.setAttribute("href", "/notes/" + note.id);
    // aElm.addEventListener("click", function(e) { deleteHTMLEvent(e, aElm) }, false); //ここは削除メソッド実装時に使用
    pElm.setAttribute("class", "note_body");
    // ノードの結合(各子要素にテキストを追加)
    spanElm.appendChild(nameText);
    aElm.appendChild(deleteText);
    pElm.appendChild(bodyText);
    // ノードの結合(親要素に子要素を追加)
    divElm.appendChild(spanElm);
    divElm.appendChild(aElm);
    divElm.appendChild(pElm);
    return divElm;
  };
  // メモ投稿(POSTメソッド)の処理
  document.getElementById("note_input").addEventListener("submit", function(e) {
    e.preventDefault();  // デフォルトのイベント(HTMLデータ送信など)を無効にする
    //送信データの生成
    let inputText = document.getElementsByClassName("note_form-text")[0].value;  // textareaの入力値を取得
    let url = document.getElementById("note_input").getAttribute("action") + ".json";  // 末尾に[.json]を追加して送信データがjson形式であることを指定
    let hashData = {  // 送信するデータをハッシュ形式で指定
      note: {body: inputText}  // 入力テキストを送信
      // authenticity_token: token  // セキュリティトークンの送信(ここから送信することも可能)
    };
    let data = JSON.stringify(hashData); // 送信用のjson形式に変換
    // Ajax通信を実行
    let xmlHR = new XMLHttpRequest();  // XMLHttpRequestオブジェクトの作成
    xmlHR.open("POST", url, true);  // open(HTTPメソッド, URL, 非同期通信[true:default]か同期通信[false]か)
    xmlHR.responseType = "json";  // レスポンスデータをjson形式と指定
    xmlHR.setRequestHeader("Content-Type", "application/json");  // リクエストヘッダーを追加(HTTP通信でJSONを送る際のルール)
    xmlHR.setRequestHeader("X-CSRF-Token", token);  // リクエストヘッダーを追加(セキュリティトークンの追加)
    xmlHR.send(data);  // sendメソッドでサーバに送信
    // 受信したデータの処理
    xmlHR.onreadystatechange = function() {
      if (xmlHR.readyState === 4) {  // readyStateが4になればデータの読込み完了
        if (xmlHR.status === 200) {  // statusが200の場合はリクエストが成功
          let note = xmlHR.response;  // 受信したjsonデータを変数noteに格納
          let html = createHTML(note);  // 受信データを元にHTMLを作成
          document.getElementsByClassName("notes")[0].appendChild(html);  // 作成したHTMLをドキュメントに追加
          document.getElementsByClassName("note_form-text")[0].value = "";  // テキストエリアを空白に戻す
        } else {  // statusが200以外の場合はリクエストが適切でなかったとしてエラー表示
          alert("error");
        }
        document.getElementsByClassName("note_form-btn")[0].disabled = false;  // submitボタンのdisableを解除
        document.getElementsByClassName("note_form-btn")[0].removeAttribute("data-disable-with");  // submitボタンのdisableを解除(Rails5.0以降はこちらも必要)
      }
    };
  }, false);
});

●参考サイト
基本的なことは、全般的に次の記事を参考にさせていただきました。
JavascriptのAjaxについての基本まとめ

2-2-1. ノード(Elementオブジェクト)の取得等

最初に、jQueryとJavaScriptの記述の違いとして、基本的なところを記録しておきます。

上記の、JavaScriptのサンプルコードでは、ノード(Elementオブジェクト)の取得等において、次のようなメソッドを使用しています。
対比が分かりやすいように、表の右側には、対応するjQueryの記述を入れました。

項番 内容 JavaScript jQueryt
1 ID名から取得 document.getElementById("note-id") $("#note-id")
2 class名から取得 document.getElementsByClassName("note_form-text")[0] $(".note_form-text")
3 name名から取得 document.getElementsByName("csrf-token")[0] $("*[name=csrf-token]")
4 属性値を取得(getAttributeメソッド) document.getElementById("note_input").getAttribute("action") $("#note_input").attr("action")
5 ページ全体の読込みができたことの確認 window.addEventListener("load", function() {}) $(function() {})

表中の項番1から3は、要素(Elementオブジェクト)の取得で使用するメソッドです。

class名、name名を指定してElementオブジェクトを取得する場合、jQueryでは最初に見つかったElementオブジェクトを返しますが、JavaScriptでは該当する全ての要素を返すため、要素を指定する番号[0]を付すことが必要です。

一方、id名を指定してElementオブジェクトを取得する場合は、結果は1つのみ(idは原則として1つしか使用されない)ですので、要素の番号指定は不要です。
idによる指定では、getElementByIdというように、[Element]と単数形になっていることからも、取得するノード数が1つであることが分かります。

2-2-2. HTMLデータの生成

ページに追加するHTML作成において使用しているメソッドについて、jQueryと比較しつつ簡単にまとめておきます。

例として、投稿者名とコメントを表示するだけの簡単なHTMLを書いてみます。
まず、jQueryでは、次のように、バッククオートでHTML文を囲むことで、簡単にHTMLの生成ができます。

sample.js
let html = `<div class="comment">
              <p class="comment_name">投稿者:山田</p>
              こんにちは!
            </div>`

これと同じ内容を、JavaScriptで記述すると、次のようになります。

sample.js
    let divElm = document.createElement("div");  // divタグを生成: <div></div>
    divElm.setAttribute("class", "comment");  // class名を追加: <div class="comment"></div>

    let pElm = document.createElement("p");  // pタグを生成: <p></p>
    pElm.setAttribute("class", "omment_name");  // class名を追加: <p class="comment_name"></p>
    pElm.appendChild(document.createTextNode("投稿者:山田"));  // テキストノードを追加: <p class="comment_name">投稿者:山田</p>

    let commentText = document.createTextNode("こんにちは!");  // テキストノードを生成: "こんにちは!"
    divElm.appendChild(pElm);  // divタグ(divElm)の子要素にpタグ(pElm)を追加
    divElm.appendChild(commentText); // divタグ(divElm)の子要素にテキストノード("こんにちは!")を追加

    let html = divElm;

JavaScriptにおけるHTML生成のためのメソッドは、主に次のとおりです。
これらを組み合わせて、HTMLを生成していくということになります(もっと簡単な方法があれば良いのですが)。

項番 項目 構文 具体例
1 要素(タグ)の生成 document.createElement(タグ名) document.createElement("div")
2 テキストノードの生成 document.createTextNode(テキスト文) document.createTextNode("こんにちは!")
3 属性の追加 要素ノード.setAttribute(属性名, 属性値) divElm.setAttribute("class", "comment")
4 子要素として追加 親ノード.appendChild(子ノード) divElm.appendChild(pElm)

●参考サイト
【JavaScript入門】appendと何が違う?appendChild徹底解説

2-2-3. サーバへのリクエスト送信

sample.js
let xmlHR = new XMLHttpRequest();  // XMLHttpRequestオブジェクトの作成
xmlHR.open("POST", url, true);  // open(HTTPメソッド, URL, 非同期通信[true:default]か同期通信[false]か)
xmlHR.responseType = "json";  // レスポンスデータをjson形式と指定
xmlHR.setRequestHeader("Content-Type", "application/json");  // リクエストヘッダーを追加(HTTP通信でJSONを送る際のルール)
xmlHR.setRequestHeader("X-CSRF-Token", token);  // リクエストヘッダーを追加(セキュリティトークンの追加)
xmlHR.send(data);  // sendメソッドでサーバに送信

サーバにリクエストを送信するために使用しているオブジェクト及びメソッドは、次のとおりです。

項番 項目 内容
1 XMLHttpRequestオブジェクト サーバとの通信を行うためのオブジェクト(API)。このオブジェクトにより、ページ全体の更新をすることなくサーバとの送受信を行うことができる。
2 XMLHttpRequest.open()メソッド XMLHttpRequestオブジェクトのメソッド。リクエストの作成に使用する。
3 XMLHttpRequest.responseTypeメソッド XMLHttpRequestオブジェクトのメソッド。サーバから返信されるデータの形式を指定。
4 XMLHttpRequest.setRequestHeaderメソッド XMLHttpRequestオブジェクトのメソッド。リクエストヘッダーを追加する。
5 XMLHttpRequest.send()メソッド XMLHttpRequestオブジェクトのメソッド。引数に指定したデータをサーバに送信する。

2-2-3-1. XMLHttpRequestオブジェクト

まず、1つめのXMLHttpRequestオブジェクトですが、これについては、次の説明が直感的に分かりやすいです。

XMLHttpRequestはブラウザ上でサーバーとHTTP通信を行うためのAPIです。名前にXMLが付いていますがXMLに限ったものではなく,HTTPリクエストを投げてテキスト形式かDOMノードでレスポンスを受け取る機能を持っています。(これでできる! クロスブラウザJavaScript入門

このXMLHttpRequestオブジェクトを介して、サーバへのリクエストを作成してデータのやり取りを行うことになります。

●参考サイト
MDN Web Docs / XMLHttpRequest

2-2-3-2. XMLHttpRequest.open()メソッド

構文:XMLHttpRequest.open(HTTPメソッド, URL, 非同期通信か同期通信か)
このopen()メソッドは、リクエストを作成する場合に使用します。
第1引数でHTTPメソッド、第2引数でURL、第3引数で非同期通信[true]か同期通信[false]かを指定します。第3引数はデフォルトがtrueなので、一般的には省略されているようです。

更に、第4引数(ユーザ名)、第5引数(パスワード)もありますので、詳しくは「MDN Web Docs / XMLHttpRequest.open()」を参照してください。

なお、JSON形式で送信する場合、URLの指定において、末尾に.JSONを付すことが必要です。これにより、送信データがjson形式であることが指定されます。

2-2-3-3. XMLHttpRequest.responseTypeメソッド

responseTypeメソッドでは、サーバから返信されるデータの形式を指定します。
ここでは、返信されるデータ形式として"json"を指定していますが、そのほかの形式として、"arraybuffer""blob""document""text"などの形式の指定ができます(詳細は「MDN Web Docs - XMLHttpRequest.responseType」を参照してください)。

2-2-3-4. XMLHttpRequest.setRequestHeaderメソッド

これは、HTTP通信におけるリクエストヘッダを追加するメソッドです(参考「MDN Web Docs / XMLHttpRequest.setRequestHeader()」)。

sample.js
xmlHR.setRequestHeader("Content-Type", "application/json");  // リクエストヘッダーを追加(HTTP通信でJSONを送る際のルール)
xmlHR.setRequestHeader("X-CSRF-Token", token);  // リクエストヘッダーを追加(セキュリティトークンの追加)

JSON形式のリクエストを送信する場合には、上記1行目のように、Content-Typeヘッダに"application/json"(又は"text/json")というMIMEタイプの指定が必要となります(引用サイト:JSON リクエストとレスポンス)。

2行目のコードは、セキュリティトークンをリクエストヘッダに追加する処理です。
このセキュリティトークンの詳細は、次の項「2-2-4. セキュリティトークンの送信」に書いています。

●参考サイト
XMLHttpRequestでJSONをPOST
ajaxでpostするときに必要なリクエストヘッダ

2-2-3-5. XMLHttpRequest.send()メソッド

send()メソッドは、リクエストをサーバに送信するメソッドです。送信するデータを引数に指定することができます(参考「MDN Web Docs / XMLHttpRequest.send()」)。
関係部分を抜粋すると、次のようになっています。

sample.js
let hashData = {  // 送信するデータをハッシュ形式で指定
  note: {body: inputText}  // 入力テキストを送信
};
let data = JSON.stringify(hashData); // 送信用のjson形式に変換
xmlHR.send(data);  // sendメソッドでサーバにリクエストを送信

上記のコードでは、まず、ハッシュオブジェクトとしてデータを作成し、これを、JSON.stringifyメソッドを使用して、JSON文字列に変換しています(参考「MDN Web Docs / JSON.stringify()」)。
これにより、引数に指定したデータをJSON形式として送信することができます。

2-2-4. セキュリティトークンの送信

JavaScriptでAjaxを実装する場合には、セキュリティトークン(サーバから発行されるワンタイムパスワードのようなもの)を意識する必要があります。

自動的にセキュリティトークンが送信されるjQueryの場合とは異なり(前出の「2-1-2. セキュリティトークンについて」を参照)、JavaScriptでAjaxを記述する場合には、サーバへのHTTPリクエストにセキュリティトークンを付加する処理が必要となります。
この処理を書かなければ、POSTメソッドやDELETEメソッドによるリクエストを、サーバ側で受け付けることができません。

サンプルコードから、セキュリティトークンに関する部分を抜粋すると、次のようになっています。

sample.js
let token = document.getElementsByName("csrf-token")[0].content; //セキュリティトークンの取得
// (中略)
let xmlHR = new XMLHttpRequest();  // XMLHttpRequestオブジェクトの作成
// (中略)
xmlHR.setRequestHeader("X-CSRF-Token", token);  // リクエストヘッダーを追加(セキュリティトークンの追加)

上記の最初の行の処理で、ページのHTMLヘッダからセキュリティトークンの情報を抽出し、最後の行で、リクエストヘッダにセキュリティトークンを追加する処理を行っています。

なお、セキュリティトークンは、ページのHTMLを見れば確認できます。

(HTMLのヘッダ部分に表示されているセキュリティトークン)

sample.html
<head>
  <title>DemoApp</title>
  <meta name="csrf-param" content="authenticity_token">
  <meta name="csrf-token" content="RqHWJuRG/kobdPDTVdQEaJAl7hwpmfptmuJbamK/+5JxZ24HAchnR32G4PfRtxyq/SPTFy8rIqK6Jq/3odbyrA==">
  <!-- 略 -->
</head>

ヘッダ部分の3行目にある<meta name="csrf-token" content="RqHWJuRG/kobdPDTVdQEaJAl7hwpmfptmuJbamK/+5JxZ24HAchnR32G4PfRtxyq/SPTFy8rIqK6Jq/3odbyrA==">がセキュリティトークンです。
この、contentの属性値を拾ってサーバに送信していると言うことになります。

また、Formタグ(下記)の中にも、name="authenticity_token"の属性valueに、セキュリティートークンが格納されています。こちらからデータを拾って送信しても同様の結果を得ることができます。

sample.html
<form id="note_input" class="note_form" action="/notes" accept-charset="UTF-8" method="post">
  <input name="utf8" type="hidden" value="✓">
  <input type="hidden" name="authenticity_token" value="RqHWJuRG/kobdPDTVdQEaJAl7hwpmfptmuJbamK/+5JxZ24HAchnR32G4PfRtxyq/SPTFy8rIqK6Jq/3odbyrA==">
  <textarea class="note_form-text" name="note[body]" id="note_body"></textarea>
  <input type="submit" name="commit" value="メモを登録" class="note_form-btn" data-disable-with="メモを登録">
</form>

詳細は、Rails で JavaScript を使用する / 6 AjaxのCSRF(Cross-Site Request Forgery)トークンを参照してください。

2-2-5. サーバからのレスポンスの受信

2-2-5-1. レスポンス処理の構造

JavaScriptにおいては、次のように記述することで、リクエストの成否に合わせて処理が実行されます。

(JavaScriptにおけるレスポンスの処理)

sample.js
xmlHR.onreadystatechange = function() {
  if (xmlHR.readyState === 4) {  // readyStateが4になればデータの読込み完了
    if (xmlHR.status === 200) {  // statusが200の場合はリクエストが成功
      // (1) リクエストが成功した場合に行う処理
    } else {  // statusが200以外の場合はリクエストが適切でなかったとしてエラー表示
      // (2) リクエストが成功しなかった場合に行う処理
    }
    // (3) リクエストの成功・失敗に関わらず行う処理
  }
};

下記は、jQueryでのコード記載です。JavaScriptにおける(1)から(3)の処理とほぼ同じ形で対応しています(細かい違いはあるかもしれませんが)。

(jQueryにおけるレスポンスの処理)

sample.js
.done(function(data) {
  // (1) リクエストが成功した場合に行う処理
})
.fail(function() {
  // (2) リクエストが成功しなかった場合に行う処理
})
.always(function() {
  // (3) リクエストの成功・失敗に関わらず行う処理
});

2-2-5-2. レスポンスデータの取得について

JavaScriptのレスポンスデータは、次のように取得することができます。
(JavaScriptにおけるレスポンスデータの取得)

sample.js
if (xmlHR.status === 200) {  // statusが200の場合はリクエストが成功
  let note = xmlHR.response;  // XMLHttpRequest.responseメソッドからレスポンスデータを取得できる
  // (略)
}

対比として、jQueryにおけるレスポンスデータの取得も以下に記載しておきます。
(jQueryにおけるレスポンスデータの取得)

sample.js
.done(function(data) {
  let note = data;  // 引数のdataからレスポンスデータを取得できる
  // (略)
})

2-2-5-3. readyStateプロパティとstatusプロパティについて

XMLHttpRequest.readyStateプロパティ

これは、XMLHttpRequestオブジェクトのプロパティとなります。
readyStateプロパティは、XMLHttpRequestのインスタンスの状態を0から4の数値で返します。
数値と状態の対比は次のとおりです(引用サイト「MDN Web Docs / XMLHttpRequest.readyState」→こちらを見ていただいた方が正確です)。

戻り値 状態 内容
0 UNSENT XMLHttpRequestのインスタンス作成済み
1 OPENED open()メソッド呼び出し済み
2 HEADERS_RECEIVED send()メソッド呼び出し済み
3 LOADING レスポンスデータの読み込み中
4 DONE 読み込み完了

サンプルコードでは、if (xmlHR.readyState === 4)という条件を満たした場合に、レスポンスデータの処理等を行うという構造になっています。

XMLHttpRequest.statusプロパティ

このXMLHttpRequest.statusプロパティには、サーバから送信される「HTTPステータスコード」が格納されています(引用サイト「MDN Web Docs / XMLHttpRequest.status」)。
このHTTPステータスコードにより、HTTPリクエストが正常に終了したかどうかが分かります。
下記は今回の記事作成中に見かけたHTTPステータスコードです(ほんの一例です)。

コード 意味 説明
200 OK リクエストが成功した
400 Bad Request 無効なリクエストが送信されたなど
404 Not Found リクエストされたリソースが見つからなかった
422 Unprocessable Entity リクエストは正しいがサーバで処理できない

例えば、セキュリティトークンが送信されていない場合、422のエラーが生じます。
その他、コードの一覧は「MDN Web Docs / HTTP レスポンスステータスコード」などで確認できます。

サンプルコードでは、if (xmlHR.status === 200)という条件を満たした場合にはリクエスト成功時の処理を記述し、条件を満たさなかった場合にはリクエスト失敗時の処理を記述するという構造になっています。

3. 削除(DELETEメソッド)についてのAjaxのコード

次に、投稿内容を削除するDELETEメソッドについてのAjax通信です。

3-1. jQueryでAjaxを記載(DELETEメソッド)

まず、jQueryでの実装例です。
新たに削除(DELETE)メソッドを追加した部分は、後半の20行分となります。

note.js
$(function() {
  // 追加するHTMLデータを生成する関数
  function createHTML(note) {
    let html = `<div class="note" id="note${note.id}">
                  <span class="note_name">投稿者:${note.user_name}</span>
                  <a class="note_delete" rel="nofollow" data-method="delete" href="/notes/${note.id}">削除</a>
                  <p class="note_body">${note.body}</p>
                </div>`
    return html;
  }
  // メモ投稿(POSTメソッド)の処理
  $("#note_input").on("submit", function(e) {
    e.preventDefault();  // デフォルトのイベント(HTMLデータ送信など)を無効にする
    let inputText = $(".note_form-text").val();  // textareaの入力値を取得
    let url = $(this).attr("action");  // action属性のurlを抽出
    $.ajax({
      url: url,  // リクエストを送信するURLを指定
      type: "POST",  // HTTPメソッドを指定(デフォルトはGET)
      data: {  // 送信するデータをハッシュ形式で指定
        "note[body]": inputText
      },
      dataType: "json"  // レスポンスデータをjson形式と指定する
    })
    .done(function(data) {
      let html = createHTML(data);  // 受信したデータ(data)を元に追加するURLを生成(createHTML関数は冒頭で定義)
      $(".notes").append(html);  // 生成したHTMLをappendメソッドでドキュメントに追加
      $(".note_form-text").val("");  // textareaを空にする
    })
    .fail(function() {
      alert("error!");  // 通信に失敗した場合はアラートを表示
    })
    .always(function() {
      $(".note_form-btn").prop("disabled", false);  // submitボタンのdisableを解除
      $(".note_form-btn").removeAttr("data-disable-with");  // submitボタンのdisableを解除(Rails5.0以降はこちらも必要)
    });
  });
  // メモ削除(DELETEメソッド)の処理
  $(".notes").on("click", ".note_delete", function(e) {
    e.preventDefault();  // デフォルトのイベント(リンクURLへの遷移処理など)を無効にする
    e.stopPropagation();  // 現在のイベントのさらなる伝播(DELETEメソッドの実行)を止める
    let url = $(this).attr("href");
    $.ajax({
      url: url,
      type: "POST",  // 原則に従って"DELETE"メソッドを使用しない
      data: {
        _method: "delete",  // ここで"DELETE"メソッドを使用することを指定
      },
      dataType: "json"
    })
    .done(function(data) {
      $("#note" + data.id).remove();  // レスポンスデータのIDを元に投稿を削除
    })
    .fail(function(XMLHttpRequest) {
      alert(XMLHttpRequest.status);
    });
  });
});

基本的な形式は、POSTメソッドと変わりません。
異なる部分を中心に、以下、説明を書いていきます。

3-1-1. 動的に追加した要素をクリックする(jQuery)

前提として、jQuery(JavaScript)のファイル読み込み処理について確認しておきます。

まず、jQuery(JavaScript)のコードを記載したファイルの読み込みですが、これは、最初のページ読み込み時にのみ行われます。
次のような、クリックなどのイベント発火による処理も、クリックされるたびにファイルが読み込まれているわけではなく、最初の1回のみ読み込まれて、それをブラウザ内に記憶しておき、都度ブラウザ内の記憶を呼び出して実行しているということになります。

sample.js
$("#note_input").on("submit", function(e) {
  // イベント発火時に実行する内容
})

このようにイベントに基づき発火するメソッド(関数)は、「イベントリスナー(イベント実行リストのようなもの)」という名目でブラウザ内に登録されています。

以上のことから、ページを読み込んだ後(JavaScriptのファイルの読み込みが終わった後)に、動的に新たに追加された要素は、イベントリスナーとして登録されないことになるため、クリックしても反応しないということになってしまいます。

jQueryでは、この問題を、簡単に解決できるようになっています。
サンプルコード上で、その処理を実現している部分は、次の部分となります。

sample.js
$(".notes").on("click", ".note_delete", function(e) {
  // イベント発火時の処理内容を記載
});

これを構文として書き表すと次のようになります。
$(親要素のセレクタ).on(イベントの種類, 子要素のセレクタ, 関数等のオブジェクト)

この構文において、クリック(他のイベントも同じ)によるイベント発火は、見かけ上、子要素へのクリックに基づき実行されます。
ところが、実際は、親要素へのクリックにより、イベント発火が行われているという仕組みになっています。

そのため、イベント発火の元となる親要素は、最初(ページ読み込み時)に存在している必要がありますが、子要素は、後から追加された動的な要素であっても構わないという仕組みになっています(と理解しています)。

具体的にこのjQueryのメソッドどのような構造なのかについては、下記のサイトを参考にしていただければと思います。

●参考サイト
[jQuery] on() で後から追加した要素にもイベントを定義したい
jQuery write less, do more. / .on()

3-1-2. preventDefault()メソッド及びstopPropagation()メソッド

次に、デフォルトの処理等をキャンセルするpreventDefault()メソッド及びstopPropagation()メソッドについてです。
サンプルコードでは、次のように記載しています。

sample.js
$(".notes").on("click", ".note_delete", function(e) {
  e.preventDefault();  // デフォルトのイベント(リンクURLへの遷移処理)を無効にする
  e.stopPropagation();  // 現在のイベントのさらなる伝播(DELETEメソッドの実行)を止める
  // (略)
});

これらの記述により、HTMLに基づくデフォルトのイベント処理をキャンセルすることで、無用の処理を発生させず、Ajaxでの処理との重複などを避けることができます。
以下、個別に見てみます。

3-1-2-1. Event.preventDefault()メソッド

preventDefault()メソッドは、デフォルトのイベント処理をキャンセルして実行しないようにするメソッドです(参照:MDN Web Docs / Event.preventDefault())。

サンプルコードでは、次のaタグにおけるリンク機能が、preventDefault()メソッドによりキャンセルされています。

sample.html
<a class="note_delete" rel="nofollow" data-method="delete" href="/notes/5">削除</a>

3-1-2-2. event.stopPropagation()メソッド

stopPropagation()メソッドは、現在のイベントの更なる伝播をキャンセルするメソッドです(参照:MDN Web Docs / event.stopPropagation)。

サンプルコードでは、上記aタグの属性data-method="delete"によるdeleteメソッドの実行が、stopPropagation()メソッドによりキャンセルされています。

なお、全ての削除処理をAjax通信のみで行うのであれば、data-method="delete"の部分を消してしまうことでも同様の結果が得られると思います(stopPropagation()メソッドがなくてもエラーなく処理が実行できることになります)。

3-1-3. HTTPにおけるDELETEメソッドについて

3-1-3-1. POSTメソッドによる削除

サンプルコードでは、削除(DELETE)の処理であるにも関わらず、次のように、typeとして"POST"メソッドを指定しています。

sample.js
$.ajax({
  url: url,
  type: "POST",  // 原則に従って"DELETE"メソッドを使用しない
  data: {
    _method: "delete",  // ここで"DELETE"メソッドを使用することを指定
  },
  dataType: "json"
})

POSTメソッドを使用して、削除機能を実装する理由は、「jQueryの日本語リファレンス / $.ajax()」にある次の説明のとおりで、DELETEメソッドが全てのブラウザでサポートされている保証がないからです。

キー:type
型:String 初期値:'GET'
リクエストのタイプ("POST"または"GET")を指定します。
注意: PUTやDELETEのような、他のHTTPリクエストメソッドも、ここで指定することが可能ですが、 全てのブラウザでサポートされている保証がありません。

具体的な、コードの記載については、次の記事などを参考とさせていただきました。
[Laravel / jQuery] 非同期(Ajax)でレコードを削除したい

3-1-3-2. DELETEメソッドによる削除

どこまでの環境で動作するかは確認できていませんが、次のようにDELETEメソッドによる記載をしても、Ajaxによる削除を行うことが可能です。

sample.js
$.ajax({
  url: url,
  type: "DELETE",  // 原則に従って"DELETE"メソッドを使用しない
  dataType: "json"
})

3-2. JavaScriptでAjaxを記載(DELETEメソッド)

次に、JavaScriptでの削除(DELETEメソッド)機能の実装例です。

note.js
window.addEventListener("load", function() {
  let token = document.getElementsByName("csrf-token")[0].content; //セキュリティトークンの取得
  // 追加するHTMLデータを生成する関数
  function createHTML(note) {
    // 必要となるタグ及びテキストノードを生成
    let divElm = document.createElement("div"); 
    let spanElm = document.createElement("span");
    let aElm = document.createElement("a");
    let pElm = document.createElement("p");
    let nameText = document.createTextNode("投稿者:" + note.user_name);
    let deleteText = document.createTextNode(" 削除");
    let bodyText = document.createTextNode(note.body);
    // 各タグに属性・属性値を付加
    divElm.setAttribute("class", "note");
    divElm.setAttribute("id", "note" + note.id);
    spanElm.setAttribute("class", "note_name");
    aElm.setAttribute("class", "note_delete");
    aElm.setAttribute("rel", "nofollow");
    aElm.setAttribute("data-method", "delete");
    aElm.setAttribute("href", "/notes/" + note.id);
    aElm.addEventListener("click", function(e) { deleteHTMLEvent(e, aElm) }, false); //ここが大事!
    pElm.setAttribute("class", "note_body");
    // ノードの結合(各子要素にテキストを追加)
    spanElm.appendChild(nameText);
    aElm.appendChild(deleteText);
    pElm.appendChild(bodyText);
    // ノードの結合(親要素に子要素を追加)
    divElm.appendChild(spanElm);
    divElm.appendChild(aElm);
    divElm.appendChild(pElm);
    return divElm;
  };

  // メモ投稿(POSTメソッド)の処理
  let addHTMLEvent = function(e) {
    e.preventDefault();  // デフォルトのイベント(HTMLデータ送信など)を無効にする
    //送信データの生成
    let inputText = document.getElementsByClassName("note_form-text")[0].value;  // textareaの入力値を取得
    let url = document.getElementById("note_input").getAttribute("action") + ".json";  // 末尾に[.json]を追加して送信データがjson形式であることを指定
    let hashData = {  // 送信するデータをハッシュ形式で指定
      note: {body: inputText}  // 入力テキストを送信
      // authenticity_token: token  // セキュリティトークンの送信(ここから送信することも可能)
    };
    let data = JSON.stringify(hashData); // 送信用のjson形式に変換
    // Ajax通信を実行
    let xmlHR = new XMLHttpRequest();  // XMLHttpRequestオブジェクトの作成
    xmlHR.open("POST", url, true);  // open(HTTPメソッド, URL, 非同期通信[true:default]か同期通信[false]か)
    xmlHR.responseType = "json";  // レスポンスデータをjson形式と指定
    xmlHR.setRequestHeader("Content-Type", "application/json");  // リクエストヘッダーを追加(HTTP通信でJSONを送る際のルール)
    xmlHR.setRequestHeader("X-CSRF-Token", token);  // リクエストヘッダーを追加(セキュリティトークンの追加)
    xmlHR.send(data);  // sendメソッドでサーバに送信
    // 受信したデータの処理
    xmlHR.onreadystatechange = function() {
      if (xmlHR.readyState === 4) {  // readyStateが4になればデータの読込み完了
        if (xmlHR.status === 200) {  // statusが200の場合はリクエストが成功
          let note = xmlHR.response;  // 受信したjsonデータを変数noteに格納
          let html = createHTML(note);  // 受信データを元にHTMLを作成
          document.getElementsByClassName("notes")[0].appendChild(html);  // 作成したHTMLをドキュメントに追加
          document.getElementsByClassName("note_form-text")[0].value = "";  // テキストエリアを空白に戻す
        } else {  // statusが200以外の場合はリクエストが適切でなかったとしてエラー表示
          alert("error");
        }
        document.getElementsByClassName("note_form-btn")[0].disabled = false;  // submitボタンのdisableを解除
        document.getElementsByClassName("note_form-btn")[0].removeAttribute("data-disable-with");  // submitボタンのdisableを解除(Rails5.0以降はこちらも必要)
      }
    };
  };
  document.getElementById("note_input").addEventListener("submit", addHTMLEvent, false);

  // 削除イベントの処理
  let deleteHTMLEvent = function(e, noteDelete) {
    e.preventDefault();
    e.stopPropagation();
    let url = noteDelete.getAttribute("href") + ".json"; //末尾に .json を追加することで送信データがjson形式であることを指定する
    // Ajax通信を実行
    let xmlHR = new XMLHttpRequest(); //XMLHttpRequestの作成
    xmlHR.open("DELETE", url, true); //open(HTTPメソッド, URL, 非同期通信[true:default]か同期通信[false]か)
    xmlHR.responseType = "json"; //レスポンスデータを json形式と指定する
    xmlHR.setRequestHeader("Content-Type", "application/json");  // リクエストヘッダーを追加(HTTP通信でJSONを送る際のルール)
    xmlHR.setRequestHeader("X-CSRF-Token", token);  // リクエストヘッダーを追加(セキュリティトークンの追加)
    xmlHR.send(); //サーバに送信
    // 受信したデータの処理
    xmlHR.onreadystatechange = function() {
      if (xmlHR.readyState === 4) {
        if (xmlHR.status === 200) {
          let note = xmlHR.response;
          document.getElementById("note" + note.id).remove();
        } else {
          alert(xmlHR.status);
        }
      }
    };
  };
  let noteDeletes = document.getElementsByClassName("note_delete")
  Array.prototype.forEach.call(noteDeletes, function(noteDelete) {
    noteDelete.addEventListener("click", function(e) { deleteHTMLEvent(e, noteDelete) }, false);
  });
});

POSTメソッドのサンプルコードに、末尾の27行を追加した上で、その他に若干の修正を行っています。

3-2-1. AjaxによるDELETEメソッドの送信(JavaScript)

Ajax通信を行うために、XMLHttpRequestオブジェクトを使ってサーバと通信するという点は、基本的にPOSTメソッドの場合と変わりません。

該当部分のコードは次のとおりです。

sample.js
let xmlHR = new XMLHttpRequest(); //XMLHttpRequestの作成
xmlHR.open("DELETE", url, true); //open(HTTPメソッド, URL, 非同期通信[true:default]か同期通信[false]か)
xmlHR.responseType = "json"; //レスポンスデータを json形式と指定する
xmlHR.setRequestHeader("Content-Type", "application/json");  // リクエストヘッダーを追加(HTTP通信でJSONを送る際のルール)
xmlHR.setRequestHeader("X-CSRF-Token", token);  // リクエストヘッダーを追加(セキュリティトークンの追加)
xmlHR.send(); //サーバに送信

異なると言えば、send()メソッドの送信時に、引数としてデータを指定していないということくらいでしょうか。

削除時のログは、次のようになっています。

log/development.log
Started DELETE "/notes/591.json" for ::1 at 2020-01-19 15:53:48 +0900
Processing by NotesController#destroy as JSON
  Parameters: {"id"=>"591", "note"=>{}}

send()メソッドの引数として"id"は特に指定していませんが、パラメータとしてしっかり送られています。
URLの情報に"id"が含まれているので、当然と言えば当然だと思います。

なお、このJavaScriptにおいても、POSTメソッドを使用した削除機能(DELETEメソッド)の実装を試みましたが、設定が上手く行かず、現時点では実装できていません。

3-2-2. 複数の要素にイベントリスナーを適用する方法(JavaScript)

POSTメソッドの実装時に、イベントリスナーを適用するノード(要素、タグ)は、formタグの中のsubmitボタンのクリックイベントの1箇所だけでした。
しかし、DELETEメソッドでは、表示されている全ての投稿の削除ボタンにイベントリスナーを設定する必要があります。

投稿の全てに、イベントリスナーを設定するには、次のように、Array.prototype.forEach.callメソッドなどを用いて該当する全ての要素にイベントリスナーを登録することになります。
この方法は、「【JavaScript】イベントリスナを複数要素にまとめて登録する方法」で解説されている内容を使用させていただきました。

sample.js
let noteDeletes = document.getElementsByClassName("note_delete")
Array.prototype.forEach.call(noteDeletes, function(noteDelete) {
  noteDelete.addEventListener("click", function(e) { deleteHTMLEvent(e, noteDelete) }, false);
});

これは、for文で単純にループさせることでも可能です。

なお、コールバック関数に引数があるため、function(e) { deleteHTMLEvent(e, noteDelete) },という特殊な書き方になっています。
これについては、「Javascriptでイベントハンドラのコールバック関数に引数を渡す」の記事等を参考にさせていただきました。詳細はリンク先を参照してください。

●参考サイト
配列ライクなオブジェクトをforEachするときのイディオム
JavaScriptでコールバック関数にあらかじめ引数を渡したい!

3-2-3. 動的に追加された要素へのイベントリスナーの適用(JavaScript)

動的に追加された要素(本サンプルではメモ投稿を表示するノード)に、後からイベントリスナーを登録するには、多少の工夫が必要となります。

大事なところは、createHTML関数の中に追加した次の1行です(上から21行目)。

sample.js
aElm.addEventListener("click", function(e) { deleteHTMLEvent(e, aElm) }, false); //ここが大事!

新規投稿のHTMLが生成がされる度に、上記の1行でaタグに新しいイベントリスナーを追加するようにしています。

ここでコールバック関数として呼び出されているのはfunction(e) { deleteHTMLEvent(e, aElm) }の部分です(コールバック関数に引数を渡す方法については、「Javascriptでイベントハンドラのコールバック関数に引数を渡す」を参照)。

呼びだされるコールバック関数は、最初にページをロードする際に読み込む関数と同じもので、具体的には、次の部分となります。

sample.js
let deleteHTMLEvent = function(e, noteDelete) {
  e.preventDefault();
  e.stopPropagation();
  let url = noteDelete.getAttribute("href") + ".json"; //末尾に .json を追加することで送信データがjson形式であることを指定する
  // Ajax通信を実行
  let xmlHR = new XMLHttpRequest(); //XMLHttpRequestの作成
  xmlHR.open("DELETE", url, true); //open(HTTPメソッド, URL, 非同期通信[true:default]か同期通信[false]か)
  xmlHR.responseType = "json"; //レスポンスデータを json形式と指定する
  xmlHR.setRequestHeader("Content-Type", "application/json");  // リクエストヘッダーを追加(HTTP通信でJSONを送る際のルール)
  xmlHR.setRequestHeader("X-CSRF-Token", token);  // リクエストヘッダーを追加(セキュリティトークンの追加)
  xmlHR.send(); //サーバに送信
  // 受信したデータの処理
  xmlHR.onreadystatechange = function() {
    if (xmlHR.readyState === 4) {
      if (xmlHR.status === 200) {
        let note = xmlHR.response;
        document.getElementById("note" + note.id).remove();
      } else {
        alert(xmlHR.status);
      }
    }
  };
};

新規の投稿がページに追加される度に、上記の関数が呼び出されて、イベントリスナーとして追加登録されているということになります。

●参考サイト
[JavaScript] イベント処理を動的に追加する
動的に追加した要素にaddEventListnerを設定する方法

4. おわりに

まとめておきたかった内容は、以上のところです。

挙動は確認しているので、動作はするはずですが、もっと良い書き方や、正しい書き方があるのだろうと思います。
お気付きのことがあれば、ご指摘等をいただけると幸いです。

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

整数から任意の桁の数字を取得する方法

取得方法

sample.rb
num = 3259

# 1000の位
place_1000 = (num / 1000) % 10
# 100の位
place_100 = (num / 100) % 10
# 10の位
place_10 = (num / 10) % 10
# 1の位
place_1 = (num / 1) % 10

puts place_1000
puts place_100
puts place_10
puts place_1

出力結果

sample.rb
3
2
5
9

解説

sample.rb
num = 3259

# 10の位
place_10 = (num / 10) % 10

=> 5

上記の計算式を電卓で打つと
(num / 10) => 325.9
325.9 % 10 => 5.9

答え 5.9

これがどうして、5が返り値として帰ってくるかというと、
rubyでは整数同士の計算は、整数が返り値として返ってくるからです。
そのため上記の計算結果により、
十の位であるである5が返り値となるわけです。

Integerクラス

大前提として、
integerクラスは整数のみを扱います。

もし小数点以下の数値が混ざっていた場合には、
to_iメソッドを用いて整数のみを取得します。

sample.rb
num = 3259.67854

# 1000の位
place_1000 = (num.to_i / 1000) % 10
# 100の位
place_100 = (num.to_i / 100) % 10
# 10の位
place_10 = (num.to_i / 10) % 10
# 1の位
place_1 = (num.to_i / 1) % 10

puts place_1000
puts place_100
puts place_10
puts place_1

出力結果

sample.rb
3
2
5
9

以上です。

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

NoMethodError in Tweets#new

undefined method `from_tag' for #<#Class:0x00000000050b1058:0x00007fed8162ca98> Did you mean? form_tag

formタグのスペルが間違えていた(誤from)

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

[Ruby on Rails]新規アプリケーションの作成

新規アプリケーションの作成

新規アプリケーションの作成

tarminal
% rails _5.2.4.1_ new アプリ名 -d mysql

Git Hubとの連携の準備

tarminal
% cd アプリ名

アプリ名 % git init

GitHub Desktopでの操作

  1. 左上の▽ をクリックする
  2. Add▽ をクリックする
  3. Add Existing Repository をクリックし、ファイルを選択する
  4. Add Reposity をクリックする(この時、サイドバーのGItHubのユーザー名がOtherになってても大丈夫)

GitHubでの操作

  1. サイドバーの緑色のNEWアイコンをクリックする
  2. リポジトリ名を入力する。なんでもいいが、アプリ名と同じにした方が分かりやすい
  3. HTTPにして、URLをコピーする

GitHubとの連携

tarminal
アプリ名 % git remote add origin URL

URLには先ほどコピーしたものを貼り付ける。
このタイミングで、GitHubDesctopでの名前がOtherから自分のユーザー名に変わっているはず。(変わってなかったら、GitHubDesctopを再起動してみる)

最初のコミット

この時点で、最初にマスターでコミットしておくといい。
1. GitHub Desktopでコミット名を入力する。なんでもいいが、initial commitとしておく
2. commit to master をクリックする
3. 上のPublish Repositoryをクリックする

よく使うGemの準備と導入

ブランチを新たに切って、Gemのインストールと導入を行う

よく使うGemのインストール

Gemfile
gem 'haml-rails'
gem 'font-awesome-sass'
gem 'jquery-rails'

group :development, :test do
  gem 'pry-rails'
end

Gemfileに上記を記入して、bundle installを行う。

erbからhamlへ変換

tarminal
HAML_RAILS_DELETE_ERB=true rails haml:erb2haml

これで、元のerbを削除して、hamlに変換できる

jQueryの導入

/app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs

上記をapplication.jsの一番下に追記。
rails 6以降はjQueryの導入の仕方が変わった。
/config/webpacker/environment.jsを生成するために、以下のコマンドを実行してから、以下のリンクを参考にjQueryを導入する。

tarminal
%  rails webpacker:install

https://qiita.com/masahisa/items/eaacb0c3b82f4a11fc13

scssの準備

/app/asset/stylesheetsにあるapplication.cssの名前をapplication.scssに変更する。scssをインポートする時は以下のように追記する。

/app/asset/stylesheets/application.scss
@import "reset";
@import "test";
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]入力した2整数を行ごとに合計し、n行の和を求める

条件

与えられたn行のスペース区切りの2つの整数に対し、2つの整数をそれぞれ足し合わせ、その結果をすべての行について足し合わせて出力する。
2つの整数が同じだった場合は、2つの整数を掛け合わせてから、その結果を足し合わせることとする。

コード

count = gets.chomp.to_i
sum = 0

(1..count).each { |i|
    num = gets.chomp.split(' ')
    num1 = num[0].to_i
    num2 = num[1].to_i

    if num1 == num2
        sum += num1 * num2
    else
        sum += num1 + num2
    end
}

puts sum

動作環境

Ruby 2.6.5
macOS Catalina 10.15.2

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

【Rails】ActionMailer + AWS SES

はじめに

Rails アプリケーションからユーザーへメールを飛ばす方法としてAWS SESというサービスがあるので、こちらの使い方を説明させていただきます。
設定方法なので知っていればすぐにできるんですが、知らなければやり方を見つけるだけでかなり時間がかかってしまうと思います。
時間短縮の一助になれば嬉しいです。

関連リンク

Gmailを用いたActionMailerの使い方についても下記に載せておくので、必要であれば参考にしてください。。

アプリケーション作成

以下の項目については、上記の"Gmailを用いた ActionMailer の使い方"と同様となるため、そちらをご参照ください。

  • Mailer作成コマンド(rails g mailer ~)
  • Mailer
  • Controller
  • View

ドメイン関連

下記リンク先をご参照ください。

IAMユーザーの作成

下記リンク先をご参照ください。

config / yaml設定

config/development.rb
  # ActionMailer Setting with AWS SES
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.perform_caching = true
  config.action_mailer.default_url_options = { host: Rails.application.secrets.host }
  ActionMailer::Base.smtp_settings = {
      :address =>        Rails.application.secrets.address,
      :port =>           587,
      :domain =>         Rails.application.secrets.domain,
      :authentication => :login,
      :user_name =>      Rails.application.secrets.access_key_id,
      :password =>       Rails.application.secrets.secret_access_key
  }
config/production.rb
  # ActionMailer Setting with AWS SES
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.perform_caching = true
  config.action_mailer.default_url_options = { host: Rails.application.secrets.host }
  ActionMailer::Base.smtp_settings = {
      :address =>        Rails.application.secrets.address,
      :port =>           587,
      :domain =>         Rails.application.secrets.domain,
      :authentication => :login,
      :user_name =>      Rails.application.secrets.access_key_id,
      :password =>       Rails.application.secrets.secret_access_key
  }
secret.yml
development:
  host:              ec2-*****.ap-northeast-1.compute.amazonaws.com  # EC2インスタンス の パブリック DNS (IPv4) を入力する。
  access_key_id:     A******************A                            # 上記で取得した値
  secret_access_key: B******************************************B    # 上記で取得した値
  address:           email-smtp.us-east-1.amazonaws.com              # 自身が仕様しているリージョン
  domain:            c*****.com                                      # 上記で取得した値

production:
  host:              <%= ENV['AWS_HOST'] %>               # ElasticBeanstalk を使う場合、環境変数へ入力する。
  access_key_id:     <%= ENV['AWS_ACCESS_KEY_ID'] %>      # ElasticBeanstalk を使う場合、環境変数へ入力する。
  secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>  # ElasticBeanstalk を使う場合、環境変数へ入力する。
  address:           <%= ENV['AWS_ADDRESS'] %>            # ElasticBeanstalk を使う場合、環境変数へ入力する。
  domain:            <%= ENV['AWS_DOMAIN'] %>             # ElasticBeanstalk を使う場合、環境変数へ入力する。

まとめ

今回、ドメインを取得するところもAWSのサービスを使って説明をさせていただきましたが、ドメイン取得については他のサービスを使ったほうがコストが低いケースもありますので、あくまで一例とさせていただきます。
個人的には色々なサービスを使うよりもAWSでまとめて管理できるほうが管理コストも含めると安いのではないかなーと思ったりします。
お好きな方法で取り組んでいただけたらなと思います。

参考

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

MacでのRuby環境構築2020

MacでのRuby環境構築についてまとめます。改めて公式文書を読みながら進めたところ、今までやっていたことをスキップできる!と気づいたので、久々にQiitaに書こうと思います。

事前準備

動作確認環境

  • MacBook Pro (13-inch, 2019, Four Thunderbolt 3 ports)
  • Catalina 10.15.1

今回構築する環境の概要、ゴール

複数バージョンのRubyを管理するために、rbenv を使用します。インストールにはHomebrewを使います。
最終的に $ ruby --version で意図したバージョン名が表示されることを確認します。

前提

以下の環境であることを前提とします。

  • Homebrewを使えること
    • Homebrewがインストールできていない方は、 Homebrew を確認してインストールをお願いします
  • gitを使えること
    • Homebrewが使用できれば、 $ brew install git でインストール可能です。

rbenv環境構築

基本的に、https://github.com/rbenv/rbenv#installation にしたがって操作していきます。

1. rbenv のインストール

以下のコマンドを実行します。

$ brew install rbenv

rbenvがインストールされます。
同時に、 ruby-build もインストールされます。

2. rbenv のセットアップ

以下のコマンドを実行します。

$ rbenv init
# Load rbenv automatically by appending
# the following to ~/.zshrc:

eval "$(rbenv init -)"

上に書いたようなログが出力されています。指示通りに eval "$(rbenv init -)"~/.zshrc に書き込みます。
私の場合は、zsh を使用しているため、このようなログ(# the following to ~/.zshrc:)が出ていますが、お使いのシェルによって、どのファイルに書き込むかは(ログの内容も)変わってきます。

eval "$(rbenv init -)" を ~/.zshrc に書きむとは何を意味するのか
ちょっと話題がそれますが、簡単に説明しておきます。
zsh を使用している環境において ~/.zshrc ファイルに書き込まれた内容は、ログインしたときやターミナルを開いた時などに実行されます(ここでは詳しくは説明しません)。~/.zshrc ファイルに eval "$(rbenv init -) を書き込むということは、ログインするときやターミナルを開き直す度に、 eval "$(rbenv init -) を実行したい、ということになります。

3. ターミナル再起動

ターミナルを再起動することで、 ~/.zshrc ファイルが読み込まれます。
ターミナル再起動の代わりに、 $ source ~/.zshrc でも内容を反映することが可能です。

4. rbenvインストール環境確認

以下のコマンドを実行し、バージョンが表示されればrbenvのインストールは完了です。

$ rbenv
rbenv 1.1.2
...
...

ruby環境構築

インストールした rbenv を利用して ruby をインストールしていきます。
こちらも基本的に https://github.com/rbenv/rbenv#installing-ruby-versions にしたがって操作をします。

1. インストール可能なバージョンの確認

以下のコマンドで、インストール可能なrubyのバージョンを確認します。

$ rbenv install -l
1.8.5-p52
1.8.5-p113
1.8.5-p114
1.8.5-p115
1.8.5-p231
...
...
2.7.0
...
...

2. ruby のインストール

今回は現時点で stable (安定版)となっている2.7.0をインストールします。

$ rbenv install 2.7.0

また、全てのシェルでこのバージョンを使用したいため、global設定を行います。

$ rbenv global 2.7.0

3. ターミナルの再起動

特に手順には書いてありませんでしたが、私の環境ではターミナルの再起動を行うことで意図したバージョンのrubyを使えるようになりました。

4. rubyがインストールされたことを確認する

以下のコマンドで、rubyのバージョンを確かめます。

$ ruby --version

意図したバージョンになっていることが確認できれば、インストールは完了です。

参考

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

ページ遷移先でリロードしないと非同期通信(ajax)できない

はじめに

某プログラミングスクールの課題で、Railsを使ってECサイトを作成しています。

エラー発生時の状況

トップページにて、link_to でページに移動するとリロードしないと非同期通信できない

エラーの仮説

turbolinksが邪魔してそう...

turbolinksってなんだっけ?

turbolinksとは、ページ遷移をAjaxに置き換え、JavaScriptやCSSのパースを省略することで高速化するgemで、Rails 4からはデフォルトで使用されるようになります。

原因

data-turbolinkが働いて遷移したページではイベントが発火しない事があるらしい.

対策

= link_to new_item_path,data: {"turbolinks" => false}, class: "seller_btn" do

これ→

{"turbolinks" => false},

data-turbolinkをオフにする

遷移元のリンクタグにdata属性を追加

参考記事

Rails6でjqueryアニメーションライブラリanimsitionの使用 | 躓いたことなど...
https://qiita.com/lookatachic/items/cc3accb542fca0eaf43a

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

#Stripe API + #Ruby でサブスクリプションの請求で支払いが失敗したテストデータを作成する

  • No attach payment method to customer
  • Create SubscriptionSchedule
  • Stripe::Invoice.pay
# Docs

# https://stripe.com/docs/api/subscription_schedules/create
#
# Invoicing workflow | Stripe Billing
# https://stripe.com/docs/billing/invoices/workflow
#
# https://stripe.com/docs/api/invoices/

# Code

require 'stripe'

Stripe::api_key = ENV['STRIPE_SECRET_KEY']

product = Stripe::Product.create(name: "Gold plan #{rand(9999999999)}")
plan = Stripe::Plan.create(interval: 'month', currency: 'jpy', amount: 1000, product: product.id)
tax_rate = Stripe::TaxRate.create(display_name: 'Tax Rate', percentage: 10.0, inclusive: false)
customer = Stripe::Customer.create
payment_method = Stripe::PaymentMethod.create(type: 'card', card: { number: '4242424242424242', exp_year: 2030, exp_month: 01})

# No attach payment method
# customer_payment_method = Stripe::PaymentMethod.attach(payment_method.id, customer: customer.id)

customer = Stripe::Customer.retrieve(customer.id)

# Set start_date to immediately start schedule
subscription_schedule = Stripe::SubscriptionSchedule.create(
  {
    customer: customer.id,
    start_date: 'now',
    phases: [
      {
        plans:
          [
            {plan: plan.id, quantity: 1},
          ],
          default_payment_method: payment_method.id,
          default_tax_rates: [tax_rate],
      },
    ],
  }
)

latest_invoice = Stripe::Subscription.retrieve(id: subscription_schedule.subscription, expand: ['latest_invoice']).latest_invoice

begin
  Stripe::Invoice.pay(latest_invoice.id)
rescue Stripe::CardError => exception
  puts exception.message
  # Stripe::CardError: Cannot charge a customer that has no active card
end

latest_invoice = Stripe::Subscription.retrieve(id: subscription_schedule.subscription, expand: ['latest_invoice']).latest_invoice

payment_intent = Stripe::PaymentIntent.retrieve(latest_invoice.payment_intent)

puts '=' * 100
puts "INVOICE"
puts '-' * 100
puts latest_invoice


puts '=' * 100
puts "PAYMENT INTENT"
puts '-' * 100
puts payment_intent

puts '=' * 100
puts "You can See in Dash boards"
puts '-' * 100
puts "https://dashboard.stripe.com/test/subscription_schedules/#{subscription_schedule.id}"
puts "https://dashboard.stripe.com/test/subscriptions/#{subscription_schedule.subscription}"
puts "https://dashboard.stripe.com/test/invoices/#{latest_invoice.id}"

# $ STRIPE_SECRET_KEY=sk_test_xxx ruby ~/y/stripe/invoice-failure-subscription-schedule.rb
# Cannot charge a customer that has no active card
# ====================================================================================================
# INVOICE
# ----------------------------------------------------------------------------------------------------
# {
#   "id": "in_1G27vXCmti5jpytUKjNPvSEs",
#   "object": "invoice",
#   "account_country": "JP",
#   "account_name": "yumainaura",
#   "amount_due": 1100,
#   "amount_paid": 0,
#   "amount_remaining": 1100,
#   "application_fee_amount": null,
#   "attempt_count": 1,
#   "attempted": true,
#   "auto_advance": true,
#   "billing_reason": "subscription_create",
#   "charge": null,
#   "collection_method": "charge_automatically",
#   "created": 1579317887,
#   "currency": "jpy",
#   "custom_fields": null,
#   "customer": "cus_GZGSZ0GhDJaQJk",
#   "customer_address": null,
#   "customer_email": null,
#   "customer_name": null,
#   "customer_phone": null,
#   "customer_shipping": null,
#   "customer_tax_exempt": "none",
#   "customer_tax_ids": [

#   ],
#   "default_payment_method": null,
#   "default_source": null,
#   "default_tax_rates": [
#     {
#       "id": "txr_1G27vUCmti5jpytUyNK9MbxD",
#       "object": "tax_rate",
#       "active": true,
#       "created": 1579317884,
#       "description": null,
#       "display_name": "Tax Rate",
#       "inclusive": false,
#       "jurisdiction": null,
#       "livemode": false,
#       "metadata": {
#       },
#       "percentage": 10.0
#     }
#   ],
#   "description": null,
#   "discount": null,
#   "due_date": null,
#   "ending_balance": 0,
#   "footer": null,
#   "hosted_invoice_url": "https://pay.stripe.com/invoice/invst_UxerJurenn1Cw4pFghbiaVxnIl",
#   "invoice_pdf": "https://pay.stripe.com/invoice/invst_UxerJurenn1Cw4pFghbiaVxnIl/pdf",
#   "lines": {
#     "object": "list",
#     "data": [
#       {
#         "id": "sli_0616e17807f234",
#         "object": "line_item",
#         "amount": 1000,
#         "currency": "jpy",
#         "description": "1 × Gold plan 5102348880 (at ¥1,000 / month)",
#         "discountable": true,
#         "livemode": false,
#         "metadata": {
#         },
#         "period": {
#           "end": 1581996287,
#           "start": 1579317887
#         },
#         "plan": {
#           "id": "plan_GZGSaZUc6735b0",
#           "object": "plan",
#           "active": true,
#           "aggregate_usage": null,
#           "amount": 1000,
#           "amount_decimal": "1000",
#           "billing_scheme": "per_unit",
#           "created": 1579317883,
#           "currency": "jpy",
#           "interval": "month",
#           "interval_count": 1,
#           "livemode": false,
#           "metadata": {
#           },
#           "nickname": null,
#           "product": "prod_GZGSh0Su4l7pzQ",
#           "tiers": null,
#           "tiers_mode": null,
#           "transform_usage": null,
#           "trial_period_days": null,
#           "usage_type": "licensed"
#         },
#         "proration": false,
#         "quantity": 1,
#         "subscription": "sub_GZGS8XDtly5sH4",
#         "subscription_item": "si_GZGSz9VgJjuH9g",
#         "tax_amounts": [
#           {
#             "amount": 100,
#             "inclusive": false,
#             "tax_rate": "txr_1G27vUCmti5jpytUyNK9MbxD"
#           }
#         ],
#         "tax_rates": [

#         ],
#         "type": "subscription",
#         "unique_id": "il_1G27vXCmti5jpytUd3Hi7GAn"
#       }
#     ],
#     "has_more": false,
#     "total_count": 1,
#     "url": "/v1/invoices/in_1G27vXCmti5jpytUKjNPvSEs/lines"
#   },
#   "livemode": false,
#   "metadata": {
#   },
#   "next_payment_attempt": null,
#   "number": "5DF27B15-0001",
#   "paid": false,
#   "payment_intent": "pi_1G27vYCmti5jpytUh2JcpXp6",
#   "period_end": 1579317887,
#   "period_start": 1579317887,
#   "post_payment_credit_notes_amount": 0,
#   "pre_payment_credit_notes_amount": 0,
#   "receipt_number": null,
#   "starting_balance": 0,
#   "statement_descriptor": null,
#   "status": "open",
#   "status_transitions": {
#     "finalized_at": 1579317888,
#     "marked_uncollectible_at": null,
#     "paid_at": null,
#     "voided_at": null
#   },
#   "subscription": "sub_GZGS8XDtly5sH4",
#   "subtotal": 1000,
#   "tax": 100,
#   "tax_percent": 10.0,
#   "total": 1100,
#   "total_tax_amounts": [
#     {
#       "amount": 100,
#       "inclusive": false,
#       "tax_rate": "txr_1G27vUCmti5jpytUyNK9MbxD"
#     }
#   ],
#   "webhooks_delivered_at": null
# }
# ====================================================================================================
# PAYMENT INTENT
# ----------------------------------------------------------------------------------------------------
# {
#   "id": "pi_1G27vYCmti5jpytUh2JcpXp6",
#   "object": "payment_intent",
#   "amount": 1100,
#   "amount_capturable": 0,
#   "amount_received": 0,
#   "application": null,
#   "application_fee_amount": null,
#   "canceled_at": null,
#   "cancellation_reason": null,
#   "capture_method": "automatic",
#   "charges": {
#     "object": "list",
#     "data": [

#     ],
#     "has_more": false,
#     "total_count": 0,
#     "url": "/v1/charges?payment_intent=pi_1G27vYCmti5jpytUh2JcpXp6"
#   },
#   "client_secret": "pi_1G27vYCmti5jpytUh2JcpXp6_secret_4WoOHxA8JRDIF50hViRA1duI6",
#   "confirmation_method": "automatic",
#   "created": 1579317888,
#   "currency": "jpy",
#   "customer": "cus_GZGSZ0GhDJaQJk",
#   "description": "Invoice 5DF27B15-0001",
#   "invoice": "in_1G27vXCmti5jpytUKjNPvSEs",
#   "last_payment_error": null,
#   "livemode": false,
#   "metadata": {
#   },
#   "next_action": null,
#   "on_behalf_of": null,
#   "payment_method": null,
#   "payment_method_options": {
#     "card": {
#       "installments": null,
#       "request_three_d_secure": "automatic"
#     }
#   },
#   "payment_method_types": [
#     "card"
#   ],
#   "receipt_email": null,
#   "review": null,
#   "setup_future_usage": null,
#   "shipping": null,
#   "source": null,
#   "statement_descriptor": null,
#   "statement_descriptor_suffix": null,
#   "status": "requires_payment_method",
#   "transfer_data": null,
#   "transfer_group": null
# }
# ====================================================================================================
# You can See in Dash boards
# ----------------------------------------------------------------------------------------------------
# https://dashboard.stripe.com/test/subscription_schedules/sub_sched_1G27vXCmti5jpytU4cIDMnpV
# https://dashboard.stripe.com/test/subscriptions/sub_GZGS8XDtly5sH4
# https://dashboard.stripe.com/test/invoices/in_1G27vXCmti5jpytUKjNPvSEs

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2952

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

ハッシュロケットをシンボルに書き換える

修正内容

ハッシュの記載方法についてハッシュロケット → シンボルに修正する必要があったので、変更内容について記載します。
ハッシュロケットは古い記載方法で、今はシンボルで書くことが主流のようです。

ハッシュロケット

:test => "hashrocket"

シンボル

test: "simbol"

修正後

今回は、以下ようなmetaタグを修正しました。

before

%meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/

after

%meta{content: "text/html; charset=UTF-8", "http-equiv": "Content-Type"}/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

https(SSL)通信の環境下でjavascriptが動かなくなる場合の原因と解決方法 ( 本番環境(AWS)でjavascriptを読み込む方法 )

はじめに

某プログラミングスクールの課題で、Railsを使ってECサイトを作成しています。

発生時の状況

商品出品画面のカテゴリー選択時、javascriptを使ってサブカテゴリーを入力できる機能を実装
本番環境にデプロイすると、カテゴリー選択時にサブカテゴリーが出現せず入力できませんでした。

エラーの仮説

・javascriptが本番環境で読み込めてない

エラーの原因

本番環境つまり、httpsによるSSL暗号通信下では、読み込んでない。

対策

1,jQuery本体をダウンロードして自分のサーバーで動かす方法。

2,URLをhttpsに変更する方法。

application.html
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

3,プロトコルを指定しない方法。

application.html
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

3つ目の方法が一番汎用性が高いのでおすすめです。この記述方法はjavascriptに限らず、CSSや画像のパスの指定にも使えるので、覚えておくと便利だそうです!!


参考記事

https://www.webernote.net/webcreate/https-javascript.html


最後に

自分のメモ用と、アウトプットとして記事に投稿させていただきました。

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

[Ruby]複数行の数値の入力・出力

はじめに

※今回の投稿が初めてとなります。見づらい点や誤りがあれば教えてください。
知識のアウトプットとしてQiitaに載せました。

複数行の数値の入力・出力

nums = gets.chomp.to_i

nums.times do
  input_num = gets.chomp.to_i
  puts input_num
end

入力した数値をnumsとし、nums回分だけ、入力した数値を出力する。

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

injectの解釈

eachより簡潔に書ける

inject.rb
def keisan
  (1..20).inject(0) do | sum, num|
    sum += num
  end
end
#sum = 0 inject(0)←かっこ内(引数)が変数の初期値。

ただし、メソッドを書いてそこの値を戻すようにしないと、
メソッドの外(スコープ外のため)では使えない。

eachとの使い分けは「見やすいかどうか」で決める。
Rubyは自由度が高いようので、
色んな書き方ができそう。

他人が見たときにわかりやすく書くことが重要

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

競プロでなんとなく使っていたメソッドを調べた

RubyでAtCoderを解いていて、なんとなく雰囲気で使っていたメソッドを調べた。

map

こんな感じで使っていた

1 2 3 4 5

こんな感じの標準入力を受け取る際に、

array = gets.chomp.split(" ").map(&:to_i) 
p array # => [1, 2, 3, 4, 5]

として、配列に文字列ではなく、数値として格納する際に利用していた。

ここでのmapの役割

配列の各要素に対して、ブロックを評価した結果を全て含む配列を返す。Rubyのリファレンスマニュアルの利用例を以下に紹介

# すべて 3 倍にする
p [1, 2, 3].map {|n| n * 3 }  # => [3, 6, 9]

なるほど。

map(&:to_i)の"&:"って何?

mapでやっていることは、リファレンスマニュアルの例で理解できた。じゃあ、&は何者なのか?

&

今回のケースでは以下のような役割があるらしい。

Proc オブジェクトをブロックとして使う

リファレンス

Procとは

ブロックをコンテキスト(ローカル変数のスコープやスタックフ レーム)とともにオブジェクト化した手続きオブジェクトです。

リファレンス

ちょっとよくわからないけど、ブロック付きのメソッドに対して、&を使ってProcオブジェクトを呼び出すと、いい感じに書けるらしい。

つまり、初めに戻って、

array = gets.chomp.split(" ").map(&:to_i) 

array = gets.chomp.split(" ").map { |number| number.to_i }

の振る舞いは同じで、前者の方が直感的でわかりやすい気がする。

inject

こんな感じで使っていた

[1,2,3,4,5]

こんな感じの配列の和を計算するときに、

array.inject(:+) # => 15

ここでのinjectの役割

ブロックを使って繰り返し計算を行うのに使う。injectメソッドにブロックの代わりにシンボルsymでメソッド名を渡すことができる。
第1引数initに初期値を指定すると、「初期値.メソッド(要素1)」、「前回の結果.メソッド(要素2)」、...を計算する。

リファレンスの例を紹介

numbers = [4, 3, 9, 8, 5, 6, 1, 7, 2]
puts numbers.inject(:+) # => 45
puts numbers.inject(100, :-) #=> 55

リファレンス

まとめ

知らないことが多くて楽しい。競技プログラミングをちびちび頑張る。

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