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

AtCoder Beginner Contest 143

はじめに

Atcorder ABC143へ初参加。
結果は問A~問Cまでの3問のみ正答。
言語:ruby
Ver:Ruby 2.3.3
URL:https://atcoder.jp/contests/abc143/tasks

143-A(Curtain)

・この問題は窓の幅とカーテンの1辺のサイズ×2と比較する問題
※ただし、値がマイナスにならないよう注意

143-A.rb
A,B = gets.split(' ').map(&:to_i)
Num = A - B*2

if Num < 0
  Num = 0
end

p Num

143-B(TAKOYAKI FESTIVAL 2019)

・たこ焼きの組み合わせの選び方の問題
 1つ目のたこ焼きを限定して、2つ目のたこ焼きを選択

143-B.rb
N = gets.to_i
takoyaki = gets.split(' ').map(&:to_i)

taste_total = 0
a = 0

while a < N do
    taste = 0
    b = a + 1

    while b < N do
        taste = taste + takoyaki[a]*takoyaki[b] 
        b = b + 1
    end
    taste_total = taste_total + taste
    a = a + 1

end

puts taste_total

143-C(Slimes)

・隣接する文字列を判定する問題
・先頭から次の文字が同じかどうか比較し、異なるとスライムの総数を増加

143-C.rb
N = gets.to_i
slime = gets.chomp
slime_total = 0

slime.length.times do |num|
    if slime[num] != slime[num + 1]
        slime_total = slime_total + 1
    end
end

p slime_total

143-D(Triangles)

・解法を最適化する問題

1.すべての辺を1つずつ計算( 計算時間がO(N^3) )

※ 計算時間がかかりすぎるので改善

143-D.rb
N = gets.to_i
hen = gets.split(' ').map(&:to_i)
hen.sort!

triangle_total = 0
a = 0

while a < N - 2 do
    b = a + 1
    while b < N - 1 do 
        c = b + 1
        while c < N do
            if hen[a] + hen[b] > hen[c]
                triangle_total += 1
                c+= 1
            else 
                break
            end
        end
        b = b + 1
    end
    a = a + 1
end

p triangle_total

2.解放を改善(計算時間がO(N^2 log2))

改善のポイント

  1. 143-Bの考え方を応用する
  2. 入力された数値がばらばらだと辺の大小関係が分かりにくい
  3. 入力された内容を昇順にソートする
  4. 三角形の成立条件は「最小の二辺が最大の辺より大きい」という条件に言い換えができる
    ・a < b + c
    ・b < c + a
    ・c < a + b
  5. 最小の2辺を固定したうえで、最大の辺が条件を満たす組み合わせを考える
  6. 最小の2辺(a + b)で残りの1辺がどの値まで条件を満たすか(> c)を考える
  7. 1つずつ左から探索(線形探索)する場合は時間がかかる
  8. 2分木のソート方法を用いて、a + b に近似する値を探索する
    https://qiita.com/ryosuketter/items/2798b09330e7102b6cfe
143-D-2.rb
N = gets.to_i
hen = gets.split(' ').map(&:to_i)
hen.sort!

triangle_total = 0
a = 0

# aの候補の最大要素は配列数の2個前
while a < N - 2 do
    b = a + 1

    # bの候補の最大要素は配列数の1個前
    while b < N - 1 do 
        check_num = hen[a] + hen[b]

        # a + b がソート対象の最小値以下の場合は三角形の成立条件を満たすものはなし
        if check_num <= hen[b + 1]

        # a + b がソート対象の最大値より大きい場合は全ての数値が三角形の成立条件を満たす
        elsif check_num > hen[N - 1]
            triangle_total = triangle_total + N - 1 - b

        # a + b がソート対象範囲内の数字 
        else
            # ソートの先頭と末尾を定義
            head = b + 1
            tail = N - 1

            # ソート対象の中央値より上か下かで範囲を絞る
            while head <= tail

                center = (head + tail) / 2   

                if hen[center] >= check_num
                    tail = center - 1

                elsif hen[center] < check_num
                    if check_num <= hen[center + 1] 
                        triangle_total = triangle_total + center - b
                        break
                    else 
                        head = center + 1
                    end
                end
            end
        end
        b = b + 1
    end
    a = a + 1
end

p triangle_total

3.Rubyのライブラリを使用して改善

・bsearch_indexを使用して、探索用の処理に書き換える
https://docs.ruby-lang.org/ja/latest/method/Array/i/bsearch_index.html

143-D-3.rb
N = gets.to_i
hen = gets.split(' ').map(&:to_i)
hen.sort!

triangle_total = 0
a = 0

while a < N - 2 do
    b = a + 1

    while b < N - 1 do 
        check_num = hen[a] + hen[b]

        if check_num <= hen[b + 1]
        elsif check_num > hen[N - 1]
            triangle_total = triangle_total + N - 1 - b
        else            
           c = hen.bsearch_index { |x|  x >= check_num }
           triangle_total = triangle_total + c - 1 - b
        end
        b = b + 1
    end
    a = a + 1
end

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

インクリメンタルサーチについての復習

インクリメンタルサーチの復習

Ajaxの非同期通信を学習中で、前回はコメントの投稿をページの再読み込みなしで表示させました。
今回は文字を入力して検索する際、検索ボタンを押して再読み込みをさせずに非同期で検索結果を表示させるようにします。

インクリメンタルサーチとは

そもそもインクリメンタルサーチとは、
Incremental Search
Incremental : 増加、増大する、追加する
Search : 検索
と単語の意味で考えると正直分かりませんが、IT用語で「文字入力するたびに自動的に検索が行われる検索方法」を指します。

インクリメンタルサーチの実装

インクリメンタルサーチ実装の流れを復習していきます。
あくまでAjax、JavaScriptの流れを把握することが目的なので、HTMLなどの表記はある程度省略していきます。

