- 投稿日:2021-01-18T23:36:01+09:00
Rubyでハッシュをうまく使って注文の合計金額を計算する
この記事ではmacOSにインストールしたRuby2.6.3を使っています。
ゼロからわかるRuby超入門の練習問題(p176)を自分なりの解き方をしたのでメモします。
キーはシンボルで書くものだという思い込みがあったので、最初この方法での解き方ができませんでした。ハッシュの使い所を見極める力が必要だと感じました。問:メニューは、コーヒー300円とカフェラテ400円の2つがあり、サイズはショートだと+0円、トールだと+50円、ベンティだと+100円になる。メソッドを使って注文をとってください。
ターミナルから注文がとれるバージョンdef price puts "ご注文は何にしますか?" item = gets.chomp puts "サイズは何にしますか?" size = gets.chomp items = {"コーヒー" => 300, "カフェラテ" => 400} sizes = {"ショート" => 0, "トール" => 50, "ベンティ" => 100} total = items[item] + sizes[size] puts "#{total}円になります" end priceキーワード引数とデフォルト値を使う方法(本の答えとほぼ同じ)def price(item: "コーヒー", size: "トール") items = {"コーヒー" => 300, "カフェラテ" => 400} sizes = {"ショート" => 0, "トール" => 50, "ベンティ" => 100} items[item] + sizes[size] end puts price(item: "カフェラテ", size: "ベンティ")
- 投稿日:2021-01-18T23:13:50+09:00
Railsで個人開発してたら、Railsがめっちゃ叩かれて辛みだった話
こんにちは、だむはです。
去年の12月に個人開発している「sister」というサービスをリリースしたのですが、開発中にRails叩きがおこり、辛みだったので、その時のことをかこうと思います。ちなみに、サービスはまだベータ版です。
「sister」ってどんなサービス?
「sister」は一言で言うと、IT業界に特化した女性向けのキャリア/スキルシェアサービスです。MENTAから多大なる影響を受けています。MENTAは「師弟関係」、sisterは上下関係をもたない「姉妹関係」と言う部分でシスターフット大切にしていこう!って感じです。
興味があったらみてってください。
あなたのsisterを探そう環境
sisterの開発はこんな感じです。
・Ruby
・Rails
・Stripe
・AWS S3
・Heroku
ちなみにローカルはDockerです。はい、フロントエンドにモダンな技術も使ってないし、インフラもAWS使ってないです
なぜこの環境なの?
それは、純粋に私一番使える言語がRailsだったからです!
Herokuはデプロイ楽だし、お金の管理がしやすいからです!
理由はこれだけ!開発期間
2020年9月〜12月の約4ヶ月くらいです。
実はもともとポートフォリオとして、少しだけ作って放置していたものをベースに作りました。作業量としてはこんな感じです。
・仕事終わりに3時間くらい週3
・土日4−5時間(やったりやらなかったり)だらだらやっても終わらんと思って、12月に入り、今年中にリリースすると決めてからは、ほぼ毎日夜中まで実装してました。
事件は突然起きる、、、
12月の中旬くらいに突如、Twitterのトレンドに「Rails」があがったのです。
Twitterで定期的に起こる、Railsフルボッコ現象でした。。。不安になる私
実はsister開発中も何回か「Railsはオワコン」とかの記事を見ていたので、私も、フロントエンドにReactで、Firebaseを導入しようとしてみたり、AWSに環境構築しなおそうかなとか考えてみたり、いろいろとTryしてみようとしたのですが、全て諦めました。
諦めた理由は、時間をかければ取り入れられますが、私が技術力不足で新しいことを取り入れようとすると、インプットにかなり時間をとられるので、サービスリリースを最優先しようと判断したからです。
しかし、、、
12月中旬、、、
Twitterのトレンドに「Rails」があがるほどのフルボッコ、、、さすがに、うわーやばいのかなー、、Railsで開発するのやばいのかなーーと不安が再熱してしまいました。
私はどっちかというと、技術にめっちゃ興味がある方ではないのですが、やっぱりエンジニアとして、モダンな技術は使いたいし、sisterをリリースしたときに「どんな技術使ってるんですか?」と聞かれたら、モダンな技術つかってますって言ってみたい。
私は立ち止まって考えた
私は立ち止まって考えてみました。
自分は何がしたいんだ?目的はなんだ?
個人開発は技術力向上が目的なのか?
新しい技術を使うために始めたのか?違うだろ!!
自分が作りたいサービスがあって、それを世にリリースしたいからだろ!!と、本来の目的を思い出しました。
ちゃんと説明すると、
「モダンな技術を身につけることではなく、サービスをリリースすることがいちばんの目的」
ということです。そして私はリリースまで駆け抜けた
12月中旬の出来事だったので、ここで新しい技術を導入することはリリースを遅らせることにつながるのもありましたし、サービスをまずリリースすることが目的だったので、雑念を消して、寄り道せず、リリースまで駆け抜けることができました。
こうして、「sister ベータ版」は無事、12月中にリリースされたのでした。
エンジニアと個人開発
「sister」開発中は、エンジニアとしての自分と個人開発者としての自分が葛藤していました。
もちろん、個人開発者はエンジニアなんですけど、違う部分としては自分が開発しているサービスをどうしたいのかが最優先なのかなって、個人開発しながら思いました。エンジニアとしては、モダンな技術使いたい!流行ってる技術使いたい!って気持ちがあるけど、実際ユーザーが使う時って、裏で何使ってるか全く気にしてないよね?って部分です。
最後に
現在、「sister ベータ版」はリリースして3週間くらいたっていて、80人以上の方にご登録いただいています。
IT業界に特化した女性向けのサービスと言うこともあり、ターゲット層の母数自体が少ないため、最初は20ー30人くらいかな〜と予想していたのですが、予想より反響があったので、嬉しいです。改修がんばろう!ってなります。
ベータ版としてミニマムで早くリリースすることで、ユーザーの反応を早めにみれたのが、本当良かったかなと思ってます。
結論、どんなときも、何を目的をするかがいちばん大事。
そして、個人開発で学べることは多い!最後まで、読んでいただきありがとうございました!
おまけ
「sister ベータ版」もよろしくお願いします!
あなたのsisterを探そう
- 投稿日:2021-01-18T22:56:37+09:00
Rubyで文字列の出現回数を数える
この記事ではmacOSにインストールしたRuby2.6.3を使っています。
ゼロからわかるRuby超入門の練習問題(p154)を自分なりの解き方をしたのでメモします。
1対1対応しているものについてはハッシュをうまく使う必要があると気づいた。問:文字列"caffelatte"の中で使われているアルファベットとその回数を数えてください。
hash = {} hash.default = 0 #文字1つ1つを配列にしてアルファベット順に並び替える array = "caffelatte".chars.sort #文字をキー,回数を値としたhashをつくる array.each { |x| hash[x] += 1} #最後に文字が何回使われていたかを表示する hash.each { |letter, count| puts "#{letter}は#{count}回使われている"}
- 投稿日:2021-01-18T22:28:34+09:00
RubyでJSの即時関数みたいなことをやる
ちょっと例が悪いですが、自分用メモ。
↓こんなのがRubyで書きたかった。
const filepath = (() => { return "hoge/moge/huga"; })();別に普通に定義しても良いですが、処理部がごちゃごちゃしてくる場合は、変数の定義も処理部と切り離したいと思い、考えてみました。
class Hoge def initialize @root = root end def filepath(path) File.join(@root.call, path) end private def root # pathへの代入は初期化の@rootへ代入された時のみ path = "hoge/moge/huga" -> { path } end end hoge = Hoge.new p hoge.filepath("a") # => "hoge/moge/huga/a" p hoge.filepath("i") # => "hoge/moge/huga/i" p hoge.filepath("u") # => "hoge/moge/huga/u" p hoge.filepath("e") # => "hoge/moge/huga/e" p hoge.filepath("o") # => "hoge/moge/huga/o"この例の
root
メソッドで定義しているpath
は直書きですが、なにかの処理結果をpath
に格納して使いまわしたい場合は、何度も処理を走らせる必要がないので、いい感じ。
- 投稿日:2021-01-18T21:37:01+09:00
【Rails】raty.jsを使った評価機能を作る
raty.jsを使って、railsアプリに星画像付きの評価機能を実装する方法です。jqueryが必要です。
環境
$ rails -v Rails 5.2.4.4jqueryを導入
Gemfilegem 'jquery-rails'$ bundle installapp/assets/javascripts/application.js//= require jquery
jquery.raty.jsを作成
$ touch app/assets/javascripts/jquery.raty.js作成したファイルに、https://github.com/wbotelhos/raty/blob/master/lib/jquery.raty.js 内の記述をコピペ。その後
application.js
でrequire(jqueryより後に書く)app/assets/javascripts/application.js//= require jquery.raty.js
星の画像を配置
https://github.com/wbotelhos/raty/tree/master/lib/images から
star-on.png
、star-off.png
、star-half.png
をダウンロードして、app/assets/images
以下に配置。評価を保存するカラムを持つデータを作成
$ rails g migration addEvaluationToMovie evaluation:float
db/migrate/202101011111.rbclass AddEvaluationToMovie < ActiveRecord::Migration[5.2] def change add_column :movies, :evaluation, :float end end$ rails db:migrate
評価投稿画面を作成
今回は例として、お気に入りの映画のタイトルを投稿する画面で、評価も一緒に投稿できるようにしてみます。
app/views/movies/new.html.erb<h2>お気に入りの映画を投稿</h2> <%= form_with(model: movie, local: true) do |form| %> <div> <%= form.label :title %> <%= form.text_field :title %> </div> <div id="evaluate_stars"> <label>評価</label> </div> <div class="actions"> <%= form.submit %> </div> <% end %> <script> $('#evaluate_stars').raty({ starOn: "<%= asset_path('star-on.png') %>", starOff: "<%= asset_path('star-off.png') %>", starHalf: "<%= asset_path('star-half.png') %>", scoreName: 'movie[evaluation]' //登録するモデル名とカラム名を記述 }); </script>app/controllers/movies_controller.rbdef movie_params params.require(:movie).permit(:title, :evaluation) # evaluationを追加 end投稿した評価を表示
ここでは例としてindexページに評価を表示します。
app/views/movies/index.html.erb<h1>映画一覧</h1> <table> <thead> <tr> <th>タイトル</th> <th>評価</th> </tr> </thead> <tbody> <% @movies.each do |movie| %> <tr> <td><%= movie.title %></td> <td class="movie-evaluation" data-score="<%= movie.evaluation %>"></td> </tr> <% end %> </tbody> </table> <script> $('.movie-evaluation').raty({ readOnly: true, score: function() { return $(this).attr('data-score'); }, path: '/assets/' }); </script>参考
- 投稿日:2021-01-18T21:09:26+09:00
エラー解決を通してdeviseのメソッドuser_signed_in?とcurrent_userの理解が深まった話
開発環境
Mac OS Catalina 10.15.7
ruby 2.6系
rails 6.0系事の発端
転職用のポートフォリオとしてオリジナルアプリを作っていたある日のことでした。
未ログインユーザーが詳細ページに遷移するリクエストをローカルで行ったところ、以下のエラーが発生解決法
結論から言うと user_signed_in? && の条件を追加することで解決しました。
エラー時のコード
show.html.erb<% if current_user.id == @post.user.id %> <div class = "btn-contents"> <%= link_to '編集する', edit_post_path(@post.id), class: "edit-btn"%> <%= link_to '削除する', "#", class: "delete-btn"%> </div> <% end %>
エラー解決後のコード
show.html.erb<% if user_signed_in? && current_user.id == @post.user.id %> <div class = "btn-contents"> <%= link_to '編集する', edit_post_path(@post.id), class: "edit-btn"%> <%= link_to '削除する', "#", class: "delete-btn"%> </div> <% end %>なんとも呆気ない話でしたね。
原因
未ログインユーザーに対する条件を忘れたことで、current_userメソッドが未ログインユーザーのリクエストの際でも使われてしまったことが原因でした。
current_userは、現在ログインしているユーザーの情報を取得できるメソッドですが、今回の場合、ログインしていないのに、ログイン情報を取得しようとしました。
そのため、「current_user??? そもそもログインしてないんでそんな情報ないんですけど???」みたいな感じでrails先生に怒られてしまったと言うわけですね。
なので、まずは手前の条件で user_signed_in?メソッドを使うことで、ログインしていなければfalseが返ってくるようにします。
そうすれば、未ログイン時には後ろの条件に遷移しなくなるのでエラーが解決するわけでした。(A && B と言う記述は、Aの条件がfalseだった場合、Bの条件は参照されません)対策
対策としては「メソッドに対するなんとなくの理解をやめること」だと思っています。
エラーの原因としては上記の通りですけど、根本的な原因は「メソッドをなんとなく覚えて理解したつもりで使っていたこと」だと思っています。
気をつけます、、、
- 投稿日:2021-01-18T21:02:44+09:00
[ruby]演算子について
はじめに
こんばんは
私は某スクールに通っているものです
先月卒業したばかりなのですが学んだ事をアウトプットしております
前回、[Ruby]文字列で使えるメソッドを作ったので今回は演算子について触れたいと思います!演算子とは?
演算子とはプログラミングで計算したり、比較したりする時に使う記号です
これはめちゃくちゃ使うから覚えてくださいねと言ったけどそのほとんどは小学校でやった+とかーとかばかりだから身構えなくていいかも
+ 足し算 - 引き算 * 掛け算 / 割り算 % 剰余 ** べき乗 Math::PI 円周率(π)を合わせる定数 Math.sqrt 平方根を求める ぶちゃけ、べき乗以下は半年プログラミング学習してたけど使わんかったから頭の隅に置いておいてください
=> nil irb(main):115:0> 1 + 1 => 2 irb(main):116:0> 1 - 1 => 0 irb(main):117:0> 1 * 2 => 2 irb(main):118:0> 4 / 2 => 2言うまでもないけど四則演算の働き
irb(main):119:0> 3 / 4 => 0ちなみに小数点は表示されない
なぜならこの数字はIntegerクラスであって、Integerは数字を整数で表しているからねirb(main):122:0> n = 3 / 4 => 0 irb(main):123:0> n.class => Integer代入されたnをclassで確認したら”Integer”と確認できました
%(剰余)
上の図で剰余と記述されていましたが何って話ですよね?
剰余と言うのは割ったあまりを指しますirb(main):128:0> 9 % 7 => 29 / 7 = 1 ・・・ 余り2だから、2が表示されたわけですね
べき乗
べき乗と言うのは簡単に言うと累乗だね
irb(main):131:0> 4 ** 2 => 16これを見ていただければわかりますが
4 * 4 = 16だねMath::PI
Math::PIは円周率
でも3.14よりより細かい計算ができるよirb(main):138:0> 3 * 3 *3.14 => 28.26 irb(main):139:0> 3 * 3 * Math::PI => 28.274333882308138見てわかるかもだけど小数点第二位が違う
Math.sqrt
これは平方根を求めるメソッドだね
Math.sqrt(数字)で使えます
irb(main):140:0> Math.sqrt(4) => 2.04の場合を求めたけど√4の場合は2だね
irb(main):143:0> Math.sqrt(2) => 1.4142135623730951ひとよひとよにひとみごろ、懐かしい
最後に
演算子はこの先かなり使うから覚えよう
でもべき乗以下は頭に入れる程度でいいよ
- 投稿日:2021-01-18T18:59:16+09:00
テーブルにカラムを追加する方法
今回はmigrateファイルの記述をして追加する方法を実行します。
migrateファイルを編集
まず、README.mdを参考にmigrateファイルを編集します。
README.md## items テーブル | Column | Type | Options | | ----------------- | ---------- | ----------------- | | name | string | null: false | | explanation | text | null: false | | category_id | integer | null: false | | state_id | integer | null: false | | delivery_price_id | integer | null: false | | prefectures_id | integer | null: false | | delivery_time_id | integer | null: false | | price | integer | null: false | | user | references | foreign_key: true | ### Association - belongs_to :user - has_one :purchase20210118070956_create_items.rbclass CreateItems < ActiveRecord::Migration[6.0] def change create_table :items do |t| t.string :name, null: false t.text :explanation, null: false t.integer :category_id, null: false t.integer :state_id, null: false t.integer :delivery_price_id, null: false t.integer :prefectures_id, null: false t.integer :delivery_time_id, null: false t.integer :price, null: false t.references :user, foreign_key: true t.timestamps null: false end end endrails db:rollback rails db:migrateの実行
記述が終わったら以下2点の実行
Terminal
% rails db:rollback
% rails db:migrate
最後に
テーブルの確認
これでテーブルの追加が完了です!
- 投稿日:2021-01-18T18:21:40+09:00
ActiveRecord_RelationとCouldn't find Item without an IDのエラーについて
まず、ActiveRecord_Relation:とは
例えばコントローラーで
def index User.all endと定義した場合
Use.allで持ってきたもののことを言います。
この時、allはレコードとカラムの情報のみを持ってきます。Couldn't find Item without an IDとは
要約するとIDが取得できませんよ。という意味
例えばコントローラーで
def index User.find(params[:id]) endと定義したとします。
この時paramsの[:id]の部分が取得できません。ということになります。
状況
- このようなエラーが出る場合はどんな時か?
それはルーティングにネストをしていて、親になっているものから情報を取得しようとした場合である。
- なぜか?
シークエルプロなどでテーブルを確認すれば一目瞭然。
子要素に親要素の情報は入っていないから。この時、allはレコードとカラムからとってくるという記述の意味がわかります。
解決策
- paramsの後に親要素となるidの指定をしよう
親.find(params(:親_id)具体的には
親にusers,子にcommentsなんていうネストをしていた場合
commentsコントローラーに
@user = User.find(params(:user_id)終わりに
このエラーの解決に結構時間がかかりましたが、解決してみれば単純なことでした。
まだまだ理解していないことが多いことがよくわかりました。
未熟故に間違ったことを記述している可能性や抜けていること、誤字脱字などがあるかもしれません。
初めての投稿ということで読みにくいが、それも少しずつ改善していければと思いつつ終了とさせていただきたいです。
- 投稿日:2021-01-18T17:16:27+09:00
RSpec~Taskモデルのバリデーションテスト作成まで
RSpecとは
Rubyにおけるテスティイングフレームワークのことです。
Rubyでは標準で
Minitest
という別のフレームワークがあります。Ruby on RailsチュートリアルでもMinitest
でテスト方法を教わります。
ですが、実際に現場で使われているテスティイングフレームワークはRSpec
が圧倒的に多いようです。本記事では
RSpec
をインストールし、既に定義されているモデルに対してバリデーションテストするまでを記載します。使用技術のバージョン
・Ruby: 2.6.4
・Ruby on Rails: 5.2.3モデルの概要
今回取り扱うモデルは
user
モデルとtask
モデルです。[userモデル]
app/models/user.rbclass User < ApplicationRecord has_many :tasks, dependent: :destroy validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] } validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] } validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] } validates :email, uniqueness: true, presence: true end[taskモデル]
app/models/task.rbclass Task < ApplicationRecord belongs_to :user validates :title, presence: true, uniqueness: true validates :status, presence: true enum status: { todo: 0, doing: 1, done: 2 } end今回は
task
モデルのバリデーションをテストするため、user
モデルのバリデーションの説明は割愛します。
task
モデルから分かる通り、title
カラムとstatus
カラムにバリデーション(制限)がかけられています。
title
カラム
presence: true
=> 値が必ず格納されていなければならない
uniqueness: true
=> 格納されている値がユニークな存在でなければならない
status
カラム
presence: true
=> 値が必ず格納されていなければならない※
enum
についてですが、列挙型と呼ばれているもので、数値のカラムに対してプログラム上で別名を与えることができます。
status
はinteger
型が格納されていますが、「0がtodoのステータスで、1がdoingのステータスで、、、、」というように、人間にはstatus
の数値が何を表すのか理解しづらいです。そこでDB上では数値で扱うが、プログラマから見たら文字列で扱うというように設定できるenum
を使用するのです。詳しくはこちらを参照してください。導入手順
ここから少し長いですが、1つずつ段階ごとに手順を紹介します。
RSpecとFactoryBotをインストール
RSpec
は上述した通りテスティイングフレームワークです。
FactoryBot
とはRSpec
でテストする時にモデルのレコードを作成しテストで使用することができます。インストール手順は公式のREADMEの手順を参照しています。
RSpecの公式README
FactoryBotの公式README
Gemfile
の:development
グループと:test
グループの両方にrspec-rails
とfactory_bot_rails
を追加します。Gemfilegroup :development, :test do gem 'factory_bot_rails' gem 'rspec-rails', '~> 4.0.2' gem 'byebug', platforms: %i[mri mingw x64_mingw] endいつも通り
bundle install
$ bundle installそれに加えて、
$ rails generate rspec:install
を実行`rails generate rspec:install` Running via Spring preloader in process 31987 create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rbこれでインストールは完了です。
Task model specのファイルを作成
$ rails generate rspec:model task Running via Spring preloader in process 32051 create spec/models/task_spec.rb invoke factory_bot create spec/factories/tasks.rb念のため
task_spec.rb
のテストが実行可能かを確認しましょう。$ bundle exec rspec spec/models/task_spec.rb * Pending: (Failures listed here are expected and do not affect your suite's status) 1) Task add some examples to (or delete) /Users/sakidendaiki/Downloads/RUNTEQ/tasks/option_tasks/sample_app_for_rspec/spec/models/task_spec.rb # Not yet implemented # ./spec/models/task_spec.rb:4 Finished in 0.0053 seconds (files took 1.6 seconds to load) 1 example, 0 failures, 1 pendingtaskモデルとuserモデルのFactoryBotのテストデータを作成する
task
モデルのFactoryBot
をファイルを作成します。
モデルに対してFactoryBot
のファイルを作成する場合、$ rails g factory_bot:model モデル名と記述します。今回はタスクモデルのテストですので、
$ rails g factory_bot:model task
のコマンドを実行してみましょう。$ bin/rails g factory_bot:model task Running via Spring preloader in process 28632 identical spec/factories/tasks.rb次に
task
のFactoryBot
を以下のように記述します。spec/factories/tasks.rbFactoryBot.define do factory :task do sequence(:title, "title_1") content { "content" } status { :todo } deadline { 1.week.from_now } association :user end end上記を定義することで、
FactoryBot.create(:task)
で上で定義されたtask
モデルを呼び出すことができます。
コンソールで試してみましょう。$ FactoryBot.create(:task) (0.1ms) SAVEPOINT active_record_1 User Exists (0.7ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "user_1@example.com"], ["LIMIT", 1]] 略 => #<Task:0x00007f92a4342b08 id: 1, title: "title_1", content: "content", status: "todo", deadline: Mon, 25 Jan 2021 07:24:22 UTC +00:00, created_at: Mon, 18 Jan 2021 07:24:22 UTC +00:00, updated_at: Mon, 18 Jan 2021 07:24:22 UTC +00:00, user_id: 1>
sequence(:title, "title_1")
は一度FactoryBotを呼ばれると、title
カラムに対し"title_1"という文字列を格納します。
二度目に呼ばれる時は文字列は"title_2"となります。つまり、呼び出されるたびに末尾の数字に1を加えます。何故このようなことをするかというと、
title
カラムにはuniqueness: true
のバリデーションがあるためです。
title
カラムに値がかぶるのを防ぐために、呼び出されるたびにtitle
カラムの値を変えています。
sequence
メソッドはこのようにuniqueness: true
がある時に使用されます。
deadline
カラムで定義されている1.week.from_now
は文字通りの意味です。
一週間後の同時刻の時間を出力します。
本当にRailsって理解しやすいですよね。
association :user
は外部キー参照と非常に似ています。
task
モデルはuser
モデルと1:多の関係にあるので、user_id
カラムがあります。
association :user
とすることで、FactoryBot
のファイルで定義されているfactory :user
のテストデータを参照します。
Task
にはuser_id
も必要となることを踏まえてuser
のFactoryBot
も作成する必要があります。bin/rails g factory_bot:model user Running via Spring preloader in process 29285 create spec/factories/users.rb
user
モデルのFactoryBot
を記述spec/factories/users.rbFactoryBot.define do factory :user do sequence(:email) { |n| "user_#{n}@example.com" } password { "password" } password_confirmation { "password" } end end
sequence
メソッドの記載方法がtask
モデルで記載した時と違いますが、内容は同じです。
つまり、FactoryBot
のファクトリである:user
が呼び出されるたびに、n
の値がどんどん増えていくことで、これでテストデータの準備は完了です!
--format documentation を追記(テスト記述前その1)
RSpec
のテストを記述する前に、.rspec
ファイルに--format documentation
を追記しましょう。.rspec--require spec_helper --format documentationこれにより、テストの実行結果をドキュメント形式に変更します。
簡単にいうと、テスト結果が人間に読みやすくなります。config.include FactoryBot::Syntax::Methodsを追記(テスト記述前その2)
テストを記載する際、テストデータの呼び出し方は
FactoryBot.create(:task)
や、FactoryBot.build(:task)
といったように、必ずFactoryBot
と記載しなければいけません。しかし、テストデータを呼び出す際に毎回書くのは冗長的です。
そこでconfig.include FactoryBot::Syntax::Methods
をruby:spec/rails_helper.rb
に記載します。spec/rails_helper.rbconfig.include FactoryBot::Syntax::Methodsこうすることで、テストデータを呼び出す時、
create(:task)
というように、FactoryBot
の記述を省略できます。task_spec.rbにモデルバリデーションに関するテストを作成
いよいよテストを記述します。
spec/models/task_spec.rbrequire 'rails_helper' RSpec.describe Task, type: :model do describe 'validation' do it '全ての属性が適切に格納されていれば有効' do task = build(:task) expect(task).to be_valid expect(task.errors).to be_empty end it 'タイトルがなければ無効' do task_without_title = build(:task, title: "") expect(task_without_title).to be_invalid expect(task_without_title.errors[:title]).to eq ["can't be blank"] end it 'ステータスがなければ無効' do task_without_status = build(:task, status: nil) expect(task_without_status).to be_invalid expect(task_without_status.errors[:status]).to eq ["can't be blank"] end it '同じタイトルが重複していれば無効' do task = create(:task) task_with_duplicated_title = build(:task, title: task.title) expect(task_with_duplicated_title).to be_invalid expect(task_with_duplicated_title.errors[:title]).to eq ["has already been taken"] end it 'タイトルが別名であれば有効' do task = create(:task) task_with_another_title = build(:task, title: 'another_title') expect(task_with_another_title).to be_valid expect(task_with_another_title.errors).to be_empty end end endit '〜〜〜〜〜〜' doの箇所にテストの内容が記載しています。
1つ目のテスト'全ての値が適切に格納されていれば有効'は、テストデータをそのまま呼び出しテストしています。
テストデータはバリデーションを考慮して値が定義されているので有効であるはずです。
expect(task).to be_valid
とexpect(task.errors).to be_empty
は直感的に理解できるのではないでしょうか。
変数task
はbe_valid
(有効)であり、task.errors
はbe_empty
(空値)であることをテストしています。
もしバリデーションに引っかかっていたらtask.errors
には何かしらのエラーメッセージが格納されているはずです。
expect(task_without_title).to be_invalid
とexpect(task_without_title.errors[:title]).to eq ["can't be blank"]
はどうでしょうか。簡単に推測できると思います。テスト結果を確認
RSpec
でテストします。
$ bundle exec rspec
をターミナルに実行するだけです。$ bundle exec rspec Task validation is valid with all attributes is invalid without title is invalid without status is invalid with a duplicate title is valid with another title Finished in 0.14338 seconds (files took 1.39 seconds to load) 5 examples, 0 failuresバリデーションを削除してテストが失敗するか確認
本当にテストが正しいのかを確認するためにあえてテストを失敗させる方法があります。
例えば、2つ目のテスト'タイトルがなければ無効'が成功するのは、task
モデルのtitle
カラムに対してpresence: true
のバリデーションがあるためです。では、presence: true
を削除してみましょう。validates :title, uniqueness: true【テストの実行結果】
Failures: 1) Task validation is invalid without title Failure/Error: expect(task.errors[:title]).to include("can't be blank") expected [] to include "can't be blank" # ./spec/models/task_spec.rb:12:in `block (3 levels) in <top (required)>' Finished in 0.10893 seconds (files took 0.7813 seconds to load) 5 examples, 1 failure Failed examples: rspec ./spec/models/task_spec.rb:9 # Task validation is invalid without title成功していたテストが失敗したことで、自分の書いたテストが正しいテストであったことを証明できるのです。
- 投稿日:2021-01-18T17:12:11+09:00
Chefの概要を分かりやすく書いてみました
最近の現場で初めて、chefを使用しました。
あまり情報が少ないため、概要を書いてみました。
Chefとはいったい??
ウィキペディアにはこう書かれていました。
RubyとErlangで記述された構成管理ツールである
ん??どういうこと??
すごく簡単に言えば、
chefはインフラ構築を自動化できるプラットフォームなのです。
Chefを使用するメリット
Chefでインフラの構成を自動化すれば、環境構築時にすべて自分で用意する必要はありません。
繰り返し作業を集約でき、無駄な時間を減らせます。
大規模のプロジェクトによく使われるようです。ChefはRubyで書かれる
ChefはRubyで書けるため、Rubyの基礎知識が必要となります。
Chefの主な構成要素
Recipe
実行する内容の定義を書く場所
Attribute
変数などをまとめている場所
Template
設定ファイルを作るためのテンプレート書く場所
File
バイナリファイルなどを置く場所
Cookbook
上記などをまとめたディレクトリ
Knife
コマンドラインツールで、Cookbook や Recipe を操作します
Chefの名前について
シェフと聞いて、
まず思い浮かぶのは
料理人ですよね!?ここのchefはそのイメージで名前がつけられた
ようです。だから
レシピとかクックブックとかの名前になっているんですね
なんかおしゃれーですねまとめ
chefはインフラ構築を自動化できるのもので
Rubyで書けるんですねーーー!!!実際の書き方はまた別でまとめます。ありがとうございました。
- 投稿日:2021-01-18T17:03:01+09:00
railsでほぼ静的なページの作り方
引用先
Railsチュートリアル※ rails new appをしている状況で新しくページを追加するところからです
ターミナルでコマンド実行
まずは作業ディレクトリへ移動
cd ~移動後コマンド実行
$ rails generate controller <コントローラ名> <アクション名> <アクション名> ・・・ # 例 $ rails generate controller StaticPages home help上記コマンドを実行すれば
・コントローラの作成
・ルーティングの設定
・ビューファイルの作成
を自動で作成してくれるので非常に便利かつ簡単ですね!実際これだけでページは作れちゃいます。
新たにページを追加したいとなった時
例えば、新たにaboutページを作成したいとなった時に、手動でファイルを作成したりコードを書けば追加もできます。
手順
①ルーティングの設定
②コントローラにアクション追加
③ビューファイルの作成①ルーティングの設定
config/routes.rbRails.application.routes.draw do get 'static_pages/home' get 'static_pages/help' get 'static_pages/about' ←追加 end②コントローラにアクション追加
app/controllers/static_pages_controller.rbclass StaticPagesController < ApplicationController def home end def help end def about ←追加 end ←追加 end③ビューファイルの作成
app/views/static_pagesにabout.html.erbを右クリックで新規作成app/views/static_pages/about.html.erb<h1>About作成</h1> ・・・ ・・・ ・・・これだけでページの追加は完了です。
URLは末にstatic_pages/aboutをつければwebで開きます。
(https://~~~/static_pages/about)
- 投稿日:2021-01-18T13:08:34+09:00
必ず役に立つ!Ruby on Railsでの開発で使えるデバッグコード
こ~れなんなん、これなんなん
こんにちは。
開発で最も必要とされる技術ってデバッグ力だと思っています。特にバグを大量生産することが得意な僕にとって、デバッグ力はとても大事。
そこで、普段開発で使ってるデバッグ用のコードをまとめておこうかと思います。
デバッグコード
Ruby on Railsで使える、デバッグで役に立ちそうなコードの一覧を書いていきます。なお、Rubyで使えるコードもあります。
byebug起動時や
rails console
で使用することを想定しています。byebugの使い方よく分からんて人は以下の記事で使えるようにしてください。
https://web-nari.net/2018/03/16/byebug/メソッド一覧
hoge
という変数の参照できるメソッドの中で、メソッド名にmethod_name
が含まれるメソッドの一覧を返します。hoge.methods.select {|name| name.to_s.include?("method_name") }メソッド定義場所
hoge
という変数の参照できるfoo
メソッドの定義されてるクラスを返します。hoge.method(:foo)ソースファイルのパス
hoge
という変数の参照できるfoo
メソッドの定義されてるファイルのパスを返します。hoge.method(:foo).source_locationコールバック検索
Hoge
というクラスに定義されてるsave時のコールバックの一覧を返します。cb.kind.eql?
の引数にafterかbeforeを与えると、それぞれafter_コールバック関数、before_コールバック関数に定義されてるコールバックを返します。Hoge._save_callbacks.select { |cb| cb.kind.eql?(:after) }.map { |cb| cb.filter }その他のコールバック関数
save
だけではなくて以下のコールバック関数がそれぞれ利用可能。=> [:validate, :validation, :initialize, :find, :touch, :save, :create, :update, :destroy, :commit, :rollback, :before_commit, :before_commit_without_transaction_enrollment, :commit_without_transaction_enrollment, :rollback_without_transaction_enrollment]例えば
after_validation
のコールバックの一覧が知りたい場合は、以下のようにすればいい。Hoge._validation_callbacks.select { |cb| cb.kind.eql?(:after) }.map { |cb| cb.filter }クラス化
:string
という名前から該当するクラスに変換します。例の場合だと、String
が返ってきます。該当するクラスがない場合はNameError: uninitialized constant
が発生します。:string.to_s.classify.constantizeクラスの確認
hoge
というオブジェクトのクラスを確認する。hoge.class継承元を検索する
hoge
というオブジェクトの継承クラスやそのさらに祖先も検索できる。hoge.superclass # 親クラスを検索 hoge.ancestors # 祖先を検索インスタンス変数の確認
hoge
というオブジェクトが持ってるインスタンス変数を返します。クラス変数はclass_variables
で参照可能。hoge.instance_variablesまとめ
タイトルの勢いの割に書いてみたらすんごい少なかった。
これからも追加していきます。
- 投稿日:2021-01-18T12:47:24+09:00
0からRuby on Railsの環境構築【Cloud9】(Rubyのバージョン変更からRailsのインストールまで)
Ruby on Railsの環境構築(Cloud9)
本記事はRuby on Railsの環境構築を初心者の方でも迷わずできるように解説した記事です。
Ruby on Railsは、広範囲にわたる開発で使用されています。日本のスタートアップでも採用されていることが多く、食べログやnote、会計ソフトのfreeeなどもRuby on Railsによって開発されています。
そんなRuby on Railsですが、2019年8月にバージョン6.0がリリース、2020年12月にはバージョン6.1がリリースされました。また、Ruby自体も2020年12月にバージョン3.0に大型アップデートされ新しい機能が追加されています。
ここでは、Ruby 3.0とRuby on Rails 6.1.1の環境構築方法を説明していきます。
環境
- Cloud9 (Mac、Windowsどちらも対応可能です)
- Macでの環境構築方法の記事も公開しています。
- Amazon Linux2
- Cloud9の環境を立ち上げる際に、Amazon Linux2を選択している必要があります。
Rubyのバージョンを確認する
以下のURLからコンソールにサインインして、Cloud9を立ち上げましょう。
https://us-east-2.console.aws.amazon.com/console/
Cloud9上のTerminalを立ち上げて、以下のコマンドを実行してRubyのバージョンを確認しましょう。
ruby -v
以下のように数字が表示されます。
ruby -v ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]ここではRubyの環境が「2.6.3」であることがわかります。
Rubyのバージョンを変更する
それではここからRubyのバージョンを変更していきます。
Rubyのバージョン管理ツールであるrvmを使います。
以下のコマンドでインストール済みのRubyのバージョンを確認します。
rvm list実行結果は以下になります。
rvm list =* ruby-2.6.3 [ x86_64 ] # => - current # =* - current && default # * - defaultまた、以下のコマンドでインストール可能なRubyのバージョンを一覧表示します。
rvm list known実行結果で、自分のインストールしたいバージョンがあることを確認してください。
rvm list known //====略==== # MRI Rubies [ruby-]1.8.6[-p420] [ruby-]1.8.7[-head] # security released on head [ruby-]1.9.1[-p431] [ruby-]1.9.2[-p330] [ruby-]1.9.3[-p551] [ruby-]2.0.0[-p648] [ruby-]2.1[.10] [ruby-]2.2[.10] [ruby-]2.3[.8] [ruby-]2.4[.6] [ruby-]2.5[.5] [ruby-]2.6[.3] ruby-head //====略====ここでインストールしたいバージョンがない場合は、rvmのアップデートをおこなう必要があります。
アップデートをするには、以下のように
get
コマンドを実行します。rvm get latestrvm get latest //====略==== Thanks for installing RVM Please consider donating to our open collective to help us maintain RVM. Donate: https://opencollective.com/rvm/donate RVM reloaded!インストールしたrvmのバージョンを確認しましょう。
rvm -v rvm 1.29.10 (1.29.10) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io]再度インストールしたいバージョンがあるかを確認しましょう。
rvm list known //====略==== # MRI Rubies [ruby-]1.8.6[-p420] [ruby-]1.8.7[-head] # security released on head [ruby-]1.9.1[-p431] [ruby-]1.9.2[-p330] [ruby-]1.9.3[-p551] [ruby-]2.0.0[-p648] [ruby-]2.1[.10] [ruby-]2.2[.10] [ruby-]2.3[.8] [ruby-]2.4[.10] [ruby-]2.5[.8] [ruby-]2.6[.6] [ruby-]2.7[.2] [ruby-]3[.0.0] #←インストールしたいバージョン ruby-head //====略====次に、以下のコマンドを実行してRubyのバージョンをインストールします。
<version>
の部分には、インストールするバージョンを指定してください。rvm install <version>
以下のように表示されれば正常にインストールされています。
ここでは
3.0
を指定しています。rvm install 3.0 //===略=== ruby-2.7.2 - #generating global wrappers....... ruby-2.7.2 - #gemset created /home/ec2-user/.rvm/gems/ruby-2.7.0 ruby-2.7.2 - #importing gemsetfile /home/ec2-user/.rvm/gemsets/default.gems evaluated to empty gem list ruby-2.7.2 - #generating default wrappers.......以下のコマンドで使用するバージョンを指定しましょう。
rvm use <version> rvm use 3.0.0最後に以下のコマンドで、先ほど指定したバージョンが表示されることを確認してください。
ruby -v ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]以上でRubyのバージョン変更は終了です。
Bundlerをインストール
Rubyをインストールしたら、次にBundlerをインストールします。
RubyではGemというライブラリを使いパッケージを管理しています。
Gemコマンドで簡単にインストールやアンインストールができますが、複数のGemを使うとGem同士で依存関係が生まれ、バージョン違いによる不具合が出てきます。
そこで、BundlerでGemのそれぞれのバージョンを正確に追跡し管理して、Rubyプロジェクトに一貫した環境を提供します。
それではBundlerをインストールしましょう。以下のコマンドを入力します。
gem install bundler
コマンドを実行するとインストールが始まります。
インストールされたBlundlerのバージョンを確認しましょう。
次のコマンドを入力します。
bundler -v
コマンドの実行後、次のように表示されます。
bundler -v Bundler version 2.1.4
「version 2.1.4」とバージョン情報が表示されました。
これでBlundlerがインストール済みであることを確認できました。
yarnをインストール
次にyarnをインストールします。
yarnはJavaScriptのライブラリの利用に必要なパッケージマネージャです。
yarnと互換性のあるnpmというパッケージマネージャもあります。しかし、Rails6ではWebpackerが標準になったことにより、yarnが必要です。
それでは、yarnのインストールを行いましょう。
次のコマンドを入力します。
npm i -g yarn
yarnが実際にインストールされたかを確認するために、次のコマンドを入力します。
yarn -v
コマンドを実行すると、次のように表示されます。
yarn -v 1.22.10
ここでは「
1.16.0
」と表示されました。バージョン情報が表示されていれば、yarnがインストールされたことが確認できます。Ruby on Railsのバージョンを確認する
ここからはRailsのバージョンを確認していきます。
Cloud9のTerminalで以下のコマンドを実行してRailsのバージョンを確認しましょう。
rails -v
以下のようにデフォルトで指定されるRailsのバージョンが表示されます。
rails -v Rails 5.0.0
rails new
を実行する際にバージョンを指定しないとこのバージョンでアプリケーションが作成されます。次に、インストールされているRailsのバージョンを確認しましょう。
以下のコマンドを実行して下さい。
gem list rails以下のように表示されるので、railsの項目を確認しましょう。
「5.0.0」がインストールされていることがわかるかと思います。gem list rails *** LOCAL GEMS *** rails (5.0.0) rails-dom-testing (2.0.3) rails-html-sanitizer (1.3.0) sprockets-rails (3.2.2)Ruby on Railsのインストール
いよいよ、Railsをインストールします。
Railsのインストール時に「
-v バージョン番号
」とバージョンを指定してインストールできます。今回はバージョン「
6.1.1
」をインストールします。次のコマンドを入力します。
gem install rails -v 6.1.1コマンドを実行すると、インストールを開始します。インストールの完了までに数分かかることがあります。
Railsをインストールしたら、Railsのバージョンを確認するために次のコマンドを入力します。
rails -v
コマンドを実行すると、次の画面が表示されます。
rails -v Rails 6.1.1
「
Rails 6.1.1
」と表示されました。これで無事にRuby on Railsのインストールが完了しました。
※本記事はTechpitの教材を一部修正したものです。
参考
- 投稿日:2021-01-18T11:28:50+09:00
Rails 記事詳細リンクは、ループ内でしか機能しない?
背景
Page#homeで記事一覧(Posts#index)を表示し、そこに記事詳細(Posts#show)をリンクを貼った。そのときに学んだ事の備忘録。
結論
記事詳細リンクはこう貼った。(成功したコード)
<% if @posts.any? %> <% @posts.each do |p| %> #記事詳細のリンク↓ ++<%= link_to post_path(p), class: 'text-body' do %> <div class="card mt-4"> <%= image_tag p.image.url, class: 'img-fluid' %> <div class="card-body"> <h5 class="card-title"><%= p.title %></h5> <p class="card-text"><%= p.tag %></p> </div> </div> <% end %> <% end %> <% end %>
エラーが起きたコード
↓Couldn't find Post without an ID / ActiveRecord::RecordNotFound
<% if @posts.any? %> ++ <%= link_to post_path(post), class: 'text-body' do %> <% @posts.each do |p| %> <div class="card mt-4"> <%= link_to post_path(p), class: 'text-body' do %> <%= image_tag p.image.url, class: 'img-fluid' %> <div class="card-body"> <h5 class="card-title"><%= p.title %></h5> <p class="card-text"><%= p.tag %></p> </div> </div> <% end %> ++ <% end %> <% end %>なぜこれでワークしたのか?
restfulのこのコードは、
post_path(post)繰り返し処理の
<% @posts.each do |post| %>|post|からきている。
↓
繰り返し処理の外で、<%= link_to post_path(post), class: 'text-body' do %>と書くと、(post)の意味がコンピュータにはわからなくなる。
もう一度うまく行ったコード
<% if @posts.any? %> <% @posts.each do |p| %> #記事詳細のリンク↓ ++<%= link_to post_path(p), class: 'text-body' do %> <div class="card mt-4"> <%= image_tag p.image.url, class: 'img-fluid' %> <div class="card-body"> <h5 class="card-title"><%= p.title %></h5> <p class="card-text"><%= p.tag %></p> </div> </div> <% end %> <% end %> <% end %>この記事を参考にしました。https://skillhub.jp/courses/158/lessons/835
さいごに
本当にループ内でしか記事詳細は機能しないのかはわかりませんが(ユーザープロフィールなどでは、ループは記述していなくても、記事詳細のリンクはワークしている。)、とりあえずうまく行っているので、深くは追求しません。
- 投稿日:2021-01-18T09:59:54+09:00
wrong number of arguments (given 1, expected 0)
いくつかエラー原因はあるかと思いますが、僕の場合は、paramsと[:id]の間にスペースが空いているという事でした。
#スペースが空いている @post = Post.find(params [:id])エラーが消えた。
@post = Post.find(params[:id])
- 投稿日:2021-01-18T09:09:13+09:00
Ruby on Railsのconfig設定
この記事ではRails6.1のconfig設定をメモしています。
config/application.rb
タイムゾーンをローカルに設定する
config.time_zone = 'Tokyo' config.active_record.default_timezone = :localconfig/puma.rb
developmentではlocalhost:3000、productionではunix socketを使用する
if ENV.fetch("RAILS_ENV", "development") == 'production' bind "unix://#{Rails.root}/tmp/sockets/puma.sock" else port ENV.fetch("PORT") { 3000 } endconfig/environments/production.rb
キャッシュにredisを使用する
cache_servers = %w(redis://localhost:6379/1) config.cache_store = :redis_cache_store, { url: cache_servers, connect_timeout: 30, read_timeout: 0.5, write_timeout: 0.5, reconnect_attemps: 1, error_handler: -> (method:, returning:, exception:){ Raven.capture_exception exception, level: 'warning', tags: { method: method, returning: returning } } }セッションにredisを使用する
config.session_store :redis_store, servers: [{ host: 'localhost', port: 6379, db: 0, namespace: 'sessions', }], key: "_session_name_", expire_after: 30.minutesRailsでredisを使用するためGemfileにgemを追加する
Gemfilegem 'redis-rails'config/webpacker.yml
webpackを使用している場合はhtmlの変更も検知できるようにする?
ここの設定は検討が必要extensions: ... - .html ...
- 投稿日:2021-01-18T07:59:08+09:00
不正な config/master.key が原因でRailsの起動に失敗するケースとその対処方法
発生する問題
git cloneでローカルに持ってきたRailsアプリを、
rails s
やrails c
で起動しようとすると以下のようなエラーが発生する。$ rails s => Booting Puma => Rails 6.1.0 application starting in development => Run `bin/rails server --help` for more startup options Exiting /Users/jnito/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-6.1.0/lib/active_support/message_encryptor.rb:203:in `rescue in _decrypt': ActiveSupport::MessageEncryptor::InvalidMessage (ActiveSupport::MessageEncryptor::InvalidMessage)エラーの原因
config/master.key
の鍵がconfig/credentials.yml.enc
用の鍵と一致していないため、例外が発生してRailsが起動しなくなった。もう少し詳しく
config/master.key
は通常、.gitignore
に追加してソースコード管理の対象外とするファイルである。そのため、git cloneしてもconfig/master.key
はローカルにはコピーされない。その状態で「
credentials.yml.enc
を編集したい」と思ってEDITOR="vi" bin/rails credentials:edit
のようなコマンドを実行すると、以下のようなメッセージとともに、config/master.key
が新規に作成されてしまう。(加えて、credentials.yml.enc
の編集もできない)$ EDITOR="vi" bin/rails credentials:edit Adding config/master.key to store the encryption key: 6eb4ad(以下略) Save this in a password manager your team can access. If you lose the key, no one, including you, can access anything encrypted with it. create config/master.key Couldn't decrypt config/credentials.yml.enc. Perhaps you passed the wrong key?ただし、新規に作られた
config/master.key
はgit cloneしてきたconfig/credentials.yml.enc
の鍵ではないため、Rails起動時に復号化に失敗して例外(ActiveSupport::MessageEncryptor::InvalidMessage)が発生してしまう。対処方法
状況によって対処方法は異なる。
git cloneする前にcredentials.yml.encが使われていた場合
開発チーム内で鍵の共有方法が決められているはずなので、その運用ルールに従って適切な
config/master.key
を自分のローカルに配置する。credentials.yml.encが今まで使われていなかった場合
自動的に作られた
config/master.key
を削除すればRailsが起動するようになる。今後も
credentials.yml.enc
を使わないのであれば、config/credentials.yml.enc
ごと削除するのも一手。逆に、これから
credentials.yml.enc
を使っていく(=自分がチーム内で最初の利用者になる)場合は、新規にconfig/credentials.yml.enc
を作り直した上でcredentials/master.key
をチーム内で適切に管理・共有する必要がある。
(ただし、どこかから適切なconfig/master.key
を入手できれば新規に作り直す必要はない)# 既存のcredentials.yml.encを削除 $ rm config/credentials.yml.enc # credentials.yml.encとmaster.keyを新規に作成して編集する $ EDITOR="vi" bin/rails credentials:edit Adding config/master.key to store the encryption key: 917c31e316061bcdd909754a56897f61 Save this in a password manager your team can access. If you lose the key, no one, including you, can access anything encrypted with it. create config/master.key File encrypted and saved.動作確認環境
- Rails 6.1.0
- 投稿日:2021-01-18T07:56:43+09:00
SNS認証時のエラーについて
経緯
ウィザード形式のユーザー登録機能実装後、
SNS認証を追加するために
registrations_controller内に必要な記述をした。
(userモデルは記述済み。)controllerclass Users::RegistrationsController < Devise::RegistrationsController #一部省略 def create if params[:sns_auth] == 'true' pass = Devise.friendly_token params[:user][:password] = pass params[:user][:password_confirmation] = pass end super #ここまでがSNS認証実装に追記した記述 @user = User.new(sign_up_params) #ここからウィザード形式のための記述 unless @user.valid? render :new and return end session["devise.regist_data"] = {user: @user.attributes} session["devise.regist_data"][:user]["password"] = params[:user][:password] @profile = @user.build_profile render :new_profile end #一部省略 endエラー内容
新規登録を行ってみたところ下記のようなエラーメッセージが出る。
AbstractController::DoubleRenderError in Users::RegistrationsController#create同一アクション内にrenderもしくはredirectが複数回使われていますよ!という内容。
renderの後にand returnで処理を終わらせているのになぜ?となる。。エラー原因
SNS認証を実装するために追記した記述の最後にある
superによるもの。
(superとは、継承元のクラスにある、superを記述したメソッドと同名のメソッドを呼び起こすことができる。)呼び起こし先は下記である。
(https://github.com/heartcombo/devise/blob/master/app/controllers/devise/registrations_controller.rb) 参照元RegistrationsControllerclass Devise::RegistrationsController < DeviseController # 一部省略 def create build_resource(sign_up_params) resource.save yield resource if block_given? if resource.persisted? if resource.active_for_authentication? set_flash_message! :notice, :signed_up sign_up(resource_name, resource) respond_with resource, location: after_sign_up_path_for(resource) else set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}" expire_data_after_sign_in! respond_with resource, location: after_inactive_sign_up_path_for(resource) end else clean_up_passwords resource set_minimum_password_length respond_with resource end end # 一部省略 endこの中のrespond_withがどうやらエラーの原因みたい。
respond_withは、renderやredirectの役割を持っているため。
よってsuperで呼び起こしたメソッド内でrender or redilectしようとしたけど、その後の記述でまたrenderがあり
どっち?となりエラーが起きたと考えられる。
- 投稿日:2021-01-18T02:44:30+09:00
36年前の沢口靖子さんのプロフィールを確認した話
当時の記憶
36年前当時、JR-200 (Wikipedia)というグラフィック機能はイマイチなパソコンを使っていました。歳がバレますね(笑)。
パソコン雑誌に掲載されるプログラムの数も限られており、他機種からの移植をしようにもグラフィック機能がネックで諦めることが多かったように思います。
その当時のPOPCOM (Wikipedia)という雑誌にデビューしたばかりくらいの沢口靖子さんを題材にしたプログラムが掲載されたのですが、自分のパソコンでは実行できないと諦めたと思っていました。自分の記憶では沢口靖子さんの顔をパソコンのグラフィックで表示するプログラムだと思っていました。何故かその事がずっと気がかりというか頭の片隅にあったのですが、偶然メルカリでその雑誌を見かけて思わず買ってしまいました。表紙に見憶えがありすぐに分かりました。目次の写真もあったので記事の内容も確認できました。値段は当時の値段の約6倍の3000円しましたけど。
実際の記事
届いた雑誌を開いてみると、広告や記事を読むと当時が思い出されとても懐かしかったです。漫画で世界時計のプログラムの解説がされていたりして隔世の感がありました。
そして肝心の沢口靖子さんのプログラムを確認すると…全然グラフィック系の命令は無く、ほぼほぼPRINT文とDATA文という…。昔の記憶なんて当てになりませんね。これやったら全然JR-200でも実行できたやん。多分。
ソースコード
取り敢えず内容が気になったので、BASICより簡単そうなRubyで書き直してみました。
DATA
文の部分はスマホでGoogleさんの文字認識に助けてもらい入力しました。
もう36年前で時効?と思いますのでソース掲載してみます(問題はご指摘いただければ幸いです)。沢口靖子さんの当時のプロフィールをご覧になりたい方はお試しください
一応、動作確認した環境は以下です。
- macOS 11.2 Beta (20D5042d)
- ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin20]
sawaguchi-yasuko.rb#!/usr/bin/ruby #-*- coding:utf-8 -*- require 'nkf' questions = [ "セイネンガッピ : ", "シュッシンチ : ", "シンチョウ,タイジュウ,B・W・H ノ サイズ : ", "ケツエキガタ,セイザ : ", "カゾクコウセイ : ", "シュミ : ", "スキナ スポーツ : ", "トクイナ カモク : ", "スキナ タベモノ : ", "イッテミタイ クニ : ", "スキナ タレント ミュージシャン : ", "リソウノ タイプノ ダンセイ : ", "キライナ タイプノ ダンセイ : ", "デビューノ キッカケ : ", "ドンナ オンナノヒトニ ナッテイキタイカ : ", "スキナ イロ : ", ] data = [ 161,100,93,89,104,89,95,95,67,372,372,386,374,62,429,62,386,374,364, 62,389,67,95,102,110,193,214,62,100,102,209,201,62,62,108,93,89,102, 108,89,108,106,67,126,62,374,478,398,62,429,398,384,478,62,386,478,67, 401,401,62,424,424,62,362,413,62,421,62,100,413,475,62,374,396,478, 379,67,372,475,374,478,379,374,475,389,355,367,62,369,364,374,478,374, 475,389,355,367,67,406,413,391,62,391,364,369,364,67,369,364,384,478, 62,374,478,62,391,376,62,406,478,389,398,67,386,458,398,478,62,464, 364,62,411,458,64,62,456,360,379,478,464,408,62,447,67,391,364,391,62, 77,424,364,389,478,62,374,478,62,364,398,456,367,411,62,408,384,469, 79,67,453,360,439,475,62,362,475,461,62,372,429,384,360,391,67,401, 355,357,426,481,461,62,426,355,367,376,475,62,406,478,62,364,357,389, 355,413,364,406,62,398,421,389,364,62,426,408,62,374,478,62,364,364, 411,67,389,403,384,379,406,62,389,478,389,475,62,374,389,478,355,367, 62,421,62,426,408,62,374,478,62,364,450,67,398,478,364,95,374,364,62, 408,367,434,367,62,389,475,406,478,467,458,62,421,62,372,360,406,478, 341,389,355,475,62,406,478,62,453,367,389,355,367,62,389,437,389,398, 67,393,364,374,379,62,374,458,62,391,432,478,406,62,374,472,364,364, 62,408,62,364,472,467,464,62,372,475,411,62,421,62,426,408,62,413,62, 411,461,398,364,67,389,469,62,426,481,475,379,67,133,151,126,159,141, ] ans = "" data.each {|a| b = (300.0*Math.sin(a.to_f/10.0*3.1416/180.0)).to_i c = b.chr if c == '#' puts questions.shift, NKF.nkf("--oc=UTF-8 --ic=CP932", ans), ans = "" next end ans << c }追記(2021-01-18):
リンクがちゃんと動作していなかったのを修正しました。 @scivolaさん、編集リクエストありがとうございました!
- 投稿日:2021-01-18T01:44:41+09:00
Rails 検索機能の実装
Rails で検索機能を実装
View に form_tag で検索窓を設置
<div class="search_field"> <p>検索:</p> <%= form_tag(posts_path, :method => 'get') do %> <%= text_field_tag :search, "", {class: "search_field_type"}%> <%= submit_tag 'Search', :name => nil, class: "search_field_btn" %> <% end %> </div>form_tag(posts_path, :method => 'get') の path の部分はクラス名によって任意に変更してください。
Model に検索のためのメソッド search を設定
post.rb
def self.search(search) if search Post.where(["title LIKE ?", "%#{search}%"]) else Post.all end endtitle カラムの中の一部でも検索した文字列が該当すればヒットするように設定。
詳しくは SQL の where 句を調べれば何となく設定している意味わかります。
https://www.wakhok.ac.jp/biblion/1994/DB/subsection2.4.3.5.html
Controller の調整
posts_controller.rb
def index @posts = Post.all.includes(:user).order(id: "DESC").search(params[:search]) endこれで検索にヒットした記事が @posts に入る。
あとは View 側で一覧表示処理をすれば OK です。
- 投稿日:2021-01-18T01:44:14+09:00
Rakefileでdirectoryタスクを記述してもディレクトリが作成されない
事象
Rakefileでdirectoryタスクを記述してもディレクトリが作成されない。
結論
directoryタスクを記述(宣言)するだけではなくて、そのタスクが
実行されるようにどこからか呼び出さないといけなかった。詳細
directory "doc" desc "create task" task "create" do cd "doc" end> rake -t create ** Invoke default (first_time) ** Invoke create (first_time) ** Execute create cd doc rake aborted! Errno::ENOENT: No such file or directory @ dir_s_chdir - doccreateタスクの前提タスクとして、ディレクトリタスク(名前はdoc)を指定しないといけなかった。
directory "doc" desc "create task" task "create" => "doc" do cd "doc" end> rake -t create ** Invoke create (first_time) ** Invoke doc (first_time) ** Execute doc mkdir -p doc ** Execute create cd doc4時間くらい悩んだ。
しかし、Rakeの情報源、あんまり見つからないなぁ。asciidoctor絡みで、少し蛇足情報を↓
蛇足: 話の背景
asciidoctorを使ってadocファイルをhtmlに変換できるようになったのは良いが
さて公開はどうしようと思ったところで、Github Pagesにアップするか、
いっそのことHugoを使ってみるか。と思って調べてたんだけど、どうも画像を置くディレクトリの
取り扱いが難しい。contents/posts/api.adoc にコンテンツを置いて、画像ディレクトリは
contents/posts/images/image01.png のようにしたくて、
adocの方では、:imagesdir: ./images
を指定。参照のところで
:image: image01.png[]
。$ asciidoctor -b html5 ./api.adocこれで
contents/posts/api.html
が作成されよしよし。
hugo service -D
でHTML確認しようと思ったら、
hugoが絡むと、publish/posts/api.html
からは
publish/posts/api/images/image01.png
を参照するように構成される。apiディレクトリはどこからきた…?
画像ディレクトリの置き場を
contents/posts/api/images/image01.png
に
置き換えれば、HTML表示上は整合製がとれるから問題ないんだけれど、
そうすると、asciidoctor
のコマンド実行でHTML出力した時のパスと異なるし(marked2でプレビューする際に、プロセッサ、プリプロセッサとしてasciidoctorを使用してHTML出力させてるため困る)。ということで、 hugoは諦めて、
asciidoctor
コマンドの挙動でそのままHTMLを作って
生成される画像パス通りに画像ファイルを配置して、、、。みたいなことをやりたくて
rake
を使ってみたら、変なところではまった。蛇足2: marked2でasciidoctorを使う
~/bin/asciidoctor-marked2#!/usr/bin/env ruby # -*- coding: utf-8 -*- # frozen_string_literal: true marked_origin = ENV["MARKED_ORIGIN"] || Dir.pwd rbenv = File.expand_path("~/.rbenv/bin/rbenv") asciidoctor = "#{rbenv} exec asciidoctor" cmd = "#{asciidoctor} -a skip-front-matter --safe-mode safe --base-dir #{marked_origin} --backend html5 -o - -" system(cmd)こんな感じのRubyスクリプトを書いて実行権限をつけておいて。
こんな感じでCustom Processorで指定すると、marked2でasciidocのプレビューができます。
蛇足3: Hugoで必要なFront Matterをasciidoctorコマンド利用時にスキップする
Hugoがらみだと、Front Matterというのを記事につけなければいけなくて、
asciidoctorコマンドの処理と干渉して困っていたんだけれど。Hugo側は、adocのH1見だしや、ドキュメントヘッダは良い感じに読み飛ばしてくれる
asciidoctorに
-a skip-front-matter
を付ければ良いんですね。https://docs.asciidoctor.org/asciidoctor/latest/html-backend/skip-front-matter/
- 投稿日:2021-01-18T01:39:57+09:00
【Ruby on Rails】Rails Tutorialを全部終えたので、追加で「ストック機能」を実装してみた
はじめに
Railsで「qiita」のような記事をストックする機能を実装したので、その方法を備忘録としてまとめておきます。
(基本的にはいいね機能と同じやり方)※また、以下に記載する方法は前提として「Rails tutorial」を最後まで終えた程で記載させていただきます。
実装すること
・記事をストックするボタンを作成する。
・記事がどれくらいストックされているか表示する。
・ストックした投稿をストック一覧ページで確認できるようにする。Stockモデルを作成する
ストック機能を実装するにあたって、最初のステップとして
Stockモデル
を作成します。$ rails g model Stockカラムは2つ作成します。
・post_id
カラム
・user_id
カラム
マイグレーションファイルは以下のように修正します。xxxxxxxxxxxxx_create_stocks.rbclass CreateStocks < ActiveRecord::Migration[6.0] def change create_table :stocks do |t| t.references :user, foreign_key: true t.references :post, foreign_key: true t.timestamps end add_index :stocks, [:user_id, :post_id], unique: true end end$ rails db:migrate2つのカラムは、それぞれ外部キーとして追加しておきます。
このテーブルは中間テーブルとして活用し、誰がどの投稿をストックしたかという情報を格納しておきます。
また、user_id
カラムとpost_id
カラムの組み合わせが一意になるようにインデックスを設定しておきます。アソシエーションを設定する
以下のようにアソシエーションを設定します。
stock.rb
にはアソシエーションのついでにバリデーションも追加しておきます。user.rb
user.rbhas_many :stocks, dependent: :destroypost.rb
post.rbhas_many :stocks, dependent: :destroystock.rb
stock.rbbelongs_to :user belongs_to :post validates :user_id, presence: true validates :post_id, presence: truestockコントローラを作成する
次に
stockコントローラ
を作成します。$ rails g controller Stocksコントローラに処理を加える
stocks_controller.rb
stockコントローラ
を作成したら、今回の実装に必要なアクションを定義しておきます。stocks_controller.rbclass StocksController < ApplicationController def create @stock = current_user.stocks.create(post_id: params[:post_id]) redirect_back(fallback_location: root_path) end def destroy @stock = Stock.find_by( post_id: params[:post_id], user_id: current_user.id ) @stock.destroy redirect_back(fallback_location: root_path) end def index @stocks = Stock.where(user_id: current_user.id) end endposts_controller.rb
次に
postコントローラ
のshowアクションの部分に変数を追加します。
post/showページにストックボタンを表示する為です。posts_controller.rbdef show @post = Post.find_by(id: params[:id]) @stock = Stock.find_by( user_id: current_user.id, post_id: @post.id ) endルーティングを設定する
次に必要なルーティングを追加していきます。
routes.rb
routes.rbresources :stocks, only:[:create, :destroy, :index]今回はストックする機能(create)とストックを解除する機能(destroy)の他にストックした記事を表示する為の「index」も追加しておきます。
ストックしているかどうかの確認の処理と加える
ユーザーが記事に対して、すでにいストックをしているのかどうかを確認することができるように
stock?
メソッドを定義しておきます。user.rb
user.rb#ユーザーがすでにストックしているか確認する def stock?(post) self.stocks.exists?(post_id: post.id) endViewを追加&変更する
ストックボタンを実装する為に、まずはパーシャルを3つ作成していきます。
shared/_stock.html.erb
_stock.html.erb<%= form_with(url:stocks_path, local: true) do |f| %> <div><%= hidden_field_tag :post_id, @post.id %></div> <%= f.submit "ストック", class:"btn-sm" %> <% end %>shared/_unstock.html.erb
_unstock.html.erb<%= form_with(url: stock_path(@stock), html: {method: :delete}, local: true) do |f| %> <div><%= hidden_field_tag :post_id, @post.id %></div> <%= f.submit "ストックをやめる", class:"btn-sm" %> <% end %>shared/_stock_form.html.erb
_stock_form.html.erb<% if current_user.stock?(@post) %> <%= render "shared/unstock" %> <% else %> <%= render "shared/stock" %> <% end %> <%= @post.stocks.count %>posts/show
ボタンを追加したいところに以下パーシャルを追加します。
show.html.erb<%= render "shared/stock_form" %>stocks/index.html.erb
indek
ページを新しく追加し、ストックした記事を表示させます。index.html.erb<h1>ストック一覧</h1> <h2>あなたがストックしたポートフォリオの一覧です。</h2> <% @stocks.each do |stock| %> <%= link_to stock.post.name, post_path(stock.post) %> <% end %>完成画
おわり
以上で終了です。
機能の実装方法を記述しているので、デザインは全く気にしていません。
デザインはお好みで付け足してみてください。ソースコードはこちら
ポートフォリオ作成中のものですが。以下にあります。
https://github.com/iittaa/Sharing_Portfolio
- 投稿日:2021-01-18T00:16:24+09:00
Jekyllで日本語カテゴリをデコードして表示
2021/01/18追記:GitHub Pages上ではデコードが成功していない!原因が解消でき次第この文言は削除する。
URLとエンコードの組み合わせで記事は多いが、Jekyllで作成した記事中のエンコードされた日本語文字列をデコードする話は見かけないので、ここにまとめておく。
環境と状況
- jekyll 4.2.0
- ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x64-mingw32]
Jekyllテーマのひとつ、Minimal Mistakesでは2021/01/17時点でベータ機能だが、breadcrumbs機能が使える。
パンくずリスト(breadcrumb list)は、ウェブサイト内でのウェブページの位置を、ツリー構造を持ったハイパーリンクの一覧として示すもの。
パンくずリスト - WikipediaMinimal MistakesではJekyllのカテゴリーでツリー構造を作り、"ホーム/親カテゴリー/子カテゴリー/タイトル"というスタイルで記事トップに表示してくれる。つまり、以下のようにFront matterを定義したポストファイルの場合、
--- title: "OMG! OMG!" categories: - OMG - Bloody ---
Home/OMG/Bloody/OMG! OMG!
のように表示してくれる。それぞれがリンクをもっているので、カテゴリーで他の記事を探す際や親、子カテゴリーを把握するのに便利である。しかし、カテゴリーを日本語で定義すると、エンコードされた文字列が表示されて非常に見た目が悪い。
Apple TV+1年間無料で1時間の電話問い合わせ - Mt. Nishiブログより。あ、宣伝です。
ともかく、デコードすれば良い。あとはJekyll的にどう実装するかである。
実装
YAMLファイル(色々と基本設定をしているファイル
_config.yml
)が./
にあるとして、Minimal Mistakesの場合、カテゴリー名をエンコードしてくれちゃっている(breadcrumbsを作ってくれている)プログラムは./_includes/breadcrumbs.html
で実装されている。以下はその一部。./_includes/breadcrumbs.html<a href="{{ crumb | downcase | replace: '%20', '-' | prepend: path_type | prepend: crumb_path | relative_url }}" itemprop="item"><span itemprop="name">{{ crumb | replace: '-', ' ' | replace: '%20', ' ' | capitalize }}</span></a>今回の場合、リンクそのものは問題ないので無視して(
<a href="...>
部分)、実際にページ上に表示される{{ crumb ...}}
部分をいじればよさそうである。他のテーマでも、仮にbreadcrumbsが実装されている場合は、その機能をオン・オフするパラメータ(ex.
breadcrumbs: true
)で全体の検索をかけたら見つかりやすいと思う。フィルター(上記コードの
capitalize
みたいなもの)を新しく定義していじることにする。まず、./_plugins/decode_category.rb
ファイルを作成する。_plugins
フォルダがなければ作成する。Rubyで新たにJekyll用のフィルターを作成する。
./_plugins/decode_category.rbrequire "URI" module CustomFilter def decode_category(input) URI.decode_www_form(input) end end Liquid::Template.register_filter(CustomFilter)
input
にはカテゴリー名が入る。Liquid~
部分はJekyll用フィルターとしてmoduleを定義するおまじないである。(URI.decode(input)
でも可能だが、obsoleteだとwarningメッセージが出る。)
{{ crumb | replace: '-', ' ' | replace: '%20', ' ' | capitalize | decode_category }}
のように、修正箇所に| decode_category
を加える。以上。
まとめ
日本語カテゴリーがエンコードされて表示される問題を、新しくフィルターを定義することでデコードさせて対処した。実装し終えてみると超簡単だが、情報が少ないので苦労した。同様にしてエンコード、URLのエンコード・デコードも実装できる。
参考資料
module URI (Ruby 3.0.0 リファレンスマニュアル)
jekyll カテゴリをURLは英語のままに日本語化する方法 - メモ用サブブログ