- 投稿日:2020-07-11T15:33:39+09:00
TECH CAMP学習 個人アプリ作成③
ユーザー情報編集機能の追加
まずはターミナルにて以下のコードを入力します。
terminal.$ rails g controller users続いてルーティングで下記を入力します。
routes.rbdevise_for :users root "photos#index" resources :users, only: [:edit, :update]resourcesの意味は日本語では資源をいう意味で、usersの中で編集と更新の機能を利用するという解釈でよいと思います。
続いてコントローラーを編集します。
users_controller.rbdef edit end def update if current_user.update(user_params) redirect_to root_path else render :edit end end private def user_params params.require(:user).permit(:name, :email) end①current_userはdeviseのヘルパーメソッドで、ログイン中のユーザー情報を取得できます。
②redirect_toは本来受け取ってるパスとは別のパスへ転送します。上記ではroot_pathに転送するということです。
③上記が失敗した場合、renderはeditを呼び出します。
④privateはプライベートメソッドで、クラス外から呼び出すことのできないメソッドです。
メリットしては
・classの外部から呼び出されたら困るメソッドを隔離
・可読性、classの外部から呼び出されたメソッドを探すときにprivate以下の部分は目を通さなくてよくなる。
⑤user_params以下はストロングパラメーターで、指定したキーを持つパラメーターのみを受け取るようにするものです。
⑥ストロングパラメーターの中でuserを要求(require)して:name,:emailの許可(permit)を得る。詳細を確認するにはupdateの直後にbinding.pryを使用する。
- 投稿日:2020-07-11T15:07:00+09:00
Rails5でECサイトを作る⑦ ~Address、Genreモデル編~
はじめに
架空のベーカリーで買い物できるECサイトを作るシリーズ、Rails5でECサイトを作る⑥の続きです。
今回はアプリ本体の実装に戻り、購入したパンの送り先住所を管理するAddressモデルと、商品をまとめるGenreモデルの周辺を作っていきます。ソースコード
https://github.com/Sn16799/bakeryFUMIZUKI
Modelのアソシエーション
Address
controller
app/controllers/addresses_controller.rbclass AddressesController < ApplicationController before_action :authenticate_customer! before_action :set_customer def edit @address = Address.find(params[:id]) if @address.customer_id != current_customer.id redirect_back(fallback_location: root_path) flash[:danger] = 'お探しのページにアクセスできません。' end end def index @address = Address.new @addresses = @customer.addresses end def create @address = @customer.addresses.build(address_params) if @address.save flash[:success] = '新しい住所を登録しました!' redirect_to addresses_path else @addresses = @customer.addresses flash[:danger] = '入力内容をご確認ください。各入力欄は2文字以上で記入されていますか?' render :index end end def update @address = Address.find(params[:id]) if @address.update(address_params) flash[:success] = '住所情報を更新しました!' redirect_to addresses_path else flash[:danger] = '入力内容をご確認ください。各入力欄は2文字以上で記入されていますか?' render :edit end end def destroy @address = Address.find(params[:id]) @address.destroy flash[:info] = '登録した住所を削除しました。' redirect_to addresses_path end private def set_customer @customer = current_customer end def address_params params.require(:address).permit(:post_code, :address, :addressee) end endindex画面
app/views/addresses/index.html.erb<div class="col-lg-10 offset-lg-1 offset-1 space"> <div class="container-fluid"> <h2> <span style="display: inline-block;">配送先</span> <span style="display: inline-block;">登録/一覧</span> </h2> </div> <!-- 新規住所フォーム --> <div class="container space"> <h3>新しく住所を登録</h3> <%= form_with(model: @address, local: true, class: 'container-fluid') do |f| %> <div class="form-group"> <%= f.label :郵便番号(ハイフンなし) %> <%= f.text_field :post_code, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :住所 %> <%= f.text_field :address, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :宛名 %> <%= f.text_field :addressee, class: 'form-control' %> </div> <div class="action w-25 offset-lg-11"> <%= f.submit '登録する', class: 'btn btn-danger' %> </div> <% end %> </div> <!-- 住所一覧 --> <div class="container space"> <h3>住所一覧</h3> <div class="row"> <div class="col-lg-3"> <strong>郵便番号</strong> </div> <div class="col-lg-3"> <strong>住所</strong> </div> <div class="col-lg-3"> <strong>宛名</strong> </div> </div> <% @addresses.each do |address| %> <div class="row"> <div class="col-lg-3"> <%= address.post_code %> </div> <div class="col-lg-3"> <%= address.address %> </div> <div class="col-lg-3"> <%= address.addressee %> </div> <div class="col-lg-3"> <%= link_to "編集する", edit_address_path(address), class:"btn btn-danger" %> <%= link_to "削除する", address_path(address), method: :delete, class:"btn btn-danger" %> </div> </div> <% end %> </div> </div>edit画面
app/views/addresses/edit.html.erb<div class="col-lg-10 offset-lg-1 space"> <div class="container"> <h2>配送先編集</h2> <%= form_with(model: @address, local: true) do |f| %> <div class="form-group"> <%= f.label :郵便番号(ハイフンなし) %> <%= f.text_field :post_code, autofocus: true, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :住所 %> <%= f.text_field :address, class: 'form-control' %> </div> <div class="form-group"> <%= f.label :宛名 %> <%= f.text_field :addressee, class: 'form-control' %> </div> <div class="action w-25 offset-lg-11"> <%= f.submit '更新する', class: 'btn btn-danger' %> </div> <% end %> </div> </div>Genre
controller
app/controllers/genres_controoler.rbclass GenresController < ApplicationController def show @genre = Genre.find(params[:id]) @genres = Genre.where(validity: true) @products = @genre.products.page(params[:page]).per(9) end private def genre_params params.require(:genre).permit(:name,:id) end endindex
ファイル名はindexとしていますが、商品ジャンルの一覧画面というものはなく、いくつかのページで部分テンプレートとしてこのファイルを呼び出し、ジャンルごとの絞り込み表示をできるようにします。実質的にはサイドバーです。
app/views/genres/_index.html.erb<div id="sidebar" class="col-lg-2"> <table class='table'> <thead> <tr> <th>ジャンル検索</th> </tr> </thead> <tbody> <% genres.each do |genre| %> <tr> <td><%= link_to genre.name, genre_path(genre) %></td> </tr> <% end %> <tr> <td><%= link_to "⇒ すべての商品を見る", products_path, class: 'dark-blue-letter' %></td> </tr> </tbody> </table> </div>show画面
商品ジャンルの詳細画面は、例えば「食パン」「総菜パン」といったジャンルに属した商品を絞り込み表示する画面にします。
app/views/genres/show.html.erb<div class="col-lg-10 space"> <div class="container"> <h2><%= @genre.name %>一覧(全<%= @genre.products.count %>種)</h2> </div> <div class="container"> <% @genre.products.each do |gp| %> <%= render 'products/box', product: gp %> <% end %> </div> </div> <%= render 'genres/index', genres: @genres %>※部分テンプレート
app/views/products/_box.html.erb<div class="col-lg-4 product-box space"> <%= link_to product_path(product) do %> <div class="row"> <h4><%= product.name %></h4> </div> <div class="row"> <%= attachment_image_tag(product, :image, :fill, 220, 180, fallback: 'no_img.jpg') %> </div> <% end %> <div class="row"> <h4><%= price_include_tax(product.price) %></h4> </div> </div>データベース上は税抜価格しか保存されていないのですが、画面上の表示では税込価格にしたい。そんな時は、ヘルパーメソッドを利用します。
ヘルパー
app/helpers/application_helper.rbdef price_include_tax(price) price = price * 1.08 "#{price.round}円" end後記
AddressもGenreも、いたって標準的なCRUD機能のみなので、ほとんど迷わずにできたと思います。難しくなるのはこれから……。注文機能のあるOrderモデルは、以前作った時にはかなり苦労させられましたが、レスポンシブ対応の画面にして、より簡潔なコードで再現できるのでしょうか? 次回へ続く!
- 投稿日:2020-07-11T13:18:41+09:00
Ruby on Rails アプリケーション新規作成コマンド
アプリケーション新規作成
アプリケーションを作成するコマンドは
rails new アプリケーション名例:
rails new myapp
オプション指定
アプリケーション新規作成時にオプションを指定することができる。
Rails バージョン指定
バージョンを指定してアプリケーションを作成する場合は
rails バージョン new アプリケーション名例:
rails _5.2.3_ new myapp
データベース指定
データベースを指定してアプリケーションを作成する場合は
rails new アプリケーション名 -d データベースの種類例:
rails new myapp -d mysql
データベースの指定がない場合、データベースはデフォルトのsqliteとなる。
テストを作成しない
テストを作成せずにアプリケーションを作成する場合は
rails new アプリケーション名 -T.gitignoreファイルを作成しない
.gitignoreファイルを作成せずにアプリケーションを作成する場合は
rails new アプリケーション名 -Gその他のオプション
上記以外にも様々なオプションが存在する。
次のコマンドでヘルプを表示し、全てのオプションコマンドとその内容を確認できる。
rails new -h
- 投稿日:2020-07-11T12:59:05+09:00
【AWS SDK】EC2自動構築用スクリプト
目標
AWS SDK(AWS SDK for Ruby)を利用してEC2自動構築を行う。
はじめに
AWS SDKの基礎知識をまとめた後、実際の動作をAWS SDK for Rubyを利用したEC2自動構築で試してみます。
AWS SDK
AWSが提供しているAPIの一種で、AWSがサポートする様々なプログラミング言語にライブラリとしてインポートして利用します。
現在以下の言語がサポートされており、各種プログラムからのAWSリソース操作を可能にします。・C++
・Go
・Java
・JavaScript
・.NET
・Node.js
・PHP
・Python
・Ruby(本記事ではこれを利用)AWS CLI、SDK利用時の認証
AWS CLI(OSコマンドライン上やシェルスクリプト、PowerShell上で利用)、AWS SDK(各種プログラミング言語上で利用)を利用する際には認証情報が必要となります。
認証情報の設定方法には以下3つの方法が存在しており、参照優先度が異なります。
項番 優先度 認証情報の設定箇所 1 高 OS環境変数 2 中 認証情報ファイル 3 低 インスタンスプロファイル(IAMロール認証) ①OS環境変数
環境変数のAWS_ACCESS_KEY_ID および AWS_SECRET_ACCESS_KEYという環境変数を利用して認証情報の設定が可能です。
認証設定方法の中で最も優先度が高いです。
例えば以下のように設定を行います。# For LINUX export AWS_ACCESS_KEY_ID=your_access_key_id export AWS_SECRET_ACCESS_KEY=your_secret_access_key # For Windows set AWS_ACCESS_KEY_ID=your_access_key_id set AWS_SECRET_ACCESS_KEY=your_secret_access_key②認証情報ファイル
OSユーザのホームディレクトリ内の「aws」ディレクトリに存在する「credentials」というファイルが認証情報ファイルです。
そのファイル内のaws_access_key_idとaws_secret_access_keyにアクセスキーとシークレットアクセスキーを投入します。# 認証情報ファイル ~/.aws/credentials
[default] aws_access_key_id = your_access_key_id aws_secret_access_key = your_secret_access_key③インスタンスプロファイル
インスタンスプロファイルとは、IAMロールを利用した認証で利用される実行環境ことです。
IAMロール作成時に自動作成されます。
IAMロールを納めるための容器であり、EC2にアタッチする時に必要なコネクターの役割をするようです。
IAMロールを利用するこの認証方法はAWS SDK及びAWS CLI利用時のベストプラクティスであり、アクセスキーとシークレットアクセスキーを利用する上記2つの方法より認証情報漏洩のリスクが軽減されるため推奨されております。
ちなみに、認証方法としての優先度が最も低いため、環境変数や認証情報ファイル内の情報に注意を払う必要があります。インスタンスプロファイルの詳細に関しては、以下サイトが参考になりました。
EC2にIAMRole情報を渡すインスタンスプロファイルを知っていますか?作業の流れ
項番 タイトル 1 AWS SDK for Rubyのセットアップ 2 EC2自動構築用スクリプトのセットアップ 3 動作検証 手順
1.AWS SDK for Rubyのセットアップ
①AWS SDK for Rubyインストール
かなり時間かかるので注意…gem install aws-sdk
②アクセスキーとシークレットアクセスキーの発行
アクセスキーとシークレットアクセスキーが未発行の場合は新規作成する必要があります。
手順は以下を参考
【AWS CLI】Red Hat Enterprise Linux 8でAWS CLIを使用可能にする(3. アクセスキーIDとシークレットアクセスキーの発行)③認証情報ファイルの編集
今回はAWS SDKの認証情報を認証情報ファイル内に設定する形で保存します。
認証情報ファイル~/.aws/credentials
内のaws_access_key_id
とaws_secret_access_key
にアクセスキーとシークレットアクセスキーを記載してください。# 認証情報ファイル編集 vi ~/.aws/credentials
[default] aws_access_key_id = your_access_key_id aws_secret_access_key = your_secret_access_key2.EC2自動構築用スクリプトのセットアップ
①EC2自動構築用スクリプトをAWS SDKをセットアップした環境に配備
スクリプト内の各種環境依存の変数(<ami_id>
、<keypair_name>
、<security_group_id>
、<instance_type>
、<az_name>
、<subnet_id>
、<userdata_pass>
)は環境に従って書き換えをします。また、
<userdata_pass>
に指定したファイルの中にはEC2に設定したいユーザデータ処理を書いておきます。ファイル名: ec2_create.rb# ********************************************************************************** # 機能概要: EC2の自動構築 # スクリプト用法: ruby <スクリプトパス> <インスタンス名> # ********************************************************************************** unless ARGV.size() == 1 puts "The number of arguments is incorrect." exit end require 'aws-sdk' require 'base64' # EC2の構成要素定義(以下変数を環境に応じて編集) image_id = '<ami_id>' # AMIID key_name = '<keypair_name>' # キーペア名 security_group_ids = '<security_group_id>' # セキュリティグループID instance_type = '<instance_type>' # インスタンスタイプ availability_zone = '<az_name>' # 利用AZ subnet_id = '<subnet_id>' # 利用サブネットID user_data = '<userdata_pass>' # ユーザデータのファイルパス # ユーザデータ設定 if File.exist?(user_data) file = File.open(user_data) script = file.read file.close else script = '' end encoded_script = Base64.encode64(script) # EC2リソース操作用インスタンス作成 ec2 = Aws::EC2::Resource.new # EC2作成実施 instance = ec2.create_instances({ image_id: image_id, min_count: 1, max_count: 1, key_name: key_name, security_group_ids: [security_group_ids], user_data: encoded_script, instance_type: instance_type, placement: { availability_zone: availability_zone }, subnet_id: subnet_id }) # インスタンスが利用可能になるまで待機 ec2.client.wait_until(:instance_running, {instance_ids: [instance[0].id]}) # インスタンス名(Nameタグ)を付与 instance_name = ARGV[0] instance.batch_create_tags({ tags: [{ key: 'Name', value: instance_name }]}) puts "#{instance_name}(#{instance[0].id})が作成されました!"3.動作検証
<前提>
以下のパラメータでEC2を作成します。# EC2の構成要素定義(以下変数を環境に応じて編集) image_id = 'ami-067152a7c26866dcb' # AMIID key_name = 'mykeypair' # キーペア名 security_group_ids = 'sg-64a59718' # セキュリティグループID instance_type = 't2.medium' # インスタンスタイプ availability_zone = 'ap-northeast-1a' # 利用AZ subnet_id = 'subnet-41d23b09' # 利用サブネットID user_data = '/tmp/userdata' # ユーザデータのファイルパスまた、EC2に設定予定のユーザデータは以下の内容
$ cat /tmp/userdata #!/bin/bash yum update -y yum install -y httpd systemctl start httpd systemctl enable httpd①スクリプト実行
インスタンス名を第一引数に指定してスクリプト実行します。$ ruby ec2_create.rb test_server test_server(i-0a4fd9cfe1613a8d6)が作成されました!
②結果確認
インスタンス名、インスタンスタイプ、サブネットID、キーペア名、AZ名、AMIIDがスクリプト内での設定値通り設定されています。
一応OSログイン確認と、ユーザデータが正常に実行されているかも確認してみます。
# httpdがユーザデータ実行により起動している $ systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled) Active: active (running) since Sat 2020-07-11 03:03:01 UTC; 12min ago Docs: man:httpd.service(8) Main PID: 21821 (httpd) Status: "Total requests: 0; Idle/Busy workers 100/0;Requests/sec: 0; Bytes served/sec: 0 B/sec" CGroup: /system.slice/httpd.service tq21821 /usr/sbin/httpd -DFOREGROUND tq21831 /usr/sbin/httpd -DFOREGROUND tq21832 /usr/sbin/httpd -DFOREGROUND tq21833 /usr/sbin/httpd -DFOREGROUND tq21834 /usr/sbin/httpd -DFOREGROUND mq21835 /usr/sbin/httpd -DFOREGROUND Jul 11 03:03:01 ip-172-31-40-241.ap-northeast-1.compute.internal systemd[1]: Starting The Apache HTTP Server... Jul 11 03:03:01 ip-172-31-40-241.ap-northeast-1.compute.internal systemd[1]: Started The Apache HTTP Server. # また、enabledにもなっている $ systemctl is-enabled httpd enabled全て設定値通りに作成されているのでOKとします!
参考サイト
各種サービスにおけるAWS SDK For Rubyを利用した実装例が確認できます。
AWS SDK for Ruby コード例所感
AWS CLIは業務でよく使っていたのですが、AWS SDKを使ったのはこれが初でした。
AWS CLIよりコーディングとしての実装はかなり楽に感じます(例えばインスタンスのステータス待ちなどは、AWS CLIだとForループで何秒間隔で状態確認をするみたいな実装をしていたのですが、AWS SDKだとwait_untilメソッドを呼ぶだけの1行コードで済んでしまいました)。
今回は以上です。見て頂きありがとうございました。
- 投稿日:2020-07-11T12:59:05+09:00
【AWS SDK】EC2自動構築用スクリプト開発
目標
AWS SDK(AWS SDK for Ruby)を利用してEC2自動構築を行う。
はじめに
AWS SDKの基礎知識をまとめた後、実際の動作をAWS SDK for Rubyを利用したEC2自動構築で試してみます。
AWS SDK
AWSが提供しているAPIの一種で、AWSがサポートする様々なプログラミング言語にライブラリとしてインポートして利用します。
現在以下の言語がサポートされており、各種プログラムからのAWSリソース操作を可能にします。・C++
・Go
・Java
・JavaScript
・.NET
・Node.js
・PHP
・Python
・Ruby(本記事ではこれを利用)AWS CLI、SDK利用時の認証
AWS CLI(OSコマンドライン上やシェルスクリプト、PowerShell上で利用)、AWS SDK(各種プログラミング言語上で利用)を利用する際には認証情報が必要となります。
認証情報の設定方法には以下3つの方法が存在しており、参照優先度が異なります。
項番 優先度 認証情報の設定箇所 1 高 OS環境変数 2 中 認証情報ファイル 3 低 インスタンスプロファイル(IAMロール認証) ①OS環境変数
環境変数のAWS_ACCESS_KEY_ID および AWS_SECRET_ACCESS_KEYという環境変数を利用して認証情報の設定が可能です。
認証設定方法の中で最も優先度が高いです。
例えば以下のように設定を行います。# For LINUX export AWS_ACCESS_KEY_ID=your_access_key_id export AWS_SECRET_ACCESS_KEY=your_secret_access_key # For Windows set AWS_ACCESS_KEY_ID=your_access_key_id set AWS_SECRET_ACCESS_KEY=your_secret_access_key②認証情報ファイル
OSユーザのホームディレクトリ内の「aws」ディレクトリに存在する「credentials」というファイルが認証情報ファイルです。
そのファイル内のaws_access_key_idとaws_secret_access_keyにアクセスキーとシークレットアクセスキーを投入します。# 認証情報ファイル ~/.aws/credentials
[default] aws_access_key_id = your_access_key_id aws_secret_access_key = your_secret_access_key③インスタンスプロファイル
インスタンスプロファイルとは、IAMロールを利用した認証で利用される実行環境ことです。
IAMロール作成時に自動作成されます。
IAMロールを納めるための容器であり、EC2にアタッチする時に必要なコネクターの役割をするようです。
IAMロールを利用するこの認証方法はAWS SDK及びAWS CLI利用時のベストプラクティスであり、アクセスキーとシークレットアクセスキーを利用する上記2つの方法より認証情報漏洩のリスクが軽減されるため推奨されております。
ちなみに、認証方法としての優先度が最も低いため、環境変数や認証情報ファイル内の情報に注意を払う必要があります。インスタンスプロファイルの詳細に関しては、以下サイトが参考になりました。
EC2にIAMRole情報を渡すインスタンスプロファイルを知っていますか?作業の流れ
項番 タイトル 1 AWS SDK for Rubyのセットアップ 2 EC2自動構築用スクリプトのセットアップ 3 動作検証 手順
1.AWS SDK for Rubyのセットアップ
①AWS SDK for Rubyインストール
かなり時間かかるので注意…gem install aws-sdk
②アクセスキーとシークレットアクセスキーの発行
アクセスキーとシークレットアクセスキーが未発行の場合は新規作成する必要があります。
手順は以下を参考
【AWS CLI】Red Hat Enterprise Linux 8でAWS CLIを使用可能にする(3. アクセスキーIDとシークレットアクセスキーの発行)③認証情報ファイルの編集
今回はAWS SDKの認証情報を認証情報ファイル内に設定する形で保存します。
認証情報ファイル~/.aws/credentials
内のaws_access_key_id
とaws_secret_access_key
にアクセスキーとシークレットアクセスキーを記載してください。# 認証情報ファイル編集 vi ~/.aws/credentials
[default] aws_access_key_id = your_access_key_id aws_secret_access_key = your_secret_access_key2.EC2自動構築用スクリプトのセットアップ
①EC2自動構築用スクリプトをAWS SDKをセットアップした環境に配備
スクリプト内の各種環境依存の変数(<ami_id>
、<keypair_name>
、<security_group_id>
、<instance_type>
、<az_name>
、<subnet_id>
、<userdata_pass>
)は環境に従って書き換えをします。また、
<userdata_pass>
に指定したファイルの中にはEC2に設定したいユーザデータ処理を書いておきます。ファイル名: ec2_create.rb# ********************************************************************************** # 機能概要: EC2の自動構築 # スクリプト用法: ruby <スクリプトパス> <インスタンス名> # ********************************************************************************** unless ARGV.size() == 1 puts "The number of arguments is incorrect." exit end require 'aws-sdk' require 'base64' # EC2の構成要素定義(以下変数を環境に応じて編集) image_id = '<ami_id>' # AMIID key_name = '<keypair_name>' # キーペア名 security_group_ids = '<security_group_id>' # セキュリティグループID instance_type = '<instance_type>' # インスタンスタイプ availability_zone = '<az_name>' # 利用AZ subnet_id = '<subnet_id>' # 利用サブネットID user_data = '<userdata_pass>' # ユーザデータのファイルパス # ユーザデータ設定 if File.exist?(user_data) file = File.open(user_data) script = file.read file.close else script = '' end encoded_script = Base64.encode64(script) # EC2リソース操作用インスタンス作成 ec2 = Aws::EC2::Resource.new # EC2作成実施 instance = ec2.create_instances({ image_id: image_id, min_count: 1, max_count: 1, key_name: key_name, security_group_ids: [security_group_ids], user_data: encoded_script, instance_type: instance_type, placement: { availability_zone: availability_zone }, subnet_id: subnet_id }) # インスタンスが利用可能になるまで待機 ec2.client.wait_until(:instance_running, {instance_ids: [instance[0].id]}) # インスタンス名(Nameタグ)を付与 instance_name = ARGV[0] instance.batch_create_tags({ tags: [{ key: 'Name', value: instance_name }]}) puts "#{instance_name}(#{instance[0].id})が作成されました!"3.動作検証
<前提>
以下のパラメータでEC2を作成します。# EC2の構成要素定義(以下変数を環境に応じて編集) image_id = 'ami-067152a7c26866dcb' # AMIID key_name = 'mykeypair' # キーペア名 security_group_ids = 'sg-64a59718' # セキュリティグループID instance_type = 't2.medium' # インスタンスタイプ availability_zone = 'ap-northeast-1a' # 利用AZ subnet_id = 'subnet-41d23b09' # 利用サブネットID user_data = '/tmp/userdata' # ユーザデータのファイルパスまた、EC2に設定予定のユーザデータは以下の内容
$ cat /tmp/userdata #!/bin/bash yum update -y yum install -y httpd systemctl start httpd systemctl enable httpd①スクリプト実行
インスタンス名を第一引数に指定してスクリプト実行します。$ ruby ec2_create.rb test_server test_server(i-0a4fd9cfe1613a8d6)が作成されました!
②結果確認
インスタンス名、インスタンスタイプ、サブネットID、キーペア名、AZ名、AMIIDがスクリプト内での設定値通り設定されています。
一応OSログイン確認と、ユーザデータが正常に実行されているかも確認してみます。
# httpdがユーザデータ実行により起動している $ systemctl status httpd ● httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled) Active: active (running) since Sat 2020-07-11 03:03:01 UTC; 12min ago Docs: man:httpd.service(8) Main PID: 21821 (httpd) Status: "Total requests: 0; Idle/Busy workers 100/0;Requests/sec: 0; Bytes served/sec: 0 B/sec" CGroup: /system.slice/httpd.service tq21821 /usr/sbin/httpd -DFOREGROUND tq21831 /usr/sbin/httpd -DFOREGROUND tq21832 /usr/sbin/httpd -DFOREGROUND tq21833 /usr/sbin/httpd -DFOREGROUND tq21834 /usr/sbin/httpd -DFOREGROUND mq21835 /usr/sbin/httpd -DFOREGROUND Jul 11 03:03:01 ip-172-31-40-241.ap-northeast-1.compute.internal systemd[1]: Starting The Apache HTTP Server... Jul 11 03:03:01 ip-172-31-40-241.ap-northeast-1.compute.internal systemd[1]: Started The Apache HTTP Server. # また、enabledにもなっている $ systemctl is-enabled httpd enabled全て設定値通りに作成されているのでOKとします!
参考サイト
各種サービスにおけるAWS SDK For Rubyを利用した実装例が確認できます。
AWS SDK for Ruby コード例所感
AWS CLIは業務でよく使っていたのですが、AWS SDKを使ったのはこれが初でした。
AWS CLIよりコーディングとしての実装はかなり楽に感じます(例えばインスタンスのステータス待ちなどは、AWS CLIだとForループで何秒間隔で状態確認をするみたいな実装をしていたのですが、AWS SDKだとwait_untilメソッドを呼ぶだけの1行コードで済んでしまいました)。
今回は以上です。見て頂きありがとうございました。
- 投稿日:2020-07-11T12:35:15+09:00
[Rails] link_toのリンク先を別タブで表示させたい
Railsでlink_toを使うときに別タブで表示させたいと思い、実装したのでメモとして残しておきます。
手順
まず
link_to
で表示させたい文字列とリンク先URLを指定。<%= link_to "文字列", "リンク先URL" %>別タブで表示させるため、
target: :_blank
を追加<%= link_to "文字列", "リンク先URL", target: :_blank %>これで別タブで開けるようになる。
しかし、これだとパフォーマンスとセキュリティの面で問題が。。。この問題を回避するためには
rel="noopener noreferrer"
をつけるといいみたい。<%= link_to "文字列", "リンク先URL", target: :_blank, rel: "noopener noreferrer" %>これで安心して別タブを開けます。
- 投稿日:2020-07-11T12:04:00+09:00
新規登録時、ActionMailerでメール送信機能
概要
ActionMailerを使って新規登録時にwelcomeメールを送ります。
前提
deviseを用いての、ログイン機能の実装。
導入手順
1. ActionMailerを利用する設定
welcomeメールの送信元はGmailを使う記載方法。
config/environments/development.rb
にてActionMailerの設定方法の記述。development.rbRails.application.configure do #---中略---# config.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { port: 587, address: 'smtp.gmail.com', domain: 'gmail.com', user_name: '送信元アドレス', password: 'アプリパスワード', authentication: :plain, enable_starttls_auto: true }アプリパスワードは2段階認証を設定の上16桁のパスワードを発行し、アプリパスワードに記載する。
以下、参考にさせていただきました。
・Google メールアプリケーション用のパスワードを取得する
・Google 二段階認証設定をONにする方法2. Mailerクラス作成
$ rails g mailer UserNotice3. Mailerクラス編集
app/mailers/user_notice_mailer.rbclass UserNoticeMailer < ApplicationMailer def send_signup_email(user) @user = user mail to: @user.email, subject: "会員登録が完了しました。" end end4. メール本文作成
app/views/user_notice_mailer/send_signup_email.text.erbようこそ <%= @user.name %> 様 この度はアカウント登録頂きましてありがとうございます。5. Userモデル編集
app/models/user.rb#---追加---# after_create :send_welcome_mail def send_welcome_mail UserNoticeMailer.send_signup_email(self).deliver endafter_create を使うことで、Userが新規作成された後にメールを送信するためのメソッドを呼び出すことができます。
以上で、welcomeメールが送信されるはずです。
- 投稿日:2020-07-11T11:50:52+09:00
Digdag公式ドキュメントからDigdagを学ぶ-アーWorkflow definition
目標
Digdagの公式サイトのドキュメントのWorkflow definitionの翻訳+α
DigdagのRubyを使ってRailsにバッチを作るまでが最後の目標
http://docs.digdag.io/workflow_definition.html#目次
Getting started
Architecture
Concepts
Workflow definition
Scheduling workflow
Operators
Command reference
Language API -Ruby
REST API
Internal architecture
Release NotesWorkflow definition
Workflow definition: *.dig files
digdagワークフローは
.dig拡張子
が付いた名前のファイルで定義されます。
ファイルの名前はワークフローの名前です。たとえば、
hello_world
ワークフローを作成すると、hello_world.dig
ファイルが作成されます。 ファイルの内容は次のようになります。hello_world.digtimezone: Asia/Tokyo +step1: sh>: tasks/shell_sample.sh +step2: py>: tasks.MyWorkflow.step2 param1: this is param1 +step3: rb>: MyWorkflow.step3 require: tasks/ruby_sample.rb
timezone
パラメータは、ワークフローのタイムゾーンを構成するために使用され、セッションのタイムスタンプ変数とスケジューリングに影響を与えます。 デフォルトのタイムゾーンはUTC
です。 他の有効なタイムゾーンの例には、America/Los_Angeles、Europe/Berlin、Asia/Tokyoなどがあります。+記号はタスクを意味
+記号で始まるキー名はタスクです。 タスクは上から順に実行されます。 タスクは、別のタスクの子としてネストできます。 上記の例では、
+step2
は+main
タスクの子として+step1
の後に実行されます。operators>
type>:commandまたは_type:NAMEパラメーターを持つタスクがアクションを実行します。 シェルスクリプトの実行、Pythonメソッド、電子メールの送信など、さまざまな種類のオペレーターを選択できます。組み込みオペレーターのリストについては、オペレーターのページを参照してください。
foo>:barは以下の二つのパラメーターと同じです。 _type: foo _command: barUsing ${variables}
ワークフローは$ {...}構文を使用して変数を埋め込むことができます。
組み込み変数を使用するか、独自の変数を定義できます。
組み込み変数については以下のドキュメント参照
http://docs.digdag.io/workflow_definition.htmlCalculating variables
$ {...}構文で基本的なJavaScriptスクリプトを使用して変数を計算できます。
一般的な使用例は、タイムスタンプを別の形式にフォーマットすることです。 Digdagには、時間計算のためのMoment.jsがバンドルされています。timezone: Asia/Tokyo +format_session_time: echo>: ${moment(session_time).format("YYYY-MM-DD HH:mm:ss Z")} +format_in_utc: echo>: ${moment(session_time).utc().format("YYYY-MM-DD HH:mm:ss")} +format_tomorrow: echo>: ${moment(session_time).add(1, 'days').format("LLL")} +get_execution_time: echo>: ${moment().format("YYYY-MM-DD HH:mm:ss Z")}2020-07-10 22:05:34 +0900 [INFO] (0017@[0:default]+my_workflow+format_session_time): echo>: 2020-07-10 09:00:00 +09:00 2020-07-10 09:00:00 +09:00 2020-07-10 22:05:35 +0900 [INFO] (0017@[0:default]+my_workflow+format_in_utc): echo>: 2020-07-10 00:00:00 2020-07-10 00:00:00 2020-07-10 22:05:35 +0900 [INFO] (0017@[0:default]+my_workflow+format_tomorrow): echo>: July 11, 2020 9:00 AM July 11, 2020 9:00 AM 2020-07-10 22:05:36 +0900 [INFO] (0017@[0:default]+my_workflow+get_execution_time): echo>: 2020-07-10 22:05:36 +09:00 2020-07-10 22:05:36 +09:00Defining variables
変数定義方法
1. YAMLでの_exportパラメーターの使用 2. APIを使用してプログラムで変数を設定 3. 変数を使用したセッションの開始Using _export: parameter
YAMLファイルでは、
_export:
指示者が変数を定義します。
これはデータベースのホスト名などの静的構成をロードするのに役立ちます。タスクに_export指示者がある場合、スコープ内で変数を定義するためタスクとその子は変数を使用できます。
my_workflow.dig_export: foo: 1 +prepare: sh>: echo foo:${foo} bar:${bar} +analyze: _export: bar: 2 +step1: sh>: echo foo:${foo} bar:${bar} +dump: sh>: echo foo:${foo} bar:${bar}結果2020-07-10 22:18:11 +0900 [INFO] (0017@[0:default]+my_workflow+prepare): sh>: echo foo:1 foo:1 2020-07-10 22:18:11 +0900 [INFO] (0017@[0:default]+my_workflow+analyze+step1): sh>: echo foo:1 bar:2 foo:1 bar:2 2020-07-10 22:18:12 +0900 [INFO] (0017@[0:default]+my_workflow+dump): sh>: echo foo:1 foo:1すべてのタスクでfoo=1を使用できますが、bar=2を使用できるのは+step1(および+analyze)だけです。
Using API
言語APIを使用してプログラムで変数を設定できます。 たとえば、Python APIはdigdag.env.exportとdigdag.env.storeを提供します。
今回の内容にはRubyAPIを中心に説明したいと思います。APIについては別章で扱うのでこちらでも詳細説明はしません。import digdag class MyWorkflow(object): def prepare(self): digdag.env.store({"my_param": 2}) def analyze(self, my_var): print("my_var should be 2: %d" % my_var)Starting a session with variables
新しいワークフローセッションを開始するときに変数を設定できます。変数を設定するには、
-p KEY = VALUE
を複数回使用します。
ドキュメントにはサンプルがないので。単純に外部からもらうパラメーターをコンソルに出力するワークフローを作成します。my_workflow.dig+print_my_var1: sh>: echo my_var1:${my_var1} +print_my_var2: sh>: echo my_var2:${my_var2}my_var1とmy_bar2は実行時パラメーターから取得できます。以下のように実行してみましょう〜
digdag run my_workflow.dig --rerun -p my_var1=foo -p my_var2=bar結果2020-07-11 10:29:33 +0900 [INFO] (0017@[0:default]+my_workflow+print_my_var1): sh>: echo my_var1:foo my_var1:foo 2020-07-11 10:29:33 +0900 [INFO] (0017@[0:default]+my_workflow+print_my_var2): sh>: echo my_var2:bar my_var2:bar!include another file
YAMLファイルを小さなファイルに分割して、複雑なワークフローを整理できます。
!include
を使用して分割されたワークフローを自分のワークフローに含めることができます。my_workflow.dig+task1: !include : 'tasks/task1.dig' +task2: !include : 'tasks/task2.dig'task1.dig+task1: sh>: echo タスク1ですtask2.dig+task2: sh>: echo タスク2です
my_workflow.dig
を実行するとtask1.dig task2.dig
のタスクが実行されるのがわかります。run$digdag run my_workflow.dig --rerun 2020-07-11 10:40:39 +0900 [INFO] (0017@[0:default]+my_workflow+task1+task1): sh>: echo タスク1です タスク1です 2020-07-11 10:40:40 +0900 [INFO] (0017@[0:default]+my_workflow+task2+task2): sh>: echo タスク2です タスク2ですParallel execution
_parallel:true
がグループに設定されている場合、グループ内のタスクは並行して実行されます。my_workflow.dig+prepare: # +data1, +data2, and +data3 run in parallel. _parallel: true +data1: sh>: echo data1 +data2: sh>: echo data2 +data3: sh>: echo data3 +analyze: sh>: echo analyzedata1, data2, data3が並列で出力されています。
実行結果$ digdag run my_workflow.dig --rerun 2020-07-11 10:50:45 +0900 [INFO] (0018@[0:default]+my_workflow+prepare+data2): sh>: echo data2 2020-07-11 10:50:45 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+data1): sh>: echo data1 2020-07-11 10:50:45 +0900 [INFO] (0019@[0:default]+my_workflow+prepare+data3): sh>: echo data3 data1 data2 data3 2020-07-11 10:50:45 +0900 [INFO] (0019@[0:default]+my_workflow+analyze): sh>: echo analyze analyze
_background:true
がタスクまたはグループに設定されている場合、タスクまたはグループは前のタスクと並行して実行されます。次のタスクは、バックグラウンドタスクまたはグループの完了を待ちます。my_workflow.dig+prepare: +data1: sh>: echo data1 # +data1 and +data2 run in parallel. +data2: _background: true sh>: echo data2 # +data3 runs after +data1 and +data2. +data3: sh>: echo data3 +analyze: sh>: echo analyzedata1,data2が並列で実行されます。
結果$ digdag run my_workflow.dig --rerun 2020-07-11 11:00:06 +0900 [INFO] (0018@[0:default]+my_workflow+prepare+data2): sh>: echo data2 2020-07-11 11:00:06 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+data1): sh>: echo data1 data2 data1 2020-07-11 11:00:06 +0900 [INFO] (0018@[0:default]+my_workflow+prepare+data3): sh>: echo data3 data3 2020-07-11 11:00:06 +0900 [INFO] (0018@[0:default]+my_workflow+analyze): sh>: echo analyzeRetrying failed tasks automatically
_retry:N(Nは整数:1、2、3、…)パラメーターがグループに設定されている場合、1つ以上の子タスクが失敗したときに、グループを最初から再試行します。
my_workflow.dig+prepare: # If +erase_table, +load_data, or +check_loaded_data fail, it retries from # +erase_table again. _retry: 3 +erase_table: sh>: echo erase_table +load_data: sh>: echo load_data +check_loaded_data: sh>: tasks/error.sh +analyze: sh>: echo analyzeerror.sh#!/bin/bash exit 0最初の実行とRetry3件の4件のエラーが発生しました。Retry途中で正常に実行されたらエラー数は減ると思います。
結果$ digdag run my_workflow.dig --rerun 2020-07-11 11:21:21 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+erase_table): sh>: echo erase_table erase_table 2020-07-11 11:21:21 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+load_data): sh>: echo load_data load_data 2020-07-11 11:21:22 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+check_loaded_data): sh>: tasks/error.sh 2020-07-11 11:21:22 +0900 [ERROR] (0017@[0:default]+my_workflow+prepare+check_loaded_data): Task failed with unexpected error: Command failed with code 1 java.lang.RuntimeException: Command failed with code 1 at io.digdag.standards.operator.ShOperatorFactory$ShOperator.runTask(ShOperatorFactory.java:143) at io.digdag.util.BaseOperator.run(BaseOperator.java:35) at io.digdag.core.agent.OperatorManager.callExecutor(OperatorManager.java:315) at io.digdag.cli.Run$OperatorManagerWithSkip.callExecutor(Run.java:705) at io.digdag.core.agent.OperatorManager.runWithWorkspace(OperatorManager.java:257) at io.digdag.core.agent.OperatorManager.lambda$runWithHeartbeat$2(OperatorManager.java:137) at io.digdag.core.agent.LocalWorkspaceManager.withExtractedArchive(LocalWorkspaceManager.java:25) at io.digdag.core.agent.OperatorManager.runWithHeartbeat(OperatorManager.java:135) at io.digdag.core.agent.OperatorManager.run(OperatorManager.java:119) at io.digdag.cli.Run$OperatorManagerWithSkip.run(Run.java:687) at io.digdag.core.agent.MultiThreadAgent.lambda$null$0(MultiThreadAgent.java:127) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) 2020-07-11 11:21:22 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+erase_table): sh>: echo erase_table erase_table 2020-07-11 11:21:22 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+load_data): sh>: echo load_data load_data 2020-07-11 11:21:23 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+check_loaded_data): sh>: tasks/error.sh 2020-07-11 11:21:23 +0900 [ERROR] (0017@[0:default]+my_workflow+prepare+check_loaded_data): Task failed with unexpected error: Command failed with code 1 java.lang.RuntimeException: Command failed with code 1 at io.digdag.standards.operator.ShOperatorFactory$ShOperator.runTask(ShOperatorFactory.java:143) at io.digdag.util.BaseOperator.run(BaseOperator.java:35) at io.digdag.core.agent.OperatorManager.callExecutor(OperatorManager.java:315) at io.digdag.cli.Run$OperatorManagerWithSkip.callExecutor(Run.java:705) at io.digdag.core.agent.OperatorManager.runWithWorkspace(OperatorManager.java:257) at io.digdag.core.agent.OperatorManager.lambda$runWithHeartbeat$2(OperatorManager.java:137) at io.digdag.core.agent.LocalWorkspaceManager.withExtractedArchive(LocalWorkspaceManager.java:25) at io.digdag.core.agent.OperatorManager.runWithHeartbeat(OperatorManager.java:135) at io.digdag.core.agent.OperatorManager.run(OperatorManager.java:119) at io.digdag.cli.Run$OperatorManagerWithSkip.run(Run.java:687) at io.digdag.core.agent.MultiThreadAgent.lambda$null$0(MultiThreadAgent.java:127) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) 2020-07-11 11:21:23 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+erase_table): sh>: echo erase_table erase_table 2020-07-11 11:21:23 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+load_data): sh>: echo load_data load_data 2020-07-11 11:21:24 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+check_loaded_data): sh>: tasks/error.sh 2020-07-11 11:21:24 +0900 [ERROR] (0017@[0:default]+my_workflow+prepare+check_loaded_data): Task failed with unexpected error: Command failed with code 1 java.lang.RuntimeException: Command failed with code 1 at io.digdag.standards.operator.ShOperatorFactory$ShOperator.runTask(ShOperatorFactory.java:143) at io.digdag.util.BaseOperator.run(BaseOperator.java:35) at io.digdag.core.agent.OperatorManager.callExecutor(OperatorManager.java:315) at io.digdag.cli.Run$OperatorManagerWithSkip.callExecutor(Run.java:705) at io.digdag.core.agent.OperatorManager.runWithWorkspace(OperatorManager.java:257) at io.digdag.core.agent.OperatorManager.lambda$runWithHeartbeat$2(OperatorManager.java:137) at io.digdag.core.agent.LocalWorkspaceManager.withExtractedArchive(LocalWorkspaceManager.java:25) at io.digdag.core.agent.OperatorManager.runWithHeartbeat(OperatorManager.java:135) at io.digdag.core.agent.OperatorManager.run(OperatorManager.java:119) at io.digdag.cli.Run$OperatorManagerWithSkip.run(Run.java:687) at io.digdag.core.agent.MultiThreadAgent.lambda$null$0(MultiThreadAgent.java:127) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) 2020-07-11 11:21:24 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+erase_table): sh>: echo erase_table erase_table 2020-07-11 11:21:24 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+load_data): sh>: echo load_data load_data 2020-07-11 11:21:24 +0900 [INFO] (0017@[0:default]+my_workflow+prepare+check_loaded_data): sh>: tasks/error.sh 2020-07-11 11:21:24 +0900 [ERROR] (0017@[0:default]+my_workflow+prepare+check_loaded_data): Task failed with unexpected error: Command failed with code 1 java.lang.RuntimeException: Command failed with code 1 at io.digdag.standards.operator.ShOperatorFactory$ShOperator.runTask(ShOperatorFactory.java:143) at io.digdag.util.BaseOperator.run(BaseOperator.java:35) at io.digdag.core.agent.OperatorManager.callExecutor(OperatorManager.java:315) at io.digdag.cli.Run$OperatorManagerWithSkip.callExecutor(Run.java:705) at io.digdag.core.agent.OperatorManager.runWithWorkspace(OperatorManager.java:257) at io.digdag.core.agent.OperatorManager.lambda$runWithHeartbeat$2(OperatorManager.java:137) at io.digdag.core.agent.LocalWorkspaceManager.withExtractedArchive(LocalWorkspaceManager.java:25) at io.digdag.core.agent.OperatorManager.runWithHeartbeat(OperatorManager.java:135) at io.digdag.core.agent.OperatorManager.run(OperatorManager.java:119) at io.digdag.cli.Run$OperatorManagerWithSkip.run(Run.java:687) at io.digdag.core.agent.MultiThreadAgent.lambda$null$0(MultiThreadAgent.java:127) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) 2020-07-11 11:21:25 +0900 [INFO] (0017@[0:default]+my_workflow^failure-alert): type: notify error: * +my_workflow+prepare+check_loaded_data: Command failed with code 1 (runtime) * +my_workflow+prepare+check_loaded_data: Command failed with code 1 (runtime) * +my_workflow+prepare+check_loaded_data: Command failed with code 1 (runtime) * +my_workflow+prepare+check_loaded_data: Command failed with code 1 (runtime)次のように、間隔を_retryに設定もできます
+prepare: _retry: limit: 3 interval: 10 interval_type: exponential■limit: 再試行回数
■interval:再実行間隔時間(秒)
■interval_type:constantかexponential
constant
の場合は再実行間隔は一定になります。
exponential
の場合は再実行間隔は2 x(retry_count-1)
として再試行ごとに増加します。上記の例では最初の再試行間隔は10秒、2番目は20秒、3番目は40秒です。Sending error notification
# ワークフローが失敗する時に実行される _error: sh>: tasks/runs_when_workflow_failed.sh +analyze: sh>: tasks/analyze_prepared_data_sets.shエラーのタイミングで
mail>operator
を利用してメール送信可能
詳しい説明はオペレーターの説明でやります〜
http://docs.digdag.io/operators/mail.html
- 投稿日:2020-07-11T11:34:10+09:00
enumを日本語化して、連動したラジオボタンを生成する方法
やりたいこと
以下のようなenumをラジオボタンで表現したい。
enum loadtype: { normal: 0, trail: 1, beach: 2, track: 3 }かつ、選択肢は日本語で出るようにしたい。
enumの日本語化について
こちらの記事の内容に従って日本語化します。
https://qiita.com/tanutanu/items/d44a92425188a4489ec6日本語化したenumに連動したラジオボタンを記述
ViewのFormで以下のように記載
<%= f.collection_radio_buttons :loadtype, Post.loadtypes_i18n, :first, :last %>コードの意味は以下の通り
<%= f.collection_radio_buttons [column名], [表示対象のコレクション], [value], [textに表示する文字列] %>
- 投稿日:2020-07-11T11:34:10+09:00
emunを日本語化して、連動したラジオボタンを生成する方法
やりたいこと
以下のようなEmunをラジオボタンで表現したい。
enum loadtype: { normal: 0, trail: 1, beach: 2, track: 3 }かつ、選択肢は日本語で出るようにしたい。
enumの日本語化について
こちらの記事の内容に従って日本語化します。
https://qiita.com/tanutanu/items/d44a92425188a4489ec6日本語化したenumに連動したラジオボタンを記述
ViewのFormで以下のように記載
<%= f.collection_radio_buttons :loadtype, Post.loadtypes_i18n, :first, :last %>コードの意味は以下の通り
<%= f.collection_radio_buttons [column名], [表示対象のコレクション], [value], [textに表示する文字列] %>
- 投稿日:2020-07-11T10:52:52+09:00
[Rails]DBにデータが登録されない場合の対処法
はじめに
フォーム入力内容をDBに保存したいがハマったのでアウトプット
環境
Rails 5.0.7.2
ruby 2.5.1
mysql 14.14問題点
フォーム入力内容を保存しようとすると、パラメータに保存されているがDBに保存されない。
対処方法
保存するメソッドの後ろに!をつけて原因を確認。
すると、validation failed:User must exist(エラー内容は異なる場合がありそう)
原因
アソシエーションが組まれている際に、該当の外部キーが入っておらず、バリデーションで弾かれているのが原因。
optional: trueを記述。
goal.rbclass Goal < ApplicationRecord validates :name, presence: true, uniqueness: true validates :time, presence: true, uniqueness: true validates :days, presence: true, uniqueness: true belongs_to :user, optional: true #ここを編集 endoptional: trueとは、belongs_toの外部キーのnilを許可するというもの。
これでDBに保存できると思います。
参考にしてください!
- 投稿日:2020-07-11T05:32:31+09:00
Rubyの継承について
プログラミングの勉強日記
2020年7月11日 Progate Lv.175
RubyⅤ継承
クラスを1から作ることもできるが、すでにあるクラスを利用してそのクラスを元に新しいクラスを作れる。そうすることで共通部分をまとめることができ、効率的にコードが書ける。
あるクラスをもとにして新たなクラスを作ることを継承という。class 新しいクラス名 < 元となるクラス名
で他のクラスを継承して新しいクラスを定義することができる。新しいクラスは「子クラス」、元となるクラスは「親クラス」という。
親クラスのインスタンスメソッドが引き継がれる。food.rbrequire "./menu" class Food < Menu end子クラスのインスタンスは、継承すると子クラスは親クラスのインスタンスメソッドを引き継ぐ。
index.rbfood1=Food.new puts food1.name puts food2.infomenu.rbclass Menu attr_accessor :name #処理 def info return "#{self.name} #{self.price}円" end endインスタンス変数の追加
子クラスにインスタンス変数を追加するためには今まで通り
attr_accessor
を使う。menu.rbclass Menu attr_accessor :name attr_accessor :price endFoodクラスはMenuクラスを継承しているので。「name, price, calorie」の3つのインスタンス変数を持つ。
food.rbclass Food < Menu #Foodクラスにcalorieを追加する attr_accessor :calorie end同様にしてインスタンスメソッドも追加することができる。
オーバーライド
親にあるメソッドと同じ名前のメソッドを子クラスで定義すると、メソッドを上書きすることができる。このことをオーバーライドという。
index.rb(メソッドを呼び出す)food1~Food.new(...) food1.calorie=700 puts food1.infomenu.rb(親クラス)class Menu #処理 def info #処理 end endfood.rb(子クラス)class Food < Menu #処理 #メソッドの上書き(上書きしたメソッドが呼び出される) def info #処理 end end子クラスのインスタンスは子クラスで定義したメソッドを優先して呼び出す。よって、子クラスと親クラスに同じ名前のメソッドがある場合は、子クラスのメソッドの内容が上書きされる。initializeメソッドでもオーバーライドができる。
super
オーバーライドしたメソッドの中で
super
とすることで、親クラスの同じ名前のメソッドを呼び出せる。superではメソッドを呼び出しているので、親クラスのメソッドに合わせてsuperに対して引数を渡す必要がある。menu.rbclass Menu attr_accessor :name attr_accessor :price def initialize(name:, price:) self.name=name self.price=price end endfood.jsclass Food < Menu attr_accessor :calorie def initialize(name:, price:, calorie:) super(name: name, price: price) self.calorie=calorie end endDateクラス
日付を扱うクラス。DateクラスはRubyがすでに用意しているクラスで、
require
でdateを読み込むことでクラスを定義しなくても使える。すでに用意されているクラスは他とはrequireの書き方が異なるindex.rb# "/date"ではない! reruire "date"Dateクラスのインスタンス
Date.new
でインスタンスを生成できる。Date.today
で今日の日付のインスタンスを作れる。index.rbrequire "date" #引数に「年・月・日」を渡してDateメソッドを作成 date1=Date.new(2020,7,8) puts dateコンソール2020-07-08Dateクラスのインスタンスメソッド
多くのインスタンスメソッドがある。日曜日かどうかを真偽値で渡すのは
sunday?
メソッド。index.rbrequire "date" date1=date1.new(2020,7,11) puts date1.sunday?コンソールfalseクラスメソッド
クラスに対して呼び出すメソッドのこと。
Date.today
のtoday
のとこ。クラスメソッドはdef クラス名.メソッド名
とすることで定義できる。インスタンスメソッドと異なり、メソッド名の前にクラス名を書く。
クラスメソッドの呼び出しは、クラス名.メソッド名
とする。menu.rbclass Menu #処理 #今日の日付が日曜日かどうかを真偽値で返す def Menu.is_discount_day? #今日の日付の情報を持つDateインスタンス today=Date.today return today.sunday end end puts Menu.is_discount_day?インスタンスメソッドの中でクラスメソッドを呼び出す
menu.rbclass Menu #処理 def get_total_price #インスタンスメソッド if Menu.discount_day? #クラスメソッドの呼び出し #処理 end end end
- 投稿日:2020-07-11T04:50:10+09:00
Rubyのクラスとインスタンスについて
プログラミングの勉強日記
2020年7月11日 Progate Lv.175
RubyⅣクラスとインスタンス
ここでは簡単にクラスとインスタンスについて説明する。プログラミングでメニューという「もの」を作るためには、まずその設計図が必要でとなる。設計図をクラス、「もの」をインスタンスという。(設計図(クラス)をもとに「もの」(インスタンス)を生成)なので、インスタンスを生成するためにはクラスを用意し、クラスからインスタンスを作成、そしてインスタンスに情報を追加する。
クラスの定義
クラスは
class クラス名
で定義できる。クラスは必ず大文字ではじめ、end
を忘れずに書く。index.rbclass Menu endattr_accessor
インスタンス変数を直接変更して操作ができるようにするもの。情報を持たせるために
attr_accessor シンボル
と書く。1つのクラスに対して複数のインスタンス変数を使うこともできる。index.rbclass Menu attr_accessor :name endインスタンスの生成
クラスを元に新しくインスタンスを生成するためには
クラス名.new
とする。変数名=クラス名.new
とすることで生成したインスタンス変数を代入できる。
インスタンスに情報を持たせるためには、クラスで用意したインスタンス変数に値を代入する。インスタンス.変数名=値
でインスタンス変数に値をセットすることができる。index.rbclass Menu #処理 end #変数menu1にMenuクラスからのインスタンスを生成し、代入する menu1=Menu.new menu1.name="すし"クラスの中でメソッドを使う
クラスの中で定義したメソッドはインスタンスに対して使うようにして呼び出す。
インスタンス.メソッド名
で呼び出す。クラスの中で定義したメソッドを呼び出すものをインスタンスメソッドという。index.rbclass Menu attr_accessor :name attr_accessor :price #クラスの中でメソッドを定義 def show puts "menu" end end menu1=Menu.new #クラスの中で定義したメソッドの呼び出し menu1.showコンソールmenuインスタンスメソッド
クラスの中で定義したインスタンスに対して呼び出すメソッドのこと。引数を受け取ったり戻り値を返したりすることができる。インスタンスが持つ情報である「インスタンス変数」とインスタンスに対して呼び出すインスタンスメソッドはクラスの中で定義する。
index.rbclass Menu #処理 def show(data) return "#{data}です" end end menu1=Menu.new puts menu1.show("メニュー")コンソールメニューですインスタンスメソッドの中でインスタンス変数を使う
インスタンスメソッドの中では特殊な変数
self
を用いてself.変数名
とすることで、インスタンス変数を扱える。インスタンスメソッドでは変数self
に呼び出したインスタンス自身が代入されている。index.rbclass Menu #処理 def show_name puts "#{self.name}です" end end menu1=Menu1.new menu1.name="すし" #インスタンスメソッドの呼び出し menu1.show_nameコンソールすしですinitializeメソッド
インスタンスを生成した直後に処理を実行できる。
クラス名.new
でインスタンスを生成した直後に自動で呼び出される。index.rbclass Menu #処理 def initialize puts "メニュー" end end #Menu.newが実行されると自動でinitializeメソッドを呼び出す menu1=Menu.newコンソールメニューインスタンスメソッドの中では
self.変数名
でインスタンス変数を扱うことができ、self.変数名=値
でインスタンス変数に値を代入する。index.rbclass Menu #処理 def initialize self.name="すし" end end menu1=Menu.new puts menu1.nameコンソールすしinitializeメソッドの変数
initializeメソッドにも引数を渡すことができる。
クラス.new
に対して引数を渡すことでその値を渡せる。もちろんキーワード引数を用いることもできる。index.rbclass Menu #処理 def initialize(message) puts message end end menu1=Menu.new("Hello")コンソールHellorequire
ファイルの読み込みを行う。(main文がわかれていて、別のファイルのクラスを読み込みたいときに使う)
index.rbrequie "./menu" menu1=Menu.new(name: "すし"menu.rbclass Menu attr_accessor :name attr_accessor :price endgets.chomp
入力を受け付けるときに使う。このコードが実行されるとコンソールが入力待機状態になる。
変数=gets.chomp
とすることでエンターキーを押されるまで入力された値を変数に代入できる。index.rbputs "名前の入力" name=gets.chomp puts "あなたの名前は#{name}です"コンソール名前の入力 田中(入力する) あなたの名前は田中です数値を受け取る場合は、
gets.chomp.to_i
とすることで入力された内容を数値に変換できる。index.rbputs "数字の入力" number=gets.chomp.to_i #入力された内容を数値に変換 puts "#{number}の2倍は#{number*2}です"
- 投稿日:2020-07-11T03:27:33+09:00
個人アプリ作成#1
- 投稿日:2020-07-11T01:32:38+09:00
[Rails]carrierwaveを使って画像を保存する
タイトルの通りになります。
ゴールは画像を表示させるまでとなります。
gemを導入
初めはgemを入れます。
バージョンを指定しなければ最新のものがインストールされます。Gemfilegem 'carrirewave'ターミナルでいつものよろしくお願いします。
$ bundle installアップローダファイルの生成
次にアップローダファイルを作成しましょう。
$ rails generate uploader Image create app/uploaders/image_uploader.rbこれで
app/uploaders
の中にimage_uploader.rb
が生成されていると思います。ちなみに
Image
の部分はマウントするモデル名だとか,
わかりやすい名前。私はよくImageで生成しています。image_uploader.rbclass ImageUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick # include CarrierWave::MiniMagick # process resize_to_fit: [300, 200] # version :thumb do # process :resize_to_fit => [50, 50] # end # 以下省略 endこのファイルの中で画像アップロードに関する設定ができます。例えば、アップロードする画像の拡張子の制限だったり、画像サイズのリサイズだったりいろんなことができます。
モデルに書く
画像アップロードさせたいモデルに先ほど作成したファイルをマウントしてあげましょう。
今回はhogeモデルのimageカラムに画像を登録させたいケースで考えます。
hoge.rbclass hoge < ApplicationRecord mount_uploader :image, ImageUploader end登録するためのFormを作る
各ページ必要な情報を渡してあげます。
hoges_controller.rbclass ArticlesController < ApplicationController def new @article = Article.new end def create @article = Article.new(user_params) if @article.save redirect_to user_path(@article) else render :new end end private def article_params params.require(:article).permit(:image) end end/hoges/new.html<%= form_with model: @hoge, local: true do %> <div class="field"> <%= f.label :image %> <%= f.file_field :image %> <!--画像はfile_field --> <div class="action"> <%= f.submit %> </div> <% end %>アップロードした画像の表示
画像の表示は基本的には、以下のように記述すると表示されます。
<%= image_tag @article.image.url %> <%= image_tag @article.image.to_s %>終わりに
最後まで読んでいただきありがとうございます。
お勉強している方に少しでも力になればと思います。
- 投稿日:2020-07-11T00:35:26+09:00
NokogiriをRspecでテストする。
Nokogiriに渡す引数をmock化してテストしたので残す。
FUGA_FUGA = ['企業', '秘密'] def hogegiri_method(fuga) HOGE_COUNT = 5 HOGE_COUNT.times do |time| html = Nokogiri::HTML(OpenURI.open_uri(url)) a, b, c = 正規表現わちゃわちゃ break if FUGAFUGA.include?(a) end { a: a, b: b, c: c } end色々見せられないコードなのでクソみたいなコードだが、、、今回mockとして偽物の戻り値を作りたいのが
html = Nokogiri::HTML(OpenURI.open_uri('内緒'))ここ
だけどNokogiriの戻り値を直接and_returnで設定するのは難しい。
- 戻り値がNokogiri::HTML::Documentといって単純なhtmlとかを返していない
- method内部でnokogiri特有の戻り値にしか使えないmethodを使っている。
など
つまり、nokogiriには仕事をさせつつURLにはアクセスせず目的のデータを取らないといけない。
じゃ、どうするか
OpenUri.open_uriこいつの戻り値をmock化してNokogiriの引数に渡すべき値を渡す。
Nokogiriの引数には解析したいHTMLがどうやら入るらしい。
それなら、本番環境ではアクセスして取ってくるURL先のHTMLをコピってhtmlfile作ってそこにペーストしてそいつを戻り値のmockにしてあげればいいのだ!!なんて簡単なことに記事づかなかったのだ。ファイナルアンサー
spec/test.htmlHTMLコピペallow(OpenURI).to receive(:open_uri).and_return(File.new("#{Rails.root}/spec/test.html"))完成
これでまるでurlにアクセスして取ってきたように、methodが動作します。
さらに、and_return
の引数をnil,File.new("#{Rails.root}/spec/test.html")
みたいにやると今回作ったmethodのロジックとして値を取得できなかったら最高5回はループするので、roopしていることも確認できますね。インターン始めたから個人開発じゃ絶対しないようなことばっかしてるから毎回詰まりそうになるけど何とか教えてもらったり粘ったりしてガンバている
ちょっと成長したかも