20200711のRailsに関する記事は19件です。

Railsでのorderメソッドの説明

ツイート機能やタイムライン機能をwebアプリケーションに実装した時、新しく投稿すると下に表示されてしまいます。

その順序を並び替えるのに「orderメソッド」を使用します。その使い方を説明します。

orderメソッド


テーブルから取得してきたインスタンス群を並び替えることができるメソッドです。

引数に「テーブルカラム名、並び替える順序」を記述して変化させる。


書き方例


def index
  @tweets = Tweet.order("created_at DESC")
end

上の記述でツイートを作成順に逆から並べると言う意味になります。

並び替える順序

  • ASC : 昇順
  • DESC : 降順

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

Rails 6 + Vue(単一コンポーネント) + vue-router + Vuetify だけどMPA(SPAではない) - Multi SPA

構成

ひとつのRailsアプリケーション上に、単一コンポーネントで構成されたSPAを配置します。
まずはひとつのSPAをおくことを目標にしますが、同様に複数のSPAを置くことを意図しています。(よって完全なSPAではありません。またMulti-SPAとなります。)

構成のイメージ:

  • /shared
  • /private
  • /private/episode/:id/schedule
    • ここをSPAにする

Rails

Rails は、MVCなMPAアプリケーションをつくります。
Rails 6 webpacker を利用して vue を用います。webpacker と Vue の導入については説明を省きます。

Layout

SPA 用の layout を設けて、コントローラ(あるいはアクション)で切り替えます。ここでは application_spa とします。

application_spa は後述します。コントローラ#アクション schedule で layout を指定すると下記のようになります。これで episode_controller の schedule だけ application_spa を利用することになりました。

episode_controller.rb
  def schedule
    respond_to do |format|
      format.html { render :schedule, :layout => 'application_spa' }
    end
  end

のちに指定する vue-router と、ここで SPA対象となるRails controller#actionは、同じURLで同じSPAを使う必要があります。(そうでない場合、ひとつのURLで別のものが表示されてしまいます)

Vue 周りの構成

Vue を SFC(単一ファイルコンポーネント)で利用します。SFCなVueからは axios で API を利用します。Vuetify, vue-router, axios については説明を省略します。

この先の手順は Vue のSingle File Components 利用における Vuetify, vue-router, axios と同じです。

用意するもの:
- Vuejs
- vue-router
- Vuetify
- axios

使わないもの:
- Nuxt.js

Rails View

action view

View への routing は、Railsの routes.rb でaction schedule に向けられているとします。それにより schedule.html.haml が用いられるため、layout と schedule.html.haml をSPA用にします。

schedule.html.haml は、content_for を利用して、 layout で使用する javascript_pack_tag と stylesheet_pack_tag を切り替えられるようにします。切り替えられるようにする理由は、複数のSPAを作成する予定のためです(つまりMulti-SPAです)。

view/../schedule.html.haml
- content_for :pack, 'schedules'
#schedules

#schedules の指定は、のちほどJavaScriptで利用します。

layout

layoutは、 view で指定されたcontent_forによってJSファイルを読み込むようにします。stylesheet_pack_tag も指定しないと Vuetify のデザインが当たらないので注意しましょう。

layout/application_spa.html.haml
!!! 5
%html
  %head
    %title= ""
    = csrf_meta_tags
    = yield :meta
    = stylesheet_pack_tag "#{yield :pack}"
    = javascript_pack_tag "#{yield :pack}"
  %body
    = yield

Rails側のViewは以上です。大変シンプルですね。

Vue の呼び出し

javascript_pack_tag が呼び出す schedule.js が起点(エントリーポイント)となります。(よく見かける application.js を呼び出す部分に相当します。)

schedule.js

Rails View で指定した ID #schedules を使います。

packs/schedule.js
// 様々なimport や Vue.use の記述は省略

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    el: '#schedules',  // schedule.html.haml で指定したもの
    vuetify, // import した vuetify 
    router, // import した vue-router
    render: h => h(App)
  }).$mount()
})

app.vue

schedule.js が render: h => h(App) で指定した app.vue は、 router-view を呼びます。記述には pug を用いています。

packs/app.vue
<template lang="pug">
  v-app
    v-app-bar( dark )
      v-toolbar-title Service Name is Unknown
    v-main
      v-container
        v-layout( wrap )
          router-view
</template>

router-view に割り当てられたコンポーネントは、次の router の設定で行っています。

vue router

vue 側の router の設定です。
アプリケーションが / から始まらないため、mode: 'history' を忘れないようにします。
この先、複数のSPAを作成するため、router も複数にします。そのため、 pack 配下に router/ を作成します。 router を schedule.js へ書かずに別ファイルとし packs/router/schedule.js として管理します。

packs/router/schedule.js
import VueRouter from 'vue-router';
import Schedule from '@/../components/schedule.vue';

const routes = [{
  path: '/episode/:id/schedule', component: Schedule,
  // 増えた path は、Rails 側も対応
}];

export default new VueRouter({
  base: '/private/', 
  mode: 'history',
  routes
});

base: '/private/' を指定して、アプリケーションの階層を /private 配下にしています。指定すると $route.path に含まれません。 router-link で :to を用いる際にも含まれなくなります。

この router/schedule.js は schedule.js から import されています。

axios

SFC 内で axios を用いてAPIにアクセスします。CSRF_TOKENはこちらを参照しました。

.vue の中では、methods で axios を用います。
遷移を伴う一覧を routers-list で記述しました。しかし、同じpathに一致する更新は、APIで取得した値を取り直してくれません。 そこで watch により変更を監視します。 vue-router公式では beforeRouteUpdate も使えると記載があります。

今回はbeforeRouteUpdateを利用しました。

@/../components/schedule.vue
// template は省略
// props や data は省略

methods: {
  getEpisode: function (event) {
    this.axios
      .get('/api/v1/episodes/' + this.$route.params.id + '')
      .then(response => (this.episode = response.data))
      // catch error 省略
  }
},

beforeRouteUpdate(to, from, next){
  this.getEpisode()
  next()
},
// watch を使う場合1
// watch: {
//  $route (to, from) {
//    this.getEpisode()
//  }
// },
//
// watch を使う場合2
// watch: {
//  '$route': 'getEpisode'
// },
// 

mounted () {
  this.getEpisode()
},

Vue側は以上です。これで/private/episode/:id/scheduleを起点としたSPAができました。

まとめ

Rails の layout をSPA用に分け、Railsで完結するアプリケーションと、Vue によるSPAを並存させました。Railsアプリケーション全体からみると、部分的にSPAを提供できます。

複数SPAを目的にした理由

ここでは、SPAを複数置くことを考えました。どの単位に分けると良いのかは、まだ明確な指針を持っていません。そのため、複数SPAを意図した理由についても簡単に記載します。

Vueのroutingで対応ができる範囲で複数のSPAに分けるのは、あまり意味はないだろうと考えています。エントリーポイントが schedule.js になるのかどうかの違いしかないためです。

一方で、起点ごとに異なるコンポーネントを利用する場合や、vue-router の path が遷移で繋がらない場合は、エントリーポイントを分ける意味があると考えました。

今回、構築したRailsアプリケーションのうち、スケジュール管理部分をVueのSPAにしたいと思いました。それだけでひとつのアプリケーションになり得る単位です。
また、同じアプリケーションを Episode 以外のモデルでも利用するため、再利用性の高いアプリケーションにしたいと考えました。そのためSPAによってModelを横断して取り扱えるようにしようと考えました。

今後、schedule 以外のエントリーポイントが増えることを想定しました。schedule 同様に、単独でひとつのアプリケーションになり得る単位です。たとえばログ管理を考えています。

その際に、SPAを統合するイメージが湧かず、別々に切りたいと考えました。Railsが管理する単位では1つのサービスですが、クライアント側に期待する単位では、それぞれ完結しています。たとえばスケジュール管理とログ管理はまったく別の操作性を期待しています。ログとスケジュールを同時に管理したいこともありません。

以上が、複数SPAを考えた理由です。

エントリーポイント間の差分

仮に schedule 以外に log を増やしたいとしましょう。

Rails側の作業はわずかです。
SPAにしたい箇所を、layout application_spa指定することと、log.html.haml view の中で #logs を使うこと、です。

Vue は、packs/schedule.js と同等のエントリーポイントとして packs/logs.js を用意し、 el: '#logs' とします。(もちろん他のelementでも構いません。)
また packs/logs.js は、log 用の router packs/router/logs.js を読み込みます。

axios や app.vue, Vuetify は作業が不要です。

