- 投稿日:2019-12-30T23:30:00+09:00
RubyとRailsのバージョンの変更方法
Rubyのバージョン変更方法
①利用中のRubyバージョン確認
$ ruby -v②インストール可能なRubyバージョン確認
$ rbenv install --list③目的のバージョンをインストール(1-2時間かかります)
$ rbenv install 2.6.3④利用するバージョンを変更
$ rbenv global 2.6.3⑤現在コンソール上で利用しているRubyバージョン確認
$ rbenv versions⑥rehashしてあげる ※ファイルの場所を変更して使えるようにする
$ rbenv rehash指定したRailsのバージョンでプロジェクトを立ち上げる方法
①現在インストールされていて、使用できるRailsの確認
$ gem list rails②Railsの使用したいバージョンをインストール(使用したいバージョンが無かった場合)
$ gem install -v 5.1.7もう一度、使用できるRailsの確認をすると追加されている
$ gem list rails③バージョンを指定してプロジェクトを立ち上げる
$ rails _5.1.6_ new sample_app
- 投稿日:2019-12-30T22:30:06+09:00
【Rails】スクレイピングを使ってブログの記事一覧に記事内の写真を添える
はじめに
今回はRubyでスクレイピングに挑戦していきます。
前回の記事でブログを作成しました。
【Rails】summernoteを使って画像も投稿できるブログを作るブログといえばはてなブログですが、
(知り合いのお店のブログです)このようにその記事内で使われている写真が横に乗るようになっています。
これを実現してみたい。。
前回記事で使ったsummernoteですが、画像を投稿するとbase64でimgタグで直保存されます。
例えばこんな感じです
<p>ホゲホゲ</p> <p><img src="data:image/jpeg;base64,(長いので省略)" data-filename="4.jpg" style="width: 100px;"><br></p>ブログ内で写真が使われていればどこかにimgタグがあるはずなのでこれをブログの保存時に検知すれば良いのでは?
と僕は考えました。スクレイピングってすごくいろんな情報手に入るし、やれることの幅が広がりまくるとは思うんだけど、なんかやっていいサイトダメなサイトの判断とかよくわからんし手を出せてない
— さんぽし (@sanpo_shiho) December 28, 2019ということで、もっと簡単な方法もありそうなものですが、丁度スクレイピングにも興味があったのでやってみます。
実装!
こちらの記事を参考にしました。
https://morizyun.github.io/blog/ruby-nokogiri-scraping-tutorial/index.html
https://blog.takuros.net/entry/2014/04/15/070434僕はnokogiriがすでにインストールされていたのでされていない人は上記の記事を参考にインストールしてみてください。(Gemいじるだけなので簡単だと思います)
今回はDBに保存する前のデータを使用するので他のページから取ってくるという工程は不要になります。
imageというカラムに挿入することにします。
スクレイピングしてimgタグを:imageに保存
blog_posts_controller.rb@charset="UTF-8" def create @blog_post = BlogPost.new(blog_post_params) @blog_post[:created_by] = current_user.id @blog_post[:updated_by] = current_user.id doc = Nokogiri::HTML.parse(@blog_post[:content], nil, @charset) images = doc.at('//img') if images.instance_of?(Array) then @blog_post[:image] = images[0] else @blog_post[:image] = images end respond_to do |format| if @blog_post.save format.html { redirect_to @blog_post, notice: 'Blog post was successfully created.' } format.json { render :show, status: :created, location: @blog_post } else format.html { render :new } format.json { render json: @blog_post.errors, status: :unprocessable_entity } end end end
doc = Nokogiri::HTML.parse(@blog_post[:content], nil, @charset)
でblog_post[:content]
の内容全てをNokogiri::HTML::Document
に変換しています。その後の
images = doc.at('//img')
でimgタグの一番初めにヒットしたものを持ってきています。これで
blog_post[content]<p>ホゲホゲ</p> <p><img src="data:image/jpeg;base64,(長いので省略A)" data-filename="4.jpg" style="width: 100px;"><br> <img src="data:image/jpeg;base64,(長いので省略B)" data-filename="4.jpg" style="width: 100px;"> </p>このような
:content
の中身がblog_post[image]<p><img src="data:image/jpeg;base64,(長いので省略A)" data-filename="4.jpg" style="width: 100px;"><br></p>このように
:content
内の初めのimgタグの内容が:image
に保存されます。ブログの記事一覧の横に表示
blog_post/index.html.haml- @blog_posts.each do |blog_post| %table %thead %tr %th %th %tbody %tr %td{style: "max-width: 100px; height: auto;"} -if !blog_post.image.nil? = link_to blog_post.image.html_safe, blog_post -else =image_tag("hoge.jpg", :size => '100x100', style:"max-height: 100px") %td= link_to blog_post.title, blog_post %hr
style: "max-width: 100px; height: auto;"
を指定しているのは、そのまま表示すると:image
内の画像がでかすぎた時対策です。また、blog_post.content内にimgタグが存在しなかった時、blog_post.imageがnilになってエラーが出てしまうため、仮の画像を入れるようにしています。
終わりに
nokogiri便利でした。
これと似たような流れでその記事のはじめの方をダイジェストでだすということもできそうです。
- 投稿日:2019-12-30T20:48:07+09:00
【Rails】NoMethodError (undefined method `current_sign_in_at' for #<User:XXXXXXXXXXXXXX>):
はじめに
Railsでgem devise_token_authを導入時に発生した内容です。
Eメール認証の確認をしようと、
http://localhost:3000/api/v1/auth/sign_in
にPOSTリクエストを出すと以下エラーが発生しました。Started POST "/api/v1/auth/sign_in" for ... Processing by ... DeviseTokenAuth::SessionsController#create as JSON ... Completed 500 Internal Server Error in 311ms (ActiveRecord: 17.4ms | Allocations: 7912) NoMethodError (undefined method `current_sign_in_ip' for #<User:...>):※rails serverより抜粋。
このエラーに対して、自分がうまくいった解決法を残します。
環境
OS: macOS Catalina 10.15.1 Ruby: 2.6.5 Rails: 6.0.2.1結論:
user.rb
を修正Before
app/models/user.rbclass User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable include DeviseTokenAuth::Concerns::User end↓
After
app/models/user.rbclass User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable include DeviseTokenAuth::Concerns::User end
:trackable
を削除すると無事に通るようになりました。おわりに
最後まで読んで頂きありがとうございました
どなたかの参考になれば幸いです
参考にさせて頂いたサイト(いつもありがとうございます)
- 投稿日:2019-12-30T18:23:48+09:00
【Rails】summernoteを使って画像も投稿できるブログを作る
はじめに
summernoteを使って画像の投稿などにも対応したブログを作っていきたいと思います。
何か間違っている点があればコメントをください…!
レシピ
1. ER図を書く
データ構造をまとめます。ER図の作成はpgmodelerを僕は使用しています。
こんな感じになりました。
2. generator使って丸っと全体像を作成
Rails初心者の僕はgeneratorに頼りきっています。
$ rails generate scaffold Blog_post title:string content:text created_by:string updated_by:string3. migrationファイルの編集
not nullを指定したいのでmigrationファイルに直接書き込みます。
変更前
db/migrate/20191228075319_create_blog_posts.rbclass CreateBlogPosts < ActiveRecord::Migration[5.1] def change create_table :blog_posts do |t| t.string :title t.text :content t.string :created_by t.string :updated_by t.timestamps end end end変更後
db/migrate/20191228075319_create_blog_posts.rbclass CreateBlogPosts < ActiveRecord::Migration[5.1] def change create_table :blog_posts do |t| t.string :title, null: false t.text :content, null: false t.string :created_by, null: false t.string :updated_by, null: false t.timestamps null: false end end end4. migrationする
migrateファイルの編集も終わったのでmigrationをかけます。
$ bundle exec rake db:migrateこれでblog_postsテーブルがDB上に作成されます。
5. form.htmlを編集
created_by、updated_byがフォームで作成する感じになっているので内部での処理に置き換えます。
_form.html.haml= form_for @blog_post do |f| - if @blog_post.errors.any? #error_explanation %h2= "#{pluralize(@blog_post.errors.count, "error")} prohibited this blog_post from being saved:" %ul - @blog_post.errors.full_messages.each do |message| %li= message .field = f.label :title = f.text_field :title .field = f.label :content = f.text_area :content -- .field -- = f.label :created_by -- = f.text_field :created_by -- .field -- = f.label :updated_by -- = f.text_field :updated_by .actions = f.submit 'Save'(-部分を削除です)
6. created_by、updated_byを内部で入れる
# POST /blog_posts # POST /blog_posts.json def create @blog_post = BlogPost.new(blog_post_params) + blog_post[:created_by] = current_user.id + blog_post[:updated_by] = current_user.id respond_to do |format| if @blog_post.save format.html { redirect_to @blog_post, notice: 'Blog post was successfully created.' } format.json { render :show, status: :created, location: @blog_post } else format.html { render :new } format.json { render json: @blog_post.errors, status: :unprocessable_entity } end end end # PATCH/PUT /blog_posts/1 # PATCH/PUT /blog_posts/1.json def update + blog_post[:updated_by] = current_user.id respond_to do |format| if @blog_post.update(blog_post_params) format.html { redirect_to @blog_post, notice: 'Blog post was successfully updated.' } format.json { render :show, status: :ok, location: @blog_post } else format.html { render :edit } format.json { render json: @blog_post.errors, status: :unprocessable_entity } end end end(+を追加です)
7. summernoteの導入
summernoteを使用します。
このようなテキストエディタが導入できます。これで画像の投稿まで対応できます。
https://blog.seiyamaeda.com/12552
https://qiita.com/Kohei_Kishimoto0214/items/a7c75d105d530d81a678これらの記事を参考に導入します。(導入過程は省略)
_form.html.haml= form_for @blog_post do |f| - if @blog_post.errors.any? #error_explanation %h2= "#{pluralize(@blog_post.errors.count, "error")} prohibited this blog_post from being saved:" %ul - @blog_post.errors.full_messages.each do |message| %li= message .field = f.label :title = f.text_field :title .field = f.label :content = f.text_area :content, 'data-provider': :summernote .actions = f.submit 'Save' %div#summernote :javascript $(function () { $('[data-provider="summernote"]').each(function(){ $(this).summernote({ lang: 'ja-JP', height: 250, fontNames: ['Helvetica', 'sans-serif', 'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New'], fontNamesIgnoreCheck: ['Helvetica', 'sans-serif', 'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New'], }); }) })最終的にこのような形でformに実装します。
8. html_safeでhtml形式のcontentsがちゃんと表示されるようにする
summernoteによりDBに保存すると
<p>test</p>
のようにhtml形式で保存されます。generatorで作成されたshow, indexのままだとそのまま
なども表示してしまうのでこれを修正します。
contentに対してhtml_safeというメソッドをかけることで修正できます。
show.html.haml%p#notice= notice %p %b Title: = @blog_post.title %p %b Content: = @blog_post.content.html_safe = link_to 'Edit', edit_blog_post_path(@blog_post) \| = link_to 'Back', blog_posts_pathindex.html.haml%h1 Listing blog_posts %table %thead %tr %th Title %th Content %th %th %th %tbody - @blog_posts.each do |blog_post| %tr %td= blog_post.title %td= blog_post.content.html_safe %td= link_to 'Show', blog_post %td= link_to 'Edit', edit_blog_post_path(blog_post) %td= link_to 'Destroy', blog_post, method: :delete, data: { confirm: 'Are you sure?' } %br = link_to 'New Blog post', new_blog_post_pathついでにcreated_byなどの余計な情報は表示しないように変更しました。
終わりに
これで完成です。
summernoteを取り上げている記事少ないですけどめちゃ便利ですねこれ。
- 投稿日:2019-12-30T17:47:18+09:00
Ubuntu 19.10のRedmineでのいくつかのバグフィックスをした
Ubuntu 19.10のRedmineでのいくつかのバグフィックスをした
これまでもアップしたのも含め、Ubuntu 19.10のRedmineでは以下の不具合がありました。
致命的だったモーダルダイアログの件以外はちゃんとバグレポート上げれてないけど...。
- jQuery UIのバージョン違いのため、ダイアログが表示されない。
- jQuery UIのバージョン違いのため、Closeボタンの上に「Close」というテキストが表示されてしまう。
- ガントチャートでカミナリ線・関係線が表示されない。
- チケットのインポートができない。
他にも以下の便利機能を足してみました。
- インポート時の日付形式で「yyyy/mm/dd」形式を使えるようにする。
- ガントチャートの表示開始を前月にする。
- チケット新規作成時の担当者を自分にする
jQueryのバージョン違いについて
いくつかのメンバ関数がプロパティに変更されているようです。
モーダルダイアログやガントチャートのイナズマ線・関係線が表示されないのはこのためでした。
今回修正したのは以下のものです。
旧 (メンバ関数) 新 (プロパティ) zIndex() zIndex size() length 元々RubyもJavaScriptもちゃんと修めないまま修正にかかったので、もっとも手強かったのが「モーダルダイアログのクローズボタンに "Close" というテキストが表示されてしまう」というものでした。
Ubuntu (Debian) のディストリビューションではなぜか、jQeuryはシステムのlibjs-jqueryパッケージを使っているのに、jQuery UIはlibjs-jquery-uiパッケージ以外にもCSSでは一部ローカルコピーを使っていたりと、謎です。でも、jQuery UIを完全にUbuntuディストリビューションのものにするのもしんどかったので、とりあえずで、redmineパッケージにコピーしてあるJQuery UI関連のファイルを1.11.0からUbuntu 19.10のパッケージバージョンである1.12.1に置き換えました。
これで、Closeの文字も表示されなくなりました。変更したファイル
今回は、テキストファイルだけではなくバイナリファイルなんかも差し替えているので、単純な差分に収まりきらなかったためGitHubに上げておきました。
必要な方はあくまでも自己責任で、develブランチをcloneした上で、debパッケージを作ってインストールしてください。パッケージをビルドするには以下のコマンド (いくつかのパッケージのインストールが必要ですが)。
パッケージの作成に必要な前準備はここ:debパッケージ作成方法をステップバイステップでまとめました
パッケージの作成は以下のコマンド。
debuild -b
テキトーなインストールスクリプト。
install.sh#!/bin/bash version="4.0.4-1.ubuntu1.katsuya1" dpkg -i redmine_"$version"_all.deb \ redmine-mysql_"$version"_all.deb \ redmine-sqlite_"$version"_all.debMySQLでなくPostgreSQLの方は適当に書き換えてください。
これで、当面Redmineに感じていた課題感はクリアかな。
でもね
ライブラリを一本化したいというDebianの理想は十分に理解するものの、他にもバージョン違いに由来する不具合が残っていそうなので、素直にRedmineのディストリビューションで期待するjQueryをRedmineローカルで用意する方が良いような気がする...。
- 投稿日:2019-12-30T17:34:33+09:00
Railsチュートリアルメモ - 第1章
Railsチュートリアル(ローカル版)の第1章をやってみて気づいたことのメモを記載していきます
1.1 はじめに
割愛
1.2 さっそく動かす
ローカル(mac)で普通にチュートリアル通り実行していくと、最初の
printf "install: --no-document \nupdate: --no-document\n" >> ~/.gemrcgem install rails -v 5.1.6で
Fetching: concurrent-ruby-1.1.5.gem (100%) ERROR: While executing gem ... (Gem::FilePermissionError) You don't have write permissions for the /Library/Ruby/Gems/2.3.0 directory.となる。
ググると、system標準のrubyを使おうとしているため、権限エラーになっているようなので、rbenvをインストールして、別のrubyを使用(バージョンは2.5系で最新だった2.5.7を指定)brew install rbenv echo 'export PATH="~/.rbenv/shims:/usr/local/bin:$PATH"' >> ~/.bash_profile echo 'eval "$(rbenv init -)"' >> ~/.bash_profile source ~/.bash_profile rbenv install 2.5.7 rbenv global 2.5.7 rbenv versionsgem install rails -v 5.1.6今度は無事railsをインストールできた
1.3 最初のアプリケーション
1.3.1 Bundler
Gemfileとbundlerの使い方についての章
gemはrubyにおけるライブラリのこと。bundlerを使うとgemのバージョンを管理できる。
(pythonでいうところの、requirements.txtがGemfile、pipがbundler)
なお、bundler自身もgemとのこと。
Gemfile.lockというファイルがセットで存在し、.lockの方は実際にインストールしたgemが反映される。使い方は、
1. Gemfileに使用するgemを記載
2. bundle installでGemfileに記載されたgemをインストールしてくれるなお、GemfileとGemfile.lockに同じライブラリが記載されているがバージョンに差異がある場合(Gemfileのバージョンを変えたときなどに発生)には、bundle updateが必要。
bundle installはあくまで、未インストールのgemのインストールしかやってくれず、gemのバージョン更新はしないらしい。1.3.2 rails server
割愛
1.3.3 Model-View-Controller (MVC)
割愛(大事なところなので、MVCについてよく知らない人はちゃんと読んでください)
1.3.4 Hello, world!
コントローラーに変更を加え、ルーティング(URLと呼び出される処理のマッピング)を変える。
ルーティングはconfig/routes.rbに定義する。config/routes.rbRails.application.routes.draw do root 'application#hello' end↑に対して、application_controller.rbのhelloメソッドが呼び出される
1.4 Gitによるバージョン管理
割愛
1.5 デプロイする
herokuにデプロイしていく。
herokuのCLIが必要になるので、homebrewでインストールする(公式にcatalinaだとwarningが出ると書いてあったが、mojaveだと特に何も起きなかったので気にせず進める)brew tap heroku/brew && brew install herokuコマンドラインからログインし、sshキーをherokuに登録し、heroku createで新規アプリケーションを作成する
herokuにgit pushしようとすると以下のエラーが発生。
git push heroku master fatal: 'heroku' does not appear to be a git repository fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.調べてみると、ローカルのフォルダ構成が以下になってしまっていた。どうもheroku createすると、カレントディレクトリでgit initが実行され、.git/configのremoteにherokuが追加されるらしい。
tree . ├──.git/config └── environment └── hello_app └──.git/confighello_app配下の.gitディレクトリを削除し、プロジェクトルートで再度heroku create実行からやり直すと解消。
無事、herokuにデプロイできた。
1.6 最後に
割愛
- 投稿日:2019-12-30T17:31:28+09:00
Railsチュートリアルメモ 目次
はじめに
- ここ1年半くらいpythonをメインに触っていたが、転職サイトを眺めていたらまだまだRailsの需要が高い(特にスタートアップ界隈)なことに気づき、ちょうど年末で暇なので久々にRails チュートリアルを復習してみようと思った
- 以前学習したときはcloud9がawsに吸収される前だったのでcloud9を使うことに抵抗がなかったが、rails tutorialをやるためにわざわざawsにログインしてcloud9使うのは手間だなと思ったので、ローカルでやってみることにした(aws経由じゃなくても使える?みたいだが、まぁ普通にローカルの方がサクサク開発できるし)
- エディタはVSCodeを使用しています
- 章ごとに気づいたことをまとめていきます(途中で力尽きたらごめんなさい)
目次
第1章メモ
第2章メモ※後日記載予定
第3章メモ※後日記載予定
第4章メモ※後日記載予定
第5章メモ※後日記載予定
第6章メモ※後日記載予定
第7章メモ※後日記載予定
第8章メモ※後日記載予定
第9章メモ※後日記載予定
第10章メモ※後日記載予定
第11章メモ※後日記載予定
第12章メモ※後日記載予定
第13章メモ※後日記載予定
第14章メモ※後日記載予定
- 投稿日:2019-12-30T17:08:08+09:00
rails: Arrayでもwill_paginateを使えるようする方法
前提
gem pagenate
は事前に使える状態にしておく。bundle install
から使える状態までにする方法に関してはREADME
を参考にしてください。
FYI: will_paginate結論から言うと、デフォルトではwill_paginateはarray型の時は使えない。
そのため、config/initializers
の配下にちょっとした記述をする必要がある。
ファイル名はなんでもいいのでとりあえずファイルを作る。(ex:will_paginate_array.rb
)
ファイル作成後は以下の一文を追記する。# will_paginate_array.rb require 'will_paginate/array'記述後、一旦railsを再起動する。
で使えるようになるはず。
- 投稿日:2019-12-30T16:16:43+09:00
Railsチュートリアル 3章までのエラー(自分用メモ)
Rails チュートリアル 3章までのエラーと対処法
ProgateでRuby, Rails, Git, HTML&CSS 終了後にスタートしました。
事前知識は大学の授業でC言語を少し扱ったくらい。~1章
環境構築でのエラー
ProgateのRuby on Railsの環境構築の記事を見ながら進めていったとところ、rails new でフォルダが生成されず。
調べたところRails5.0以上ではNode.jsが必要なようで、インストールし解決しました。
Herokuへのデブロイ
その後1.5までは順調に進められましたがHerokuへのデブロイでエラー。
$ git push heroku master
で
$ heroku open
すると、Application errorの画面が。heroku logsでエラーの確認をしてもよく分からず、2日ほど試行錯誤。
その後あきらめてteratailで質問を投稿したところ、Rails6でのデブロイの方法がRails5までと異なるという回答を頂きました。そこで初めて自分がRails6を使っていることに気がつき、バージョンを指定してRails newからやり直し。Rails6でのデブロイは複雑だったのであきらめました。
しかしそれでもデブロイできず。
エラー文をよく見るとApp crashとありました。それを元に色々調べると、postgresqlを使用することを、
config/database.yml
で明記する必要があるようでした。
修正前
database.ymlproduction: <<: *default database: db/production.sqlite3修正後
database.ymlproduction: <<: *default adapter: postgresql database: db/production.postgresqlGemfileを書き換えるだけではだめだったようです。ここのエラーが今までで一番時間がかかり、3日くらい取られてしまいました。
2,3章
2章、3章ではそこまで大きくつまづくことはありませんでしたが、Gemfile変更後にbundle installや、Herokuへデブロイする前にcommitするのを忘れるミスがかなりありました。
まとめ
Progateは1周しかしておらず、かなり答えを見てしまうくらいの理解度だったのでまだRails チュートリアルは早いかと思いましたが、2週するのはめんどうだったので始めてみました。
期間は3章までで1週間くらい。今のところ少し時間がかかっていますが、1月中に終了を目標にがんばっていきます。
- 投稿日:2019-12-30T16:07:17+09:00
rails db:migrate でエラーが起きる時の対処法
今回は、マイグレーションをした時の対処法についてみていきます。
$ rails db:migrate -- create_table(:movies) rails aborted! StandardError: An error has occurred, this and all later migrations canceled: PG::DuplicateTable: ERROR: relation "movies" already existsこのようなエラーはすでに、同じ「movies」というテーブルが存在していますよ。っていう意味なので、moviesテーブルを削除してあげてれば問題ないです。
すべきこと
1.ターミナルで以下のように入力してあげる。
$ rails db2.#が出てくるので、その横に\dと入力してあげる
# \d3.テーブルがたくさん出てくるので該当のテーブルを削除してあげる。(今回は、moviesテーブルを削除する)
# drop table movies;4.再び,マイグレコマンドを入力する
$ rails db:migrateこれで、マイグレーションがうまくいきます。
もし、間違えているところがあればコメントしていただけると幸いです。
また、いいねを頂くと励みになりますのでよろしくお願い致します。
- 投稿日:2019-12-30T10:29:01+09:00
Rubyのキーワード引数はシンボルっぽく定義するけど、シンボルそのものではない、という話
はじめに
拙著「プロを目指す人のためのRuby入門(通称チェリー本)」の読者さんの中で、ときどきキーワード引数について困惑している人を見かけるので、この記事でキーワード引数に関する説明を補足しておきます。
Ruby初心者さんの疑問:キーワード引数はシンボルじゃないの?
Ruby初心者さんの中には、キーワード引数をシンボルとして参照できないことに疑問を持つ方がおられるようです。
たとえばこんな感じです。(「プロを目指す人のためのRuby入門」5.4.3項のコード例を一部改変)
def buy_burger(menu, drink: true, potato: true) # なんで :drink じゃないの? if drink # ... end # なんで :potato じゃないの? if potato # ... end endなるほど、そう言われてみると、メソッド定義のキーワード引数はたしかにキーがシンボルのハッシュっぽい書き方になってますね・・・。
# drink: true, potato: true の部分だけ見ると、 # たしかにキーがシンボルのハッシュっぽい def buy_burger(menu, drink: true, potato: true) # 実際、こう書けばキーがシンボルのハッシュになる hash = { drink: true, potato: true } hash[:drink] #=> true hash[:potato] #=> trueでも、これは記法が似ているだけであって、シンボルではないんです。
あくまでメソッドの引数(仮引数)なんです。ですので、上のメソッドの
menu
と同じように、メソッド内ではdrink
、potato
のように参照します。もし、
:drink
、:potato
のように書いてしまうと、これはシンボルオブジェクト(シンボルリテラル)を書いたことになります。def buy_burger(menu, drink: true, potato: true) # キーワード引数もメソッドの引数なので、menuと同様にdrinkと書く if drink # ... end # :drinkと書いた場合はメソッドの引数とは無関係な、ただのシンボルオブジェクトになってしまう if :drink # ... end # ... endとりあえず、この点については「キーワード引数はシンボルではないんです。定義するときの書き方はたしかに似てるけど、そういうもんなんです」としか説明のしようがありません。
なぜキーワード引数をシンボルっぽく書くのか?その歴史的経緯
とはいえ、キーワード引数をシンボルっぽく書くのは、おそらく歴史的な経緯があると考えています。
「プロを目指す人のためのRuby入門」の5.6.3項にも書いたように、キーワード引数が導入される前(つまりRuby 1.9以前)はハッシュを使って擬似的にキーワード引数を実現していました。
# ハッシュを引数として受け取り、疑似キーワード引数を実現する # (キーワード引数が導入される前はこう書くしかなかった) def buy_burger(menu, options = {}) # 引数のハッシュから目的の値をローカル変数に格納する drink = options[:drink] potato = options[:potato] # 引数から取り出した値を使って処理を書く if drink # ... end # ... end # このメソッド呼び出しではキーワード引数ではなく、 # { drink: true, potato: true } というハッシュオブジェクトを # 引数として渡している buy_burger('cheese', drink: true, potato: true)このようにRubyの世界では昔からハッシュとキーワード引数が近しい関係にあったため、キーワード引数の定義の仕方もハッシュの記法に似たものになったのだろう、と僕は考えています。
# 昔はみんなハッシュで書いてたんだから・・・ def buy_burger(menu, options = {}) # ... end # キーワード引数もハッシュっぽい記法になっていると自然(?) def buy_burger(menu, drink: true, potato: true) # キーワード引数にすると、このローカル変数が引数に置きかわるイメージ # drink = options[:drink] # potato = options[:potato] if drink # ... end # ... end # ちなみに、呼び出し方はどちらも変わりません buy_burger('cheese', drink: true, potato: true)ただ、この歴史的経緯を知っている人にとっては「うん、自然だね」と思えるものの、そうじゃない人にとっては「えっ、なんでシンボルじゃないの!?」とビックリする原因になるのかもしれませんね。
まとめ
というわけで、この記事では「Rubyのキーワード引数はシンボルっぽく定義するけど、シンボルそのものではない」という話を説明しました。
「プロを目指す人のためのRuby入門」の改訂版を出す機会があれば、この内容も追加しておこうと思います。
それまではこの記事を参考にしてもらえると幸いです。あわせて読みたい
Ruby 2.0でキーワード引数が導入されたときに書かれた記事です。
上で説明した「歴史的経緯」をより詳しく理解することができます。一方で、この「歴史的経緯」が原因で、Ruby本体のややこしい不具合の原因になってしまったという問題もあります。
そのため、Ruby 2.7以降ではキーワード引数の扱いが変更されています。
詳しくはこちらの記事をご覧ください。
- 投稿日:2019-12-30T08:23:07+09:00
Rubyの配列の要素をn個の配列へ順繰りに振り分け
配列や範囲オブジェクトなどに対し以下のように動作する
Enumerable#balance
を実装したい。ロードバランサーのラウンドロビン方式みたいな感じ。pp (:A..:Z).balance(7) # 均等にならない場合 #=> [[:A, :H, :O, :V], # [:B, :I, :P, :W], # [:C, :J, :Q, :X], # [:D, :K, :R, :Y], # [:E, :L, :S, :Z], # [:F, :M, :T], # [:G, :N, :U]] pp (:Α..:Ω).balance(4) # 均等になる場合 #=> [[:Α, :Ε, :Ι, :Ν, :Ρ, :Φ], # [:Β, :Ζ, :Κ, :Ξ, :Σ, :Χ], # [:Γ, :Η, :Λ, :Ο, :Τ, :Ψ], # [:Δ, :Θ, :Μ, :Π, :Υ, :Ω]] pp [].balance(3) # 空配列の場合 #=> [[], [], []]
作ったらこうなった。
module Enumerable def balance(n) m = nil each_slice(n).to_a.tap do |*, la| return Array.new(n) { [] } unless la m = la.size la[n-1] = la[n-1] # nil padding end.transpose.tap do |ta| ta.drop(m).each(&:pop) # remove padding end end end
- 基本戦略は
each_slice(n).to_a.transpose
。- しかし、転置
Array#transpose
は各要素の大きさが揃っていることが必要。- なので転置の前後でパディングの追加と削除を実施する。
- 転置前は最終行の配列長を他と同じになるよう増やす。(参考:不足分をnilで埋めた配列を取得)
- 転置後は増やした分だけ各行の末端を削る。(※
#compact
だと元々あったnilまで消してしまう)- 細かい配列操作は破壊的に行い、なるべく新しいインスタンス生成を抑える。
Object#tap
が活躍。- 空配列の転置は都合が悪いので、早めに検知してreturnする。(参考:空の配列に対するRubyのメソッドの挙動)
無限のリストでも処理できるようにn個のEnumeratorを返すことも考えたが、使い勝手や効率がよくわからないので保留。
module Enumerable def lazy_balance(n) enum = lazy Array.new(n) { |i| enum.drop(i).each_slice(n).map(&:first) } end endこれは
Enumerator::Lazy
の配列を返しているため、最後に各要素を#force
(#to_a
)すれば最初の例と同じ結果が得られる。pp (:A..:Z).lazy_balance(7).map(&:force) pp (:Α..:Ω).lazy_balance(4).map(&:force) pp [].lazy_balance(3).map(&:force)
- 投稿日:2019-12-30T00:48:40+09:00
プログラミング初心者がRailsでサービスを作ってみる
まえがき
プログラミング初心者がRuby on Railsで何か一つサービスを作ってみる。
※職場のエンジニアの方の協力を得つつ…□前提
▼プログラミングスキル
- ProgateでRilasを1周している。
- 「いきなりはじめるPHP入門」で少しだけPHPをやったことがある。
▼ステータス
- Ruby on Railsで書かれているサービスのプロダクトマネージャーをやっている。
- 開発ディレクションっぽいことをやっている(けど、コードとかに関する知識がなく、もやもやしている。)
□目標(ゆるく)
- 自分でサービスを作ってみることで、ディレクションスキルを上げる。
- 企画〜リリースまでの手順を、実体験をもとに把握する。
- なんとなくコードが読める。工数がわかる。
□計画(ゆるく)
- 12月からはじめて4月くらいでリリースできるといいな。
- 12月
- ドットインストール見る。
- Progateやる。
- チュートリアル見る。
□つまずいたところたち※随時更新
▼12/29
Railsが立ち上がらない。
ActiveRecord::ConnectionNotEstablished No connection pool with ‘primary’ found.
→これを参考に解決。Railsを始めてsqlite3まわりのエラーで躓いている人たちへ。このコマンドも叩いた。「$ rails db:create」▼12/30
初期画面以降のプレビュー画面の開き方がわからない。
Progateの教材をCloud9でやろうとした。プレビュー画面の開き方がわからなかった。
→Cloud9でPreview Runing Applicationを選択。開いたページのURLの末尾に/Top/homeなど自分の作ったページのURLを指定して解決。□ログ※随時更新
- 投稿日:2019-12-30T00:10:06+09:00
Railsの新規プロジェクトを指定のRubyバージョンで作成する。
はじめに
久しぶりにRailsの新しいプロジェクトを作成するに当たり、Rubyバージョンを最新のものを利用しようとしてつまずいたので、今後の自分のためにも記事として残しておく。
環境
・rbenvはすでにインストールされているものとする。
https://qiita.com/Alex_mht_code/items/d2db2eba17830e36a5f1
・macOS
Catalina1. プロジェクトを運用するディレクトリを作成する。
$ mkdir app_name $ cd app_name2. Rubyのバージョンを最新にする。
現在使用しているバージョンを確認する
$ rbenv versions system 2.5.1 * 2.6.5以下のコマンドでインストール可能なバージョンリストが表示される。
$ rbenv -lインストールしたいバージョンがない場合は以下のコマンドでアップデート
(2019/12/29時点では安定の最新バージョンは2.7.0)$ brew update$ rbenv install 2.7.0インストールが完了すると以下のように表示が増える
$ rbenv versions system 2.5.1 * 2.6.5 2.7.0現在はバージョン2.6.5が指定されているので、2.7.0を指定する。
このときコマンドが以下の2パターンあるので使い分けてください。$ rbenv local 2.7.0 これは現在のディレクトリのプロジェクトのみに適用。 $ rbenv global 2.7.0 その他のプロジェクトのバージョンも全てに適用。僕は別のアプリを2.6.5で作っているのでlocalで実行。
3. Railsのプロジェクトを作成する。
$ bundle initGemfileが生成される。
開いて以下のように編集# gem rails → gem rails (コメントアウトを外して保存)$ bundle installここでオプションを付けるかどうかも議論はありますが、プロジェクトが多くなってきてスキルが上がってきたときに考えたほうが理解できると思います。
ざっくりとした説明としてはオプションを付けたほうが容量を節約した運用ができるよということ。以下のコマンドでプロジェクトを作成。DBの選択などオプションはありますが、シンプルに作っていきます。
rails new .Gemfileを上書きするか聞かれるのでyを入力し、return。
今回は以下のメッセージが出たので対応。
Yarn not installed. Please download and install Yarn from https://yarnpkg.com/lang/en/docs/install/こちらを参考に。
https://qiita.com/libertyu/items/1eb74adc817ab8971100
以下のコマンドを実行。$ brew install yarn続いて
$ rails webpacker:installこれで準備OK! 以下で起動!
$ rails s以下にアクセスして起動を確認。
http://localhost:3000/まとめ
一つ一つのコマンドの意味を理解してくことは、とても大切です。
一方で、完璧に理解して進むことは時間がいくらあっても足りません。
今回はコマンド入力しただけで進んだ、次回はそのうちの一つのコマンドについて調べて理解が進んだ状態で進んだというように
毎回毎回少しずつ学習していくことがいいのではないかと考えています。もし、上記のコマンドでうまく行かなかった際はつまずいた箇所を指摘していただけると幸いです。
共に頑張っていきましょう!
- 投稿日:2019-12-30T00:01:10+09:00
ajaxのフォローボタンが反応しないエラー(500 Internal Server Error)
フォローできない 500 Internal Server Error
ajaxのフォローボタンが反応しない。データベースも変更がない。
consolelogのエラー内容
app/models/user.rb:44 Completed 500 Internal Server Error in 32ms (ActiveRecord: 5.2ms) ActiveRecord::RecordNotUnique (SQLite3::ConstraintException: UNIQUE constraint failed: relationships.follower_id, relationships.followed_id: INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)): app/models/user.rb:44:in `follow' app/controllers/relationships_controller.rb:6:in `create'対象のコード
class RelationshipsController < ApplicationController before_action :user_signed_in? def create @user = User.find_by(params[:followed_id]) # 対象ユーザーを受け取る current_user.follow(@user) # 対象ユーザーをフォローする respond_to do |format| format.html { redirect_to @user } format.js end end def destroy @user = Relationship.find(params[:id]).followed # Relationshipからidを取得 current_user.unfollow(@user) respond_to do |format| format.html { redirect_to @user } format.js end end endclass User < ApplicationRecord has_many :posts, dependent: :destroy has_many :likes has_many :comments has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", \# デフォルトではuser_idに紐づけられるので指定 dependent: :destroy has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", \# デフォルトではuser_idに紐づけられるので指定 dependent: :destroy has_many :following, through: :active_relationships, \# active_relationshipsメソッドを呼び出し source: :followed \# 各要素に対してfollowedメソッドを実行 has_many :followers, through: :passive_relationships, \# active_relationshipsメソッドを呼び出し source: :follower \# 各要素に対してfollowedメソッドを実行 \# Include default devise modules. Others available are: \# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable validates :name, presence: true, length: { maximum: 50 } validates :email, length: { maximum: 255 } validates :password, length: { minimum: 6 } def update\_without\_current_password(params, *options) params.delete(:current_password) if params\[:password\].blank? && params\[:password_confirmation\].blank? params.delete(:password) params.delete(:password_confirmation) end result = update_attributes(params, *options) clean\_up\_passwords result end \# フォロー機能のメソッド \# ユーザーをフォローする def follow(other_user) following << other_user end \# アンフォロー def unfollow(other_user) active_relationships.find_by(followed_id: other_user.id).destroy end \# 既にフォロー済みのユーザーに含まれていないか確認 def following?(other_user) following.include?(other_user) end end原因
どうもこのファイルの中に問題がありそう。
500はコントローラー側に問題があるエラーの様解決方法
@user = User.find_by(params[:followed_id]) # 対象ユーザーを受け取るが
find_by
でなくfind
が正解だった。修正後問題なく動作しフォロー機能挙動。
備考
ちなみに
find
メソッドとfind_by
メソッドの違いは、、、
find
メソッド
- 検索したいレコードを全て抽出する。
- マッチするレコードが無い場合はエラーとなる。
find_by
メソッド
- 検索したいレコードの最初に一致した1件だけを抽出
- マッチするレコードが見当たらない場合nilが返ってくる。
使い分け
find
→IDがわかっている場合。
find_by
IDが不明且つ別の条件でレコードを検索したい場合。