search.js
$(function() {
  $(".search__form").on("keyup", function(){

    var input = $(".search__form").val();

    $.ajax({
      type: 'GET',
      url: '/products/search',
      data: { keyword: input },
      dataType: 'json'
    })

    .done(function(products) {
      $(".検索結果表示クラス").empty();
      if (products.length !==0) {
        $(".検索結果表示クラス").append("インクリメンタルサーチの結果を表示させる記述");
      }
      else {
        $(".検索結果表示クラス").append("検索結果がない旨を表示させる記述";
      }
    })
    .fail(function(){
      alert('映画検索に失敗しました');
    })

全体的な記述は上記のようになると思います。
細かい内容をおさらいしていきます。

$(".search__form").on("keyup", function(){

keyupイベントはキーボードのキーが上がった時に発火します。文字入力でキーを離したタイミングですね。
イベントの発火を検知する場所は、ここでは"search__form"クラスを持つ検索フォームが指定されています。

    var input = $(".search__form").val();

検索フォームに入力されている値をinputに代入します。

    $.ajax({
      type: 'GET',
      url: '/products/search',
      data: { keyword: input },
      dataType: 'json'
    })

Ajaxで非同期通信を行う際に指定する内容を記述します。
URL、data内のkey名(keyword)はルーティングやインスタンス変数の定義により変わるので一例です。
今回の記述では、{ keyword: input }データを/products/searchパスにGETメソッドで送り、結果をJSON形式で返してもらいます。

    .done(function(products) {
      $(".検索結果表示クラス").empty();
      if (products.length !==0) {
        $(".検索結果表示クラス").append("インクリメンタルサーチの結果を表示させる記述");
      }
      else {
        "検索結果がない旨を表示させる記述";
      }
    })
    .fail(function(){
      alert('映画検索に失敗しました');
    })

.doneメソッドと.failメソッドは対で記述しておきましょう。そして.failメソッドはalertで「エラーが発生しました」と表示される程度でいいかと思います。

今回の.doneメソッドでは、まず検索結果をproductsに代入します。
現在表示されている検索結果をemptyメソッドで消去します。
emptyメソッドは、その要素そのものではなく子要素を削除します。これで検索結果を表示させる箱は残しながら、中身を全て消してリセットしてしまいます。その後検索結果をリセットされた中身に追加して表示させるようにします。

ifを使って場合分けを行います。検索結果の個数が0でなければその結果を表示させ、検索結果の個数が0の場合は検索結果がない旨の記述が適用されます。

結果

検索フォームに文字入力をすると、キーから指を離すたびにフォーム内の文字列が拾われて都度その文字列で検索をかけられます。
文字を入力する度に検索が実行されているわけですから、PC本体やネット回線のスペックがそれなりにないと負荷がすごいことになりそうですね。
ブロードバンドが普及する前にインクリメンタルサーチが使われていたら、どうなっていたのでしょうか?

JavaScript(jQuery)の書き方にはまだ慣れませんが、書いている中身はなんとなく分かってきた気がします。一気に書くとさっぱり分かりませんが、一つ一つ読み解きながら進めていくとどうにか分かるようになってきました。

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

Rails初学者必見!クラスの作り方分かってないと色々やばい!

なぜ書こうと思ったか

Rails系プログラミングスクール卒業生に
クラスの定義に関して簡単な問題を出したところ、
絶望的にできなかった。

その問題というのはこちら

「User.new(first_name: '太郎').first_name」で「太郎」を取得できるよう、クラス「User」を作成してください。

また、「User.new(first_name: '太郎', last_name: '田中').full_name」で「田中太郎」が取得できるよう、インスタンスメソッド「full_name」を定義してください。

答え書いた方がいいですか?

答え書いちゃうと巷のプログラミングスクールとおんなじのマニュアルお化けになっちゃうので、書きたくないです。
書き方は一つじゃないですし・・・。
頑張って調べて挑戦してみてください。

終わりに

知ってたら5分かからないくらいで書けちゃうコードなんですよね。
Rails系のプログラミングスクールを卒業しても、戦力になっていない人が多いのは、
単純に、Rubyの最低限の基礎知識もわかってないからなんだろうなと、切に思います。
もし、あなたがプログラミングスクールを卒業しても、
この問題を5分足らずで書けないなら、Rubyの基礎をしっかり固める時期だと認識しましょう。

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

未経験がマッチングアプリを作ってみた話【Rails】

はじめに

未経験からのWeb系企業転職をするためのポートフォリオを作成しました。
マッチング機能を用いてエンジニアをつなぐサービスです。
8月から勉強をはじめ、10月から着手し完成まで至りました。(改修は随時行なっていきます。)

なぜ作った?

私は現在地方で働いています。
だいぶ田舎にいるため、東京都比べてしまうとコミュニティが少ないです。というかありません。
このような壁があり、エンジニアとの繋がりが少ないのです。

そこで、エンジニア向けのマッチングアプリがあればこの問題は解決できるのでは?というのが始まりです。
Twitterでよくない?という声が聞こえてきますが、Twitterではその人の得意な技術や好きな分野を調べなくてはなりません。
このサービスでは、「タグ機能」があり、その人の好きな分野をすぐに知ることができます。

 サービス紹介

スクリーンショット 2019-10-20 21.15.18.png

【リンク】
https://engineer-link.herokuapp.com

【Github】
https://github.com/kotaro-saitou/engineer-link-app

Engineer Linkは、エンジニアとエンジニアが気軽にメッセージを送りあえるサービスです。

主な機能

・画像付きユーザー登録機能
・(ログイン時)フォロー/アンフォロー機能
・記事の投稿機能
・タグ(Ruby、Pythonなどの自分の好きな技術など)の画像付き投稿機能
・相互フォローのみリアルタイムメッセージ機能
・記事、タグ、ユーザー一覧でのページネーション機能

技術

・Rails5.2.2
・Ruby2.5.4
・CircleCI
・Rspec
・Heroku
・AmazonS3(プロフィール画像、タグの画像)
・ActionCable(リアルタイムチャット)

工夫

・CircleCIを用いて自動デプロイを実装
・Githubでissue管理を行なった

参考にしたサイト

【Rails】Herokuで画像を投稿できるようにする方法(ActiveStorage + Amazon S3)

https://qiita.com/hmmrjn/items/479c9e9ce82771f1b6d7

Rails5.2から導入されたcredentials.yml.encを極める

https://qiita.com/yuuuking/items/53a37a2e998972be32b8

Rails 5 + ActionCableで作る!シンプルなチャットアプリ(DHH氏のデモ動画より)

https://qiita.com/jnchito/items/aec75fab42804287d71b

未経験がWeb系転職成功したいならgithubでissue管理して開発しよう

https://qiita.com/fukubaka0825/items/c7710b4e87d478c8ba3b

全てとても参考になりました!!
ありがとうございます!!!

思ったこと

このサービスを開発するにあたり、以下の技術は勉強しながら実装しました。

・Rspec
・ActionCable
・CircleCI

この辺りのことを実装するにあたり、「手を動かしながら学ぶことの重要性」を感じました。(特にRspec)

Rspecの勉強はEveryday Railsが本当におすすめです!!
https://leanpub.com/everydayrailsrspec-jp
翻訳してくださっている方々に深く感謝しながら勉強しましょう。

CircleCIに関しては、調べながらやっていけば比較的簡単に導入できます。

ポートフォリオを作成できていない未経験の方へ

Progateなどで基礎を勉強することは楽しいですし、素晴らしいことです。
しかし、勉強したことを元に物を作るのはもっと楽しいです!

なんでもいいので、物を作り、そこに自分にとって未知の技術を取り入れることは、
勉強にもなり非常に楽しいです。

最後に

これからは、この二ヶ月はサーバーサイドに偏りすぎていたので、フロントの勉強に力を入れつつ
引き続き頑張ります!

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

eachで回す時の<% %>と<%= %>の違い

eachで回していて、
なぜか、以下の画像のように出力されました。
スクリーンショット 2019-10-20 21.12.05.png

[#<User id: 1, name: "aaaa", email: "aaaaa", created_at: "2019-10-19 12:10:03", updated_at: "2019-10-19 12:10:03">,
 #<User id: 2, name: "bbb", email: "bbbb", created_at: "2019-10-19 12:10:35", updated_at: "2019-10-19 12:10:35">]

この部分いらないな。てか、なんで出てきたんだろう。今までこんな事なかったのに、、、
と思っていたところ、問題は、viewでした。

index.html.erb
<h1>Users#index</h1>
<p>Find me in app/views/users/index.html.erb</p
<div>
  <%= @user.each do |user| %>
    <p>
      <%= user.name %>
      <%= user.email %>
    </p>
  <% end %>
</div>

<%= @user.each do |user| %>の=が問題でした。
<% @user.each do |user| %>とすると、いらない部分は消えました。
=をつけると、データ側も出力されるという新しい発見でした笑

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

Payjpを使って、商品購入機能を実装する【Rails】

概要

「payjp」というgemを使うことで、簡単にクレジットカード(以下CC)購入フォームと機能を作成することができます。
本記事では、某フリマアプリのクローンアプリを開発した際に購入機能を実装する上で、登録画面のマークアップからpayjp利用したサーバーサイドの実装までを紹介していきます。

実装する機能

  • Checkoutを利用した購入機能(この段階でのみCheckout利用)
  • payjp.jsを利用した実装
    • CC登録機能
    • 登録したCCをユーザー情報と紐づけて表示させる
    • CC削除機能
    • 購入機能(再実装)
  • ユーザー新規登録登録画面でもCC登録ができるようにする(+α)

バージョン情報や前提条件

  • ruby 2.5.1
  • payjp 5.2.3
  • 前提条件
    • hamlで記述
    • sassで記述
    • deviseを導入しており、ログイン機能が実装されている

実装

DB設計やルーティングの違いにより、実装する上で記述内容に差異が生じるかと思います。
適宜考えて実装していただけると幸いです。

payjp gemをインストール

Gemfileに以下を記述して、bundle installします。

gem 'payjp'

payjpのサイトにアカウント登録

payjpのサイトでアカウントを登録します。
https://pay.jp/

APIキーを確認

登録が完了したら、payjpの管理画面でAPIキーを確認します。
今回確認するのは、テスト用の公開鍵(pk~)と秘密鍵(sk~)です。
こちらは後ほど使用するので、画面を残しておきましょう。
Image from Gyazo

購入機能の実装(checkout利用)

続いて購入機能の実装です。
購入機能自体はpayjpで用意されているライブラリ 「Checkout」 を利用すれば、簡単に実装できます。

1.ビューファイル編集

購入確認画面のビューファイル内の 「購入する」 の記述を、以下の記述と置き換えます。

app/views/transacts/buy.html.haml
= form_with "パスを指定" do
  :plain
  %script{type: "text/javascript", src: "https://checkout.pay.jp", class:"payjp-button", "data-text": "購入する", "data-key": "公開鍵(pk_~)"}

これだけで購入に関するフォームの記述は終了です。
置き換えた後に表示された「購入する」をクリックすると、CC登録フォームがモーダルで表示されます。
Image from Gyazo
使用するカード情報
カード: 4242424242424242(Visaのテストカード)
有効期限: 現在より未来の期日
CVC番号: 3~4桁の任意の数字
名前: 任意の名前

使用するカードはpayjpよりテストカードが用意されているので、そちらを使用しましょう。
https://pay.jp/docs/testcard

2.コントローラ編集

app/controllers/transacts_controller.rb
require 'payjp'

  def pay
    Payjp.api_key = "秘密鍵(sk_~)"
    Payjp::Charge.create(
      amount: 1100, # 決済する値段
      card: params['payjp-token'], # フォームを送信すると生成されるトークン
      currency: 'jpy'
    )
  end

後は、ルーティングを設定すれば、購入できるようになります。
実際に購入できているのが確認できます。
Image from Gyazo

再実装【payjp.jsを利用】

checkoutを利用することで、簡単に購入機能が実装できました。
しかし、現在の実装では実際の運用は難しいので、実際の運用を想定して実装し直します。

実装条件

  • ログインしているユーザーに紐づけてカード情報を登録する、削除もできる
  • ユーザーは登録したCCを使用して、商品を購入できる
  • 独自でCC登録フォームを作成する

1.テーブルの作成

マイグレーションファイルを作成し、テーブルの作成とカラムの紐付けを行います。
userは外部キーなので、references型で外部キー制約を指定しています。

db/migrate/2019**********_create_cards.rb
class CreateCards < ActiveRecord::Migration[5.2]
  def change
    create_table :cards do |t|
      t.references  :user,           null: false,    foreign_key: true
      t.string      :customer_id,    null: false
      t.string      :card_id,        null: false
    end
  end
end

記述したら、rake db:migare を行います。
なお、DBに顧客情報やカード情報そのものを保存することは禁止されているのでご注意ください。http://payjp-announce.hatenablog.com/entry/2017/11/10/182738

モデルの紐付け

モデルファイルを作成し、編集します。
以下、カードに関する紐付けの記述のみ載せています。

app/models/card.rb
class Card < ApplicationRecord
  belongs_to :user
end
app/models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :cards # 追記する
end

2.クレジットカード登録フォームのマークアップ

続いて登録フォームを作成します。
今回は、某フリマアプリに寄せてています。
一つのファイルに以下の内容も記述すると、記述量が膨大になるため、部分テンプレートを採用しています。

/_card_registration.html.haml
.credit-update
  .credit-update__label
    クレジットカード情報入力
  .card-form
    .card-form__box
      = form_tag(cards_path, method: :post, id: 'charge-form', name: "inputForm") do |f|
        .card-form__box__number
          %label{class:'box-group--label', for: 'card_number'} カード番号
          %span.input-require
            必須
          = text_field_tag "number", "", class: 'card-number--input', type: "text", id: 'card_number', maxlength: "16", placeholder: "半角数字のみ"
          .registration-error{type: "hidden", value: "必須項目です"}
          %ul.card-list
            -# assets/imagesにimageを設置しており、それをimage_tagで呼び出しています。
            %li.card-list--item{ style: "margin-left: 0;"}
              = image_tag "visa.svg", width:"49", height:"20"
            %li.card-list--item
              = image_tag "master-card.svg", width:"34", height:"20"
            %li.card-list--item  
              = image_tag "saison-card.svg", width:"30", height:"20"
            %li.card-list--item  
              = image_tag "jcb.svg", width:"32", height:"20"
            %li.card-list--item
              = image_tag "american_express.svg", width:"21", height:"20"
            %li.card-list--item
              = image_tag "dinersclub.svg", width:"32", height:"20"
            %li.card-list--item 
              = image_tag "discover.svg", width:"32", height:"20"

        .card-form__box__expire
          %label.box-group--label 有効期限
          %span.input-require
            必須
          .card-expire
            .card-expire__select-month
              %select#exp_month{name: "exp_month", type: "text"}
                %option{value: "1"}01
                %option{value: "2"}02
                %option{value: "3"}03
                %option{value: "4"}04
                %option{value: "5"}05
                %option{value: "6"}06
                %option{value: "7"}07
                %option{value: "8"}08
                %option{value: "9"}09
                %option{value: "10"}10
                %option{value: "11"}11
                %option{value: "12"}12
              %i.card-form-expire-icon
                = image_tag "arrow-bottom.png", size:"16x10",class:"arrow-bottom-icon5"
              %span{class: "month"}.card-expire__select-year
              %select#exp_year{name: "exp_year", type: "text"}
                %option{value: "2019"}19
                %option{value: "2020"}20
                %option{value: "2021"}21
                %option{value: "2022"}22
                %option{value: "2023"}23
                %option{value: "2024"}24
                %option{value: "2025"}25
                %option{value: "2026"}26
                %option{value: "2027"}27
                %option{value: "2028"}28
                %option{value: "2029"}29
              %i.card-form-expire-icon
                = image_tag "arrow-bottom.png", size: "16x10",class:"arrow-bottom-icon6"
              %span{class:"year"}.card-form__box__security-code
          %label.box-group--label{for: "cvc"} セキュリティーコード
          %span.input-require
            必須
          = text_field_tag "cvc", "", class: 'payment__security-code', type: "text", id: "cvc", maxlength: "4" ,placeholder: "カード背面4桁もしくは3桁の番号"
          .question-form
            %span.question-form__mark ?
            %span.question-form__text 
              カード裏面の番号とは?
        #card_token
        = submit_tag "追加する", class: "card-form__box__add", id: "token_submit", type: 'button'

こちらにcssを当ててあげると以下のようになります。
Image from Gyazo

3.payjp.jsを編集

payjp.jsファイルを作成し、編集します。
checkoutを利用した場合、checkoutが簡単にトークンを作成してくれていましたが、それを利用しないのでpayjp.jsでトークンを生成する処理を記述する必要があります。

app/assets/javascripts/payjp.js
$(function(){

  var submit = document.getElementById("token_submit");

  submit.addEventListener('click', function(e){  // 追加するボタンが押されたらイベント発火
    e.preventDefault();  // ボタンを一旦無効化
    Payjp.setPublicKey("秘密鍵(pk_~)");
    var card = {  // 入力されたカード情報を取得
      number: document.getElementById("card_number").value,
      exp_month: document.getElementById("exp_month").value,
      exp_year: document.getElementById("exp_year").value,
      cvc: document.getElementById("cvc").value
    };
    if (card.number == "", card.exp_month == "1", card.exp_year == "2019", card.cvc == "") {
      alert("カード情報が入力されていません。"); // 送られた値がデフォルト値だった場合
    } else { // デフォルト値以外の値が送られてきた場合
      Payjp.createToken(card, function(status, response) {  // トークンを生成
        if (status === 200) {
          $("#card_number").removeAttr("name");
          $("#exp_month").removeAttr("name");
          $("#exp_year").removeAttr("name"); 
          $("#cvc").removeAttr("name");
          $("#card_token").append(
            $('<input type="hidden" name="payjp-token">').val(response.id)
          ); 
          document.inputForm.submit();  // 生成したトークンを送信する準備を整える
          alert("登録が完了しました");
        } else {
          alert("正しいカード情報を入力してください。");
        }
      });
    }
    false
  });
});

4.payjp.jsを読み込めるようにする

application.haml.hamlに以下の内容を追記します。
%script{src: "https://js.pay.jp/", type: "text/javascript"}
%script{type: "text/javascript"} Payjp.setPublicKey('公開鍵(pk_~)');

app/views/layouts/application.html.haml
%html
  %head
    %meta{content: "text/html; charset=UTF-8", http: { equiv: "Content-Type" }}
    %title FreemarketSample59a
    = csrf_meta_tags
    = csp_meta_tag
    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload'
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
    %script{src: "https://js.pay.jp/", type: "text/javascript"}
    %script{type: "text/javascript"} Payjp.setPublicKey('公開鍵(pk_~)');
  %body
    = yield

5.コントローラを編集

続いてコントローラを編集します。
先ほどはtransacts_controller(商品取引に関する)を編集しましたが、今回は新たにcards_controller(カードに関する)を作成し、それを編集します。

app/controllers/cards_controller.rb
class CardsController < ApplicationController
  require 'payjp'
  before_action :set_card

  # 後ほど登録したクレジットの表示画面を作成します。
  def index
  end

  # クレジットカード情報入力画面
  def new
    if @card
      redirect_to card_path unless @card
    else
      render 'mypages/create_card'
    end
  end

  # 登録画面で入力した情報をDBに保存
  def create
    Payjp.api_key = "秘密鍵(sk_~)"
    if params['payjp-token'].blank?
      render 'mypages/create_card'
    else
      customer = Payjp::Customer.create( # ここで先ほど生成したトークンを顧客情報と紐付け、PAY.JP管理サイトに送信
        email: current_user.email,
        card: params['payjp-token'],
        metadata: {user_id: current_user.id} # 記述しなくても大丈夫です
      )
      @card = Card.new(user_id: current_user.id, customer_id: customer.id, card_id: customer.default_card)
      if @card.save
        redirect_to cards_path
      else
        render 'mypages/create_card'
      end
    end
  end

  # 後ほど削除機能を実装します。
  def destroy
  end

  private

  def set_card
    @card = Card.where(user_id: current_user.id).first if Card.where(user_id: current_user.id).present?
  end
end

秘密鍵を必ず設置するようにしましょう。

6.ルーティングを設定

ルーティングには今後実装するindex、destroyのアクションも記述します。

config/routes.rb
resources :cards , only: [:new, :index, :create, :destroy]

これでユーザーがクレジットカードを登録できるようになりました。

最後に

本記事の紹介は、一旦ここまでの実装で終わります。
また後日続きの実装を載せたいと考えているので、本記事を通して少しでも読者様の参考になれば幸いです。
また、学習期間3ヶ月の若輩者ですので、記事に多々不備があるかと思います。
ご意見やご質問がありましたらお気軽にご連絡ください。

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

Railsのmemberとcollectionについての備忘録

routes.rbの設定で使うmember,collectionの違い

Railsのルーターは受け取ったURLをコントローラーに割り当てる役割を持っている。
ルーターを設定する時に、member,collectionという機能について調べたため、備忘録として残す。

memberの役割

memberは、特定のモデル(idで判別)に対するアクションを設定する時に使う。

ex)

routes.rb
resources :users do
  member do
      get 'like'
    end
  end
ルーターの設定
like_users GET  /users/:id/like(.:format)  users#like

collectionの役割

collectionは、全てのモデルに対するアクションを設定する時に使う。

ex)

routes.rb
resources :users do
  member do
      get 'search'
    end
  end
ルーターの設定
search_users GET  /users/search(.:format)  users#search

まとめ

RESTfulな7つのルーティング以外を設定したい場合、
特定のモデルに対してのアクション
member
全てのモデルのアクション
collection
で設定する。

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

CircleCIでbundler2のインストール時に実行ファイルの削除でYnの入力を求められるやつの対策

CircleCIでbundler2系を使いたいので以下のコマンドを実行するようにしていたが

     - run:
          name: setup bundler 2
          command: | 
              sudo gem uninstall bundler
              sudo rm /usr/local/bin/bundle
              sudo rm /usr/local/bin/bundler
              sudo gem update --system
              sudo gem install bundler

以下のログを出してタイムアウトすることがあった

Remove executables:
    bundler

in addition to the gem? [Yn]  Remove executables:
    bundler

in addition to the gem? [Yn]  Remove executables:
    bundler

in addition to the gem? [Yn]  context canceled

要はユーザーからの入力を待ってタイムアウトしている。

対策

こうすればよかった

      - run:
          name: setup bundler 2
          command: |
              sudo gem update --system
              sudo gem uninstall -ax bundler
              sudo gem install bundle

gem uninstallはaオプション(バージョン問わず該当するものを全て削除)とxオプション(インタラクティブに実行形式ファイルの削除を問わずアンインストールを続行する)を取れるので、それを指定すればよかった。rmコマンドとかいらない。

yesコマンドを使ってもいいのでは?と思ったがなんか動かなかった。理由は調べてないのでわからん。

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

最近学習したrailsコマンド(Progateにて)

Progateにて最近学習した内容をアウトプッティングしてみた

1.アプリ作成

rails new アプリ名_app
例) rails new tweeeet_app

2.機能追加(コントローラー作成)

rails g controller コントローラー名 アクション名
例) rails g controller image index

ホームページ作成
例) rails g controller home top

3. データベースの追加(テーブル作成)

rails g model テーブル名の単数形 カラム名:データ型
例) rails g model User name:string
  ※複数カラムを作成したい場合⬇︎
rails g model テーブル名の単数形 カラム名:データ型 カラム名:データ型•••
例) rails g model User name:string number: integer
rails db:migrate
例) rails db:migrate

