- 投稿日:2020-10-16T23:44:36+09:00
【Rails物語】デルメルの法則〜知りすぎてはいけない〜
概要
とある街に駆け出しのエンジニアのY君がいました
彼にとってプログラミングは難しいものでした
しかし彼はつよつよエンジニアになりたいと願い、数ヶ月前から毎日プログラミングの勉強をしてました
そんなある日、彼はより実践的な経験値を得るため共同開発チームに入るのでした
与えられたミッション(タスク)をこなし報告(プルリク)を出した時、それは起こりました、、、
デルメルの怒り
Y君はこんなコードを書いていました
<%= "#{message.user.name}さん : " %> <!-- messageの中身はこんな感じ{"user_id"=>1, "content"=>"やっほー"} -->、、、これがデルメルの怒りに触れてしまったのです。
デルメル「知りすぎじゃーーーーーー!!」
デルメル曰く、プログラミングの世界では「知りすぎている」のは罪なのです
この場合
message
はuser_id
は知っていて紐づいている。だけどuser
自体の事は知らないし、そのname
なんて事は知らないはずなのですギャングがボスの存在は知っていても本当の名前を知ってはならないように、、、(ディア○ロ思考)
これをデルメルの法則と言います
Y君は絶望の淵に立たされました
賢者の救援
どうすれば良いのかわからず途方にくれていたY君
そこへ仲間の賢者(つよつよエンジニア)がやってきてこう言いました(レビュー)
賢者「delegate使えばええんやで、スッ」
Y君「!!」
必殺delegate
delegateマクロを使うとメソッドを簡単に委譲できる
Y君は上記記事を参考に下記のようにコードを書き加えました
class User < ApplicationRecord has_many :messages def user_name "#{name}さん : " end end class Message < ApplicationRecord belongs_to :user delegate :user_name, to: :user endすると先ほどのデルメルぷんぷんコードはこうなりました、、、
<%= message.user_name %>これを見たデルメルは「まぁよかろう」とだけ言って去って行きました
罪の晴れたY君は、デルメルの法則を肝に命じて、新たな開発を進めていくのでした。
この物語は半フィクションです、あと本当にこれでデルメルの法則を守れてるのか微妙かもです、この方がいいよというやり方あったら是非教えてください!
- 投稿日:2020-10-16T23:44:36+09:00
【Rails物語】デメテルの法則〜知りすぎてはいけない〜
概要
とある街に駆け出しのエンジニアのY君がいました
彼にとってプログラミングは難しいものでした
しかし彼はつよつよエンジニアになりたいと願い、数ヶ月前から毎日プログラミングの勉強をしてました
そんなある日、彼はより実践的な経験値を得るため共同開発チームに入るのでした
与えられたミッション(タスク)をこなし報告(プルリク)を出した時、それは起こりました、、、
デメテルの怒り
Y君はこんなコードを書いていました
<%= "#{message.user.name}さん : " %> <!-- messageの中身はこんな感じ{"user_id"=>1, "content"=>"やっほー"} -->、、、これがデメテルの怒りに触れてしまったのです。
デメテル「知りすぎじゃーーーーーー!!」
デメテル曰く、プログラミングの世界では「知りすぎている」のは罪なのです
この場合
message
はuser_id
は知っていて紐づいている。だけどuser
自体の事は知らないし、そのname
なんて事は知らないはずなのですギャングがボスの存在は知っていても本当の名前を知ってはならないように、、、(ディア○ロ思考)
これをデメテルの法則と言います
Y君は絶望の淵に立たされました
賢者の救援
どうすれば良いのかわからず途方にくれていたY君
そこへ仲間の賢者(つよつよエンジニア)がやってきてこう言いました(レビュー)
賢者「delegate使えばええんやで、スッ」
Y君「!!」
必殺delegate
delegateマクロを使うとメソッドを簡単に委譲できる
Y君は上記記事を参考に下記のようにコードを書き加えました
class User < ApplicationRecord has_many :messages def user_name "#{name}さん : " end end class Message < ApplicationRecord belongs_to :user delegate :user_name, to: :user endすると先ほどのデメテルぷんぷんコードはこうなりました、、、
<%= message.user_name %>これを見たデメテルは「まぁよかろう」とだけ言って去って行きました
罪の晴れたY君は、デメテルの法則を肝に命じて、新たな開発を進めていくのでした。
この物語は半フィクションです、あと本当にこれでデメテルの法則を守れてるのか微妙かもです、この方がいいよというやり方あったら是非教えてください!
- 投稿日:2020-10-16T23:31:59+09:00
【図解】硬貨の和を再帰関数で求める【Ruby】
はじめに
ProjectEulerのProbem31についてです。
再帰関数での他の人の解答を参考にして、再帰関数初心者が解説してみました。
下で図解もしているので、そちらもぜひ見てください。問題
Problem 31 「硬貨の和」
イギリスでは硬貨はポンド£とペンスpがあり,一般的に流通している硬貨は以下の8種類である.
1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p).
以下の方法で£2を作ることが可能である.
1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p
これらの硬貨を使って£2を作る方法は何通りあるか?方針
coins = [1,2,5,10,20,50,100,200]
の中から任意の一枚ずつ取り出していく作業を、200になるまで繰り返す。
人間の手で行ってたらとてもじゃないけど、数えきれないのでこういう時は「再帰関数」
!再帰関数とは?
def ~ end
で定義した関数内で自分自身を呼び出す関数のことをいいます。
有名なものだとフィボナッチ関数などはよく再帰関数の入門として扱われたりします。
初めて聞いたよという方は検索してみてください。別の解説記事:【図解】階乗の再帰関数【Ruby】
解答
def coin_count(coins, goal, last = 0) if goal == 0 return 1 end total = 0 coins.each do |c| if c < last next end if goal >= c total += coin_count(coins, goal - c, c) end end total end coins = [1,2,5,10,20,50,100,200] puts coin_count(coins,200)恥ずかしながら自力では解けなかったのでこちらを元に作成しました。
この解法を日本語で解説しようと思います。解説
今回作りたいのは
200
という数値。
そこでgoal = 200
と設定して、硬貨一つの値まず関数内のeach文内から説明を始めます。
if c < last next endこれは直近で足した数
last
を超える数は足さないようにしています。
50 + 50 + 100
と50 + 100 + 50
は今回の問題では同じなので、昇順に足していくパターンのみを採用するようにします。
nextはブロック内のnext以降の処理をスキップするという処理になります。次に、再帰関数を用いている関数内の3つ目のif文内です。
if goal >= c total += coin_count(coins, goal - c, c) end今回、
c
はcoins = [1,2,5,10,20,50,100,200]
の要素のいずれかなので1 <= c
です。
ですのでcoin_count(coins, goal, last)
の引数として渡されるgoal
はgoal - c
は0に向かって減っていきます。仮に
c = 50、 goal = 200
の時は下のようになります。例) 再帰関数で goal - c が繰り返されていくと、引数の goal は 1回目: 150 ※coin_count(coins, 150, 50)で呼び出し 2回目: 100 ※coin_count(coins, 100, 50) 3回目: 50 ※coin_count(coins, 50, 50) 4回目: 0 ※coin_count(coins, 0, 50)4回目に
coin_count(coins, 0, 50)
で関数が呼び出されたとき、if goal == 0 return 1 endになるので、ここまできてようやく、
total += coin_count(coins, goal - c, c)の
coin_count関数
が戻り値 1
を持つことになります。
そこででようやくtotal += 1
となり、1プラスされます。
goal > 0
の時goal < 0
の時この場合は、
total = 0
が0のまま戻り値に渡されます。
最終的に組み合わせの総数がtotalの値として得られます。図解
この、関数が繰り返し呼び出されていく状態を図にしてみました。
終わりに
説明の足りない点や、間違っている点、よりわかりやすい説明があればどしどし教えてください。
読んでいただきありがとうございました。
- 投稿日:2020-10-16T23:23:54+09:00
【図解】階乗の再帰関数【Ruby】
はじめに
自分が初めて再帰関数を知ったときに、理解するのに苦労しました。
その際に紙に書いたことで理解できたので、それを今回図解しました。再帰関数とは?
def ~ end
で定義した関数内で自分自身を呼び出す関数のことをいいます。
有名なものだとフィボナッチ関数などがよく再帰関数の入門として扱われたりします。
初めて聞いた方は検索してみてください。階乗とは
1からnまでの連続するn個の自然数の積をnの階乗という。
n!と書き、例えば4!=1×2×3×4=24と表す。
ただし、0の階乗は1とする。引用元:goo辞書
再帰関数を用いたコード
def factorial(num) if num == 1 || num == 0 return 1 end return num * factorial(num - 1) end※
num < 0
が渡されてしまった場合は今回は考慮していません。非常にシンプルなコードですが、脳内で考えていると意外と混乱します。
図解
終わりに
まだまだ再帰関数初心者なので複雑なものは私もまだ混乱します。
ただ、今回図解したコードなどが基本になっていると思うので迷った時は初心に戻ろうと思います。この記事がどなたかの再帰関数理解の一助になれば幸いです。
- 投稿日:2020-10-16T21:24:55+09:00
Qiita に J 言語のシンタックスハイライトがないので自分で実装した
はじめに
最近 J 言語 にハマっているのですが、残念なことに Qiita のシンタックスハイライトは J をサポートしてくれていません 。
J は特に見た目がアレな言語なので、記事を書く側も読む側もシンタックスハイライトがあるとモチベーションが上がると思います。ということで、自分で実装することにしました。その記録を、ここに残しておこうと思います。
他の言語のシンタックスハイライトを実装する時に参考になるかもしれませんし、J 言語が他の言語と違いすぎてあまり参考にならないかもしれません。
大まかな手順
Qiita のシンタックスハイライトには現在 Rouge という Ruby 製のライブラリが使われています。このライブラリにプルリクエストを送ることで、J のハイライトを追加しようと考えました。
Rouge をフォークしたら、lexer 開発のガイド を見ながら始めます。
最初に、lexer を定義するファイルと、spec を追加します。
lib/rouge/lexers/j.rb# -*- coding: utf-8 -*- # # frozen_string_literal: true module Rouge module Lexers class J < RegexLexer title 'J' desc "The J programming language (www.jsoftware.com)" tag 'j' filenames '*.ijs', '*.ijt' # ここに lexer の実装を書く end end endspec/lexers/j_spec.rb# -*- coding: utf-8 -*- # # frozen_string_literal: true describe Rouge::Lexers::J do let(:subject) { Rouge::Lexers::J.new } describe 'guessing' do include Support::Guessing it 'guesses by filename' do assert_guess :filename => 'foo.ijs' assert_guess :filename => 'foo.ijt' end end describe 'lexing' do include Support::Lexing # ここにテストを書く end endこの他に
lib/rouge/demos/j
とspec/visual/samples/j
が必要ですが、始めは空のファイルで構いません。あとは、以下の手順を並行して行います。言葉で解説するより実際のコードを見た方が早いので、細かい説明は省きます。知っている/よく使う言語の lexer を見れば大体分かると思います。
spec を書く
RSpec の書き方を知っていれば、特に困ることはないと思います 1。
テストには
assert_tokens_equal
を使います。assert_tokens_equal "コード", トークン1, トークン2, ...トークンは、
[名前, テキスト]
の組で表します。トークンの名前については、一覧 を参照してください。実際のところ spec がほとんど書かれていない言語も多いようなので、あまり詳細に書く必要はないのかもしれません。
lexer を書く
lexer の記述にも、DSL(EDSL) が使われています。
state シンボル do rule 正規表現, トークン ... end詳しいことはここでは説明しません。分からないときは他の言語の lexer を見ると参考になると思います 2。
visual sample / demo を書く
visual sample (
spec/visual/samples/j
) は、正しくハイライトされるかを、目で見てチェックするためのテキストファイルです。ある程度の大きさのプログラムでも、単なるトークンの羅列でも構いません。demo (
lib/rouge/demos/j
) は、rouge.jneen.net に表示される短いコードです。テスト
README に書いてありますが、spec は rake を使ってテストします。visual sample は、rackup を実行してチェックします。(
localhost:9292
で demo が、localhost:9292/j
で visual sample が表示されます。)こだわった点
※ ここに書いてある内容は、J 言語を知らない人には全く通じないと思われます。
J のコードをハイライトする上で、一番大切なのは、全ての記号を演算子として扱ってはいけないという点です。記号を色分けできなければ、シンタックスハイライトの意味が半減してしまいます。
そこで、verb を関数 (
Name.Function
) 、adverb/conjunction を演算子 (Operator
) として扱うことにしました。これによって、>:@i.
のような式が読みやすくなります。可読性のための工夫点はもう一つあります。explicit definition の定義部分が文字列リテラルの場合 (例:
dyad : 'x + y'
)、リテラルの内部を式としてハイライトするようにしました。おわりに
実は Ruby をまともに書いたのは、これが初めてなのですが、案外簡単に書けたように思います 3。視覚的にデバッグできるのが楽でした。
Rouge にプルリクエストを送ったところ、無事マージされました 。先日リリースされた v3.24.0 に含まれています (デモ)。
あとは Qiita が対応してくれるのを待つばかりです。
余談
Rouge v3.24.0 リリースのコメントより抜粋:
This release has two new lexers: one for e-mails (中略) and one for J (why not another language starting with J?).
やっぱり J ってネタ言語扱いなんでしょうか……
- 投稿日:2020-10-16T21:24:39+09:00
【NuxtJS×RailsAPI】折角0からプログラミングを勉強したので、推しキャラの誕生日アプリを作ってみた。
自己紹介
Webエンジニアをしたり、マジシャンをしたり、オンライン家庭教師をしたり、イベントを企画したり、色々なことをしておりますyukiと申します。
今年の1月からプログラミングを学習し、DMMWEBCAMP入学を経てエンジニアとして勤務、約4ヶ月ほどが経過しました。過去学習してきたことなどは、こちらの記事にまとめてありますので、良ければご覧ください。
【卒業生】DMMWEBCAMPに通おうか迷っている人に伝えたい事成果物
NuxtJS/RailsAPI/AWS/Firebase
【Webアプリ】https://tokidosaya.com
【GitHub】https://github.com/yuki-snow1823/project-saya
コードはお世話になっているエンジニアの方にレビューをしていただき、現在リファクタリング中です。ご容赦くださいませ。この記事を読んで得られるもの、わかること
【技術面】
- NuxtJS×RailsAPIのWebアプリ作成の時につまづいた部分と対応策の例
- TwitterAPI使用の申請をした話
- その他学びになったtips
【余談】
- プログラミング未経験から、推しの誕生日をお祝いするに至るまでの流れ
- 「発想とプログラミング」に関する自分のポエム
もし、読み進めてくださる方がいらしたら、よろしくお願いします。
その前に推しキャラと協力してくださった神の紹介
今回、自分の突拍子もないプロジェクトに賛同してくださった二人をご紹介します。
イラストレーターのKrarisとWebデザイナーのももりんごさんです。お二人は素敵な絵を描いてくださったり、パーツのデザインを考えてくださったり、サイズを調整する案を出してくださったり、色彩のアドバイスをくださったり…とにかく全面的に協力してくださいました。
関係的には、Krarisは私の元バイト先の後輩で、ももりんごさんはDWCの先輩です。
改めて、お二人に感謝申し上げます。推し:リトルバスターズEXより 朱鷺戸沙耶
この金髪の子です。お誕生日おめでとう。(10/21)詳しいシナリオはここには書きませんが、ちょっと個人的に思うことには報われなさすぎるキャラクターなので、なんとか幸せになって欲しいなと思い、このアプリを作るに至りました。ちなみに、男の子は理樹君という主人公です。
是非、リトルバスターズとリトルバスターズEX、アニメやゲームで体験してみてください。技術の話 - NuxtJS×RailsAPIのアプリ作成の時につまづいた部分と対応策の例
こちらでは作成の際にぶつかったエラーと対策に関して、TwitterAPI以外のものをまとめます。
[Rails]
gem 'active_model_serializers'
がインストールできないjsonをrailsAPI側から返すにあたり、gem serializersを使用しようと思いました。しかし、一向にbundle installが通らず、以下のエラーが出ていました。
Could not find case_transform-0.2 in any of the sources Run `bundle install` to install missing gems.こちらの件に関しては、
spring stop
を実行したのちに、bundle installを再度実行で解決しました。bundle install周りって結構エラーが起きがちですが、これまでの経験上パスの指定が間違っていることが多い印象です。which bundleして一旦削除するとかも手だと思います。
[Rails]ミス:クラスメソッド名とカラム名を同じにしてしまった
これはもう自分がアホとしか言いようがないのですが、Counterテーブルというテーブルを作成したのち、カラム名の指定を
count
というものにしました。よくよく考えたらrubyには元々そんなメソッドがありました…。counter_controller.rbcounter = Counter.find(1) counter.count = counter.count + 1こんなみるも無残なコードを書いてしまいましたが、無事に動きました。リファクタリングやより良い実装ができるように精進します。この件は対策とかはなく、命名を注意してくださいということをお伝えできればと思います。
[Nuxt]Vuetifyでrowやcolの指定が効かないと思ったら…
Vuetifyというライブラリでデザインをしていたのですが、どうしてもグリッドシステムが思うように適用されないことがありました。しばし悩んだのち調べてみたところ、こんな記事を見つけました。
https://dev83.com/vue-vuetify-basic/
(引用させていただいております。)
v-appはVeutifyを使うために必須の要素です。Vuetifyのコンポーネントは必ずv-app要素の内側に書く必要があります。v-appで囲まないでVuetifyのコンポーネントを使うと予期しない動作や表示になってしまいます。下記のようにVuetifyのコンポーネントをv-appで囲みます。
( ω) ^ ^
案の定<v-app>
で囲んでいないだけでした。しっかりとガイド読んでから使えよと戒めになりました。TwitterAPI使用の申請、実装をした話
これが今回のプロジェクト1の衝撃だったのですが、「ハッシュタグのツイート収集」に関して私は公式から提供されているウィジェットを使えばいいやと思っていました。
ところが、調べてみると2018年にそのサポートは切れており、TwitterのDeveloperとして登録をして、自分でその機能を作らなければいけないことがわかりました。ただ、どうしても実装したい機能だったので、1から調べて申請することにしました。
【参考にさせていただいた記事】
https://qiita.com/kngsym2018/items/2524d21455aac111cdeeありがとうございました。Twitter側のUIは変わっていましたが、ほとんどこの通りに行いました。英語に関しては、急いでいたこともあったので、全部日本語で入力したのちにGoogle翻訳(最近の精度にびっくりしました。)、流石に変な部分を修正して申請しました。
3日くらいで通ったのですが趣味で使用すること(金銭が絡まない)と危険な行為を一切しないことを強調した文章にしたところ、自分の場合はスムーズに通ったような気がします。
技術の話は次で終わりですが、gem twitterを使ってハッシュタグツイートを収集する方法に関しては、また別途記事でまとめたいと思います!チュートリアルでも作りたいですね!
その他学びになったtips
ツイートする時にカード?みたいなのを出したい。
リンクをツイートする時とかにふわっと出てくるアレ、名前も知らなかったのですが実装に成功しました。
名前は、Twitter Cardというらしいです。NuxtのSPAモードでの実装はどうやるんだろう…と悩んでいたところ、こちらの素晴らしい記事に救っていただきました。Nuxt(SPAモード) + FirebaseでTwitterシェア用にOGP画像の設定をしたい。
そして、こちらのサイトを使うとリアルタイムでチェックができます。
イラストの登場にふわっとアニメーションをつけたい
イラストがふわっと出ればいいなーと思っていたところ、友人がAnimate.cssなるものを教えてくれました。今回はガッツリコードを書いてアニメーションをつけるつもりもなかったので、CDNで導入してクラス名をつけるだけで、ふわっとしたアニメーションがつきました。感謝!
余談
ここからは、未経験からエンジニアになった自分が完全に自分が作りたいものを作るに至った考えやポエムをまとめたいと思います。自己満足なので、イラっとしない寛大な方だけお進みください。
プログラミング未経験から、推しの誕生日をお祝いするに至るまでの流れ(withWEBデザイナーさん、イラストレーターさん)
キャラクターの誕生日アプリを作りたい!と思ってから以下の順序で完成に至りました。
①キャラクターの誕生日とは、どんなことをするものなのか調べる
こちらの結論は、イラストを投稿したり、お祝いメッセージを書いたりすることでした。ですから、ハッシュタグをつけたツイートの収集+投稿機能は必須で入れたいと思いました。
②イラストが必要なため、イラストレーターを探して依頼する
私は絵が描けませんし、可愛い沙耶と理樹君の絵が見たかったので、昔から仕事を依頼していたKrarisに依頼をしました。
③アイテムの配置を考えた時に、Krarisや自分よりも知見の人を探す必要があった
Krarisもアイテムの配置や色彩に知見はありましたが、本人とも話した結果、Webデザイナーとして活動している人からもアドバイスをもらおうという結論に至り、DWCの先輩であるももりんごさんに参加いただきました。
④ハッシュタグツイートの収集にTwitter Developerの申請が必要と知る
不慣れな英語で申請をし、英語のドキュメントを読んでgem twitterを駆使してなんとか実装しました
ここでも、一度エンジニアの先輩にお世話になりました。⑤せっかくならフロントとバックを分けたいのでNuxtJSを引っ張り出す
これはもう何度も出していますが、こちらのチュートリアルのおかげです。
https://qiita.com/saongtx7/items/d97ef5aec393e704fd3f全て、何か必要がある→行動する、勉強するというプロセスでした。今回の実装にあたり、何個も何個も勉強になった部分があるので、これからもアウトプットとして個人開発は行っていきたいなと思います。
「発想とプログラミング」に関する自分のポエム
自分の強みは発想力だと思っています。これまでも、あまり人が思いつかないことや、思いついてもめんどくさくてやらないことを、色々やってきた自負が少しだけあります。ただ、形にしたくても、どうしてもできなくて挫折した経験もあります。
今、プログラミングを学び始めて半年くらいが経ち、改めて「発想を形にする力」を貸してもらっていると感じています。それと同時に、プログラミングだけじゃ駄目だという思いが強くなっています。
デザイナーさんがいるからアプリの見た目が良くなるし、イラストレーターさんがいるから絵を通じて想いを伝えられるし、営業さんがいるから作ったものが売れるし、マネージャーさんやディレクターさんがいるから作ることに専念できるし…いろんな人がいろんな発想を使って頑張っているから、素敵なアプリやプロダクトができていると今回改めて勉強になりました。
本当に、周りの方には感謝するばかりです。ありがとうございます。
さいごに
リトルバスターズというゲームをやって、この朱鷺戸沙耶と主人公の理樹君ががたどった結末を見た時に、私は大きなショックを受けました。当然現実には存在しませんが「この二人に幸せになって欲しい」「流石にキャラクターの誕生日に向けたアプリを作る人は少ないのではないか→やってやろう!」という思いで完成に至りました。
作りたいものを形にできるって、ありがたいことだなと思います。
これからも好きなものを作っていこうと思います!
もし、プログラミングはできないけど…推しの誕生日はお祝いしたい!みたいな人がいらしたら、是非連絡ください。
作りたいものを形にしたい方も、応援したいなと思います。そして、もしも、、、万が一、、、朱鷺戸沙耶と理樹君が好きでここまで読み切った猛者がいたとしたら…いつか沙耶アフターを作りましょう!!ご連絡待ってます!
- 投稿日:2020-10-16T20:30:36+09:00
RSpec導入後、モデルの単体テストコードを書き始めるまで
はじめに
RSpecの導入までの流れは、こちらからどうぞ
流れ
- FactoryBotの準備
- テストコードを記述するファイルの生成
- 記述の型
1. FactoryBotの準備
ディレクトリとファイルを用意する。
ex)userモデルのFactoryBotなら、spec/factories/users.rbspec/factories/users.rbFactoryBot.define do factory :user do email {Faker::Internet.free_email} #例 #以下、同じように必要なFakerを記述 end end:userの部分が、spec/models/user_spec.rbでFakerを呼び出すときに使う。
Fakerの細かい使い方は、FakerのGitHubへ
2. テストコードを記述するファイルの生成
ターミナルで、
rails g rspec:model モデル名このコマンドにより、
spec/models/モデル名_spec.rb
のファイルが生成される。ファイルの中に初めから、
spec/models/user_spec.rbRSpec.describe User, type: :model do pending "add some examples to (or delete) #{__FILE__}" #この行は削除 endこのようなコードが入っている。2行目のコードは削除して構わない。
(上の例は、モデル名にuserを指定したもの)3. 記述の型
spec/models/user_spec.rbRSpec.describe User, type: :model do describe '何をテストするのか(例)ユーザー新規登録' do before do @user = FactoryBot.build(:user) #userモデルを例にすると、userのFactoryBotを呼び出し end it "具体的なテスト項目(例)メールアドレスが必須であること" do end end end(同じく上の例は、モデル名にuserを指定したもの。)
.build(:user)←:userがFactoryBotから呼び出されている。
it
とdo
の間にexampleを入れる。
do~end
の間に、コードを記述する。
- 投稿日:2020-10-16T18:37:54+09:00
特定のテーブルのデータをすべて消去する方法
- 投稿日:2020-10-16T18:00:08+09:00
【個人開発】全ての食べ物を0kcalにするカロリー管理アプリ「Zerorie」をリリースしました
はじめに
こんにちは。
個人開発の醍醐味とは「技術の無駄遣い」だと私は思います。
そんな私も個人開発で
クソアプリを作成しましたので、以下に色々記録しておこうと思います。サービス概要
今回私は、「ゼロカロリー理論」を使って、全ての食べ物を0kcalにしてくれるカロリー管理アプリ「Zerorie」を作りました。
「ゼロカロリー理論」とは、サンドウィッチマンの伊達さんが考案したネタで、「寿司は握ることでカロリーが潰されるから0kcal」みたいな、食べ物のカロリーを0kcalにしてくれるトンデモ理論のことです。
つまりこのアプリを使えば、食事した食べ物をすべてゼロカロリーにしてくれるため、「カロリーを気にして食事を我慢する」「高カロリーの食べ物を食べたことで罪悪感を覚える」といったカロリーに関わる悩みを一切気にすること無く、食事を楽しむことができるという訳です。
アプリのURL
Githubのリポジトリ
https://github.com/ryota1116/zero_calorie
コンテストに出場!!
複数のプログラミングスクール生がポートフォリオを発表する合同コンテスト"editch"に、このアプリをもって、出場しました!
下記にイベントの様子をレポートしましたので、良かったらご覧になってください。プログラミングスクール合同コンテスト「editch」に出場しました - note
アプリの使い方
トップページです。
まるでクソアプリとは思えないような綺麗なデザインに仕上がっていますね。
このアプリは食事した食べ物を検索するところから始まります。
食事した食べ物を、食べ物の「名前」や「画像」で検索することができます。
検索結果として「食べ物のデータ」「食べ物が0kcalである理由」が表示されます。
検索結果から食べ物を選択すると、「食事記録登録ページ」が表示されます。
画像検索を行った場合は、検索に使用した画像がプレビューで表示され、画像も一緒に登録できます。
食べ物の検索時に、該当する検索結果を得られなかった場合は、「ちょっと何言ってるか分かりません」と怒られます。
その場合は、「検索条件を変更する」か、「新しく食べ物の登録」をしてみましょう。
食べ物の登録ページでは、画像検索で使用した画像も表示されます。
食べ物の登録ページで、カロリーを0以外で入力するとバリデーションエラーが発生し、「この食べ物のカロリーは0kcalです。0と入力してください。」と怒られます。ちゃんと0と入力しましょう。
ざっとメイン機能はこんなところです。
個人的に好きなカロリー理論トップ3
僕の好きなカロリー理論トップ3を勝手ながら発表します。
他にもいっぱいカロリー理論があるので、正直トップ3を決めるのは心苦しい決断でした。1位: 「なんくるないさ」=「カロリーゼロさ」(沖縄料理のカロリー理論)
沖縄に行くと街中で「なんくるないさ」の文字をよく見かけますが、そういう意味だったんですね。ますます沖縄のことが好きになりました。
2位: 野菜がカロリーを無効化してくれる。常連が野菜増し増しを注文する訳はコレである。(二郎のカロリー理論)
二郎も0kcalだったとは思わぬ発見ですね。これで安心して二郎ダイエットの日々を送れそうです。
3位: 和菓子はわびさびを体現した食べ物。わびさびの心にカロリーの話を持ち出すのは言語道断である。(和菓子のカロリー理論)
シンプルに意味がわからない。千利休が泣いていないか心配です。
なぜ作った
お笑いと食が好きだったからです。
「食」とか「笑える」に関わる面白いアプリを開発できないかと色々考えてたら、ふと思い付きました。Zerorieを使った先に見える未来
何を食べてもゼロカロリーなので、日々のカロリー管理が楽ちん。
いくら食べても摂取カロリーの合計はゼロだから計算が楽チンですね。
好きなモノを好きに食べる自分を、正当化できる。
カロリーが無くなれば自分を責める理由なんて無くなりますよね?
笑える。ネタにできる。
「あはは!深夜にラーメン食べたけど0kcalだ!」って笑って誤魔化せます。
また、周りに「食べ過ぎだよ」と小言を言われても、Zerorieに責任転嫁すればいいし、「〇〇だから0kcal」って言えば場の空気が和むはずです。使用技術
- Semantic UI
- Rails
- AWS
- Circle CIとCodeCov
- Google Cloud Vision API(画像検索機能に使用)
などER図
満たしたい要件やアプリの拡張性を考慮した結果、以下のようになりました。
テーブル設計は面白いけど、苦労しますね。
インフラ構成図
開発期間
デプロイまで2ヶ月かかりました。所要時間は約300〜350時間。
苦労した点
Active StorageとGoogle Cloud Vision APIによる画像検索能
メイン機能の1つ「画像検索機能」の実装は苦労しました。
Active Storageを使ってVision APIを叩く、画像検索に使用したデータをsessionで保持する、そもそも食べ物の画像検索機能をどうやって自前で用意するのか等、色々苦労した反面、非常に勉強になりました。Rails6系をAWSでデプロイ(Capistrano使用)
これも苦しかったです。
Rails6系はwebpackerが標準搭載となるため、yarnのインストールが必要でしたがその辺を忘れていたり、あとはnginxの設定ファイルの記述で苦労したり。
そして何より、エラー文を読みにいっても何もヒントが書かれていなかった時が最も絶望しました。インフラムズカシイ。今後Zerorieに実装したい機能
食べ物のカロリー理論を返してくれるLineBot
コレは開発当初から考えていた機能の1つです。
Lineで簡単に食べ物を検索できれば、もっと手軽にゼロカロリーの世界を楽しめると思うので、是非作り上げたいです。自分の好きなカロリー理論に草スタンプを押せる
「Zerorieを見てて草 (いいね)が押したくなった」という意見があったので、是非実装したいと思いました。
また、ユーザーも食べ物のデータを登録する時にカロリー理論を作るケースがあるので、おもしろカロリー理論のランキングみたいなのがあると良いなと思いました。おわりに
やっぱりプログラミングの楽しさは、自分の考えやアイデアを形にできることだと、改めて実感しました。
そして特に個人開発はその人の色・考え・個性が出てこそ、良さが現れるんじゃないかと思います。他にも幾つかアイデアは考えているのでまた開発してみたいですが、次は笑えるよりもビジネス寄りなものを作りたいです。
以上、ご覧頂きありがとうございました!
- 投稿日:2020-10-16T17:54:45+09:00
railsを使って画像投稿を実装するには
Railsで画像投稿を実装する方法
はじめに
自分でrailsを使ってアプリを作ろうとした時、どうやって登録するのか、
画像の実装の仕方をメモを兼ねて記載しました。まずは、ImageMagickをHomebrewからインストールします。
#ターミナル % brew install imagemagick次に、gemfile上で、記述します。バージョンが異なる場合があります。
gem 'mini_magick' gem 'image_processing', '~> 1.2'ターミナル上で、バンドルインストール。
% bundle installターミナル上で、下記を実行。
% rails active_storage:install終わりに
いつも参考にさせてもらっているQiitaで自分も誰かの役に立てればと思いました。
実際書いてみると、少し書いただけでなんだか大変ですね。なれでしょうか、少しづつアップできたらと思います。
- 投稿日:2020-10-16T16:12:22+09:00
Rails/ES6/OpenWeatherMapで天気予報を表示してみた
開発環境
Ruby: 2.6.5
Rails: 6.0.0事前準備
OpenWeatherのHPにアクセスしアカウントを作成してください。
私は下記記事を参考にアカウントを作成しました。
【Rails】OpenWeatherMapを用いて、登録住所の天気予報を、日本語で表示する方法実装
app/controllers/weathers_controller.rbclass WeathersController < ApplicationController def index; end endapp/views/weathers/index.html.erb<h1>明日の天気検索サイト</h1> <%= select_tag 'select', options_for_select({ "札幌": 2128295, "青森": 2130658, "盛岡": 2111834, "仙台": 2111149, "秋田": 2113126, "山形": 2110556, "福島": 2112923, "水戸": 2111901, "宇都宮": 1849053, "前橋": 1857843, "埼玉": 6940394, "千葉": 2113015, "東京": 1850147, "横浜": 1848354, "新潟": 1855431, "富山": 1849876, "金沢": 1860243, "福井": 1863983, "山梨": 1848649, "長野": 1856215, "岐阜": 1863640, "静岡": 1851715, "名古屋": 1856057, "津": 1849796, "大津": 1853574, "京都": 1857910, "大阪": 1853909, "神戸": 1859171, "奈良": 1855612, "和歌山": 1926004, "鳥取": 1849890, "松江": 1857550, "岡山": 1854383, "広島": 1862415, "山口": 1848689, "徳島": 1850158, "高松": 1851100, "松山": 1926099, "高知": 1859146, "福岡": 1863967, "佐賀": 1853303, "長崎": 1856177, "熊本": 1858421, "大分": 1854487, "宮崎": 1856717, "鹿児島": 1860827, "那覇": 1856035, }, 1), id: "wheather-select" %> <div id="city-name"></div> <div id="weather"></div> <%= javascript_pack_tag 'weather' %>app/javascript/packs/weather.js// OpenWeatherAPIを使用しています。下記URLからアカウントを作成し、 // APIキー情報を利用してAPI_KEY変数に上書きしてください。 // https://openweathermap.org/ // API_KEYはアカウント登録した際のAPIキーを使用して下さい const API_KEY = "***" window.onload = function() { weather_search() }; const wheather_select = document.querySelector('#wheather-select'); const options = document.querySelectorAll("#wheather-select option"); const weather_search = function () { const index = this.selectedIndex; const city_id = index ? options[index].value : wheather_select.value const url = 'http://api.openweathermap.org/data/2.5/forecast?id=' + city_id + '&appid=' + API_KEY; fetch(url).then((data) => { return data.json(); }).then((json) => { let insertHTML = ""; let tomorrow = 8 let weather = document.querySelector('#weather') insertHTML += buildHTML(json, tomorrow); weather.innerHTML = insertHTML }).catch(error => { console.error(error); }); } wheather_select.addEventListener('change', weather_search); function buildHTML(data, i) { let Week = new Array("(日)","(月)","(火)","(水)","(木)","(金)","(土)"); let date = new Date(data.list[i].dt_txt); date.setHours(date.getHours() + 3); let month = date.getMonth()+1; let day = month + "月" + date.getDate() + "日" + Week[date.getDay()] + date.getHours() + ":00"; let icon = data.list[i].weather[0].icon; let main = weatherJavaneseConversion(data.list[i].weather[0].main) let html = '<div class="weather-report">' + '<h2>明日の天気は'+ main +'です!</h2>' + '<img src="http://openweathermap.org/img/w/' + icon + '.png">' + '<div class="weather-date">' + day + '</div>' + '</div>' ; return html } function weatherJavaneseConversion(name) { switch (name) { case "Clear": return "晴れ" case 'Clouds': return "曇り" case "Rain": return "雨" case "Snow": return "雪" default: console.log(name) return name } }参考
RailsでOpenWeatherMapから天気予報を取得する
【Rails/JS】無料API「OpenWeatherMap」で天気予報を表示する
【Rails】OpenWeatherMapを用いて、登録住所の天気予報を、日本語で表示する方法
- 投稿日:2020-10-16T15:55:39+09:00
【while、each】【超初心者向け】繰り返し処理を使って配列に押し込む方法
はじめに
こんにちは!
最近はrubyの問題にハマっていて、気付いたら夜中、みたいない毎日です…。
そん中、意外と便利なのに、記事としてあまり見かけない方法で
while文なんかを使って一気に配列から配列に押し込む方法をご紹介いたします。each文の活用
# こちらでは数字と文字列がごちゃごちゃになった配列を数字の配列(int)と文字の配列(str)に分けてます x_arry = [1,"a",2,"b",3,"c"] int = [] str = [] i = 0 x_arry.each do |n| i += 1 if n.to_s =~ /^[0-9]+$/ #こちらは正規表現で、数字のみを条件分岐してます。 int << n else str << n end end print int # => [1, 2, 3] print str # =>["a", "b", "c"]※正規表現については詳しい記事が山ほどありますので、そちらをご参照くださいませm(_ _)m
参考記事①
https://programming-dojo.com/%E3%80%90ruby%E5%85%A5%E9%96%80%E3%80%91%E6%95%B0%E5%AD%97%E3%81%8B%E3%81%A9%E3%81%86%E3%81%8B%E3%82%92%E5%88%A4%E5%AE%9A%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/
参考記事②
https://qiita.com/pecooh/items/ee392125727f04bafaed次はwhile文みてみましょう。
while文の活用
# 配列にそれぞれaの配列とbの配列にある数字を足し算して配列に打ち込んでます。 a = [43,56,77,22,45,66,78,99,53,44] b = [1,2,3,4,5,6,7,8,9,10] answer_arry = [] i = 0 while i < a.length do ans = a[i] + b[i] answer_arry << ans i += 1 end print answer_arry # => [44, 58, 80, 26, 50, 72, 85, 107, 62, 54]この様にして、繰り返し処理をしてもらって自動で格納してもらえるのは便利ですね❗️
しかも、格納される直前で条件分岐すると、一定の条件のものが格納することもできますので是非やってみてください?クイズとか問題とかやっているとこういう処理が必要になってくることは多々ありますので重宝です?
ぜひ、遊んでみてください☺️
ちなみにこれを使ってこんなふうにするとちょっとしたおみくじみたいなゲームとか作れそうですね☺️おまけ
a = [rand(100),rand(100),rand(100),rand(100),rand(100),rand(100),rand(100),rand(100),rand(100),rand(100)] b = [1,2,3,4,5,6,7,8,9,10] answer_arry = [] i = 0 while i < a.length do ans = a[i] + b[i] answer_arry << ans i += 1 end print answer_arryrandを使うことで数字がランダムになって出てくるので「ゾロ目の数が多い方が勝ちな!」みたいなゲームだったり、
randメソッドについてはPCとジャンケン対戦ができる様なゲームに応用ができたり楽しいです?それではまた❗️
ありがとうございましたm(_ _)m✨
- 投稿日:2020-10-16T15:39:10+09:00
【Ruby on Rails】初回ログイン時・jQueryを使用した、画面を真っ二つに割る方法
目標
開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina前提
【Ruby on Rails】初回アクセス時に一度だけ表示(jquery.cookie.js使用)
こちらに少し手を加えた形となりますので、
コードをそのまま使うとcookieがなくなるまでは表示出来ません。実際のコード
app/views/layouts/application.html.erb<div class="indication-left"></div> # 追加 <div class="indication-right"></div> # 追加 <div class="box"> <p>下記の表示終了ボタンを押すと、<br>更新しても見ることは出来ません。<br> 新しいブラウザを立ち上げると表示されます。 </p> <button>表示終了</button> </div>app/assets/stylesheets/application.css/* 追加 */ /* ここから */ .indication-left, .indication-right{ position: fixed; top: 0; width: 100%; height: 100vh; background-image: url("image1.jpg"); background-size: cover; background-position: center; z-index: 1040; transition: 3s; } .indication-left{ left: 0; clip: rect(0px 50vw 100vh 0px); } .indication-right{ right: 0; clip: rect(0px 100vw 100vh 50vw); } .leftslide{ transform: translateX(-100%); } .rightslide{ transform: translateX(100%); } /* ここまで */ .box{ position: absolute; top: 40%; left: 35%; width: 400px; height: 200px; background-color: #ffffff; z-index: 1050; /* 変更 */ } .box p{ padding: 15px; } .box button{ display: block; margin: 0 auto; }app/assets/javascripts/application.js$(function(){ $(".indication").show(); if($.cookie('Flg') == 'on'){ $(".box").hide(); // indicationの子ではなくなったので追加 $(".indication-right").hide(); // 追加 $(".indication-left").hide(); // 追加 }else{ $(".box").show(); // 追加 $(".indication-right").show(); // 追加 $(".indication-left").show(); // 追加 } $(".box button").click(function(){ $(".indication-right").addClass("rightslide"); // 追加 $(".indication-left").addClass("leftslide"); // 追加 $(".box").fadeOut(); // 追加 $.cookie('Flg', 'on', { expires: 1, path: '/' }); }); });z-indexについて
bootstrapを使用する場合、今回のような機能の実装では
.fixed-topよりも高い位置に表示する必要があったため、
1030より高い数値を指定しております。bootstrap.css.fixed-top { position: fixed; top: 0; right: 0; left: 0; z-index: 1030; }clip: rect( ); について
clip要素とは画像などの要素について、切り抜き領域の外にある内容を表示せず、
切り抜き領域を指定するプロパティです。
指定方法はrectととなり、rectの長さには
rect(上端からの距離, 右端からの距離, 下端からの距離, 左端からの距離)のように、
上から時計回りの順でコンマで区切って4つの値を指定します。右側の画像
clip: rect(0px 50vw 100vh 0px);
左側の画像clip: rect(0px 100vw 100vh 50vw);
まとめ
jqueryを使用せず、cssのinputを使用した方法もあるため、
両方の知識を入れておくのは大事だと思います。
近々更新できればと思います。またtwitterではQiitaにはアップしていない技術や考え方もアップしていますので、
よければフォローして頂けると嬉しいです。
詳しくはこちら https://twitter.com/japwork
- 投稿日:2020-10-16T15:03:57+09:00
【Rails】人気投稿をランキング形式で表示させる【簡単】
個人開発アプリを作成していて、人気投稿順に見られると使いやすくなると思い、
基本的にこちらの記事を参考にしながら実装しました。routes.rb
routes.rbget 'rank' => 'shops#rank'私の場合はshopsコントローラーのrankアクションに設定しました。
shops_controller.rb
shops_controller.rbdef rank @all_ranks = Shop.find(Favorite.group(:id).order('count(shop_id) desc').limit(5).pluck(:shop_id)) endshopsコントローラーにrankメソッドを定義。いいねが多い投稿順に表示させます。
rank.html.slim
rank.html.slim- @all_ranks.each.with_index(1) do |shop, i| h5.col-md-5 .card.shadow | No. = i | = link_to shop.name, shop_path(shop) = link_to((image_tag shop.picture.url), shop_path(shop.id), class: 'shop-picture') if shop.picture.url.present?viewファイルを作成し、表示させます。例として抜粋しました。
後はCSSで外観を修正します。@all_ranks.each.with_index(1) do |shop, i|上記より、いいねが多い順に表示させることができます。
完成
お手軽にランク形式での表示が完成しました。
- 投稿日:2020-10-16T14:51:04+09:00
【Rails6】cocoonを使った動的フォーム入力画面の作り方
はじめに
本記事では、以下のような入力フォームを作成することをゴールとしています。
概要
レシピとレシピに必要となる食材をまとめてDBに保存する機能の作成
テーブル構成
レシピとレシピの食材は親子関係であるため、以下のテーブル構成となります
親:レシピ( recipes )
子:レシピの食材( recipe_ingredients )
※ingredient_idはactivie_hashで実装します
実装
以下の順に実施していきます。
1.jqueryの導入
2.cocoonの導入
3.モデルの作成
4.コントローラーの作成
5.ビューの作成1.jqueryの導入
rails6でcocoonを使えるようにするために、jqueryをインストールします。
$ yarn add jqueryconfig/webpack/environment.jsを編集します
config/webpack/environment.jsconst { environment } = require('@rails/webpacker') #追記ここから const webpack = require('webpack') environment.plugins.prepend('Provide', new webpack.ProvidePlugin({ $: 'jquery/src/jquery', jQuery: 'jquery/src/jquery' }) ) #追記ここまで module.exports = environment2.cocoonの導入
gemの導入
Gemfilegem 'cocoon'$ bundle installライブラリの追加
$ yarn add github:nathanvda/cocoon#c24ba53実行後、以下2点の項目をクリアできていればOKです。
・app/assets/javascripts/cocoon.jsが作成されている
・package.jsonに以下の記述が追加されているpackage.json"cocoon": "github:nathanvda/cocoon#c24ba53"最後に、app/javascriptspacks/application.jsに以下の内容を追記
app/javascriptspacks/application.jsrequire('jquery') import "cocoon";3.モデルの作成
今回の実装内容と関係のない記述は省いております。
モデルの作成
$ rails g model Recipe $ rails g model RecipeIngredientマイグレーションファイルの編集
class CreateRecipes < ActiveRecord::Migration[6.0] def change create_table :recipes do |t| t.string :name, null: false t.timestamps end end endclass CreateRecipeIngredients < ActiveRecord::Migration[6.0] def change create_table :recipe_ingredients do |t| t.references :recipe, null: false, foreign_key: true t.integer :ingredient_id, null: false t.integer :quantity, null: false t.timestamps end end endマイグレーションの実行
$ rails db:migrateアソシエーションの設定
recipe.rbclass Recipe < ApplicationRecord has_many :recipe_ingredients, dependent: :destroy accepts_nested_attributes_for :recipe_ingredients endrecipe_ingredient.rbclass RecipeIngredient < ApplicationRecord belongs_to :recipe endaccepts_nested_attributes_for
指定したモデルのデータを配列としてパラメーターに含めることが出来ます。
つまり、recipeとrecipe_ingredients両モデルのデータをまとめて保存できるようになります。4.コントローラーの作成
コントローラーの作成
$ rails g controller recipes new createコントローラーの内容を編集
recipes_controller.rbclass RecipesController < ApplicationController def new @recipe = Recipe.new @recipe_ingredients = @recipe.recipe_ingredients.build end def create @recipe = Recipe.new(recipe_params) if @recipe.save redirect_to root_path else render action: :new end end private def recipe_params params.require(:recipe).permit(:name, recipe_ingredients_attributes: [:id, :ingredient_id, :quantity, :_destroy]) end endaccepts_nested_attributes_forで指定したrecipe_ingredientモデルを、
paramsにrecipe_ingredients_attributes: []として、追加して送っています。5.ビューの作成
モデルと同様に、今回の実装内容と関係のない記述は省いております。
※クラス名等も記述していないため、このコードのままではレイアウトは崩れます。recipes/new.html.erb<%= form_with model: @recipe, url: '/recipes', method: :post, local: true do |f| %> <!-- レシピ名 --> <%= f.text_area :name %> <!-- 食材入力フィールド --> <%= f.fields_for :recipe_ingredients do |t| %> <%= render "recipes/recipe_ingredient_fields", f: t %> <% end %> <!-- 食材追加ボタン --> <%= link_to_add_association "追加", f, :recipe_ingredients %> <% end %>fields_for
form_with内で異なるモデルを編集できるようになります。
recipes/_recipe_ingredient_fields.html.erb<div class="nested-fields"> <%= f.collection_select(:ingredient_id, {}, :id, :name, {}) %> <%= f.number_field :quantity %> <div>個</div> <%= link_to_remove_association "削除", f %> </div>nested-fieldsクラスが指定されたdivタグで囲んだ範囲が追加・削除する領域です。
レンダリングする部分テンプレート名には注意してください。
「_子モデル_fields.html.erb」でないとエラーとなります。お疲れさまでした。
以上で、動的入力フォームが作成できるかと思います。参考
Rails6でのcocoonの導入
ネストしたフォームを簡潔に実装できるcocoon gemをwebpack環境でセットアップする動的入力フォームの作成について
【Rails】cocoonを用いて親子孫関係のテーブルに複数のデータを同時保存する方法fields_forについて
fields_forの上手な使い方
- 投稿日:2020-10-16T14:21:02+09:00
【Rubyバージョンエラー】Your Ruby version is ~,but your Gemfile specified ~の解決方法
はじめに
本記事では、以下のエラーについて、解決方法と、なぜそう考えたかを書いていきます。
解決するにあたっては、多くの方の記事やブログを参考にさせていただきました。
リンクは、該当箇所に随時貼らせていただいてます。Your Ruby version is 2.6.3, but your Gemfile specified 2.5.1対象読者
・Rubyを学習しはじめて、数ヶ月〜1年程度の方。
・上記のエラーに初めてあたった方。
・「環境変数の設定をどうにかすれば、解決するらしいことはわかった。」
「しかし、vimやシェルという言葉を聞くと、解決する前に一呼吸おきたくなる」という方。※本記事では、vimやシェルなどの用語について詳しい説明はいたしません。
目的
・vimを操作してrbenv下のファイルにPATH(以下、たんに「パス」といいます。)を通し、バージョンエラーを解決する。
・なぜ、パスを通すことで、問題が解決するのかを理解する。
「わかりそう」で「わからない」でも「わかった気」になれるIT用語「Path」
前提
・rbenv(rubyのバージョン管理用ツール)はインストール済み。
・変更したいrubyバージョン(今回でいうと、2.5.1)がインストールされていることは、確認済み。
目次
1 エラー内容
2 Rubyのバージョン切り替えを試す
3 2で解決しなかった場合、rubyの参照先を確認する
3−1 rubyコマンドを実行したときに起きていること
3−2 rubyコマンドの実行元ファイルを確認する方法4 rubyコマンドが、rbenv下にあるファイルから実行されるように設定する(パスを通す)
5 最後に
1 エラー内容
エラー内容はこちらです。
Your Ruby version is 2.6.3, but your Gemfile specified 2.5.1エラーの意味は、
「あなたのPC環境におけるRubyのバージョンは、2.6.3です。」
「しかし、あなたが現在開発しているアプリケーションのRubyのバージョンは、2.5.1です。」
というものです。2 Rubyのバージョン切り替えを試す。
以下の手順で、バージョンの切り替えを試してみます。
・Rubyのバージョンを確認する。
・rbenvを使用し、バージョンを切り替える。
・切り替わったか確認する。(1)自分のPC環境におけるRubyのバージョンを確認する。
ruby -v(2)開発中のアプリケーションに適用されているRubyのバージョンを確認する。
rbenv -v(3)rbenvを使用し、バージョンの変更を試みる。
特定のディレクトリで使うRubyのバージョンを指定するとき。
rbenv local 2.6.3システム全体で使うRubyのバージョンを指定するとき。
rbenv global 2.6.3(4)Rubyのバージョンが切り替わっているか、確認する。
ruby -v ruby 2.6.3p645 (〜)... //出力結果切り替りませんでした。
3 2で解決しなかった場合、参照先を確認する
上記の方法で切り替わらない場合、rubyコマンドを実行する際の参照先が違う可能性があります。
本記事では、エラーの原因や、なぜその方法で解決できるのかを考えていきます。
このため、以下の流れで説明します。3−1 rubyコマンドを実行したときに起きていること
3−2 rubyコマンドの実行元ファイルを確認する方法3−1 rubyコマンドを実行したときに起きていること
私たちが持っているPC内には、あらゆるコマンドが格納されたファイルがたくさん入っています。
例えば、私たちがターミナル上でコマンドを打っているとき。
PC内では、たくさんあるファイルの中から、ターミナルで打ったコマンドが格納されているファイルを見つけ出し、そこからコマンドを取り出して実行します。rubyコマンドも同じです。
しかし、rubyコマンドが入っているファイルは、一つではない場合があります。
どういうことかというと、今回のようにrbenvをインストールした場合は、
・インストールした際に作られた、rbenv下にあるファイル
・もともとPC内にあるファイル
の両方に、rubyコマンドが入っている場合がある、ということです。今回のケースでいうと、
・rbenv下にあるファイル → 2.5.1バージョンのrubyコマンドが入っている。
・もともとPC内にあるファイル → 2.6.3バージョンのrubyコマンドが入っている。
という状況が考えられる、ということです。3-2 rubyコマンドの実行元ファイルを確認する方法
では、実際に「rubyコマンドを打った場合、どこのファイルに入っているrubyコマンドを実行しているのか?」を確認してみましょう。
参照先を確認するには、whichコマンドを使用します。which ruby usr/bin/ruby //出力結果この出力結果は、
「rubyコマンドを実行することで、usr下のファイルにあるrubyコマンドを実行する」
という意味になります。rbenvによってrubyのバージョン管理を行っている場合、rubyコマンドはrbenv下にあるファイルにも入っています。
そのrubyコマンドが実行されていれば、以下のような出力結果になります。/Users/you/.rbenv/shims/rubyなぜこのような構成になるかについては、こちらの記事を参考にさせていただきました。
rbenvの役割記事の一部を引用させていただくと、
rbenvをインストールする際に、~/.rbenv/ (ルートフォルダ )
~/.rbenv/shims/ (rubyやgemがインストールしてくれるコマンドのラッパーを保存するフォルダ)
などなどがPCに入ります。
このshimsフォルダの中に、実行したいrubyコマンドが格納されます。4 rubyコマンドが、rbenv下にあるファイルから実行されるように設定する(パスを通す)
PCに、「usr/bin/ruby」ではなく、「/Users/you/.rbenv/shims/ruby」の方から、rubyコマンドを持ってくるよう、設定してあげます。
参考にさせていただいたのは、以下の記事です。
ここでは、以下の手順を書いていくこととします。
・vimを開く
・コードを書いて、保存、終了する。
・変更内容を反映させる。(1)下記コマンドで、vimを開きます。
(シェルでbashを使用されている方は、「.zshrc」を「.bash_profile」にかえてください。)vim ~/.zshrc(2) i を押して、編集可能な状態にします。画面上は何も変わりませんが、追記や編集が可能になっています。
(3)以下のコードを書きます。すでに記載がある場合は、その下の行に書きます。
export PATH="~/.rbenv/shims:/usr/local/bin:$PATH" eval "$(rbenv init -)"(4) esc を押して、保存、終了が可能な状態にします。カーソルが一番下あたりに移動すると思います。
(5) :wq と打ち、enter を押します。
(6)元の画面に戻りますので、以下のコマンドを打ちます。sourceコマンドは、変更を反映させるコマンドです。source ~/.zshrc(7)rubyのバージョンを確認します。
ruby -v ruby 2.5.1p645 (〜)... //出力結果うまくいきました!
5 最後に
記事の内容に間違い、誤字脱字などありましたら、コメントで教えていただけるとありがたいです。
参考、引用させていただいた記事、ブログの著者の方々、ありがとうございます。
- 投稿日:2020-10-16T12:38:45+09:00
Leet文字列への変換プログラム
【概要】
1.結論
2.Leet文字とは何か
3.どのようにコーディングするか
4.開発環境
1.結論
gsub!メソッドを使う
2.Leet文字とは何か
主に、インターネットで使用されるアルファベットの表記法のことをいいます。例えば、"HELLO"であれば"H3LL5"となります。アルファベットに似た数字や文字を当てはめる形になります。下記に具体例の表を載せておきますが、もちろんA~Zまであり、その表現方法はいくつもあります。
文字 数字 A 4 B 13 C [ D ) E 3 .
.
.
文字 数字 Z 2 参考にしたURL:
Wikipedia:Leet符号表
3.どのようにコーディングするか
変数 = gets.chomp 変数.gsub!(/変換前の文字列/,'変換後の文字列') puts 変数 #ex) #leet_str = gets.chomp #leet_str.gsub!(/Z/,'2') #puts leet_str #"Z"と入力すると"2"に変換される他の方法もありますが、自分はgsub!を使用しました。
参考にしたURL:
【Ruby入門】文字列の置換方法まとめ(gsub sub regex)
4.開発環境
Mac catalina 10.15.4
Vscode
Ruby 2.6.5
Rails 6.0.3.3
- 投稿日:2020-10-16T10:56:37+09:00
RubyでC拡張を使っているgemをインストールする際に、make -j4 みたいに複数のCPUのコアを使って早く終わらせたい
というわけで、正解は
export GNUMAKEFLAGS=-j4です。お手持ちのコンピュータのコア数に合わせて
GNUMAKEFLAGS=-j8 gem update等してみてください。C拡張をゴリゴリ使っているgemの場合アップデートが高速に終了するかも知れません。
この記事は以上です。
- 投稿日:2020-10-16T10:56:37+09:00
RubyでC拡張ありのgemをインストールする際に、make -j4 みたいに複数のCPUのコアを使って早く終わらせたい
というわけで、正解は
export GNUMAKEFLAGS=-j4です。お手持ちのコンピュータのコア数に合わせて
GNUMAKEFLAGS=-j8 gem update等してみてください。C拡張をゴリゴリ使っているgemの場合アップデートが高速に終了するかも知れません。
この記事は以上です。
- 投稿日:2020-10-16T10:56:24+09:00
トランザクションシミュレータ(ロック待ちバージョン)
トランザクションの検証が面倒すぎる。モデルとしては単純そうだから、作ってしまおう、と思った。
Ruby
tran_request.rbclass Request end class Read < Request attr_reader :var def initialize(var) @var = var end def to_s() return "Read(#{@var})" end end class Write < Request attr_reader :var,:val def initialize(var,val) @var = var @val = val end def to_s() return "Write(#{@var},#{@val})" end end class Insert < Request attr_reader :var, :val def initialize(var,val) @var = var @val = val end def to_s() return "Insert(#{@var},#{@val})" end end class Delete < Request attr_reader :var def initialize(var) @var = var end def to_s() return "Delete(#{@var})" end end class Lock < Request attr_reader :tr,:var,:lock_type def initialize(tr,var,lock_type) @tr = tr @var = var @lock_type = lock_type end def to_s() return "Lock(#{@tr},#{@var},#{@lock_type})" end end class Unlock < Request attr_reader :tr,:var def initialize(tr,var) @tr = tr @var = var end def to_s() return "Unlock(#{@tr},#{@var})" end end class Begin < Request def to_s() return "Begin" end end class Rollback < Request def to_s() return "Rollback" end end class Commit < Request def to_s() return "Commit" end end class Database attr_accessor :vars , :locks def initialize() @vars = {} @locks = [] end def read(tr,var) if @locks.filter{|l| l.tr != tr && l.var == var && l.lock_type == :Exclusive}.empty? then return :success , @vars[var] else return :failure , nil end end def write(tr,var,val) if @locks.filter{|l| l.tr != tr && l.var == var}.empty? then @vars[var] = val return :success else return :failure end end def insert(tr,var,val) write(tr,var,val) end def delete(tr,var) if @locks.filter{|l| l.tr != tr && l.var == var}.empty? then unlock(tr,var) @vars.delete(var) return :success else return :failure end end def lock(tr,var,lock_type) if lock_type == :Exclusive then if @locks.filter{|l| l.tr != tr && l.var == var}.empty? then @locks << Lock.new(tr,var,lock_type) return :success end return :failure else if @locks.filter{|l| l.tr != tr && l.var == var && l.lock_type == :Exclusive}.empty? then @locks << Lock.new(tr,var,lock_type) return :success end return :failure end end def unlock(tr,var) @locks.filter!{|l| !(l.tr == tr && l.var == var)} return :success end def unlock_all(tr) @locks.filter!{|l| l.tr != tr} return :success end def begin(tr) unlock_all(tr) return :success end def rollback(tr) unlock_all(tr) return :success end def commit(tr) unlock_all(tr) return :success end end class Transaction attr_reader :db,:name,:requests,:counter,:vars,:status def initialize(db,name,requests) @db = db @name = name @requests = requests @counter = 0 @vars = {} @status = :running # :running or :waiting end def next() req = @requests[@counter] if req.is_a?(Read) then status, value = @db.read(@name, req.var) if status == :success then @vars[req.var] = value @status = :running @counter += 1 puts "#{@name} Read(#{req.var}) => #{value}" else @status = :waiting puts "#{@name} Read(#{req.var}) => fail" end elsif req.is_a?(Write) then status = @db.write(@name, req.var, req.val) if status == :success then @status = :running @counter += 1 puts "#{@name} Write(#{req.var},#{req.val}) => success" else @status = :waiting puts "#{@name} Write(#{req.var},#{req.val}) => fail" end elsif req.is_a?(Insert) then status = @db.insert(@name, req.var, req.val) if status == :success then @status = :running @counter += 1 puts "#{@name} Insert(#{req.var},#{req.val}) => success" else @status = :waiting puts "#{@name} Write(#{req.var},#{req.val}) => fail" end elsif req.is_a?(Delete) then status = @db.delete(@name, req.var) if status == :success then @status = :running @counter += 1 puts "#{@name} Delete(#{req.var}) => success" else @status = :waiting puts "#{@name} Delete(#{req.var}) => fail" end elsif req.is_a?(Lock) then status = @db.lock(@name, req.var,req.lock_type) if status == :success then @status = :running @counter += 1 puts "#{@name} Lock(#{req.var},#{req.lock_type}) => success" else @status = :waiting puts "#{@name} Lock(#{req.var},#{req.lock_type}) => fail" end elsif req.is_a?(Unlock) then status = @db.unlock(@name, req.var) if status == :success then @status = :running @counter += 1 puts "#{@name} Unock(#{req.var}) => success" else @status = :waiting puts "#{@name} Unock(#{req.var}) => fail" end elsif req.is_a?(Begin) then status = @db.begin(@name) if status == :success then @status = :running @counter += 1 puts "#{@name} Begin => success" else @status = :waiting puts "#{@name} Begin => fail" end elsif req.is_a?(Rollback) then status = @db.rollback(@name) if status == :success then @status = :running @counter += 1 puts "#{@name} Rollback => success" else @status = :waiting puts "#{@name} Rollback => fail" end elsif req.is_a?(Commit) then status = @db.rollback(@name) if status == :success then @status = :running @counter += 1 puts "#{@name} Commit => success" else @status = :waiting puts "#{@name} Commit => fail" end end end def current_request_info() return @requests[counter].to_s() end end class Scheduler attr_reader :trs, :schedule, :counter def initialize(trs, schedule) @trs = trs @schedule = schedule @counter = 0 end def next() # check for dead lock. if @trs.all?{|tr_name, tr| tr.status == :waiting} then raise("Dead lock.") end # re-run for all locked transactions. transaction_status() @trs.each {|tr_name, tr| if tr.status == :waiting then print "re-try:" tr.next() end } # execute scheduled transaction. printf("%6d:", @counter) @trs[@schedule[@counter]].next() @counter += 1 end def transaction_status() @trs.each {|tr_name, tr| puts "status:#{tr_name}: #{tr.status} #{tr.current_request_info()}" } end end @tr_a_req = [ Begin.new(), Write.new("X",10), Read.new("X"), Write.new("X",20), Commit.new() ] @tr_b_req = [ Begin.new(), Lock.new("tr_a","X",:Shared), Commit.new() ] @schedule = [ "tr_a", "tr_a", "tr_b", "tr_b", "tr_a", "tr_a", "tr_b", ] @db = Database.new() @tr_a = Transaction.new(@db,"tr_a",@tr_a_req) @tr_b = Transaction.new(@db,"tr_b",@tr_b_req) @trs = { "tr_a" => @tr_a, "tr_b" => @tr_b } @master = Scheduler.new(@trs, @schedule) @schedule.size().times { puts @master.next() }実行例
status:tr_a: running Begin status:tr_b: running Begin 0:tr_a Begin => success status:tr_a: running Write(X,10) status:tr_b: running Begin 1:tr_a Write(X,10) => success status:tr_a: running Read(X) status:tr_b: running Begin 2:tr_b Begin => success status:tr_a: running Read(X) status:tr_b: running Lock(tr_a,X,Shared) 3:tr_b Lock(X,Shared) => success status:tr_a: running Read(X) status:tr_b: running Commit 4:tr_a Read(X) => 10 status:tr_a: running Write(X,20) status:tr_b: running Commit 5:tr_a Write(X,20) => fail status:tr_a: waiting Write(X,20) status:tr_b: running Commit re-try:tr_a Write(X,20) => fail 6:tr_b Commit => successステップ3でtr_bが共有ロックを取得したので、ステップ5でtr_aのWriteが失敗する。その後、tr_bがコミットをすると、tr_bが取得したロックが全て外れる。ので、このままステップ7を実行すれば、tr_aの待ちが解消されてWriteが成功する。
- 投稿日:2020-10-16T09:55:30+09:00
rails コマンドが使えなくなった時の対処法
- 投稿日:2020-10-16T02:16:53+09:00
deviseユーザー登録
1 deviseをインストール
ユーザー登録機能を実装するためには、deviseというgemをインストールします。
Gemfileで gem 'devise'を追加
ターミナルでbundle install
ここでrails s でサーバー起動しないと反映されない
rails g devise:install で設定ファイルを作成
2 ユーザーモデル+テーブルを作る
ここで注意したいのが普通のモデルを作る時と同じではないということです。
モデル作成
ターミナルで rails g devise user と入力(この時ルーティングが自動設定され、マイグレーションファイルも生成される)
db→migrate→2020〜のファイルにてテーブルに追加するカラムやカラムの型を記載する。
class DeviseCreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| t.string :name, null: false t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: ""テーブル作成
- ターミナルでrails db:migrate コマンドでテーブル作成
3 サインアップ(新規登録)
サインアップ時に入力する情報はパラメーターとしてサーバーに送信される。
通常コントローラーのストロングパラメーターで受け取るパラメーターを制限しているが、deviseの時は書き方が違う。devise_parameter_sanitizerメソッド
deviseのUserモデルに関わる「ログイン」「新規登録」などのリクエストからパラメーターを取得できる。
下の記述は、サインアップの時にニックネームというキーのパラメーターを許可する記述。実際ニックネームの後にも新規登録時に必要な項目が追記される。ここでのpermitの中の引数は普通のパラメーターの時と違うので注意!class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? private def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) end end初めのbefore_actionはdeviseによる処理であればconfigure_permitted_paramatersメソッドを実行するという設定。丸々必要なのだが、この辺は調べると出てくるので丸々覚える必要はない
これにより新規登録時の情報がテーブルに保存できている。
- 投稿日:2020-10-16T00:36:48+09:00
オブジェクト指向という考え方について1
Railsの勉強をやり直し始めたのでアウトプットとして残します。
オブジェクトとは、単純に翻訳すると「対象物」です。決して特殊なものではなく、世の中で皆さん自身も含めて皆さんがやり取りするもの全てがオブジェクトに当たります。オブジェクト指向とは、コンピューターの世界の全ての処理をオブジェクトを中心に取り扱う考え方です。
■オブジェクト(対象)とは、
●ある目的の振る舞いと、名前などの特徴となる固有の値とを持つ
●必要な時に呼び出される振る舞いを通し、固有の値を利用して目的とする処理を行うものだと言えます。少し硬い表現になりますが、この意味を理解することが重要です。
■オブジェクト思考プログラミングとは
何かのイベントを行う時、一般的に「式次第(プログラム)」を用意します。式を滞りながら運営するためには、この手順がしっかりできていることが重要です。なんらかの目的を持ったコンピュータプログラムを作ること(プログラミング)も、作業の手順(ロジックまたは手続き)をその目的に従って組み立てることになります。
従来、プログラミングは「全ての手順を一連のロジックで組み立てる」という方法で行われてきました。一方、オブジェクト指向は、●オブジェクトというものを設定する
●オブジェクトに必要なロジックをそれぞれのオブジェクト内に閉じ込めて、必要な時にオブジェクトに指示して呼び出す。という形で処理を組み立てていく方法です。Rubyは、「オブジェクト指向を完全に実現したプログラミング言語」と言われます。その理由はRubyで扱う全てのものがオブジェクトだからです。Rubyの中で取り扱う「数値」「文字の連なり(文字列)」「情報のかたまり」など、全てがオブジェクトとして扱われます。
以上のことを認識することがRubyのオブジェクト指向を習得Railsを理解していく第一歩です。某プログラミングスクールを卒業しただけでまだまだ理解が浅いのでこれからも頑張ります!!?