つまり、複数のエントリーポイント間の差分は、対応する router/*.js の path と、利用するコンポーネント群の違いになります。

おわり

以上、Rails の layout をSPA用に分け、エントリーポイントを複数にすることで、Vue によるSPAを複数作成することができました。

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

高卒フリーターから、Web系自社開発企業のエンジニアになるまで

はじめに

一浪して入った大学を中退し、webエンジニアを目指して学習を開始してから約半年間、
晴れてweb系自社開発企業様からサーバーサイドエンジニアとして内定を頂けたので、学習過程や、就職活動で得た知見を共有したいと思います。
就職活動中の方のご参考になれば幸いです!

僕のスペックは以下の通りです。
今年23歳/高卒/フリーター/社会人経験なし
謙遜ではなく、どう考えても高スペックではありません。
こんな僕が内定を得るためには、そう選択肢はありません。やるべきことは決まっていました。

モダンな技術を使用したポートフォリオを作ること。

以下、学習手順と、就職活動内容を書きます。

目次

  1. ポートフォリオ紹介
  2. ProgateにてHello World (2020/1月)
  3. ドットインストールを貪る (2020/2月)
  4. モダンな技術にチャレンジ(Docker,AWS) (2020/3月)
  5. ポートフォリオ作成 (2020/4月~6月)
  6. いざ、就職活動へ (2020/6月~7月)
  7. 就活を終えての所感

ポートフォリオ紹介

これが僕が作成したポートフォリオになります。

サイトURL
Githubリポジトリ

概要

「美味しい」を共有するをモットーに、お食事招待、レストラン検索や料理に関する記事投稿ができるSNSアプリです。

クラウドアーキテクチャ

クラウドアーキテクチャ

言語・使用技術

  • インフラ
    • AWS(ECS-FARGATE/ALB/Route53/VPC/RDS)
    • Terraform (本番環境インフラをコードで管理)
    • CircleCI (CI/CD)
    • Docker/docker-compose
  • バックエンド
    • Ruby 2.6.3
    • Rails(API) 5.2.3
    • Mysql 5.7.30
  • フロントエンド
    • Nuxt.js(SSR) 2.0.0
    • element-ui (CSSフレームワーク)

高評価を頂いた点

  • トップページ等のデザインが凝ってあり、印象が良い。
  • UI/UXが整っている。
  • SSR、CI/CD、Terraform等の高度な技術を取り入れている。

補足説明

完成8割段階で就職活動を開始し内定を得られたので、まだまだ改善点が多く、ポートフォリオのレベルとしては高いとは言えないと思います。あくまで必要最低限レベルくらいだったと思います...。

1、ProgateにてHello World (2020/1月)

学習した講座
1 HTML/CSS
2 Javascript
3 Ruby
4 Ruby on rails
5 SQL
6 Command Line
7 Git

「プログラミングを学べば、バイト代くらいは余裕で稼げます」

こんな煽り文句を鵜呑みにした僕は、Progateにて学習を開始します。
各種基本文法をざっくり習得し、「プログラミングってこんな感じなんだな」程度の知見を得ます。
まだ個人でサービスを開発するレベルには程遠い感じでした。

2、ドットインストールを貪る (2020/2月)

学習した講座
1 詳解HTML 基礎文法編
2 詳解CSS 基礎文法編
3 詳解JavaScript 基礎文法編
4 詳解JavaScript オブジェクト編
5 詳解JavaScript DOM編
6 ローカル開発環境の構築
7 Ruby on Rails 5 入門
8 Active Record 入門
9 Sinatora 入門
10 Mysql 入門
11 シェルスクリプト 入門
12 その他自分が面白そうと思った講座

ローカル開発環境の構築をハンズオンで行い、自分のローカル上でアプリケーションを開発できるようになりました。
「自分で0からwebアプリを作れる」ということに無限大の可能性を感じ、プログラミングにどっぷりハマります。

この段階でプログラミングを小遣い稼ぎではなく、職として学んで行くことを決意しました。

上記の講座以外にも自分で興味を持った講座を貪ってました(寄り道しすぎたのは反省点です...)。

この期間で基礎を徹底的にインプットしまくったおかげで、新しい言語や応用の技術に挑戦するとき、「基礎×基礎」で何でも理解できるという自信がつきました。
まぁ要するに「俺にわからないことはない」という過信ですね。
以降難しい技術の学習が楽しいものと思えるようになったのはこの期間のおかげです。

しかし、その過信も次のフェーズで打ち砕かれます。

3、モダンな技術にチャレンジ(Docker,AWS) (2020/3月)

学習に使用した教材
1 キタミ式 基本情報処理技術者試験
2 Udemyの「これだけでOK! AWS認定ソリューションアーキテクト」
3 Docker/Kubernetes 実践コンテナ開発入門

この期間が一番辛かった時期だったと思います。

ローカルで開発したwebアプリを公開したいと思い、AWSの学習を開始しました。
しかし、ここでインフラの壁にぶち当たります。

どの入門系書籍や記事を見ても「チョットナニイッテルカワカラナイ」

インフラを理解するにはコンピューターサイエンスの幅広い知識が必要となり、
今までの知見では太刀打ちできません。
吐かれるエラー文も意味がわからず、ググっても解決方法が見つからない...。

よって最低限の知識を得るために「キタミ式 基本情報処理技術者試験」を購入し、ざっくり通読しました。
基本ソフトウェア、ネットワーク、セキュリティの知見を得たことで、AWSの各種サービスの役割を理解できるようになりました。

世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで
次にこの記事をハンズオンでやったおかげで初めてRailsのデプロイに成功しました。

さらにDockerを用いた開発手法のメリットも理解できるようになり、「Docker/Kubernetes 実践コンテナ開発入門」を購入し、Dockerにも挑戦しました。

ローカルのRailsアプリをDocker化することに成功したところで、必要な知識はもう十分という実感を得ました。
満を辞してポートフォリオ作成に取り掛かります。

4、ポートフォリオ作成 (2020/4月~6月)

参考にした教材や記事
1 Vue,Nuxt超入門
2 Webサイト デザイン集
3 Github 超入門
4 いまさらだけどCircleCIに入門したので分かりやすくまとめてみた
5 初心者でもできる! ECS × ECR × CircleCIでRailsアプリケーションをコンテナデプロイ
6 AWS,FargateとTerraformで最強&簡単なインフラ環境を目指す

この時期は毎日が楽しかったです。
夢中でコード書いて、気づいたら夜が明けていたこともありました(真似しないでください)。
やはりプログラミングが好きなんだなと再実感しました。

フロントエンドも何かしらのフレームワークを使い、凝ってみたいと思い、Nuxt.jsを採用し、RailsをAPIサーバーとして連携させる構成にしました。
Nuxtに関しては未学習だったため、教材や公式ドキュメントを見てキャッチアップしながらポートフォリオを作成していきました。

あとポートフォリオのトップページやログイン直後の画面はめちゃくちゃ凝っておくことをお勧めします。
採用担当者様からサイトの第一印象でその後の評価も決まるとのアドバイスをいただきました。

CI/CDやTerraformの構築に関しては、Qiita等で同様の環境での実装例を調べ、サンプルソースコードで理解できない箇所は公式ドキュメントを参考にしていました。

5、いざ、就職活動へ (2020/6月~7月)

はやく働きたいという焦りから、ポートフォリオ完成の8割の段階でWantedlyへの応募を開始し、就職活動しながらポートフォリオをブラッシュアップしていきました。
実際この選択は失敗だったなと反省しております。
なぜなら応募して返信があった場合、その会社様についての企業研究や面接対策を優先しないといけないため、ポートフォリオに時間を割くことができなかったからです。

ある採用担当者様から、「技術力は認めるけど、ちょっと機能が不十分ですね...」とのご指摘を頂く羽目になりました。

「焦り」はこんな簡単なことすら気付けなくしてしまう。恐ろしいですね。
今回希望する企業様から内定を頂けたのは本当に幸いでした。
皆様には100%完成した段階で応募をかけ始めることをお勧めします。

結果的に12社応募し、ご返事いただけたのは4社でした。

それと面接対策として僕がやっていたのは
業務未経験のWeb系エンジニア志望者が面接で聞かれる頻出質問とその対策
こちらの動画を参考に、質問における返答のテンプレ集を作成しました。
聞かれる質問の8割ぐらいはこの動画と同様のものでしたので、結果的に大成功でした。

そして焦りと後悔の葛藤の中、Twitterへの直営業を考えていた矢先、
第一希望として応募させていただきた企業様から内定の連絡をいただきました。

6、就活を終えての所感

「高卒フリーターの業務未経験でもwebエンジニアになれます!」なんて言うのは胡散臭いので気が引けますが、しっかりとしたプロセスを踏めば内定をもらえるというのを証明できたと思います。

学習順序を間違えなかったのが挫折しなかった一番の要因だと思います。

この半年間ここまでの熱量を注げたのはそれくらいプログラミングが好きだったからです。
生半可じゃ確実に無理でした。
今後ともこの熱量を絶やさずWebエンジニアとして成長していきたいと思います。

最後までお読みいただきありがとうございました!。

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

未経験が受託企業に入って1年経ったので、学んだスキル全部書いてみる

はじめに 

2019年6月にキャリアをスタートさせました。
Railsエンジニアとして1年でいろいろなことを学んできたので、簡単にまとめていきます。

いろいろなキャリアの積み方があるのでどれほど成長できてるのかは不明ですが、ほんの一例としてご参考ください。
※表現のおかしい点等ありましたら、ご指摘頂けますと嬉しいです。

前回:未経験が受託企業に入って半年経ったので、学んだスキル全部書いてみる

簡単な経歴

・一般大卒
 パソコンスキルはitunesで音楽入れる程度
・某アパレルチェーンで3年勤務
 内2年管理職
 パソコンスキルはWordで毎週報告書を書く程度
・2018/12〜  Mac購入
       某オンラインスクールで勉強開始
・2019/3〜 独学開始、都内もくもく会に週1参加、Menta契約、アプリ作成
・2019/6〜 就職
・2020/7 現在(2年目)

半年〜1年で学んだこと

1)技術関連

Ruby/Rails

4ヶ月くらいは毎日触っていました。
入社当初よりだいぶ読むのに抵抗が無くなったなと思ってましたが、初めて別PJに移ると、同じRailsでもすんなり読めない箇所が結構出てきたので、まだまだ甘いんだなと痛感しました。

業務フロー的にすごく複雑な機能の設計〜開発を担当し、作りながらお客様と何度も要件を擦り合わせ、完成後も改めてメンバーへ共有するのにはとても苦労した覚えがあります。

また、これまでは機能の追加や改修、0から開発などやってきましたが、次案件では主にCapybara Seleniumを使った スクレイピングをやりました。
Jobの仕組みや各サイトごとの要素取得のコツ、そして今まで以上にbyebugの貴重さを学びました。

SQL(ActiveRecord)

実際に使ってもらうお客様の1部署が多忙で、システムを使っての作業まで回らないということがよくあり、最初のうちはこちらで「毎月◯日に叩くコマンド」というのを複数用意して実行していました(もうやりたくない)。

基本的には某テーブルに溜まっている先月分のデータのステータスを、条件に当てはまれば全て「未→承認」に変える、というものでしたが、先方の業務フローがいまいち不明瞭だったり、イレギュラーがあまりにも多く、実際のデータに弾かれて一発で終わるなんてまあありませんでした。
そのため「叩く→止まる(トランザクションで戻る)→要件確認して都度コード修正」が毎回しばらく続きました。

こんな書き方があるのか、というところまで色々と試しながら書いていけたかなと思います。

Git

わざわざ書くことではないかもですが、日常的にgit stash git log --oneline を使うようになりました。
また、コンフリクトが発生しても、焦らずに冷静に対応できるようになりました(経験って怖い)。

WordPress

Rails案件と掛け持ちして、20%くらいの時間を割いてやってました。

テーマ選定(Cocoon,OnePress等5テーマで試作)から、実際のお客様の要望に合わせたサイト製作、複数プラグイン導入、Lightsailでのサーバー利用、Route 53でのドメイン作成、Letsencryptを使用したSSL化、Google Analyticsなどの周辺設定 等、一通り経験できました。

直接PHPをいじっての細かいカスタマイズはまだまだできないですが、テーマに乗っかればそこそこのサイトなら作れるようになりました。

0から新規サービス立ち上げ

こちらはとても良い経験になりました。

同PJ内ではありますが、新規サービスの要件定義〜設計〜開発〜テスト〜リリースまで一貫して行いました。
コンセプトは同じでもユーザー層が変わったため、画面が業務用のB向け→C向けに変わったのが印象的でした。

タイトなスケジュールの中、Railsはもちろん、Deviseの詳細設定や別サービスとのDB共有、Viewの細かい調整やまで行いました。
Pure CSSに慣れてなく、初めてフロントが嫌になりました(当時)。

AWS

実務では主にCloudFront AWS WAF Elastic Load Balancer ACM Route 53あたりを触りました。
まったく分からない中でなんとか模索しつつ、何日も日を跨ぎながらやった記憶があります。
セキュリティの仕組みと知識が大まかにつきました。
他にもCloudWatch Trusted Advisor GuardDutyあたりも触りました。
気付いたらコンソール画面への抵抗も無くなっていました。
だいぶ時間を使ってたので、コードが書きたいなあと思うことも多かったです。

個人ではAWS SAAという資格に合格しました。
実務で触ったあとにかなりの実力不足を痛感したので、せめて最低ラインまで浅く広く知識を得たいと思い学習し始めました。
結果、半年前の目標を達成できた形になりました。

AWS認定ソリューションアーキテクトアソシエイト(AWS SAA)合格記
↑ 詳細はこちらに書いてますので、よかったら読んでみてください。

ステージング/本番環境へのデプロイ

新規サービス立ち上げ時も、相変わらず毎週のようにデプロイ作業を行なっていました。
本番が落ちるなんていつものことでした(まだリリース前だったので勘弁)。
焦りながら必死でバグを見つけて修正していたので、デプロイする日は胃がキリキリしていました。

開発環境がガラッと変わると、本番へ反映させてもcssがうまく反映されずなんだこりゃと思うことがありましたが、単純にrails assets:precompileを忘れてただけということがありました。

今まで先人の用意したデプロイ一括コマンドにあやかって叩いてたので、甘えてたなと思うと同時に、そういえば自分の独学時代もHerokuにだけcss反映されずによく困ってたなwとか思い出して、懐かしんでました。

2)顧客対応

タスク/スケジュール管理

PLを任されていた関係で、雨のように降り注いでくる要望の全てを、お客様用へはガントチャート、社内ではTrelloでまとめて管理していました。

ガントチャートGoogleスプレッドシートから使用していました。
エクセルができないまま使い始めたので、大学時代にちゃんとやっとけば後悔しました。
Googleスプレッドシートを爆速で活用するための基本&応用の関数10選

Trelloは自分で提案して導入し、時間の限られる中でチームで効率よくタスクが見渡せるように工夫しました。
カスタマイズがしやすかったので、カードごとの工数表示やボードごとの自動工数計算など、必要なものはどんどん取り入れてました。
小規模チームのタスクをTrelloで管理する

業務効率化

半年前にもありましたが、対応部署の増加や繁忙期が重なるなどでやりとりに苦戦するようになり、さらなる工夫が求められました。

・毎週のMTGを必要がない限り無くした
・部署ごとのチャットでの問い合わせで、問題に対する情報が足りず何往復もすることが多かったため、必須項目を絞り込みGoogleフォーム問い合わせ+スプレッドシート自動更新+slack通知で大幅な時短化をさせた
・なるべく常にYESで答えていたが、多すぎるタスク量と開発の優先順位を考えてNOも使うようにした(でも相手の気を損ねないようにする)

などを行いました。

[補足]次の1年でやりたいこと

今後学びたいスキルを書いていきます。
開発に力を入れたいです。

※ アドバイス等頂けると嬉しいです。

Rails

上でも書いてますが案件が代わり改めて基礎力が低いことを通感しました。
原点に戻って、基礎からサッと復習したいなと思います。
Ruby on Rails チュートリアル
プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで
現場で使える Ruby on Rails 5速習実践ガイド

あとは別PJだけでなく、社外で書かれてるコードを読むのも勉強になるので、githubで公開されてるコード等を積極的に読みたいです。

AWSをいじる

資格こそ取ったものの、実際に触るとなると全然できる自信がありません。
いわゆる「頭でっかち」状態だと思います。

自分でVPC周りを組み立ててみたり、LambdaやSQSなどよく使われているであろうサービスにも慣れておきたいです。
できればコンテナサービス周りにも手を出してみたいです(先にDockerの勉強も必要ですが…)

JavaScript

実務であまりJavaScriptを触っておらず、エンジニアとして結構な弱点だと感じています。
Vue.jsやReactなどに焦点をおかず、まずはじっくり生JavaScriptに慣れていこうと思います。

改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで
JavaScript Primer
まずはこの辺りを参考にする予定です。

IT基礎

知識不足を日々感じます。
まずは基本情報でIT基礎を網羅的に頭に叩き込みます。
時期もちょうど良いので、秋の試験まで受けてしまおうかなと。

その後Linux SQLあたりも、王道めな技術書をざっと読んで早く頭に入れてしまいたいです。

チームリーダー

一度経験させて頂きましたが、やや特殊な案件だった(笑)ため、普通の案件で経験してみたいです
相手が強いエンジニアの方でもきちんとコミュニケーションできるように、早くITスキルを高めていきます。

突破力

ググり力こそ上がりましたが、基礎知識不足のせいか諦めて社内の人を頼ることも多々ありました。
ベースの力をつけるのはもちろん、その上で分からないことも粘り強く最後まで諦めないようにして、突破できる力をつけたいです。
ただ仕事なので、これ以上無駄だと判断したらタイミングよく見切るクセもつけたいです。

なるべく意識してるのですが、この境目がなかなか難しいです。

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

Ruby on Rails 遷移元によって条件を変更させる2パターン

プログラミングスクールのチーム開発でECサイトを作成している際、
どこのリンク先から遷移したかでインスタンス変数を変えたい・・・
というような場面がありました。
その時に2パターンの方法があったので、備忘録として残します。

【環境】
Ruby: 2.5.7
Rails: 5.2.4
使用した中で関係するgem:devise(顧客、管理者を作成)

前提条件

  • 管理者側のページで注文一覧を表示する際、下記のように設定したい。
    • トップページから遷移 => 本日作成された注文一覧
    • 顧客の詳細ページから遷移 => その顧客の注文一覧
    • ヘッダーの注文一覧ボタンから遷移 => 全ての注文一覧

テーブル

schema.rb
create_table "orders", force: :cascade do |t|
   t.integer "user_id", null: false
   (略)
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

create_table "users", force: :cascade do |t|
   (略)
  end

モデル(アソシエーション)

user.rb
has_many :orders, dependent: :destroy
order.rb
belongs_to :user

ルート

namespace :admins do
    get '', to: 'products#top'
    resources :users, only: [:index, :show, :edit , :update]
    resources :orders, only: [:index, :show, :update]
end

実装パターン①:遷移元をコントローラで取得する

参考にした記事:Railsで遷移元のコントローラーとアクションを取得する

admins/orders_controller.rb
def index
 path = Rails.application.routes.recognize_path(request.referer)

 # path[:controller]で遷移元コントローラーを、path[:action]でアクションを取得
 if path[:controller] == "admins/products" && path[:action] == "top"
   @orders = Order.where(created_at: Date.today)
  elsif  path[:controller] == "admins/users" && path[:action] == "show"
    @orders = Order.where(user_id: path[:id])
  else 
   @orders = Order.all
  end
end

これで、3パターンの変数を持たせることができました。
ただ、この内容だと【トップ画面からヘッダーの注文履歴ボタンを押した】際、
トップ画面から遷移したと判断されてしまいます。
そのため、最終的には下記の方法で実装を行いました。

実装パターン②:リンク元にパラメーターを付ける

遷移元のリンクページには下記の通りパラメータを持たせました。

admins/products/top.html.erb
 <%= link_to "本日の注文件数", admins_orders_path(order_sort: 0) %>
admins/users/show.html.erb
 <%= link_to "注文一覧", admins_orders_path(order_sort: 1) %>

そしてコントローラ側では、持ってきたパラメーターで変数を振り分け

admins/orders_controller.rb
def index
 case params[:order_sort]
 when "0"
   @orders = Order.where(created_at: Date.today)
 when "1"
   @user = User.find(params[:user_id])
   @orders = @user.orders
 else
   @orders = Order.all
 end
end

これによって①の問題は解消されました。

まとめ

  • ①の方法であればコントローラ側で一目見れば分岐条件がわかりやすいと思うが、状況に応じた実装が求められる。
  • ①の方法を応用すれば、updateやdestroyアクションのリダイレクト先などを分岐させることも可能。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TECH CAMP学習 個人アプリ作成③

ユーザー情報編集機能の追加

まずはターミナルにて以下のコードを入力します。

terminal.
$ rails g controller users

続いてルーティングで下記を入力します。

routes.rb
 devise_for :users
 root "photos#index"
 resources :users, only: [:edit, :update]

resourcesの意味は日本語では資源をいう意味で、usersの中で編集と更新の機能を利用するという解釈でよいと思います。

続いてコントローラーを編集します。

users_controller.rb
def 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を使用する。

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

Rails5でECサイトを作る⑦ ~Address、Genreモデル編~

はじめに

架空のベーカリーで買い物できるECサイトを作るシリーズ、Rails5でECサイトを作る⑥の続きです。
今回はアプリ本体の実装に戻り、購入したパンの送り先住所を管理するAddressモデルと、商品をまとめるGenreモデルの周辺を作っていきます。

ソースコード

https://github.com/Sn16799/bakeryFUMIZUKI

Modelのアソシエーション

association.jpg

Address

controller

app/controllers/addresses_controller.rb
class 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
end

index画面

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.rb
class 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
end

index

ファイル名は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.rb
  def price_include_tax(price)
    price = price * 1.08
    "#{price.round}円"
  end

後記

AddressもGenreも、いたって標準的なCRUD機能のみなので、ほとんど迷わずにできたと思います。難しくなるのはこれから……。注文機能のあるOrderモデルは、以前作った時にはかなり苦労させられましたが、レスポンシブ対応の画面にして、より簡潔なコードで再現できるのでしょうか? 次回へ続く!

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

[翻訳]開発者が避けるべき8つのRailsのよくあるミス

原著者Hunny Jummani氏の許諾を得て翻訳・公開しています。
記事: The 8 Most Common Rails Mistakes Developers Should Avoid
原文公開日: 2020年4月22日

image.png

Ruby on Railsはスケーラブル可能なパワフルなWebフレームワークで、完全なフロントエンドのサポートを持ったエンタープライズ対応のアプリケーションです。設定より規約やMVCパターンのようなRuby on Rails開発のシンプルな点が、高機能のアプリに対して信頼性を高めています。

Railsの開発者は、開発時間を短縮したり余計なコードを書かなくて済むRubyGemsを好んでいます。RubyGemsは標準的なプラクティスに従い、高速にMVPsをビルドし、製品の市場投入時間を短縮しています。

ですが、Railsの開発者は完璧ではありません。Ruby on Railsプロジェクトには固有の誤りはありませんが、一般的にはヒューマンエラーにより問題が発生します。
Ruby on Rails開発者には、経験を積むことでしか乗り越えられない落とし穴があります。この記事では、プロジェクトを頓挫させてしまう可能性がある8つのよくあるRuby on Rails開発のミスにスポットを当てています。

Ruby on Rails開発者が避けるべき8つの最もよくあるミス

以下のRails開発者のよくあるミスは、プロジェクトのボトルネックとなることがあります。心配しないでください、将来あなたがこれらのミスをしないための解決方法を提供しています。Ruby on Railsプロジェクトでこれらのミスを避け、お客さんのためにバグのない解決方法を開発するためのためのガイダンスを提供します。

ミス #1: N+1クエリ

アプリケーションに問題が発生することはありませんが、アプリケーションのパフォーマンスに影響を与えます。

例:

users = User.where(is_active: true)
names = users.map { |user| user.profile.name }

レコードに関連づいたクエリを投げている間は、N+1クエリを避けることができます。

例:

users = User.where(is_active: true).includes(:profile)
names = users.map { |user| user.profile.name }

クエリの数を減らしてアプリケーションのパフォーマンスを向上させ、eager loadingを追加するタイミングを知らせてくれるbullet gemを使いましょう。

ミス #2: 設定を安全に保っていない

アプリケーションはGoogle カレンダーやStripe Payment、AWSのような外部サービスを使う可能性があり、そのAPIキーやクレデンシャルはcredentials.ymlsecrets.ymlファイルに保存されています。

そのファイルは残りのアプリケーションと一緒にソースコードリポジトリに誤ってチェックインされてしまうことがあります。これが起きると、リポジトリにアクセスできる誰もが簡単にアプリケーションのユーザを危険に晒すことができてしまいます。

ミス #3: ロジックをコントローラに詰め込みすぎる

開発者は、コントローラ内のロジックが多すぎます。コントローラを薄くしておきましょう。単一責任の原則に違反してはいけません。

何を含めればいいのでしょうか?

セッションハンドル、モデル選択、パラメータ要求の管理、レンダリング、リダイレクトです。

ミス #4: ロジックをモデルに詰め込みすぎる

開発者は、emailの通知や、あるフォーマットから別のフォーマットへのデータ変換などのロジックを、モデルに詰め込みすぎています。

このコア機能はプレーンなrubyオブジェクトのようなサービスを使用して処理できるので、ActiveRecordから削除することができます。)

何を含めればいいのでしょうか?

構成 (例:リレーションやバリデーション)

データベースで一握りの属性を更新、保存することをカプセル化するためのシンプルなミューテーションメソッド

内部モデル情報を隠すためのアクセスラッパ

例: first_namelast_nameの組み合わせを表すfull_name

洗練されたクエリ。モデルクラス外でwhereメソッドを使ったりクエリを構築してはいけません。

ミス #5: 外部サービスの呼び出しによるブロック

Ruby on Railsアプリケーションのサードパーティ提供者は通常、APIの統合を非常に簡単にしてくれますが、速度が遅くなることもあります。

そのため、呼び出しでブロックされないために、Railsアプリケーションからサービスを呼び出すのではなく、コードのいくつかをバックグラウンドジョブに移動させるべきです。

これらのAPIサービスをキューイングするには、Delayed Job、Sidekiqなどを使う必要があります。

ミス #6: 不適切な述語メソッドの使用

述語メソッドはクエスチョンマーク(?) で終わり、論理値を返すメソッドです。

述語メソッドを作る時は、その目的について知っておくことが大切です。

メソッドが実行するものに名前をつけるべきであり、実行するアクションと名前が矛盾してはいけません。

例えば、ユーザが本を持っているかどうか確認するために、以下のようなユーザモデルで述語メソッドを定義すべきです。

def books_available?
self.books.present?
end

ミス #7: メモ化を使ってない

メモ化はRuby on Rails開発でアクセッサのスピードアップに使用されるテクニックです。
変数の初期化や手間がかかる作業のメソッドの結果をキャッシュします。

キャッシュの変数を初期化するためには||=を使用します。

パラメータの値や関連するレコードから変数を初期化するために使用されます。

例:

def google_calendar_event
@event ||= GoogleCalendarEvent.find_by(event_id: params[:event_id])
end

ミス #8: Ruby on Railsプロジェクトの命名規則に従っていない

開発中は以下のポイントを心に留めておいてください。

  • テーブル名は、モデルとテーブルを自動的にマップするために複数形にしてください。

  • モデル名は単数形にしてください。

  • クラスで予約名を使わないでください。例えば、"Class" は使えませんが、"Klass" は使えます。

  • コントローラ名は複数形にして、複数の単語を使いたい場合はキャメルケースを使ってください。

  • 複雑さを避けるためにデフォルトのRESTfulのルートに従ってください。

Ruby on Railsは、ウェブアプリケーション開発のためのパワフルで多機能なフレームワークです。Ruby on Railsアプリケーションがいくつかの限界を持つ一方で、デプロイメントやデバッギングをしている間、開発者が注意を払っていないためにミスが起きてしまいます。

上記のミスを避けることで、一流のRuby on Railsアプリケーションを構築できます。Ruby on Railsアプリケーション開発のリーディングカンパニーであるBoTreeでは、アプリケーションからバグを取り除くためのRubyベストプラクティスに従っています。当社の開発者は経験豊富なので、これらの失敗を学んでおり、プロジェクトを頓挫させないようにしています。高速でエラーがない開発のために、当社からRuby on Railsの開発者を雇うことができます。

詳細はこちらをクリックしてください。

BoTree Technologie では、25名以上のエンジニアのRuby on Railsチームでエンタープライズアプリケーションを構築しています。

私達は、Python、RPA、AI、Django、Javascript、ReactJSも専門としています。

コンサルティングは無料です。あなたの成長のお手伝いをさせてください。

翻訳を終えて

仕事でRails開発を始めて間もないので、このようなアンチパターンを知ることができ、助かりました。
上記の中でN+1は有名なので知っていましたが、メモ化はその存在自体を全く知りませんでした。
なるべく落とし穴にはまらないように進めていきたいですね。
誤訳等ありましたらご指摘をお願いします。

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

Webpacker::Manifest::MissingEntryError

画像のエラーを解決するのに手間取りすぎたから、備忘録メモです。

スクリーンショット 2020-07-10 16.38.40.png

先に結論

僕の場合はdocker-compose run --rm web yarn installしたら解決した

環境

【環境】
・Ruby 2.6.6
・Rails 6.0.3.2
・Docker

Dockerfile

qiita.rb
FROM ruby:2.6

# install package to docker container
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev \
    && apt-get install apt-transport-https \
    && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
    && apt-get update && apt-get install -y yarn \
    && curl -sL https://deb.nodesource.com/setup_10.x | bash - \
    && apt-get install -y nodejs \
    && mkdir /アプリ名

WORKDIR /FANTRA
COPY Gemfile /アプリ名/Gemfile
COPY Gemfile.lock /アプリ名/Gemfile.lock

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

確認したこと

  • webpackerインストールしてるか?
    • rails webpacker:install 実行ずみ (実行すると、conflict config/webpacker.ymlが出る)
  • yarnインストールしてるか?
    • 1.22.4
  • nodejsインストールしてるか?
    • v10.21.0

必要な物は揃っているはずなのになぜかできない。。。

わかってなかったこと

yarn ないし npm はruby でいうところのbundlerで、ライブラリを管理してくれるnodejsのツールのこと。
bundle installしないと ruby のライブラリが利用できないのと同じようにyarn installを実行しないと、 package.json によって指定されている nodejs ライブラリがダウンロードされず実行できないらしい。
webpack はnodejs のライブラリやから、このエラーを解消するためにはyarn installbundle install と同じような取り扱いで実行する必要があるらしい。

yarn installをしてみる

qiita.rb
 $  docker-compose run --rm web yarn install 

無事にエラーを解決することができました。

メモ

DockerfileのRUNしてるものとかymlの内容とかで実行しないといけないコマンド違ってくるのかな??

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

[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" %>

これで安心して別タブを開けます。

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

新規登録時、ActionMailerでメール送信機能

概要

ActionMailerを使って新規登録時にwelcomeメールを送ります。

前提

deviseを用いての、ログイン機能の実装。

導入手順

1. ActionMailerを利用する設定

welcomeメールの送信元はGmailを使う記載方法。
config/environments/development.rbにてActionMailerの設定方法の記述。

development.rb
Rails.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 UserNotice

3. Mailerクラス編集

app/mailers/user_notice_mailer.rb
class UserNoticeMailer < ApplicationMailer
  def send_signup_email(user)
    @user = user
    mail to: @user.email, subject: "会員登録が完了しました。"
  end
end

4. メール本文作成

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
  end

after_create を使うことで、Userが新規作成された後にメールを送信するためのメソッドを呼び出すことができます。

以上で、welcomeメールが送信されるはずです。

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

[Rails]DBにデータが登録されない場合の対処法

はじめに

フォーム入力内容をDBに保存したいがハマったのでアウトプット

環境

Rails 5.0.7.2
ruby 2.5.1
mysql 14.14

問題点

フォーム入力内容を保存しようとすると、パラメータに保存されているがDBに保存されない。

対処方法

保存するメソッドの後ろに!をつけて原因を確認。

すると、validation failed:User must exist(エラー内容は異なる場合がありそう)

原因

アソシエーションが組まれている際に、該当の外部キーが入っておらず、バリデーションで弾かれているのが原因。

optional: trueを記述。

goal.rb
class 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 #ここを編集
end

optional: trueとは、belongs_toの外部キーのnilを許可するというもの。

これでDBに保存できると思います。
参考にしてください!

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

Rails6・unicorn、nginx、postgresのdockerコンテナを別々で立てる

紹介する内容

  • dockerコンテナを三つ(Rails6とunicorn一緒に、nginx、postgres)にして立てます
  • nginxのUNIX-domain socketとdocker composeのvolume活用します

結論

Rails6コンテナはunicornをforegroundで起動、nginxのUNIX-domain socketをdocker composeのvolumeマウントしたら動きます

  • Rails6コンテナはunicornをforegroundで起動させて、nginxの通信を待ちながらコンテナが落ちない(exit 0)ようにします
  • nginxのUNIX-domain socketをdocker composeのvolumeを使って、Rails6コンテナのunicornに共有させます
    • UNIX-domain socket使いますからportはnginxとpostgresだけ開けば十分です

AWS ECSなどに活用できます

  • dockerコンテナを別々に立てる予定のECSにこのコンテナ構成動くか?の事前試しができます

priでデバックしずらくなりますから、開発環境ならRailsコンテナ一つ立ててbundle exec rails sすることを推奨します

  • binding.pryはかかりますが、ユーザーが値を入力できるところがよく分かりません
    • Railsコンテナにdocker attachしてもだめでした

紹介始めます

ディレクトリ構成

全体ソースコードは https://github.com/cheekykorkind/qiita-example/tree/master/rails/6/unicorn-nginx で確認できます

  • 全体図
    allD.png

  • Rails6とunicorn dockerファイルデレクトリー

  • nginx dockerファイルデレクトリー
    dockerD.png

nginx confファイル説明

いろいろ書かれてますが、必要な部分だけ書きます

  • ./docker/nginx/nginx.confファイルです
    • include /etc/nginx/conf.d/*.conf; によって、./docker/nginx/web.conf が読み込めます
  • ./docker/nginx/web.confファイルです
    • 全てのrequestが location / に入ります
    • proxy_pass http://unicorn; によって upstream unicorn に行きます
    • unix:/workspace/myapp/tmp/unicorn.todo.sock に来たらUNIX-domain socketで通信します
upstream unicorn {
  server unix:/workspace/myapp/tmp/unicorn.todo.sock fail_timeout=0;
}

server {
  listen       80;

  location / {
    ...省略

    proxy_pass http://unicorn;

    ...省略
  }
  ...省略
}

unicorn設定ファイル説明

  • ./config/unicorn/development.rb ファイルです
  • listen "#{RAILS_ROOT}/tmp/unicorn.todo.sock" をnginxのconfファイルの内容と同じパスを書くことでUNIX-domain socketの通信ができます

docker周り実装説明

  • ./docker-compose.yml ファイルです
  • bundle exec unicorn -c ./config/unicorn/development.rbでunicornをforegroundで起動します
  • UNIX-domain socketの専用volumeを作ります
volumes:
  nginx_socket:
    driver: local

そして、nginx_socket:/workspace/myapp/tmpでdockerコンテナ中でマウントするパスを設定します。
unicornとnginxが同じファイルをマウントできるように各々のコンテナに同じく書きます。

試し順番です

  1. docker composeがあるデレクトリーに移動します
    • cd qitta-example/rails/6/unicorn-nginx
  2. dockerコンテナをバックグラウンドで起動します
    • DOCKER_UID=$(id -u $USER) DOCKER_GID=$(id -g $USER) docker-compose up -d

unicorn.gif

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

rake routesでDevise.secret_key was not set. Please add the following to your Devise initializer:がでる

環境

ruby 2.5.1
Rails 5.0.7.2

やろうとしたこと

1)rake routes

早速ターミナルがエラー吐き出しています。
ターミナルを見ます。

rake aborted!
Devise.secret_key was not set. Please add the following to your Devise initializer:

  config.secret_key = 'e9216d77fb79ef05XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX’

Please ensure you restarted your application after installing Devise or setting the key.

2)ここの文に注目します。
installingDevisesetting the keyに注目しなよって言っています。

Please ensure you restarted your application after installing Devise or setting the key.

3)config/initializers/devise.rbの以下のコメントアウトを外す

Befor

# config.secret_key = '7f2977830281c6192ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ’

After

 config.secret_key = '7f2977830281c6192ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ

念のためターミナルで出力されたsecret_keyをいれる

  config.secret_key = 'e9216d77fb79ef05XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

完了done!

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

[Rails]carrierwaveを使って画像を保存する

タイトルの通りになります。

ゴールは画像を表示させるまでとなります。

gemを導入

初めはgemを入れます。
バージョンを指定しなければ最新のものがインストールされます。

Gemfile
gem 'carrirewave'

ターミナルでいつものよろしくお願いします。

$ bundle install

アップローダファイルの生成

次にアップローダファイルを作成しましょう。

$ rails generate uploader Image
create  app/uploaders/image_uploader.rb

これでapp/uploadersの中にimage_uploader.rbが生成されていると思います。

ちなみにImageの部分はマウントするモデル名だとか,
わかりやすい名前。私はよくImageで生成しています。

image_uploader.rb
class 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.rb
class hoge < ApplicationRecord
  mount_uploader :image, ImageUploader
end

登録するためのFormを作る

各ページ必要な情報を渡してあげます。

hoges_controller.rb
class 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 %> 

終わりに

最後まで読んでいただきありがとうございます。
お勉強している方に少しでも力になればと思います。

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

Ruby on Rails チュートリアル(第4版) 第7章

7.1.1 演習

1.ブラウザから /about にアクセスし、デバッグ情報が表示されていることを確認してください。このページを表示するとき、どのコントローラとアクションが使われていたでしょうか? paramsの内容から確認してみましょう。
 controller: static_pages
 action: about
2.Railsコンソールを開き、データベースから最初のユーザー情報を取得し、変数userに格納してください。その後、puts user.attributes.to_yamlを実行すると何が表示されますか? ここで表示された結果と、yメソッドを使ったy user.attributesの実行結果を比較してみましょう。

>> puts user.attributes.to_yaml
---
id: 1
name: Michael Hartl
email: mhartl@example.jp
created_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &1 2020-07-04 13:19:58.704260000 Z
  zone: &2 !ruby/object:ActiveSupport::TimeZone
    name: Etc/UTC
  time: *1
updated_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &3 2020-07-04 13:29:40.208038000 Z
  zone: *2
  time: *3
password_digest: "$2a$10$lIblqgEiCfLVJhfbugPd/epzBk4kR7Vi6bMYF8e2Yjb3iIkPwLrS6"
=> nil
>> y user.attributes
---
id: 1
name: Michael Hartl
email: mhartl@example.jp
created_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &1 2020-07-04 13:19:58.704260000 Z
  zone: &2 !ruby/object:ActiveSupport::TimeZone
    name: Etc/UTC
  time: *1
updated_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &3 2020-07-04 13:29:40.208038000 Z
  zone: *2
  time: *3
password_digest: "$2a$10$lIblqgEiCfLVJhfbugPd/epzBk4kR7Vi6bMYF8e2Yjb3iIkPwLrS6"
=> nil

7.1.2 演習

1.埋め込みRubyを使って、マジックカラム (created_atとupdated_at) の値をshowページに表示してみましょう (リスト 7.4)。

/app/views/users/show.html.erb
<%= @user.name %>, <%= @user.email %><%= @user.created_at %><%= @user.updated_at %>

2.埋め込みRubyを使って、Time.nowの結果をshowページに表示してみましょう。ページを更新すると、その結果はどう変わっていますか? 確認してみてください。
 秒数が進む。日本の時間とはズレているみたい。

<%= @user.name %>, <%= @user.email %><%= @user.created_at %><%= @user.updated_at %><%= Time.now %>

7.1.3 演習

1.showアクションの中にdebuggerを差し込み (リスト 7.6)、ブラウザから /users/1 にアクセスしてみましょう。その後コンソールに移り、putsメソッドを使ってparamsハッシュの中身をYAML形式で表示してみましょう。ヒント: 7.1.1.1の演習を参考にしてください。その演習ではdebugメソッドで表示したデバッグ情報を、どのようにしてYAML形式で表示していたでしょうか?

(byebug) puts @user.attributes.to_yaml
---
id: 1
name: Michael Hartl
email: mhartl@example.jp
created_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &1 2020-07-04 13:19:58.704260000 Z
  zone: &2 !ruby/object:ActiveSupport::TimeZone
    name: Etc/UTC
  time: *1
updated_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &3 2020-07-04 13:29:40.208038000 Z
  zone: *2
  time: *3
password_digest: "$2a$10$lIblqgEiCfLVJhfbugPd/epzBk4kR7Vi6bMYF8e2Yjb3iIkPwLrS6"
nil

2.newアクションの中にdebuggerを差し込み、/users/new にアクセスしてみましょう。@userの内容はどのようになっているでしょうか? 確認してみてください。

(byebug) @user
Started GET "/users/new" for 182.168.132.37 at 2020-07-10 13:03:56 +0000
#<User id: 1, name: "Michael Hartl", email: "mhartl@example.jp", created_at: "2020-07-04 13:19:58", updated_at: "2020-07-04 13:29:40", password_digest: "$2a$10$lIblqgEiCfLVJhfbugPd/epzBk4kR7Vi6bMYF8e2Yjb...">

7.1.4 演習

1.(任意) Gravatar上にアカウントを作成し、あなたのメールアドレスと適当な画像を紐付けてみてください。メールアドレスをMD5ハッシュ化して、紐付けた画像がちゃんと表示されるかどうか試してみましょう。
 省略

2.7.1.4で定義したgravatar_forヘルパーをリスト 7.12のように変更して、sizeをオプション引数として受け取れるようにしてみましょう。うまく変更できると、gravatar_for user, size: 50といった呼び出し方ができるようになります。重要: この改善したヘルパーは10.3.1で実際に使います。忘れずに実装しておきましょう。
 省略

3.オプション引数は今でもRubyコミュニティで一般的に使われていますが、Ruby 2.0から導入された新機能「キーワード引数 (Keyword Arguments)」でも実現することができます。先ほど変更したリスト 7.12を、リスト 7.13のように置き換えてもうまく動くことを確認してみましょう。この2つの実装方法はどういった違いがあるのでしょうか? 考えてみてください。
 良く分からなかったのでオプション引数とキーワード引数について調べました。
 キーワード引数とオプション引数についてまとめてみた

7.2.1 演習

1.試しに、リスト 7.15にある:nameを:nomeに置き換えてみましょう。どんなエラーメッセージが表示されるようになりますか?

undefined methodnome' for #User:0x00007ff659c23830`

2.試しに、ブロックの変数fをすべてfoobarに置き換えてみて、結果が変わらないことを確認してみてください。確かに結果は変わりませんが、変数名をfoobarとするのはあまり良い変更ではなさそうですね。その理由について考えてみてください。
 考えよう演習は正解が分からないので難しい。
 Formの変数名なので関係ないfoobarはあまり良くない、という感じなのかな?

7.2.2 演習

1.Learn Enough HTML to Be DangerousではHTMLをすべて手動で書き起こしていますが、なぜformタグを使わなかったのでしょうか? 理由を考えてみてください。
 Formタグは便利だけど、勝手に考えてコードを作ってくれる=仕組みをちゃんと理解出来ないから。
 なのかな。Rails凄い。

7.3.2 演習

1./signup?admin=1 にアクセスし、paramsの中にadmin属性が含まれていることをデバッグ情報から確認してみましょう。
 admin: '1'の記載あり

7.3.3 演習

1.最小文字数を5に変更すると、エラーメッセージも自動的に更新されることを確かめてみましょう。

/app/models/user.rb
validates :password, presence: true, length: { minimum: 5 }

Password is too short (minimum is 5 characters)

2.未送信のユーザー登録フォーム (図 7.12) のURLと、送信済みのユーザー登録フォーム (図 7.18) のURLを比べてみましょう。なぜURLは違っているのでしょうか? 考えてみてください。
 newアクション→createアクションに変わったから。

7.3.4 演習

1.リスト 7.20で実装したエラーメッセージに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.25にテンプレートを用意しておいたので、参考にしてください。
考えたけど良く分からずカンニングしてしまいました。
 エラーメッセージに対するテスト: Railsチュートリアル備忘録 - 7章
 assert_selectでHTMLのタグの中身を確認する
 エラーだったときに下記のdivが展開されてるかどうかのテストかな。

<div id="error_explanation">
<div class="alert alert-danger">
/sample_app/test/integration/users_signup_test.rb
assert_select 'div#error_explanation'
assert_select 'div.alert'

2.ユーザー登録フォームのURLは /signup ですが、無効なユーザー登録データを送付するとURLが /users に変わってしまいます。これはリスト 5.43で追加した名前付きルート (/signup) と、RESTfulなルーティング (リスト 7.3) のデフォルト設定との差異によって生じた結果です。リスト 7.26とリスト 7.27の内容を参考に、この問題を解決してみてください。うまくいけばどちらのURLも /signup になるはずです。あれ、でもテストは greenのままになっていますね...、なぜでしょうか? (考えてみてください)
resources :users のcreateアクションがusers_pathだから。

3.リスト 7.25のpost部分を変更して、先ほどの演習課題で作った新しいURL (/signup) に合わせてみましょう。また、テストが greenのままになっている点も確認してください。

/test/integration/users_signup_test.rb
post signup_path, params: { user: { name:  "",
                                    email: "user@invalid",
                                    password:              "foo",
                                    password_confirmation: "bar" } }

4.リスト 7.27のフォームを以前の状態 (リスト 7.20) に戻してみて、テストがやはり greenになっていることを確認してください。これは問題です! なぜなら、現在postが送信されているURLは正しくないのですから。assert_selectを使ったテストをリスト 7.25に追加し、このバグを検知できるようにしてみましょう (テストを追加して redになれば成功です)。その後、変更後のフォーム (リスト 7.27) に戻してみて、テストが green になることを確認してみましょう。ヒント: フォームから送信してテストするのではなく、'form[action="/signup"]'という部分が存在するかどうかに着目してテストしてみましょう。

/sample_app/test/integration/users_signup_test.rb
assert_select 'form[action="/signup"]'

7.4.1 演習

1.有効な情報を送信し、ユーザーが実際に作成されたことを、Railsコンソールを使って確認してみましょう。
 省略
2.リスト 7.28を更新し、redirect_to user_url(@user)とredirect_to @userが同じ結果になることを確認してみましょう。
 省略

7.4.3 演習

1.コンソールに移り、文字列内の式展開 (4.2.2) でシンボルを呼び出してみましょう。例えば"#{:success}"といったコードを実行すると、どんな値が返ってきますか? 確認してみてください。

>> "#{:success}"
=> "success"

2.先ほどの演習で試した結果を参考に、リスト 7.30のflashはどのような結果になるか考えてみてください。
 valueの値の文字列が返ってくる。

7.4.3 演習

1.Railsコンソールを使って、新しいユーザーが本当に作成されたのかもう一度チェックしてみましょう。結果は、リスト 7.32のようになるはずです。
 省略

2.自分のメールアドレスでユーザー登録を試してみましょう。既にGravatarに登録している場合、適切な画像が表示されているか確認してみてください。
 省略

7.4.4 演習

1.7.4.2で実装したflashに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト 7.34に最小限のテンプレートを用意しておいたので、参考にしてください (FILL_INの部分を適切なコードに置き換えると完成します)。ちなみに、テキストに対するテストは壊れやすいです。文量の少ないflashのキーであっても、それは同じです。筆者の場合、flashが空でないかをテストするだけの場合が多いです。

/test/integration/users_signup_test.rb
assert_not flash.empty?

2.本文中でも指摘しましたが、flash用のHTML (リスト 7.31) は読みにくいです。より読みやすくしたリスト 7.35のコードに変更してみましょう。変更が終わったらテストスイートを実行し、正常に動作することを確認してください。なお、このコードでは、Railsのcontent_tagというヘルパーを使っています。
 省略

3.リスト 7.28のリダイレクトの行をコメントアウトすると、テストが失敗することを確認してみましょう。
 REDになる。

4.リスト 7.28で、@user.saveの部分をfalseに置き換えたとしましょう (バグを埋め込んでしまったと仮定してください)。このとき、assert_differenceのテストではどのようにしてこのバグを検知するでしょうか? テストコードを追って考えてみてください。
 'User.count', 1にならないのを検知する。

またしてもエラー

heroku run rails db:migrateがまたうまくいかない。前章でもエラーが。何故だろう。
前章と同じく、heroku command not found の対処を参考に。

7.5.3 演習

1.ブラウザから本番環境 (Heroku) にアクセスし、SSLの鍵マークがかかっているか、URLがhttpsになっているかどうかを確認してみましょう。
 無事デプロイ出来て、確認出来た。

2.本番環境でユーザーを作成してみましょう。Gravatarの画像は正しく表示されているでしょうか?
 省略

メモ

  • Railsには標準で3つ環境が備わっており、それぞれ開発環境 (development)、テスト環境 (test)、本番環境 (production)と呼ぶ
  • any?メソッドはちょうどempty?と逆の動作で、要素が1つでもある場合はtrue、ない場合はfalseを返す。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Puma caught this error: Missing `secret_key_base` for 'development' environment, set this value in `config/secrets.yml` (RuntimeError)

前提

rails sをしてlocalhost:3000/にアクセスするとエラーが頻発する
タイトルと全く同じエラーをさっさと解決したい場合は8)から閲覧してください

環境

ruby 2.5.1
Rails 5.0.7.2

最終エラー

localhost:3000/で正しいviewではなく以下の表示がされる

Puma caught this error: Missing `secret_key_base` for 'development' environment, set this value in `config/secrets.yml` (RuntimeError)
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application.rb:513:in `validate_secret_key_config!'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application.rb:246:in `env_config'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/engine.rb:693:in `build_request'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application.rb:521:in `build_request'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/engine.rb:521:in `call'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/puma-3.12.6/lib/puma/configuration.rb:227:in `call'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/puma-3.12.6/lib/puma/server.rb:706:in `handle_request'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/puma-3.12.6/lib/puma/server.rb:476:in `process_client'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/puma-3.12.6/lib/puma/server.rb:334:in `block in run'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/puma-3.12.6/lib/puma/thread_pool.rb:135:in `block in spawn_thread'

現在のエラーに至る過程

1)rails sをしたときに以下のメッセージがコンソールにでる

Could not find fog-aws-3.6.4 in any of the sources
Run `bundle install` to install missing gems.

2)bundle instalをする

3)再度rails sすると以下のメッセージがでる

/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/devise-4.7.1/lib/devise/rails/routes.rb:500:in `raise_no_secret_key': Devise.secret_key was not set. Please add the following to your Devise initializer: (RuntimeError)

  config.secret_key = '65a95ec21686dc552d1312XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX’

補足:エラー全体

rails s
=> Booting Puma
=> Rails 5.0.7.2 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
Exiting
Traceback (most recent call last):
        65: from bin/rails:3:in `<main>'
        64: from bin/rails:3:in `load'
        63: from /Users/ユーザー名/Desktop/fleamarket_sample_72a_ver02/bin/spring:15:in `<top (required)>'
        62: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
        61: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
        60: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/spring-2.1.0/lib/spring/binstub.rb:11:in `<top (required)>'
        59: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/spring-2.1.0/lib/spring/binstub.rb:11:in `load'
        58: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/spring-2.1.0/bin/spring:49:in `<top (required)>'
        57: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/spring-2.1.0/lib/spring/client.rb:30:in `run'
        56: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/spring-2.1.0/lib/spring/client/command.rb:7:in `call'
        55: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/spring-2.1.0/lib/spring/client/rails.rb:28:in `call'
        54: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/spring-2.1.0/lib/spring/client/rails.rb:28:in `load'
        53: from /Users/ユーザー名/Desktop/fleamarket_sample_72a_ver02/bin/rails:9:in `<top (required)>'
        52: from /Users/ユーザー名/Desktop/fleamarket_sample_72a_ver02/bin/rails:9:in `require'
        51: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/commands.rb:18:in `<top (required)>'
        50: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
        49: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/commands/commands_tasks.rb:85:in `server'
        48: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/commands/commands_tasks.rb:85:in `tap'
        47: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/commands/commands_tasks.rb:90:in `block in server'
        46: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/commands/server.rb:102:in `start'
        45: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/commands/server.rb:148:in `log_to_stdout'
        44: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rack-2.2.2/lib/rack/server.rb:422:in `wrapped_app'
        43: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/commands/server.rb:84:in `app'
        42: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rack-2.2.2/lib/rack/server.rb:249:in `app'
        41: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rack-2.2.2/lib/rack/server.rb:349:in `build_app_and_options_from_config'
        40: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rack-2.2.2/lib/rack/builder.rb:66:in `parse_file'
        39: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rack-2.2.2/lib/rack/builder.rb:105:in `load_file'
        38: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rack-2.2.2/lib/rack/builder.rb:116:in `new_from_string'
        37: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rack-2.2.2/lib/rack/builder.rb:116:in `eval'
        36: from /Users/ユーザー名/Desktop/fleamarket_sample_72a_ver02/config.ru:3:in `block in <main>'
        35: from /Users/ユーザー名/Desktop/fleamarket_sample_72a_ver02/config.ru:3:in `require_relative'
        34: from /Users/ユーザー名/Desktop/fleamarket_sample_72a_ver02/config/environment.rb:5:in `<top (required)>'
        33: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application.rb:352:in `initialize!'
        32: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/initializable.rb:54:in `run_initializers'
        31: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:205:in `tsort_each'
        30: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:226:in `tsort_each'
        29: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:347:in `each_strongly_connected_component'
        28: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:347:in `call'
        27: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:347:in `each'
        26: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:349:in `block in each_strongly_connected_component'
        25: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:431:in `each_strongly_connected_component_from'
        24: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
        23: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/2.5.0/tsort.rb:228:in `block in tsort_each'
        22: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/initializable.rb:55:in `block in run_initializers'
        21: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/initializable.rb:30:in `run'
        20: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/initializable.rb:30:in `instance_exec'
        19: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application/finisher.rb:119:in `block in <module:Finisher>'
        18: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application/routes_reloader.rb:7:in `execute_if_updated'
        17: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application/routes_reloader.rb:27:in `updater'
        16: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.0.7.2/lib/active_support/file_update_checker.rb:77:in `execute'
        15: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application/routes_reloader.rb:26:in `block in updater'
        14: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application/routes_reloader.rb:16:in `reload!'
        13: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application/routes_reloader.rb:40:in `load_paths'
        12: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application/routes_reloader.rb:40:in `each'
        11: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application/routes_reloader.rb:40:in `block in load_paths'
        10: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.0.7.2/lib/active_support/dependencies.rb:287:in `load'
         9: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.0.7.2/lib/active_support/dependencies.rb:259:in `load_dependency'
         8: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.0.7.2/lib/active_support/dependencies.rb:287:in `block in load'
         7: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/activesupport-5.0.7.2/lib/active_support/dependencies.rb:287:in `load'
         6: from /Users/ユーザー名/Desktop/fleamarket_sample_72a_ver02/config/routes.rb:1:in `<top (required)>'
         5: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/actionpack-5.0.7.2/lib/action_dispatch/routing/route_set.rb:373:in `draw'
         4: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/actionpack-5.0.7.2/lib/action_dispatch/routing/route_set.rb:391:in `eval_block'
         3: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/actionpack-5.0.7.2/lib/action_dispatch/routing/route_set.rb:391:in `instance_exec'
         2: from /Users/ユーザー名/Desktop/fleamarket_sample_72a_ver02/config/routes.rb:15:in `block in <top (required)>'
         1: from /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/devise-4.7.1/lib/devise/rails/routes.rb:228:in `devise_for'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/devise-4.7.1/lib/devise/rails/routes.rb:500:in `raise_no_secret_key': Devise.secret_key was not set. Please add the following to your Devise initializer: (RuntimeError)

  config.secret_key = '65a95ec21686dc552d1312XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

4)
Devise.secret_key was not set.に注目して
config/initializers/devise.rbのコメントアウトを外す

Befor

# config.secret_key = '7f2977830281c6192ccdb7XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX’

After

config.secret_key = '7f2977830281c6192ccdb7XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

5)まだエラー

=> Booting Puma
=> Rails 5.0.7.2 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options

=> Booting Puma
=> Rails 5.0.7.2 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.6 (ruby 2.5.1-p57), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:3000
Use Ctrl-C to stop
DEPRECATION WARNING: You didn't set `secret_key_base`. Read the upgrade documentation to learn more about this new config option. (called from env_config at /Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application.rb:246)
2020-07-10 22:34:13 +0900: Rack app error handling request { GET / }
#<RuntimeError: Missing `secret_key_base` for 'development' environment, set this value in `config/secrets.yml`>

6)1)fog-aws-3.6.4に注目して以下のサイトを確認
https://github.com/fog/fog-aws

手順に沿って設定

Add this line to your application's Gemfile:

gem 'fog-aws'

And then execute:

$ bundle

Or install it yourself as:

$ gem install fog-aws

7)念のため再度bundle installしてからrails s

localhost:3000/にアクセスすると正しいviewではなく以下の表示がされる

Puma caught this error: Missing `secret_key_base` for 'development' environment, set this value in `config/secrets.yml` (RuntimeError)
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application.rb:513:in `validate_secret_key_config!'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application.rb:246:in `env_config'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/engine.rb:693:in `build_request'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/application.rb:521:in `build_request'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/railties-5.0.7.2/lib/rails/engine.rb:521:in `call'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/puma-3.12.6/lib/puma/configuration.rb:227:in `call'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/puma-3.12.6/lib/puma/server.rb:706:in `handle_request'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/puma-3.12.6/lib/puma/server.rb:476:in `process_client'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/puma-3.12.6/lib/puma/server.rb:334:in `block in run'
/Users/ユーザー名/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/puma-3.12.6/lib/puma/thread_pool.rb:135:in `block in spawn_thread'

8)先頭のsecrets.ymlに注目

config/secrets.yml

が存在していない

9)config/secrets.ymlを作成

10)secret_key_baseを生成

11)rails sが成功。localhost:3000/も表示される

参考記事

rails s が失敗して`raise_no_secret_key': Devise.secret_key was not setと言われました。
Rails で Missing 'secret_key_base' 真っ白な画面 
rails s が失敗してERROR RuntimeError: Missing secret_key_base と言われました。

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

[Rails] Devise でgem' html2slim'を使用した際のエラーについて

Deviseを使用した際,「$rails g devise:views」を実行後、
$ bundle exec erb2slim app/views/devise -dを実行した。

その後、「localhost:3000/users/sign_up」に飛んだ際にエラー発生。
スクリーンショット 2020-07-10 23.56.55.png

app/views/devise/shared/_error_messages.html.slim
- if resource.errors.any?
  #error_explanation
    h2
      = I18n.t("errors.messages.not_saved",
      -                  count: resource.errors.count,
      -                  resource: resource.class.model_name.human.downcase

よく見ると slim の構文が間違っている。
-」は埋め込みrubyを記述する際に使用するので、構文がおかしい。
よって、「-」を消去することでエラーが回避された。

感想

エラー内容は単純なものでしたが、自動でファイルの中身を
修正してくれるgemは完璧ではないという事を学べました。

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

【Rails/Docker/mysql】ホストPC直下で開発していたWEBアプリをコンテナで動かしてみたぉ

はじめに

Udemyの動画でDockerを学んだ後、ローカルホストで開発していたアプリケーションをコンテナで動かしてみました。

以下を参考にしながら、まずはdocker-composeを使わずにやってみました。

参考

udemy 米国AI開発者がゼロから教えるDocker講座
https://hub.docker.com/_/rails
https://qiita.com/tatsuya-miyamoto/items/08bd6ea142d02708614f
https://qiita.com/y-suna/items/e52b3af1d80c52b66b31
https://qiita.com/Masato338/items/f162394fbc37fc490dfb

実行環境

アプリケーションサーバ(コンテナ直下): puma4.3.3 (rails5.2.4.1 / ruby 2.5.1)
Database(ホスト直下): MySQL 5.6.47
コンテナ(ホスト直下): docker 19.03.8
ホスト: macOS Catalina 10.15.5

実施手順

既存アプリケーションを壊さないよう、対象となるアプリケーションのフォルダをコピーして実行しました。

$ cp -r ~/project/memo-space ~/project/memo-space_v2

 ~/project/memo-space -> ~/project/memo-space_v2

Dockerfileを作成する。

Dockerfile
FROM ruby:2.5.1
RUN apt-get update
RUN apt-get install -y mysql-client nodejs vim --no-install-recommends
RUN rm -rf /var/lib/apt/lists/*
RUN mkdir /myproject
WORKDIR /myproject
ADD Gemfile /myproject/Gemfile
ADD Gemfile.lock /myproject/Gemfile.lock
RUN gem install bundler
ADD . /myproject

Dockerfileに基づきビルド

terminal
$ cd ~/projects/memo-space_v2
$ docker build .

docker image を確認します。

terminal
 docker images
REPOSITORY                     TAG                 IMAGE ID            CREATED             SIZE
<none>                         <none>              df22a2c4c7f7        56 minutes ago      1.07GB
...

dockerを実行します。
この時、ホストのrails pumaのポート3000番とmysqlのポート3306番にコンテナのポートを紐づけます。
また、ホストのアプリケーションフォルダ「~/projects/memo-space_v2」にコンテナのフォルダ「/myproject」をマウントします。

terminal
$ docker run -it -p 3000:3000 -p 3306:3306 -v ~/projects/memo-space_v2:/myproject df22a2c4c7f7 bash

ファイル「Gemfile.lock」を削除します。

terminal
$ rm Gemfile.lock

bundle installを実行し、必要なGemをインストールします。

terminal
$ bundle install

database.ymlに記載された既存のアプリの定義を次の通り修正します。

修正箇所

・mysqlのhost(追記)
・アプリケーション名(修正)

config/database.yml
# MySQL. Versions 5.0 and up are supported.
#
# Install the MySQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem 'mysql2'
#
# And be sure to use new-style password hashing:
#   http://dev.mysql.com/doc/refman/5.7/en/old-client.html
#
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password:
  socket: /tmp/mysql.sock

development:
  <<: *default
  database: memo-space_v2_development   #修正
  host: docker.for.mac.localhost        #追記

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: memo-space_v2_test          #修正

# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
#   DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
#   production:
#     url: <%= ENV['DATABASE_URL'] %>
#
production:
  <<: *default
  database: memo-space_v2_production   #修正
  adapter: postgresql
  # database: postgresql
  encording: unicorde
  pool: 5
  username: memo-space
  url: <%= ENV['DATABASE_URL'] %>
  password: <%= ENV['MEMO-SPACE_DATABASE_PASSWORD'] %>

※既存アプリでは、元々production環境として、heroku上のpostgresqlを利用しており上記の記載となっていますが、未修正です。

※コンテナからホストPCへのアクセスは、次の通り指定することで実行できました。

terminal
mysql --host=docker.for.mac.localhost -u root

dbをcreateします。

terminal
rails db:create

dbをmigrateします。

terminal
rails db:migrate

コンテナの全てのインタフェースにバインディングすることで、ホストPCからアクセスできるようにします。

terminal
rails s -p 3000 -b '0.0.0.0'

以上により、ホストPCからブラウザでURL「localhost:3000」を指定し、アプリにアクセスできました。

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