- 投稿日:2020-12-23T23:33:04+09:00
[Ruby on Rails] kaminariの使い方
kaminariとは?
rubyのgemの一つで、ページネーションを作るためのものです。
kaminariのインストール
以下を
Gemfileに追加します。gem 'kaminari'ターミナルでインストールします。
$ bundle installコントローラーにページネーションのコードを追加
class PostsController < ApplicationController def index @posts = Post.all @posts = Post.page(params[:page]).per(15) end end
page(params[:page]).per(15)の部分でページあたりいくつ表示させるかを設定します。
ページネーションが表示されるのは、表示される数が設定した数より多い場合です。ビューファイルにページネーションのコードを追加
ビューでページネーションを表示させる場所に追加します。
<%= paginate @posts %>kaminariを日本語化する
kaminariの表示はデフォルトだと英語です。
日本語化する場合は、config/localesにkaminari_ja.ymlというファイルを作成すると管理しやすいです。
kaminari_ja.ymlに以下を追記します。ja: views: pagination: first: "« 最初" last: "最後 »" previous: "‹ 前" next: "次 ›" truncate: "..." helpers: page_entries_info: one_page: display_entries: zero: "" one: "<strong>1-1</strong>/1件中" other: "<strong>1-%{count}</strong>/%{count}件中" more_pages: display_entries: "<strong>%{first}-%{last}</strong>/%{total}件中"
- 投稿日:2020-12-23T23:21:12+09:00
【Ruby】モジュール
モジュール
- 「インスタンス化できないクラス」のようなもの。
ClassクラスはModuleクラスのサブクラス。- 「クラス = モジュール + インスタンス化能力」
Mix-in
制限された多重継承
Rubyは、クラスは1つしか継承できない。
Rubyは、モジュールという「クラスのようだがクラスではない」ものならば何個でも継承できる。
- クラスがモジュールを継承することを
includeすると呼ぶ。モジュールと継承
includeしたモジュールはクラスのancestorsメソッドに現れる。クラスがモジュールを
includeすると、モジュールのインスタンスメソッドが手に入る。インスタンス化不能性
- モジュールをインスタンス化することはできない。
- Mix-inはあくまでも「トッピング」。主となるクラスに従として実装を提供するための「機能の集まり」(module)であり、具体的なもの(object)ではない。
名前空間
- 異なる名前空間に属する同じ名前のクラスは、たまたま名前が同じでも別物として扱われる。
- Rubyでは名前空間を提供するためにもモジュールを利用する習慣がある。
参照
- 投稿日:2020-12-23T23:10:11+09:00
irb でサクっとパフォーマンスを調べる
irb の進化が止まらない。最も先進的な REPL は Ruby の irb かもしれない1。複数行編集,シンタックスハイライト,オートインデント,メソッド名補完は衝撃的だった。
さて,2020 年 12 月 20 日リリースの irb 1.2.8 で measure という機能が追加された。
これは式の評価にかかる時間を計測してくれるもの。Ruby で 100 万番目の素数を表示するのは
require "prime" p Prime.lazy.drop(999999).firstでいい。答えは 15485863。
これがどのくらいの時間で実行されるのかが知りたいことがある。計測手段はいくつもあるが irb 上でサクッとできるなら,それはそれで嬉しい。では
gem update irbで最新の irb をインストールしておこう。記事執筆時点(2020/12/23)での最新は 1.2.9 だ。
そして,
irb -r primeで irb を起動する。
いや,もちろんirbで起動しておいて,
irb(main):001:0> require "prime"とやってもいいのだが,面倒なので,使うライブラリーは
-rオプションに渡したほうがいい。そして,
irb(main):001:0> measureと打つと,式の評価時間を計測するモード2になる・・・はずなのだが,実はバージョン 1.2.8, 1.2.9 では何らかの手違いにより,LoadError が出るようになっている3。
幸い,measure コマンドが使えるようにするためのプルリクエスト が @elim さんにより出され,既に取り込まれている。現状では,仕方ないので
irb(main):001:0> IRB.conf[:MEASURE] = trueと打とう。こんなの覚えられないけど,覚えなくていい。次のバージョンでは
measureでいけるようになるはずだ。こうすると,irb 上で式を評価するたび,いちいち時間を測ってくれる。
ではさっそく:irb(main):002:0> Prime.lazy.drop(999999).first processing time: 7.395823s => 154858637 秒ちょいだった。
計測モードをやめるには
irb(main):003:0> IRB.conf[:MEASURE] = falseとする。
measureコマンドが使えるようになったら,irb(main):003:0> measure :offでいけるようになるはずだ。
ところで,この手の速度(実行時間)計測では気をつけないといけないことがいろいろある。
irb の measure 機能は,デフォルトでは処理前後の時刻の差を取っているだけのようなので,CPU が他のことで忙しいときに計測すると長めの値が出そう。
実行時間はさまざまな要因で揺れ動く。実際,さきの 100 万番目の素数は,もう一度測ったら 6.5 秒程度であった。計測結果が
7.395823sなどと出ていても,有効数字はそんなに無い。単に Float をそのまんま表示しているだけ。
まあおそらく小数第二位の桁(0.01 の桁)には全く意味が無いだろう。だから,
irb(main):004:0> 1+1 processing time: 0.000282s => 2とやって出てくる「0.000282s」という数字には全く意味が無い。「ホトンドゼロデスネー」としか言えない。
このような〈軽すぎる式評価〉の実行時間は,多数回実行させて求める必要がある。そうなるとベンチマークテストライブラリーの出番なわけで,irb なんかでやることではなくなってしまうだろう。なお,筆者は試してないが,計測・表示の処理はカスタマイズできるようだ。
ユーザーのホームディレクトリーの .irbrc というファイルに,IRB.conf[:MEASURE_PROC][:CUSTOM] = proc { |context, code, line_no, &block| # 云々 }てな感じで記述すればいいらしい。
これで,例えば「結果表示は小数第一位までにするぞ」とかが可能になる。具体的なことは irb のコミットメッセージをどうぞ:
https://github.com/ruby/irb/commit/3899eaf2e21fcb8b6c1ba3fdb1ac4ec8c9b4798f
- 投稿日:2020-12-23T22:56:09+09:00
【Ruby】真 (true) とは何か
最近技術書を読んでいて、わかった気になってるなーと思い、
細かいことでも頻繁にアウトプットしていくことにしました。未来の自分のための記事です。
※検証Rubyバージョン: 2.7.2
結論
Rubyでは
falseとnil以外のすべての値が真です。TrueとFalse
Rubyにおいて、すべてのデータ・値は何らかのクラスに属するオブジェクトです。
もちろんtrueやfalseも例外ではありません。irb> true.class => TrueClass irb> false.class => FalseClass irb> true.class.ancestors => [TrueClass, Object, Kernel, BasicObject] irb> false.class.ancestors => [FalseClass, Object, Kernel, BasicObject]falseとnil以外が真であるなら、
0や(空文字)も真です。# 「0」は true irb> p 0 ? true : false true => true # 「 」もtrue irb> p '' ? true : false (irb):15: warning: string literal in condition true => trueしかし他の言語では「0」は偽であることが多く、JavaScriptでは0や空文字はfalsyです。
nilとfalse
nilとfalseを区別するには
nil?メソッドがあります。irb> a = nil => nil irb> a.nil? => true irb> b = false => false irb> b.nil? => falseRubyのプログラムを書いていると予想外にnilが混入することがあるので、nilチェックをよくしますが、
nilオブジェクトは型変換をしてあげるとnil回避ができます。irb> nil.to_s => "" irb> nil.to_i => 0 irb> nil.to_a => [] irb> nil.to_h => {} irb> nil.to_sym NoMethodError (undefined method `to_sym' for nil:NilClass)
to_symはだめっぽい。追記
Rubyにおける真/偽とtrue/falseを混同していたのですが、下記記事がとても参考になりました。
Ruby の真/偽と true/false は違う
- 投稿日:2020-12-23T22:32:05+09:00
Rails の credentials.yml.enc と master.key の関係性について(undefined method `[]' for nil:NilClass (NoMethodError))
職場で運用している Rails で作成した、顧客と収益アプリの公開用 clone を作成する際に、題名のところでハマったのでメモ用として残しておきます。
経緯
- GitHub のプライベートリポジトリからローカルにソースコードを clone
- プライバシー情報を削除後、
.gitファイルを削除して、アプリ名(folder 名)を_v2へと変更git initして、リモートリポジトリへプッシュ- CircleCi と連携させて、master に merge されたタイミングで自動テストを組む
- RDS を本番環境の db にしたいので、ローカルで
credentials.ymlに、RDS の情報を打ち込む設定を行う(このとき、credentials:editコマンドを一度使用して、なぜか一度 credential ファイルを削除、credeitials:editコマンドで再作成という謎の手順を踏んでいます)- ECR にコンテナデプロイして、ECS のタスクを実行しようとしたところ、ECR のコンテナが起動していない
- 開発用の Dockerfile を push していたため、
rails sコマンドを起動しておらず追記- 一度、開発環境で動作確認しようと思い、
docker buildすると、サーバーが起動しないエラー発生(undefined method `[]' for nil:NilClass (NoMethodError))ここまでが経緯です。
AWS は一度お試しアプリで、本番環境として自動デプロイを組んだことがあるという程度で、サービスに関してよく理解しないまま進めていました。
何が問題だったのか
問題は、普通に 5 番目の項目ですね。笑
少し詳しくみると、
- credential.yml.enc は、master.key を使用して暗号化、復号される
git push時に、master.key はプッシュされない(.gitignore に記載されているため)つまり、clone してきた時点で master.key は無かった。 そして、一回目の
credentials:editコマンド時に master.key が作成された。しかも、一度 credentials.yml ファイルを削除して、再度
credentials:editコマンドを行っていたので、master.key との整合性が取れていない credentials.yml が作成されていた。そのため暗号化も復号もできておらず、一度作成していた RDS の設定も消えていた。という感じですかね?多分
どう解決したか
この場合は、
- 一度、master.key と credentials.yml を削除
credentials.editコマンドで 2 つのファイルを再作成という工程でいけました!
結構ありがちなエラーみたいで調べたらたくさん情報が出てきたけど、いい勉強になった。
同じようなケースの方は私の屍を超えていってください!!
- 投稿日:2020-12-23T22:07:14+09:00
【rails】railsとjsを用いて「いいね機能」を実装してみた
今回はrailsとjsでいいね機能を実装していきたいと思います
** また最後におまけでユーザーがいいねした投稿を表示できるような機能も実装していきます**
jsを読み込んだりする説明は割愛!
参考にさせていただいた記事
https://techtechmedia.com/favorite-function-rails/
https://qiita.com/hayabusa3703/items/2b916e652a1dc85bb6e3完成予想図
下準備
ユーザーはたくさんの投稿にいいねをして、投稿もたくさんのユーザーにいいねされるので
likesテーブルを中間テーブルにした、ユーザと投稿の多対多のテーブル構造
rails g model likeマイグレーションファイル
class CreateLikes < ActiveRecord::Migration[5.0] def change create_table :likes do |t| t.integer :user_id t.integer :drink_id t.timestamps end end endrails g controller likesアソシエーション
like.rb
class Like < ApplicationRecord belongs_to :user belongs_to :drink, counter_cache: :likes_count end・counter_cahce: :likes_countはリレーションされているlikeの数の値をリレーション先のlikes_countというカラムの値に入れますよっていう意味です。なのでlikes_countカラムをstoriesテーブルに追加しましょう。(rails g migration AddLikes_countToStories likes_count:integerをターミナルで実行すればオッケーです。)
この文章の参照元drink.rb
class Drink < ApplicationRecord has_many :likes has_many :liking_users, through: :likes, source: :user endliking_usersモデルは無いので、likesテーブルを中間テーブルにして、userモデルとアソシエーションを汲みますよーってことをrailsに伝えてます
has_manyはbelongs_toはアソシエーションを組むのが本質ではなくて、メソッドを作るメソッド。
つまり,@drink.liking_userとかやったら、その投稿にいいねしたユーザー一覧を取得できるメソッドができるし、アソシエーションも組める
user.rb
has_many :likes has_many :like_drinks, through: :likes, source: :drinkこれも、user.like_drinksとかやったら、そのユーザーがいいねした投稿一覧が取得できる
これは、インスタ、Twitterによくある、そのユーザーがいいねした投稿を表示する時に便利has_manyはbelongs_toはアソシエーションを組むのが本質ではなくて、メソッドを作るメソッド。
これを覚えて帰りましょう。
いいねボタンの記述
drinks/index.html.erb
こちらは投稿一覧のページになります
<%if @drinks%> <% @drinks.each do |drink|%> <li class='list'> <%= link_to drink_path(drink.id) do %> <%= link_to user_path(drink.user.id) do%> <div class="user-info-timeline"> <%=image_tag drink.user.image.variant(resize: '60x60'),class: "user-img-timeline" if drink.user.image.attached?%> <div class="username-timeline"> <%= drink.user.nickname %> </div> </div> <% end %> <div class='item-img-content'> <%= image_tag drink.image , class: "item-img" if drink.image.attached? %> <%# if drink.trade%> <%# end %> </div> <div class='item-info'> <h3 class='item-name'> <%= drink.name %> </h3> <div class='item-price'> <span><%= drink.price %>円<br>(税込み)</span> <div class='star-btn'> <%# image_tag "star.png", class:"star-icon" %> <span class='star-count'>0</span> </div> </div> <div class='item-explain'> <%= drink.explain%> </div> <div class='item-tag'> <% drink.tags.each do |tag| %> #<%=tag.tag_name%> <%end%> </div> <%= render "likes/like",drink: drink%> </div> <% end %> </li> <%end%><%= render "likes/like",drink: drink%>に注目して欲しいです!
まずは可読性を高めるために
画像のいいねボタンを部分テンプレートで切り出しています、そして、,drink: drinkの部分ですが、
<% @drinks.each do |drink|%>のeach文内のブロック変数を、likes/likeにも適用するために変数を受け渡しています。
ブロック変数とは(分かる人は飛ばして)
ブロック変数とは、each文やらtimes文,form_withとか、そのメソッド内だけで使える変数です。
つまり、eachだったらeachから endまでの範囲無で使える変数@drinksにはいろんな情報が、配列として入っていますが、|drink|
とすることで、配列の中の一つ一つの情報がdrinkに入っていって、@drinksにある配列の数だけ表示しますlikes/_like.html.erb
パーシャル(部分テンプレート)であることを分かりやすくするために慣習的にファイル名を_likeとしてます。
ただ<%= render "likes/like",drink: drink%>
で呼び出す時はアンダーバーはいりません
<div class="like" id="like-link-<%= drink.id %>"> <% if current_user.likes.find_by(drink_id: drink.id) %> <%= link_to unlike_path(drink.id), method: :delete, remote: true do %> <div class = "iine__button">❤️<%= drink.likes.count %></div> <% end %> <% else %> <%= link_to like_path(drink.id), method: :post, remote: true do %> <div class = "iine__button">♡️<%= drink.likes.count %></div> <% end %> <% end %> </div>id="like-link-<%= drink.id %>"
がミソ。
jsで非同期で画面を切り替えたいので、idを取得できるように、投稿ごとにidを区別するために
このように記述しましょう。<%= link_to unlike_path(drink.id), method: :delete, remote: true do %>, remote: true
と記述することにより、
リンクを押した時にajaxが発火するので非同期で通信が行われます。
いいねボタンを押したらいいねがすでについてれば、unlike_pathそうじゃなければlike_pathに飛びます
それぞれのpathをまだ定義してないので、このままじゃルーティングエラーになってしまうので
routes.rb
post 'like/:drink_id' ,to: 'likes#like', as: 'like' delete 'like/:drink_id',to: 'likes#unlike', as: 'unlike'と記述しましょう
as: 'like' とすることにより本来ならlikes_like_path(drink.id)とパス指定をしなきゃいけないのですが、
like_path(drink.id)でlikes#likeにpostリクエストを送ることができますこれで、リンクを踏んでリクエストを送ることができたので、次はコントローラーをみていきましょう
likes_controller
class LikesController < ApplicationController include SessionsHelper before_action :set_variables def like like = current_user.likes.new(drink_id: @drink.id) #redirect_to drinks_path # jsを用いるので画面遷移は行わない #binding.pry like.save end def unlike like = current_user.likes.find_by(drink_id: @drink.id).destroy #binding.pry end private def set_variables @drink = Drink.find(params[:drink_id]) @id_name = "#like-link-#{@drink.id}" end endremote: trueのリンクからlike,unlikeアクションが呼び出されるので、
デフォルトの遷移先はilke.js.erb,unlike.js.erbとそれぞれなります。「⚠︎ @id_name = "#like-link-#{@drink.id}"
とControllerにViewの処理を書くのは、MVCパターン的にあまりよろしくないと思いますね。」とご指摘をいただいたので、あまりよく無いですが、機能的には問題無いので一旦次いきます。
likes/like.js.erb
$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", drink: @drink )) %>');/likes/unlike.js.erb
$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", drink: @drink )) %>');likes/_like.html.erbにまた戻ります
この時にまた
drink: @drink
と書いて_like.html.erbに変数を受け渡してあげましょうこの@drinkは
likes_controllerの
private def set_variables @drink = Drink.find(params[:drink_id]) @id_name = "#like-link-#{@drink.id}" endの@drinkです。
以上で実装終了です。お疲れ様でした。
おまけ、ユーザーがいいねした投稿を表紙
users/show.html.erb
<%= link_to "#{@user.nickname}がいいねした投稿",user_likes_path(@user.id)%>こんな感じのリンクを作成
@userhはusers#showで@user = User.find(params[:id])
とかよくある感じで定義してますuser_like_pathはまだ定義してないので
routes.rb
get 'user/likes/:id', to: 'users#likes',as: 'user_likes' resources :users do member do get :following,:followers # memberメソッドを使うと # ユーザーidが含まれてるURlを扱うようになる end endresources :userとかみんなやると思うので、resourcesの上に get 'user/likes/:id', to: 'users#likes',as: 'user_likes'
を書きましょう
これで、 リンクを踏んだらusers#likesにGETリクエストを飛ばすことができます
users_controller
def likes @user = User.find(params[:id]) @drinks = @user.like_drinks.paginate(page: params[:page],per_page: 10).order("created_at DESC") endこんな感じで実装しましょう
.paginate(page: params[:page],per_page: 10)
はページネーション をまだ取り入れてなければ書かなくて大丈夫です。
.like_drinksメソッドは
has_many :like_drinks, through: :likes, source: :drinkとuser.rbで書いたので、ユーザーがいいねした投稿一覧を取得できます。
デフォルトで、users/likes.html.erbにリダイレクトされるので、そのビューも用意しましょう
users/likes.html.erb
<div class="user-profile"> <h2 class="user-profile-name"><%= current_user.nickname %></h2> <h2><%= image_tag @user.image.variant(resize: '100x100'),class: 'user-img' if @user.image.attached? %></h2> <div class="user-like-post"> <%= link_to "#{@user.nickname}がいいねした投稿",user_likes_path(@user.id)%> </div> <div class="user-edit"> <% if current_user?(@user) %> <%= link_to "プロフィールを編集",edit_user_path(@user)%> <% end %> </div> <% unless current_user?(@user) %> <div id="follow_form"> <% if current_user.following?(@user) %> <%= render 'unfollow' %> <% else %> <%= render 'follow' %> <% end %> </div> <% end %> </div> <% @user ||= current_user %> <div class="stats"> <a href="<%= following_user_path(@user) %>"> <strong id="following" class="stat"> <%= @user.following.count %> </strong> following </a> <a href="<%= followers_user_path(@user) %>"> <strong id="followers" class="stat"> <%= @user.followers.count %> </strong> followers </a> </div> <div class='main'> <%# 商品一覧 %> <div class='item-contents'> <h2 class='title'><%= @user.nickname%>の投稿</h2> <%= will_paginate @drinks%> <ul class='item-lists'> <%# 商品のインスタンス変数になにか入っている場合、中身のすべてを展開できるようにしましょう %> <%if @drinks%> <% @drinks.each do |drink|%> <li class='list'> <%= link_to drink_path(drink.id) do %> <div class='item-img-content'> <%= image_tag drink.image , class: "item-img" if drink.image.attached? %> <%# if drink.trade%> <%# end %> </div> <div class='item-info'> <h3 class='item-name'> <%= drink.name %> </h3> <div class='item-price'> <span><%= drink.price %>円<br>(税込み)</span> <div class='star-btn'> <%# image_tag "star.png", class:"star-icon" %> <span class='star-count'>0</span> </div> </div> <div class="item-explain"> <%= drink.explain%> </div> </div> <% end %> </li> <%end%> </ul> <%= will_paginate @drinks%> </div> <%end%> </div>自分はこんな感じ
これで以上です。お疲れ様でした。
今までのコードのまとめ
- 投稿日:2020-12-23T21:59:23+09:00
「小学生から楽しむ きらきら Ruby プログラミング」スモウルビーのご紹介
初めに
ご覧頂き、ありがとうございます。
名古屋でプログラミング講師を致しておりますアトリヱ未來と申します。
普段は、プログラミングを学びたい初心者の方々(小中学生〜大人)を対象に、HTML/CSS や Ruby on Rails、C言語(Arduino) を教えております。
今日は Ruby 3.0 リリースということもあり、それに因んだことを書きたい気持ちもありましたが、技術的な解説は先達の方々が記してくださっているので、ここでは少し指点を変えて、お子様向けのプログラミングのご紹介です。
小学生から楽しむ きらきら Ruby プログラミング (Amazon)
テキストプログラミング言語 と ビジュアルプログラミング言語
プログラミングを構成する要素として、逐次、反復、条件 があります。
例えば、以下のコードです。puts "Hello World!" 3.times do puts "Congratulations! Ruby 3.0!" end n = gets.to_i if n.even? puts "偶数です" else puts "奇数です" end「おまじない」なしに平易に書けるRubyのおかげで、
プログラミングが分からなくても、英語が読めるならば、ほぼ自明です。しかしながら、小学生となると、英語が読めません。また、キーボードの入力も大変です。
お子様向けにC言語(Arduino)を教えていますが、ほとんどの子はタイピングはできませんし、コピーはどうするの? など、コーディングする上での基礎的なキーボードの使い方を指導するところから始まります。そこで、登場したのが、ビジュアルプログラミング言語と呼ばれるGUIベースのプログラミング言語です。
ブロックを組み立てることで、順次、反復、条件の3つがコーディングが可能です。
有名なところでは、スクラッチ(Scratch)があります。小学校におけるプログラミング教育
プログラミング思考を養い、社会に情報技術が活きていることを学ぶことが目的となっているようです。
プログラミング思考とは、「意図を満たすべく、適宜ブロックが配置できる能力」の事らしく、
要は、アルゴリズムを理解し、逐次、反復、条件で成り立つコードを組み立てられるようになりましょうということのようです。
そして、家電や車など、身の回りの様々な分野で、コンピュータが使われているので、それを動かしているプログラム(=コンピュータへの指示書)を書けるようになりましょう、ということのようです。(文部科学省:小学校プログラミング教育の手引(第三版) を読んだ雑感です)
SmalRuby スモウルビー とは
島根県在住の高尾宏治さん(NPO法人 Ruby プログラミング少年団 理事長)が開発したプログラム言語です。
ブロックによるビジュアルプログラミングと、
テキストによるRubyコーディングが両方できるのが特徴です。https://smalruby.jp/smalruby3-gui/
をブラウザで開くと体験できます。ブラウザベースなので、Mac の他、iPad でも利用可能ですが、
テキストベースでコーディングを行う際には、無線キーボードがあると快適です。
(iPhoneはさすがに画面が小さく難しかったです)今日から君はプログラマー! プログラミング体験
猫が鼠を追いかけるゲームを作ります。
ドラッグ&ドロップでブロックを組み立てます。
緑の旗を押すと、猫が鼠に向かって動き、「捕まえた」と言います。同じプログラムをテキストベースでも書くことができます。
同じプログラムをコーディングするに際し、
始めてのうちは、ビジュアルプログラミング
学習が進むにつれ、より本格的なテキストベースのRubyプログラミングへと
繋げられるように配慮されている点が、本当に素晴らしいと思います。ちなみに、両者は連動しているので、
ブロックを操作するのが大変に感じたわたくしは、
キーボードからテキストベースで入力いたしました。コードのアップロードやダウンロードも可能となっていますので、
教材としての配布も容易で、とても良いと思います。正多角形を描く
三角形の内角の和は180度です。
四角形は、三角形二個に分割することができますから、
四角形の内角の和は180×2=360度です。
ですので、正四角形(=正方形)の一つの角の大きさは、360 ÷ 4 = 90度 です。一般にN角形は、三角形(Nー2)個に分割することができますから、
N角形の内角の和は180×(N−2)度です。
ですので、正N角形の一つの角の大きさは、180×(N−2) ÷ N 度 です。これを踏まえて、正N角形を描くコードは次のようになります。
(ここでは N = 5 として、正五角形を描いています)正八角形を45度ずつ回転させて描くと次のような綺麗な幾何学模様も描くことができます。
終わりに
簡単なご紹介でしたが、お子様のプログラミング入門には最適なテキストかと思います。
(教師用の付録として指導上の注意点も巻末に紹介されています)テキストには、他にも
音楽をつくったり、なぞなぞゲームや、シューティングゲーム、
マイクロビットを使った簡単な電子工作がご紹介されています。この冬休み、お子様とご一緒にプログラミングを楽しまれてはいかがでしょうか?
みなさまのお役に立てば幸いです。参考
- 投稿日:2020-12-23T21:41:41+09:00
[メモ] ?で終わるメソッド
勉強用のメモ代わりとして記事にさせていただきます。
?で終わるメソッド
Rubyのメソッド名は?で終わらせることができる。?で終わるメソッドは慣習として真偽値を返すメソッドになっている。
# 空文字列ならtrue、そうでもなければfalse ''.empty? #=> true 'abc'.empty? #=> false # 引数の文字列が含まれていればtrue、そうでもなければfalse 'movie'.include?('mo') #=> true 'movie'.include?('at') #=> false # 奇数ならtrue、そうでもなければfalse 3.odd? #=> true 4.odd? #=> false?で終わるメソッドは自分で定義することができる。
# 2の倍数ならtrue、それ以外はfalseを返す def multiple_of_two?(n) n % 2 == 0 end multiple_of_two(1) #=> false multiple_of_two(2) #=> true multiple_of_two(3) #=> falseまとめ
真偽値を返す目的のメソッドであれば、?で終わらせるようにした方が良い。
- 投稿日:2020-12-23T19:12:30+09:00
【Rails】フォームのオートフォーカスによる画面エラー
Railsで個人アプリを開発しているときに発生したエラーです。
備忘録としてエラーの発見から解消までを記します。事象
スマートフォン(OS:iOS、ブラウザ:Google Chrome,Safari)でサインアップページにアクセスすると、ページのレイアウトが一瞬だけ表示された後、「このページは開けません。」というエラーが発生する。
ただし、該当URLを直打ちでアクセスするとページが正常に表示される。(これについては最終的に謎のまま終わりました・・・)
調査
他に同様のエラーが出るページは無いか探したところ、プロフィール編集ページでも発生しました。
サインアップページとプロフィール編集ページの共通点は、ユーザー情報を入力するフォームがあることでした。
サインアップページ
プロフィール編集ページ
原因
1画面内にautofocus: trueのフォームが複数存在すると、スマートフォンで閲覧した際にエラーが発生するようです。
そのため、autofocus: trueを一番最初の入力フォームにのみ残し、残りのフォームから消したところエラーは解消されました。
PCで閲覧する際にはエラーが出ないので見落としていました・・・
フォームをコピペで増やしていくのもいいですが、この辺りをしっかりメンテナンスしないといけないですね・・・。
- 投稿日:2020-12-23T19:05:33+09:00
lib/配下のファイルをリロードするたびに再読み込みする
めも
TL;DR
config/application.rb... config.eager_load_paths += ["#{Rails.root}/lib"] ...参考記事: https://stackoverflow.com/questions/3282655/ruby-on-rails-3-reload-lib-directory-for-each-request
requireは不要になるので削除。
開発環境でのみonにする
開発時にしか行わないことなので、本番で毎回読み込まないようにするには以下のように書く。
config/application.rb... config.eager_load_paths += ["#{Rails.root}/lib"] if Rails.env.development? ...呼び出し側require './lib/invoice_pdf' unless Rails.env.development?
- 投稿日:2020-12-23T17:32:46+09:00
「OpenSSH keys only supported if ED25519 is available」のエラーの解決方法
- 投稿日:2020-12-23T17:13:43+09:00
[Rails]本番環境のデータベースをリセットする方法(Capistrano版)
はじめに
前提
・Railsを使用してアプリケーションを開発
・Capistranoでの自動デプロイを実装している
・AWSのEC2にてサーバーを構築している
・RDSでMySQLを使用している背景
私は開発環境では、rails db:migrate:resetにていつもデータベースを作り直していましたが、本番環境ではどのようにすればいいのかという疑問から実装しました。
本番環境のデータベースをリセットする
まずはターミナルを用いてEC2で自分のアプリケーションフォルダの階層まで進む。
Capistranoでの実装をしているのでミスをしないように基本的にはcurrentディレクトリで作業するようにする。terminal[ec2-user@ip-222-22-2-222 アプリ名]cd currentcurrentRAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rails db:dropこれでデータベースが消去することができました。
データベース再度作成する
今回はRDSにMySQLを導入している前提でお話していきます。
まず引き続きターミナルは同じディレクトリでmysqlに接続します。
mysql -u (マスタユーザ名) -p -h (エンドポイント)
「-u」はユーザ名、「-p」はパスワードの入力、「-h」は接続先の情報を表すオプションです。
なお、「エンドポイント」は、RDSメニューで確認し、入力してください。
「パスワード」は、「マスターパスワード情報」で設定したパスワードを入力します。terminalmysql -u root -p -h rds-mysql-server.xxx.ap-northeast-1.rds.amazonaws.comこのコマンドを打つとpasswordの入力が求められるので入力すると無事mysqlに接続が完了しました。
削除したアプリケーション名と同じ名前をつけデータベースを再度作成します。
terminalmysql> create database アプリケーション名;再度本番環境でmigrateを実行します。
terminalbundle exec rails db:migrate RAILS_ENV=productionこれでデータベースのリセットは終了です。
あとは本番環境でcapistranoのコマンドを叩いて終了ですterminalbundle exec cap production deployお読みいただきありがとうございました
- 投稿日:2020-12-23T16:44:27+09:00
roman numerals
お題
アラビア数字(arabic numerals)を受け取って,ローマ数字(roman numerals)を返すmethodを書きなさい
ローマ数字は記号の組み合わせ
ローマ数字は1,5,10,50,100,500,1000を1文字で表現できるらしく、その組み合わせで数字を表現する
じゃあ、受け取った数字を大きい数から割っていったらいいだけじゃん、と思ってまずは一気に書き上げたdef roman_numerals(num) m, modulo = div(num,1000) d, modulo = div(modulo,500) c, modulo = div(modulo,100) l, modulo = div(modulo,50) x, modulo = div(modulo,10) v, modulo = div(modulo,5) val = 'M' * m + 'D' * d + 'C' * c + 'L' * l + 'X' * x + 'V' * v + 'I' * modulo return val end def div(num, waru) div = num / waru modulo = num % waru return div, modulo end [1,2,4,5,6,9,10,11,14,15,19,38,42,49,51,97,99,439,483,499,732,961,999,1999].each do |number| puts "#{number} : #{roman_numerals(number)}" end結果
1 : I 2 : II 4 : IIII 5 : V 6 : VI 9 : VIIII 10 : X 11 : XI 14 : XIIII 15 : XV 19 : XVIIII 38 : XXXVIII 42 : XXXXII 49 : XXXXVIIII 51 : LI 97 : LXXXXVII 99 : LXXXXVIIII 439 : CCCCXXXVIIII 483 : CCCCLXXXIII 499 : CCCCLXXXXVIIII 732 : DCCXXXII 961 : DCCCCLXI 999 : DCCCCLXXXXVIIII 1999 : MDCCCCLXXXXVIIIIこれで完成と思うじゃないですか。ところがそうはいかないのがローマ数字
3はIII、38はXXXVIIIなのはいい。しかし、4はIV、9はIX、99はXCIXなのである。誰だよこんなめんどくさい数字思いついた奴は4と9は例外
プログラムというよりローマ数字の勉強になってきたが、このややこしいルールを理解するキーは「4」と「9」である。上述のとおり4がIV(5-1)、9がIX(10-1)。4と9はVやXという基本の数字の左側に端数のIが来て、基本の数字から端数を牽くという構造になってる
なので、14はXIV(10+(5-1))、42はXLII((50-10)+2)、49はXLIX((50-10)+(10-1))。49はIL(50-1)じゃないことに注意。同様に99はIC(100-1)ではなくXCIX((100-10)+(10-1))なのである。もーややこしい
つまり、一桁一桁ごとに計算していけばなんとかなりそうだdef roman_numerals(num) m, modulo = calcDiv(num,1000) d, modulo, cm = calcDiv(modulo,500) c, modulo, cd = calcDiv(modulo,100) l, modulo, xc = calcDiv(modulo,50) x, modulo, xl = calcDiv(modulo,10) v, modulo, ix = calcDiv(modulo,5) i, modulo, iv = calcDiv(modulo,1) val = 'M' * m + 'CM' * cm + 'D' * d + 'CD' * cd + 'C' * c + 'XC' * xc + 'L' * l + 'XL' * xl + 'X' * x + 'IX' * ix + 'V' * v + 'IV' * iv + 'I' * i return val end def calcDiv(num, waru) div = num / waru modulo = num % waru reigai = 0 if waru == 500 if div == 1 and num - 900 >= 1 div = 0 reigai = 1 end elsif waru == 100 if div == 4 div = 0 reigai = 1 end elsif waru == 50 if div == 1 and num - 90 >= 1 div = 0 reigai = 1 end elsif waru == 10 if div == 4 div = 0 reigai = 1 end elsif waru == 5 if div == 1 and num == 9 div = 0 reigai = 1 end elsif waru == 1 if div == 4 div = 0 reigai = 1 end end return div, modulo, reigai end汚いコードだが、reigaiという変数が4や9の例外的な表記をするかどうかの判定用の変数とした。とりあえずこれで条件を分けられたでしょう。実行。
1 : I 2 : II 4 : IV 5 : V 6 : VI 9 : IXIV 10 : X 11 : XI 14 : XIV 15 : XV 19 : XIXIV 38 : XXXVIII 42 : XLII 49 : XLIXIV 51 : LI 97 : XCXLVII 99 : XCXLIXIV 439 : CDXXXIXIV 483 : CDLXXXIII 499 : CDXCXLIXIV 732 : DCCXXXII 961 : CMCDLXI 999 : CMCDXCXLIXIV 1999 : MCMCDXCXLIXIVん?なんか色々おかしいぞ。おかしいのは9、19、49、97、99、439、499、961、999、1999。つまり9絡みで何かが起きてる。
よく見ると、9はIXが出てほしいが、IVというのが余計についてる。19も同じく。つまり4が余分についてるんだな。
プログラムを見直すとなるほど、例えば7行目でcalcDivを呼び出して、9の時は例外的な表記(IX)しますよーとしてるのはいいのだけど、それで処理を終えるべきところを次の行に進んでまたcalcDivを呼んでしまい、9%5の答えは4だからIVを表記しなければ!となっているなので、roman_numeralsのvalの前にif cm == 1 cd = 0 end if xc == 1 xl = 0 end if ix == 1 iv = 0 endを追加して、それぞれの位で9を表記することになったら、4はもう表記するなと定義してやった。すると
1 : I 2 : II 4 : IV 5 : V 6 : VI 9 : IX 10 : X 11 : XI 14 : XIV 15 : XV 19 : XIX 38 : XXXVIII 42 : XLII 49 : XLIX 51 : LI 97 : XCVII 99 : XCIX 439 : CDXXXIX 483 : CDLXXXIII 499 : CDXCIX 732 : DCCXXXII 961 : CMLXI 999 : CMXCIX 1999 : MCMXCIXと、ちゃんと出力できた。どうだ古代ローマ人見たか。refactoringとかはもう体力がないので一旦ここで勘弁してください
参考記事
- source ~/MasahiroOba/grad_members_20f/members/MasahiroOba/roman_numerals.org
- 投稿日:2020-12-23T16:42:09+09:00
変数の中身を意識したい!
言語
Ruby
対象読者
- 変数の型を意識していない方。
- プログラミングを勉強したばかりの方
- 変数がよくわからない方
- RubyでNoMethodErrorが発生した際に原因がパッと思い浮かばない。
記事の内容
変数や型の難しい箇所(参照型、値型、null、スコープなど)は説明せず、変数のイメージをつかんで頂けるように作成しております。
最低限知って欲しいこと
変数の中身を意識すること!
これだけです。これは静的でも動的でも変わらず重要なことです。
イメージ図はこんな感じです。変数の中身を意識しないプログラマーは往々にしてこのような書き方をします。
変数さん! 料理をしてください!!変数の中身が大工さんだと知らないんですね。なので、大工さんに対して料理をしてと言ってしまいます。
コード的に説明すると以下の感じです。#大工クラスを定義しています。 class Carpenter #建設メソッドを定義しています。 def construction puts "建設します" end end #大工クラスを実体化しています。 hennsuu = Carpenter.new #大工クラスに定義されていないメソッドを呼び出しています。 hennsuu.cooking分かるでしょうか?実際に存在しているクラスを用いてもう一度説明します。
RubyのStringクラスとIntegerクラスの公式ドキュメントをみてください。
- String
- Integer
サイトの中で検索(command + F)でupcaseと検索してください。
Stringではヒットしますが、Integerではヒットしません。
これはStringクラスではupcaseメソッドが定義されていますが、Integerクラスではupcaseメソッドが定義されていないことを示します。
では、実際にコードを実行してみましょう。hennsuu = "abcdef" puts hennsuu.upcase #"ABCDEF" hennsuu2 = 12345 puts hennsuu2.upcase #NoMethodError hennsuu.class # String hennsuu.class # IntegerNoMethodエラーが出ましたね。これはIntegerクラスにはupcaseメソッドが定義されていないためにエラーが発生しています。
このように、変数の中身を意識せずもっと言えば変数の中身のクラスを意識せずにプログラムは書けないのです。まとめ
変数の中身を意識しないと思わぬバグやエラーに苦しめられることがあります。
変数の中身は意識するようにしましょう!外部サイト
今回使用している可愛い絵に関しては下記サイトにて取得したものです。
素材に関しての著作権はみふねたかし様が保持されております。
https://www.irasutoya.com/
- 投稿日:2020-12-23T16:31:04+09:00
Rails に CircleCIを導入する
はじめに
Rails に CircleCI を導入して GitHub に push すれば自動でテストなどが走るようにしていきます。
手順
- CircleCI の自分のアカウントで CI を走らせたいリポジトリを選択
- .circleci 以下に config.yml を作成
- push する
実装
Rails のアプリ作成(MySQLで作ります。)
$ rails new circleci_practice -d mysqlscaffoldで適当なCRUDを作成します。
circleci_practic $ rails g scaffold users name:string email:stringGemfile に rspec を追加し bundle install
適当な RSpec を一つ書いてみてください。
.circleci ディレクトリを作り config.yml を作成
そのままでは Docker上でMySQLに接続できないのでhostの部分を編集してください。
とりあえず以下のような感じでいいと思います。confg/database.ymltest: <<: *default database: circleci_parctice_test # ↓これを追加 host: <%= ENV['DATABASE_HOST'] } %>circleci/config.ymlversion: 2 jobs: test: docker: - image: circleci/ruby:2.7.1-node environment: RAILS_ENV: test DATABASE_HOST: 127.0.0.1 - image: circleci/mysql:5.7 environment: MYSQL_ALLOW_EMPTY_PASSWORD: true MYSQL_ROOT_PASSWORD: '' MYSQL_DATABASE: circleci_parctice_test working_directory: ~/circleci_parctice steps: - checkout - run: name: "bundle install" command: bundle install --path vendor/bundle # MySQLと接続できるまで待ちます - run: name: "waiting DB start" command: dockerize -wait tcp://127.0.0.1:3306 -timeout 1m - run: name: "initialize DB" command: | bundle exec rake db:create bundle exec rake db:migrate - run: name: RSpec command: bundle exec rspec workflows: version: 2 workflows: jobs: - testこれで push すると自動でCIが走ると思います。
おわりに
他にも rubocop を入れたり、 slim-lint を入れたりするといい感じになると思います。
yarn install や bundle install で cache を使ったりすると速くなります。
- 投稿日:2020-12-23T16:26:47+09:00
第9回
assert_equal
assert_equalとは,equalか否かを確認する関数であり,今回はこの関数がどのような振る舞いをするのかを確認する.
assert_equal.rbp 1==1 def assert_equal(expected,result) return expected==result end p assert_equal(1,1)出力結果は以下のようになる.
true truecolorize
ターミナルの出力に色付けするStringの拡張機能です.通常の色付け方法だとコードが覚えにくい,読みにくいというデメリットを抱えているが,このgemがあればそれらの問題を解決することができる.
colorize.rbrequire 'colorize' def assert_equal(expected, result) if expected == result puts 'true'.green else puts 'false'.red end end p "assert_equal(1, 1)" p "assert_equal(1, 2)" assert_equal(1, 1) assert_equal(1, 2)
- source ~/grad_members_20f/members/NobuakiMori/L09_assert_equal.org
- 投稿日:2020-12-23T15:50:02+09:00
anyenvでrbenvとnodenvを管理する
はじめに
anyenvを導入した理由はrubyの管理をrbenvからanyenv経由でrbenvをインストールする形式に変えたかったからです。ついでにNode.jsのバージョン管理もnodebrewからnodenvに切り替えました。こうすることで、.zshrc(bashを使う人は.bash_profile)にanyenvのパスを通すだけで済むのでファイルをきれいに保つことができます。anyenv経由でnodenvをインストール
Node.jsをいったんアンインストールする
こちらの記事を参考にさせていただきました。また、
rm -rf .npm-globalでグローバルインストールしたパッケージ等も削除しました。グローバルインストールをしない理由ということでこちらの記事も参考にさせていただきました。anyenvを導入
brewでインストールしていきます。
$ brew install anyenv $ echo 'eval "$(anyenv init -)"' >> ~/.zshrc $ exec $SHELL -l $ anyenv install nodenv $ exec $SHELL -l
~/.zshrcの部分は使っているシェルに応じて変更してください。これだけで完了です!プラグインの導入
anyenv-update
これはnodenvを含めた
~envをまとめてアップデートできるanyenv updateとういコマンドを提供してくれます。$ mkdir -p $(anyenv root)/plugins $ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update
$anyenv rootの場所はホームディレクトリ直下です。$ anyenv root /Users/shuntagami/.anyenvnodenv-default-packages
anyenv プラグインのanyenv-update と、npmインストール時にデフォルトでいっしょにインストールしておくパッケージを指定できるnodenvプラグインです。
$ mkdir -p $(anyenv root)/plugins $ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update $ mkdir -p "$(nodenv root)"/plugins $ git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages" $ touch $(nodenv root)/default-packages
default-packagesの中身にインストールしたいパッケージを指定しますdefault-packagesyarn typescript ts-node typesyncnodenvでNode.jsをインストール
$ nodenv install -l $ nodenv install 15.4.0 $ nodenv global 15.4.0最新の15.4.0をインストールしました。これで
node -vと打ってインストールしたバージョンが入っていれば成功です!anyenv経由でrbenvをインストール
rbenvの詳細はこちらの記事でまとめました。
nodenvの導入時と同様、いったんrbenvをアンインストールする必要があります。rbenvをアンインストールする
こちらの記事を参考にさせていただきました。
再びrbenvをインストール
$ anyenv install rbenv $ rbenv install 2.6.5 $ rbenv global 2.6.5バージョンは適宜変えてください。以上で完了です!
補足 railsコマンドがみつからないと言われた時
私はコマンドも消してしまったので再インストールしました。
$ gem install bundler $ gem install rails --version='6.0.0'
- 投稿日:2020-12-23T15:50:02+09:00
anyenvに移行しました!(anyenvでrbenvとnodenvを管理する)
はじめに
anyenvを導入した理由はrubyの管理をrbenvからanyenv経由でrbenvをインストールする形式に変えたかったからです。ついでにNode.jsのバージョン管理もnodebrewからnodenvに切り替えました。こうすることで、.zshrc(bashを使う人は.bash_profile)にanyenvのパスを通すだけで済むのでファイルをきれいに保つことができます。anyenv経由でnodenvをインストール
Node.jsをいったんアンインストールする
こちらの記事を参考にさせていただきました。また、
rm -rf .npm-globalでグローバルインストールしたパッケージ等も削除しました。グローバルインストールをしない理由ということでこちらの記事も参考にさせていただきました。anyenvを導入
brewでインストールしていきます。
$ brew install anyenv $ echo 'eval "$(anyenv init -)"' >> ~/.zshrc $ exec $SHELL -l $ anyenv install nodenv $ exec $SHELL -l
~/.zshrcの部分は使っているシェルに応じて変更してください。これだけで完了です!プラグインの導入
anyenv-update
これはnodenvを含めた
~envをまとめてアップデートできるanyenv updateというコマンドを提供してくれます。$ mkdir -p $(anyenv root)/plugins $ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update
$anyenv rootの場所はホームディレクトリ直下です。$ anyenv root /Users/shuntagami/.anyenv以下が
anyenv updateコマンドを実行したときの私の例です。$ anyenv update Skipping 'anyenv'; not git repo Updating 'anyenv/anyenv-update'... Updating 'nodenv'... Updating 'nodenv/node-build'... Updating 'nodenv/nodenv-default-packages'... Updating 'nodenv/nodenv-vars'... Updating 'rbenv'... Updating 'rbenv/ruby-build'... Updating 'tfenv'... Updating 'anyenv manifest directory'...nodenv-default-packages
anyenv プラグインのanyenv-update と、npmインストール時にデフォルトでいっしょにインストールしておくパッケージを指定できるnodenvプラグインです。
$ mkdir -p $(anyenv root)/plugins $ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update $ mkdir -p "$(nodenv root)"/plugins $ git clone https://github.com/nodenv/nodenv-default-packages.git "$(nodenv root)/plugins/nodenv-default-packages" $ touch $(nodenv root)/default-packages
default-packagesの中身にインストールしたいパッケージを指定しますdefault-packagesyarn typescript ts-node typesyncnodenvでNode.jsをインストール
$ nodenv install -l $ nodenv install 15.4.0 $ nodenv global 15.4.0最新の15.4.0をインストールしました。これで
node -vと打ってインストールしたバージョンが入っていれば成功です!anyenv経由でrbenvをインストール
rbenvの詳細はこちらの記事でまとめました。
nodenvの導入時と同様、いったんrbenvをアンインストールする必要があります。rbenvをアンインストールする
こちらの記事を参考にさせていただきました。
再びrbenvをインストール
$ anyenv install rbenv $ rbenv install 2.6.5 $ rbenv global 2.6.5バージョンは適宜変えてください。以上で完了です!
補足 railsコマンドがみつからないと言われた時
私はrailsコマンドも消してしまったので再インストールしました。
$ gem install bundler $ gem install rails --version='6.0.0'
- 投稿日:2020-12-23T15:40:51+09:00
【Ruby】searchとeach_with_indexを使用したプログラム
論理的思考強化の為、ドリルの復習をしています。
初学者のため、何かお気づきの点がありましたらご教示いただけますと幸いです。
今回は私の失敗談です。問題
以下の配列から、数を探して何番目に含まれているか結果を返すsearchメソッドをeach_with_indexを用いて作成してください。
ruby.rbinput = [3, 5, 9 ,12, 15, 21, 29, 35, 42, 51, 62, 78, 81, 87, 92, 93]模範解答
ruby.rbdef search(target_num, input) input.each_with_index do |num, index| if num == target_num puts "#{index + 1}番目にあります" return end end puts "その数は含まれていません" end input = [3, 5, 9 ,12, 15, 21, 29, 35, 42, 51, 62, 78, 81, 87, 92, 93] search(11, input) #結果 その数は含まれていません私のミス解答
ruby.rbdef search(target_num, input) input.each_with_index do |num, index| if num == target_num puts "#{num}は、#{index + 1}番目にあります。" else puts "#{num}は含まれていません。" end end end input = [3, 5, 9 ,12, 15, 21, 29, 35, 42, 51, 62, 78, 81, 87, 92, 93] search(12, input) #結果 3は含まれていません。 5は含まれていません。 9は含まれていません。 12は、4番目にあります。 15は含まれていません。 21は含まれていません。 29は含まれていません。 35は含まれていません。 42は含まれていません。 51は含まれていません。 62は含まれていません。 78は含まれていません。 81は含まれていません。 87は含まれていません。 92は含まれていません。 93は含まれていません。反省
引数で渡しているinputの配列の要素全てを処理結果を出力してしまっていますし、
さらに条件分岐にelseを使っているので、一致しなければ配列内に存在していても「含まれていない」と出てしまいます。そのため、以下の2点が必要になります。
①elseをなくす
②returnで止める①each_with_indexは繰り返し処理なので、input配列の値一つ一つを確認してくれます。
そのためelseを置いてしまうとそれぞれの結果も表示させてしまうのでifのみに留めます。ruby.rbinput.each_with_index do |num, index| if num == target_num puts "#{index + 1}番目にあります" end endそしてeach_with_indexさんの配列内の値全ての確認が終了したら、
結果として「含まれていない」を返して欲しいので、条件分岐+繰り返し処理の外にputsを置きます。ruby.rbdef search(target_num, input) input.each_with_index do |num, index| if num == target_num puts "#{index + 1}番目にあります" end end puts "その数は含まれていません" end②目的の数値の存在確認ができても、今のままだと「その数は含まれていません」も表示されてしまいます。
以下のような感じです。ruby.rbdef search(target_num, input) input.each_with_index do |num, index| if num == target_num puts "#{index + 1}番目にあります" end end puts "その数は含まれていません" end input = [3, 5, 9 ,12, 15, 21, 29, 35, 42, 51, 62, 78, 81, 87, 92, 93] search(12, input) #結果 4番目にあります その数は含まれていません値の存在を確認できたらその時点でメソッドを終了させたいので、「◯番目にあります」と表示させた後にreturnを置きます。
今回は理解不足で時間がかかってしまいましたが、処理の流れをしっかり理解でき収穫は大きかったと思います!
無理やりポジティブにしていますが、理解が遅い点に結構凹んでいます。。
明日も頑張ろう!!
- 投稿日:2020-12-23T15:14:28+09:00
herokuでback-ground-imageを表示させたい。
環境
ruby 2.6.5
rails 6.0.0
mysql2 0.3.5問題
ローカル環境では問題なく表示されていた背景画像が本番環境(heroku)では表示されていなかった。
試したこと
config/environments/production.rb# Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = falseから
config/environments/production.rb# Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = falseに変更
参照記事→https://qiita.com/___xxx_/items/fa15f358beba2b9389daしかし期待された挙動はうまく行かなかった。
他の記事でも大概この内容で本番環境で挙動がうまく行っている傾向にあるものの、自分はダメだった。
解決策
2015年の少し古い記事を発見→https://www.cotegg.com/blog/?p=189
試してみる。①cssからscssに変更
②合わせてscssの内容も変更
main.css.main { display: flex; background-image: url("../main-bg.png"); /* 画像のURLを指定 */ background-size:cover; background-repeat: no-repeat; background-attachment: fixed; }から
main.scss.main { display: flex; background-image: image-url("main-bg.png"); /* ここの記述を変更 */ background-size:cover; background-repeat: no-repeat; background-attachment: fixed; }に変更
ローカル環境で不具合がないことを確認③プリコンパイル実施
% bundle exec rake assets:precompile RAILS_ENV=productionここでコミットして本番環境にpushすることを忘れないこと。
% git push heroku master本番環境にて挙動を確認。
うまく行った。
- 投稿日:2020-12-23T15:07:00+09:00
Ruby処理系自作入門
はじめに
この記事はRuby Advent Calendar 2020 22日目の記事です。遅刻して申し訳ありません。
Rustという言語でrurubyというRuby処理系の独自実装を作っており、今回はその紹介をします。RustでRubyということでrurubyという安直なネーミングにしましたが、発音しずらい上に本家Rubyと紛らわしく恐れ多いので後悔しております。もう少し技術的なポエムを言語実装Advent Calendar 2020に書きましたので、興味のある方はそちらもご参照頂ければ。
Rustについて
Rustは比較的新しいプログラミング言語で、C言語と同様の静的型付け言語です。最新の研究成果を取り入れた柔軟で堅固な型システムと型推論機構を持ち、高速かつメモリ安全・スレッド安全な言語となっています。また、高度な抽象化機能を実行速度を犠牲にしないやり方でうまく取り入れていて、言語処理系のようなシステムプログラミングには最適です。
逆に、所有権システムという独特の仕組みがあってプログラマに一定の制約が課されます。コンパイルが通れば安全ですが、慣れるまではコンパイルを通すまでが大変、と巷では言われています。プログラムの書きやすさは犠牲にして高速性と安全性を追求するという、ある意味Rubyとは正反対の思想のプログラミング言語ですね。(どちらの言語が良いか、という不毛な議論はするつもりはありません。場面に応じて自分が使いやすいものを使えばよいと思います。念のため。)
実装の現状について
基本的には現在のRubyと同様にプログラムを中間表現(バイトコード)にコンパイルして仮想マシンが解釈実行するタイプの処理系です。パーサは独自に古典的な再帰下降パーサを心を込めて手書きで書いています(つらい)。不要になったオブジェクトを回収するガーベジコレクタも、単純なものですが自前で実装しています。
現在の進捗状況ですが、ほとんどの制御構文やリテラルは動きます。組み込みクラスは基本的なものは実装していますが、まだまだクラスもメソッドも足りていないのと、特殊変数など、まだ適当な実装になっている部分が多数あります。Rubyベンチマーク界のゴールデンスタンダードであるOptcarrotも動きます。実行速度の方も頑張ってはいますが、残念ながらRuby3.0.0-previewの2.7倍、--optという自己書き換えで高速化するオプションをつけた場合は2.1倍遅いです。いろいろなベンチマークの結果はこちら
今年の夏に話題になった大塚製薬のカロリーメイト・リキッドのプログラマ向けCUIサイトに隠されている
人間にはとても読めないひどい超絶技巧を駆使したプログラムであるQuineも動かしました。
動きカクカク問題、@Linda_pp さんの助言により完璧に動作するようになりました!(嬉しかったのでしつこく投稿) pic.twitter.com/tvPV1K4a3i
— monochrome (@s_isshiki1969) August 18, 2020Rubyの標準ライブラリもrequireで読めるようになっています。ただCバインディングを使って書かれたものは動きませんし、一部文法がサポートされていないのと、メソッドも足りないので全然ダメです。実はこの原稿を書くにあたっていくつか試しましたが、ほとんど動かなかったのでへこんでいます。一つ一つ動かせるようにしていくことが当面の課題です。
使い方
レポジトリはこちらです。
https://github.com/sisshiki1969/ruruby❯ git clone https://github.com/sisshiki1969/ruruby.git ❯ cd ruruby実行環境としてはLinuxを想定しています。Rustが動く環境であればWindowsでもOKなはずですが検証していません。WSL2やdocker上での実行を推奨します。
実行にはまずRustのインストールが必要です。少し手数がかかりますが、基本的にはコマンド一発で終わりますので、Rubyのビルドに比べると飛躍的に簡単です。
https://www.rust-lang.org/ja/learn/get-startedRustは
stable/beta/nightlyという3つのリリースチャンネル(バージョンみたいなものだと思ってください)があり、rurubyの実行にはnightlyチャンネル(Rubyでいうとpreview的なもの)が必要です。
インストールの際にnightlyを選択するか、もしくはとりあえずデフォルトのインストールを行い、その後で下記のようにnightlyをデフォルトのチャンネルとしてください。❯ rustup install nightly ❯ rustup default nightlyレポジトリのルートで
cargo runを実行すると自動的にコンパイルされ、できた実行ファイルが実行されます。--releaseオプションを付けるとコンパイルに時間がかかりますが、最適化され、高速化された実行ファイルが生成・実行されます。実行ファイルは
target/debug/ruruby(--release なしの場合)かtarget/release/ruruby(--release ありの場合)に配置されますが、cargo runを使えば適当に実行されますのであまり気にしなくて大丈夫です。
tests/以下にいくつかベンチマーク・プログラム(多くはRubyのリポジトリ内にあるもの)がありますので試してみてください。まずは定番、フィボナッチ数列の素朴な計算です。
❯ cargo run --release tests/app_fibo.rb Compiling ruruby v0.2.0 (/mnt/c/Users/sissh/Documents/GitHub/ruruby) Finished release [optimized] target(s) in 30.04s Running `target/release/ruruby tests/app_fibo.rb` 9227465aobenchはシンプルなレイトレーシングです。標準出力に
.ppm形式のデータを吐き出しますので、convertコマンド(ImageMagick)などで適当なフォーマットに変換する必要があります。❯ cargo run --release tests/app_aobench.rb > ao.ppm Finished release [optimized] target(s) in 1.15s Running `target/release/ruruby tests/app_aobench.rb`![aobench.PNG] ❯ convert ao.ppm ao.jpgファイル名を省略するとREPL(Rubyでいう
irb)が起動します。❯ cargo run Compiling ruruby v0.2.0 (/mnt/c/Users/sissh/Documents/GitHub/ruruby) Finished dev [unoptimized + debuginfo] target(s) in 1m 24s Running `target/debug/ruruby` irb:0> a = "Ruby" => "Ruby" irb:0> puts "Hello #{a} world!" Hello Ruby world! => nilエラー処理もエラーの発生場所と対処方法が分かりやすいように頑張ってスタックトレースを表示してくれます。
❯ cargo run --release tests/so_nbody.rb Finished release [optimized] target(s) in 1m 26s Running `target/release/ruruby tests/so_nbody.rb` -0.169075164 NoMethodError(no method `distances' for #<Planet:0x7f7509584a00>:Planet) 0:/mnt/c/Users/sissh/Documents/GitHub/ruruby/tests/so_nbody.rb:31 mag = dt / (distances * distance * distance) ^^^^^^^^^ 1:/mnt/c/Users/sissh/Documents/GitHub/ruruby/tests/so_nbody.rb:143 b.move_from_i(BODIES, nbodies, dt, i + 1) ^^^^^^^^^^^ 2:/mnt/c/Users/sissh/Documents/GitHub/ruruby/tests/so_nbody.rb:139 n.times do ^^^^^まとめ
「Ruby処理系自作入門」と題していますが「オレの作ったRuby自作処理系への入門」になってしまいました。ご容赦ください。
言語処理系製作、大変楽しく勉強になりますので(Rustの勉強にももちろんなりますし、Rubyの実行モデルの理解も深まりますので一挙両得です)、皆さんもぜひオレオレ言語処理系製作にチャレンジしてみてください。
- 投稿日:2020-12-23T14:41:26+09:00
roman numerals
これからすること
一言にすると,「アラビア数字をローマ数字に変換」.ローマ数字の仕組みは
辺りを参照で.
方針
1000の位,100の位,10の位,1の位に分けて考える.ローマ数字はどの位であっても4と9だけイレギュラーな表記をする(なんでや)から例外として扱う.位の数字が5以上になると先頭に5, 50, 500を表す文字を付ける必要がある.
最初に作ったコード
構造としては,各位ごとの表記を配列に追加していって,後でまとめて出力.
output = [] input = ARGV[0].to_i if input >= 1000 th = input / 1000 output.push("M" * th) input = input - th * 1000 end if input >= 100 ha = input / 100 if ha == 4 output.push("CD") elsif ha == 9 output.push("CM") elsif ha > 5 output.push("D") output.push("C"*(ha - 5)) else output.push("C"*ha) end input = input - ha * 100 end if input >= 10 te = input / 10 if te == 4 output.push("XL") elsif te == 9 output.push("XC") elsif te > 5 output.push("L") output.push("X"*(te - 5)) else output.push("X"*te) end input = input - te * 10 end if input == 4 output.push("IV") elsif input == 9 output.push("IX") elsif input > 5 output.push("V") output.push("I"*(input - 5)) else output.push("I"*input) end result = "" for string in output result = result + string end puts result使うと,
> ruby roman_numerals.rb 439 CDXXXIXと正しく出力できた.
再帰メソッド化
先ほどのコードを見ると,文字が違うだけで処理は同じなのが見て取れる.そこで一連の処理をrank = 位ごとに配列に保存する文字列を変更するメソッド化した.保存場所となる配列はmainでもさらに,1000の位,100の位…とmainで呼び出さず,再帰メソッド化することで一度のメソッド呼び出しで全ての処理を行えるようにした.再帰から抜け出す方法は,
- 入力が0
- 現在処理しているのが1の位
また,出力もメソッド化することで再帰を抜ける時に出力されるようにした.以下コード
$output = [] def roman(input,rank) if input < rank return roman(input,rank / 10) end if rank == 1000 word = ["M", "", ""] elsif rank == 100 word = ["C", "D", "M"] elsif rank == 10 word = ["X", "L", "C"] else word = ["I", "V", "X"] end if input == 0 print_result($output) return end num = input / rank if num == 4 $output.push(word[0] + word[1]) elsif num == 9 $output.push(word[0] + word[2]) elsif num > 5 $output.push(word[1]) $output.push(word[0] * (num - 5)) else $output.push(word[0] * num) end if rank != 1 input = input - num * rank return roman(input,rank / 10) end print_result($output) return end def print_result(output) result = "" for string in output result = result + string end puts result end input = ARGV[0].to_i roman(input,1000)使ってみると,
> ruby roman_numerals.rb 439 CDXXXIXと正しく出力できた.まだリファクタリングできそうな気はするが,とりあえずこの辺で
- source ~/grad_members_20f/members/musutafakemaru/ex1.org
- 投稿日:2020-12-23T14:30:38+09:00
roman numerals
これからすること
一言にすると,「アラビア数字をローマ数字に変換」.ローマ数字の仕組みは
辺りを参照で.
方針
1000の位,100の位,10の位,1の位に分けて考える.ローマ数字はどの位であっても4と9だけイレギュラーな表記をする(なんでや)から例外として扱う.位の数字が5以上になると先頭に5, 50, 500を表す文字を付ける必要がある.
最初に作ったコード
構造としては,各位ごとの表記を配列に追加していって,後でまとめて出力.
output = [] input = ARGV[0].to_i if input >= 1000 th = input / 1000 output.push("M" * th) input = input - th * 1000 end if input >= 100 ha = input / 100 if ha == 4 output.push("CD") elsif ha == 9 output.push("CM") elsif ha > 5 output.push("D") output.push("C"*(ha - 5)) else output.push("C"*ha) end input = input - ha * 100 end if input >= 10 te = input / 10 if te == 4 output.push("XL") elsif te == 9 output.push("XC") elsif te > 5 output.push("L") output.push("X"*(te - 5)) else output.push("X"*te) end input = input - te * 10 end if input == 4 output.push("IV") elsif input == 9 output.push("IX") elsif input > 5 output.push("V") output.push("I"*(input - 5)) else output.push("I"*input) end result = "" for string in output result = result + string end puts result使うと,
> ruby roman_numerals.rb 439 CDXXXIXと正しく出力できた.
再帰関数化して短く
先ほどのコードを見ると,文字が違うだけで処理は同じなのが見て取れる.そこで一連の処理をrank = 位ごとに配列に保存する文字列を変更する関数化した.さらに,1000の位,100の位…とmainで呼び出さず,再帰関数化することで一度の関数呼び出しで全ての処理を行えるようにした.以下コード
$output = [] def roman(input,rank) if input < rank return roman(input,rank / 10) end if rank == 1000 one = "M" five = "" ten = "" elsif rank == 100 one = "C" five = "D" ten = "M" elsif rank == 10 one = "X" five = "L" ten = "C" else one = "I" five = "V" ten = "X" end num = input / rank if num == 4 $output.push(one + five) elsif num == 9 $output.push(one + ten) elsif num > 5 $output.push(five) $output.push(one * (num - 5)) else $output.push(one * num) end if input > 10 input = input - num * rank return roman(input,rank / 10) end return end input = ARGV[0].to_i roman(input,1000) result = "" for string in $output result = result + string end puts result使ってみると,
> ruby roman_numerals.rb 439 CDXXXIXと正しく出力できた.まだリファクタリングできそうな気はするが,とりあえずこの辺で
- source ~/grad_members_20f/members/musutafakemaru/ex1.org
- 投稿日:2020-12-23T13:36:29+09:00
Webの仕組みとHTTPについて
ブラウザとサーバー
ブラウザは今表示されてる画面
サーバーは色んなデータを保存してる大きなPCのようなもの
サーバーからファイルをダウンロードすることでブラウザに表示される
ファイルの中にはHTML、CSS、JSなどの情報が入ってる図で表すとこんな感じ
(ブラウザ) ←←←←←← (サーバー)
ファイル
(HTML,CSS,JS)HTTP
HTTPとはブラウザとサーバーのやりとりの方法
基本はリクエストとレスポンスリクエスト
→→→→→→
(ブラウザ) (サーバー)
←←←←←←
レスポンスリクエストで欲しい情報をサーバーに投げてサーバーがその情報を返してくれる
このリクエストとレスポンスのやりとりのルールを作るのがサーバーサイド言語HTTPの基本メソッド4つ
1.GET データの取得
Webページを表示するときに使用このページ見たい
→→→→→→
(ブラウザ) (サーバー)
←←←←←←
ページの情報2.POST データの送信
フォームなどでデータを保存するに使用データ保存して
→→→→→→
(ブラウザ) (サーバー)
←←←←←←
保存したよ3.PUT データの更新
既存のデータを書き換えるときに使用データ変更して
→→→→→→
(ブラウザ) (サーバー)
←←←←←←
変更したよ4.DELETE データの削除
既存データを消すときに使用データ消して
→→→→→→
(ブラウザ) (サーバー)
←←←←←←
消したよTwitterで例えると
ツイートを表示 GET
ツイートの新規投稿 POST
ツイートの更新 PUT (Twitterはできなかったはず)
ツイート削除 DELETEIPアドレスとドメイン
IPアドレスとはサーバー内でデータに割り振られている住所みたいなもので数字で管理されている
ドメインとは「~~.com」の部分
IPアドレスだと分かりにくいのでDNSというシステムがIPアドレスとドメインを紐付けている最後にWebサイト(Qiita)が表示されるまで
qiita.comのIPアドレス教えて
→→→→→→
(ブラウザ) (DNS)
←←←←←←
IPアドレスを返すDNSから取得したIPアドレスにGETリクエスト
→→→→→→
(ブラウザ) (サーバー)
←←←←←←
HTML、CSS、JSを返すこんな感じです
- 投稿日:2020-12-23T12:36:05+09:00
Windows VSCode環境で"Debugger terminal error: Process failed: spawn bundle ENOENT"
お困りごと
Windows VSCodeでRubyをデバッグしたい。
まずは、bundlerを介した環境でデバッグしたいため、launch.jsonのuseBundlerをtrueで設定することからスタートlaunch.json{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Debug Local File", "type": "Ruby", "request": "launch", "program": "${workspaceRoot}/main.rb", "useBundler": true } ] }いざ、デバッグを開始するとVSCodeのDEBUG CONSOLEに以下のエラーが表示される。
Debugger terminal error: Process failed: spawn bundle ENOENTswpawnは日本語で"生成"、 "ENOENT"はError No Etry"の略らしい
意訳すると、「bundleプロセス生成しようとおもったけど、見つかんない」といったところ。解決策
bundleが呼び出せるようにする記述をlaunch.jsonに記述する。
pathToBundlerの部分がその記述。launch.json{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Debug Local File", "type": "Ruby", "request": "launch", "program": "${workspaceRoot}/main.rb", "useBundler": true, "pathToBundler": "bundle.bat" } ] }以上
- 投稿日:2020-12-23T12:10:29+09:00
【Ruby on Rails】タグ付機能/タグ絞り込み機能の実装
はじめに
Railsで
gem acts-as-taggable-onを用いて、タグ機能を実装しました。
仕様としては、よくあるパターンでインスタンス作成画面でユーザーが任意のタグを入力して保存するというものです。
↓
実装イメージイメージのように
①new画面でインスタンスを作成②confirm画面で入力内容を確認
③createアクション実行で一覧画面にリダイレクト
④タグをクリックすると、そのタグに紐づくインスタンスが一覧表示される
それでは実装に入ります!
前提(開発環境)
- macOS Catalina
- Ruby 2.6.5
- Ruby on Rails 6.0.0
- Visual Studio Code
- データベース: Mysql
- テンプレートエンジン: haml
目次
1.事前準備
2.ModelとController
3.View1. 事前準備
今回はacts-as-taggable-onというGemを使うのですが、インストールの途中でエラーが発生してしまうので、そのエラーがでないように実装していきましょう。順番に記載します。
Gemfilegem 'acts-as-taggable-on', '~> 6.0' #追加ターミナル% bundle installターミナル% rails acts_as_taggable_on_engine:install:migrations
rails acts_as_taggable_on_engine:install:migrationsを実行すると必要なマイグレーションファイルが作成されます。通常はここでrails db:migrateを実行してデータベースに反映させるのですが、Mysqlを使用している場合、ここで必要な処理を行わないとエラーが起きてしまいます。もしも、migrateさせてしまった場合は
rails db:rollbackでファイルを戻しましょう。acts-as-taggable-on公式リファレンスの通りターミナルでコマンドを実行します。
ターミナル% rails acts_as_taggable_on_engine:tag_names:collate_bin次に先程生成されたマイグレーションファイルを編集します。(ファイル名に気をつけてください)
db/migrate/・・・・_add_missing_unique_indices.acts_as_taggable_on_engine.rb# This migration comes from acts_as_taggable_on_engine (originally 2) if ActiveRecord.gem_version >= Gem::Version.new('5.0') class AddMissingUniqueIndices < ActiveRecord::Migration[4.2]; end else class AddMissingUniqueIndices < ActiveRecord::Migration; end end AddMissingUniqueIndices.class_eval do def self.up add_index ActsAsTaggableOn.tags_table, :name, unique: true # remove_index ActsAsTaggableOn.taggings_table, :tag_id if index_exists? (ActsAsTaggableOn.taggings_table, :tag_id) #コメントアウトする if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) #追加 remove_foreign_key :taggings, :tags #追加 remove_index ActsAsTaggableOn.taggings_table, :tag_id #追加 end remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx' add_index ActsAsTaggableOn.taggings_table, [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type], unique: true, name: 'taggings_idx' end def self.down remove_index ActsAsTaggableOn.tags_table, :name remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_idx' add_index ActsAsTaggableOn.taggings_table, :tag_id unless index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) add_index ActsAsTaggableOn.taggings_table, [:taggable_id, :taggable_type, :context], name: 'taggings_taggable_context_idx' end end①11行目をコメントアウト
db/migrate/・・・・_add_missing_unique_indices.acts_as_taggable_on_engine.rb# remove_index ActsAsTaggableOn.taggings_table, :tag_id if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id)②12行目から以下の記述を追加
db/migrate/・・・・_add_missing_unique_indices.acts_as_taggable_on_engine.rbif index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) #追加 remove_foreign_key :taggings, :tags #追加 remove_index ActsAsTaggableOn.taggings_table, :tag_id #追加 endこれで処理はバッチリです!
ターミナル% rails db:migrate次に一覧画面に表示しておくタグをseed.rbに入れておきます。
db/seed.rb# タグ付紐付け(%w()の中に記述) array = %w(ダンス 歌 撮影モデル サロン・カットモデル 演技 マジック アイドリング エキストラ 楽器演奏 リモート可能 東京 神奈川 埼玉 千葉 群馬 茨城 兵庫 北海道 大阪 京都 愛知 福岡 5000円~ 10000円~ 15000円~ 20000円~ 25000円~ 30000円~ 積極募集 Youtube広告モデル) array.each{ |tag| tag_list = ActsAsTaggableOn::Tag.new tag_list.name = tag tag_list.save }ターミナル% rails db:seed
rails db:seedがうまくいくとMysqlにデータが保存されると思います。
ここまでで事前準備は整いました。
2. ModelとController
タグを付与したいモデルに以下の記述を追加します。
app/models/event.rbclass Event < ApplicationRecord acts_as_taggable #追加 endapp/controllers/events_controller.rbclass EventsController < ApplicationController def index #一覧画面にタグを表示 @tags1 = ActsAsTaggableOn::Tag.where("id < ?", 10) @tags2 = ActsAsTaggableOn::Tag.where(id: 11..22) @tags3 = ActsAsTaggableOn::Tag.where(id: 23...29) @tags4 = ActsAsTaggableOn::Tag.where(id: 29...31) # タグ検索時にそのタグずけしているものを表示 if params[:tag_name] @events = Event.tagged_with("#{params[:tag_name]}").includes(:recruiter) else @events = Event.all.includes(:recruiter) end end def new @event = Event.new end def confirm @event = Event.new(event_params) if @event.invalid? render :new end end def create @event = Event.new(event_params) @event.recruiter_id = current_user.id render :new and return if params[:back] || !@event.save if @event.save redirect_to root_path else render :new end end def show @event = Event.find_by(id: params[:id]) end private def event_params params.require(:event).permit(:event_name, :datetime, :prefecture, :place, :detail, :tag_list) #ストロングパラメーターに :tag_listを追加 end def set_user @user = current_user end end解説は後にまとめて行います。先にViewパートに移ります。
3.View
インスタンスにタグ付けを行う
views/events.new.html.haml= form_with(model: @event, url: confirm_events_path(@event), local: true, id: "new-event") do |f| . . .create-item-2 = f.label :tag_list, "タグ付け" = f.text_field :tag_list, value: @event.tag_list.join(','), placeholder: "ダンス,東京,・・・" . . .create-btn-field = f.submit "確認画面へ"views/events.new.html.haml.create-item-2 = f.label :tag_list, "タグ付け" = f.text_field :tag_list, value: @event.tag_list.join(','), placeholder: "ダンス,東京,・・・"上記の部分を
form_withにネストさせることで「@eventに対してタグを付与させますよ」という意味になります。
url: confirm_events_path(@event)となっていますが、確認画面を挟まない場合は
= form_with(model: @event, local: true, id: "new-event") do |f|で大丈夫です。
コントローラーの部分もconfirmアクションは必要ありません。
確認画面の実装はこちらを御覧ください
@event.tag_list.join(',')カンマで区切ることによって、入力されたタグが配列の形でデータが保存されます。
一覧画面にタグを表示させる
views/events.html.haml%p.tag-word ジャンルで検索 - @tags1.each do |genre| = link_to "#{genre.name}(#{genre.taggings_count})", events_path(tag_name: genre.name), class: "tag-link"コントローラーの部分で
@tag1はこのように定義されていますapp/controllers/events_controller.rbdef index #一覧画面にタグを表示 @tags1 = ActsAsTaggableOn::Tag.where("id < ?", 10)意味としては「seed.rbで事前に作成したタグの中からidが10番未満のタグを
@tags1に代入」となります。
viewでは@tags1をeachメソッドで一覧表示しています。
残りの@tags2@tags3@tags4も同じ手順で一覧画面に表示しています。(#{genre.taggings_count})
.taggings_countメソッドはタグに紐付いているインスタンスの数をviewに表示してくれます。events_path(tag_name: genre.name)
タグをクリックすると、そのタグが紐付いているインスタンスを一覧表示します。
app/controllers/events_controller.rbdef index . . if params[:tag_name] @events = Event.tagged_with("#{params[:tag_name]}").includes(:recruiter) else @events = Event.all.includes(:recruiter) end end
Event.tagged_with("#{params[:tag_name]}")とすることでタグが紐付いているインスタンスの情報を全て取得してくれます。実装は以上です!
おわりに
最後まで読んでいただきありがとうございました!
お疲れ様でした。。参考文献
Rails | acts-as-taggable-on を使ったタグ機能の実装 | 備忘録
Railsにタグ機能をつける。acts-as-taggable-on使用
- 投稿日:2020-12-23T11:12:48+09:00
Railsチュートリアル/インデックスをつけることの意義
はじめに
こんにちは.初投稿です.自分の勉強のメモ用です.初心者なので,間違っているところなどあると思います.コメントでご指摘いただけるとありがたいです.
疑問点
rails tutorial でたびたび出てきていたテーブルにインデックスを振る,という操作が一体何のために行われているか?が疑問でした.自分の疑問点としては,「インデックスとは」でググると,「データ検索を行う際に無駄が多いため,索引としてインデックスを追加する」という趣旨の文がたくさん出てきたのですが,これがよくわかりませんでした.インデックス?インデックス番号を使ったのではだめなのか?と感じていました.具体的には以下のようなコードがある場合です.
Railstutorial 第4版 (Rails5.1対応) リスト14.1
db/migrate/[timestamp]_create_relationships.rbclass CreateRelationships < ActiveRecord::Migration[5.1] def change create_table :relationships do |t| t.integer :follower_id t.integer :followed_id t.timestamps end add_index :relationships, :follower_id add_index :relationships, :followed_id add_index :relationships, [:follower_id, :followed_id], unique: true end endインデックスを追加するとは
インデックスの追加とは,もともとあるテーブルから,idと特定のカラムを抜き出すことを指すようです.僕は初め,次のコード
add_index :relationships, :follower_idは,follower_idカラムにインデックス番号を順に1,2,3と加えていくことだと思っていました.ゆえに「インデックス番号でその役割よくね?」と考えていました.しかし,上記のコードが意味するところはそうではなく,「relationshipsテーブルからfollower_idカラムとidカラムのみを抜き出したデータを作成して保存する」という行為であるようです.
メリット
つまり,インデックスがない状態ではfollower_idで検索しようとした際にfollower_idの値をすべてのテーブルのfollower_idカラムから一つずつ検索せねばならず,またfollower_idの値が欲しいだけであるのにrelationshipsテーブルの他のカラムもすべて読み込むことになる,ということです.インデックスを作成しておくことで,欲しいカラムのみを用いて検索を行えるため,検索スピードが向上します.
デメリット
ただし,これはいいことばかりではなく,1データあたりに処理しなければならない動作が増えるので,データ追加時の処理が重くなる,というデメリットがあるようなので,適用したいモデルに合っているかをよく考えて実装する必要がありそうです.
参考文献
dbonline インデックスの意味とメリットデメリット|SQLite入門
https://www.dbonline.jp/sqlite/index/index1.html
- 投稿日:2020-12-23T10:00:22+09:00
Homebrew + rbenv + ruby-build の関係性(後編)実際にrubyとRailsの導入もできます!
前回の記事ではbrewでインストールしたプログラムが
/usr/local/Cellerディレクトリなどに入りシンボリンクを作成することでパスが通ること、各ディレクトリ構成について説明しました。今回は実際にrbenvとruby-buildをインストールして、それぞれのコマンドで何をしているのかを説明していきます。rbenv, ruby-buildのインストール
まずはbrewを最新の状態にしておきます。
% brew update次に
rbenvとruby-buildのインストールです。ruby-buildはrbenvのプラグインの1つです。rbenv installというRubyのバージョンをインストールするコマンドを提供しています。rbenvをインストールする際に、セットでインストールします。% brew install rbenv ruby-buildRBENV_ROOT とは
次のコマンドの説明に必要なのでこの用語の説明をします。RBENV_ROOT というのは rbenv の shims (後述) と versions (Ruby のインストール先) がある場所(パス)を指し示すための環境変数です。 デフォルトではホームディレクトリに直下になっており、以下のように確認できます。
% rbenv root /Users/shuntagami/.rbenvrbenv initの実体
話を戻します。rbenv のインストール手順によると以下のコマンドを実行するようにあります。
% echo 'eval "$(rbenv init -)"' >> ~/.zshrc #(`~/.zshrc`の部分は使っているシェルに応じて変えてください。)これは何を行うのでしょうか? 答えは書いてあるとおり、
shimsとautocompletionを有効化します。shimsには何が入っているのか確認してみましょう。% cd .rbenv % ls shims version versions % cd shims % ls bundle bundler erb gem irb rake rdoc ri ruby testrb,,,bundleやらrakeやらrailsを使ったことのある人ならおなじみのコマンドが入っています。つまり、
shimsを有効化することにより、$RBENV_ROOT/shimsをPATHに入れ,これらのコマンドがどこからでも使えるようになるということです。最後にシェルの設定ファイルの変更を以下のコマンドで読み込みましょう。source ~/.zshrc #(`~/.zshrc`の部分は使っているシェルに応じて変えてください。)rbenvでrubyをインストールする
ここまでで
rbenvのインストールが完了したのでrubyをインストールしていきます。インストール可能なコマンドは以下のように確認しましょう。% rbenv install --list特に理由がなければ最新のバージョンをインストールしましょう。
% rbenv install 2.7.2インストールしたバージョンを使うために以下のコマンドを実行します。
% rbenv global 2.7.2最後に
rbenvを読み込み、変更を反映させます。新しいバージョンの Ruby を入れたときや、実行ファイルを提供する gem を入れたあとには実行するようにしましょう。% rbenv rehashRailsを用意する
rubyの導入が終わり、せっかくなので
Railsの導入手順も説明していきます。bundlerのインストール
bundlerとは、gemのバージョンやgemの依存関係を管理してくれるgemです。bundlerを使うことで、複数人での開発やgemのバージョンが上がってもエラーを起こさずに開発できます。
% gem install bundlerbundler自体もgemなのでインストールが完了すると
1 gem installedと表示されるはずです。Railsのインストール
% gem install rails --version='6.0.0'バージョンを指定してインストールします。完了したら先ほど説明した
rbenv rehashコマンドを実行しましょう。参考
rbenv + ruby-build はどうやって動いているのか
https://takatoshiono.hatenablog.com/entry/2015/01/09/012040rbenv公式
https://github.com/rbenv/rbenv#basic-github-checkoutまとめ
rubyとRailsのインストール手順とそれぞれのコマンドの意味を見ていきました。あとはデータベース,yarn,Node.jsをインストールすればRailsを動かすことができるようになります
- 投稿日:2020-12-23T09:17:43+09:00
Railsの賞味期限を延ばす方法をまとめてみる
はじめに
年末になりアドベントカレンダーが盛り上がってますね。Railsのエントリーも賑わっています。
以下のエントリーに触発されて、大規模な開発になっても少しでもうまく開発が進む方法まとめてみようと思いました。
https://okuramasafumi.hatenablog.jp/entry/2020/12/16/224401
https://blog.unasuke.com/2020/i-have-to-learn-those-things-in-the-future/考え方
この記事では、DDDやアーキテクチャについて議論は取り扱わず、具体的なRubyのコーディング方法を中心にまとめていきます。
まず、原則的になぜRailsが大規模に向かないのか?開発が進むにつれしんどくなっていく理由について考えてみます。大規模開発に向かない理由:
- クラスやオブジェクトの参照箇所が正確に把握できない。このため、リファクタリングはもちろん機能追加などがやり辛く、開発のスピード・品質が悪くなっていく。こんな経験ありませんか?
- Gemのソースコードを追っていたけど、メソッドの定義がどこかわからなくなってしまう
- ネストが深すぎて処理内容がよくわからなくなるRubyのようなスクリプト型言語とコンパイル型言語の大きな違いの1つに動的な関数呼び出しがあり、これが規模が大きくなるにつれ開発を難しくする要因の大きない理由になっています。これを解決する手段として、DDDなどの手法を採用することも多いと思います。
参照箇所がなるべく明示的になるコーディングをする
参照箇所が明示的にわかるようにするには、大まかにいうと "メタプロを避ける" ということが言えます。
具体的には、
- 動的なメソッド定義を避ける=
-#respond_to?を使って処理を変えるコードは避ける
-#sendを使わない
- 静的な処理を動的に処理しない
(例: viewでActiveRecordのattributeを.eachを使って表示を作る )という感じです。
メタプロではありませんが、Railsの標準機能でも避けた方が良い機能もあります。
- Concernなどのモジュールのinclude/mixinは避ける 別クラスに移譲するべき
- ActiveRecordの
before_*/after_*を避ける サービスパターン、コマンドパターンなどで別クラスからメソッドをコールするコーディングルールを定める
より実践的な運用を考えると、ここまで紹介してきたようなポイントを、コーディングルールとして定めてチームで共有しておくと負債の増加が防げて望ましい状況ができます。
また、これらのことが守られているかはレビューでチェックする必要が出てきますが、レビュアーの負担が増えるため、 querly などのツールで検出することが理想的です。
これがあれば新規メンバー加入時も、チームの負担を少なく品質を守り、また新規メンバーもなるべく自力で馴染んでいくことができます。雑感
まとめてみるということで雑記帳のようになりましたが、そのうち統合的に再編集したい気持ちもあります。
ぜひ、よくわからないこと(避けるべき理由や対処法)や他にも気をつけた方が良いポイントなどコメントもらえたら嬉しいです
- 投稿日:2020-12-23T07:26:53+09:00
Payjp v2 ざっくり実装
最近Payjpのv2なるものがリリースされたらしく、Payjp側もセキュリティの向上のためv2の使用を推奨しているらしいです。
少し前に質問があり、気になったので実装してみました。忘れないようにメモとして記事作りました。
細かい説明は省きます。リファレンス見れば誰でも簡単に実装できます。v1との違いは?
v1ではトークン化APIのみを提供しており、カード情報入力フォームは加盟店側で用意する必要がありました。 しかしより安全なクレジットカード商取引のために、最新のPCI-DSSでは、カード情報入力フォームを決済代行業者側で用意することが求められています。 で、 payjp.js v2では、これらのフォームを弊社ドメインのiframe内で用意し、かつ独自スタイルの適用・イベント監視などv1で実現できた機能を提供いたします。 加えて、カード番号入力時の自動フォーマットやレスポンシブデザインなど、ニーズの高かった機能をデフォルトで提供致します。との事です。Payjp側で用意された入力フォームを使えるので、わざわざ作らなくて良い感じらしいですね。
ところでPCI-DSSってセキュリティの基準か何かの基準??
金融業界出身の方いたら、教えて欲しいです。ざっくりと実装
- application.html.hamlにv2のcdnを記載 %head %script{src: "https://js.pay.jp/v2/pay.js", type: "text/javascript"} .....payjp.jsコード
$(function(){ if(document.location.pathname !== "/cards/new") return false; // カード登録ページじゃい時は処理を実行しない const payjp = Payjp('pk_test_7370ce03239ee60f10ca694c') //公開鍵を読み込む。 // Payjp.setPublicKey('pk_...................')としなくて良いらしい const elements = payjp.elements(); // payjpのインスタンス生成 const cardElement = elements.create('card', {style: {base: {color: 'black'}}}) // ここでformを生成してる。createの第一引数には、「card」「cardNumber」「cardExpiry」「cardCvc」とかのタイプを選んで作れる。 // cardだとカード番号、有効期限、cvcの3つをまとめて横並びにしたフォームを生成する // フォームを分けたい人は「cardNumber」「cardExpiry」「cardCvc」を引数にして作ると良いです。 cardElement.mount('#card-element'); // 任意のセレクタ(#card-element)に対してiframe(入力フォームを付与する) const submit_btn = $("#info_submit") // いつものリファクタ submit_btn.click(function(e) { e.preventDefault(); // submitしないように止める payjp.createToken(cardElement).then(function(response) { if (response.error) { // Payjp側からの返ってくるオブジェクトがerrorオブジェクトを持ってた場合 // = 通信に失敗したとき alert(response.error.message) // どの情報に対して不備があるのか教えてくれる。 // 下記に記載しているが該当箇所のエラーを知らせてくれる regist_card.prop('disabled', false); // いつものやつ return ; } else { alert("カード登録が完了しました") $("#card_token").append( `<input type="hidden" name="payjp_token" value=${response.id}> <input type="hidden" name="card_token" value=${response.card.id}>` // これもいつものやつ ); cardElement.clear() // 入力情報を消す $('#card_form')[0].submit() } }) }); });ビュー
.mypage.horizontal-padding-15 = render 'shared/side_bar' .mypage-main .block.horizontal-padding-25 .block__menu 支払い方法 .block.horizontal-padding-25 #card-element -# ↑これ追加しただけ。この#card-elemntがマウントされるセレクタ(名前は何でも良い) #card-icons = image_tag 'icon_visa.png' = image_tag 'icon_master.jpg' = image_tag 'icon_saison.png' = image_tag 'icon_jcb.png' = image_tag 'icon_american.svg' = image_tag 'icon_diners.png' = image_tag 'icon_discover.png' = form_with model: @card, id: "card_form" do |f| #card_token = f.submit "次へ進む", class:"button back-red font-white", id: "info_submit"実際のカード情報入力ページ
入力に不備があると該当箇所に対してアラートもしっかりと出る(V1もできる)
使ってみた所感
好きな方を使いましょう!
あんまり変わらないです!























