- 投稿日:2020-02-19T23:26:02+09:00
未経験から転職活動する際に、後悔したこと
未経験エンジニアが転職活動した際に、後悔しないようにする。
自分がいろいろ方向性が定まっておらず、後悔した点を多いので、記載しました。
まずは学習した言語で個人制作のAppを作成する。
Ruby on Railsを学習しているなら、まずはRailsでAppを作成する。
作成が完了できたら、Rubyの会社に応募していく。
まだ何も学習していない人は、PHP/Laravelから学習するといいと思います。なぜなら求人数がPHPの方が圧倒的に多いからです。「DB設計は完璧なものを作ろう」
→これなかなか作業できない。やっちゃえ精神の方が学習スピード早いです。
難しい機能を作ろうとしている際は、いきなりDB設計完璧にしようとしても経験値が不足しているのだから、進みません。
複数回作り直してもいいのだから、基本的なDB設計をしたら作ってしまおう。完璧主義な人は陥りやすいかな。足りないカラムは後で足せばいい。「デザインどうしよう???」
デザインに時間をかけてはダメ!!それよりも学習しなければいけないことが、たくさんある。
WEBデザイナーではないし、デザインに時間がけず、ダサいサイトでもいいから、とにかく機能を実装する。その後デザインを綺麗にすればいい。機能が揃ってから、興味を引くようなデザインにすればいい。WEBデザイナーになりたいのか、プログラマーになりたいのかどっち?「どの言語で個人Appをつくろうかなあ??」
この時間無駄です。まずは自分ができる言語で作ればいいじゃん。
Rubyを学習した後にPHPで制作するか悩んでいた自分に言ってやりたい。「このアイデア公開したくないなあ。」
自分の制作物を見せるべきか、悩むことがあると思います。
いいアイデアだとなおさらです。ですが、どうせ今の自分にはそれを世に広げる技術がないのだから、まずは作ってしまえばいい。作成ができたら、面接にも持っていけるし、投資家に出資してくれと相談もできる。それに起業するような人材は、すでに独立していると思うし、そんなアイデアパクらんでしょ。思った以上に人は行動しないから、大丈夫。ポートフォリオとしてサービスを展開していきましょう!それが誰よりもそのアイデア広げる道です。「ポートフォリオサイトを作った方がいいかな」 → 後悔
個人制作物を十分に揃えていない状態で作成、割と後悔しました。
結局、個人制作物を出してと言われるので、ポートフォリオサイトはなくてもいいです。個人制作物が増えてからじっくり作成するべきで、始めに作るのは時間の無駄。
制作物がないポートフォリオには価値がない(笑)
人物像だけ紹介されたも「で? 技術は??」ってなるわけです。
わざわざHTML、CSS、PHP、Laravelなどでゴリゴリ書いてポートフォリオサイトは作らなくてもいい。
他のポートフォリオになりそうなサービスを利用した方が、賢いGithubをポートフォリオにすれば、よかった!!
まずは、下記のリンクを開いて欲しい。どんな機能があるのかわかりやすい。
これで十分にポートフォリオになる。
参考になるGithubアカウントGithubはどれだけ学習しているのか、その日の色分けでわかる。
個人制作の機能説明に関しては、下記のリンクのように充実するといいでしょう。Githubアカウントは間違いなくチェックされるので、まずはGithubのアカウントの充実度をあげればいい。
その後でポートフォリオサイト作れば、すぐに作れる。Dockerより、Vue.jsをやればよかった。
Docker学習したいと思う気持ちがでるけど、Javascriptの方が重視している企業が多かった。
RubyとVue.jsを使って制作している企業もあるので、Vue.jsとReact.jsを学習するべき。Vue.js
ドットインストール:Vue.js
js系は一部の機能だけ実装できたり、必要な分だけで済む。ミニアプリを作成してGithubにあげておけばアピールに繋がるはず。
Dockerは途中から導入できるので、ひとまず慣れている普段のローカル環境で個人Appを作成すればいい。Dockerは途中から切り替えればOK
今制作した制作物を途中からDockerに切り替えることが可能。だったら、とりあえず個人制作物を作成完了してから、切り替えればいい。そうすれば、就職活動を始めるタイミングも早まるし、制作物も途中からDockerに切り替えることで学習規模も小さく抑えられる。Dockerの技術を深めるのであれば、本命の制作物ではなくて、どうなってもいいミニアプリでじっくり試した方がいい。
ドットインストール(Docker)
ドットインストール(試験運用中Docker)
途中からDockerに切り替える新しい言語で個人制作する。(PHP)
Ruby on Railsをやったら、今度はPHPでAppを作成する。
DB設計に時間をかけすぎず、失敗しながら作成していく。
本当に大きな失敗をしたら、作り直せばいい。
とにかく作る回数、経験値が不足しているから、試作品をサクサク作ればいい。「大作を作ろう」とは思わない
これは失敗するフラグです。機能を細かく分けて、一つの機能だけのAppを作ればいい。
- カレンダー機能だけ
- JSによるゲーム
- 簡単な投稿機能、いいね!機能これらはミニアプリで学習してから、本命のAppに実装すればいい。
いきなり本命Appに実装しようとしない。細かく学べば、一日一機能学習できるUdemyで学習して後悔。。。失敗しない教材はドットインストール
Udemyはオススメしない。プログラミング学習に特化したサイトではないので、使いづらい。
また、教材のスキルレベルが低い。Windowsでの制作が多いため、やりづらい。PHPは下記から学習するのが望ましい。
ドットインストール:Laravel講座正直、Udemyで学習を始めたのは大きな失敗だった。時間の無駄だったと後悔。
ドットインストールはPHPのカリキュラムが充実している。おそらく製作者もPHPを使っているのだろう。明らかにPHP/Laravelだけ充実度が違う。
PHPはドットインストールから学習した方が確実
- 投稿日:2020-02-19T22:06:23+09:00
【初学者のつまずきを記録】RailsでのMySQL接続:mysql.sock問題
はじめに
RailsでのMySQL接続手順について、備忘録的にまとめようと思います。
作業自体はごく基本的な内容かと思いますが、
初学者である僕が実際につまずいたことを重点的に記載しました。
僕と同じ初学者の方のご参考の一助となればうれしいです。
後半部分ではつまずいた原因について検討してみました。※僕自身は「RailsのDBを(初めから| |後から)MySQLに変更する」を参考にしながら進めました。
前準備:MySQLを指定してRailsアプリを作成
Railsの仕様上、データベースを指定しない場合は自動的にSQLiteとなる。
$ rails new アプリ名 -d mysql作成されたconfig/database.ymlを確認。
config/database.ymldefault: &default adapter: mysql2 (略)確かにMySQLとなっていることを確認。
MySQLとの接続
手順通り接続を試みる。が、エラー発生。
$ mysql -u root ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)mysql.sockを通じての接続ができなかったとのこと。
調べてみると、mysql.sockファイル自体が存在しないことが問題らしい。
確かに、tmpフォルダ内を確認してみても存在しない。下記実行で作成。$ sudo touch /tmp/mysql.sockその上で接続を試みても同じようなエラー発生。
$ mysql -u root ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (38)そもそも、mysql.sockファイルとは何だろう?
調べてみると、サーバーとクライアントの間で通信を行う際の仲介役的な役割らしい。
ここで、MySQLサーバーを起動していないことに気づきました。
というかエラー分にもしっかりMySQL server
とありますね。MySQLサーバーを起動。
$ mysql.server start Starting MySQL .. ERROR! The server quit without updating PID file (/usr/local/var/mysql/xxxx.pid).PIDファイルがないとのお達し。
指定された場所に作成をする。$ touch /usr/local/var/mysql/xxxx.pidその上でサーバー起動を試みるも、、、
$ mysql.server start Starting MySQL .rm: /tmp/mysql.sock: Permission denied 2020-02-17T12:13:34.6NZ mysqld_safe Fatal error: Can't remove the socket file: /tmp/mysql.sock. Please remove the file manually (略)別のエラーが発生。
mysql.sockを削除しろと??
ないと言うから作ったのに。言われるがままに削除し、
どこか不本意な気持ちのまま再度サーバー起動。成功。$ mysql.server start Starting MySQL . SUCCESS! $ mysql -u root Welcome to the MySQL monitor. (略) mysql>MySQLへの接続も成功したようだ。釈然としない。
成功した理由を考えてみた
調べてみると、mysql.sockはMySQLサーバー起動時に自動的に作成されるファイルであり、
手動で作成する類のものではないらしい。
つまり、一番始めに発生した下記エラーは、mysql.sockを作成していないことが問題ではなく、
「Can't connect
:つなげない状態」であったことが問題であったと思われる。$ mysql -u root ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)よって、mysql.sockがない状態でも、
PIDファイルを作成してサーバー起動を成功させることによって(=ここでmysql.sockが自動作成され)
MySQLへの接続も成功した、、、という理解でいいのだろうか。では手動作成のmysql.sockは何が問題であったのか?
調べてみるとどうやら手動で作成したmysql.sockは権限に問題があるようだ。
検証のために再度手動でmysql.sockを作成し、
自動作成(サーバー起動時)されたmysql.sockと$ ls -l
の出力を比較してみた。#手動 -rw-r--r-- 1 root wheel 0 2 17 22:56 mysql.sock #自動作成 srwxrwxrwx 1 xxxx(ユーザー名) wheel 0 2 17 22:45 mysql.sockなるほど違う。
手動で作成された方はrootユーザーのみが書き込みの権限を持っている。
書き込みの権限がないとうまくいかないので、(参考:MySQL の設定上の考慮事項)
rootユーザー以外での実行ができない状態となっている??ならばと手動作成した上でrootユーザー権限でサーバー起動を試みてみる。
$ sudo mysql.server start Starting MySQL . ERROR! The server quit without updating PID file (/usr/local/var/mysql/xxxx.pid).だめかー。
では手動作成したものを自動作成と同じ権限に変えてみる。
これでダメな理由はないはず。$ sudo chmod 777 mysql.sock #権限を変更 $ sudo chown xxxx(ユーザー名) mysql.sock #所有者も変更 $ mysql.server start Starting MySQL . SUCCESS!成功した!
手動でmysql.sockを作成した場合でも
権限周りを調整することによりサーバー起動ができることの確認がとれました。まとめ
解決方法を色々とググりながら進めましたが、
「まずはじっくりとエラー分を解釈すること」の大切さを痛感しました。
またlinux等の知識が不足しており、後半の検証部分は気持ちとしてしこりが残っております。
(rootユーザー権限でサーバー起動できなかったあたり等)
今後の課題にするようにしたいと思います。はじめての記事投稿でしたが、思考を整理するとても良い機会となりました。
誤り・認識違い、アドバイス等ご指摘いただけましたらとてもありがたいです。
- 投稿日:2020-02-19T16:52:09+09:00
サイドナビのリンクを開くと、そのナビだけ色が変わる。
サイドナビの色を変化させる。
サイドナビでナビをクリックすると、ナビの色が変化して、今開いているナビがわかるようにする。
処理の作成
application_helper.rbmodule ApplicationHelper def active_class(link_path) # (link_path)にrake routeの_pathを記載する。 "active" if request.fullpath == link_path # もしも開いたページのURL(request.fullpath)が、指定の(link_path)と同じ場合、"active"になる。 end endclassを付与させる
html.haml%li{class: "#{active_class(users_path)}"} // application_helper.rbで作成した処理により、activeが付与する。 // 指定したリンク以外では、activeは付与しない。 // この例の場合、users_pathのため、users#showのページを開くとナビの色が変化するこれで、完成
おまけにHTMLとSCSS
haml%ul.list-group %li{class: "list-group-item #{active_class(user_path)}"} = link_to "", user_path, class: "nav-list__link" %p マイページ %li{class: "list-group-item #{active_class(new_product_path)}"} = link_to "", new_product_path, class: "nav-list__link" %p コンテンツscss.list-group{ width: 250px; &-item{ position: relative; /* li全体をaタグにする。 */ a{ text-decoration: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; } &::after{ /* 矢印 */ position: absolute; top: 40%; right: 13px; content: ''; width: 8px; height: 8px; border-top: 2px solid #ccc; border-right: 2px solid #ccc; -webkit-transform: rotate( -50%); -webkit-transform: rotate( -50%); transform: rotate(45deg); transition: all .2s; } &:hover{ background: #fafafa; &::after{ right: 9px; border-color: #333; } } /* 今開いているページのナビは、色を変化 */ &.active{ background: #EEEEEE; font-weight: bold; border-color: #3333; color: #212529; &::after{ border-top: 2px solid #333333; border-right: 2px solid #333333; } .nav-list{ &__link{ text-decoration: none; font-weight: bold; color: #333333; font-size: 14px; } } &:hover{ background: #EEEEEE; } } } }
- 投稿日:2020-02-19T16:46:52+09:00
フリマアプリ購入機能実装(pay.jp)購入編
前回の記事の続きです。
Payjp(Pay.jp)から既に顧客IDとカードIDを取得済みでの購入を想定しています。
前提条件
・devise/hamlが導入済みでログインができている
・payjpのアカウントが既に取得できていて、ユーザーとカードの登録が完了しており、cardテーブルに以下の情報が登録されている
user_id ... UserテーブルのID
customer_id ... payjpの顧客ID
card_id ... payjpのデフォルトカードID
顧客IDとデフォルトカードIDは以下の画面で顧客ごとに確認できます。
1.コントローラーを作成しよう
コントローラとビュー(indexとdone)を作成するため、下記コマンドを実行します。
$ rails g controller buyers index doneコントローラーの中身を編集
app/controllers/buyers_controller.rbclass BuyersController < ApplicationController require 'payjp'#Payjpの読み込み before_action :set_card, :set_item def index if @card.blank? #登録された情報がない場合にカード登録画面に移動 redirect_to new_card_path else Payjp.api_key = Rails.application.credentials[:PAYJP_PRIVATE_KEY] #保管した顧客IDでpayjpから情報取得 customer = Payjp::Customer.retrieve(@card.customer_id) #カード情報表示のためインスタンス変数に代入 @default_card_information = customer.cards.retrieve(@card.card_id) end end def pay Payjp.api_key = Rails.application.credentials[:PAYJP_PRIVATE_KEY] Payjp::Charge.create( :amount => @item.price, #支払金額を引っ張ってくる :customer => @card.customer_id, #顧客ID :currency => 'jpy', #日本円 ) redirect_to done_item_buyers_path #完了画面に移動 end def done end private def set_card @card = Card.find_by(user_id: current_user.id) end def set_item @item = Item.find(params[:item_id]) end endset_card , set_itemの記述で、cardとitemの情報をもってきます
今回は、先ほど作成したindexから、form_tag内のaction: 'pay'で、controller内のpayを発動2.購入画面と完了画面を作成しよう
購入画面
app/views/buyers/index.html.haml%h2.buy-content__title 購入内容の確認 .buy-content__item .buy-content__item__inner .buy-item-main .buy-item-image = image_tag "#{@item.images[0].url}", size: '64x64', class: 'buydetails-contet__image' .buy-item-detail .buy-item-name = @item.name %p.buy-price = "¥#{@item.price.to_s}" %span.shipping-free (税込) 送料込み .buy-content__item %form.buy-form .buy-price-table .buy-price-table__left 支払金額 .buy-price-table__right = "¥#{@item.price.to_s}" .buy-content__user-info .buy-content__user-info__inner %h3 支払方法 .user-info-update = link_to "変更する", "#", calss:"update-btn" .user-info-text - if @default_card_information.blank? %br / - else = "**** **** **** " + "#{@default_card_information.last4}" %br - exp_month = @default_card_information.exp_month.to_s - exp_year = @default_card_information.exp_year.to_s.slice(2,3) = "有効期限 " + exp_month + " / " + exp_year %br .buy-content__user-info .buy-content__user-info__inner %h3 配送先 .user-info-update = link_to "変更する","#", calss:"update-btn" .user-info-text 〒111-1111 %br 大阪府大阪市北区〇〇1-11 %br 山田太郎 = form_tag(action: :pay, method: :post) do %button.buy-button{type:"submit"} 購入する*exp_monthはカードの期限月、exp_yearは期限年、last4はカードの下4桁を取得
PAYJP カードオブジェクト完了画面
app/views/purchase/done.html.haml%h1.buy-content__attention %i.far.fa-clock 発送をお待ちください %h2.buy-content__attention__title 購入が完了しました .buy-content__item .buy-content__item__inner .buy-item-main .buy-item-image = image_tag "#{@item.images[0].url}", size: '64x64', class: 'buydetails-contet__image' .buy-item-detail .buy-item-name = @item.name %p.buy-price %span = "¥#{@item.price.to_s}" %span .shipping-free (税込) 送料込み .buy-content__item %form.buy-form .buy-price-table .buy-price-table__left 支払金額 .buy-price-table__right = "¥#{@item.price.to_s}" .buy-content__user-info__submit = link_to "トップページへ戻る", root_path, class: 'buy-content__user-info__submit__button'3.ルートを設定しよう
ルーティング設定ですが、商品詳細ページからitem_idを引き継ぎたかったので、下記のようにしました
config/routes.rbresources :items do resources :buyers, only: [:index] do collection do get 'done', to: 'buyers#done' post 'pay', to: 'buyers#pay' end end end購入の確認
参考
https://qiita.com/takachan_coding/items/d21c0d2621368c9b0d9b
https://qiita.com/Ikuy_h/items/7232ba32e0b728ff77aa
- 投稿日:2020-02-19T15:26:37+09:00
ruby 2.7.0 CSV の liberal_parsing オプションについて調査してみた
概要
Ruby標準ライブラリのCSVの挙動についてのまとめ。
ruby 2.4.0 CSV の liberal_parsing オプションについて調査してみた のruby2.7.0バージョン。csvの仕様
rubyの標準ライブラリのCSVパーサーではRFC4180準拠のパースが行われる。
こちらの仕様では、コンマの直後と次のコンマの直前に"
をおくことで、コンマを含んだ文字列を一つのフィールドとして扱うことができる。CSV.parse('a,"b,c",d') => [["a", "b,c", "d"]]ダブルクオートを含む文字列を表すためには、もう一つのダブルクオートでエスケープする必要がある。
CSV.parse('a,"b""c",d') => [["a", "b\"c", "d"]]しかし、RFC4180はなかなか厳密な仕様で例えば
a,b"c,d
のような文字列は不正なcsvになり、例外が発生する。CSV.parse('a,b"c,d') # CSV::MalformedCSVError (Illegal quoting in line 1.)一方で上記のようなフォーマットは頻繁に出てきうるため、これを厳密にエラーにしていると取り扱いが難しい場合がある。そのために実用的なパースを行うための
liberal_parsing
というオプションがruby2.4.0から導入されている。
上記の記事ではruby2.4を使ってliberal_parsingの挙動について調査を行っているが、ruby2.7で確認したところ挙動が変わっていたところも多くあったのでここでまとめておく。全般的にruby2.7の方が挙動が直感的になっている。liberal_parsing のruby2.7での挙動
ダブルクォートに囲まれたフィールドの場合
正しくエスケープされたダブルクォートの場合
CSV.parse('a,"bb""b",c', liberal_parsing: true) => [["a", "bb\"b", "c"]]文中にエスケープされていないダブルクォートがある場合
CSV.parse('a,"bb"b",c', liberal_parsing: true) => [["a", "\"bb\"b\"", "c"]]ruby2.4では例外は発生していた。
ダブルクォートに囲まれていないフィールドの場合
途中にダブルクォートがある場合
SV.parse('a,bb"b,c', liberal_parsing: true) => [["a", "bb\"b", "c"]]途中にエスケープされたダブルクォートがある場合
CSV.parse('a,bb""b,c', liberal_parsing: true) => [["a", "bb\"\"b", "c"]]先頭にのみダブルクォートがある場合
CSV.parse('a,"bbb,c', liberal_parsing: true) # CSV::MalformedCSVError (Unclosed quoted field in line 1.)ruby2.4と同様に例外。
先頭と途中にダブルクォートがあって、ダブルクォートの合計が偶数の場合
CSV.parse('a,"bb"b,c', liberal_parsing: true) => [["a", "\"bb\"b", "c"]]先頭と途中にダブルクォートがあって、ダブルクォートの合計が奇数の場合
CSV.parse('a,"b"b"b,c', liberal_parsing: true) => [["a", "\"b\"b\"b", "c"]]ruby2.4では例外が発生していたが、ruby2.7ではパースできるようになった。
末尾と途中にダブルクォートがある場合
CSV.parse('a,bb"b",c', liberal_parsing: true) => [["a", "bb\"b\"", "c"]]ruby2.4と同様にパースできる。
- 投稿日:2020-02-19T15:23:25+09:00
ActiveAdmin 画面遷移時にfilterのパラメーターをセットする
親要素から子要素に遷移する際, 親要素のidで絞りたい
これだけと何をいってるかわからないので
例) Company : Shop = 1 : N のRelationの時, Companyの画面から, Shopの一覧画面へ遷移する時, そのままではデータが大きすぎるので, 最初からShopを外部キーのCompany_Idで絞っておきたい# カスタム アクション # 一度変数に入れないと, エスケープされずエラーとなる query = "q[company_id_equals]" action_item :my_button1, only: :show do link_to "店舗の一覧に遷移する", active_admin_shops_path({query => company.id}) endこれだけで遷移と同時にfilterがかかります.
調べても載っておらず, 尚且つ需要があると思うので書きました.
- 投稿日:2020-02-19T14:48:35+09:00
フリマアプリ購入機能実装(pay.jp)
最終課題のチーム開発で担当した部分を自分の復習を兼ねて記録用に書きました。
商品の講入機能の実装
・クレジット登録(payjp)
削除・再登録できる
・Payjpのコンソールからその売り上げが確認できる
・クレジット登録していないと購入できない
・購入すると商品状態が切り替わる(売り切れる)PAY.JP クレジットカード機能
payjpとは?
PAY.JPを使うと、シンプルなAPIで
Webサービスなどにクレジットカード決済機能を簡単に導入できる。前提条件
hamlでの記載(gem 'haml-rails')
deviseが導入済みでログインができている
→Devise未導入や何もない状態からスタートする場合は、
『Devise導入の設定手順 ~haml使用/pay.jp導入の前準備~ (Rails)』を先に実施。1.PAY.JPアカウントの作成
Payjpのサイトでアカウントを作成。
2.APIを確認しよう
ダッシュボードのAPIより確認ができます。
今回はテストモードでの実装なので、テスト秘密鍵とテスト公開鍵を使用。
3.payjpのgemを設置しよう
下記をgemfileに記載しbundle installを実施.
gem 'payjp'4.payjp.jsを読み込めるようにしよう
%script{src: "https://js.pay.jp/", type: "text/javascript"}を下記の通り追記します。
app/views/layouts/application.html.haml%html %head %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ %title payjptest %script{src: "https://js.pay.jp/", type: "text/javascript"} -# このscriptを記載 = csrf_meta_tags = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %body = yield5.テーブルを作成しよう
下記コマンドでpayjpのデータを保管するテーブルを作成します。
rails g model Card user_id:integer customer_id:string card_id:stringテーブルのカラムの紐づけは下記の通りです。
user_id ... Userテーブルのid
customer_id ... payjpの顧客id
card_id ... payjpのデフォルトカードid※デフォルトカードidはトークンとは違う
db/migrate/20200206000000_create_cards.rbclass CreateCards < ActiveRecord::Migration[5.2] def change create_table :cards do |t| t.integer :user_id, null: false t.string :customer_id, null: false t.string :card_id, null: false t.timestamps end end endマイグレーションを実施
$ rails db:migrate※カード情報そのものを保存することは禁止されている
payjpに保管されている情報を顧客idやカードidで呼び出すことで情報取得や支払いなどに対応6.コントローラーを作成しよう
app/controllers/cards_controller.rbclass CardsController < ApplicationController def new card = Card.where(user_id: current_user.id) redirect_to card_path(current_user.id) if card.exists? end def pay #payjpとCardのデータベース作成 Payjp.api_key = Rails.application.credentials[:PAYJP_PRIVATE_KEY] #保管した顧客IDでpayjpから情報取得 if params['payjp-token'].blank? redirect_to new_card_path else customer = Payjp::Customer.create( card: params['payjp-token'], metadata: {user_id: current_user.id} ) @card = Card.new(user_id: current_user.id, customer_id: customer.id, card_id: customer.default_card) if @card.save redirect_to card_path(current_user.id) else redirect_to pay_cards_path end end end def destroy #PayjpとCardデータベースを削除 card = Card.find_by(user_id: current_user.id) if card.blank? else Payjp.api_key = Rails.application.credentials[:PAYJP_PRIVATE_KEY] customer = Payjp::Customer.retrieve(card.customer_id) customer.delete card.delete end redirect_to new_card_path end def show #Cardのデータpayjpに送り情報を取り出す card = Card.find_by(user_id: current_user.id) if card.blank? redirect_to new_card_path else Payjp.api_key = Rails.application.credentials[:PAYJP_PRIVATE_KEY] customer = Payjp::Customer.retrieve(card.customer_id) @default_card_information = customer.cards.retrieve(card.card_id) end end endコントローラ内のRails.application.credentials[:PAYJP_PRIVATE_KEY]は環境変数でテスト秘密鍵を設定し読み込みます。
credential.ymlファイルにAPIキーを記載credential.ymlPAYJP_PRIVATE_KEY = 'sk_test_000000000000000000000000' PAYJP_KEY = 'pk_test_00000000000000000000000'(自分が他にも参考にした記事、メモ)
※where:与えられた条件にマッチするレコードをすべて返す。
https://qiita.com/nakayuu07/items/3d5e2f8784b6f18186f2
※blank?:空のオブジェクト、またはnilのオブジェクトかどうかを判定するメソッド。 ( empty? || nil? と同等) 空のオブジェクト、またはnilのオブジェクトの場合は true 値が存在する場合は false
※ActiveRecordで条件に一致するデータが存在するかどうかを調べたいときは、exists?を使う。
https://qiita.com/uw9623/items/851ac5f71e316834c0fa
※メタデータとは、本体であるデータに関する付帯情報が記載されたデータです。データのためのデータ7.カードの登録画面を作成しよう
登録画面と確認兼削除画面の2つを作成
登録画面
app/view/cards/new.html.haml= form_tag(pay_cards_path, method: :post, id: 'charge-form', name: "inputForm") do %label カード番号 .require 必須 = text_field_tag "number", "", class: "number", placeholder: "半角数字のみ" ,maxlength: "16", type: "text", id: "card_number" %ul.signup-card-list %li = image_tag "https://www-mercari-jp.akamaized.net/assets/img/card/visa.svg?238737266",width:"49px",height:"20px" %li = image_tag "https://www-mercari-jp.akamaized.net/assets/img/card/master-card.svg?238737266",width:"34px",height:"20px" %li = image_tag "https://www-mercari-jp.akamaized.net/assets/img/card/saison-card.svg?238737266",width:"30px",height:"20px" %li = image_tag "https://www-mercari-jp.akamaized.net/assets/img/card/jcb.svg?238737266",width:"32px",height:"20px" %li = image_tag "https://www-mercari-jp.akamaized.net/assets/img/card/american_express.svg?238737266",width:"21px",height:"20px" %li = image_tag "https://www-mercari-jp.akamaized.net/assets/img/card/dinersclub.svg?238737266",width:"32px",height:"20px" %li = image_tag "https://www-mercari-jp.akamaized.net/assets/img/card/discover.svg?238737266",width:"32px",height:"20px" %br %label 有効期限 .require 必須 %select#exp_month{name: "exp_month", type: "text"} %option{value: ""} -- %option{value: "1"}01 %option{value: "2"}02 %option{value: "3"}03 %option{value: "4"}04 %option{value: "5"}05 %option{value: "6"}06 %option{value: "7"}07 %option{value: "8"}08 %option{value: "9"}09 %option{value: "10"}10 %option{value: "11"}11 %option{value: "12"}12 %span 月/ %select#exp_year{name: "exp_year", type: "text"} %option{value: ""} -- %option{value: "2019"}19 %option{value: "2020"}20 %option{value: "2021"}21 %option{value: "2022"}22 %option{value: "2023"}23 %option{value: "2024"}24 %option{value: "2025"}25 %option{value: "2026"}26 %option{value: "2027"}27 %option{value: "2028"}28 %option{value: "2029"}29 %span 年 %br %br %label セキュリティコード .require 必須 = text_field_tag "cvc", "", class: "cvc", placeholder: "カード背面3~4桁の番号", maxlength: "4", id: "cvc" %br #card_token %br = submit_tag "追加する", id: "token_submit"※参考記事がこの書き方だったのでそのまま書きましたが
他の記事も載せておきます
【Rails】date_selectタグの使い方メモ(自分が他にも参考にした記事、メモ)
※text_field_tagの使い方
text_field_tagヘルパーは、Viewファイルのform_withヘルパー内に記載することで、文字列入力用のテキストボックスを実現することができるViewヘルパーtext_field_tag(テキストボックス名, 初期文字列, {オプション1, オプション2,,,})
「テキストボックス名」は、テキストフィールドのidとnameに割り当てられ、フォームから値を取得するときのキー
「初期文字列」は、テキストフィールドのデフォルト値で、指定された文字列が入力された状態で表示される
オプションには、以下の4種類に加え、各種HTML属性をハッシュ形式で設定することが可能
:disable 無効化(trueにすると、入力できなくなる)
:size 表示可能文字数
:maxlength 入力可能文字数
:placeholder フィールド内にデフォルト表示される文字列。ただし、値として設定されているわけではなく、フォーカスが当たると削除される※append()の基本的な使い方
append()メソッドは、指定した要素内の最後に引数のコンテンツを追加するメソッドです。
コンテンツにはテキストの他、HTML要素やJQueryオブジェクトが指定できる。確認兼削除画面
app/view/card/show.html.hamlクレジットカード情報 .form-content %br = "**** **** **** " + @default_card_information.last4 %br - exp_month = @default_card_information.exp_month.to_s - exp_year = @default_card_information.exp_year.to_s.slice(2,3) = exp_month + " / " + exp_year = form_tag(card_path(current_user.id), method: :delete, id: 'charge-form', name: "inputForm") do %input{ type: "hidden", name: "card_id", value: "" } %button.delete-btn 削除する(自分が他にも参考にした記事、メモ)
※sliceでカード年数の2、3番目を取得
※type属性をhiddenにしたときの大きな特徴は、「送信したいデータがブラウザに表示されない」ブラウザ上では見えないことを利用して、ユーザーからの命令がどのような種類なのかをサーバーに送信するときに判断させるために、hiddenを使うこともある8.Payjpにデータを送りトークンを取得しよう
jQueryを使用するので、railsに未設定の場合は設定をしてください。
設定方法はこちら←app/assets/javascripts/payjp.jsdocument.addEventListener( "DOMContentLoaded", e => {//DOM読み込みが完了したら実行 if (document.getElementById("token_submit") != null) { //token_submitというidがnullの場合、下記コードを実行しない Payjp.setPublicKey("pk_test_31f3ec18c086406c969b76cb"); //ここに公開鍵を直書き let btn = document.getElementById("token_submit"); //IDがtoken_submitの場合に取得 btn.addEventListener("click", e => { //ボタンが押されたときに作動 e.preventDefault(); //ボタンを一旦無効 let card = {//カード情報生成 number: document.getElementById("card_number").value, cvc: document.getElementById("cvc").value, exp_month: document.getElementById("exp_month").value, exp_year: document.getElementById("exp_year").value }; //入力されたデータを取得 Payjp.createToken(card, (status, response) => {//トークン生成 if (status === 200) { //成功した場合 $("#card_number").removeAttr("name"); $("#cvc").removeAttr("name"); $("#exp_month").removeAttr("name"); $("#exp_year").removeAttr("name"); //データを自サーバにpostしないように削除 $("#card_token").append( $('<input type="hidden" name="payjp-token">').val(response.id) ); //取得したトークンを送信できる状態 document.inputForm.submit(); alert("登録が完了しました"); } else { alert("カード情報が正しくありません。"); } }); }); } }, false );(自分が他にも参考にした記事、メモ)
※ボタンを押したタイミングで PAY.JP のサーバにクレジットカード情報を送信し、結果としてトークンなどの情報を受け取っている
※removeさせることでカード情報をparamsの値として含まれないようにしています
※HTTP レスポンスステータスコードは、特定の HTTP リクエストが正常に完了したどうかを示します。レスポンスは 5 つのクラスに分類されています。
1. 情報レスポンス (100–199),
2. 成功レスポンス (200–299),
3. リダイレクト (300–399),
4. クライアントエラー (400–499),
5. サーバエラー (500–599)
-成功レスポンス200-
リクエストが成功したことを示します。成功が意味することは、 HTTP メソッドにより異なります。
* GET: リソースが読み込まれ、メッセージ本文で転送された。
* HEAD: メッセージ本文にエンティティヘッダーある。
* PUT または POST: 操作の結果を表すリソースがメッセージ本文で送信される。
* TRACE: メッセージ本文に、サーバーが受け取ったリクエストメッセージが含まれている。9.ルートを作成しよう
config/routes.rbresources :cards, only: [:new, :show, :destroy] do collection do post 'pay', to: 'cards#pay' end end(自分が他にも参考にした記事、メモ)
リソースベースのルーティングでは「index」「show」「new」「edit」「create」「update」「destroy」の7つのアクションへのルーティング自動で設定されます。これに別のアクションを呼び出すためのルーティングを追加する為collectionを記述します。10.カードを登録してみよう
http://localhost:3000/cards/new にアクセスして登録できるか確認。
その時、テストカードで登録するようにしてください。
それ以外を打ち込んだ場合はトークンが発行できずはねられてしまいます。購入編←
参考
https://qiita.com/takachan_coding/items/f7e70794b9ca03b559dd
- 投稿日:2020-02-19T12:44:01+09:00
Rails6 のちょい足しな新機能を試す 121(PostgreSQL index_exists?編)
はじめに
Rails 6 に追加された新機能を試す第121段。 今回は、
PostgreSQL index_exists?
編です。
Rails 6 (と Rails 5.2.4.1) では、index_exists?
が正しく動作しないバグが fix されています。Ruby 2.6.5, Rails 6.0.2.1, Rails 5.2.4.1, Rails 5.2.3, PostgreSQL 12.0 で確認しました。 (Rails 6.0.0 でこの修正が入っています。)
$ rails --version Rails 6.0.2.1今回は、 name の属性を持つ User モデルを作り、インデックスを追加して確認してみます。
Rails プロジェクトを作る
Rails プロジェクトを新たに作成します。
$ rails new rails_sandbox $ cd rails_sandboxUser モデルを作る
name
属性を持つUser
モデルを作ります。$ bin/rails g model User name
インデックスを追加する
今回は2つのインデックスを作成します。
lower(name)
を指定したインデックスと、単純にname
カラムを指定したインデックスです。bin/rails g migration add_index_lower_name_to_usersdb/migrate/20200207213234_add_index_lower_name_to_users.rbclass AddIndexLowerNameToUsers < ActiveRecord::Migration[6.0] def change add_index :users, 'lower(name)', name: 'index_lower_name' add_index :users, 'name', name: 'index_name' end endマイグレーションを実行する
マイグレーションを実行します。
bin/rails db:create db:migraterails console で確認する
rails c
を実行します。$ bin/rails c Running via Spring preloader in process 344 Loading development environment (Rails 6.0.2.1)
indexes
で登録されているインデックスを調べてみましょう。
(わかりやすいように表示は折り返してます。)irb(main):001:0> User.connection.indexes(:users) => [ #<ActiveRecord::ConnectionAdapters::IndexDefinition:0x00005617bc6ef580 @table=:users, @name="index_lower_name", @unique=false, @columns="lower((name)::text)", # <= ここに注目 @lengths={}, @orders={}, @opclasses={}, @where=nil, @type=nil, @using=:btree, @comment=nil>, #<ActiveRecord::ConnectionAdapters::IndexDefinition:0x00005617bc8c3be0 @table=:users, @name="index_name", @unique=false, @columns=["name"], # <= ここに注目 @lengths={}, @orders={}, @opclasses={}, @where=nil, @type=nil, @using=:btree, @comment=nil> ]
index_lower_name
に対応する@columns
が'lower((name)::text)'
で String であるのに対して、index_name
に対応する@columns
が["name"]
とArray になっていることに注意してください。
index_exists?
を使ってindex_name
が存在することを確認します。irb(main):002:0> User.connection.index_exists?(:users, "name", name: :index_name) => true今度は、
index_lower_name
が存在することを確認します。irb(main):003:0> User.connection.index_exists?(:users, "lower((name)::text)", name: :index_lower_name) => trueRails 5 では
Rails 5.2.4.1 では、 Rails 6 と同じ動作ですが、 Rails 5.2.3 では、
index_lower_name
の存在を確認したときfalse
を返します。irb(main):001:0> User.connection.index_exists?(:users, "name", name: :index_name) => true irb(main):002:0> User.connection.index_exists?(:users, "lower((name)::text)", name: :index_lower_name) => false試したソース
https://github.com/suketa/rails_sandbox/tree/try121_index_exists_of_postgresql
参考情報
- 投稿日:2020-02-19T11:15:52+09:00
ActiveRecord::PendingMigrationErrorの解決方法
1.エラーメッセージの内容
2.エラーの原因
エラーの理由はmigrateし忘れてますよというものです。
考えられる理由は下記の3つです1.単純にrails db:createのあとrails db:migrateし忘れている
2.すでにmigrationが完了している(up)状態でマイグレーションファイルを更新しrails db:migrateしてしまった
3.マイグレーションファイルの中に入れた外部参照キーが参照するテーブルがない状態でrails db:creatしてしまった(マイグレーションファイルの記述に誤りがある)3.エラーの解決方法
1.単純にrails db:createのあとrails db:migrateし忘れている
マイグレーション実行の下記のコマンドを打ち込みましょう
$ rails db:migrate2.すでにmigrationが完了している(up)状態でマイグレーションファイルを更新しrails db:migrateしてしまった
方法1 既存のテーブルを全削除し、再度マイグレーションの状態をupにする下記のコマンドを打ち込みましょう
$ rails db:migrate:reset方法2 マイグレーションの最新のものだけ実行を取りやめて編集可能(down)にするコマンドrails db:rollbackののち、マイグレーションファイルの更新コマンドrails db:migrateを打ちましょう
$ rails db:rollback $ rails db:migrate※方法1と方法2の違いは処理速度(工程)の量の違いです。テーブルの数が多い場合は方法2としましょう。テーブルが数十程度なら方法1でも2でも大差はありません
※マイグレーションの状態を調べるコマンドは下記の通りです
$ rails db:migrate:status3.マイグレーションファイルの中に入れた外部参照キーが参照するテーブルがない状態でrails db:creatしてしまった
①-1外部参照されるマイグレーションファイルを作成し、app/modelの中のファイルにアソシエーションを追記(または修正)する
①-2マイグレーションファイルから不必要な外部参照キーを消す②上記①のどちらかの修正のあとに下記コマンドを打ち込みましょう
$ rails db:migrate:reset4.すでにデータベースにレコードを入れてしまい、それを残したまま修正する方法(補足)
新たに追加したい項目のテーブルを作成し、本来追加したかったテーブルのidを外部キーとする方法をとりましょう。
- 投稿日:2020-02-19T11:03:43+09:00
RubyのMysql2の各種情報(バージョン情報やオプション)を取得する方法
背景
Ruby on Rails 等で MySQLを利用している時、低レイヤーの根深い問題や、よくわからないエラーに遭遇したことはありますでしょうか?
RubyでMySQLを利用する際によく使われるライブラリである
Mysql2
では、C拡張を利用してlibmysqlclient
とリンクし、データベースサーバ上のmysqld
に対してアクセスしている都合上、複数の概念に対してのバージョン情報やオプションを扱う必要があります。これらのバージョン情報は、問題の原因究明に重要であるため、取得方法を知っておくことで、効率的な作業が可能になります。自分が調べる時は勿論、他の人に手伝ってもらう時にも重要なヒントになります。
TL; DR
以下のようなデータを記録しておくと、調査が捗ります。
# Railsの場合の Mysql2::Client のオブジェクトの取得方法の例 client = ActiveRecord::Base.connection.raw_connection # 自前の場合の Mysql2::Client のオブジェクトの作成方法の例 client = Mysql2::Client.new(host: 'localhost', username: 'root') puts JSON.pretty_generate( 'RUBY_DESCRIPTION' => RUBY_DESCRIPTION, # Rubyの詳細 'Mysql2::VERSION' => Mysql2::VERSION, # gemのバージョン 'Mysql2::Client#info' => client.info, # libmysqlclient のバージョン 'Mysql2::Client#server_info' => client.server_info, # 接続先の MySQL Server のバージョン ) # 接続に関するオプション (おまけ) puts JSON.pretty_generate(client.query_options.reject { |k, _v| k == :password }) # 他にも重要な情報はあるが、最低限、パスワードは取り除く出力例
{ "RUBY_DESCRIPTION": "ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]", "Mysql2::VERSION": "0.4.10", "Mysql2::Client#info": { "id": 80016, "version": "8.0.16", "header_version": "8.0.16" }, "Mysql2::Client#server_info": { "id": 50637, "version": "5.6.37" } }各種バージョン
Rubyの詳細情報
ruby -v
の詳細が記述されます。https://docs.ruby-lang.org/ja/latest/method/Object/c/RUBY_DESCRIPTION.html
gem のバージョン
gem のバージョンは、以下の Mysql2 自体のバージョン
Mysql2::VERSION
になります。https://github.com/brianmario/mysql2
コードは以下のような形になっています。
module Mysql2 VERSION = "0.5.3".freeze endlibmysqlclient のバージョン
Mysql2 は、独自に MySQLサーバ との通信プロトコルを実装しているわけではなく、 MySQLの提供している、Cで書かれたクライアントライブラリを利用してアクセスしています。
この時のライブラリのバージョンによって、微妙な挙動の差異が発生する場合があるので、この情報も重要です。Mysql2 では、以下のようにC拡張で
Mysql2::Client#info
が実装されています。version = rb_str_new2(mysql_get_client_info());これが呼び出している
mysql_get_client_info()
が何を返すかは、MySQLのドキュメントに記述されています。https://dev.mysql.com/doc/refman/5.6/en/mysql-get-client-info.html
Returns a string that represents the MySQL client library version (for example, "5.6.48").
ということで、クライアントライブラリ
libmysqlclient
のバージョンが返却されます。接続先の MySQL Server のバージョン
問題が発生した時は、クライアントバージョンは勿論ですが、相手側のサーババージョンも気になるところです。
Mysql2 では、以下のようにC拡張で
Mysql2::Client#server_info
が実装されています。server_info = rb_str_new2(mysql_get_server_info(wrapper->client));これが呼び出している
mysql_get_server_info()
が何を返すかは、MySQLのドキュメントに記述されています。https://dev.mysql.com/doc/refman/5.6/en/mysql-get-server-info.html
Returns a string that represents the MySQL server version (for example, "5.6.48").
ということで、接続先の MySQL サーバのバージョンが返却されます。
接続に関するオプション
MySQLのサーバ側で設定されているオプションも勿論大切ですが、各クライアント(コネクション)毎に維持されているオプションも、細かい挙動を知る上では重要になってきます。
Mysql2 では、以下のようにインスタンス変数へのアクセッサーが生やされて、
Mysql2::Client#query_options
があるので、それを利用する形が良いと思われます。※ このアクセッサーにはパスワードを含む情報が格納されているので、情報共有の際には、それをしっかり取り除くことを留意しておいてください。
参考
https://github.com/brianmario/mysql2
https://dev.mysql.com/doc/refman/5.6/en/c-api-server-client-versions.html
- 投稿日:2020-02-19T08:47:11+09:00
Pathname#joinは単純に複数の文字列を連結しているだけではない、という話
はじめに
Rubyの
Pathname#join
メソッドを使うと2つのパスを連結することができます。pathname = Pathname.new('/pen/pineapple') pathname.join('apple/pen').to_s #=> "/pen/pineapple/apple/pen"上の結果を見ると、
Pathname#join
は2つのパスを/
で連結してくれるメソッドのように見えます。しかし、
join
メソッドの引数が/
で始まっていると、少し直感に反した動きになります。pathname = Pathname.new('/pen/pineapple') # "/"で始まるパスを指定すると、引数そのものを表すパスが戻り値になる pathname.join('/apple/pen').to_s #=> "/apple/pen"ご覧のとおり、引数で渡した"/apple/pen"がそのまま戻り値になってしまいました。
(つまり、単純な文字列連結になっていない)
join
メソッドに複数の引数を渡したときも同様です。pathname = Pathname.new('/pen') # 複数の引数を渡す("/"で始まるパスなし) pathname.join('pineapple', 'apple', 'pen').to_s #=> "/pen/pineapple/apple/pen" # 複数の引数を渡す(2つ目の引数が"/"で始まると、その手前のパスが消える) pathname.join('pineapple', '/apple', 'pen').to_s #=> "/apple/pen"ところで、
Pathname#join
と少し似たメソッドでFile.join
があります。
File.join
はシンプルに複数のパスを連結してくれるので、人によっては「File.join
の方が自分の欲しいメソッドだ」と思うかもしれません。# 以下のメソッド呼び出しはいずれも同じ結果が返る # ("/"で始まる引数が2つ目以降に登場しても手前のパスが維持される) File.join('/pen/pineapple', 'apple/pen') #=> "/pen/pineapple/apple/pen" File.join('/pen/pineapple', '/apple/pen') #=> "/pen/pineapple/apple/pen" File.join('/pen', 'pineapple', 'apple', 'pen') #=> "/pen/pineapple/apple/pen" File.join('/pen', 'pineapple', '/apple', 'pen') #=> "/pen/pineapple/apple/pen"では、
Pathname#join
メソッドはなぜ、このように直感に反する動きをするのでしょうか?
この点が気になったので、以下のissueで僕の疑問点を質問してみました。そこで得られたzverok氏の回答が「なるほど」と思ったので、その内容を以下にまとめます。
Pathname#joinはシェルのcdコマンドのように振る舞う
zverok氏いわく、
Pathname#join
はシェルのcd
コマンドのように考えるのが良いそうです。
すなわち、pathname = Pathname.new('/pen/pineapple') pathname.join('apple/pen').to_sが意味するところは、
$ cd /pen/pineapple $ cd apple/pen $ pwd /pen/pineapple/apple/penに近い、というわけです。
(「同じ」ではなく、「近い」と書いたのは、cd
コマンドとは異なり、Pathname#join
で指定するパスはpen.jpg
のようなファイル名でも良いからです)この理屈が頭に入っていれば、
/
で始まる引数を渡したときに、手前のパスが消える理由も納得がいきます。# Pathname.new('/pen/pineapple').join('/apple/pen') のイメージ $ cd /pen/pineapple $ cd /apple/pen $ pwd /apple/pen # Pathname.new('/pen').join('pineapple', '/apple', 'pen') のイメージ $ cd /pen $ cd pineapple $ cd /apple $ cd pen $ pwd /apple/pencdコマンドのように"."や".."も渡せる
「
cd
コマンドに近い」ということがわかれば、Pathname#join
メソッドに.
や..
が渡せることを知っても驚きは小さいかもしれません。pathname = Pathname.new('/pen/pineapple') # 現在のパスのまま pathname.join('.').to_s #=> "/pen/pineapple" # 1つ上のパスへ pathname.join('..').to_s #=> "/pen" # 1つ上のパスに上がり、そこからappleというパスを指定 pathname.join('../apple').to_s #=> "/pen/apple"このように、
Pathname#join
は単純に複数のパス文字列を/
で連結するのではなく、あたかもシェル上のパスを扱うかのように振る舞います。
単純な文字列連結だと思っていると、/
で始まる引数が渡されたときの挙動が不自然に思えてしまうので注意してください。(って、かつての僕がそうだったんですが?)Pathname#joinを使うと便利なユースケース(?)
zverok氏はPathname#joinを使うと便利なユースケースについても言及してくれました。
たとえば、設定によって「特定のディレクトリ以下を相対パスで指定したい場合」と、「特定のディレクトリに関係なく、絶対パスで指定したい場合」の2パターンがあるときに、Pathname#join
を使えば条件分岐なしでこの挙動を実現できます。zverok氏が示してくれたコード例とは異なりますが、説明用にサンプルコードを書くとしたらこんな感じです。
(注:これはあくまで説明用のサンプルコードです。実際にこんなメソッドがあるとかなり危険です?)require 'pathname' require 'fileutils' # 指定されたパスでlsコマンドの実行結果を表示するメソッド def ls(path) # 基準となるパスを/varとする base_path = Pathname.new('/var') # 引数pathの値に応じて、/var内、/var外のどちらのディレクトリも指定可能 target_path = base_path.join(path) # 対象となるパスでlsコマンドを実行 puts system("ls #{target_path}" end # /var/logが対象になる(相対パス指定) ls('log') # /binが対象になる(絶対パス指定) ls('/bin')実際にこういうコードが必要になる機会がどれくらい頻繁にあるのかはわかりませんが、たしかに
Pathname#join
ならではの使い方かもしれません。まとめ
というわけで、この記事では
Pathname#join
メソッドの挙動を正しく理解するための考え方を説明してみました。ちょっと余談になりますが、
Pathname#join
がややこしいのは、File#join
と同じメソッド名で、なおかつPathname
とFile
という、概念的にもよく似たクラスなのに挙動が異なる点かもしれません。
使い方によっては両者はほぼ同じようにパスを連結するので、そうした点がPathname#join
の仕様を勘違いしてしまう原因になっている気がします。
もしかすると、join
以外の名前(たとえばPathname#merge
とか)を与えていたら、「単純に連結するだけじゃないのかも?」と予想しやすかったかもしれません。いずれにせよ、「
Pathname#join
は単純に文字列を連結するだけではない」ということをこの記事を読んで理解してもらえれば幸いです。あわせて読みたい
Rails.root
メソッドが返すのもPathnameオブジェクトです。
RailsプログラマがPathnameオブジェクトを一番よく使うのはこのユースケースかもしれません。
- 投稿日:2020-02-19T01:40:52+09:00
画像アップロード機能実装について
目的
作成したアプリケーションに画像アップロード機能を実装するための手順や
考え方などを備忘録的に残していきます。画像アップロードとは
アップロードとはファイルやデータをPCやスマホからインターネット上のサーバーへ転送することを指します。
画像アップロードの仕組み
- ファイル選択ボタンを押して、画像を選択
- 送信ボタンをおす
- 画像ファイルを画像パスがリクエストして送信される
- 画像ファイルはアクセス先コンピューター内に、画像のパスはDBないにあるテーブルに保存される (画像の保存先には外部のストレージを使うことが多い。AWSサービスのS3など..)
実装の流れ
ライブラリのインストール
Gemfilegem 'carrierwave' gem 'mini_magick'bundle installアプリの雛形を作成
scaffoldで必要なモデルやコントローラーなど一式を作成します。
モデル名はFeedとし、画像を保存するためにimageカラムを作成します。$ rails g scaffold feed image:text $ rails db:migrateアップローダファイルを作成
アップローダファイルとは アップロードに関する設定 をするためのファイルです。
アップロードに関する設定とは以下が挙げられます。
- 外部ストレージを連携するかどうか
- 保存形式
- 画像ファイルの保存先の設定
- 画像サイズの調整
$ rails g uploader Imageapp/uploader/image_uploader.rbclass ImageUploader < CarrierWave::Uploader::Base include CarrierWave::MiniMagick # MiniMagickをincludeすると画像サイズ調整ができるようになる storage :file # 保存形式の設定。他に使うものとしてはfog形式などがある。 process :resize_to_limit => [50, 50] # 画像サイズの調整 # 画像ファイルの保存先の設定 # 保存先を指定するには `store_dir` というメソッドに定義します。 def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end end保存形式を指定しているのが storage :file という記述です。
file以外の保存形式として クラウドのストレージ が挙げられます。
クラウドのストレージとして、Amazonが提供している S3 というサービスが有名です。file形式は画像が実体として存在しますが、クラウドのストレージにおける画像は実体が存在しません。この場合は専用のgemを利用して storage :fog という記述をします。最後に該当のモデルにカラムとアップローダを指定します。
Feedモデル内に以下を記述app/model/feed.rb~省略~ #アップローダ定義 mount_uploader :image, ImageUploader ~省略~上記の記述をすることで、feedsテーブルの中にあるimageカラムにimageUploaderという名前のアップローダ機能を追加することができます。
まとめ
アップローダ定義の意味を総括すると以下のようになります。
- feedモデルが関係しているのでfeedsテーブルが関係している
- mount_uploaderは、画像アップロードの宣言をしている
- :imageはfeedsテーブル内の画像パスが入っているカラム名を指している
- ImageUploaderは、ImageUploaderファイル内の設定を元にアップロードすることを意味している
- 投稿日:2020-02-19T00:02:19+09:00
Rubyの配列で使えるメソッド
Railsアプリで配列データを分けて出力しようとした時に色々試したので記録しとこうと思います。
students = ["Tom","Mike"],["Sam","Eric"],["Billy"],["Jorge","Nancy","Rick"]適当に配列を用意しました。
flatten
入力p students.flatten出力["Tom", "Mike", "Sam", "Eric", "Billy", "Jorge", "Nancy", "Rick"]配列がくっつきます。
join
入力p students.join出力"TomMikeSamEricBillyJorgeNancyRick"
これもくっつきます。
flattenのクラスはArray、joinの場合はstringです。
ただ作ってたアプリでは複数の配列からランダムに一つずつ出力したかったため、これらでは上手くいかなくて(なぜかflattenでくっつかなかった)
sample
入力p students.sample(1)出力[["Jorge", "Nancy", "Rick"]]sampleは配列の要素を1個(引数を指定した場合は自身の要素数を越えない範囲で n 個) ランダムに選んで返します。
この場合はランダムに配列を1つ出力しています。入力students.each do |student| p student.sample(1) end出力["Mike"] ["Sam"] ["Billy"] ["Jorge"]この場合は一度ループ処理をしているので各配列から一つずつ要素が出力されています。
これを応用してアプリの機能が実装できました。配列面白いですよね。
参考