4. データベースに情報追加(カラム追加)

rails g migration データ名(add_カラム名_to _追加先のテーブル名)
例) rails g migration add_nickname_to_users
add_column :テーブル名, :カラム名, :データ型 (migrationファイルの”データ名”の中に記入)
例) add_column :users, :name, :string
rails d:b migrate
例) rails db:migrate

〜追記〜

  • データ型は知る限り3種類あり

    • string : 文字列(短い)
    • text : 複数行にわたる場合
    • integer : 数値
  • データの値の変換

    • .to_s ••• 文字列へ
    • .to_i ••• 数値へ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyで英語翻訳ごっこ!:正規表現で語順の違いを解決してみた

gsubメソッドで翻訳ごっこして遊んでみた

Rubyという言語には、文字列をお手軽に変換する「gsub」というメソッドがあります。

fruits.rb
input = "apple"
input.gsub("apple","banana")   #=>"banana"

こんな感じで"apple"を"banana"に変えられます。
gsubメソッドを知ったとき、「これで翻訳できるんとちゃう…?」と思ったのでやってみました。
"I love you" を日本語に翻訳するコードを書いてみます。

love.rb
input = "I love you"
input_1 = input.gsub("I","私は")               #=>"私は love you"
input_2 = input_1.gsub("love","愛してる")    #=>"私は 愛してる you"
input_3 = input_2.gsub("you","あなたを")    #=>"私は 愛してる あなたを"

