<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>Rails | みんたく</title>
	<atom:link href="https://mintaku-blog.net/category/develop/rails/feed/" rel="self" type="application/rss+xml" />
	<link>https://mintaku-blog.net</link>
	<description>みんたくの技術ブログ</description>
	<lastBuildDate>Sat, 05 Dec 2020 03:14:14 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.0.11</generator>

<image>
	<url>https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2018/06/cropped-ipad-820272_640.jpg?fit=32%2C32&#038;ssl=1</url>
	<title>Rails | みんたく</title>
	<link>https://mintaku-blog.net</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">144480658</site>	<item>
		<title>Cloud BuildにRSpecを組み込んでCI/CDパイプラインを構築する</title>
		<link>https://mintaku-blog.net/cloudbuild-rspec/</link>
					<comments>https://mintaku-blog.net/cloudbuild-rspec/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Sat, 05 Dec 2020 03:16:32 +0000</pubDate>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[GCP]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1755</guid>

					<description><![CDATA[<p>RailsにRSpecを導入して、Cloud BuildでCI/CDパイプラインを構築するところまでをまとめています。 RailsにRSpecを導入する ま …</p>
The post <a href="https://mintaku-blog.net/cloudbuild-rspec/">Cloud BuildにRSpecを組み込んでCI/CDパイプラインを構築する</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>RailsにRSpecを導入して、Cloud BuildでCI/CDパイプラインを構築するところまでをまとめています。</p>
<h2>RailsにRSpecを導入する</h2>
<p>まずはRailsにRSpecを導入します。</p>
<p>実際にローカル環境でRSpecによるテストができるところまでの手順をまとめています。</p>
<h3>「rspec-rails」、「factory_bot_rails」、「rails-controller-testing」のインストール</h3>
<p>Gemfileを以下の3つのGemを追加しbundle installします。</p>
<p>・Gemfile</p><pre class="crayon-plain-tag">group :development, :test do
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem 'rails-controller-testing'
end</pre><p></p>
<h3>RSpecに必要なファイルを生成する</h3>
<p>以下のコマンドでRSpecに必要なファイルを生成します。</p><pre class="crayon-plain-tag">$ bundle exec rails generate rspec:install</pre><p>&nbsp;</p>
<p>成功すると以下の3つのファイルが生成されていることが確認できます。</p><pre class="crayon-plain-tag">.rspec
spec/spec_helper.rb
spec/rails_helper.rb</pre><p></p>
<h3>RSpec実行に必要な設定をする</h3>
<p>Factory Botの設定をrails_helper.rbに追加します。</p>
<p>・spec/rails_helper.rb</p><pre class="crayon-plain-tag">RSpec.configure do |config|

  ...

  config.include FactoryBot::Syntax::Methods
end</pre><p>&nbsp;</p>
<p>Rails.loggerのログを出力するには以下を追加します。</p><pre class="crayon-plain-tag">Rails.logger = Logger.new(STDOUT)</pre><p>&nbsp;</p>
<p>rspec用のテストファイルを作成するために、application.rbに以下を追加します。</p>
<p>・application.rb</p><pre class="crayon-plain-tag">config.generators do |g|

...

  g.test_framework :rspec,
                   fixtures: true,
                   view_specs: false,
                   helper_specs: false,
                   routing_specs: false,
                   controller_specs: true,
                   request_specs: false
  g.fixture_replacement :factory_bot, dir: "spec/factories"
end</pre><p>&nbsp;</p>
<p>.rspecに以下を追加するとテストの実行結果が見やすくなります。</p>
<p>・.rspec</p><pre class="crayon-plain-tag">--format documentation</pre><p>&nbsp;</p>
<h3>テストを作成する</h3>
<p>factoriesやcontrollerを用意し、テストを作成していきます。</p>
<p>今回はテスト作成の過程は省略します。詳しくは以下の記事などを参考にしてください。</p>

<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Farticle-ogp-background-afbab5eb44e0b055cce1258705637a91.png%3Fixlib%3Drb-4.0.0%26w%3D1200%26blend64%3DaHR0cHM6Ly9xaWl0YS11c2VyLXByb2ZpbGUtaW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRnFpaXRhLWltYWdlLXN0b3JlLnMzLmFwLW5vcnRoZWFzdC0xLmFtYXpvbmF3cy5jb20lMkYwJTJGMjU4MjE5JTJGcHJvZmlsZS1pbWFnZXMlMkYxNjgwNTY3NTg2P2l4bGliPXJiLTQuMC4wJmFyPTElM0ExJmZpdD1jcm9wJm1hc2s9ZWxsaXBzZSZiZz1GRkZGRkYmZm09cG5nMzImcz02MjM5ZWY2ZGQzYzY5NTE1NzFjYTNhZTAxODA2YTUwNQ%26blend-x%3D120%26blend-y%3D467%26blend-w%3D82%26blend-h%3D82%26blend-mode%3Dnormal%26s%3D8a3ae59c828745ab802ed15fe5247ba2?ixlib=rb-4.0.0&amp;w=1200&amp;fm=jpg&amp;mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk2MCZoPTMyNCZ0eHQ9JUUzJTgwJTkwUmFpbHMlRTMlODAlOTElRTUlOUYlQkElRTYlOUMlQUMlRTclOUElODQlRTMlODElQUFBUEklRTMlODMlODYlRTMlODIlQjklRTMlODMlODglRTMlODElQUUlRTYlOUIlQjglRTMlODElOEQlRTYlOTYlQjkmdHh0LWFsaWduPWxlZnQlMkN0b3AmdHh0LWNvbG9yPSUyMzFFMjEyMSZ0eHQtZm9udD1IaXJhZ2lubyUyMFNhbnMlMjBXNiZ0eHQtc2l6ZT01NiZ0eHQtcGFkPTAmcz1mNzU3NTdiMDNhYTFkMGNmZTQ4MTI0M2VlYTc3YjBkOQ&amp;mark-x=120&amp;mark-y=112&amp;blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTgzOCZoPTU4JnR4dD0lNDBrLXBlbmd1aW4tc2F0byZ0eHQtY29sb3I9JTIzMUUyMTIxJnR4dC1mb250PUhpcmFnaW5vJTIwU2FucyUyMFc2JnR4dC1zaXplPTM2JnR4dC1wYWQ9MCZzPWRjYmQ1YjU1Y2EzYTU3MzI4NzI0NzQ1NTdiZjU2NzJk&amp;blend-x=242&amp;blend-y=480&amp;blend-w=838&amp;blend-h=46&amp;blend-fit=crop&amp;blend-crop=left%2Cbottom&amp;blend-mode=normal&amp;s=61d27baeec9f515f64f141cd85d98a8f" alt="">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://qiita.com/k-penguin-sato/items/defdb828bd54729272ad">【Rails】基本的なAPIテストの書き方 #RSpec - Qiita</a>
			</p>
							<div class="ys-blog-card__dscr">
					概要 Railsで作成されたAPIのテストについてこちらを参考に必要最低限の情報&hellip;				</div>
										<div class="ys-blog-card__domain">qiita.com</div>
					</div>
	</div>
</div>

<p>例として本のデータを取得するAPIの本コントローラのshowメソッドに対してテストを行います。</p>
<p>・spec/factories/books.rb</p><pre class="crayon-plain-tag">FactoryBot.define do
  factory :book do
    name {'テスト本'}
  end
end</pre><p>&nbsp;</p>
<p>・spec/controllers/books_controller_spec.rb</p><pre class="crayon-plain-tag">describe Api::BooksController, type: :controller do
  context '/api/books' do
    describe 'GET #show' do
      render_views

      before do
        @book = create(:book)
        get :show, params: { id: @book.id }
        @json = JSON.parse(response.body)
      end

      it '200チェック' do
        expect(response).to be_success
        expect(response.status).to eq 200
      end

      it '本データチェック' do
        expect(@json[name]).to eq(@book.name)
      end

    end
  end
end</pre><p>&nbsp;</p>
<p>ローカルでテストが実行できるか確認します。</p><pre class="crayon-plain-tag">$ docker-compose exec web bundle exec rspec</pre><p>&nbsp;</p>
<p>正常にテストが成功すると以下のようなログが出力されます。</p><pre class="crayon-plain-tag">Finished in 0.58604 seconds (files took 4.32 seconds to load)
2 examples, 0 failures</pre><p>&nbsp;</p>
<h2>Cloud BuildでRSpecを組み込む</h2>
<p>ローカルでのRSpecによるテストが確認できたら、Cloud BuildにRSpecを組み込んでCI/CDパイプラインを構築していきます。</p>
<h3>Dockerfileの作成</h3>
<p>Rubyの公式イメージを元に作成しています。</p>
<p>テストする際のDBの接続先はMariaDBを使用します。</p>
<p>・Dockerfile</p><pre class="crayon-plain-tag">FROM ruby:2.5.8-alpine3.11

RUN set -ex \
  &amp;&amp; apk update \
  &amp;&amp; apk upgrade \
  &amp;&amp; apk add --no-cache \
    build-base \
    bash \
    curl \
    nodejs \
    tzdata \
    mariadb \
    mariadb-client \
    mariadb-dev \
    openrc \
    libc6-compat \
    gcompat \
  &amp;&amp; mkdir -p /run/openrc \
  &amp;&amp; touch /run/openrc/softlevel \
  &amp;&amp; rc-status \
  &amp;&amp; /etc/init.d/mariadb setup \
  &amp;&amp; gem install bundler

RUN mkdir /app_name
ENV APP_ROOT /app_name
WORKDIR $APP_ROOT

ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock

RUN bundle install
ADD . $APP_ROOT</pre><p></p>
<h3>テスト用のDB設定</h3>
<p>database.ymlに以下のようにテスト用のDBを設定します。</p>
<p>・database.yml</p><pre class="crayon-plain-tag">test:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  pool: 5
  username: root
  password: password
  database: test_db</pre><p></p>
<h3>テスト実行用のシェルスクリプト作成</h3>
<p>Cloud Buildで実行するシェルスクリプトを作成します。</p>
<p>MariaDBを起動して、DBを作成しテストを実行できる環境を整えます。その後、テストを実行します。</p>
<p>・rspec.sh</p><pre class="crayon-plain-tag">#!/bin/bash

# MariaDBの起動
rc-service mariadb start

# テスト用DB作成・マイグレーション
RAILS_ENV=test rails db:create db:migrate

# RSpecテスト実行
RAILS_ENV=test bundle exec rspec</pre><p>&nbsp;</p>
<p>Dockerイメージを作成し、RSpecのテストを行いGAEにデプロイする</p>
<p>cloudbuild-staging.yamlのスクリプトでDockerイメージの作成、RSpecのテスト、GAEのステージング環境にデプロイを行います。いわゆるCI/CDパイプラインを構築して、実行しています。</p>
<p>Gitでプッシュするとcloudbuild.yamlのスクリプトが自動で実行されるようにしています。</p>
<p>Cloud BuildとGitの連携方法に関してはここでは省略しています。</p>
<p>・cloudbuild-staging.yaml</p><pre class="crayon-plain-tag">steps:
- name: 'alpine'
  args: ['echo', '${_ENVIRONMENT}']
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/cloud-build-rspec', '.']
- name: 'gcr.io/$PROJECT_ID/cloud-build-rspec'
  args: ['/bin/bash', 'rspec.sh']
- name: 'gcr.io/cloud-builders/gcloud'
  args: ['app', 'deploy', 'app-staging.yaml']</pre><p></p>
<h2>Cloud BuildでRSpecによる自動テストの動作確認</h2>
<h3>テスト失敗時</h3>
<p><img data-attachment-id="1758" data-permalink="https://mintaku-blog.net/cloudbuild-rspec/%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2020-12-04-11-47-03/" data-orig-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/66bc48dce88fffeb13e8b9e7468914af.png?fit=642%2C568&amp;ssl=1" data-orig-size="642,568" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="スクリーンショット 2020-12-04 11.47.03" data-image-description="" data-image-caption="" data-medium-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/66bc48dce88fffeb13e8b9e7468914af.png?fit=300%2C265&amp;ssl=1" data-large-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/66bc48dce88fffeb13e8b9e7468914af.png?fit=642%2C568&amp;ssl=1" loading="lazy" class="aligncenter size-full wp-image-1758" src="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/66bc48dce88fffeb13e8b9e7468914af.png?resize=642%2C568&#038;ssl=1" alt="" width="642" height="568" srcset="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/66bc48dce88fffeb13e8b9e7468914af.png?w=642&amp;ssl=1 642w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/66bc48dce88fffeb13e8b9e7468914af.png?resize=300%2C265&amp;ssl=1 300w" sizes="(max-width: 642px) 100vw, 642px" data-recalc-dims="1" /></p>
<p>テスト失敗した際の挙動を確認します。</p>
<p>テストが失敗するとCloud Buildのステップが停止し、デプロイされることはありません。</p>
<p>&nbsp;</p>
<h3>テスト成功時</h3>
<p><img data-attachment-id="1757" data-permalink="https://mintaku-blog.net/cloudbuild-rspec/%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2020-12-04-11-44-46/" data-orig-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/8598053bbfd3757798c574887266d953.png?fit=646%2C570&amp;ssl=1" data-orig-size="646,570" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="スクリーンショット 2020-12-04 11.44.46" data-image-description="" data-image-caption="" data-medium-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/8598053bbfd3757798c574887266d953.png?fit=300%2C265&amp;ssl=1" data-large-file="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/8598053bbfd3757798c574887266d953.png?fit=646%2C570&amp;ssl=1" loading="lazy" class="aligncenter size-full wp-image-1757" src="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/8598053bbfd3757798c574887266d953.png?resize=646%2C570&#038;ssl=1" alt="" width="646" height="570" srcset="https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/8598053bbfd3757798c574887266d953.png?w=646&amp;ssl=1 646w, https://i0.wp.com/mintaku-blog.net/mintaku/wp-content/uploads/2020/12/8598053bbfd3757798c574887266d953.png?resize=300%2C265&amp;ssl=1 300w" sizes="(max-width: 646px) 100vw, 646px" data-recalc-dims="1" /></p>
<p>テスト成功時の挙動を確認します。</p>
<p>テストが成功すると、そのままGAEへのデプロイが始まり、無事完了しました。</p>
<p>参考<br />

<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Farticle-ogp-background-afbab5eb44e0b055cce1258705637a91.png%3Fixlib%3Drb-4.0.0%26w%3D1200%26blend64%3DaHR0cHM6Ly9xaWl0YS11c2VyLXByb2ZpbGUtaW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRnFpaXRhLWltYWdlLXN0b3JlLnMzLmFtYXpvbmF3cy5jb20lMkYwJTJGOTI0NDMlMkZwcm9maWxlLWltYWdlcyUyRjE1MTUzMDE0MzA_aXhsaWI9cmItNC4wLjAmYXI9MSUzQTEmZml0PWNyb3AmbWFzaz1lbGxpcHNlJmJnPUZGRkZGRiZmbT1wbmczMiZzPTMxYTUxOTYwMDQ5NmZkODIxYzRmYWRkNjg0OWEzYjdh%26blend-x%3D120%26blend-y%3D467%26blend-w%3D82%26blend-h%3D82%26blend-mode%3Dnormal%26s%3D629b82b84ed85c57d016578c4f910980?ixlib=rb-4.0.0&amp;w=1200&amp;fm=jpg&amp;mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk2MCZoPTMyNCZ0eHQ9UmFpbHM1JUUzJTgxJUI4JUUzJTgxJUFFUnNwZWMlRTUlQjAlOEUlRTUlODUlQTUlRTMlODElOEIlRTMlODIlODklRTUlQUUlOUYlRTglQTElOEMlRTclQTIlQkElRTglQUElOEQlRTMlODElQkUlRTMlODElQTcmdHh0LWFsaWduPWxlZnQlMkN0b3AmdHh0LWNvbG9yPSUyMzFFMjEyMSZ0eHQtZm9udD1IaXJhZ2lubyUyMFNhbnMlMjBXNiZ0eHQtc2l6ZT01NiZ0eHQtcGFkPTAmcz0yYjIyZDUxZGZiODMxMjNiNzQ5YTdhYzNlYmRmMjc2YQ&amp;mark-x=120&amp;mark-y=112&amp;blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTgzOCZoPTU4JnR4dD0lNDByeW91emkmdHh0LWNvbG9yPSUyMzFFMjEyMSZ0eHQtZm9udD1IaXJhZ2lubyUyMFNhbnMlMjBXNiZ0eHQtc2l6ZT0zNiZ0eHQtcGFkPTAmcz05MzQ4MjVlNTA0ZjFjZmQ3YzVhODdiYTAwYTU1MzgwOQ&amp;blend-x=242&amp;blend-y=480&amp;blend-w=838&amp;blend-h=46&amp;blend-fit=crop&amp;blend-crop=left%2Cbottom&amp;blend-mode=normal&amp;s=8b94ca1a5b685ea586f15bb1cbf49af9" alt="">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://qiita.com/ryouzi/items/de7336e6175530723b30">Rails5へのRspec導入から実行確認まで #Ruby - Qiita</a>
			</p>
							<div class="ys-blog-card__dscr">
					株式会社TECH LUCKという会社で代表兼エンジニアをしている齊藤です。 DX&hellip;				</div>
										<div class="ys-blog-card__domain">qiita.com</div>
					</div>
	</div>
</div>
</p>

<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Farticle-ogp-background-afbab5eb44e0b055cce1258705637a91.png%3Fixlib%3Drb-4.0.0%26w%3D1200%26blend64%3DaHR0cHM6Ly9xaWl0YS11c2VyLXByb2ZpbGUtaW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRnFpaXRhLWltYWdlLXN0b3JlLnMzLmFtYXpvbmF3cy5jb20lMkYwJTJGOTIzNTklMkZwcm9maWxlLWltYWdlcyUyRjE1MTU0MDEwNTc_aXhsaWI9cmItNC4wLjAmYXI9MSUzQTEmZml0PWNyb3AmbWFzaz1lbGxpcHNlJmJnPUZGRkZGRiZmbT1wbmczMiZzPWJkMDA4YjE0MTgyNTBiZDZkYTI2MmIyMWU5Y2RiMWU1%26blend-x%3D120%26blend-y%3D462%26blend-w%3D90%26blend-h%3D90%26blend-mode%3Dnormal%26mark64%3DaHR0cHM6Ly9xaWl0YS1vcmdhbml6YXRpb24taW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRnMzLWFwLW5vcnRoZWFzdC0xLmFtYXpvbmF3cy5jb20lMkZxaWl0YS1vcmdhbml6YXRpb24taW1hZ2UlMkZiYjVjYzY5OWQzYTk1Y2NiOGQwMDYyNGY1ZDY5YjczNjE1ZTIxZWI4JTJGb3JpZ2luYWwuanBnJTNGMTUxMTE0NDk2OD9peGxpYj1yYi00LjAuMCZ3PTQ0Jmg9NDQmZml0PWNyb3AmbWFzaz1jb3JuZXJzJmNvcm5lci1yYWRpdXM9OCZiZz1GRkZGRkYmYm9yZGVyPTIlMkNGRkZGRkYmZm09cG5nMzImcz1hMzNiNDc5NTlkOTk3ZDg1ZmRkNGJjNmJlOGNhZDA0Yw%26mark-x%3D186%26mark-y%3D515%26mark-w%3D40%26mark-h%3D40%26s%3Dbd5952a35f6da25c79b93e05a964eb9f?ixlib=rb-4.0.0&amp;w=1200&amp;fm=jpg&amp;mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk2MCZoPTMyNCZ0eHQ9R29vZ2xlJTIwQ2xvdWQlMjBCdWlsZCVFMyU4MSVBN1JTcGVjJUUzJTgxJUFFJUUzJTgzJTg2JUUzJTgyJUI5JUUzJTgzJTg4JUUzJTgyJTkyJUU4JUExJThDJUUzJTgxJTg2JnR4dC1hbGlnbj1sZWZ0JTJDdG9wJnR4dC1jb2xvcj0lMjMxRTIxMjEmdHh0LWZvbnQ9SGlyYWdpbm8lMjBTYW5zJTIwVzYmdHh0LXNpemU9NTYmdHh0LXBhZD0wJnM9N2M2NmZjYTZjZjI5ZWE2MmEwNTc2MmU4MTk2YTFkMGQ&amp;mark-x=120&amp;mark-y=112&amp;blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTgzOCZoPTU4JnR4dD0lNDBlbnRhMDcwMSZ0eHQtY29sb3I9JTIzMUUyMTIxJnR4dC1mb250PUhpcmFnaW5vJTIwU2FucyUyMFc2JnR4dC1zaXplPTM2JnR4dC1wYWQ9MCZzPTA3Y2QyY2Q1ODg2ZWJjMmMzMGUzYzY0OTE5ZWMwZDQ4&amp;blend-x=242&amp;blend-y=454&amp;blend-w=838&amp;blend-h=46&amp;blend-fit=crop&amp;blend-crop=left%2Cbottom&amp;blend-mode=normal&amp;txt64=5bKp5omL55yM56uL5aSn5a2m&amp;txt-x=242&amp;txt-y=539&amp;txt-width=838&amp;txt-clip=end%2Cellipsis&amp;txt-color=%231E2121&amp;txt-font=Hiragino%20Sans%20W6&amp;txt-size=28&amp;s=97c7b1f9ccdd80567a11ca644127076e" alt="">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://qiita.com/enta0701/items/34e896792f5533a8275c">Google Cloud BuildでRSpecのテストを行う #Rails - Qiita</a>
			</p>
							<div class="ys-blog-card__dscr">
					先日、Google Cloud BuildとGithubの連携が発表され、CIの&hellip;				</div>
										<div class="ys-blog-card__domain">qiita.com</div>
					</div>
	</div>
</div>The post <a href="https://mintaku-blog.net/cloudbuild-rspec/">Cloud BuildにRSpecを組み込んでCI/CDパイプラインを構築する</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/cloudbuild-rspec/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1755</post-id>	</item>
		<item>
		<title>【Rails】Google Analytics Reporting APIでレポートデータ取得する方法</title>
		<link>https://mintaku-blog.net/rails-ga/</link>
					<comments>https://mintaku-blog.net/rails-ga/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Sat, 05 Sep 2020 03:03:43 +0000</pubDate>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[GCP]]></category>
		<category><![CDATA[その他]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1558</guid>

					<description><![CDATA[<p>RailsでGoogle Analytics Reporting APIでレポートデータ取得する方法をまとめました。レポートデータ取得処理を独自クラス化し、 …</p>
The post <a href="https://mintaku-blog.net/rails-ga/">【Rails】Google Analytics Reporting APIでレポートデータ取得する方法</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>RailsでGoogle Analytics Reporting APIでレポートデータ取得する方法をまとめました。レポートデータ取得処理を独自クラス化し、JSON形式のレスポンスで返すところまでを紹介しています。</p>
<h2>Google Analytics Reporting APIの設定準備</h2>
<p>Google Analytics Reporting APIの設定は以下の記事を参考にしました。</p>
<ul>
<li>プロジェクトの作成</li>
<li>サービスアカウントの作成</li>
<li>秘密鍵のJSONファイルを生成 ← あとで使います</li>
<li>Googleアナリティクスにユーザ追加</li>
<li>ビューIDの確認 ← あとで使います</li>
</ul>
<p>参考：<a href="https://qiita.com/ryota-sasabe/items/a5efd2aac244cfcce5c7" target="_blank" rel="noopener noreferrer">Google Analytics API を叩いてデータを取得するまでの流れ（Ruby）</a></p>
<p>&nbsp;</p>
<h2>RailsでGoogleアナリティクスレポートデータ取得処理をクラス化</h2>
<p>Googleアナリティクスレポートデータ取得処理を独自クラス化し、コントローラで使えるようにします。</p>
<p>先程生成した秘密鍵のJSONファイルは「analytics_auth.json」として配置し、authメソッドでGoogleアナリティクスAPIに認証を行ってからレポートデータを取得しにいきます。</p>
<p>report_pv_count_by_dateメソッドで、取得開始日と取得終了日を引数(デフォルトは当日)で指定してその期間のレポートデータを取得できるようにします。</p>
<p>・lib/analytics.rb</p><pre class="crayon-plain-tag">require 'google/apis/analyticsreporting_v4'

class Analytics

  # レポート対象を指定してオブジェクトを生成
  # @param [String] base_url レポート対象サイトのURL
  # @param [String] view_id ビューID
  def initialize(base_url, view_id)
    @base_url = base_url
    @view_id = view_id
    @analytics = Google::Apis::AnalyticsreportingV4
    auth
  end

  # 指定した期間のページごとのPVを集計
  # @param [String] date 日付を表す文字列
  # @return [Hash] 累計PV及びページごとのPV
  def report_pv_count_by_date(start_date = 'today', end_date = 'today')
    date_range = @analytics::DateRange.new(start_date: start_date, end_date: end_date)
    metric = @analytics::Metric.new(expression: 'ga:users', alias: 'users')
    dimension = @analytics::Dimension.new(name: 'ga:pagePath')
    order_by = @analytics::OrderBy.new(field_name: 'ga:users', sort_order: 'DESCENDING')
    request = @analytics::GetReportsRequest.new(
      report_requests: [@analytics::ReportRequest.new(
        view_id: @view_id,
        metrics: [metric],
        date_ranges: [date_range],
        dimensions: [dimension],
        order_bys: [order_by],
      )]
    )
    response = @client.batch_get_reports(request)
    data = response.reports.first.data
    return {
      total: data.totals.first.values.first,
      pages: data.rows.map do |row|
        {
          url: @base_url + row.dimensions.first,
          dir: row.dimensions.first,
          views: row.metrics.first.values.first
        }
      end
    }
  end

  private

  # GoogleアナリティクスAPIに認証
  # 同ディレクトリにanalytics_auth.jsonを配置
  def auth
    scope = ['https://www.googleapis.com/auth/analytics.readonly']
    @client = @analytics::AnalyticsReportingService.new
    @client.authorization = Google::Auth::ServiceAccountCredentials.make_creds(
      json_key_io: File.open('analytics_auth.json'),
      scope: scope
    )
  end

end</pre><p>&nbsp;</p>
<p>独自クラスを作成したら、呼び出せるようにapplication.rbに以下を追記します。</p>
<p>・config/application.rb</p><pre class="crayon-plain-tag">require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module AppName
  class Application &lt; Rails::Application

    ...

    # 独自クラスパス指定
    config.autoload_paths += %W(#{config.root}/lib) ←追加

    ...

  end
end</pre><p>&nbsp;</p>
<p>uninitialized constantエラーで定義したクラスを読むことができない場合は、config/initializersディレクトリ配下にrequirements.rbファイルを作成し、以下を追記してください。</p>
<p>・config/initializers/requirements.rb</p><pre class="crayon-plain-tag">require Rails.root.join('lib/analytics.rb')</pre><p>&nbsp;</p>
<h2>クラス化したGoogleアナリティクスレポートデータ取得メソッド呼び出し</h2>
<p>実際にコントローラで呼び出してみます。初期化のBASE_URLとVIEW_IDは環境変数として.envに設定し呼び出して使うようにします。</p>
<p>フロントからパラメータ(取得開始日・取得終了日)をPOSTし、それらを引数にして実行しています。レスポンスはJSON形式で返すようにしています。</p>
<p>あとはroutes.rbにルーティングを設定して完了です。</p>
<p>・app/controllers/api/analytics_controller.rb</p><pre class="crayon-plain-tag">class Api::AnalyticsController &lt; Api::ApplicationController

  def report
    analytics = ::Analytics.new(ENV['BASE_URL'], ENV['VIEW_ID'])

    render json: analytics.report_pv_count_by_date(params[:start_date], params[:end_date])
  end

end</pre><p>以上、RailsでGoogle Analytics Reporting APIでレポートデータ取得する方法でした。</p>The post <a href="https://mintaku-blog.net/rails-ga/">【Rails】Google Analytics Reporting APIでレポートデータ取得する方法</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/rails-ga/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1558</post-id>	</item>
		<item>
		<title>【GCP】GAEでRailsのステージング環境と本番環境を構築する方法</title>
		<link>https://mintaku-blog.net/gae-rails-env/</link>
					<comments>https://mintaku-blog.net/gae-rails-env/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Mon, 20 Apr 2020 14:08:00 +0000</pubDate>
				<category><![CDATA[GCP]]></category>
		<category><![CDATA[Rails]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1426</guid>

					<description><![CDATA[<p>Google App EngineでRailsのステージング環境と本番環境を構築したので、その方法をメモしておきます。 database.ymlの編集 da …</p>
The post <a href="https://mintaku-blog.net/gae-rails-env/">【GCP】GAEでRailsのステージング環境と本番環境を構築する方法</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>Google App EngineでRailsのステージング環境と本番環境を構築したので、その方法をメモしておきます。</p>
<h2>database.ymlの編集</h2>
<p>database.ymlにステージング環境用と本番環境用のDB情報をそれぞれ追加します。</p>
<p>・database.yml</p><pre class="crayon-plain-tag">...

staging:
  adapter: mysql2
  encoding: utf8
  pool: 5
  timeout: 5000
  username: sample_stg
  password: sample_stg_pass
  database: sample_stg
  socket: /cloudsql/sample_stg:asia-northeast1:sample_stg

production:
  adapter: mysql2
  encoding: utf8
  pool: 5
  timeout: 5000
  username: sample_prod
  password: sample_prod_pass
  database: sample_prod
  socket: /cloudsql/sample_prod:asia-northeast1:sample_prod

...</pre><p>&nbsp;</p>
<h2>app.yamlの編集</h2>
<p>GAEにデプロイするステージング環境と本番環境のapp.yamlファイルを作成します。</p>
<p>今回はGAEのスタンダード環境で構築しています。「RAILS_ENV=」で環境を指定します。</p>
<p>また、env_variablesで環境変数を指定することもできます。</p>
<p>・app-staging.yaml</p><pre class="crayon-plain-tag">service: api--staging
runtime: ruby25
entrypoint: RAILS_ENV=staging bundle exec rails s -e staging
env_variables:
  SAMPLE_API_KEY: 'SAMPLE_STAGING_API_KEY'</pre><p>&nbsp;</p>
<p>・app-production.yaml</p><pre class="crayon-plain-tag">service: api--production
runtime: ruby25
entrypoint: RAILS_ENV=production bundle exec rails s -e production
env_variables:
  SAMPLE_API_KEY: 'SAMPLE_PRODUCTION_API_KEY'</pre><p>&nbsp;</p>
<h2>dispatch.yamlの作成</h2>
<p>dispatch.yamlを作成し、ドメインとサービスを紐づけます。これでGAEのサービスにデプロイした際に、指定したURLに割り当てることができます。</p>
<p>・dispatch.yaml</p><pre class="crayon-plain-tag">dispatch:
- url: "*stg.sample.com/*"
service: api--staging
- url: "*sample.com/*"
service: api--production</pre><p>dispatch.yamlが作成できたら、以下のコマンドでデプロイします。</p><pre class="crayon-plain-tag">$ gcloud app deploy dispatch.yaml</pre><p>&nbsp;</p>
<h2>Railsプロジェクトをステージング環境と本番環境にそれぞれデプロイ</h2>
<p>ここまで準備が完了したら、実際にRailsプロジェクトをそれぞれの環境にデプロイします。</p>
<h3>■ステージング環境</h3>
<p>デプロイする前にRailsアセットをコンパイルします。</p><pre class="crayon-plain-tag">$ RAILS_ENV=staging bundle exec rails assets:precompile</pre><p>app-staging.yamlを指定してデプロイします。</p><pre class="crayon-plain-tag">$ gcloud app deploy app-staging.yaml</pre><p>&nbsp;</p>
<p>■本番環境</p>
<p>ステージング環境と同様、デプロイする前にコンパイルします。</p><pre class="crayon-plain-tag">$ RAILS_ENV=production bundle exec rails assets:precompile</pre><p>app-production.yamlを指定してデプロイします。</p><pre class="crayon-plain-tag">$ gcloud app deploy app-production.yaml</pre><p>デプロイする際に、target serviceがそれぞれの環境が選択できているか確認しておきましょう。</p><pre class="crayon-plain-tag">target service: [api--staging]</pre><p>&nbsp;</p>
<h2>マイグレーション・シードの実行</h2>
<p>マイグレーションとシードもそれぞれの環境に合わせて行いますappengineを使ってマイグレーションを実行します。</p>
<p>シード処理は時間がかかることがあるので、デフォルトの10分を30分まで伸ばしています。</p>
<h3>■ステージング環境</h3>
<p>マイグレーション処理</p><pre class="crayon-plain-tag">$ bundle exec rake appengine:exec GAE_SERVICE=api--staging -- bundle exec rails db:migrate RAILS_ENV=staging</pre><p>シード処理</p><pre class="crayon-plain-tag">$ bundle exec rake appengine:exec GAE_SERVICE=api--staging GAE_TIMEOUT=30m -- bundle exec rails db:seed RAILS_ENV=staging</pre><p>&nbsp;</p>
<h3>■本番環境</h3>
<p>マイグレーション処理</p><pre class="crayon-plain-tag">$ bundle exec rake appengine:exec GAE_SERVICE=api--production -- bundle exec rails db:migrate RAILS_ENV=production</pre><p>シード処理</p><pre class="crayon-plain-tag">$ bundle exec rake appengine:exec GAE_SERVICE=api--production GAE_TIMEOUT=30m -- bundle exec rails db:seed RAILS_ENV=production</pre><p>&nbsp;</p>
<h2>① ハマりポイント「Errno::EROFS: Read-only file system @ rb_sysopen &#8211; /srv/db/schema.rb」</h2>
<p>追加のマイグレーションファイルを作成し、実行するときにエラー発生。schema.rbが読み取り専用のため(スタンダード環境だから？)、実行できずにハマる。。</p><pre class="crayon-plain-tag">$ bundle exec rake appengine:exec GAE_SERVICE=api--staging -- bundle exec rails db:migrate</pre><p></p><pre class="crayon-plain-tag">[STDERR] rails aborted!
[STDERR] Errno::EROFS: Read-only file system @ rb_sysopen - /srv/db/schema.rb</pre><p>&nbsp;</p>
<p>解決策として、作成したマイグレーションファイルのバージョンを指定することで実行できました。</p><pre class="crayon-plain-tag">$ bundle exec rake appengine:exec GAE_SERVICE=api--staging -- bundle exec rails db:migrate:up VERSION=20200401000000</pre><p></p><pre class="crayon-plain-tag">[STDOUT] == 20200401000000 AddColumnTests: migrating ===================
[STDOUT] -- add_column(:tests, :test_id, :string, {:null=&gt;true, :after=&gt;:sample_id})
[STDOUT] -&gt; 0.0461s
[STDOUT] == 20200401000000 AddColumnTests: migrated (0.0462s) ==========
[STDOUT]

EXIT STATUS: 0</pre><p>&nbsp;</p>
<h2>②ハマりポイント「Cannot run migrations because another migration process is currently running.」</h2>
<p>マイグレーション対象のDBに接続した状態で、実行するとエラー発生。</p><pre class="crayon-plain-tag">[STDERR] rails aborted!
[STDERR] ActiveRecord::ConcurrentMigrationError:
[STDERR]
[STDERR] Cannot run migrations because another migration process is currently running.</pre><p>&nbsp;</p>
<p>DBにログインし、以下のコマンドで処理中のプロセスの一覧を表示し、該当のプロセスを終了させて再度実行すれば通ります。</p><pre class="crayon-plain-tag">$ SHOW PROCESSLIST</pre><p>&nbsp;</p>
<p>以上、GAEでRailsのステージング環境と本番環境を構築する方法を紹介しました。</p>The post <a href="https://mintaku-blog.net/gae-rails-env/">【GCP】GAEでRailsのステージング環境と本番環境を構築する方法</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/gae-rails-env/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1426</post-id>	</item>
		<item>
		<title>【Rails】APIにCORSの設定をする方法</title>
		<link>https://mintaku-blog.net/rails-cors/</link>
					<comments>https://mintaku-blog.net/rails-cors/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Mon, 09 Mar 2020 14:17:29 +0000</pubDate>
				<category><![CDATA[Rails]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1371</guid>

					<description><![CDATA[<p>RailsのAPIでCORSの設定をする方法をメモしておきます。gemを使うことでサクッと実装できました。 CORS(Cross-Origin Resour …</p>
The post <a href="https://mintaku-blog.net/rails-cors/">【Rails】APIにCORSの設定をする方法</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>RailsのAPIでCORSの設定をする方法をメモしておきます。gemを使うことでサクッと実装できました。</p>
<h2>CORS(Cross-Origin Resource Sharing)とは</h2>
<p>CORSとは、クライアントが自分と違うオリジンをもつサーバーからのアクセスを許可する仕組みのことです。</p>
<p>WebクライアントとAPIを分ける場合などで、異なるオリジン間の通信を行うためにはCORSの設定をする必要があります。</p>
<p>以下のページに詳しく書かれています。</p>
<p>参考：<a href="https://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34" target="_blank" rel="noopener noreferrer">CORSまとめ</a></p>
<p>※オリジン(ex. http://www.example.com:3000)はスキーム「http://」、ホスト「www.example.com」、ポート「3000」の組み合わせからなります</p>
<p>&nbsp;</p>
<h2>「rack-cors」gemの追加</h2>
<p>「rack-cors」gemを追加します。</p>
<p>参考：<a href="https://github.com/cyu/rack-cors" target="_blank" rel="noopener noreferrer">rack-cors</a></p>
<p>・Gemfile</p><pre class="crayon-plain-tag">gem 'rack-cors'</pre><p>&nbsp;</p>
<h2>CORSの設定</h2>
<p>application.rbにCORSの設定をしていきます。</p>
<p>・config/application.rb</p><pre class="crayon-plain-tag">config.middleware.insert_before 0, Rack::Cors do
    allow do
        origins "*"
        resource "*",
        headers: :any,
        methods: [:get, :post, :options, :head]
    end
end</pre><p>「*」の場合は全て許可することを意味し、以下の通りに設定していきます。</p>
<ul>
<li>origins (文字列 or 正規表現) : どのオリジンを許可するか(APIを叩く側)</li>
<li>resource (文字列 or 正規表現) : 許可したいリソースファイル</li>
<li>methods (文字列 or Array or :any) : 許可したいHTTPメソッド</li>
<li>headers (文字列 or Array or :any) : 許可したいHTTPヘッダー</li>
</ul>
<p>実際に設定していくとはのような感じになりました。</p>
<p>・config/application.rb</p><pre class="crayon-plain-tag">config.middleware.insert_before 0, Rack::Cors do
    allow do
        origins "http://localhost:8000", "https://example.jp"
        resource "*",
        headers: :any,
        methods: [:get, :post, :patch, :delete, :options, :head]
    end
end</pre><p>&nbsp;</p>
<h2>CORSの設定が反映されているか確認</h2>
<p>以下のコマンドで再起動し、CORSの設定が反映されているか確認します。</p><pre class="crayon-plain-tag">$ docker-compose restart</pre><p>&nbsp;</p>
<p>「http://localhost:5000」からAPI(http://localhost:7000)にアクセスした時のレスポンスを見てみます。実際にアクセスするとコンソールに以下のようなエラーメッセージが表示され、アクセスできないことが確認できました。</p><pre class="crayon-plain-tag">Access to XMLHttpRequest at 'http://localhost:7000/api/tests/' from origin 'http://localhost:5000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.</pre><p>&nbsp;</p>
<p>次にCORSで設定した「http://localhost:8000」でアクセスしてみます。NetworkのAPIのResponse Headersに以下のレスポンスが帰ってき、アクセスできることが確認できます。</p><pre class="crayon-plain-tag">Response Headers
    Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS, HEAD
    Access-Control-Allow-Origin: http://localhost:8000
    Access-Control-Expose-Headers:</pre><p>以上、RailsのAPIでCORSの設定をする方法を紹介しました。</p>The post <a href="https://mintaku-blog.net/rails-cors/">【Rails】APIにCORSの設定をする方法</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/rails-cors/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1371</post-id>	</item>
		<item>
		<title>Google App EngineのRuby 2.5 StandardでRails環境構築</title>
		<link>https://mintaku-blog.net/gae-rails/</link>
					<comments>https://mintaku-blog.net/gae-rails/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Wed, 19 Feb 2020 13:08:38 +0000</pubDate>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[GCP]]></category>
		<category><![CDATA[環境構築]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1338</guid>

					<description><![CDATA[<p>Google App EngineのRuby 2.5 StandardでRails環境構築する方法とCloud SQLのMySQLとの接続方法について紹介し …</p>
The post <a href="https://mintaku-blog.net/gae-rails/">Google App EngineのRuby 2.5 StandardでRails環境構築</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>Google App EngineのRuby 2.5 StandardでRails環境構築する方法とCloud SQLのMySQLとの接続方法について紹介します。</p>
<p>基本的には以下の公式を参考に環境構築しています。</p>
<ul>
<li><a href="https://cloud.google.com/ruby/rails/appengine?hl=ja" target="_blank" rel="noopener noreferrer">Running Rails 5 on App Engine flexible environment | Ruby</a></li>
<li><a href="https://cloud.google.com/ruby/rails/using-cloudsql-mysql?hl=ja" target="_blank" rel="noopener noreferrer">Using Cloud SQL for MySQL with Rails 5 | Ruby</a></li>
</ul>
<h2>前提条件</h2>
<p>デプロイするRailsプロジェクトが以下に当てはまっていることが前提条件です。</p>
<ul>
<li>Ruby バージョン2.3.4以降をインストール</li>
<li>Rails 5 gem をインストール</li>
<li>Bundler gem をインストール</li>
</ul>
<p>&nbsp;</p>
<h2>必要なAPIを有効化</h2>
<p>データストア、Pub / Sub、Cloud Storage JSON、Stackdriver Logging、およびGoogle+必要なAPIを有効にします。</p>

<div class="ys-blog-card">
	<div class="ys-blog-card__container">
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://console.cloud.google.com/flows/enableapi?apiid=datastore.googleapis.com,pubsub,storage_api,logging,plus&_ga=2.48516846.936357722.1581502708-977931022.1573203289">Google Cloud Platform</a>
			</p>
										<div class="ys-blog-card__domain">console.cloud.google.com</div>
					</div>
	</div>
</div>

<p>&nbsp;</p>
<h2>app.yamlの作成</h2>
<p>GAEにデプロイするRailsアプリのディレクトリ直下にapp.yamlを作成します。</p>
<p>今回はスタンダード環境なので「env: flex」はコメントアウトしておきます。またapp.yamlに環境変数を書くことでGAEに環境変数を渡せます。</p>
<p>secret keyは以下のコマンドで確認できます。</p><pre class="crayon-plain-tag">$ bundle exec rails secret</pre><p>・app.yaml</p><pre class="crayon-plain-tag">entrypoint: bundle exec rackup --port $PORT
runtime: ruby25
# env: flex
env_variables:
    SECRET_KEY_BASE: &lt; Your secret key&gt;</pre><p>GAEにデプロイしたアプリはDocker上で動くためDockerfileがプロジェクト直下にあるとruntime rubyでデプロイができません。</p>
<p>runtime rubyでデプロイする場合は、DockerfileをDockerfile-devなどに置き換えておきましょう。</p>
<p>&nbsp;</p>
<h2>.gcloudignoreの作成</h2>
<p>デプロイしないファイルを指定するために.gcloudignoreファイルを作成します。</p>
<p>・.gcloudignore</p><pre class="crayon-plain-tag">.gcloudignore
#!include:.gitignore
.git
.gitignore

!/public/assets</pre><p>&nbsp;</p>
<h2>MySQL用Cloud SQLをセットアップ</h2>
<p>RailsアプリでCloud SQLを使用するために、mysql2とappenginegemをGemfileファイルに追加します</p><pre class="crayon-plain-tag">$ bundle add mysql2
$ bundle add appengine</pre><p>以下のコマンドでインスタンス接続名を取得できます。<br />
gcloud sql instances describe &lt; インスタンス名 &gt;</p>
<p>・database.yml</p><pre class="crayon-plain-tag">production:
    adapter: mysql2
    encoding: utf8
    pool: 5
    timeout: 5000
    username: &lt; username &gt;
    password: &lt; password &gt;
    database: &lt; database &gt;
    socket: /cloudsql/&lt; インスタンス接続名 &gt;</pre><p>&nbsp;</p>
<h2>GAEにデプロイ</h2>
<p>以下のコマンドで、GAEアプリを作成します。</p><pre class="crayon-plain-tag">$ gcloud app create</pre><p>デプロイする前に、Railsアセットをプリコンパイルします。</p><pre class="crayon-plain-tag">$ bundle exec bin/rails assets:precompile</pre><p>アセットのコンパイルが完了したらGAEにデプロイします。</p><pre class="crayon-plain-tag">$ gcloud app deploy</pre><p>&nbsp;</p>
<h2>マイグレーションでテーブル作成</h2>
<p>以下のコマンドでプロジェクトを一覧表示できます。アプリのデプロイに使用するプロジェクトを見つけ、プロジェクト番号をコピーします。</p><pre class="crayon-plain-tag">$ gcloud projects list</pre><p>roles/editorデータベース移行を実行するロールのプロジェクトIAMポリシーに新しいメンバーを追加します。</p><pre class="crayon-plain-tag">$ gcloud projects add-iam-policy-binding &lt; YOUR-PROJECT-ID &gt; --member=serviceAccount: &lt; PROJECT_NUMBER &gt; @cloudbuild.gserviceaccount.com --role=roles/editor</pre><p>先ほど追加したappengineを使用して、Cloud SQL上でのマイグレーションを実行します。</p><pre class="crayon-plain-tag">$ bundle exec rake appengine:exec -- bundle exec rake db:migrate</pre><p>Cloud SQL画面の「Cloud Shellを使用して接続」から以下のコマンドで接続し、実際にテーブルが作成されたか確認してみましょう。</p><pre class="crayon-plain-tag">$ gcloud sql connect &lt; インスタンス名 &gt; --user=&lt; username &gt; --quiet</pre><p>&nbsp;</p>
<h2>以降のデプロイ</h2>
<p>以降、ソースを更新して再度デプロイする場合は以下のコマンドで実行する流れになります。</p><pre class="crayon-plain-tag">$ bundle exec bin/rails assets:precompile
$ gcloud app deploy
$ bundle exec rake appengine:exec -- bundle exec rake db:migrate</pre><p>&nbsp;</p>
<h2>ハマりポイント</h2>
<h3>① コンパイル時にエラー</h3>
<p>コンパイル時に以下のエラーが発生。</p><pre class="crayon-plain-tag">Note: Google::Cloud::Logging is disabled because it failed to authorize with the service.
Note: Google::Cloud::Debugger is disabled because it failed to authorize with the service.
Note: Google::Cloud::ErrorReporting is disabled because it failed to authorize with the service.
Note: Google::Cloud::Trace is disabled because it failed to authorize with the service.</pre><p>develop環境では使用しないようfalseにすることで解決しました。</p>
<p>・development.rb</p><pre class="crayon-plain-tag">config.google_cloud.use_logging = false
config.google_cloud.use_debugger = false
config.google_cloud.use_error_reporting = false
config.google_cloud.use_trace = false</pre><p>&nbsp;</p>
<h3>② MySQLに接続できない</h3>
<p>以下のエラーによりCloud SQLのMySQLに接続できずハマり。。</p><pre class="crayon-plain-tag">[STDERR] rake aborted!
[STDERR] Mysql2::Error::ConnectionError: Unknown MySQL server host '/cloudsql/&lt; インスタンス接続名 &gt;' (2)</pre><p>公式ではdatabase.yamlの「/cloudsql/&lt; インスタンス接続名 &gt;」の箇所がhostとなっていたため、hostで書いていたのですが、socketに変更することで解決しました。。</p>
<p>参考：</p>

<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://opengraph.githubassets.com/124e3ac85ab876ecb3ee9a13f260488c6765a97302a572b589bb25a41b6144ed/GoogleCloudPlatform/ruby-docs-samples" alt="">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://github.com/GoogleCloudPlatform/ruby-docs-samples/blob/0a6e898a1acd140ca04c63c7b68da1468acb4082/appengine/rails-cloudsql-postgres/config/database.yml">ruby-docs-samples/appengine/rails-cloudsql-postgres/config/database.yml at 0a6e898a1acd140ca04c63c7b68da1468acb4082 · GoogleCloudPlatform/ruby-docs-samples · GitHub</a>
			</p>
							<div class="ys-blog-card__dscr">
					Ruby samples for Google Cloud Platform p&hellip;				</div>
										<div class="ys-blog-card__domain">github.com</div>
					</div>
	</div>
</div>

<p>・修正前</p><pre class="crayon-plain-tag">production:
    adapter: mysql2
    encoding: utf8
    pool: 5
    timeout: 5000
    username: &lt; username &gt;
    password: &lt; password &gt;
    database: &lt; database &gt;
    host: /cloudsql/&lt; インスタンス接続名 &gt;</pre><p>・修正後</p><pre class="crayon-plain-tag">production:
    adapter: mysql2
    encoding: utf8
    pool: 5
    timeout: 5000
    username: &lt; username &gt;
    password: &lt; password &gt;
    database: &lt; database &gt;
    socket: /cloudsql/&lt; インスタンス接続名 &gt;</pre><p>&nbsp;</p>
<h3>③ GAEのデフォルトTime Outが10分なためSeedが完了しない</h3>
<p>GAEのデフォルトタイムアウトが10mのため、Seedが完了せず途中で終了してしまう。。</p><pre class="crayon-plain-tag">TIMEOUT: 10m</pre><p>GAE_TIMEOUTオプションでタイムアウトを伸ばすことができました。1h23m45sの形式で指定できます。</p><pre class="crayon-plain-tag">bundle exec rake appengine:exec GAE_TIMEOUT=30m -- bundle exec rake db:seed</pre><p>参考：</p>

<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://opengraph.githubassets.com/52b5158113b4bc826f0dc7f99a3c78803f2eb2dc31ffe6537b7bd59dcc946bd6/GoogleCloudPlatform/appengine-ruby/issues/26" alt="">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://github.com/GoogleCloudPlatform/appengine-ruby/issues/26">How to set the value of timeout? · Issue #26 · GoogleCloudPlatform/appengine-ruby · GitHub</a>
			</p>
							<div class="ys-blog-card__dscr">
					Could not find this documented anywhere &hellip;				</div>
										<div class="ys-blog-card__domain">github.com</div>
					</div>
	</div>
</div>

<p>以上、Google App EngineのRuby 2.5 StandardでRails環境構築する方法とCloud SQLのMySQLとの接続方法でした。</p>The post <a href="https://mintaku-blog.net/gae-rails/">Google App EngineのRuby 2.5 StandardでRails環境構築</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/gae-rails/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1338</post-id>	</item>
		<item>
		<title>【Rails】鉄道会社・路線・駅データをseedで取得・インポートする</title>
		<link>https://mintaku-blog.net/rails-station/</link>
					<comments>https://mintaku-blog.net/rails-station/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Sun, 02 Feb 2020 11:29:53 +0000</pubDate>
				<category><![CDATA[Rails]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1325</guid>

					<description><![CDATA[<p>Railsで鉄道会社・路線・駅のCSVファイルからデータを挿入する方法を紹介します。 駅データ.jpで鉄道会社・路線・駅データをダウンロードし、Railsプ …</p>
The post <a href="https://mintaku-blog.net/rails-station/">【Rails】鉄道会社・路線・駅データをseedで取得・インポートする</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>Railsで鉄道会社・路線・駅のCSVファイルからデータを挿入する方法を紹介します。</p>
<h2>駅データ.jpで鉄道会社・路線・駅データをダウンロードし、Railsプロジェクトに格納</h2>
<p>駅データ.jpで鉄道会社・路線・駅データをダウンロードします。ダウンロードするためには会員登録が必要です。</p>
<p>ダウンロードしたCSVファイルをdb/data配下に格納しておきます。</p>
<ul>
<li>db/data/companies.csv (鉄道会社データ)</li>
<li>db/data/lines.csv (路線データ)</li>
<li>db/data/stations.csv (駅データ)</li>
</ul>
<p><span style="font-size: 8px;">駅関連データ提供元：駅データ 無料ダウンロード 『<a href="https://www.ekidata.jp/" target="_blank" rel="noopener noreferrer">駅データ.jp</a>』</span></p>
<p>&nbsp;</p>
<h2>鉄道会社・路線・駅のマイグレーション作成</h2>
<p>まずは、鉄道会社・路線・駅のマイグレーションファイルを作成します。</p>
<p>・db/migrate/20200101000001_create_companies.rb</p><pre class="crayon-plain-tag">class CreateCompanies &lt; ActiveRecord::Migration[5.2]
    def change
        create_table :companies, id: false, options: 'ENGINE=InnoDB COLLATE=utf8_general_ci' do |t|
            t.integer :company_id, null: false, unique: true
            t.string :name
            t.string :kana
            t.string :formal_name
            t.string :abbreviation_name

            t.datetime :created_at
            t.datetime :updated_at
            t.datetime :deleted_at
        end
    end
end</pre><p>&nbsp;</p>
<p>・db/migrate/20200101000002_create_lines.rb</p><pre class="crayon-plain-tag">class CreateLines &lt; ActiveRecord::Migration[5.2]
    def change
        create_table :lines, id: false, options: 'ENGINE=InnoDB COLLATE=utf8_general_ci' do |t|
            t.integer :line_id, null: false, unique: true
            t.integer :company_id
            t.string :name
            t.string :kana
            t.decimal :lat, :precision =&gt; 9, :scale =&gt; 6, null: true
            t.decimal :lng, :precision =&gt; 9, :scale =&gt; 6, null: true

            t.datetime :created_at
            t.datetime :updated_at
            t.datetime :deleted_at
        end
    end
end</pre><p>&nbsp;</p>
<p>・db/migrate/20200101000003_create_stations.rb</p><pre class="crayon-plain-tag">class CreateStations &lt; ActiveRecord::Migration[5.2]
    def change
        create_table :stations, id: false, options: 'ENGINE=InnoDB COLLATE=utf8_general_ci' do |t|
            t.integer :station_id, null: false, unique: true
            t.integer :line_id
            t.string :name
            t.string :address
            t.decimal :lat, :precision =&gt; 9, :scale =&gt; 6, null: true
            t.decimal :lng, :precision =&gt; 9, :scale =&gt; 6, null: true

            t.datetime :created_at
            t.datetime :updated_at
            t.datetime :deleted_at
        end
    end
end</pre><p>&nbsp;</p>
<h2>鉄道会社・路線・駅のモデル作成</h2>
<p>鉄道会社・路線・駅のモデル作成を作成します。各々のリレーションもはっておきます。</p>
<p>・app/models/company.rb</p><pre class="crayon-plain-tag">class Company &lt; ApplicationRecord

    ##
    # リレーション
    ##
    has_many :lines

end</pre><p>&nbsp;</p>
<p>・app/models/line.rb</p><pre class="crayon-plain-tag">class Line &lt; ApplicationRecord

    ##
    # リレーション
    ##
    belongs_to :company
    has_many :stations

end</pre><p>&nbsp;</p>
<p>・app/models/station.rb</p><pre class="crayon-plain-tag">class Station &lt; ApplicationRecord

    ##
    # リレーション
    ##
    belongs_to :line

end</pre><p>&nbsp;</p>
<h2>seed.rbで鉄道会社・路線・駅データを取得し、テーブルに保存</h2>
<p>先ほど保存したCSVファイルから必要なカラムのデータを取得していきます。鉄道会社データから駅データを取得していき、完了です。</p>
<p>・db/seeds.rb</p><pre class="crayon-plain-tag">require 'csv'

# 鉄道会社CSVファイルパス
COMPANY_PATH = "db/data/companies.csv"

# 鉄道会社カラム指定定数
COMPANY_CSVROW_COMPANY_ID = 0
COMPANY_CSVROW_NAME = 2
COMPANY_CSVROW_KANA = 3
COMPANY_CSVROW_FORMAL_NAME = 4
COMPANY_CSVROW_ABBREVIATION_NAME = 5

# 鉄道会社CSVを読み込みテーブルに保存
CSV.foreach(COMPANY_PATH) do |row|
    company_id = row[COMPANY_CSVROW_COMPANY_ID]
    name = row[COMPANY_CSVROW_NAME]
    kana = row[COMPANY_CSVROW_KANA]
    formal_name = row[COMPANY_CSVROW_FORMAL_NAME]
    abbreviation_name = row[COMPANY_CSVROW_ABBREVIATION_NAME]
    Company.find_or_create_by(
        :company_id =&gt; company_id,
        :name =&gt; name, :kana =&gt; kana,
        :formal_name =&gt; formal_name,
        :abbreviation_name =&gt; abbreviation_name
    )
end

# 路線CSVファイルパス
LINE_PATH = "db/data/lines.csv"

# 路線カラム指定定数
LINE_CSVROW_LINE_ID = 0
LINE_CSVROW_COMPANY_ID = 1
LINE_CSVROW_NAME = 2
LINE_CSVROW_KANA = 3
LINE_CSVROW_LNG = 8
LINE_CSVROW_LAT = 9

# 路線CSVを読み込みテーブルに保存
CSV.foreach(LINE_PATH) do |row|
    line_id = row[LINE_CSVROW_LINE_ID]
    company_id = row[LINE_CSVROW_COMPANY_ID]
    name = row[LINE_CSVROW_NAME]
    kana = row[LINE_CSVROW_KANA]
    lng = row[LINE_CSVROW_LNG]
    lat = row[LINE_CSVROW_LAT]

    Line.find_or_create_by(
        :line_id =&gt; line_id,
        :company_id =&gt; company_id,
        :name =&gt; name,
        :kana =&gt; kana,
        :lat =&gt; lat,
        :lng =&gt; lng
    )
end

# 駅CSVファイルパス
STATION_PATH = "db/data/stations.csv"

# 駅カラム指定定数
STATION_CSVROW_STATION_ID = 0
STATION_CSVROW_NAME = 2
STATION_CSVROW_LINE_ID = 5
STATION_CSVROW_ADDRESS = 8
STATION_CSVROW_LNG = 9
STATION_CSVROW_LAT = 10

# 駅CSVを読み込みテーブルに保存
CSV.foreach(STATION_PATH) do |row|
    station_id = row[STATION_CSVROW_STATION_ID]
    name = row[STATION_CSVROW_NAME]
    line_id = row[STATION_CSVROW_LINE_ID]
    address = row[STATION_CSVROW_ADDRESS]
    lng = row[STATION_CSVROW_LNG]
    lat = row[STATION_CSVROW_LAT]

    Station.find_or_create_by(
        :station_id =&gt; station_id,
        :line_id =&gt; line_id,
        :name =&gt; name,
        :address =&gt; address,
        :lat =&gt; lat,
        :lng =&gt; lng
    )
end</pre><p>以上、鉄道会社・路線・駅データをseedで取得・インポートする方法でした。</p>The post <a href="https://mintaku-blog.net/rails-station/">【Rails】鉄道会社・路線・駅データをseedで取得・インポートする</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/rails-station/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1325</post-id>	</item>
		<item>
		<title>【Rails】都道府県・市区町村データをseedで取得・インポートする</title>
		<link>https://mintaku-blog.net/rails-prefecture/</link>
					<comments>https://mintaku-blog.net/rails-prefecture/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Sat, 01 Feb 2020 12:27:41 +0000</pubDate>
				<category><![CDATA[Rails]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1323</guid>

					<description><![CDATA[<p>Railsで都道府県・市区町村のZIPファイルからCSVファイルに変換し、シードにて都道府県・市区町村データをインポートする方法を紹介します。 都道府県・市 …</p>
The post <a href="https://mintaku-blog.net/rails-prefecture/">【Rails】都道府県・市区町村データをseedで取得・インポートする</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>Railsで都道府県・市区町村のZIPファイルからCSVファイルに変換し、シードにて都道府県・市区町村データをインポートする方法を紹介します。</p>
<h2>都道府県・市区町村のマイグレーション作成</h2>
<p>まずは、都道府県・市区町村のマイグレーションファイルを作成します。</p>
<p>・db/migrate/20200101000001_create_prefectures.rb</p><pre class="crayon-plain-tag">class CreatePrefectures &lt; ActiveRecord::Migration[5.2]
    def change
        create_table :prefectures, id: :int, unsigned: true, auto_increment: true, options: 'ENGINE=InnoDB COLLATE=utf8_general_ci' do |t|
            t.string :name, null: false, limit: 4

            t.datetime :created_at
            t.datetime :updated_at
            t.datetime :deleted_at
        end
    end
end</pre><p>&nbsp;</p>
<p>・db/migrate/20200101000002_create_cities.rb</p><pre class="crayon-plain-tag">class CreateCities &lt; ActiveRecord::Migration[5.2]
    def change
        create_table :cities, id: :int, unsigned: true, auto_increment: true, options: 'ENGINE=InnoDB COLLATE=utf8_general_ci' do |t|
            t.integer :prefecture_id
            t.string :name, null: false, limit: 16

            t.datetime :created_at
            t.datetime :updated_at
            t.datetime :deleted_at
        end
    end
end</pre><p>&nbsp;</p>
<h2>都道府県・市区町村のモデル作成</h2>
<p>都道府県と市区町村のモデル作成します。1対多のリレーションもはっておきます。</p>
<p>・app/models/prefecture.rb</p><pre class="crayon-plain-tag">class Prefecture &lt; ApplicationRecord

    ##
    # リレーション
    ##
    has_many :cities

end</pre><p>&nbsp;</p>
<p>・app/models/city.rb</p><pre class="crayon-plain-tag">class City &lt; ApplicationRecord

    ##
    # リレーション
    ##
    belongs_to :prefecture

end</pre><p>&nbsp;</p>
<h2>seed.rbで都道府県・市区町村データを取得し、テーブルに保存</h2>
<p>seed.rbファイルに都道府県・市区町村データを取得し、テーブルに保存する処理を書いていきます。</p>
<p>今回は日本郵便の都道府県ZIPファイルのダウンロードURLから取得していきます。一度ファイルをダウンロードして、Railsプロジェクトに格納してから、そこを参照して取得しても良いかと思います。</p>
<p>ZIPファイルを一度CSVファイルに変換し、db/配下に保存します。</p>
<p>その後、保存したCSVファイルから都道府県名と市区町村名のカラムを取得し、先ほど作成した都道府県と市区町村のテーブルに挿入していきます。</p>
<p>全取得が完了したら、保存していたCSVファイルを削除して完了です。</p>
<p>・db/seeds.rb</p><pre class="crayon-plain-tag">require 'csv'
require 'zip'

PREF_CITY_URL = "http://www.post.japanpost.jp/zipcode/dl/kogaki/zip/ken_all.zip"
SAVEDIR = "db/"
CSVROW_PREFNAME = 6
CSVROW_CITYNAME = 7
save_path = ""

# 都道府県・市区町村ZIPを解凍しCSVとして保存
open(URI.escape(PREF_CITY_URL)) do |file|
    ::Zip::File.open_buffer(file.read) do |zf|
        zf.each do |entry|
            savePath = SAVEDIR + entry.name
            zf.extract(entry, save_path) { true }
        end
    end
end

# 都道府県・市区町村CSVを読み込みテーブルに保存
CSV.foreach(save_path, encoding: "Shift_JIS:UTF-8") do |row|
    pref_name = row[CSVROW_PREFNAME]
    city_name = row[CSVROW_CITYNAME]
    pref = Prefecture.find_or_create_by(:name =&gt; pref_name)
    City.find_or_create_by(:name =&gt; city_name, prefecture_id: pref.id)
end

# 保存したCSVファイルを削除
File.unlink save_path</pre><p>以上、都道府県・市区町村データをseedで取得・インポートする方法でした。</p>The post <a href="https://mintaku-blog.net/rails-prefecture/">【Rails】都道府県・市区町村データをseedで取得・インポートする</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/rails-prefecture/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1323</post-id>	</item>
		<item>
		<title>【Rails】非同期でPDFファイルをアップロードする方法</title>
		<link>https://mintaku-blog.net/rails-pdfupload/</link>
					<comments>https://mintaku-blog.net/rails-pdfupload/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Wed, 02 Oct 2019 11:00:31 +0000</pubDate>
				<category><![CDATA[Rails]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1207</guid>

					<description><![CDATA[<p>Ruby on RailsでPDFファイルを非同期でアップロードする方法を紹介します。PDFファイルを選択または変更した場合にファイル情報が非同期でサーバに …</p>
The post <a href="https://mintaku-blog.net/rails-pdfupload/">【Rails】非同期でPDFファイルをアップロードする方法</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>Ruby on RailsでPDFファイルを非同期でアップロードする方法を紹介します。PDFファイルを選択または変更した場合にファイル情報が非同期でサーバにアップロードされ、その情報を再度フロントに返すイメージです。</p>
<h2>carriwaveを使ってPDFファイルカラムが存在するモデルと紐づけ</h2>
<p>Gemのcarriwaveを使ってファイルをアップロードするため、インストールや設定をしておく必要があります(今回は省略)。</p>
<p>ファイルパスを保存するカラム(今回の場合はfile_name)を指定してモデルと紐づけます。</p><pre class="crayon-plain-tag">class Hoge &lt; ApplicationRecord

...

    # pdfアップローダー
    mount_uploader :pdf, FileUploader, mount_on: :file_name

....

end</pre><p></p>
<h2>PDFファイルアップロード処理(フロントエンド)</h2>
<p>ViewでPDFファイrを選択するHTMLを生成します。</p>
<p>PDFファイルが選択された場合、または変更された場合に「Application.hoges.pdf_upload($(this))」イベントが発火します。</p><pre class="crayon-plain-tag">&lt;%= form_for @hoge, url: { action: action, id: @hoge.id }, html: { multipart: true }, remote: true, data: { method: :confirm } do |f| %&gt;

...

    &lt;div&gt;
        &lt;%= f.hidden_field :id %&gt;
        &lt;%= f.label :pdf do %&gt;
            PDFファイル選択
            &lt;%= f.file_field :pdf, accept: ".pdf", url: "#{upload_tempfile_hoges_path}", onchange: "Application.hoges.pdf_upload($(this))" %&gt;
            &lt;%= f.hidden_field :file_name, id: "file_name" %&gt;
            &lt;%= f.hidden_field :pdf_cache, id: "cache_name" %&gt;
        &lt;% end %&gt;
        &lt;a class="pdf_file_name_label"&gt;&lt;%= f.object.file_name || "選択されていません" %&gt;&lt;/a&gt;
    &lt;/div&gt;

...

&lt;% end %&gt;</pre><p>&nbsp;</p>
<p>CoffeeScriptで非同期のアップロード処理を書いていきます。</p>
<p>読み込んだPDFファイルの情報をFormDataに格納し、urlのパスにデータを送ります。</p>
<p>PDFアップロードに成功した場合は、その情報をViewに書き換えます。失敗した場合は、エラーモーダル(処理は省略)を表示させます。</p><pre class="crayon-plain-tag">class HogessController

    index: -&gt;

        @pdf_upload = (input) -&gt;
            file_uploader(input)

        # PDFアップロード
        file_uploader = (input) -&gt;
            file = input.prop('files')[0]
            formData = new FormData()
            formData.append("file", file)
            formData.append("filename", file.name)
            formData.append("name", input.parent().siblings('#file_link').find("label").attr("for"))

            $.ajax
                url: input.attr('url'),
                method: 'post',
                dataType: 'json',
                data: formData,
                processData: false,
                contentType: false
                
                .done (res) -&gt;
                    input.siblings('#cache_name').val(res.cache)
                    input.siblings('#file_name').val(file.name)
                    input.parents('.image_file-selection').find('.pdf_file_name_label').text(file.name).attr('href', res.pdf_url).attr('target', '_blank')
                    return

                .fail (xhr, status, error) -&gt;
                    Application.show_error_modal(xhr)
                    return

        return

this.Application.hoges = new HogesController</pre><p></p>
<h2>PDFファイルアップロード処理(サーバサイド)</h2>
<p>発火したイベントによってPDFファイルデータが非同期でサーバに送られます。サーバに送るためにroutes.rbrbにルーティングを追加しておきます。</p>
<p>送られたデータを先ほど紐づけたモデルからアップロードします。アップロードしたデータの返り値を@fileに格納します。</p><pre class="crayon-plain-tag">post :upload_tempfile</pre><p></p><pre class="crayon-plain-tag">class Hoge &lt; ApplicationRecord

...

    # PDFファイルアップロード
    def upload_tempfile
       @file = ::Hoge.new(pdf: params[:file], file_name: params[:filename])
    end

...

end</pre><p>&nbsp;</p>
<p>@fileのデータをJSON形式で変数に格納し、アップロード結果をViewに表示します。</p>
<p>今回の場合は、画面でアップロードしたファイルを確認できるようにするためファイルパスを返しています。</p>
<p>また、DBに登録や更新処理をする際のパラメータとして、アップロードしたキャッシュデータをinputタグに埋め込んでいます。</p>
<p>・upload_tempfile.js.erb</p><pre class="crayon-plain-tag">upload_tempfile.js.erb
{
    "pdf_url": "&lt;%= @file.pdf.url %&gt;",
    "cache": "&lt;%= @file.pdf.cache_name %&gt;"
}</pre><p>以上、Railsで非同期にPDFファイルをアップロードする方法でした。</p>The post <a href="https://mintaku-blog.net/rails-pdfupload/">【Rails】非同期でPDFファイルをアップロードする方法</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/rails-pdfupload/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1207</post-id>	</item>
		<item>
		<title>【Rails】Ajaxを使った非同期処理の流れを解説</title>
		<link>https://mintaku-blog.net/rails-asynchronous/</link>
					<comments>https://mintaku-blog.net/rails-asynchronous/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Thu, 12 Sep 2019 10:30:45 +0000</pubDate>
				<category><![CDATA[Rails]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1193</guid>

					<description><![CDATA[<p>RailsでAjaxを使ってサーバサイドと非同期通信する方法を紹介します。例として企業編集をAjaxを使って非同期で編集します。 1. 編集画面(View) …</p>
The post <a href="https://mintaku-blog.net/rails-asynchronous/">【Rails】Ajaxを使った非同期処理の流れを解説</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>RailsでAjaxを使ってサーバサイドと非同期通信する方法を紹介します。例として企業編集をAjaxを使って非同期で編集します。</p>
<h2>1. 編集画面(View)</h2>
<p>Viewで企業編集画面を用意します。formで編集したい項目を囲い、submitボタンが押下されたらサーバに送られ、フロントで受け取ります。</p>
<p>今回はAjaxを使った非同期通信の流れを紹介するため、それ以外の処理やモデル、routes.rbやja.ymlなどの定義は適宜読み替えてください。</p><pre class="crayon-plain-tag">...
    &lt;div data-method="edit_error_message"&gt;&lt;/div&gt;
    &lt;%= form_for @company, url: edit_confirm_company_path(@company), remote: true, data: { method: :edit_confirm } do |f| %&gt;

...

        &lt;dl&gt;
            &lt;dt&gt;企業名&lt;span class="required"&gt;*&lt;/span&gt;&lt;/dt&gt;
            &lt;dd&gt;&lt;%= f.text_field :company_name, class: "form_input", placeholder: "例：テスト企業" %&gt;&lt;/dd&gt;
        &lt;/dl&gt;

...

        &lt;button type="submit"&gt;&lt;%= t('view.buttons.edit') %&gt;&lt;/button&gt;
    &lt;% end %&gt;

...</pre><p></p>
<h2>2. サーバサイドで編集確認処理(Controller)</h2>
<p>Viewから送られてきたパラメータを受け取り、バリデーションします(バリデーションはモデルに書いてある前提)。</p>
<p>バリデーションに引っかかった場合は、エラーを返します。</p>
<p>成功した場合は、セッションにパラメータを格納します。非同期処理なため、JSON形式で返すformat.jsonを使用します。</p><pre class="crayon-plain-tag">class CompaniesController &lt; ApplicationController
    before_action :set_company, only: [:edit, :edit_confirm]

    COMPANY_PARAM = :company_param

...

    def edit_confirm
        respond_to do |format|
            @company.assign_attributes(edit_company_params)
            @company.validate

            if @company.errors.blank?
                session[COMPANY_PARAM] = edit_company_params
                format.json { render json: @company, status: :ok }
            else
                format.json { fail AsyncRetryValidationError, @company.errors }
            end
        end
    end

...

    private

    def set_company
        @company = ::Company.find(params[:id])
    end

    def edit_company_paramss
        params.require(:company).permit(:company_name)
    end
end</pre><p></p>
<h2>3. サーバからの編集確認結果を受け取る(View・CoffeeScript)</h2>
<p>サーバから帰ってきたデータはフロントで受け取ります。</p>
<p>バリデーションに成功した場合は、data-remodal-idがedit-modal-confirmの編集確認モーダルが呼び出されます。</p>
<p>バリデーションに失敗した場合は、Viewの&lt;div data-method=&#8221;edit_error_message&#8221;&gt;&lt;/div&gt;にエラーメッセージを表示させます。</p><pre class="crayon-plain-tag">...

$('[data-method="edit_confirm"]')
    .on 'ajax:success', (event, data, status, xhr)-&gt;
        $('[data-remodal-id="edit-modal-confirm"]').remodal().open()
        return
    .on 'ajax:error', (event, xhr, status, error)-&gt;
        data = $.parseJSON(xhr.responseText)
        element = $('[data-method="edit_error_message"]')
        show_error_message element, data.errors
        return

show_error_message = (element, error_messages) -&gt;
    element.addClass 'error_block'
        element.find('p').remove()
        for error_message in error_messages
            element.append '&lt;p&gt;' + error_message.message + '&lt;/P&gt;'
        return

...</pre><p>&nbsp;</p>
<p>バリデーションに成功した場合、以下の編集確認モーダルが表示されます。</p>
<p>モーダルに表示された編集ボタンが押下されたら、編集処理を行うアクションにデータを送ります。</p><pre class="crayon-plain-tag">...

&lt;div id="remodal" class="remodal modal_common" data-remodal-id="edit-modal-confirm" data-remodal-options="hashTracking: false, closeOnOutsideClick: false"&gt;
    &lt;div class="modal_text"&gt;
        &lt;p&gt;企業情報を編集します。よろしいですか？&lt;/p&gt;
    &lt;/div&gt;
    &lt;%= form_for @company, url: edit_company_path(@company), remote: true, data: { method: :edit } do |f| %&gt;
        &lt;div class="modal_cancel"&gt;
            &lt;button type="button" data-remodal-action="cancel"&gt;&lt;%= t('view.messages.cancel') %&gt;&lt;/button&gt;
            &lt;button type="submit"&gt;&lt;%= t('view.messages.do_edit') %&gt;&lt;/button&gt;
        &lt;/div&gt;
    &lt;% end %&gt;
&lt;/div&gt;

...</pre><p></p>
<h2>4. サーバサイドで編集処理(Controller)</h2>
<p>バリエーションに成功したパラメータを先ほど格納したセッションから取り出し、編集処理を行います(実際の編集処理は割愛します)。</p>
<p>先ほどのCompaniesControllerに以下の処理を追記するような形です。</p><pre class="crayon-plain-tag">...

def edit
    respond_to do |format|
        edit_company = session[COMPANY_PARAM]
        company = ::Company::editr.new(@company, edit_company).edit
        if company.errors.blank?
            format.json { render json: company, status: :ok }
        else
            format.json { fail AsyncRetryValidationError, company.errors }
        end
    end
end

...</pre><p></p>
<h2>5. サーバからの編集結果を受け取る(View・CoffeeScript)</h2>
<p>編集処理の結果をフロントで受け取ります。</p>
<p>編集処理に成功した場合は、data-remodal-idがedit-modal-completionの編集完了モーダルが呼び出されます。</p><pre class="crayon-plain-tag">...

$('[data-method="edit"]')
    .on 'ajax:success', (event, data, status, xhr)-&gt;
        data = $.parseJSON(xhr.responseText)
        $('[data-remodal-id="edit-modal-completion"]').remodal().open()
        return
    .on 'ajax:error', (event, xhr, status, error)-&gt;
        Application.show_error_modal(xhr)
        return

...</pre><p>&nbsp;</p>
<p>編集処理に失敗した場合は、エラーモダールを表示させます。エラーモーダルは他でも使用するためApplication.js.coffeeに共通化しています。</p><pre class="crayon-plain-tag">...

this.Application.show_error_modal = (error, id = 'remodal-error') -&gt;
    if $.isArray(error)
        error_messages = error
    else if error.responseJSON &amp;&amp; error.responseJSON.errors
        error_messages = error.responseJSON.errors
    else
    error_messages = []

    $element = $('[data-remodal-id="' + id + '"]')

    if error_messages.length &gt; 0
        $element.find('.modal_text').empty()
        for error_message in error_messages
            $element.find('.modal_text').append '&lt;p&gt;' + error_message.message + '&lt;/p&gt;'

    $element.remodal().open()

    return

...</pre><p>&nbsp;</p>
<p>編集に完了した場合、以下の編集完了モーダルが表示されます。</p>
<p>閉じるボタンを押下すると、画面がリロードされ編集された新しい情報が更新されます。</p><pre class="crayon-plain-tag">...

&lt;div id="remodal" class="remodal modal_common" data-remodal-id="edit-modal-completion" data-remodal-options="hashTracking: false, closeOnOutsideClick: false"&gt;
    &lt;div class="modal_text"&gt;
        &lt;p&gt;編集が完了しました。&lt;/p&gt;
    &lt;/div&gt;
    &lt;div class="modal_cancel"&gt;
        &lt;button type="button" class="btn_white btn_big" data-remodal-action="cancel", onclick="location.reload();"&gt;閉じる&lt;/button&gt;
    &lt;/div&gt;
&lt;/div&gt;

...</pre><p>&nbsp;</p>
<p>編集に失敗した場合、以下のシステムエラーモーダルが表示されます。</p>
<p>エラーメッセージが存在しない場合は、デフォルトのメッセージを表示するようにしています。</p><pre class="crayon-plain-tag">...

&lt;%
    options ||= {}
    options[:messages] ||= []
    if options[:messages].blank?
        options[:messages] &lt;&lt; 'システムエラーが発生しました。'
        options[:messages] &lt;&lt; 'しばらく時間をおいてから再度お試しください。'
    end
    options[:remodal_id] ||= 'remodal-error'
    options[:remodal_options] ||= 'hashTracking: false, closeOnOutsideClick: false'
%&gt;
&lt;div data-remodal-id="&lt;%= options[:remodal_id] %&gt;" data-remodal-options="&lt;%= options[:remodal_options] %&gt;"&gt;
    &lt;div class="modal_text"&gt;
        &lt;% options[:messages].each do |message| %&gt;
            &lt;p&gt;&lt;%= message %&gt;&lt;/p&gt;
        &lt;% end %&gt;
    &lt;/div&gt;
    &lt;div class="modal_cancel"&gt;
        &lt;button type="button" data-remodal-action="cancel"&gt;閉じる&lt;/button&gt;
    &lt;/div&gt;
&lt;/div&gt;

...</pre><p>以上、RailsでAjaxを使った非同期処理の流れを紹介しました。</p>The post <a href="https://mintaku-blog.net/rails-asynchronous/">【Rails】Ajaxを使った非同期処理の流れを解説</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/rails-asynchronous/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1193</post-id>	</item>
		<item>
		<title>【Rails】before_actionメソッドの使い方</title>
		<link>https://mintaku-blog.net/rails-before-action/</link>
					<comments>https://mintaku-blog.net/rails-before-action/#respond</comments>
		
		<dc:creator><![CDATA[みんたく]]></dc:creator>
		<pubDate>Thu, 15 Aug 2019 09:22:06 +0000</pubDate>
				<category><![CDATA[Rails]]></category>
		<guid isPermaLink="false">https://mintaku-blog.net/?p=1107</guid>

					<description><![CDATA[<p>Ruby on Railsでbefore_actionメソッドを使う方法を紹介します。 【Rails】before_actionメソッドの使い方 befor …</p>
The post <a href="https://mintaku-blog.net/rails-before-action/">【Rails】before_actionメソッドの使い方</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></description>
										<content:encoded><![CDATA[<p>Ruby on Railsでbefore_actionメソッドを使う方法を紹介します。</p>
<h2>【Rails】before_actionメソッドの使い方</h2>
<p>before_actionメソッドを定義することで、アクションが実行される前に処理を行ってくれます。</p>
<p>複数のアクションで同じような処理をしている場合は、before_actionを活用することで、コードを簡素化することができます。</p><pre class="crayon-plain-tag">class TestsController &lt; ApplicationController
    before_action :set_test

    def index
    end

    private

    def set_test
        @test = Test.find(params[:id])
    end
end</pre><p>以下のようにactionを指定して、定義することもできます。</p><pre class="crayon-plain-tag">before_action :set_hoge, only: [:index]</pre><p>ただbefore_actionを使いすぎると、actionごとの変数やメソッドが一見わかりづらくなってしまう場合があります。</p>
<p>認証など共通して事前に処理があるものは継承元のapplication_controller.rbなどに書いておくのが良いと思います。</p>
<p>&nbsp;</p>
<p>参考：</p>

<div class="ys-blog-card">
	<div class="ys-blog-card__container">
					<figure class="ys-blog-card__image">
				<img src="https://i0.wp.com/railsguides.jp/images/cover_for_facebook.png?w=800&#038;ssl=1" alt="" data-recalc-dims="1">			</figure>
				<div class="ys-blog-card__text">
			<p class="ys-blog-card__title">
				<a class="ys-blog-card__link" href="https://railsguides.jp/action_controller_overview.html">
  Action Controller の概要 - Railsガイド
</a>
			</p>
							<div class="ys-blog-card__dscr">
					本ガイドでは、コントローラの動作と、アプリケーションのリクエストサイクルでコント&hellip;				</div>
										<div class="ys-blog-card__domain">railsguides.jp</div>
					</div>
	</div>
</div>The post <a href="https://mintaku-blog.net/rails-before-action/">【Rails】before_actionメソッドの使い方</a> first appeared on <a href="https://mintaku-blog.net">みんたく</a>.]]></content:encoded>
					
					<wfw:commentRss>https://mintaku-blog.net/rails-before-action/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1107</post-id>	</item>
	</channel>
</rss>
