- 投稿日:2021-01-21T23:15:38+09:00
初めての投稿
今日から本格的にプログラミングの勉強を始めました!
分からない事だらけですが挫けず継続します初日 ゼロからわかるRuby超入門 P.100まで!
- 投稿日:2021-01-21T22:39:51+09:00
slickを用いた画像スライド
[概要]
1.結論
2.なぜslickを使うのか
3.完成形
4.どのように使うのか
5.ここから学んだこと1.結論
slickの公式サイトからダウンロードして実装する
2.なぜslickを使うのか
簡単に実装できるからです。具体的にいうと公式サイトからファイルをダウンロードをして、必要なものを自分のフォルダの中に挿入して、あとは公式のところから載っているものから自分が使用たいデザインを選んで、実装していくだけだからです。
3.完成形
4.どのように使うのか
1)まず表示させたいビューファイルに画像をいくつか記述します。index.html.erb<div class="slider"> <div><%= image_tag 'S__19701764.jpg' %></div> <div><%= image_tag 'S__19701765.jpg' %></div> <div><%= image_tag 'S__19701762.jpg' %></div> </div>2)次にslickの公式サイトに飛び(https://kenwheeler.github.io/slick/)get it nowのボタンを押してDownload Nowのボタンを押してダウンロードする。
3)ダウンロードしたファイルを解凍し、ファイルを自分のフォルダに追加する。
ダウンロードして解凍したファイルを開きslickのファイルを開く。
その中のファイルのajax-loader.gif, fonts, slick-theme.css, slick.css を自分のアプリのCSSファイルに挿入4)jsフォルダに上記の画像のslick.jsを追加
5)application.html.erbに追加したファイルを記述
一番上にあるajax.googleapis.comはJQueryの読み込みをしている。application.html.erb<head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script type="text/javascript" src="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.css"/> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.9.0/slick-theme.min.css"> </head>6)最後に最初のビューファイルに記述した画像の記述の下にjavaScritを記述
この記述は自分がデザインしたい記述をそのままコピーして記述する。私はこのデザインをしたかったのでこの記述をコピーしました!!index.html.erb<div class="slider"> <div><%= image_tag 'S__19701764.jpg' %></div> <div><%= image_tag 'S__19701765.jpg' %></div> <div><%= image_tag 'S__19701762.jpg' %></div> </div> <script> $('.slider').slick({ autoplay:true, autoplaySpeed:3000, dots:true, });7)完成!!
これだけで3のような完成形が作られます!!
私はindex.html.erbに記述しましたが、application.html.erbや他のhtmlファイルに記述することもできます。5.ここから学んだこと
最初はうまく写真が反映されなかったりjsが動いてなかったりして苦労しましたが、しっかりと検証ツールを使いどこが間違っているのかを確認することができました!!slick自体の導入はシンプルで簡単なので是非試してみてください!!
このほかにもたくさんのデザインと自分の記述次第で好きなようにアレンジできるので試してみてください!!
- 投稿日:2021-01-21T22:16:32+09:00
#Rails redirect_toではhttpリクエストをgetからdeleteに変更できない!?
Rails redirect_toではhttpリクエストをgetからdeleteに変更できない!?
rails 5.2.2
を使ってます。今回はユーザーの退会ボタンを押したら退会処理がなされてかつ、ログアウトされ、ログイン画面に飛ばすような設計にしてそれを実装しようとしていたのですが、そこで問題が発生しました!!
それは退会処理後のログアウトを呼び出そうとして
redirect_to user_sessions_path
を実行したところ、「httpリクエストがgetですよ」とのことでdeleteに修正しようとしたが
残念
redirect_to user_sessions_path, method: :delete
ではできなかった
これ以上解決方法が浮かばなかったのでいろいろ調べたところ
deleteになってしまった場合はstatusコードを編集することでgetにできるらしいが
getからdeleteは無理なようだ・・・
postは指定できないことは知っていたがまだredirect_toに弊害があったとは・・・皆さんもきおつけてくださいね
僕はこれに2時間近く囚われてしまい悔しいです笑笑
- 投稿日:2021-01-21T21:38:46+09:00
【Rails】【Controllerから外部APIを叩く】 OpenWeatherMap APIで天気情報を取得
はじめに
外部APIを叩くという経験が初めてだったので、どんな大変な作業が待っているのだろうと心構えをしていたのですが、驚いたことに
実は難易度が低くて、高機能が実装できるハイコスパな方法
でした。
今回実装した機能の概要は以下の通りです。メモ程度ですが参考程度に載せておきます。前提
- deviseでユーザーに関する機能作成済み
- food_record(料理記録)モデルを作成済み
- new, createなどの基本的な機能は既に実装済み
環境構築
- Ruby2.7
- Ruby on Rails6
API_KEYの取得
OpenWeatherMapで会員登録して、API_KEYを取得します。こちらは、多くの記事が出回っているので、割愛させて頂きます。
https://qiita.com/matsubishi5/items/fcd77eacb0ed111299e2
↑私はこちらを参照しました。
http://api.openweathermap.org/data/2.5/weather?q=Tokyo&appid=
末尾に取得したAPI_KEYを貼り付けて、ブラウザで実行してみてください。東京の今の天気が返ってくるはずです。これだけで、APIってこんなものかと実感できますし、疎通確認の意味でもやっておいた方が無難です。
ちなみに、会員登録からAPI_KEYが使えるようになるまで数時間程度かかります。(いくぞうは、3時間くらいでした)必要なライブラリをインストール & API_KEYを格納
Gemfile.#API_KEYを環境変数として管理する(Keyを外部流出させないための措置) gem 'dotenv-rails' #アプリケーション内でHTTPリクエストを投げたい場合に使うクラス gem 'httpclient'Gemfile記載後、bundle installします。
..env.
OPEN_WEATHER_MAP_API = 'API_KEY貼り付け'
URI = 'https://api.openweathermap.org/data/2.5/weather'
.envには、前もって取得したAPI_KEYをコピペします。
.gitignore./.envここで絶対に忘れずに.gitignoreを作ってください。.envをgithubにpushした場合、全世界にAPI_KEYを晒すことになってしまいます。(私は1回やらかしました、、、)
APIを叩く処理
controllerでAPIを叩きます。
-Api::OpenWeatherMap::Request
という処理がありますが、こちらで、下記モジュール(request.rb)を呼び出しています。
- コードがブサイクでごめんなさい!!!!でもちゃんと動きます。food_records_controller.rbdef create #料理記録 @food_record = current_user.food_records.build(food_record_params) @food_record.food_date = Time.zone.today #天気API #リクエストを出す open_weather = Api::OpenWeatherMap::Request.new(current_user.location_id) #戻り値を受け取る response = open_weather.request if @food_record.valid? #APIが正常に動作した場合 if response['cod'] == 200 params_weather = Api::OpenWeatherMap::Request.attributes_for(response) @food_record.update(params_weather) flash[:notice] = "登録に成功しました" else #APIが正常に動作しない場合も料理情報は記録する flash[:notice] = "天気情報の取得に失敗しましたが、登録に成功しました" end redirect_to root_url else render 'new' end end
- 処理本体は、モジュールに書きました。
- 今回、取得する天気の地域
としてlocation_idを使用していますが、'id: location_id'をq: 'Tokyo'
というように変えても上手くいきます。lib/api/open_weather_map/request.rbmodule Api module OpenWeatherMap class Request attr_accessor :query def initialize(location_id) @query = { id: location_id, units: 'metric', appid: 'OPEN_WEATHER_MAP_API'] } end def request client = HTTPClient.new request = client.get(ENV['URI'], query) JSON.parse(request.body) end # 取得したjsonをparamsに変換 def self.attributes_for(attrs) { weather_main: attrs['weather'][0]['main'], weather_description: attrs['weather'][0]['description'], weather_icon: attrs['weather'][0]['icon'], weather_id: attrs['weather'][0]['id'], temp: attrs['main']['temp'], temp_max: attrs['main']['temp_max'], temp_min: attrs['main']['temp_min'], humidity: attrs['main']['humidity'], pressure: attrs['main']['pressure'] } end end end end
lib以下はデフォルトではtask以外読み込まれないので、config/application.rbに、以下の設定が必要ですconfig/application.rbconfig.paths.add 'lib', eager_load: true完成
<%= image_tag "http://openweathermap.org/img/wn/#{@food_record.weather_icon.chop}d@2x.png" %>
アイコンはこのように表示させています。chop処理をしているのは、夜に天気を取得すると、太陽が黒色になってしまうからです。
{@food_record.weather_icon.chop}d
このようにchopで末尾のnを削って、d「昼間(おそらくdaytime)」を付け足しています。補足
- 今回はlocation_idを使用しました。私は、下記のCSVを読み込ませて、cityテーブルに初期データとして投入して使用しています。
- 都市名, location_id, 緯度, 経度 の順になっています。
- 説明は割愛しますが、下記のようにすれば簡単に実装できます。
db/csv/cities.csv札幌,2128295,141.346939,43.064171 青森,2130658,140.740005,40.82444 盛岡,2111834,141.152496,39.703609 仙台,2111149,140.871933,38.26889 秋田,2113126,140.116669,39.716671 山形,2110556,140.363327,38.240559 福島,2112923,140.467773,37.75 水戸,2111901,140.446671,36.341389 宇都宮,1849053,139.883606,36.56583 前橋,1857843,139.060837,36.391109 さいたま,1853226,35.857208 千葉,2113015,140.123337,35.604721 東京,1850147,139.691711,35.689499 横浜,1848354,139.642502,35.447781 新潟,1855431,139.023605,37.902222 富山,1849876,137.211395,36.695278 金沢,1860243,136.625565,36.59444 福井,1863983,136.225174,35.850101 山梨,1848649,138.608002,35.61602 長野,1856210,138.040771,36.13464 岐阜,1863640,137.053986,35.78091 静岡,1851715,138.325424,35.025219 名古屋,1856057,136.906403,35.181469 津,1849796,136.508606,34.730282 大津,1853574,135.868332,35.00444 京都,1857910,135.753845,35.021069 大阪,1853909,135.502182,34.693741 神戸,1859171,135.182999,34.691299 奈良,1855612,135.804855,34.685051 和歌山,1926004,135.167496,34.226109 鳥取,1849890,133.850815,35.367859 松江,1857550,133.050568,35.472221 岡山,1854383,133.934998,34.661671 広島,1862415,132.459366,34.396271 山口,1848689,131.47139,34.185829 徳島,1850158,134.559433,34.06583 高松,1851100,134.043335,34.340279 松山,1926099,132.765747,33.839161 高知,1859146,133.531113,33.559719 福岡,1863967,130.41806,33.606392 佐賀,1853303,130.298798,33.249321 長崎,1856177,129.873611,32.74472 熊本,1858421,130.741669,32.789719 大分,1854487,131.612503,33.23806 宮崎,1856717,131.423889,31.91111 鹿児島,1860827,130.558136,31.560181 那覇,1856035,127.681107,26.2125db/seeds.rbrequire "csv" CSV.foreach('db/csv/cities.csv') do |row| City.create( name: row[0], location_id: row[1], lon: row[2], lat: row[3] ) end
上記ファイルを作って、cityテーブルも作成済みならば、下記コマンドを実行します。rails db:seed最後に
少しでも参考になればLGMTよろしくお願いします。
また、説明不十分な点や、説明が間違っている箇所が御座いましたら、コメントいただけると幸いです。Twitterもやっているので、よかったらフォローお願いします!!
https://twitter.com/engineer_ikuzou
- 投稿日:2021-01-21T18:39:38+09:00
RailsでPostgreSQLを使うぞ!「FATAL: role "postgres" does not existの解消」にむけて
はじめに
Railsチュートリアなんとか一通り終え(理解度としては3割程度なので終えたと言えるかどうかはおいといて...)、
自身のローカル環境にてRuby, Railsの環境を構築する際に発生した問題に関して、自身のメモのため、初学者の方で同様の問題が発生した方向けに投稿します!開発環境
- macOS Catalina 10.15.7
- MacBook Pro (13-inch, 2020, Two Thunderbolt 3 ports)
- プロセッサ 1.4 GHz クアッドコアIntel Core i5
メモリ 8GB
Ruby 2.5.7
Rails 5.2.3
PostgreSQL 13.1
導入までの一連の流れに関して
一連の環境構築までの流れに関しては下記記事が大変参考になると思いますので、ぜひ見てください。
今回はPostgreSQLの導入部にフォーカスして記事を作りたいと思います!【完全版】MacでRails環境構築する手順の全て
https://qiita.com/kodai_0122/items/56168eaec28eb7b1b93bエラー FATAL: role "postgres" does not exist の発生
ここからが本題です!
一通り上記記事を参考に環境構築を終え、railsを起動しようとしたところ、$ rails s下記のようなエラーが発生し、railsをが起動できない状況に...
ん?そもそもロールってなに?
詳しくは下記が参考になると思いますが、PostgreSQL 11.5文書
https://www.postgresql.jp/document/11/html/role-attributes.html「PostgreSQLは、ロールという概念を使用してデータベースへの接続承認を管理する」らしい。
つまり、role postgres does not existというのは、ロール(データベースへアクセスする権限みたいなもの?)として、postgreがないよっていことで解釈しました。そこで実際に下記コマンドを使用して、データベースのロール状態を確認すると...
$ psql postgrespsql (11.4) Type "help" for help. postgres=# \du # 現在のロール確認(ちなみにバッククオート\は「option+¥」キーですよ)Role name | Attributes | Member of ---------------+------------------------------------------------------------+----------- araishuntarou | Superuser, Create role, Create DB, Replication, Bypass RLS | {} | | {}やはりRole nameにpostgresがない!!
では、ここのroleにpostgresを追加すれば全てが解決できるぞと思い、
コマンド入力! そしてすかさず確認!$ createuser postgres$ psql postgrespsql (11.4) Type "help" for help. postgres=# \du # 現在のロール確認(ちなみにバッククオート\は「option+¥」キーですよ)Role name | Attributes | Member of ---------------+------------------------------------------------------------+----------- araishuntarou | Superuser, Create role, Create DB, Replication, Bypass RLS | {} postgres | | {}やったー!roleにpostgresが追加できてる。うれしー。これで解決と思いました...。
第二の関門 PG::InsufficientPrivilege: ERROR: permission denied to create database
やっとの思いで、railの起動まで辿り着きましたが、
$ rails s
なんとまたエラーが発生!!心折れます....。
PG::InsufficientPrivilege: ERROR: permission denied to create database
う~ん、データベースを作る権限がないということかな?言われてみれば確かに、postgresのAttributesに何の記述もされていないぞ。
Role name | Attributes | Member of ---------------+------------------------------------------------------------+----------- araishuntarou | Superuser, Create role, Create DB, Replication, Bypass RLS | {} postgres | | {}そこで、postgres にsuperuserの権限を与えようと下記コマンドを実施!
superuserは「ログイン以外に何でもできる権限をロールに与える」ため、危険な場合もあるとのこと。
(ここら辺はあとで勉強しよう... 本来は目的に応じた権限のみを与えた方がいいのかも)$ psql postgrespsql (11.4) Type "help" for help. postgres=# DROP ROLE postgres ; # 一度postgresを削除(役割だけを追加する方法が不明..) postgres=# CREATE ROLE postgres SUPERUSER ; # superuserの権限を持ったpostgresを作成! postgres=# \du # 現在のロール確認Role name | Attributes | Member of ---------------+------------------------------------------------------------+--------- araishuntarou | Superuser, Create role, Create DB, Replication, Bypass RLS | {} postgres | Superuser | {}やっとここまできたか...
最後の関門 ActiveRecord::PendingMigrationError
datebaseをmigrateしてないですよという意味だと捉え、
$ rails db:create #データベース自体(テーブルを保管しておく全体のシステム)を作るからの
$ rails db:migrate #データベースの中にテーブルを作ったり、カラムを変更したりするときに実行するを実行!!
最後に
$ rails s出来たー!!!!
最後に
稚拙な文章にもかかわらず最後までお読みいただきまして誠にありがとうございました。
私も完全には理解できていないところが多く、これかわも勉強しなければなりませんが、
少同じ問題で困っている方々の助けに慣れれば幸いです!
- 投稿日:2021-01-21T18:35:17+09:00
Rubyの最新のバージョンをインストールしようとしたらrbenvとanyenvにぶつかった
出発点
「プロを目指す人のためのRuby入門」が、「Macに標準搭載されているRubyは最新ではないので、アップデートした方がいいよ」と仰るので素直に従うことに。irbで日本語が化けてしまう症状にもこれが効くそうです。
Rubyの最新版のインストールにはrbenvというのがイケてるらしい。
そのrbenvをインストールするには、anyenvがいいらしい。言語のインストールってサクッと終わる話かと思いきや、意外に階層が..。
そしてややハマったのでメモ...。rbenvとは?
冷静になると、Ruby environmentと読めます。Rubyの環境設定ツール。anyenvとは?
同じ容量で、Any environmentと読めます。rbenvだけでなく他の言語の環境設定ツールもまとめて管理してくれるツールだそうな。始める前に作業環境の確認
MacBook Pro 13 2020
macOS Catalina 1O.15.71. anyenvをインストール
Rubyをインストールするrbenvをインストールするanyenvをインストールします。Homebrew、GitHubの2パターンがあります。
Homebrewの場合
1) anyenvをインストール
$ brew install anyenv2) シェルにanyenvをセットアップ
シェルとは?
CUIとかCLIとか呼ばれるやつ。Macではターミナルとも呼び、bashだのzshだのがある。$ anyenv initinitとは?
initialize(初期化)の意味。3) ターミナルを再起動
エラーメッセージが出なければOK。
Gitの場合
自分はこれはやっていないので、こちらのGitHubをご参照。
4)manifest directoryを初期化する
manifest directoryとは?(調査中。汗)
下記メッセージが出たら、
y
と回答しEnter。$ anyenv install --init Manifest directory doesn't exist: /Users/kakudaisuke/.config/anyenv/anyenv-install Do you want to checkout ? [y/N]: yこれで、anyenvのインストールは終了!
2. rbenvをインストール
1) 下記コマンドでanyenvでインストールできるリストが見られます。
そこにrbenvも出てくるはず。
$ anyenv install -l Renv crenv denv erlenv exenv goenv hsenv jenv jlenv luaenv nodenv phpenv plenv pyenv rbenv sbtenv scalaenv swiftenv tfenv2) rbenvをインストール
Rubyをインストールするrbenvをanyenvでインストールします。
$ anyenv install rbenv /var/folders/zq/lzp3d6kd2h1_00b25knb8tyr0000gn/T/rbenv.20210114135021.64582 ~ Cloning https://github.com/rbenv/rbenv.git master to rbenv... Cloning into 'rbenv'... remote: Enumerating objects: 10, done. remote: Counting objects: 100% (10/10), done. remote: Compressing objects: 100% (10/10), done. remote: Total 2878 (delta 3), reused 2 (delta 0), pack-reused 2868 Receiving objects: 100% (2878/2878), 562.74 KiB | 748.00 KiB/s, done. Resolving deltas: 100% (1795/1795), done. ~ ~/.anyenv/envs/rbenv/plugins ~ Cloning https://github.com/rbenv/ruby-build.git master to ruby-build... Cloning into 'ruby-build'... remote: Enumerating objects: 7, done. remote: Counting objects: 100% (7/7), done. remote: Compressing objects: 100% (6/6), done. remote: Total 11314 (delta 0), reused 2 (delta 0), pack-reused 11307 Receiving objects: 100% (11314/11314), 2.40 MiB | 1.83 MiB/s, done. Resolving deltas: 100% (7467/7467), done. ~ Install rbenv succeeded!成功したようです。
3) シェル(ターミナル)を再起動
これをしないとせっかくインストールした内容がシェルに設定されないようです。
$ exec $SHELL -lこれでrbenvのインストールが完了しました!
下記コマンドでバージョンが表示されればOKです。rbenv -v
4)
commande not found: rbenv
の場合3)で
commande not found: rbenv
になった場合は、パスが通っていないことが原因と考えられますので、こちらのQiita記事を参照すると良いです。パスが通っていないとは?
シェル様(ターミナルの主)に新しくインストールしたソフトの「転入届」が未提出である状態。3. Rubyをインストール
いよいよ、rbenvを使用して自分の欲しいバージョンを指定してインストールしましょう。
rbenv install 2.3.5irbで日本語もきちんと表示されるようになりました。
めでたしめでたし。最後に
教えてくれた先輩方に大感謝。
もしご指摘などあれば、バシバシお願い申し上げます!
- 投稿日:2021-01-21T16:56:54+09:00
Formオブジェクトを用いて作成したデータを、編集・更新する方法
要点
- 初心者がアプリを作るときの参考に
- formオブジェクトを用いた編集・更新機能は複雑なので詳しく知りたいorおさらいしたい
- 下記のエラーを解消する方法
param is missing or the value is empty: 'Formオブジェクト名'はじめに
メルカリのようなフリマアプリを作成中で、Formオブジェクトを用いて商品にタグ付けして編集・更新(edit・update)する機能を実装まで行いました
各モデルとコントローラーは以下のようになります
- Item/商品
- Tag/タグ
- TagItemRelation/商品とタグの中間テーブル
- TagsItem/ ItemとTagを同時に保存するためのFormオブジェクト
/app/model/item.rbclass Item < ApplicationRecord has_many :tag_item_relations, foreign_key: :item_id, dependent: :destroy has_many :tags, through: :tag_item_relations end/app/model/tag.rbclass Tag < ApplicationRecord has_many :tag_item_relations has_many :items, through: :tag_item_relations validates :tag_name, uniqueness: true end/app/model/tag_item_relation.rbclass TagItemRelation < ApplicationRecord belongs_to :item belongs_to :tag end/app/form/tags_item.rbclass TagsItem include ActiveModel::Model attr_accessor :item_name, :tag_name with_options presence: true do validates :item_name end def save item = Item.create(item_name: item_name) tag = Tag.where(tag_name: tag_name).first_or_initialize tag.save TagItemRelation.create(item_id: item.id, tag_id: tag.id) end endコントローラー
/app/controller/items_controller.rbclass ItemsController < ApplicationController def index @items = Item.all.order('created_at ASC') end def edit @item = current_user.items.find(params[:id]) @tegs_item = TagsItem.new(item: @item) end def update @item = current_user.items.find(params[:id]) @tags_item = TagsItem.new(update_items_params, item: @item) if @tags_item.valid? @tags_item.save redirect_to root_path else render :edit end end private def update_items_params params.require(:item).permit( :item_name, :tag_name ) end endしかし実際に商品を編集・更新をしてみると、、、
param is missing or the value is empty: 'tags_item'のエラーが出てしまい、商品の更新ができませんでした
調べたこと
エラー内容を読んでみると生成したFormオブジェクトの'tag_item_relation'が空になっているとのこと
そして、binding.pryなどを使って調べてみると、そもそもこの記述では'tags_item'が編集・更新では機能していないことが分かります
さらに原因を調べてみると新規投稿(new・create)と編集更新(edit・update)の機能を仕分けしていなかったことだと分かりましたFormオブジェクトで編集更新
今回のエラーの原因はFormオブジェクトに記載したsaveが新規投稿と編集更新で仕分けされていなかったことだと分かりました
/app/form/tags_item.rbclass TagsItem include ActiveModel::Model attr_accessor :item_name, :tag_name with_options presence: true do validates :item_name end def save item = Item.create(item_name: item_name) #ここがcreateアクションのみ tag = Tag.where(tag_name: tag_name).first_or_initialize tag.save TagItemRelation.create(item_id: item.id, tag_id: tag.id) end endこの記述を
/app/form/tags_item.rbclass TagsItem include ActiveModel::Model attr_accessor :item_name, :tag_name with_options presence: true do validates :item_name end # itemがすでに保存されているものか、新規のものかで、PUTとPATCHを分ける delegate :persisted?, to: :item # initializeでFormオブジェクトの値を初期化し、更新の際はdefault_attributesを呼び出す設定 def initialize(attributes = nil, item: Item.new) @item = item attributes ||= default_attributes super(attributes) end def save return if invalid? ActiveRecord::Base.transaction do # mapメソッドを使いsplit_tag_namesをtagの情報に変換 tags = split_tag_names.map { |tag_name| Tag.find_or_create_by!(tag_name: tag_name) } item.update!(item_name: item_name, tags: tags) end rescue ActiveRecord::RecordInvalid false end # formを飛ばす場所を(#createか#updateか)を判別して、切り替えている def to_model item end private attr_reader :item def default_attributes { item_name: item.item_name, tag_name: item.tags.pluck(:tag_name).join(',') } end def split_tag_names tag_name.split(',') end endに変更することで無事にタグ付け機能の編集更新を行うことができました
参考にしたサイト
関連記事
- 投稿日:2021-01-21T15:42:27+09:00
Railsによるfollow機能の実装
はじめに
Railsを使ってfollow機能を実装する中で気づいたことなどを自分用のメモとして手順と共に残しておこうと思い記事を書きました。間違っているところなどありましたらご指摘いただけると助かります!
Follow機能実装にあたって行う手順
- Followモデルを作り、dbにfollowsテーブルを追加
- 各モデルで関連付けを実装、フォロワー・フォローしている人を簡単に取得できるメソッドを定義する。
- 各controller, viewでそれらを表示
おおまかにはこんな流れです。基本的にはRailsチュートリアル第4版を参考にしています。
Followsモデルを追加
terminalbin/rails g model Follow follower_id:integer followed_id:integer上のように、rails g のコマンドでモデルを作ります。今回は、あるユーザーから見て、フォローされている人のidを格納するfollower_idカラム、そのユーザー自身のidを格納するfollowed_idカラム、を定義しました。
そうすると、migrationファイルが生成されるので、terminalbin/rails db:migrateを実行します。これでデータベースへの反映は完了です。
各モデルでの関連付けの実装
今回は、Followモデルには User モデルのidのみを格納するので、Userモデルとの関連付けのみを行います。下のコードは、あるユーザーがフォローしているユーザーを取得するために行う関連付けの例です。
User.rbhas_many :active_follows, class_name: "Follow", foreign_key: "follower_id", dependent: :destroy has_many :following, through: :active_follows, source: :followedFollow.rbbelongs_to :follower, class_name: "User"関連付けについては以下のような規則で行います。
1. model同士の関係性を1対1なのか、1対多なのか考える。
2. 1対1の場合はbelongs_to, 1対多の場合はhas_many, で関連付けする。今回であれば、Userモデルから見た場合、Followという行為は何回でも行えるものなので、1ユーザーに対して多数のFollowの情報が存在します。よって、1対多の関係です。
反対に、Followモデルから見た場合、一つのFollowレコードに対して、フォローしているユーザーは一人、フォローされているユーザーも一人です。よって、1対1の関係です。(ただし、Followモデルの中のfollower_idが取得したいのか、followed_idが取得したいのか、ということは関連付けを一つ行っただけではわかりません。なので、今回はフォローする側、される側、の二つに分けて関連付けを行います。初め僕はここがよくわからなかったので注釈としておきます)
また、関連付けでは、次の項目を指定することができます。
- 関連付けをするメソッドの名前
- 関連づけるクラスの名前
- 参照する外部キーの名前
- 関連付け先のモデルとの依存関係
これは、以下のように対応しています。
User.rbhas_many :active_follows, #ここ1 class_name: "Follow", #ここ2 foreign_key: "follower_id" #ここ3 dependent: :destroy #ここ41で、Userモデルのインスタンスに対して使う関連付け名(要するにメソッド名)を定義します。
2で、そのメソッドを用いたときどのクラスを参照するか指定します。今回はFollowモデルとの関連付けなのでFollowを指定しています。
3で、Followモデルのどのカラムを参照するか指定します。今回はあるユーザーがフォローしているユーザーを取得したいので、外部キーとしてfollower_idを指定します。
4で、そのモデルのインスタンスが削除された場合、紐づいているモデルの対応するカラムも削除するかどうかを指定します。ユーザーを削除した場合、フォロー情報も消去したいのでdependent: :destroyを指定します。ここまでUserモデル側での関連付けについて説明しましたが、Followモデル側では次のコードのみで済みます。
Follow.rbbelongs_to : follower, class_name: "User"これは、Railsの自動的な推測によります。こちらでは、1と2の指定のみで、誰がフォローしているかの情報をfollowerというメソッドで取り出せるようになります。class名を指定すると、そのクラスの主キーがfollower_idと一致するユーザーを取得してくれます。
最後に、今のままではUserインスタンスに対してactive_followsを用いてもFollowテーブルの情報が返ってくるのみです。フォローしているユーザーまで返してくれると便利なので、following関連付けを追加します。それが以下の部分です。
User.rbhas_many :following, through: active_follows, source: :followedこれは、followingという名前の関連付け(メソッド)を実行すれば、active_followsというメソッドを実行して、さらにそれにより得られたFollowテーブルのデータたちのfollowed_id というカラムのidを持つユーザーを取得してね、という意味になります。sourceに指定したfollowedの _id の部分はRailsによって補完されます。
以上で、Userモデルのインスタンスからフォロワーを取得してくる関連付けが定義できました!長々とわかりづらく書いてしまい申し訳ありません・・・
各controller,viewでの表示
これは他のところでも用いている方法と全く同じなので割愛します。取得した@userなどのインスタンス変数に対して先ほど定義したfollowingメソッドを実行し、レイアウトを整えるのみです。
終わりに
チュートリアル1周目にはあまり理解できていなかった部分を、ポートフォリオを作っている最中にしっくりきたので言葉に起こしておきました。自分なりの理解なのでわかりづらいところ、間違っているところなどあると思います。参考にされる際はその点御留意くださりますと幸いです。
- 投稿日:2021-01-21T15:34:13+09:00
if文とunless文の使い分け
エラーばかり投稿してもつまらないので、今回はif文とunless文について学んだことを書いていきたいと思います。
if文とは
rubyif A = B #処理 end
AはBである
という内容が、
正しければ処理を実行し、
正しくなければ処理を実行しません。unless文とは
rubyunless A = B #処理 end
AはBである
という内容が、
正しければ実行せず、
正しくなければ実行します。
=
は等しいかどうかを比較するときに使います。
if文ではAとBが等しいときにtrue
となり、処理を実行し、
逆にunless文ではAとB等しくない時にtrue
となり、処理を実行するというわけです。それでは比較演算子の1つである
!=
を使うとどうなるでしょうか?比較演算子の
!=
とは
=
とは逆で等しくないことを比較するときに使う比較演算子です。
要するにrubyif A != B #処理 end
AはBではない
という内容が
正しければ処理を実行し、
正しくなければ処理を実行しません。rubyunless A != B #処理 end
AはBではない
という内容が、
正しければ実行せず、
正しくなければ実行します。何やらこんがらがってきました。
文字に書くと難しくなってきましたが、要するに
if A = B と unless A != Bは同じ
if A != B と unless A = B は同じ
という解釈で大丈夫そうです。どちらを使えばいいか
可読性も考え、基本的には
if文
を使用した方が良いみたいです。英語における
ifの否定(if not)
とunless
の違い英語が得意だ!という人からすると信じられないかもしれませんね。
理由は、例えば
A if he isn't B.
という英文と
A unless he is B.
という英文は全く違う意味だからです英語の授業ではないので英文の例文や意味は記述しません。
ただ、プログラミングにおけるifとunlessは難しく考えず、逆の意味程度にとどめておいた方が良いかもしれません。終わり
英語が得意ではなかったですが、ifとunlessの使い方を疑問に思い、調べたり聞いたりした結果でした。
調べる前までは、
肯定したければif
否定したければunless
という考え方でした。これからは基本的にはif文を使っていきたいと思います。
- 投稿日:2021-01-21T14:19:01+09:00
コロナ禍での未経験からエンジニア転職に成功した上での感想
前置き
あくまで一個人の感想であり、特定の人物や団体に誹謗中傷はやめてください。
年齢は20代前半
高卒
文系です。数学への苦手意識は高い。
転職は初今の未経験からのエンジニア転職の状況について
正直かなり厳しい状況です。現在、未経験からのエンジニア転職はスクール卒のRubyを少しかじった未経験で溢れかえっており、経験者のエンジニアが欲しい企業と未経験から転職したいエンジニアの需要と供給が合致していません。正直、企業は未経験に技術は求めていないので、高度な技術を使用したポートフォリオは必要ありません。(Docker,CIなど)
そもそもポートフォリオをみていない可能性があります。なぜなら面接でポートフォリオの話題がでることがほとんどありません。なので、未経験が転職するのに必要なのは高度な技術を使用しすごいポートフォリオを作るより、面接対策を徹底したほうが転職できる可能性が高まると今回の転職活動で感じました。現在スクール卒のエンジニアが応募しすぎて、またスクール卒かよと思われるので、スクール卒を隠して独学で勉強しましたと言うと差別化を図れるかもしれません。(他の同期も同じ意見の方が多いです。)自分の転職活動について
自分は8月に退職してあるスクールに入校しました。そこから3カ月おなじみのRubyOnRailsを使い11月にポートフォリオが完成した為、転職活動を始めました。11-1月の間でwantedlyで140件程応募しましたがほとんど書類選考は通りません。11月にいきなりReact/GOを使用している自社の書類選考が通りましたが3次面接で落ちました。
また振り出しに戻り12月からまた応募をどんどん出していき、12月もRailsを使用している自社の書類選考が通りましたが最終でおちました。
そして、また振り出しに戻り1月からまた応募を出してくことでJavaの企業に就職が決まりました。自分がしたこと
ポートフォリオが完成した後rspec、factorybot、Capybaraでテストを書いて、リファクタリングをしました。
これは11月に受けた企業がテストを重視する企業だったので行いました。CapybaraでE2Eテストを書いたり、N+1問題をなおしたり、クラスメソッドやインスタンスメソッドを使用してDRYコードを目指したり、Viewでロジックをなるべく書かないなど意識しました。自分としてもテストやDRYコードを意識してリファクタリングを行った方がいいと思います。なぜなら、もし面接する企業がgithubのコードをみてくれた時に他の人との差別化も図れますし、現職のエンジニアの方に聞いても、高度な技術のポートフォリオを作っているがテストも書いておらずコードも読みにくい人よりかは、テストやDRYコードを意識してリファクタリングを行っているひとの方が評価できると述べていました。
SQL
Rails以外の言語を触ってみてわかるのですが、RailsはSELECT文をほとんど書かなくていい為、SQL文など意識したことはありませんでしたが、N+1問題などを解決する時に必要だなと感じ勉強しました。
使用した参考書: すっきりわかるSQL入門
2周ぐらいしてほとんど理解しました。フロントも勉強してみようと思い、JavascriptのES2015の書き方を学習したあとreactでtodoアプリ、twitterクローンを作成しました。
SQLも学習し、バックのRailsしか触ったことがなかったので、学習しました。やっぱりrailsは簡単だなと感じました。正直フロントの方がおもしろかったです。
フロントを自分は学習しましたがおすすめはしません。QiitaでアウトプットやTwitterで100daysOfCodeを行った
Qiitaで自分が覚えておきたいことなどアウトプットしました。Twitterで100daysOfCodeを行い100日間コードを毎日書き続けると言うものでしたが、熱がでたこともあり、毎日は無理だなと感じ自分ルールで毎日はやめました。Qiitaに投稿したり、100daysOfCodeを行っていると面接の自己紹介のときアピールすることができます。100daysOfCodeを行っていると自然と勉強しないといけないと思うようになるのでおすすめです。
Rubyでビンゴを作成
好きな言語でビンゴのプログラムを作成するものがありました。
標準出力と標準入力すら知らない自分には最初まったくわかりませんでしたが、4日間ほど、毎日寝て食べる以外は全て、そのプログラミングにフルコミットしました、自分はrailsしか触っていなかったので、Rubyだけでやると色々勉強できてかなり実力がついたと思いました。プログラミングの勉強を初めて一番楽しかったです、パズルを解いてるかのような感覚で没頭しました。二重配列や標準出力と標準入力をわからない人は挑戦してみるといいかもしれません。おすすめのルート
独学やスクール卒でも共通でポートフォリオが完成した後は面接対策をメインに置きながら、テストやDRYコードを意識してリファクタリングしつつ、SQLなどを学び、2カ月以内に転職できたら理想です。
しかし現実はそう甘くないとおもいます。現在すぐに転職できたと言っている人は、相当スペックが高い人、運がいい人、Web制作の会社に就職した人だと思います。スクール
正直スクール入ろうか検討している方は深く考えた方がいいです。スクールの転職保証などあてになりません。SESを否定するわけではありませんが、SES企業しか紹介してもらえないのが現実です。あと、関西ではrailsを使用している企業が少ないです。求人をみても関西はPHP/Javaの二強です。なので関西で就職したくてスクール卒の方はRailsの企業に就職するのは厳しいと考えた方がいいです。
最後に
本当に転職活動しんどかったです。Wantedy、Greenなどで応募しまくりましたが、ほとんど書類選考で落ちます。自分を否定されてるようで辛かったです。転職期間は毎週面接を受けました。11、12といいところまで行った企業も落ちて振り出しに戻った為、年末年始は久しぶりに勉強をやめてやけ酒しました。
今も未経験から転職活動をしている方たちにこの記事が参考になれば幸いです。
未経験から転職活動は辛くて、厳しいですが頑張りましょう。心の底から未経験の仲間を応援しています。
- 投稿日:2021-01-21T14:19:01+09:00
コロナ渦での未経験からエンジニア転職に成功した上での感想
前置き
あくまで一個人の感想であり、特定の人物や団体に誹謗中傷はやめてください。
年齢は20代前半
高卒
文系です。数学への苦手意識は高い。
転職は初今の未経験からのエンジニア転職の状況について
正直かなり厳しい状況です。現在、未経験からのエンジニア転職はスクール卒のRubyを少しかじった未経験で溢れかえっており、経験者のエンジニアが欲しい企業と未経験から転職したいエンジニアの需要と供給が合致していません。正直、企業は未経験に技術は求めていないので、高度な技術を使用したポートフォリオは必要ありません。(Docker,CIなど)
そもそもポートフォリオをみていない可能性があります。なぜなら面接でポートフォリオの話題がでることがほとんどありません。なので、未経験が転職するのに必要なのは高度な技術を使用しすごいポートフォリオを作るより、面接対策を徹底したほうが転職できる可能性が高まると今回の転職活動で感じました。現在スクール卒のエンジニアが応募しすぎて、またスクール卒かよと思われるので、スクール卒を隠して独学で勉強しましたと言うと差別化を図れるかもしれません。(他の同期も同じ意見の方が多いです。)自分の転職活動について
自分は8月に退職してあるスクールに入校しました。そこから3カ月おなじみのRubyOnRailsを使い11月にポートフォリオが完成した為、転職活動を始めました。11-1月の間でwantedlyで140件程応募しましたがほとんど書類選考は通りません。11月にいきなりReact/GOを使用している自社の書類選考が通りましたが3次面接で落ちました。
また振り出しに戻り12月からまた応募をどんどん出していき、12月もRailsを使用している自社の書類選考が通りましたが最終でおちました。
そして、また振り出しに戻り1月からまた応募を出してくことでJavaの企業に就職が決まりました。自分がしたこと
ポートフォリオが完成した後rspec、factorybot、Capybaraでテストを書いて、リファクタリングをしました。
これは11月に受けた企業がテストを重視する企業だったので行いました。CapybaraでE2Eテストを書いたり、N+1問題をなおしたり、クラスメソッドやインスタンスメソッドを使用してDRYコードを目指したり、Viewでロジックをなるべく書かないなど意識しました。自分としてもテストやDRYコードを意識してリファクタリングを行った方がいいと思います。なぜなら、もし面接する企業がgithubのコードをみてくれた時に他の人との差別化も図れますし、現職のエンジニアの方に聞いても、高度な技術のポートフォリオを作っているがテストも書いておらずコードも読みにくい人よりかは、テストやDRYコードを意識してリファクタリングを行っているひとの方が評価できると述べていました。
SQL
Rails以外の言語を触ってみてわかるのですが、RailsはSELECT文をほとんど書かなくていい為、SQL文など意識したことはありませんでしたが、N+1問題などを解決する時に必要だなと感じ勉強しました。
使用した参考書: すっきりわかるSQL入門
2周ぐらいしてほとんど理解しました。フロントも勉強してみようと思い、JavascriptのES2015の書き方を学習したあとreactでtodoアプリ、twitterクローンを作成しました。
SQLも学習し、バックのRailsしか触ったことがなかったので、学習しました。やっぱりrailsは簡単だなと感じました。正直フロントの方がおもしろかったです。
フロントを自分は学習しましたがおすすめはしません。QiitaでアウトプットやTwitterで100daysOfCodeを行った
Qiitaで自分が覚えておきたいことなどアウトプットしました。Twitterで100daysOfCodeを行い100日間コードを毎日書き続けると言うものでしたが、熱がでたこともあり、毎日は無理だなと感じ自分ルールで毎日はやめました。Qiitaに投稿したり、100daysOfCodeを行っていると面接の自己紹介のときアピールすることができます。100daysOfCodeを行っていると自然と勉強しないといけないと思うようになるのでおすすめです。
Rubyでビンゴを作成
好きな言語でビンゴのプログラムを作成するものがありました。
標準出力と標準入力すら知らない自分には最初まったくわかりませんでしたが、4日間ほど、毎日寝て食べる以外は全て、そのプログラミングにフルコミットしました、自分はrailsしか触っていなかったので、Rubyだけでやると色々勉強できてかなり実力がついたと思いました。プログラミングの勉強を初めて一番楽しかったです、パズルを解いてるかのような感覚で没頭しました。二重配列や標準出力と標準入力をわからない人は挑戦してみるといいかもしれません。おすすめのルート
独学やスクール卒でも共通でポートフォリオが完成した後は面接対策をメインに置きながら、テストやDRYコードを意識してリファクタリングしつつ、SQLなどを学び、2カ月以内に転職できたら理想です。
しかし現実はそう甘くないとおもいます。現在すぐに転職できたと言っている人は、相当スペックが高い人、運がいい人、Web制作の会社に就職した人だと思います。スクール
正直スクール入ろうか検討している方は深く考えた方がいいです。スクールの転職保証などあてになりません。SESを否定するわけではありませんが、SES企業しか紹介してもらえないのが現実です。あと、関西ではrailsを使用している企業が少ないです。求人をみても関西はPHP/Javaの二強です。なので関西で就職したくてスクール卒の方はRailsの企業に就職するのは厳しいと考えた方がいいです。
最後に
本当に転職活動しんどかったです。Wantedy、Greenなどで応募しまくりましたが、ほとんど書類選考で落ちます。自分を否定されてるようで辛かったです。転職期間は毎週面接を受けました。11、12といいところまで行った企業も落ちて振り出しに戻った為、年末年始は久しぶりに勉強をやめてやけ酒しました。
今も未経験から転職活動をしている方たちにこの記事が参考になれば幸いです。
未経験から転職活動は辛くて、厳しいですが頑張りましょう。心の底から未経験の仲間を応援しています。
- 投稿日:2021-01-21T14:15:26+09:00
[Rails] GoogleMapを使って、マーカーが立っている位置をDBに保存する!
前提
・Railsのアプリケーションであること
・記事作成機能が存在すること
・GoogleMapを自分のアプリケーション上で表示することができていること
※下記記事などが非常に参考になると思います。https://qiita.com/nagaseToya/items/e49977efb686ed05eadb
目標
記事作成の際、マップにマーカーを1つだけ立てられるようにし、その位置情報をDBに保存すること。
マーカーの立て方としては以下を想定。・マップをクリック
・位置を検索それではやっていきましょう!
gemのインストール ※いらない方はスキップしてください
Gemfilegem 'geocoder' gem 'gon'※この記事においてはこの2つのgemは使用しません。しかし、これらのgemが入っているという前提で進めていきます。
geocoderは便利なメソッドが豊富です。
RailsでGoogleMapを使用のであれば入れておいたほうが良いです。
gonはController内でセットした変数をJavascript内で使う事ができます。
DBに保存した位置情報を記事詳細画面で表示させる際に非常に便利です。geocoder
https://github.com/alexreisner/geocodergon
https://github.com/gazay/gonこれらを記述した後、bundle install します。
マップの情報保存用テーブル作成
指定した緯度、経度を保存するテーブルを作成します。
まず、マイグレーションファイルを作成しましょう。
マイグレーションファイルはこんな感じにします。
私の場合は、記事と地図を関連付けさせたいのでこのようになっています。
マップ単品の方は
t.references :article, null: false, foreign_key: true
の部分はいらないかと思います。timestamp_xxx.rb(マイグレーションファイル)class CreateMaps < ActiveRecord::Migration[6.0] def change create_table :maps do |t| t.references :article, null: false, foreign_key: true t.string :address t.float :latitude t.float :longitude t.timestamps end end endその後、rails db:migrate します。
モデルの関連付け
関連付けさせなくて良い方はスキップしても大丈夫です。
map.rbclass Map < ApplicationRecord belongs_to :article geocoded_by :address after_validation :geocode endarticle.rbhas_one :map, dependent: :destroyマップの表示
表示だけならこれだけでできると思います。
〇〇.html.erb<div id='map'></div> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script> <script> let map function initMap (){ myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752} map = new google.maps.Map(document.getElementById('map'), { center: myLatLng, zoom: 12 }); </script>マップのクリックしたところにマーカーを立てることができるようにする
記事作成と同時にクリックした箇所の経度、緯度を保存できるようにします。
また、マーカを削除できるボタンも追加しました。_article_form.html.erb<div id="article_form" class="field"> <%= f.label :title, "タイトル" %> <%= f.text_area :title %> マップ <div id='map'></div> <%= f.fields_for :map, @article.build_map do |map| %> <%= map.hidden_field :latitude %> <%= map.hidden_field :longitude %> <% end %> <%= f.label :content, "本文" %> <%= f.rich_text_area :content,placeholder:"入力してください" %> <%= f.label :tag_list %> <%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %> </div> <div id="article_button"><%= f.submit "投稿!",class:"btn"%></div> <% end %> <button id="del" class="btn" onclick="deleteMarker();">マーカー削除</button> <script> var marker var myLatLng var map var map_lat var map_lng function initMap(){ myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752} marker = new google.maps.Marker(); map_lat = document.getElementById('article_map_latitude') map_lng = document.getElementById('article_map_longitude') map = new google.maps.Map(document.getElementById('map'), { center: myLatLng, zoom: 8 }); google.maps.event.addListener(map, 'click', mylistener); //クリックしたときの処理 function mylistener(event){ //markerの位置を設定 //event.latLng.lat()でクリックしたところの緯度を取得 marker.setPosition(new google.maps.LatLng(event.latLng.lat(), event.latLng.lng())); //marker設置 marker.setMap(map); map_lat.value = event.latLng.lat(); map_lng.value = event.latLng.lng(); } } function deleteMarker(){ marker.setMap(null); map_lat.value = ""; map_lng.value = ""; } </script> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script> 30下記のコードによってarticleと関連付けたmapにデータを渡すことができます。
<%= f.fields_for :map, @article.build_map do |map| %> <%= map.hidden_field :latitude %> <%= map.hidden_field :longitude %> <% end %>また、これらに値を入れるコードはこの部分です。
map_lat = document.getElementById('article_map_latitude') map_lng = document.getElementById('article_map_longitude') //↑map.hidden_fieldによって生成された<input>のidです。 //人によって違うかもしれないのでブラウザの開発者モードで確認してください。 //〜〜省略〜〜 map_lat.value = event.latLng.lat(); map_lng.value = event.latLng.lng();そして、値をコントローラに渡すため、StrongParameterの設定も必要です。
binding.pryなどで、どのようなparamsが渡ってきているか確認しておいたほうが良いかと思います。article_controller.rbdef article_params params.require(:article) .permit(:content, :title, map_attributes: [:id, :latitude, :longitude]) endこれを保存するコードはこちらです。
article_controller.rbdef create @article = current_user.articles.build(article_params) if @article.save latitude = params[:article][:map][:latitude] longitude = params[:article][:map][:longitude] unless latitude.empty && longitude.empty? @map = @article.build_map( latitude: latitude, longitude: longitude ) @map.save end #〜〜〜〜〜〜省略〜〜〜〜〜〜〜〜〜〜〜 end位置検索でもマーカーを立てることができるようにする
この場合、GoogleMapAPIだけでなく、Geocoding APIも必要になってきます。
紆余曲折あり、最終的に完成したのがこちらです。<div class="row"> <div class="col s12 m10 push-m1"> <div class="card"> <div class="card-content article-card"> <%= form_with(model:@article,local: true,class:"margin-30") do |f|%> <%= render 'shared/error_messages',object: f.object %> <div id="article_form" class="field"> <div class="input-field"> <%= f.text_field :title,class: "materialize-textarea" %> <%= f.label :title, "タイトル" %> </div> <div class="input-field"> <%= f.text_field :tag_list, value: @article.tag_list.join(','), class: "materialize-textarea" %> <%= f.label :tag_list,"タグ ※コンマ区切りで記入してください" %> </div> <!-- 追加したイベント --> <div class="input-field"> <input type="text" id="address" placeholder="地名、施設名などを入力するか、地図をクリックしてマーカーを立ててください"> <label>場所</label> <a class="btn" onclick="codeAddress()">地図検索</a> <a id="del" class="btn marker-delete right" onclick="deleteMarker()"> <i class="fas fa-map-marker-alt fas-2x" style="color:red"></i>削除 </a> </div> <!-- ここまで --> <div id='map' class="margin-t-b-14"></div> <%= f.fields_for :map, @article.build_map do |map| %> <%= map.hidden_field :latitude %> <%= map.hidden_field :longitude %> <% end %> <%= f.rich_text_area :content,placeholder:"入力してください",class: "materialize-textarea" %> <%= f.label :content, "本文" %> <%= f.label :thumbnail, "サムネイル" %> <%= f.file_field :thumbnail, accept: "image/jpeg,image/gif,image/png",class:"file_field" %> <div id="article_button"><%= f.submit "投稿!",class:"btn col s6 m6 article-form-btn push-s3 push-m3"%></div> </div> <% end %> </div> </div> </div> </div> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script> <script> var marker var myLatLng var map_lat var map_lng var geocoder var map_result let map function initMap (){ myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752} map_lat = document.getElementById('article_map_latitude') map_lng = document.getElementById('article_map_longitude') marker = new google.maps.Marker(); map = new google.maps.Map(document.getElementById('map'), { center: myLatLng, zoom: 12 }); google.maps.event.addListener(map, 'click', mylistener); //クリックしたときの処理 function mylistener(event){ marker.setPosition(new google.maps.LatLng(event.latLng.lat(), event.latLng.lng())); marker.setMap(map); console.log(event.latLng.lat(), event.latLng.lng()); map_lat.value = event.latLng.lat(); map_lng.value = event.latLng.lng(); } } function deleteMarker(){ marker.setMap(null); map_lat.value = ""; map_lng.value = ""; } //追加した関数 function codeAddress(){ geocoder = new google.maps.Geocoder() let inputAddress = document.getElementById('address').value; geocoder.geocode( { 'address': inputAddress, 'region': 'jp' }, function(results, status) { if (status == 'OK') { map.setCenter(results[0].geometry.location); map_result = results[0].geometry.location; map_lat.value = map_result.lat(); map_lng.value = map_result.lng(); marker.setPosition(new google.maps.LatLng(map_result.lat(), map_result.lng())); marker.setMap(map); console.log(map_lat.value,map_lng.value); } else { alert('該当する結果がありませんでした'); initMap(); } }); } </script>随分長いコードになってしまいました。
しかし、実際は関数とイベントが1つ増えただけなのでその部分を見てもらえればわかるかと思います!
これによって、・地図をクリック
・位置を検索
することで地図上にマーカーを立て、その位置情報をDBに保存することができるようになりました!
これらを利用する方法についてはまた後日記事を書こうかと思います。
- 投稿日:2021-01-21T14:15:26+09:00
[Rails6] GoogleMapを使って、マーカーが立っている位置をDBに保存する!
前提
・Railsのアプリケーションであること
※私のRailsのバージョンは6.0.3です
・記事作成機能が存在すること
・GoogleMapを自分のアプリケーション上で表示することができていること
※下記記事などが非常に参考になると思います。https://qiita.com/nagaseToya/items/e49977efb686ed05eadb
目標
記事作成の際、マップにマーカーを1つだけ立てられるようにし、その位置情報をDBに保存すること。
マーカーの立て方としては以下を想定。・マップをクリック
・位置を検索(GeocodingAPIを使用)※記事最下部に完成版のgifあり
それではやっていきましょう!
gemのインストール ※いらない方はスキップしてください
Gemfilegem 'geocoder' gem 'gon'※この記事においてはこの2つのgemは使用しません(次の記事で使用予定)。しかし、これらのgemが入っているという前提で進めていきます。
geocoderは便利なメソッドが豊富です。
RailsでGoogleMapを使用のであれば入れておいたほうが良いです。
gonはController内でセットした変数をJavascript内で使う事ができます。
DBに保存した位置情報を記事詳細画面で表示させる際に非常に便利です。geocoder
https://github.com/alexreisner/geocodergon
https://github.com/gazay/gonこれらを記述した後、bundle install します。
マップの情報保存用テーブル作成
指定した緯度、経度を保存するテーブルを作成します。
まず、マイグレーションファイルを作成しましょう。
私の場合は、記事と地図を関連付けさせたいのでこのようになっています。
マップ単品の方は
t.references :article, null: false, foreign_key: true
の部分はいらないかと思います。timestamp_xxx.rb(マイグレーションファイル)class CreateMaps < ActiveRecord::Migration[6.0] def change create_table :maps do |t| t.references :article, null: false, foreign_key: true t.string :address t.float :latitude t.float :longitude t.timestamps end end endその後、rails db:migrate します。
モデルの関連付け
関連付けさせなくて良い方はスキップしても大丈夫です。
map.rbclass Map < ApplicationRecord belongs_to :article geocoded_by :address after_validation :geocode endarticle.rbhas_one :map, dependent: :destroyマップの表示
表示だけならこれだけでできると思います。
〇〇.html.erb<div id='map'></div> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script> <script> let map function initMap (){ myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752} map = new google.maps.Map(document.getElementById('map'), { center: myLatLng, zoom: 12 }); </script>マップ上でクリックしたところにマーカーを立てることができるようにする
記事作成の際に、マップ上でクリックした箇所にマーカーを立てられるようにします。
また、マーカーが立っている箇所の経度、緯度を保存できるようにします。
ついでにマーカーを削除できるボタンも追加しました。_article_form.html.erb<div id="article_form" class="field"> <%= f.label :title, "タイトル" %> <%= f.text_area :title %> マップ <div id='map'></div> <%= f.fields_for :map, @article.build_map do |map| %> <%= map.hidden_field :latitude %> <%= map.hidden_field :longitude %> <% end %> <%= f.label :content, "本文" %> <%= f.rich_text_area :content,placeholder:"入力してください" %> <%= f.label :tag_list %> <%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %> </div> <div id="article_button"><%= f.submit "投稿!",class:"btn"%></div> <% end %> <button id="del" class="btn" onclick="deleteMarker();">マーカー削除</button> <script> var marker var myLatLng var map var map_lat var map_lng function initMap(){ myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752} marker = new google.maps.Marker(); map_lat = document.getElementById('article_map_latitude') map_lng = document.getElementById('article_map_longitude') map = new google.maps.Map(document.getElementById('map'), { center: myLatLng, zoom: 8 }); google.maps.event.addListener(map, 'click', mylistener); //クリックしたときの処理 function mylistener(event){ //markerの位置を設定 //event.latLng.lat()でクリックしたところの緯度を取得 marker.setPosition(new google.maps.LatLng(event.latLng.lat(), event.latLng.lng())); //marker設置 marker.setMap(map); map_lat.value = event.latLng.lat(); map_lng.value = event.latLng.lng(); } } function deleteMarker(){ marker.setMap(null); map_lat.value = ""; map_lng.value = ""; } </script> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script> 30下記のコードによってarticleと関連付けたmapにデータを渡すことができます。
<%= f.fields_for :map, @article.build_map do |map| %> <%= map.hidden_field :latitude %> <%= map.hidden_field :longitude %> <% end %>また、これらに値を入れるコードはこの部分です。
map_lat = document.getElementById('article_map_latitude') map_lng = document.getElementById('article_map_longitude') //↑map.hidden_fieldによって生成された<input>のidです。 //人によって違うかもしれないのでブラウザの開発者モードで確認してください。 //〜〜省略〜〜 map_lat.value = event.latLng.lat(); map_lng.value = event.latLng.lng();そして、値をコントローラに渡すため、StrongParameterの設定も必要です。
binding.pryなどで、どのようなparamsが渡ってきているか確認しておいたほうが良いかと思います。article_controller.rbdef article_params params.require(:article) .permit(:content, :title, map_attributes: [:id, :latitude, :longitude]) endこれを保存するコードはこちらです。
article_controller.rbdef create @article = current_user.articles.build(article_params) if @article.save latitude = params[:article][:map][:latitude] longitude = params[:article][:map][:longitude] unless latitude.empty && longitude.empty? @map = @article.build_map( latitude: latitude, longitude: longitude ) @map.save end #〜〜〜〜〜〜省略〜〜〜〜〜〜〜〜〜〜〜 end位置検索でもマーカーを立てることができるようにする
この場合、GoogleMapAPIだけでなく、Geocoding APIも必要になってきます。
紆余曲折あり、最終的に完成したのがこちらです。<div class="row"> <div class="col s12 m10 push-m1"> <div class="card"> <div class="card-content article-card"> <%= form_with(model:@article,local: true,class:"margin-30") do |f|%> <%= render 'shared/error_messages',object: f.object %> <div id="article_form" class="field"> <div class="input-field"> <%= f.text_field :title,class: "materialize-textarea" %> <%= f.label :title, "タイトル" %> </div> <div class="input-field"> <%= f.text_field :tag_list, value: @article.tag_list.join(','), class: "materialize-textarea" %> <%= f.label :tag_list,"タグ ※コンマ区切りで記入してください" %> </div> <!-- 追加したイベント --> <div class="input-field"> <input type="text" id="address" placeholder="地名、施設名などを入力するか、地図をクリックしてマーカーを立ててください"> <label>場所</label> <a class="btn" onclick="codeAddress()">地図検索</a> <a id="del" class="btn marker-delete right" onclick="deleteMarker()"> <i class="fas fa-map-marker-alt fas-2x" style="color:red"></i>削除 </a> </div> <!-- ここまで --> <div id='map' class="margin-t-b-14"></div> <%= f.fields_for :map, @article.build_map do |map| %> <%= map.hidden_field :latitude %> <%= map.hidden_field :longitude %> <% end %> <%= f.rich_text_area :content,placeholder:"入力してください",class: "materialize-textarea" %> <%= f.label :content, "本文" %> <%= f.label :thumbnail, "サムネイル" %> <%= f.file_field :thumbnail, accept: "image/jpeg,image/gif,image/png",class:"file_field" %> <div id="article_button"><%= f.submit "投稿!",class:"btn col s6 m6 article-form-btn push-s3 push-m3"%></div> </div> <% end %> </div> </div> </div> </div> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script> <script> var marker var myLatLng var map_lat var map_lng var geocoder var map_result let map function initMap (){ myLatLng = {lat: 35.68090045006303, lng: 139.76690798417752} map_lat = document.getElementById('article_map_latitude') map_lng = document.getElementById('article_map_longitude') marker = new google.maps.Marker(); map = new google.maps.Map(document.getElementById('map'), { center: myLatLng, zoom: 12 }); google.maps.event.addListener(map, 'click', mylistener); //クリックしたときの処理 function mylistener(event){ marker.setPosition(new google.maps.LatLng(event.latLng.lat(), event.latLng.lng())); marker.setMap(map); console.log(event.latLng.lat(), event.latLng.lng()); map_lat.value = event.latLng.lat(); map_lng.value = event.latLng.lng(); } } function deleteMarker(){ marker.setMap(null); map_lat.value = ""; map_lng.value = ""; } //追加した関数 function codeAddress(){ geocoder = new google.maps.Geocoder() let inputAddress = document.getElementById('address').value; geocoder.geocode( { 'address': inputAddress, 'region': 'jp' }, function(results, status) { if (status == 'OK') { map.setCenter(results[0].geometry.location); map_result = results[0].geometry.location; map_lat.value = map_result.lat(); map_lng.value = map_result.lng(); marker.setPosition(new google.maps.LatLng(map_result.lat(), map_result.lng())); marker.setMap(map); console.log(map_lat.value,map_lng.value); } else { alert('該当する結果がありませんでした'); initMap(); } }); } </script>随分長いコードになってしまいました。
しかし、実際は関数とイベントが1つ増えただけなのでその部分を見てもらえればわかるかと思います!
これによって、マップをクリックor位置を検索することで地図上にマーカーを立て、その位置情報をDBに保存することができるようになりました!
これらを利用する方法についてはまた後日記事を書こうかと思います。
- 投稿日:2021-01-21T12:23:47+09:00
Railsチュートリアルはじめます
はじめに
2020年12月末からプログラミング学習を始め、2021年1月21日現在で以下の学習を終えました。
- HTML&CSSコース (#Progate)
- はじめてのHTML (#ドットインストール)
- はじめてのCSS (#ドットインストール)
- JavaScriptコース (#Progate)
- 詳解JavaScript 基礎文法編 (#ドットインストール)
- Rubyコース (#Progate)
- Ruby入門 (#ドットインストール)
- SQLコース (#Progate)
- MySQL入門 基礎編 (#ドットインストール)
- MySQL入門 応用編 (#ドットインストール)
- はじめてのGitとGitHub (#Udemy)
学習を通じて
今までは日々の学習をTwitterでアウトプットしていましたが、Qiitaでのアウトプットも挑戦しようと思います。
Railsチュートリアルの学習で以下のアウトプットをしていく予定です。
- できるようになったこと
- わからない→わかったまでの解決方法
- 感想や意気込み
また、Qiitaでのアウトプットを通じて、以下のスキルを伸ばしていきたいと考えています。
- マークダウン方式をできるようになる
- 140文字以上のアウトプットに慣れる
さいごに
- もっとこうしたほうがいいよ!
- ここのエラーはこういう意味だよ!
などなど…お気づきの点がございましたら、コメントいただけると大変励みになります。
どうぞよろしくお願い申し上げます。
- 投稿日:2021-01-21T12:01:50+09:00
bundle install エラー
実行環境:
Winodows 10 Pro (64bit)
Vagrant 2.2.14
Ubuntu 16.04.6 LTS
Docker 18.09.7
ruby:ruby 2.7.2p137
Docker のコンテ内でRubyのsinatraライブラリをインストールするために
bundle install を実行したところ、以下のエラーが発生しました。
````Fetching source index from https://rubygems.rog/ Retrying fetcher due to error (2/4): Bundler::HTTPError Could not fetch specs from https://rubygems.rog/ Retrying fetcher due to error (3/4): Bundler::HTTPError Could not fetch specs from https://rubygems.rog/ Retrying fetcher due to error (4/4): Bundler::HTTPError Could not fetch specs from https://rubygems.rog/ Could not fetch specs from https://rubygems.rog/以下のコマンドを実行したら解決したという情報を入手
````gem update --system再度インストールを実行。変化がありません。
wget https://api.rubygems.org/specs.4.8.gz を実行してダウンロードできるか試しましたが、これは成功しました。
ドメインの IP アドレスを調べました。
````root@dfed40fdb477:/var/www# host api.rubygems.org api.rubygems.org is an alias for rubygems.org. rubygems.org has address 151.101.2.132 rubygems.org has address 151.101.130.132 rubygems.org has address 151.101.194.132 rubygems.org has address 151.101.66.132 rubygems.org has IPv6 address 2a04:4e42::644 rubygems.org has IPv6 address 2a04:4e42:200::644 rubygems.org has IPv6 address 2a04:4e42:600::644 rubygems.org has IPv6 address 2a04:4e42:400::644 rubygems.org mail is handled by 10 mxb.mailgun.org. rubygems.org mail is handled by 10 mxa.mailgun.org.hostファイルに記述しました。
151.101.2.132 api.rubygems.org 151.101.130.132 api.rubygems.org 151.101.194.132 api.rubygems.org 151.101.66.132 api.rubygems.org 2a04:4e42::644 api.rubygems.org 2a04:4e42:200::644 api.rubygems.org 2a04:4e42:600::644 api.rubygems.org 2a04:4e42:400::644 api.rubygems.orgインストールが正常にできました。
- 投稿日:2021-01-21T11:28:40+09:00
【Rails チュートリアル】"NoMethodError (undefined method `activation_digest=' for #<User:0x00000003156938>
"NoMethodError (undefined method `activation_digest=' for #User:0x00000003156938
について
何故か本番環境だけで、このエラーがみられた
user.rb
def activate self.update_attribute(:activated, true) self.update_attribute(:activated_at, Time.zone.now) endとself.をつけ忘れてた
何故、ローカルだと問題なかったのか、、、
- 投稿日:2021-01-21T06:43:26+09:00
railsでのカスタムヘルパーの使い方
引用先
Railsチュートリアルカスタムヘルパーとは
Railsでは多くの組み込み関数が使えますが、使いたいメソッドがなかった時にオリジナルのメソッドを作成できます。
この新しく作ったメソッドのことをカスタムヘルパーといいます。ファイルの場所
app/helpers/application_helper.rbmodule ApplicationHelper endファイルの位置は app/helpers/application_helper.rb
最初はこのようなファイル構造になっています。使い方
例えばこのように記述する。
app/helpers/application_helper.rbmodule ApplicationHelper def full_title(page_title = '') base_title = "Sample App" if page_title.empty? base_title else page_title + " | " + base_title end end endapplication.html.erbファイルの呼び出したい箇所へ記述。
今回はタイトルタグに記述します。app/views/layouts/application.html.erb<!DOCTYPE html> <html> <head> <title><%= full_title(yield(:title)) %></title> </head> <body> <%= yield %> </body> </html>このように記述すれば、タイトルタグにヘルパーファイルに記述したこのコードが呼び出されます。
def full_title(page_title = '') base_title = "Sample App" if page_title.empty? base_title else page_title + " | " + base_title end end # コード解説 if文によって、page_titleが.empty?(入れ物が空→true,入れ物がある→false) →true:base_title(Sample App)を表示 →false:page_title|base_title(Sample App)を表示今回はコードの量が多くないため恩恵を感じにくいですが、コードを別ファイルに分けることでコードが見やすくなりますね。
- 投稿日:2021-01-21T06:43:26+09:00
railsでカスタムヘルパーの使い方
引用先
Railsチュートリアルカスタムヘルパーとは
Railsでは多くの組み込み関数が使えますが、使いたいメソッドがなかった時にオリジナルのメソッドを作成できます。
この新しく作ったメソッドのことをカスタムヘルパーといいます。ファイルの場所
app/helpers/application_helper.rbmodule ApplicationHelper endファイルの位置は app/helpers/application_helper.rb
最初はこのようなファイル構造になっています。使い方
例えばこのように記述する。
app/helpers/application_helper.rbmodule ApplicationHelper def full_title(page_title = '') base_title = "Sample App" if page_title.empty? base_title else page_title + " | " + base_title end end endapplication.html.erbファイルの呼び出したい箇所へ記述。
今回はタイトルタグに記述します。app/views/layouts/application.html.erb<!DOCTYPE html> <html> <head> <title><%= full_title(yield(:title)) %></title> </head> <body> <%= yield %> </body> </html>このように記述すれば、タイトルタグにヘルパーファイルに記述したこのコードが呼び出されます。
def full_title(page_title = '') base_title = "Sample App" if page_title.empty? base_title else page_title + " | " + base_title end end # コード解説 if文によって、page_titleが.empty?(入れ物が空→true,入れ物がある→false) →true:base_title(Sample App)を表示 →false:page_title|base_title(Sample App)を表示今回はコードの量が多くないため恩恵を感じにくいですが、コードを別ファイルに分けることでコードが見やすくなりますね。
- 投稿日:2021-01-21T03:15:51+09:00
discordrbで音声が使用できない場合の対処
環境
- OS: Windows10(64bit)
- Ruby: ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [i386-mingw32]
起動時にこんなエラーが出る!
libsodium not available! You can continue to use discordrb as normal but voice support won't work. Read https://github.com/shardlab/discordrb/wiki/Installing-libsodium for more details.対処
URLに記載の方法だと、何度やっても解消しない(おそらくx64-mingw32版Ruby用の説明のため)。
C:\Windows\System32
ではなく、C:\Windows\SysWOW64
に32bit用のlibsodium.dll
をsodium.dll
にリネームして入れてやる。libopusも入っていなければついでに
libopus.dll
をopus.dll
にリネームして入れると良いと思います。あとがき
こんな情報、私の検索能力ではどう調べても出てこなくて数日悩んで試行錯誤の上やっと気づきました(笑)
つよつよ開発者の方々はこんなこと当たり前に分かるのかなぁ、ともっと精進しなければと思わされました..参考URL
libsodium: https://download.libsodium.org/libsodium/releases/
libopus: https://dsharpplus.github.io/natives/index.html
- 投稿日:2021-01-21T02:51:53+09:00
Dockerを用いてRuby on Railsの環境構築をする方法( Docker初学者向け )
Dockerを初めて勉強するとき、多くの方が「参考書」や「入門Dockerなどのサイト」で基礎的なところから学習すると思います。
しかし、Dockerとは何なのか、何のイメージも沸いていない状態で、様々な単語を羅列されても、さっぱりわからないという方も多いと思います。( 少なくとも私はそうでした。)
なので極論、基礎すっ飛ばしてまず実際にDockerで仮想環境を構築しながら学んでいこうという話です。
そして何となくでいいのでイメージを掴んでから、基礎を学べば良いと思います。
Dockerを初めて学習する方の中で、Ruby on Railsで開発したアプリケーションをポートフォリオとしている方も多いのではないでしょうか。
なので今回は、Dockerを用いてRuby on Railsの環境構築をする方法 をご紹介いたします。ですが、超基本的なことだけ先に説明させてください。
( とにかく早く環境構築した人は 前提 まで飛んでください )Dockerとは
- Dockerエンジンとは -
Dockerエンジンとは Dockerイメージの作成 や コンテナの起動 などを行う、いわばDockerの要です。
- コンテナとは -
コンテナとはDockerエンジン上で構築される仮想環境そのものです。
コンテナは、CentOS
やUbunts
などの OS、Nginx
やMySQL
などの ミドルウェア、Rails
やWordPress
などアプリケーションまで、様々な環境を構築することができます。- Dockerイメージとは -
Dockerイメージは、コンテナを作成するための レシピ のようなものです。
CentOS、MySQL、Ruby などなど、他にも数えきれないほどのDockerイメージがあります。- Dockerfileとは -
Dockerfileは、オリジナルのDockerイメージを作成するための設計書のようなものです。
既存のDockerイメージをベースに、パッケージをインストールしたり、ファイルを書き換えたりした、新しいイメージを作成することができます。- Docker composeとは -
Docker compose とは、複数のコンテナの構築、実行する手順を自動化するツールです。
その手順などを記したファイルがdocker-compose.yml
で、これのおかげで少ないコマンドの実行で複数のコンテナを起動することができます。環境
- MacOS
- Docker 20.10.2
- docker-compose 1.27.4
- Ruby 2.5.7
- Rails 5.2.4
- MySQL 5.7
前提
Docker
およびDocker compose
のインストールを済ませてください- これ以降の説明の
myapp
の部分はご自身のアプリケーション名に変えてください- とにかく早く環境構築したい方は、説明を飛ばし、コピペして コマンドを実行してください。
アプリケーションの作業ディレクトリを作成
mkdir
で作業ディレクトリを作成します。$ mkdir myapp必要なファイルを用意
ファイル構成は以下のようになります。
myapp |-- Dockerfile |-- docker-compose.yml |-- Gemfile |-- Gemfile.lockそれでは、先ほど作成したディレクトリの中に以下の各ファイルを作成していきます。
① Gemfile
Gemfilesource 'https://rubygems.org' gem 'rails', '~> 5.2.4', '>= 5.2.4.2'③で作成する
Dockerfile
でbundle install
する項目があるのですが、そのときにこのGemfile
を使います。② Gemfile.lock
Gemfile.lock
Gemfile.lock
の中身は空でOKです。
Gemfile.lock
はGemfile
とセットで必要なので、用意します。③ Dockerfile
DockerfileFROM ruby:2.5.7 # ベースにするイメージを指定 RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs default-mysql-client vim # RailsのインストールやMySQLへの接続に必要なパッケージをインストール RUN mkdir /myapp # コンテナ内にmyappディレクトリを作成 WORKDIR /myapp # 作成したmyappディレクトリを作業用ディレクトリとして設定 COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock # ローカルの Gemfile と Gemfile.lock をコンテナ内のmyapp配下にコピー RUN bundle install # コンテナ内にコピーした Gemfile の bundle install COPY . /myapp # ローカルのmyapp配下のファイルをコンテナ内のmyapp配下にコピー
Dockerfile
は自分オリジナルの イメージ を作成するための設計書のようなものです。
今回はruby:2.5.7
という元々誰かが作ったイメージをベースに、Railsのインストールに必要なパッケージをインストールしたり、myapp
をコピーしたり、bundle install
したりと、様々なもの加えているというわけです。④ docker-compose.yml
docker-compose.ymlversion: '3' # docker-composeの書式のバージョンを指定します。(原則、最新を指定する) services: db: image: mysql:5.7 environment: MYSQL_USER: root MYSQL_ROOT_PASSWORD: pass ports: - "3306:3306" volumes: - mysql_data:/var/lib/mysql web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/myapp/ ports: - 3000:3000 depends_on: - db volumes: mysql_data:
docker-compose.yml
は複数のコンテナを定義し、実行するためのファイルです。
ファイルに書かれている単語をひとつずつ解説してきます。services:
services
の中でコンテナを定義します。
今回はMySQL
とRails
をコンテナ化するので、名前をわかりやすくdb
、web
とします。
実際に作成されるコンテナ名は「アプリケーション名 + サービス名 + 連番」でmyapp_db_1
、myapp_web_1
となります。image:
利用するイメージを指定し、コンテナを構築します。
enviroment:
MySQLに関する環境変数を設定します。(今回はパスワードのみ)
パスワードはご自身で決めて構いません。ports:
ポート番号に関するを設定します。
MySQL は3306
、 Rails は3000
というポート番号がデフォルト値として設定されています。
Railsに関してはおなじみのlocalhost:3000
の 3000 ですね。
Dockerを使わず、ローカルで環境構築した場合は直接このlocalhost:3000
にアクセスすれば良かったわけですが、Dockerを使った場合、コンテナに外部からアクセスすることができないので、ちょっとした工夫が必要です。
そこで登場するのがこのports:
です。
ports:
は- ローカル側のポート番号 : コンテナ側のポート番号
で表します。
今回、ports: - 3000:3000
としていますが、説明をわかりやすくするため、仮にもしports: - 9000:3000
に設定するとします。
これはコンテナ側の3000ポート
に9000ポート
でのアクセスを許可するという意味になります。
つまりこの場合、localhost:9000
でアクセス可能になるということになります。volumes:
ボリュームに関する設定をします。
ボリューム
とはコンテナにおいて生成されるデータを永続的に保存するためのものです。
MySQLを例にとると、MySQLをコンテナ化し、そこに テーブル や カラム などのデータを保存したとします。
しかし、そのMySQLのコンテナを削除してしまうと、保存してあったテーブル、カラムなどのデータも一緒に削除されてしまいます。
それはまずいので、データだけローカルにも保存しておく、この仕組みをボリューム
といいます。
このボリュームを利用すると、仮にコンテナを削除したとしても、新しくコンテナを立ち上げたときに、そのデータを再利用することができます。ボリュームを保存する方法は大きく分けて2つあります。
1つ目は、保存したいディレクトリをマウントする方法、2つ目は、名前付きボリュームを利用する方法 です。今回
MySQL
は2つ目の名前付きボリュームを利用する方法
を使います。この方法は、ローカルに名前付きのボリュームを作成し、そこにコンテナ側の指定したディレクトリを保存するというものです。
名前付きボリュームの場合、volumes:
は- ボリューム名 : コンテナ側の保存させたいディレクトリ
で表します。
今回、ボリューム名
をmysql_data
とし、コンテナ側の保存させたいディレクトリ
は MySQLのデータ保存場所である/var/lib/mysql
を指定します。
結果、volumes: - mysql_data:/var/lib/mysql
という書き方になります。
そして、version: や services: と同じ段落(トップレベル)の、一番下に書いているvolumes:
にボリューム名を記載し、名前付きボリュームであることを明示します。次に
Rails
は、1つ目の保存したいディレクトリをマウントする方法
を使います。
この方法は、「ローカル側の指定したディレクトリ」と「コンテナ側の指定したディレクトリ」を同期させて、両者を常に同じ状態に保つというものです。
マウントする場合、volumes:
は- ローカル側の同期させたいディレクトリ : コンテナ側の同期させたいディレクトリ
で表します。
今回、volumes: - .:/myapp/
としています。
つまり、.
( docker-compose.ymlのあるディレクトリ、つまりローカル側のmyapp配下
) と/myapp/
( コンテナ側のmyapp配下
) を同期するということです。
これにより、コンテナ起動後、ローカルのmyapp配下のファイルなどを編集すると、コンテナ側にも編集が反映されます。build:
Dockerfileのあるディレクトリをして指定します。( docker-compose.ymlから見て同じディレクトリにDockerfileが存在するので
.
)
ここのステップで ③で説明したDockerfile
を利用しオリジナルのイメージを作成し、コンテナを構築します。command:
コンテナ起動時の実行されるコマンドを設定します。
今回設定するコマンドは、bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
です。
bash -c ""
は コンテナの中に入り""
内のコマンドを実行するという命令です。
rm -f tmp/pids/server.pid
で rails server が起動していた場合、停止します。( 何らかの原因でrails server がすでに起動していた場合、新しいrails serverが起動できないため。)
bundle exec rails s -p 3000 -b '0.0.0.0'
で rails server を起動します。
&&
は複数のコマンドを実行するときに用います。depends_on:
コンテナの作成順序の設定です。
depends_on: - db
は、MySQLのコンテナ
が起動してからRailsのコンテナ
を起動するという意味です。rails new を実行
$ docker-compose run web rails new . --force --database=mysql --skip-bundleアプリケーションの作業ディレクリに移動し、
docker-compose run
コマンドでrails new
を実行します。
--force
: 同じファイルがある場合、上書きする
--database=mysql
: データベースにMySQLを指定する
--skip-bundle
: bundle install をスキップする。( bundle installはあとで行います。)このコマンドを実行すると、
Dockerfile
を元に、イメージとコンテナを作成し、そのコンテナの中でrails new
を実行します。
よって、ローカルにも見慣れたファイルが作成されたと思います。docker-compose build を実行
$ docker-compose build
rails new
でGemfile
が更新されたので、bundle install
するため、docker-compose build
を実行します。
先ほどのdocker-compose run
で作成されたイメージはrails new
される前のGemfile
をbundle install
したイメージなので、更新されたGemfile
をbundle install
したイメージを再度作成する必要があります。database.yml を編集
/config/database.yml# MySQL. Versions 5.1.10 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: # https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html # default: &default adapter: mysql2 encoding: utf8 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root # 以下2行を編集 password: pass host: db development: <<: *default database: myapp_development # 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: myapp_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: myapp_production username: myapp password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>
password:
とhost:
を編集します。
password:
にはdocker-compose.yml
のMYSQL_ROOT_PASSWORD:
に書いたパスワードを記載します。
host:
にはdocker-compose.yml
で命名したMySQLのコンテナ名db
を記載します。docker-compose up -d でコンテナを起動
$ docker-compose up -dこのコマンドでコンテナを起動します。
-d
をつけることでバックグラウンドで起動します。これでコンテナが起動できました。
以下のコマンドでちゃんと起動しているか確認しましょう。$ docker-compose ps Name Command State Ports ---------------------------------------------------------------------------------------- myapp_db_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp, 33060/tcp myapp_web_1 bash -c rm -f tmp/pids/ser ... Up 0.0.0.0:3000->3000/tcpこのように、それぞれのコンテナが
UP
になっていれば起動成功です。データベースを作成
以下のコマンドでデータベースを作成します。
$ docker-compose exec web rails db:createlocalhost:3000にアクセス
localhost:3000
にアクセスし、以下のページが表示されれば DockerによるRailsの環境構築 は完了です。
環境構築後の開発の仕方
これまで通り、ファイルの編集などは基本ローカル環境で行います。
しかしrails g
やrails db:migrate
などの railsコマンド はコンテナ内で実行します。
なので、コンテナに入る必要があります。
以下のコマンドでRailsのコンテナに入ります。$ docker-compose exec web bashするとプロンプト( ターミナルの左側の部分 )が以下のように変わると思います。( @の後ろはコンテナのID )
root@97b8e3430f3f:/myapp#これでコンテナの中に入れました。ここで railsコマンド を実行してください。
これまでのローカル環境での開発で、ターミナルで実行していたコマンドは全てこのコンテナ内で行います。( gitコマンドはローカル環境で行います )コンテナを抜けるには、以下のコマンドを実行してください。( もしくは
control + d
)$ exit- ログの表示 -
これまでは
rails s
でサーバーを起動し、ログが表示されていたと思います。
コンテナ内において、ログを表示させる方法は以下の通りです。コンテナ内に入り、以下のコマンドを実行します。
$ tail -f log/development.logこれでログが表示されます。
ターミナルでタブを3つ開き、「ローカルの操作をするタブ」、「コンテナ内でrailsコマンドなどを実行するタブ」、「ログを表示するタブ」に分けると効率的な開発ができるかもしれません。
- デバッグに関して -
開発中にエラーが発生し、controllerを編集し、デバッグしたとします。
この場合、一度サーバーを再起動させると思います。Dockerの場合、Railsのコンテナ( myapp_web_1 )ごと以下のコマンドで再起動します。
$ docker-compose restart webしかし、デバッグするたびにいちいちコンテナを再起動するのは非効率です。
なので、サーバー起動中もコードの更新をチェックしてくれるように設定します。
config/environments/development.rb
の一番下に記載されている以下をconfig.file_watcher = ActiveSupport::EventedFileUpdateChecker次のように変更します。
config.file_watcher = ActiveSupport::FileUpdateCheckerこれで再起動を行わずにデバッグが可能になります。
その他の基本的なDockerのコマンド
・ コンテナの停止、削除
$ docker-compose down・ コンテナの停止、削除および紐づいている名前付きボリュームの削除
$ docker-compose down -v※ 永続化しているデータが消えるの要注意 !
・ docker-composeのログを出力
$ docker-compose logsコンテナがうまく起動しなかったときなどにログを見ると、原因がわかったりします。
・ イメージの一覧
$ docker images・ イメージの削除
$ docker rmi イメージID・ 名前付きボリュームの一覧
$ docker volume ls・ 名前付きボリュームの削除
$ docker volume rm ボリュームの名前まとめ
最初の内は理解できないことも多いと思います。
ひとつずつ着実に理解していくのではなく、全体的に漠然と理解していくと良いと思います。この記事を読んでわからないことがあれば、コメントしていただければお答えいたします !
最後まで読んでいただきありがとうございました。
参考
- 投稿日:2021-01-21T02:39:48+09:00
ゲッターとセッターをの理解をごっちゃにしないために【なんでもかんでもアクセサを使わない!】
ゲッターとセッターの解説記事は何個かあるので、そこはお任せするとして、
あと、アクセサという便利なコードもありますが、使い分けについて書かれてる物がすくなかったので、初学者編の基礎として振り返ろうと思って書きました。まずはよくある車を設計する所です。
アクセサを使える場合
車の設計class Car attr_accessor :model, :color, :option def initialize(model, color, option) @model = model @color = color @option = option end end car = Car.new("Benz", "white", "ホイール") #=> すべて変更可能これだったら、
attr_accessor
でまとめてしまっても良いのですが、
例えば 「もう車作ってるのに、車種と色変えられたら困るよ!!!(オプションだけだったらいいけど・・・)」 という場合の書き方は、ゲッターとセッターを使う場合
車の設計class Car attr_reader :model, :color, :option attr_writer :option def initialize(model, color, option) @model = model @color = color @option = option end end car = Car.new("Benz", "white", "ホイール") #=>オプションだけ変更可能っていう書き方が普通になりますけど、
アクセサを使って更にスマートに書くのであれば、アクセサとゲッターを使うのがスマート
車の設計class Car attr_accessor :option attr_reader :model, :color def initialize(model, color, option) @model = model @color = color @option = option end end car = Car.new("Benz", "white", "ホイール") #=>オプションだけ変更可能になります。
ちなみに、
車の車種を変更しようとすると・・・
車の設計class Car attr_accessor :option attr_reader :model, :color def initialize(model, color, option) @model = model @color = color @option = option end end car = Car.new("Benz", "white", "ホイール") car.model = "BMW" #=>NoMethodError (undefined method `model=' for #<Car:0x00007fc009900440>)という風に怒られます!!「車種変えるんじゃねぇよ!もう作ってるんだよ!」という事ですね。色を変えようとしても同じようなエラーになります。
まとめ
という事で、Ruby初学者が勉強するゲッター、セッター、アクセサの使い分けについて振り返っていきました。
初学者の方の参考になれば幸いです。ありがとうございました!!