"I love you" ⇒ 「私は 愛してる あなたを」
語順に違和感がありますが、すべての英語を日本語に変えることができました。
ちなみに、もっとコードを短くしたい場合は

love.rb
input = "I love you"
input.gsub(/I|love|you/, "I"=>"私は","love"=>"愛してる","you"=>"あなたを")
#=>"私は 愛してる あなたを"

と書くこともできます。

さて、ここからが本題です。
違和感があるのは、日本語と英語では語順が違うため。
"I love you"で問題になるのは"love"と"you"の順番が逆
という点です。
汎用性も高めたいですね。"I tell you"や"I beg you"にも同じように対応したい。
つまり、

「何らかの単語」(空白)"you" ⇒ "you" (空白)「何らかの単語」

に置き換えたい。

そこで、
正規表現.png

youの前にある単語を、youの後ろにもってくる」コードを書いてみることにしました。

正規表現のキャプチャを使って語順を入れ替える

結論からいくと、こんなコードで出来ました。(実行環境はirb)

love.rb(追加部分)
input = "I love you"

if input =~ /([A-Za-z]+\s)you/
input.gsub(($1)+"you" , "you\s"+($1))
end                                     

#=>"I you love"

正規表現っていきなり見ると少しびっくりする方もいると思うので(まさに私がそう)、ゆっくり説明しようと思います。

まずinputの文字列のなかから、「何らかの単語があって、そのあとに空白があって、そのあとにyouと書いてある」部分を探します。
そういう部分のことを正規表現で表すと
 /([A-Za-z]+\s)you/ になるのです。
正規表現.png

そして、「()」で括られた部分、([A-Za-z]+\s) は今後、($1)という名前でコード上で取り扱えるようになりました。

さて、if input =~ /([A-Za-z]+\s)you/ という処理によって、"I love you"のなかの"love you"というところが抜き出され、"love(空白)" の部分が($1)として扱えるようになりました。
正規表現.png

あとは、"($1)+you" をgsubメソッドで変換して、 "you+ \s (空白) +($1)" に変形しました。

このコードで love you を you loveに。

love.rb(追加部分)
input.gsub(($1)+"you" , "you\s"+($1))

空白を入れないと "youlove" になってしまい、あとで日本語に変換できなくなってしまいます。空白は"\s"で表現できます。

引っかかった点としては、($1)を「""」で囲むと出力がうまくいかないということです。なので「+」を使って文字列を結合させています。

それでは、「"I love you"を正しい日本語の語順で翻訳する」作業をしてみます。

love.rb(追加部分)
input = "I love you"

if input =~ /([A-Za-z]+\s)you/
input =  input.gsub(($1)+"you" , "you\s"+($1))
end
#=> "I you love "

input.gsub(/I|love|you/, "I"=>"私は","love"=>"愛してる","you"=>"あなたを")
#=> "私は あなたを 愛してる "

語順が正しくなりました!

私は正規表現がとても苦手なのですが、こういう風に遊んでみた結果ちょっとだけ親しめたよ、という話でした。
正規表現ってテキストやってるだけではとっつきにくいことこの上ないので、手を動かしてみると「意外と簡単じゃん」と思えるかもしれません。

最後まで読んでくださった方、ありがとうございました。
間違っているところがあればご指摘ください。

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

法人番号APIをたたいて、RailsのDBに保存する(バルクインサートではないが。。。)

APIを叩く

法人番号API:
https://www.houjin-bangou.nta.go.jp/webapi/kyuusiyousyo.html

上記サイトに記載されているが、利用にあたってアプリケーションIDの発行を申請する必要がある。
詳しくは、HPを。。。

https://api.houjin-bangou.nta.go.jp/<バージョン番号>/diff?id=<アプリケーションIDを入れる>&from=2019-04-03&to=2019-04-03&type=12

のように期間指定してあげて、URLを送ると

<updateDate>2019-04-03</updateDate>
<changeDate>2015-10-05</changeDate>
<name>法人番号株式会社</name>
<nameImageId/>
<kind>30124</kind>
<prefectureName>東京都</prefectureName>
<cityName>千代田区</cityName>
<streetNumber>一番町99番地111</streetNumber>
<prefectureCode>13</prefectureCode>
<cityCode>1010</cityCode>
<postCode>101010101</postCode>
<successorCorporateNumber/>
<changeCause/>
<assignmentDate>2015-10-05</assignmentDate>
<furigana>ほうじんばんごう</furigana>

---中略---

<updateDate>2019-04-03</updateDate>
<changeDate>2017-10-19</changeDate>
<name>HHH HOUJIN株式会社</name>
<nameImageId/>
<kind>301124</kind>
<prefectureName>東京都</prefectureName>
<cityName>千代田区</cityName>
<streetNumber>大手町123丁目11番2321号大手町セカンドサーキュレートタワー4階</streetNumber>
<addressImageId/>
<prefectureCode>13</prefectureCode>
<cityCode>1014</cityCode>
<postCode>124124124</postCode>
<assignmentDate>2017-02-28</assignmentDate>
<furigana>トリプルエイチ</furigana>

※数字は適当、&だいぶ省略してます。
のように返ってくるのでこれをJSON形式に変換して、DBにブッコム。

データ保存 & 更新

コントローラーにメソッドを作って、viewから呼び出す。
コードはリファクタリングしてないので、いらないところとか、汚いところありますがご愛嬌で

corporation_number_controller.rb
def index
    # viewで"update_corp_num"とういparamsを送ってあげて、
    # もしparamsの値がそれならupdate_corporate_numberメソッドを呼び出す
    if params[:update_corp_num]
      update_corporate_number(params[:q])
      return
    end

    @q = CorporateNumber.all.order(corporate_number: :desc)
    -- 中略 --
  end
corporation_numbers_controller.rb
def update_corporate_number(params_q)
    CorporateNumber.delete_all

# どの期間で法人番号情報を取ってくるかを指定
# 日付指定が2桁で指定仕上げる必要があったので'%02' % ~ を入れている
    today = Date.today.months_ago(1)
    date = today.year.to_s + "-" + ('%02d' % today.month.to_s) + "-" + ('%02d' % today.day.to_s)
    from_date_i = from_date.year.to_s + "-" + ('%02d' % today.month.to_s) + "-" + ('%02d' % today.day.to_s)


# APIをたたいて情報を取得
# APIを叩く = http://...形式でリクエストを送って「情報くれ」ってAPIサーバーに伝えること
# (リクエストURIをchromeのURLの場所に貼り付け&Enterでリスポンスみれます)
# 今回貼り付けたのは完璧なURIじゃないので見れませんが。。。

    uri = URI.parse("https://api.houjin-bangou.nta.go.jp/<バージョン番号>/diff?id=<アプリケーションID>&from=#{date}&to=#{date}&type=12")
    resp = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|

    # よくわからないが、下記二行はつけないとダメらしい(詳しくわかる方教えてください)
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      http.get(uri)
    end
    # jsonの値がresultに入っている
    result = ActiveSupport::XmlMini.parse(resp.body)
    ary = []
    result["corporations"]["corporation"].each do |item|

      cnum_company_name = item["name"].values.join
      cnum_company_prefecture = item["prefectureName"].values.join
      cnum_company_city = item["cityName"].values.join
      cnum_company_street = item["streetNumber"].values.join
      cnum_zipcode = item["postCode"].values.join.to_i
      cnum_corporate_num = item["corporateNumber"].values.join.to_i

# 値がnilで入れられませんってエラーが出たので入れている
# 今思えば、compactでやったほうがよかったのかも??。。。
      next if cnum_company_name.nil?
      next if cnum_company_prefecture.nil?
      next if cnum_zipcode.nil?
      next if cnum_corporate_num.nil?

# 上で作った配列にそれぞれ入れていく      
      ary << CorporateNumber.new(
        corporate_number: cnum_corporate_num,
        company_name_jp: cnum_company_name,
        company_address_jp: cnum_company_prefecture + " " + cnum_company_city + " " + cnum_company_street,
        company_name_jp_kana: "ふりがな",
        insert_user_id: 1,
        update_user_id: 1,
        update_date: Time.now,
        insert_date: Time.now
        )
      end
      CorporateNumber.import hoge
      redirect_to action: 'index'
  end

今回のでは、一応データ更新はできたが、更新時間が長すぎてtimeoutでアプリケーションが見れなくなってしまう。
なので、リファクタリングで処理時間を短くする必要がある。例えば、今回のはSQL文が一回ごとに発行されてしまうので、バルクインサートを使って一括で入れてあげるとか、each_slice(1000)&sleep(1)を使って1000件づつ入れてあげるとか。

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

Ruby on Railsの基本的な仕組みってどうなってるの?(ルーティング、コントローラー、ビュー)

?? Ruby on Railsの基本的な仕組みってどうなってるの ??

◯ Ruby on Railsのやり始めるとき、大抵の人がビビる! ◯

Ruby on Railsを始めようとするとき、まずはインストールしてみると・・・ビビりました? ビビりますよね!?
スクリーンショット 2019-10-20 14.54.47.png

私はもともとビビりなので、めちゃくちゃビビりました・・・

たくさんのフォルダ(ディレクトリ)が並んでいて何がなんのか全くわかりませんよね。

そこで、とにかく最初に紐解くべき事(1)が今回のテーマです。


Ruby on Railsをやっている方でしたら、Rubyはもう学習されているはずです。

1、コードを書く

2、ブラウザに結果を表示させる

HTMLやRubyでは、コードを書いたら即、表示を作ることができましたが、
Ruby on Railsでは、この基本的な作業を実行するためには、次の「3つ」を理解する必要があります。

1、 ルーティング   (routes)
2、 コントローラー   (controller)
3、 ビュー      (view)   →  HTML,Rubyで学んできた部分

1、 まず、ブラウザを開く際には、そのページのアドレスが必要ですね。
  「ルーティング」ではそのアドレスを指定します。

2、 次に、1で指定したページのバックグラウンドで作業や設定などの複雑な計算をする部分(アクションと言います)を
  「コントローラー」で指定します。

3、 最後に、「ビュー」のファイルにHTMLやRubyで実際に表示されるコードを書きます。

ということで、ざっくり手間が「1」と「2」の二つ分増えるわけです。

Ruby on Rails は「ルーティング」や「コントローラー」、「ビュー」などのフォルダ(ディレクトリ)が
それぞれ繋がっていて、それぞれが反映されるようになっているので、離れているフォルダ(ディレクトリ)同士でも
勝手に連携してくれます。

スクリーンショット 2019-10-20 14.50.21.png

どこに何があるのか最初はわかりずらいですが、まずはこの三つの場所と上記のような関係を頭に入れましょう!

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

Ruby on Railsの基本、基礎的な仕組みが分からない!(ルーティング、コントローラー、ビュー編)

?? Ruby on Railsの基本的な仕組みってどうなってるの ??

◯ Ruby on Railsのやり始めるとき、大抵の人がビビる! ◯

Ruby on Railsを始めようとするとき、まずはインストールしてみると・・・ビビりました? ビビりますよね!?
スクリーンショット 2019-10-20 14.54.47.png

私はもともとビビりなので、めちゃくちゃビビりました・・・

たくさんのフォルダ(ディレクトリ)が並んでいて何がなんのか全くわかりませんよね。

そこで、とにかく最初に紐解くべき事(1)が今回のテーマです。


Ruby on Railsをやっている方でしたら、Rubyはもう学習されているはずです。

1、コードを書く

2、ブラウザに結果を表示させる

HTMLやRubyでは、コードを書いたら即、表示を作ることができましたが、
Ruby on Railsでは、この基本的な作業を実行するためには、次の「3つ」を理解する必要があります。

1、 ルーティング   (routes)
2、 コントローラー   (controller)
3、 ビュー      (view)   →  HTML,Rubyで学んできた部分

1、 まず、ブラウザを開く際には、そのページのアドレスが必要ですね。
  「ルーティング」はそのアドレスを指定する所です。

2、 次に、1で指定したページのバックグラウンドで作業や設定などの複雑な計算をする部分(アクションと言います)を
  「コントローラー」で指定します。

3、 最後に、「ビュー」のファイルにHTMLやRubyで実際に表示されるコードを書きます。

ということで、ざっくり手間が「1」と「2」の二つ分増えるわけです。
1ページだけのホームページなら、ルーティングやコントローラーは不要かもしれませんが、
複数のページが連携するようになってくると、1と2の作業が必要になってくるので、まずはここで理解に苦しみます。

Ruby on Rails は「ルーティング」や「コントローラー」、「ビュー」などのフォルダ(ディレクトリ)が
それぞれ繋がっていて、それぞれが反映されるようになっているので、離れているフォルダ(ディレクトリ)同士でも勝手に連携してくれます。

スクリーンショット 2019-10-20 14.50.21.png

一つのホームページやシステムを作るのに欠かせない最低限の部分ですので、
どこに何があるのか最初はわかりずらいですが、まずはこの三つの場所と上記のような関係を頭に入れましょう!

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

〜ダレワカ〜Ruby on Railsの基本、基礎的な仕組みが分からない!(ルーティング、コントローラー、ビュー編)

?? Ruby on Railsの基本的な仕組みってどうなってるの ??

◯ Ruby on Railsのやり始めるとき、大抵の人がビビる! ◯

こんにちは。誰でも分かる 〜ダレワカ〜 のコムリンです。

Ruby on Railsを始めようとするとき、まずはインストールしてみると・・・ビビりました? ビビりますよね!?
スクリーンショット 2019-10-20 14.54.47.png

私はもともとビビりなので、めちゃくちゃビビりました・・・

たくさんのフォルダ(ディレクトリ)が並んでいて何がなんのか全くわかりませんよね。

そこで、とにかく最初に紐解くべき事(1)が今回のテーマです。


Ruby on Railsをやっている方でしたら、Rubyはもう学習されているはずです。

1、コードを書く

2、ブラウザに結果を表示させる

HTMLやRubyでは、コードを書いたら即、表示を作ることができましたが、
Ruby on Railsでは、この基本的な作業を実行するためには、次の「3つ」を理解する必要があります。

1、 ルーティング   (routes)
2、 コントローラー   (controller)
3、 ビュー      (view)   →  HTML,Rubyで学んできた部分

1、 まず、ブラウザを開く際には、そのページのアドレスが必要ですね。
  「ルーティング」はそのアドレスを指定する所です。

2、 次に、1で指定したページのバックグラウンドで作業や設定などの複雑な計算をする部分(アクションと言います)を
  「コントローラー」で指定します。

3、 最後に、「ビュー」のファイルにHTMLやRubyで実際に表示されるコードを書きます。

ということで、ざっくり手間が「1」と「2」の二つ分増えるわけです。
1ページだけのホームページなら、ルーティングやコントローラーは不要かもしれませんが、
複数のページが連携するようになってくると、1と2の作業が必要になってくるので、まずはここで理解に苦しみます。

Ruby on Rails は「ルーティング」や「コントローラー」、「ビュー」などのフォルダ(ディレクトリ)が
それぞれ繋がっていて、それぞれが反映されるようになっているので、離れているフォルダ(ディレクトリ)同士でも勝手に連携してくれます。

スクリーンショット 2019-10-20 14.50.21.png

一つのホームページやシステムを作るのに欠かせない最低限の部分ですので、
どこに何があるのか最初はわかりずらいですが、まずはこの三つの場所と上記のような関係を頭に入れましょう!

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

Ruby on Railsにスタイルを反映させる手順

概要

Webアプリのscssファイルを更新しても本番環境に変更が反映されませんでした。
httpdリスタートしたりpassengerをリスタートしたりしま
したが変わらず。。。

解決方法

何が問題なのかと調べた結果、こちらを参考にしました。

環境

ruby 2.3.3
rails 5.1.7
SAKURA VPS
CentOS 6

手順

$ rm -rf public/assets
$ rm -rf tmp/cache
$ bundle exec rake assets:precompile
$ sudo service httpd restart 

1,2行目:実行するプロジェクトのassets,cacheフォルダを削除
3行目:プリコンパイルを行う
4行目:httpdをリスタート

以上の流れで、変更したscssファイルが反映されました。
私としては、アプリで利用するファイルの一部が残っていたので削除してもう一度作り直す、という理解になっています。

何か違う!というのがあれば教えてください!!

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

Railsを5.0から6.0にアップデートしたらコンソールで日本語が使えなくなった問題の解決

コンソールで日本語を入力したらエラーで強制終了されてしまう

Railsプロジェクトのバージョンを5.0→6.0に上げたら、Rails cで立ち上がるコンソールで日本語を入力すると、
"\xE2" from ASCII-8BIT to UTF-8 (Encoding::UndefinedConversionError)
というエラーが発生し、強制終了するようになった。

解決策

Gemfileのdevelopmentグループにあるgem 'rb-readline'を削除し、$ bundleを実行する。
これでエラーが発生しなくなりました。
なぜrb-readlineが問題だったのかわかる方、コメントお待ちしています、、、

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

gnuplotのmacosでaquaterm

gnuplot reinstall <2019-10-20 日>

結論 : 古いgnuplotを入れる.

brewのgnuplotのオプション指定ができなくなったので対処する
に従って.

: brew tap ie-developers/ie
: brew install ie-developers/ie/gnuplot --with-cairo --with-aquaterm

症状 :

  1. rubyでgnuplotがないと怒られた.
  2. gem install numo-gnuplot
  3. gnuplotがlibなしで動かない.
  4. brew install gnuplot --with-aquaterm
    1. したら,--with-aquatermがナイト..
  5. なしでinstallしたら,今度はnumo/gnuplotで
  gnuplot.rb:307:in `run':  (Numo::GnuplotError)                                                                 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Capistranoを使ってRailsプロジェクトをデプロイ

自動化ツールは一度動き出すと便利なんですが、周辺環境の変化に都度追随するのが大変です。いっそherokuとかのPaaSに移ったほうが良いのかもしれませんが、柔軟性の高さが欲しい場合もあるので、もう少し自力で頑張る方向で。

macOSをCatalinaにアップデートしたら、capコマンドが使えなくなっていたので、改めてgemをインストールします。

sudo gem install capistrano-bundler capistrano-rails

プロジェクト内のCapfileでは以下の3行をコメントアウトします。capistrano/passenger は何故か動かない(再起動に失敗する)のでそのまま無効化しています。

require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
# require "capistrano/passenger"

config/deploy.rb は以下のように使っています(repo_treeやkeep_releasesは状況や好みによって変わると思いますが)。Rails5.2系になって一番の変化は master.key です。これは git 管理しないことが推奨されているので、別の経路でサーバに転送しておく必要があります。sharedフォルダが(currentやreleasesと並んで)存在しているので、その中に設置しておいて、linked_filesの中に加えて上げると、各リリースの配備時にこのファイルのリンクを作ってくれます。

前述したようにPassengerの再起動がうまくいかないので、ここでタスクを定義して配備完了後にApacheを再起動するコマンド(apachectl restart)を実行しています。

set :application, "myapp"
set :repo_url, "git@bitbucket.org:myrepo/myapp.git"
set :repo_tree, 'railsapp'
set :keep_releases, 3

append :linked_files, "config/master.key"

namespace :deploy do
  after :publishing, :restart

  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      execute :sudo, 'apachectl restart'
    end
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ajaxを活用した非同期通信について

ajaxを使った非同期通信

10月19日現在、ajaxによる非同期通信を学習しているところですが、なかなか理解が追いつかないので自分なりにJavaScriptの復習をかねて中身を確認していきたいと思います。かなり自分向けに書いていますので、語弊がある部分もあるかもしれません。

今回は、ブログに対して投稿されるコメントが非同期通信にて行われるようにしていきます。

イベントの発火

まずは動作のきっかけ部分を記述します。きっかけはコメントを入力し、投稿ボタンをクリックした時です。

comment.js
$(function(){
  $('#newcomment').on('submit', function(e){
    e.preventDefault();
    var formdata = new FormData(this);
  })
})
  • $(function(){
    jQuery(JavaScript)のコードが読み込まれる際に、まだHTMLが読み込まれていないとエラーが発生します。1行目、$(function(){から書き始めることで、HTMLのページ情報が読み込み完了してからコードが実行されます。
    ちなみにこのfunctionメソッド(書き方あっているのかな・・・)では引数は不要ですが、引数がない場合でも()括弧を書く必要があります。

  • $('#newcomment')
    jQueryでどのボタンがトリガーになるかは、HTMLタブ内のclassもしくはidから探します。
    2行目の$('#newcomment').はHTML内のid(newcomment)を探します。#はidを探す書き方です。
    $('.〜')といったように、.を使うとclass要素から探されます。

  • .on('submit', function(e){
    イベントsubmitが起こった際に以下の関数を実施する、という部分。
    ちなみにfunction(e)のeはイベントの頭文字。
    次の行のe.preventDefaultは実行されるべきイベントをキャンセルさせます。

  • var 〜〜 = 〇〇
    変数〜〜〇〇を代入します。
    Rubyだと頭のvarなんて要りませんでしたが、他のプログラム言語では必要な場合も多いようです。

  • new FormData(this)
    フォームの情報を取得します。引数がthisの場合、イベントが発生した部分、今回はコメント投稿の部分からの取得になります。

コメントの保存

非同期通信の中で、入力したコメントがcreateメソッドで保存されるようにします。

comment.js
$(function(){
  $('#newcomment').on('submit', function(e){
    e.preventDefault();
    var formdata = new FormData(this);
    $.ajax({
      url: $(this).attr('action'),
      type: "POST",
      data: formData,
      dataType: 'json',
      proseccData: false,
      contentType: false
    })
  })
})
  • $.ajax({
    ajaxで非同期通信をする際のオプションを記述します。
    1.type HTTP通信の種類を記述。GETもしくはPOST。
    2.url リクエスト送信先のURLを記述。
    3.data 送信する値を記述。
    4.dataType データの種類を記述。今は基本的にjson
    5.processData
    6.contentType データのファイル形式を指定。基本false

  • attr
    要素が持つ属性から、指定した属性の値を返します。
    今回では投稿ボタンを含むformタグの中からaction要素を探し、その値となるURLを取得するようにします。

以上の記述で、ajaxで扱われるデータは
「json形式でテキストボックス内の文字をPOSTでコメント投稿のアドレスへ送信」
となります。すなわちコメントが投稿される形となります。

返ってくる結果を受け取る

.doneメソッドで非同期通信の結果を受け取ります。

comment.js
    .done(function(data){
      $('.comments').append(〜〜)
      $('.textbox').val('')
    })
    .fail(function(){
      alert('error')
    })

.doneメソッドで、受け取った結果を元に処理を行います。
commentsクラスの後ろに()内の記述を追加します。詳細は省略。
textboxクラスの値を空にします。今回はフォームボックスの中をクリアします。
.failメソッドは、エラーが発生した時に表示させる処理です。
alertメソッドはポップアップを表示させます。

こんな感じなんでしょうか。
まだなんとなく程度の理解ですので、もっと精進していきます。

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

Ajaxを活用した非同期通信について

ajaxを使った非同期通信

10月19日現在、ajaxによる非同期通信を学習しているところですが、なかなか理解が追いつかないので自分なりにJavaScriptの復習をかねて中身を確認していきたいと思います。かなり自分向けに書いていますので、語弊がある部分もあるかもしれません。

今回は、ブログに対して投稿されるコメントが非同期通信にて行われるようにしていきます。

イベントの発火

まずは動作のきっかけ部分を記述します。きっかけはコメントを入力し、投稿ボタンをクリックした時です。

comment.js
$(function(){
  $('#newcomment').on('submit', function(e){
    e.preventDefault();
    var formdata = new FormData(this);
  })
})
  • $(function(){
    jQuery(JavaScript)のコードが読み込まれる際に、まだHTMLが読み込まれていないとエラーが発生します。1行目、$(function(){から書き始めることで、HTMLのページ情報が読み込み完了してからコードが実行されます。
    ちなみにこのfunctionメソッド(書き方あっているのかな・・・)では引数は不要ですが、引数がない場合でも()括弧を書く必要があります。

  • $('#newcomment')
    jQueryでどのボタンがトリガーになるかは、HTMLタブ内のclassもしくはidから探します。
    2行目の$('#newcomment').はHTML内のid(newcomment)を探します。#はidを探す書き方です。
    $('.〜')といったように、.を使うとclass要素から探されます。

  • .on('submit', function(e){
    イベントsubmitが起こった際に以下の関数を実施する、という部分。
    ちなみにfunction(e)のeはイベントの頭文字。
    次の行のe.preventDefaultは実行されるべきイベントをキャンセルさせます。

  • var 〜〜 = 〇〇
    変数〜〜〇〇を代入します。
    Rubyだと頭のvarなんて要りませんでしたが、他のプログラム言語では必要な場合も多いようです。

  • new FormData(this)
    フォームの情報を取得します。引数がthisの場合、イベントが発生した部分、今回はコメント投稿の部分からの取得になります。

コメントの保存

非同期通信の中で、入力したコメントがcreateメソッドで保存されるようにします。

comment.js
$(function(){
  $('#newcomment').on('submit', function(e){
    e.preventDefault();
    var formdata = new FormData(this);
    $.ajax({
      url: $(this).attr('action'),
      type: "POST",
      data: formData,
      dataType: 'json',
      proseccData: false,
      contentType: false
    })
  })
})
  • $.ajax({
    ajaxで非同期通信をする際のオプションを記述します。
    1.type HTTP通信の種類を記述。GETもしくはPOST。
    2.url リクエスト送信先のURLを記述。
    3.data 送信する値を記述。
    4.dataType データの種類を記述。今は基本的にjson
    5.processData
    6.contentType データのファイル形式を指定。基本false

  • attr
    要素が持つ属性から、指定した属性の値を返します。
    今回では投稿ボタンを含むformタグの中からaction要素を探し、その値となるURLを取得するようにします。

以上の記述で、ajaxで扱われるデータは
「json形式でテキストボックス内の文字をPOSTでコメント投稿のアドレスへ送信」
となります。すなわちコメントが投稿される形となります。

返ってくる結果を受け取る

.doneメソッドで非同期通信の結果を受け取ります。

comment.js
    .done(function(data){
      $('.comments').append(〜〜)
      $('.textbox').val('')
    })
    .fail(function(){
      alert('error')
    })

.doneメソッドで、受け取った結果を元に処理を行います。
commentsクラスの後ろに()内の記述を追加します。詳細は省略。
textboxクラスの値を空にします。今回はフォームボックスの中をクリアします。
.failメソッドは、エラーが発生した時に表示させる処理です。
alertメソッドはポップアップを表示させます。

こんな感じなんでしょうか。
まだなんとなく程度の理解ですので、もっと精進していきます。

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

Rubyのシンタックス勉強用(組み込みクラス Fileクラス、Dirクラス)の挙動 [RUBY技術者認定試験...問題45,46,47,48,94,95,96,97,98]

基本説明

  • Fileクラス:ファイル名の変更やコピーや削除、ファイル情報の取得などの機能がある
  • Dirクラス:ディレクトリ情報の取得、ディレクトリ中のファイル一覧、ディレクトリの作成や削除の機能がある

組み込みクラス Fileクラス

File#dirname

File#dirnameは、最後のスラッシュの左側を文字列として返します。文字列にスラッシュがない場合は.を返します。

p File.dirname('/hoge/fuga/test.txt') # => "/hoge/fuga"
p File.dirname('$LIB') # => "."

(RUBY技術者認定試験...問題45)

ファイルの読み書き

File.openのモード | r+ | 読み書き両方 / ファイルの読み書き位置は先頭にセット

rは読み出しだけ

8693cd3f4de29193df3c6aa6832cce92.gif

File.openのモード | w+ | 読み書き両方 / オープン時にファイルがあれば内容を空にする

wは書き込みだけ、あとは同じ

6fb74a2a064eb24b3785a942f35fe396.gif

File.openのモード | a+ | 読み書き両方 / オープン時にファイルがあれば、ファイルの読み書き位置は末尾にセット

aは書き込みだけ、あとは同じ

bee1554676ac80603da8aff99e52dd50.gif

(RUBY技術者認定試験...問題46)

RUBY技術者認定試験...問題47

以下のコードの実行結果は?的な問題があります。

File.open('foo.txt') do |io|
  while ! io.eof?      # ファイルの終端に達した場合、true / そうでない場合 false
    print io.read(1)         # 読み込む
    io.seek(1, IO::SEEK_CUR) # 現在の位置から1文字移動
  end
end

7511a9882fb90527abc2d318cfc32236.gif

# => 結果 acdfhjlnprtvxz

RUBY技術者認定試験...問題95

以下のコードの実行結果は?

file = File.open('foo.txt')
file.seek(2)     # ファイルポインタの位置が先頭から2バイト移動する
print file.gets  # 1行読み込んで表示

b5d5d0dbc763a2bd307134814696aa7b.gif

# => 結果 cd

RUBY技術者認定試験...問題96

96_rb_—_ruby_silver.png

選択肢

- 1. gets
- 2. readline
- 3. readlines
- 4. read

ファイルを1行ずつ読み込むメソッドでお馴染みなのは getsreadline です。両者の違いは、終端に達した時の挙動です。

  • gets:これ以上読む行がない時は nil を返す
  • readline:これ以上読む行がない時は 例外(EOFError) を返す

問題を見ると、レスキューでキャッチしているので、正解は2ですね。

RUBY技術者認定試験...問題97

97_rb_—_ruby_silver.png

以下の結果になるメソッドは?

選択肢

- 1. concat
- 2. add
- 3. join
- 4. create_path

正解は3なのですが、Fileクラスのメソッド join は、パス名の区切り文字(/)を間に入れて文字列を連結します。URLを作成するときに使用します。

組み込みクラス Dirクラス

カレントディレクトリ内の、特定のワイルドカードに一致するファイルの一覧を取得して表示するコードを2パターン書きます。

files = Dir.glob("*.rb")
# files = Dir["*.rb"]

files.each{ |file| p file }
# files = Dir.glob("*.rb")
files = Dir["*.rb"]

files.each{ |file| p file }

7f6be2b811ed340ef6d63de27e4092c1.gif

(RUBY技術者認定試験...問題48)

RUBY技術者認定試験...問題98

この中で、Dirクラスのクラスメソッドでないものは?

- 1. Dir.rmdir
- 2. Dir.basename
- 3. Dir.pwd
- 4. Dir.extname
- 5. Dir.getwd

正解(Dirクラスのクラスメソッドでないもの)は、24です。この2つは、Fileクラスのメソッドです。

filename = '/hoge/fuga/test.txt'
p File.basename(filename)         # => "test.txt"
p File.basename(filename, '.txt') # => "test"
p File.dirname(filename)          # => "/hoge/fuga"
p File.extname(filename)          # => ".txt"
p File.split(filename)            # => ["/hoge/fuga", "test.txt"]

実行結果は以下でサクッと試せます

Online Ruby Editor and IDE - Fast, Powerful, Free - Repl.it

参考

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

本日の学び #4

配列に要素を追加する

ary.push("yey")
ary << "yey"

Enumerable#max, Enumerable#min

最大、最小の要素を返す。

print

改行なしで出力

numにnが含まれるかどうかの判別

num = 916
puts num.to_s.include?("1") #=> true

2つの配列から重複を省いたり、重複を得たり

a = [1,2,3]
b = [2,3,4]

a - b #=> [1,4]
a & b #=> [2,3]

Array#sort

ary = [2,3,6,4,8,9,5,7,1]
p ary.sort #=> [1,2,3,4,5,6,7,8,9]
p ary.sort { |a, b| b <=> a } #=> [9,8,7,6,5,4,3,2,1]

Float#floor

自身と等しいより小さな整数のうち最大のものを返します。

(-1.2).floor #=> -2

caseのメリット

  1. 調べる値を範囲や配列や複数の値を指定できるので柔軟
  2. 複数の条件が分岐するとき見やすい

*Array

変数呼び出し時に引数に*をつけると配列を引数として渡すことができる

お世話になりました

配列に要素を追加する
複数の配列を比較し重複する値を取る
ruby引数処理に使えるテクニック
Array#sort
Float#floor
制御構造 case

感想

paizaの使用する言語を毎回指定して、自動入力されるサンプルコードを消す作業が、非常にめんどくさい。
あと、終わった問題を解いた順に並べてほしい。

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

Ruby on Railsでログイン機能を作ろう2

前回ログイン機能を作った続き。
https://qiita.com/clipbord/items/0a716b797115f1368df1

細かいところを整えたり、アクセス制限かけたりしています。主にリンクメモです。

空白入力時のエラーメッセージの日本語化
https://qiita.com/Ushinji/items/242bfba84df7a5a67d5b

Flashメッセージ
https://pg-happy.jp/rails-flash-message.html

bootstrap導入
https://qiita.com/tqkqt0/items/d9a3f3416c242ba48ba0

text_fieldにclassを書く
https://qiita.com/KeyG/items/10691b0558ca0d0353b3

バリデーションエラー時のレイアウト崩れ修正
https://blazing.hatenablog.com/entry/2018/12/07/093314

ログイン・ログアウト判定
https://qiita.com/tobita0000/items/866de191635e6d74e392

アクセス制限
ログインユーザのみ確認可

application_controller.rb
  before_action :set_current_user

  def set_current_user
    @current_user = User.find_by(id: session[:user_id])
  end

  def authenticate_user
    if @current_user == nil
      flash[:notice] = "ログインが必要です"
      redirect_to(users_login_path)
    end
  end
user_controller.rb
before_action :authenticate_user, only: [:index,:show,:edit,:create,:update,:destroy]

欄外
devise使用。
色々調べてたら、deviseを使ったログイン機能の作成というものが出てきたので、
新しくプロジェクト作ってやってみた。次作る時はこちらをベースにしようと思った。

https://qiita.com/matcham/items/3609e72360491543b1b6

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