- 投稿日:2020-08-20T23:11:23+09:00
Railsチュートリアル 5章最後でHerokuが動かなくなる
Railsチュートリアルをもりもりとやっています。
5章に来て初めて本格的に躓いてしまったので、備忘録として記録します。herokuが動かない・・・
5章の最後に'git push heroku'をしたところこの画面がでました。
Application error An error occurred in the application and your page could not be served. If you are thee application owner, check your logs for details. You can do this from the Heroku CLI with the command heroku logs --tail対処
ひとまず色々検索して以下のコマンドを打ちました。
heroku logsherokuのログの出力
見るべきポイント:errorと書かれている行>>$ heroku logs #ログ出力 : (中略) #errorと書かれている箇所を探す : 2020-08-20T12:51:51.649616+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=enigmatic-harbor-30759.herokuapp.com request_id=6cf41616-a067-4dc6-8d2c-696895e1b968 fwd="60.76.208.83" dyno= connect= service= status=503 bytes= protocol=https :
heroku run rails crailsでコンソールを起動する
見るべきポイント:エラーになっているファイル、エラー原因>>$ heroku run rails c #コンソールの起動 : (中略) #ファイル名が書かれている箇所を探す : 4: from /app/app/helpers/application_helper_test.rb:1:in `<top (required)>' : :#最終的なエラーを確認する /app/vendor/bundle/ruby/2.6.0/gems/activesupport-5.1.6/lib/active_support/dependencies.rb:292:in `require': No such file to load -- test_helper.rb (LoadError)以上の内容でわかったこと
・logを見る限りだと、よくわからない・・・
・console起動でみると、どうやらapplication_helper_test.rbが悪さをしている?
・上のファイルがtest_helper.rbを呼んでるけど、ファイルがないと言われている。結論
application_helper_test.rbがtest_helper.rbを参照できないようフォルダ構成になっていまいました。
本来であれば、testsフォルダ内にあるべきapplication_helper_test.rbファイルが別のフォルダ(app)に入っていたようです。
ひとまず、ファイルを移動させ、再度実行するとエラーがなくなりました。感想
エラーが出た時の、ログの読み取りがうまく出来なかったのでてこずってしまいました・・・。改めて整理してみて、案外読み取るポイントってシンプルなんだなと改めておもいました。
- 投稿日:2020-08-20T22:16:08+09:00
privateメソッドとprotectedメソッド
【概要】
1.結論
2.”private”と”protected”とは何か
3.どのように”private”と”protected”を使うのか
4.ここから学んだこと
1.結論
状況に応じて使い分けるが、ほぼprivateメソッドを使えばいい!
2.”private”と”protected”とは何か
publicは制限がないメソッドですが、private,protectedの違いがわからず悩みました。
private:
クラス外からは呼び出せない。同じインスタンス内で呼び出せる。protected:
クラス外からは呼び出せない。同じインスタンス内で呼び出せる。しかし、他のインスタンスであれば同じクラスであれば呼び出せる。ということですが、3.の例を見て
理解していきましょう。3.どのように”private”と”protected”を使うのか
privateclass saturday def day holiday end private def holiday puts "今日は休日です" end end human=Human.new human.holidayprotectedclass school def class (student) @student = student end def brother(human) puts "#{class_brother}と#{human.class_brother}は兄弟です。" end end protected def class_brother @student end studentA=Student.new studentA.school("桜木") studentB=Student.new studentB.school("山田") studentA.brother("山田 ")もちろんprivateのプログラムでも、
protectedが適用できます!
ただ、protectedの方は、同じクラス内でも
違うメソッド(他のインスタンスであれば同じクラスであれば呼び出せる)のため、protectedのみでしか適用できません。4.ここから学んだこと
i)privateは使用頻度が高いと思いますが、protectedは使う範囲・頻度が限られそうです。
ii)邪魔されたくないメソッドや、影響されたくないメソッド(インスタンス変数)を使う時にはものすごく便利なのでこれからも使いつつ、プログラムを学んでいきます!
- 投稿日:2020-08-20T21:55:14+09:00
RuboCopの導入と基本的な使用方法【ポートフォリオ備忘録①】
はじめに
現在、ポートフォリオとしてオリジナルアプリを作成中ですが、自分一人でコードを書いているとついつい自分が読めれば問題ないコードを書きがちになってしまいます。
変な癖が付かないように、その辺も意識していきたいということで、RuboCopを導入することにしました。導入で学んだことを忘れないようにここに綴ります。
RuboCopとは
RuboCopとは、Ruby向けの静的解析ツールである。Ruby Style Guideのコーディング規約に従って、コードが書かれているかチェックするためのgemである。
参考:RuboCop // DocsRuboCopをインストール
Gemfileに記入
Gemfilegroup :development do gem 'rubocop', require: false gem 'rubocop-rails', require: false endインストール
terminal$ bundleRuboCopを使ってみる
terminal$ bundle exec rubocop(余談ですが、bundle execって何?という時は bundler、bundle execについて ※自分用メモ の記事がオススメ。)
なんだかエラーがたくさん出てしまった・・・
直さなければいけないところが書かれているので親切だけど、全部直すには時間がかかり過ぎてしまう。ということで以下のコマンドを実行
$ rubocop --auto-gen-configすると、.rubocop_ymlと.rubocop_todo.ymlの2つのファイルが生成される。
.rubocop_todo.yml
.rubocop_todo.ymlファイルの生成によって、先ほど大量に出た警告が一旦無視されるように設定される。すなわち.rubocop_todo.ymlに書かれているかぎり、RuboCopに怒られなくて済むということである。
試しにもう一度 budle exec rubocop を実行すると、警告が出なくなっている。
とはいえあくまでも今は一時的に警告を回避しているに過ぎないので、このファイルに何も書かれていなくてもRuboCopから警告文が表示されない状況に持っていくことが最終的な目標となる。
警告をなくす
rubocop_todo.ymlBundler/OrderedGems: Exclude: - 'Gemfile' Layout/SpaceInsideArrayLiteralBrackets: Exclude: - 'config/environments/production.rb' Layout/TrailingEmptyLines: Exclude: - 'Gemfile'例えば、.rubocop_todo.ymlに上記のような記載があったら、まずは
Bundler/OrderedGems:
Exclude:
- 'Gemfile'をコメントアウトして、bundle exec rubocop を実行。すると警告文が出る。
このあとの手順は2パターンある。
①警告に従って、コードを修正する
②コードは変えず、警告のデフォルト変更・もしくは今後警告を出さないように設定する①は文章通り、該当ファイルを開いて、コードを修正するということ。
.rubocop.yml
②については、先ほど .rubocop_todo.ymlと同時に生成された .rubocop.ymlに設定を書いていく。
- RuboCopの対象から外す時
rubocop.ymlinherit_from: .rubocop_todo.yml AllCops: # 除外するファイル Exclude: - "config/environments/*" - "vender/**/*" - "bin/**" - "db/**/*" - "tmp/**/*" - "lib/**/*" - "Gemfile"自分でExcludeを設定する時は、デフォルトの設定が外れてしまうため、それらも改めて明記する必要がある。
ちなみに、上記のファイルの最上部にinherit_from: .rubocop_todo.yml があるが、この一文がないと、.rubocop_todo_ymlに書かれた内容が反映されないので注意。
- Copの無効化
Copとは、RuboCopにおける1つのルールのことである。
上から3つ目までのCopは、有効にするか無効にするかを記述しないとエラーが出るので書く。rubocop.ymlStyle/HashEachMethods: Enabled: true Style/HashTransformKeys: Enabled: true Style/HashTransformValues: Enabled: true # 日本語のコメントを許可する Style/AsciiComments: Enabled: false警告文に対してどちらかの対処を行ったら、コメントアウトした警告を削除する。
.rubocop_todo.ymlの中身がなくなるまでこの繰り返し。まとめ
- .rubocop.yml:コーディングのルールを記載
- .rubocop_todo.yml:現在の違反部分を記載 (名前の通りToDoリスト的な役割だ!)
.rubocop_todo.ymlの違反を1つずつチェックして、コードを修正もしくは新たなルールを作って.rubocop.ymlに記載するのがRuboCopの基本的な使い方である。
おわりに
とにかく今後はRuboCopを適宜利用して、規約に沿ったコードを書いていきたいと思います。本記事で何かツッコミどころ、おかしな点などあればご教示いただけると幸いです。
尚、本記事は以下を参考にしています。
- 投稿日:2020-08-20T21:55:14+09:00
RuboCopの導入と基本的な使用方法【ポートフォリオ①】
はじめに
現在、ポートフォリオとしてオリジナルアプリを作成中ですが、自分一人でコードを書いているとついつい自分が読めれば問題ないコードを書きがちになってしまいます。
変な癖が付かないように、その辺も意識していきたいということで、RuboCopを導入することにしました。導入で学んだことを忘れないようにここに綴ります。
RuboCopとは
RuboCopとは、Ruby向けの静的解析ツールである。Ruby Style Guideのコーディング規約に従って、コードが書かれているかチェックするためのgemである。
参考:RuboCop // DocsRuboCopをインストール
Gemfileに記入
Gemfilegroup :development do gem 'rubocop', require: false gem 'rubocop-rails', require: false endインストール
terminal$ bundleRuboCopを使ってみる
terminal$ bundle exec rubocop(余談ですが、bundle execって何?という時は bundler、bundle execについて ※自分用メモ の記事がオススメ。)
なんだかエラーがたくさん出てしまった・・・
直さなければいけないところが書かれているので親切だけど、全部直すには時間がかかり過ぎてしまう。ということで以下のコマンドを実行
$ rubocop --auto-gen-configすると、.rubocop_ymlと.rubocop_todo.ymlの2つのファイルが生成される。
.rubocop_todo.yml
.rubocop_todo.ymlファイルの生成によって、先ほど大量に出た警告が一旦無視されるように設定される。すなわち.rubocop_todo.ymlに書かれているかぎり、RuboCopに怒られなくて済むということである。
試しにもう一度 budle exec rubocop を実行すると、警告が出なくなっている。
とはいえあくまでも今は一時的に警告を回避しているに過ぎないので、このファイルに何も書かれていなくてもRuboCopから警告文が表示されない状況に持っていくことが最終的な目標となる。
警告をなくす
rubocop_todo.ymlBundler/OrderedGems: Exclude: - 'Gemfile' Layout/SpaceInsideArrayLiteralBrackets: Exclude: - 'config/environments/production.rb' Layout/TrailingEmptyLines: Exclude: - 'Gemfile'例えば、.rubocop_todo.ymlに上記のような記載があったら、まずは
Bundler/OrderedGems:
Exclude:
- 'Gemfile'をコメントアウトして、bundle exec rubocop を実行。すると警告文が出る。
このあとの手順は2パターンある。
①警告に従って、コードを修正する
②コードは変えず、警告のデフォルト変更・もしくは今後警告を出さないように設定する①は文章通り、該当ファイルを開いて、コードを修正するということ。
.rubocop.yml
②については、先ほど .rubocop_todo.ymlと同時に生成された .rubocop.ymlに設定を書いていく。
- RuboCopの対象から外す時
rubocop.ymlinherit_from: .rubocop_todo.yml AllCops: # 除外するファイル Exclude: - "config/environments/*" - "vender/**/*" - "bin/**" - "db/**/*" - "tmp/**/*" - "lib/**/*" - "Gemfile"自分でExcludeを設定する時は、デフォルトの設定が外れてしまうため、それらも改めて明記する必要がある。
ちなみに、上記のファイルの最上部にinherit_from: .rubocop_todo.yml があるが、この一文がないと、.rubocop_todo_ymlに書かれた内容が反映されないので注意。
- Copの無効化
Copとは、RuboCopにおける1つのルールのことである。
上から3つ目までのCopは、有効にするか無効にするかを記述しないとエラーが出るので書く。rubocop.ymlStyle/HashEachMethods: Enabled: true Style/HashTransformKeys: Enabled: true Style/HashTransformValues: Enabled: true # 日本語のコメントを許可する Style/AsciiComments: Enabled: false警告文に対してどちらかの対処を行ったら、コメントアウトした警告を削除する。
.rubocop_todo.ymlの中身がなくなるまでこの繰り返し。まとめ
- .rubocop.yml:コーディングのルールを記載
- .rubocop_todo.yml:現在の違反部分を記載 (名前の通りToDoリスト的な役割だ!)
.rubocop_todo.ymlの違反を1つずつチェックして、コードを修正もしくは新たなルールを作って.rubocop.ymlに記載するのがRuboCopの基本的な使い方である。
おわりに
とにかく今後はRuboCopを適宜利用して、規約に沿ったコードを書いていきたいと思います。本記事で何かツッコミどころ、おかしな点などあればご教示いただけると幸いです。
尚、本記事は以下を参考にしています。
- 投稿日:2020-08-20T21:25:31+09:00
Yardを閲覧するだけのGUIアプリをRubyで作る
はじめに
ここでは1-2年前にGUIの練習のために作ったyardを見るだけのソフトを振り返って、RubyでどうやってGUIソフトを作るんだったかというのを思い出して、自分用テンプレートにまとめたいと思います。この記事は
ruby/gtk3について中級的な内容をまとめています。初心者の方はこちらを見てください。【Ruby】10分でGladeを使って作るRuby/GTK3 GUIアプリ
このソフトは、ローカルにインストールされたgemを表示するだけのやつなので、かならずしも実用的ではないよ。
Github : https://github.com/kojix2/yardview
ちょっと透けるようになっています。コードではなくGladeの設定でいじっています。
一応Gemで試せます。(環境によっては動かないかも)
gem install yardview_gtk3起動
yardview元ネタ
Lazarus Lazaridisさんの記事。Todoアプリをつくています。
- https://iridakos.com/programming/2018/01/25/creating-a-gtk-todo-application-with-ruby
- https://github.com/iridakos/gtk-todo-tutorial
この元ネタの英語の内容がすらすら読める人はそれで十分なのですが、この記事にもシグナルハンドリングで少し楽をする方法など、元ネタ以上の内容が少し含まれています。
Gemにする
実行ファイルを追加するオプション
--exe-bbundle gem yardview --exeいつもどおり
gemspecファイルを編集gemspecspec.add_dependency 'yard' spec.add_dependency 'gtk3' spec.add_dependency 'webkit2-gtk' spec.add_development_dependency 'bundler' spec.add_development_dependency 'rake'ユーザーはRubyに詳しい → Gemfile.lock は含めない
ユーザーはRuby関係ない → Gemfile.lock 残すディレクトリ構成
. ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── exe │ └── yardview ├── lib │ ├── yardview │ │ ├── application.rb │ │ ├── application_window.rb │ │ └── version.rb │ └── yardview.rb ├── resources │ ├── LICENSE │ ├── gresources.xml │ ├── ruby.png │ ├── yardview.ui │ └── yardview_screenshot.png └── yardview.gemspec面倒なので、
exe/yardviewに直接いろいろ書いています。ポイントは、途中で
glib-compile-resourcesを実行しているところです。GResourceというGnomeのリソースフレームワークを利用します。このコマンドにパスが通ってない場合は実行できないと思うので、何らかの対策が必要かも知れません。Windowsだとどうなんだろう…。あと、毎回リソースをコンパイルして、終了時に捨てる方式が本当にいいのかという問題はある気がします。yardview#!/usr/bin/env ruby require 'optparse' require 'yardview' require 'fileutils' port_num ||= 8808 opt = OptionParser.new opt.program_name = 'Yardview' opt.version = Yardview::VERSION opt.on('-p', '--port VAL', Integer) { |v| port_num = v } opt.parse!(ARGV) resource_xml = File.expand_path('../resources/gresources.xml', __dir__) resource_bin = File.expand_path('../gresource.bin', __dir__) system('glib-compile-resources', '--target', resource_bin, '--sourcedir', File.dirname(resource_xml), resource_xml) at_exit do FileUtils.rm_f(resource_bin) end app = Yardview.application resource = Gio::Resource.load(resource_bin) Gio::Resources.register(resource) app.signal_connect :activate do |application| window = Yardview::ApplicationWindow.new application, port: port_num window.present end app.run今回はウェブページを表示するので
webkit2-gtkを追加します。lib/yardview.rbrequire 'yardview/version' require 'yardview/application' require 'yardview/application_window' require 'webkit2-gtk' module Yardview endGlade側でシグナルのハンドラーの名前を指定しておきます。
使用するファイルを書いておく。
gresource.xml<?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/com/github/kojix2/yardview"> <file preprocess="xml-stripblanks">yardview.ui</file> <file>ruby.png</file> </gresource> </gresources>ここがミソなのですが、クラス変数にApplicationのインスタンスを詰め込んで参照できるようにしておきます。
lib/yardview/application.rbrequire 'gtk3' module Yardview class << self def application @@application ||= Gtk::Application.new('com.github.kojix2.yardview', :flags_none) end end end
type_register,set_template,bind_template_childset_connect_funcあたりが重要なおまじないです。
type_registerはGlibやGObjectの世界と統合につかわれるそうです。(正直よくわかっていません)
set_templateでuiファイルを指定します。ファイルパスではなく、GResourceから指定していることに注意してください。
set_template_childは、widgetにアクセスできるメソッドを提供します。下の方で変数のように登場するboxは実はwidgetにアクセスするためのメソッド呼び出しです。
set_connect_funcは自動でシグナルをRubyのメソッドにつないでいます。(ここは元ネタではやっていない部分です)lib/yardview/application_window.rbrequire 'gtk3' module Yardview class ApplicationWindow < Gtk::ApplicationWindow type_register def self.init set_template resource: '/com/github/kojix2/yardview/yardview.ui' bind_template_child 'box' set_connect_func do |handler_name| lambda do Yardview.application.active_window.__send__(handler_name) end end end attr_accessor :port def initialize(application, port: port_num) super application: application set_title 'YardView' set_icon GdkPixbuf::Pixbuf.new resource: '/com/github/kojix2/yardview/ruby.png' @port = port start_yard_server create_gui end def create_gui signal_connect('destroy') do Process.kill(:INT, @yard) @yard = nil end at_exit { Process.kill(:INT, @yard) unless @yard.nil? } @view = WebKit2Gtk::WebView.new @view.load_uri("http://localhost:#{port}") box.add @view, expand: true, fill: true @view.show end def port_open?(port) !system("lsof -i:#{port}", out: '/dev/null') end def on_home_clicked @view.load_uri("http://localhost:#{port}") end def on_back_clicked @view.go_back end def on_top_clicked @view.run_javascript('window.scrollTo(0,0);') end def on_refresh_clicked @view.reload end def start_yard_server if port_open? port @yard = spawn("yard server -g -p #{port} --reload") sleep 1 else raise "port #{port} is in use!" end end end endRubyでGUIアプリを作っていると問題になりやすいのは非同期っぽい処理ですが、コマンドの実行だけでなんとかなってしまうものは
spawnがオススメです。比較的プラットフォームを問わずに動作してくれます。以上、書いている本人もよくわかっていないところが多い記事ですが、だいたいこんな感じ書けば動きます。
これは積極的にGnomeの機能を使っていこうという感じのテンプレートです。いやいやGResourceとかいらないし、GObjectとRubyのオブジェクトを統合する必要もないし、シグナルハンドリングも毎回ベタ書きすればいいじゃん、ということならもっと簡単にできるとは思います。
この記事は以上です。
- 投稿日:2020-08-20T21:22:36+09:00
[Ruby]簡易ポーカー作ってみました。アドバイスください
Ruby で簡易ポーカー(トランプは四枚だけど)を作ってみました。
が、あまりにもわからずに可読性落ちまくりのプログラムになってしまったので
よろしければアドバイスください。qiita.rbn = gets.to_i poker = [] i = 0 check = 0 n.times do number = gets.chomp poker.push(number.chars) end while n > 0 check = poker[i].uniq.map{ |e| [e, poker[i].count(e)] } case check.size when 4 then puts "No Pair" when 3 then puts "One Pair" when 2 then if check[0][1] == 3 puts "Three Card" else puts "Two Pair" end when 1 then puts "Four Card" end i = i + 1 n = n - 1 endブロック変数の理解が浅いことが悩みです笑
- 投稿日:2020-08-20T18:40:45+09:00
ActionControllerエラー集(Ruby on Rails)※随時更新
NoMethodError in [コントローラー名]#[アクション名]→undefined method `[キーワード]' for #<>
→[コントローラー名]の[アクション名]に[キーワード]のメソッドが定義されていない。
Sprockets::Rails::Helper::AssetNotFound in [コントローラー名]#[アクション名]→The asset "" is not present in the asset pipeline.
unknown attribute '[カラム名]' for [テーブル名].
→[テーブル]に[カラム]が見つからない。
【原因】
引っ張ってきたインスタンス変数やテーブルのカラムにないカラム名を指定してしまっている。私の場合編集画面で、
edit.html.haml= form_with model: @item, do |f| = f.file_field :[カラム名]で指定したカラムがテーブルに存在していませんでした。
【対処】
①マイグレーションファイルに指定のカラムがあるか
②マイグレーションファイルの指定のカラムにスペルミスはないか
を確認ミスがあった場合、
マイグレーションファイルを修正
→rails dbコマンド周りはこちら【逆引きrails dbコマンド集】
参考記事
- 投稿日:2020-08-20T18:30:09+09:00
rails dbコマンド逆引き
はじめに
Ruby on Railsで開発をしていて、MySQLのDBを修正する機会が何度もあり、その都度エラーやデータの入れ直しが発生するため、コマンドの概要ともう少し掘り下げたところまでそれぞれの意味がおさらいできればと思い、記事にしました。
ご指摘等あればよろしくお願いいたします。入門編
①モデルファイルを作成したので、DBに反映させたい→migrate
rails g modelでモデルファイル作成後、
$ rails db:migrate②DBを作ったが、不要になったので削除したい→drop
$ rails db:drop③1つ前に反映したDBテーブルを修正するために、migrationを戻したい→rollback
$ rails db:rollback入門編まとめ
・migrateを実行すると、モデルファイルに基づいてマイグレーションファイルが作られる。
・作られたマイグレーションファイルを修正し、DBに反映するためには一度rollbackを実行しDB上から該当のテーブルを下げないといけない。(この際DBに入力したデータは削除される)初級編
①現在、どのマイグレーションファイルがDB上で有効なのか確認する→migirate:status
$ rails db:migrate:status適用されている場合はup、適用されていない場合はdownとしてファイルがターミナル上に表示される。
②任意のファイルまでrollbackを戻したい場合→rollback STEP=[ ]
$ rails db:rollback STEP=[戻したいステップ数]参考記事:【Rails】$rails db:rollbackしたい時の間違えない手順
③作成したDBを一旦削除して再度作成する(その1)→reset
$ rails db:reset④作成したDBを一旦削除して再度作成する(その2)→migrate:reset
$ rails db:migrate:reset初級編まとめ
・【resetとmigrate:resetの違い】どちらもDBを一旦dropして再度作成することは同じだが、どのファイルを元に作り直すかが異なるため、migrate:resetではマイグレーションファイルを修正していればその内容も反映して作成されるが、migrate:resetではそうならないようだ。
詳しくは以下(常にmigrate:resetを使っていれば良さそうなものだが、そうもいかない時があるようだ)
参考記事:rake db:reset と rake db:migrate:reset の違い
参考記事:rails db:migrate:resetできなかったのでrails db:resetした中級編
入力したDBの内容を消さずにカラムを修正したい→g migrationでカラム追加→db:migrate→カラム追加用のmigrationファイル削除
まとめ
コマンド 概要 オプションその他 migrate DBを作成 drop DBを削除 reset drop + migrate マイグレーションファイルの修正は反映されない migrate:reset drop + migrate マイグレーションファイルの修正も反映される migrate:rollback 1つ前に反映したmigrateまで STEP=[戻したいステップ数]で任意のmigrateまでDBを戻す migrate:status 現在どこまでmigrateが反映されているか確認する
- 投稿日:2020-08-20T18:30:09+09:00
逆引きrails dbコマンド集
はじめに
Ruby on Railsで開発をしていて、MySQLのDBを修正する機会が何度もあり、その都度エラーやデータの入れ直しが発生するため、コマンドの概要ともう少し掘り下げたところまでそれぞれの意味がおさらいできればと思い、記事にしました。
ご指摘等あればよろしくお願いいたします。入門編
①モデルファイルを作成したので、DBに反映させたい→migrate
rails g modelでモデルファイル作成後、
$ rails db:migrate②DBを作ったが、不要になったので削除したい→drop
$ rails db:drop③1つ前に反映したDBテーブルを修正するために、migrationを戻したい→rollback
$ rails db:rollback入門編まとめ
・migrateを実行すると、モデルファイルに基づいてマイグレーションファイルが作られる。
・作られたマイグレーションファイルを修正し、DBに反映するためには一度rollbackを実行しDB上から該当のテーブルを下げないといけない。(この際DBに入力したデータは削除される)初級編
①現在、どのマイグレーションファイルがDB上で有効なのか確認する→migirate:status
$ rails db:migrate:status適用されている場合はup、適用されていない場合はdownとしてファイルがターミナル上に表示される。
②任意のファイルまでrollbackを戻したい場合→rollback STEP=[ ]
$ rails db:rollback STEP=[戻したいステップ数]参考記事:【Rails】$rails db:rollbackしたい時の間違えない手順
③作成したDBを一旦削除して再度作成する(その1)→reset
$ rails db:reset④作成したDBを一旦削除して再度作成する(その2)→migrate:reset
$ rails db:migrate:reset初級編まとめ
・【resetとmigrate:resetの違い】どちらもDBを一旦dropして再度作成することは同じだが、どのファイルを元に作り直すかが異なるため、migrate:resetではマイグレーションファイルを修正していればその内容も反映して作成されるが、migrate:resetではそうならないようだ。
詳しくは以下(常にmigrate:resetを使っていれば良さそうなものだが、そうもいかない時があるようだ)
参考記事:rake db:reset と rake db:migrate:reset の違い
参考記事:rails db:migrate:resetできなかったのでrails db:resetした中級編
入力したDBの内容を消さずにカラムを修正したい→g migrationでカラム追加→db:migrate→カラム追加用のmigrationファイル削除
まとめ
コマンド 概要 オプションその他 migrate DBを作成 drop DBを削除 reset drop + migrate マイグレーションファイルの修正は反映されない migrate:reset drop + migrate マイグレーションファイルの修正も反映される migrate:rollback 1つ前に反映したmigrateまで STEP=[戻したいステップ数]で任意のmigrateまでDBを戻す migrate:status 現在どこまでmigrateが反映されているか確認する
- 投稿日:2020-08-20T18:30:09+09:00
逆引きrailsコマンド集【DB編(入門〜初級)】
はじめに
Ruby on Railsで開発をしていて、MySQLのDBを修正する機会が何度もあり、その都度エラーやデータの入れ直しが発生するため、コマンドの概要ともう少し掘り下げたところまでそれぞれの意味がおさらいできればと思い、記事にしました。
ご指摘等あればよろしくお願いいたします。入門編
①モデルファイルを作成したので、DBに反映させたい→migrate
rails g modelでモデルファイル作成後、
$ rails db:migrate②DBを作ったが、不要になったので削除したい→drop
$ rails db:drop③1つ前に反映したDBテーブルを修正するために、migrationを戻したい→rollback
$ rails db:rollback入門編まとめ
・migrateを実行すると、モデルファイルに基づいてマイグレーションファイルが作られる。
・作られたマイグレーションファイルを修正し、DBに反映するためには一度rollbackを実行しDB上から該当のテーブルを下げないといけない。(この際DBに入力したデータは削除される)初級編
①現在、どのマイグレーションファイルがDB上で有効なのか確認する→migirate:status
$ rails db:migrate:status適用されている場合はup、適用されていない場合はdownとしてファイルがターミナル上に表示される。
②任意のファイルまでrollbackを戻したい場合→rollback STEP=[ ]
$ rails db:rollback STEP=[戻したいステップ数]参考記事:【Rails】$rails db:rollbackしたい時の間違えない手順
③作成したDBを一旦削除して再度作成する(その1)→reset
$ rails db:reset④作成したDBを一旦削除して再度作成する(その2)→migrate:reset
$ rails db:migrate:reset初級編まとめ
・【resetとmigrate:resetの違い】どちらもDBを一旦dropして再度作成することは同じだが、どのファイルを元に作り直すかが異なるため、migrate:resetではマイグレーションファイルを修正していればその内容も反映して作成されるが、migrate:resetではそうならないようだ。
詳しくは以下(常にmigrate:resetを使っていれば良さそうなものだが、そうもいかない時があるようだ)
参考記事:rake db:reset と rake db:migrate:reset の違い
参考記事:rails db:migrate:resetできなかったのでrails db:resetした中級編
入力したDBの内容を消さずにカラムを修正したい→g migrationでカラム追加→db:migrate→カラム追加用のmigrationファイル削除
参考記事:Rails カラム名変更方法
参考記事:マイグレーションを使ったカラムの追加、削除、データ型の変更 [ 自分用メモ ]まとめ
コマンド 概要 オプションその他 migrate DBを作成 drop DBを削除 reset drop + migrate マイグレーションファイルの修正は反映されない migrate:reset drop + migrate マイグレーションファイルの修正も反映される migrate:rollback 1つ前に反映したmigrateまで STEP=[戻したいステップ数]で任意のmigrateまでDBを戻す migrate:status 現在どこまでmigrateが反映されているか確認する
- 投稿日:2020-08-20T16:35:39+09:00
【個人アプリ開発メモ】ログアウトしようとして生じたエラーの解決
ログアウトしようとしたらエラーが出てしまった。
エラー文
undefined method `id' for nil:NilClassposts_controller.rbdef index @posts = Post.all.includes(:user).order("created_at DESC").page(params[:page]).per(5) @post = Post.new #エラーが出たのはこの部分↓↓↓ @calorie_sum = Post.where(user_id: current_user.id, created_at: Time.zone.now.beginning_of_day..Time.zone.now.end_of_day).sum(:calorie) gon.today_sum = @calorie_sum @standard = Standard.find_by(user_id: current_user.id) @calorie_standard = @standard.calorie gon.standard = @calorie_standard if @calorie_sum >= @calorie_standard @difference = @calorie_sum - @calorie_standard else @difference = @calorie_standard - @calorie_sum end endなぜこのエラーが生じたのか?
posts_controller.rb@calorie_sum = Post.where(user_id: current_user.id, created_at: Time.zone.now.beginning_of_day..Time.zone.now.end_of_day).sum(:calorie)エラーが出たこのコードはどういう意味かというと、
Postモデル(投稿データが入ってる)から、
今日1日のcurrent_userの投稿を検索して抽出して、calorieカラムの数値を合計した結果をcalorie_sumというインスタンス変数に代入してる。なんでエラーになるかというと、
ログアウトするとuser_id: current_user.idのidのところがnilになってしまうから。解決するにはどうすればいいのか?
current_user.idがnilでもエラーにならないようにするためには、
posts_controller.rbを条件分岐で書けばいい気がする。こんな感じでどうだろうか?
posts_controller.rbdef index @posts = Post.all.includes(:user).order("created_at DESC").page(params[:page]).per(5) @post = Post.new # サインインしてる時だけ今日の摂取カロリーを表示するグラフを作るためのデータを取得する下記の関数が動く if user_signed_in? @calorie_sum = Post.where(user_id: current_user.id, created_at: Time.zone.now.beginning_of_day..Time.zone.now.end_of_day).sum(:calorie) gon.today_sum = @calorie_sum @standard = Standard.find_by(user_id: current_user.id) @calorie_standard = @standard.calorie gon.standard = @calorie_standard if @calorie_sum >= @calorie_standard @difference = @calorie_sum - @calorie_standard else @difference = @calorie_standard - @calorie_sum end else # サインインしてない時は何も動かない end endこれにあとviewのグラフの部分を
- if user_signed_in?で条件分岐して、サインインしてない時はグラフが出てこないようにしたら、無事解決しました。
- 投稿日:2020-08-20T15:44:39+09:00
【個人アプリ開発メモ】ArgumentError (wrong number of arguments (given 0, expected 2)):の解決
直面している問題
投稿しようとしたらできない。
なぜ?ターミナルに以下のエラー文が表示された。
エラー文
ターミナルArgumentError (wrong number of arguments (given 0, expected 2)): app/controllers/posts_controller.rb:21:in `create'argumentとは引数のことらしい。
2つの引数がなければいけないのに、それが0になってるからダメだという。引数が2つ必要?何のことだろう?
posts_controller.rbdef create @post = Post.new(post_params) ← ここで止まってしまってる if @post.save redirect_back(fallback_location: root_path) # なぜredirect_to root_pathじゃダメなのかわかってない else @posts = Post.includes(:user) flash.now[:alert] = '必須項目をしてください。' # フラッシュメッセージが出るか確認する redirect_back(fallback_location: root_path) end end private def post_params params.require(:post).permit(:food, :calorie, :protein, :fat, :carbo, :text, :image).merge(user_id: current_user.id) end試してみたこと
binding.pryでpost_paramsの中身を確認してみよう。
[1] pry(#<PostsController>)> post_params => <ActionController::Parameters {"food"=>"うどん", "calorie"=>"10", "protein"=>"10", "fat"=>"10", "carbo"=>"10", "text"=>"うどんを食べました", "image"=>#<ActionDispatch::Http::UploadedFile:0x00007fc7e7b4eae0 @tempfile=#<Tempfile:/var/folders/sy/26p55v5j47s2zjd8h7vr8f6h0000gn/T/RackMultipart20200820-34304-1e1sjww.png>, @original_filename="image4.png", @content_type="image/png", @headers="Content-Disposition: form-data; name=\"post[image]\"; filename=\"image4.png\"\r\nContent-Type: image/png\r\n">, "user_id"=>17} permitted: true>ここが原因ではないようだ。
https://teratail.com/questions/164134
によれば、ターミナルArgumentError (wrong number of arguments (given 0, expected 2)):これは呼び出し側の引数の数 = 0
メソッド側の引数の数 = 2
という意味らしい。呼び出し側の引数の数と、メソッド側と引数の数はそれぞれどこで設定してるのだろうか?
試したこと
画像を選択せずに、画像以外を投稿してみた。
その結果、これは問題なく投稿できた。
この結果からわかったのは、やはり問題は画像関連だということ。画像を選択して、画像も投稿しようとするとエラーが生じる。
画像を投稿しようとすると、メソッド側の引数が2になる?解決した。原因は「え?そこ?」という感じ
image_uploader.rb#[300, 200][100, 100]という感じで2つあるとダメ process resize_to_fit: [300, 200][100, 100] #1つに戻したら解決した process resize_to_fit: [300, 200]前に投稿画像サイズの設定で、2通りのサイズを設定したいと思って上記のように設定していたが、これが邪魔だったみたい。
https://qiita.com/naota7118/items/33943b8887a4990645a9余計なことしなければよかった。
- 投稿日:2020-08-20T15:14:18+09:00
【Rails】Enumを使う
どんな時に使う?
カラムstatusの値が
0だったら未処理
1だったら進行中
2だったら処理済み
みたいな時に、いい感じに書くことができます。定義
migrationファイルdef change add_column :orders, :status, :integer, default: 0 # デフォルト値は0に設定する endapp/models/order.rbclass Order < ApplicationRecord enum status: { waiting: 0, working: 1, completed: 2 } endできること
インスタンスを作成
order = Order.new( item: 'ボールペン', price: 150, status: 'working' # 今まで「1」と数値でセットしていた部分を文字列でセットできる。DBには「1」が保存される )インスタンスの中身を確認
pry(main)> order.status => "working" pry(main)> order.working? => true pry(main)> order.completed? => falseインスタンスを更新
pry(main)> order.status => "working" pry(main)> order.completed! => "completed" pry(main)> order.status => "completed"DBを検索
pry(main)> completed_orders = Order.completed => SELECT `orders`.* FROM `orders` WHERE `orders`.`status` = 2 LIMIT 11複数カラムで同じ名前を使いたい場合
こういう場合class Order < ApplicationRecord enum hoge_status: { waiting: 0, working: 1, completed: 2 } enum fuga_status: { waiting: 0, working: 1, completed: 2 } end
Order.waiting?としても、hoge_statusとfuga_statusのプログラムはどちらを見れば良いかわかりません。
こういった場合、_prefix: trueを使って解決します。prefixで解決class Order < ApplicationRecord enum hoge_status: { waiting: 0, working: 1, completed: 2 }, _prefix: true enum fuga_status: { waiting: 0, working: 1, completed: 2 }, _prefix: true endpry(main)> Order.hoge_status_completed? => SELECT `orders`.* FROM `orders` WHERE `orders`.`hoge_status` = 2 LIMIT 11 ` pry(main)> Order.fuga_status_completed? => SELECT `orders`.* FROM `orders` WHERE `orders`.`fuga_status` = 2 LIMIT 11参考
- 投稿日:2020-08-20T14:59:56+09:00
SNS認証 facebookだけ通らなかった原因
前提
- 今回、sns認証(google, facebook)を実装していました。
- そこで、googleは既存のusersテーブルと紐づくのに、facebookだけ認証が通らないエラーが発生しましたので、原因を記録します。(そのため、sns認証の細かい実装方法に関しては割愛します。)
- 原因は判明しましたが、根本的な解決には至っていませんので、あくまで原因を探す参考程度にご覧ください。
実行環境
rails 6.0.0
mysql 5.6.47
エラーの内容
今回、snsの認証とともに、userの情報を紐付けて保存する仕様にしていました。
ところがログインしようとすると、、、
おかしい、、、
Googleでは確かにログインができるのに、Facebookでは新規登録画面へ飛ばされてしまいました。原因
原因は、user.rbにありました。apiから送られてくるデータの検索方法がまずかったようです。
【user.rb】def self.from_omniauth(auth) sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create # sns認証したことがあればアソシエーションで取得 # 無ければemailでユーザー検索して取得orビルド(保存はしない) user = sns.user || User.where(email: auth.info.email).first_or_initialize( nickname: auth.info.name, email: auth.info.email ) # userが登録済みの場合はそのままログインの処理へ行くので、ここでsnsのuser_idを更新しておく if user.persisted? sns.user = user sns.save end { user: user, sns: sns } endこの記述の中だと、auth(apiから送られてくるユーザーデータ)の中から、:emailを参照してしまいます。
しかし、私自信、facebookのアカウントにemailを登録していなかったため、この検索に引っかからず、snsの情報をDBに保存していないよねと判断され、弾かれていたようです。
その後、Facebookのアカウント設定でemailを登録したところ、無事にログインできるようになりました。今後の課題
このままだと、emailをもたないfacebookユーザーは原因がわからないまま永遠にログインできない状態になってしまいます。ですので、データ参照の仕様を工夫する必要がありそうです、、、
- 投稿日:2020-08-20T14:22:04+09:00
Rails × Herokuアプリ高速化のためにやった/やりたいこと
実装が比較的楽で、インパクトが大きいものをピックアップ。
Cloudflare (CDN)
JSやCSSはCDNに乗せる。他のCDNでも良いが、Cloudflareは無料だから最高。
Cloudinary (画像配信)
画像はCloudinaryで最適化して配信。画像の圧縮、適切なサイズでの配信、Webp対応等全部やってくれる。
クエリ最適化
N+1をなくす。pluckを使う。
partialを使いすぎない
partialは、保守性可読性とパフォーマンスのトレードオフ。
collectionオプションが使える場合には活用する。ループの中の処理に気を使う
インスタンスを無駄に生成しない、など。
HTMLをgzip圧縮
Herokuだとデフォルトでgzip圧縮してくれないので、gemを使って対応。
ブラウザにキャッシュさせる期間を伸ばす
Cloudflare等の設定をいじって対応
重い処理はPumaの外に流す
例えば画像アップロードはLambda等に捌かせる
金を積む
最後はこれ
- 投稿日:2020-08-20T14:21:18+09:00
【個人アプリ開発メモ】画像が粗くなってしまう問題の解決
直面している問題
ヒントはつかめてる
画像のデフォルトのサイズは
app/uploaders/image_upload.rbのこの部分で設定できる。image_upload.rbprocess resize_to_fit: [300, 200]これと同じ大きさで画像を表示すれば、キレイに表示できる。
逆にこれと違う大きさで画像を表示しようとすると、粗くなってしまう。しかしこれで問題が完全にクリアになったわけではない。
こちらはプレビュー画像なのだが、こちらが少し粗くなってしまった。複数の画像の大きさの設定したいときはどうすればいいのだろうか?
今気づいたのだが、選択する画像によってキレイになったり粗くなったりする。
これがなぜそうなるのかがまだわからない。
- 投稿日:2020-08-20T12:59:30+09:00
【個人アプリ開発メモ】画像のプレビュー表示
やりたいこと
アップロードしたい画像を選択したら、プレビュー表示できるようにしたい。
なぜ画像プレビュー機能が必要なのか?
フォームを作っただけでは、画像を選択してもどの画像が選択されたかアップロード前に確認することができない。
アップロードされてから違う画像をアップロードしてしまったと気づくようなことを防ぐために、この機能は必要。
どう進めるか
まず画像プレビュー用のブランチを立ち上げる。
次に「画像 rails プレビュー」で検索して参考記事を探す。https://qiita.com/Masanori_N/items/71dbf648737f32dd8588
この記事を参考に進めていこう。作業内容
jQueryで実装するため、jsファイルを作る。
javasciptディレクトリ直下に「image-preview.js」ファイルを設置した。_main.html.haml.new-post = form_with model: @post, id: 'new_post' do |f| .input-box (省略) .icon .image-upload %i.fa.fa-camera.fa-2x = f.file_field :image, class: 'image_upload' .btn-square = f.submit '投稿する', class: 'post-btn' .posts = render @posts画像ファイル選択時にイベントを発火させる
まずはファイルを選択した時にイベントが発火するようにする。
image-preview.js$(function() { $(function() { $(document).on('change', 'image_upload', function() { console.log('hoge'); }) }); });これだとイベントは発火しなかった。
image-preview.js$(document).on('change', '.image_upload', function() { console.log('hoge'); })クラス名の指定で「.」が抜けているというミスが原因だった。
'image_upload'→'.image_upload'と修正したら無事イベントは発火した。画像データの読みこみ
いよいよここから難しくなる。
よくわからない時はconsole.logで中身を確認するに限る。image-preview.js$(function() { $(function() { $(document).on('change', '.image_upload', function() { console.log('hoge'); //選択したfileのオブジェクトを取得 var file = this.files[0]; console.log(file); }) }); });
console.log(file);で選択したfileを取得できているか確認する。検証画面で確認すると、
File {name: "image4.png", lastModified: 1596487703899, lastModifiedDate: Tue Aug 04 2020 05:48:23 GMT+0900 (日本標準時), webkitRelativePath: "", size: 110024, …}と出てきたので、おそらくちゃんと画像fileを取得できている。
次にFileReaderを使ってFileReaderオブジェクトを生成する。
FileReaderとは何なのか?https://developer.mozilla.org/ja/docs/Web/API/FileReader
によると、FileReader オブジェクトを使うと、ユーザーのコンピューター内にあるファイル (もしくはバッファ上の生データ) をウェブアプリケーションから非同期的に読み込むことが出来ます。読み込むファイルやデータは File ないし Blob オブジェクトとして指定します。
とある。
なるほど、ローカルにある画像ファイルを非同期で読み込むことができるのか。
image-preview.js//FileReaderオブジェクトの生成 var reader = new FileReader();これもコンソール画面で確認してみよう。
FileReader {readyState: 0, result: null, error: null, onloadstart: null, onprogress: null, …}次は
image-preview.js//readAsDataURLで指定したFileオブジェクトを読み込む reader.readAsDataURL(file);これもよくわからないのでそのままググってみよう。
https://developer.mozilla.org/ja/docs/Web/API/FileReader/readAsDataURL
ここでもMDNが参考記事として出てきた。これもFileオブジェクトを読み込むために必要らしい。
image-preview.jsvar hoge = reader.readAsDataURL(file); console.log(hoge);こんな感じで変数に代入して中身を見てみた。
すると、undefinedとなった。image-preview.js//readAsDataURLで指定したFileオブジェクトを読み込む var hoge = reader.readAsDataURL(file); console.log(hoge[0]);これだとどうか?
これもダメだ。
これは中身を見ることはできないみたい。image-preview.js//読み込み時に発火するイベント onloadメソッドは読み込みが完了したら実行する reader.onload = function() { ここに画像の読みこみ完了後の処理を書いていく }これもわからないので調べた。
https://developer.mozilla.org/ja/docs/Web/API/FileReader/onloadFileReader.onload プロパティは、readAsArrayBuffer や readAsBinaryString、 readAsDataURL、readAsText でのコンテンツ読み込みが完了して、利用可能になると発火する load イベント時に実行されるイベントハンドラを含みます
3回読んでも完全にはわからない。
readAsDataURLで画像ファイルの読みこみが完了して、利用可能になるとloadイベントが発火する。
というところまではわかった。image-preview.js//loadイベントが発火するかを調べる reader.onload = function() { console.log('hoge'); }コンソール画面でちゃんと「hoge」と出てきたので、loadイベントは問題なく発火してるとわかった。
その次にloadイベントの結果を取得する。
image-preview.js//直前に実行したイベントが返した値を取得する var image = this.result;プレビュー用のHTMLを設置します。
ここが結構時間かかるかな?と思っていたが、一発でいけました!!ここまでのコードはこんな感じです。
image-preview.js$(function() { $(function() { // プレビューHTML生成 function buildHTML() { var html = `<div class="preview-box"> <div class="upper-box"> <img src="" alt="preview" class="upload-image"> </div> <div class="lower-box"> <div class="delete-box"> <span>削除</span> </div> </div> </div>`; return html; } $(document).on('change', '.image_upload', function() { //選択したfileのオブジェクトを取得 var file = this.files[0]; //FileReaderオブジェクトの生成 var reader = new FileReader(); //readAsDataURLで指定したFileオブジェクトを読み込む reader.readAsDataURL(file); //読み込み時に発火するイベント onloadメソッドは読み込みが完了したら実行する reader.onload = function() { console.log('hoge'); //直前に実行したイベントが返した値を取得する var image = this.result; // プレビュー用のhtmlを追加 var html = buildHTML(); $('.text').append(html); //画像を追加 $(`.upper-box img`).attr('src', `${image}`); } }) }); });プレビュー画像のサイズ変更
image-preview.js// プレビューHTML生成 function buildHTML() { var html = `<div class="preview-box"> <div class="upper-box"> <img src="" alt="preview" class="upload-image" height="100px" width="100px"> </div> <div class="lower-box"> <div class="delete-box"> <span>削除</span> </div> </div> </div>`; return html; }
<img src="" alt="preview" class="upload-image" height="100px" width="100px">
imgタグのところにheightとwidthを追加すればOK?どのクラスに設定するかが大事で、親クラスに設定してもサイズは変わらないので注意です
プレビュー画像の削除
「削除」を押したらプレビュー画像が消えるようにしたい。
まず「削除」を押した時にイベントが発火するようにする。
image-preview.js$(document).on("click", '.delete-box', function(){ console.log('hogehoge'); })これで問題なく発火した。
クラス名の指定'.delete-box'が合っていればこれで発火する。削除はremoveメソッドを使えばOK。
久しぶりだから( )をつけるのを忘れていた?image-preview.js$(document).on("click", '.delete-box', function(){ $('.preview-box').remove(); })これだけだと、プレビュー画像は消えたが、まださっきの画像ファイルを選択したままになってる。
image-preview.js// 「削除」を押すと削除イベントが発火する $(document).on("click", '.delete-box', function(){ // プレビュー画像を削除 $('.preview-box').remove(); // inputタグに入ってる画像ファイルも削除 $('.image_upload').val(""); })inputタグに入ってる画像ファイルも削除しておこう。
これでプレビュー画像を削除すると、ファイルの中身が空っぽになる。2回連続ファイルを選択するとプレビュー画像が2つ表示されてしまう問題の解決
こんな感じで表示されてしまう。
これを選択するたびに入れ替わるように修正したい。
どうすればこの問題を解決できるか?考えたのは、
- すでにプレビュー画像がある場合
- プレビュー画像がない場合
に条件分岐すればいいのでは?
- プレビュー画像あり → プレビュー画像を一度削除する必要あり
- プレビュー画像なし → ここまで書いてきたコードでOK
どうやってプレビュー画像がある場合とない場合の条件分岐をするか?
image-preview.jsif($('.preview-box').length == 0){ プレビュー画像がない場合の処理 }else{ プレビュー画像がある場合の処理 }こんな感じでlengthプロパティを使って条件分岐しました。
image-preview.js// プレビュー画像がまだ場合 if($('.preview-box').length == 0){ // プレビュー用のhtmlを追加 var html = buildHTML(); $('.icon').before(html); //画像を追加 $(`.upper-box img`).attr('src', `${image}`); // すでにプレビュー画像が存在する場合 }else{ // プレビュー画像を削除 ⇦ 違うのはここです $('.preview-box').remove(); // プレビュー用のhtmlを追加 var html = buildHTML(); $('.icon').before(html); //画像を追加 $(`.upper-box img`).attr('src', `${image}`); }参考記事:https://www.sejuku.net/blog/34465
最終的に出来上がったコードはこちら
image-preview.js$(function() { $(function() { // プレビューHTML生成 function buildHTML() { var html = `<div class="preview-box"> <div class="upper-box"> <img src="" alt="preview" class="upload-image" height="100px" width="100px"> </div> <div class="lower-box"> <div class="delete-box"> <span>削除</span> </div> </div> </div>`; return html; } $(document).on('change', '.image_upload', function() { //選択したfileのオブジェクトを取得 var file = this.files[0]; //FileReaderオブジェクトの生成 var reader = new FileReader(); //readAsDataURLで指定したFileオブジェクトを読み込む reader.readAsDataURL(file); //読み込み時に発火するイベント onloadメソッドは読み込みが完了したら実行する reader.onload = function() { //直前に実行したイベントが返した値を取得する var image = this.result; // プレビュー画像がまだ場合 if($('.preview-box').length == 0){ // プレビュー用のhtmlを追加 var html = buildHTML(); $('.icon').before(html); //画像を追加 $(`.upper-box img`).attr('src', `${image}`); // すでにプレビュー画像が存在する場合 }else{ // プレビュー画像を削除 $('.preview-box').remove(); // プレビュー用のhtmlを追加 var html = buildHTML(); $('.icon').before(html); //画像を追加 $(`.upper-box img`).attr('src', `${image}`); } } }) // 「削除」を押すと削除イベントが発火する $(document).on("click", '.delete-box', function(){ // プレビュー画像を削除 $('.preview-box').remove(); // inputタグに入ってる画像ファイルも削除 $('.image_upload').val(""); }) }); });結局何をしたのか?
- プレビュー画像の表示ができた。
- プレビュー画像のサイズを小さくすることができた。
- プレビュー画像を削除できるようにした。
- 連続でファイルを選択したら、直前に選択したファイルのプレビューが消えて、新たにプレビューが表示されるようにした。
おまけ
各投稿の画像を大きくしたい。
before
after
_post.html.haml- if post.image.present? = image_tag post.image.url, class: 'post-image', width: '300px', height: '200px'各投稿は部分テンプレートで上記のHTMLで設定しており、image_tagのところにwidthとheightを追加して設定しました。
ただここで問題が。。。
見てわかるとおり、画像が粗くなってしまいました。これについては解決したらまた別の記事で書きたいと思います。
- 投稿日:2020-08-20T03:26:28+09:00
RailsでGraphQL APIからデータを取得・表示する
はじめに
初めまして、hinokageと申します。
Qiitaへの記事投稿初めて且つ、
プログラミングスクールを卒業したての知識で本記事を書きましたので
お手柔らかにお願いいたします。経緯
「Annict」という、有志が開発しているアニメ関連の情報が網羅してあるDBのGraphQLAPIが公開されています。
Railsでアプリ制作をしている際、制作していたアプリ内でこれを利用するためRailsからGraphQLAPIを叩く方法を調べたところ、「Railsで制作したアプリをAPIにする方法としてGraphQLを使用する」という内容の記事は結構な数ヒットするものの、「Railsアプリ内から公開されているGraphQLAPIを叩いてデータ取得する」という内容の記事が全くヒットせず、実装に非常に苦しみました。
私と同じように「有志が制作したGraphQLAPIをRailsで利用したい!」という方に向けて少しでも役に立てるよう本記事を投稿いたします。開発環境
- Ruby(2.6.5)
- Ruby on Rails(5.2.3)
導入
まず最初にGraphQLをrailsで使えるように
Ruby製のGraphQL Client 「graphql-client」を導入します。
(公式リファレンス:https://github.com/github/graphql-client )Gemfilegem 'graphql-client'(追記)gemfileに追記後、bundle install
bundle install次に、使用したいAPIのアクセストークンの作成を行います。
ここでは例としてGithubのGraphQL APIを使用します。GitHub APIのアクセストークンは
GitHubの自分のアイコンをクリック
Settings > Developer settings > Personal access tokens >Generate new token
から作成できます。
(ここでは詳しい作成方法は省略させていただきます。)httpアダプタ、Schema、クライアント設定のためapplication.rbに以下を追記します。
(今回はgql_testというアプリ名で制作しています。)config/application.rbmodule GqlTest(ご自身のアプリ名) ... AUTH_HEADER = "Bearer xxx" # xxx=作成した際に表示されたアクセストークン HTTP = GraphQL::Client::HTTP.new("https://api.github.com/graphql") do #上記には、接続したいAPIのエンドポイントURLを記入 def headers(context) { "Authorization": AUTH_HEADER } end end Schema = GraphQL::Client.load_schema(HTTP) # 上記を使って API サーバーから GraphQL Schema 情報を取得 Client = GraphQL::Client.new(schema: Schema, execute: HTTP) # 上記を使ってクライアントを作成 endこれで大まかな設定は完了です。
クエリ作成
次に取得したいデータのクエリを書きます。
ちなみにクエリとは
データベース管理システムに対する問合せ(処理要求)のこと。簡単に書くと
「欲しいデータを取得するためのコード」
です。GraphQLのクエリの書き方は
https://employment.en-japan.com/engineerhub/entry/2018/12/26/103000
上記のサイトが参考になると思います。もし自分でクエリを書きたい場合はご活用ください。今回はGitHubAPIを使用するのでクエリは以下のようなものを使用しました。
自分のリポジトリの新しいものから5つ分取得するクエリとなっています。query { viewer { repositories(last:5) { edges { node { name } } } } }また、自分でクエリを書きたい場合はAPIに応じたSchemaを知る必要があります。
ちなみにSchemaとは
GraphQL APIの仕様を表現するものです。スキーマ定義言語(SDL(Schema Definition Language))を使って表現します。先程のクエリを例にすると「viewer」「repositories」「node」「name」など全てSchemaです。
API作成時、作成者がデータを取り出す方法をSchemaとして文字に定義しており、
それをAPI使用者がSchemaを用いてデータを取得するために書くコードがクエリだと考えてもらえば
わかりやすいかもしれません。「クエリがどういうものなのか」を分かりやすく伝えるための自分の中で辿り着いた例えが
辞典でいう目次みたいなものがSchemaです。例えば、辞典でとある動物を調べたい、
しかしその動物の〇〇類〇〇科〇〇目などは分かるが名前はわからないみたいな時に
目次のページを見ると思います。この目次のページがSchemaです。
目次を見て〇〇類のページを開きます。
またその辞典には〇〇類の最初のページに「〇〇科一覧」みたいな目次があるかもしれません。
これもSchemaです。つまりはSchemaは
目的のデータに辿り着きたい時の検索手段
みたいなものだと考えれば分かりやすいかもしれません。
その検索手段をAPI作成者側が文字に定義しているのが
schemaだと私は解釈して使用しています。
(間違っていたら申し訳ないので、ぜひ一度GraphQL Schemaとググって調べてください)さて、Schemaの種類や使い方については
各APIの公式リファレンスを読めば書いてあると思いますが
私は「GraphiQL」というアプリケーションを使用して調べることが多いです。
GraphiQLはSchemaの確認や検索ができるほか、
APIへの接続やクエリ、返ってくるデータの確認ができるため非常に便利なツールとなっています。このアプリはブラウザ上で使用することもできますが
オススメはスタンドアローンで使用できるダウンロード版です。
https://electronjs.org/apps/graphiql少し話が逸れてしまいました本題に戻ります。
実装
先ほどのクエリをcontorollerに追加します。
その際、下記のように記述します。controllers/blogs_controller.rbclass BlogsController < ApplicationController Query = GqlTest::Client.parse <<-GRAPHQL query { viewer { repositories(last:5) { edges { node { name } } } } } GRAPHQL def index @works = result end private def result response = GqlTest::Client.query(Query) end endprivateメソッドの下層に
resultというメソッドを用意して
クエリの結果データをrailsで使用できるように加工しています。
そして、使用したいアクション内でresultを用いることで、
結果データを使用することができます。@worksというインスタンス変数にAPIからの結果データを格納したので
これをviewで使用してみます。最新5件のリポジトリ名が表示されればOKです。index.html.erb<h2>私が最近作成したリポジトリは以下の5つです</h2> <%= @works%>あれ?オブジェクトしか表示されない…?
実はこの時点では取り出したいデータを指定していなかったので
オブジェクトが表示されるだけになっています。
これは以下のようにすると表示することができます。controllers/blogs_controller.rbdef index @works = result.data.viewer.repositories.edges end private def result response = GqlTest::Client.query(Query) endこれだけだと、変数内に配列のようにリポジトリ名が複数入っている状態なので、
下記のように表示されてしまいます。
そのため、viewファイルの方でeachメソッドを使い1つずつ表示するようにします。
index.html.erb<% @works.each do |work|%> <p><%= work.node.name%></p> <% end %>…しかしこれだと、追記したコードの意味が分からないですよね。
コードの解説
実は取得したデータだけでは目的の
「自分の最新リポジトリ5件のタイトル」というデータ以外のものも含まれるので、
クエリで書いたように何の情報が欲しいのかを記述する必要があります。つまり、「自分の最新リポジトリ5件のタイトルを取得する」というクエリを書いても、
結果として返ってくるデータは「自分の最新リポジトリ5件のタイトル」だけでは無いということです。GraphiQLを使って、先程と同じクエリを送った時にどのようなデータが返ってくるのか見ると非常にわかりやすいです。
画面左側のコードが先程と同じ「最新リポジトリ5件のタイトルを取得する」クエリです
画面右側はそのクエリをAPIに送った時に返ってきた結果データです。
結果データを見るとクエリで書いたSchemaに値が入って返ってきてるのが分かると思います。
また、コードの書かれ方がハッシュと同じような形でkeyとvalueが含まれているのが分かると思います。この結果データがハッシュであった場合、リポジトリのタイトルであるnameの値を取り出そうとすると
resultという変数に結果データが格納されていた時result[:data][:viewer][:repositories][:edges].each do |work| work[:node][:name] endこのようなコードを書くと思います。
結果データはハッシュでは無いため、このような取り出し方はできないのですが
(ちなみに結果データはJSON形式で返ってきているようです。)
公式リファレンスを読むとハッシュでkeyになっていたものをメソッドのように扱うことで
上記で書いたハッシュの展開と似たような方法で取り出すことができるようです。
つまり、コードはcontrollers/blogs_controller.rbdef index @works = result.data.viewer.repositories.edges end private def result response = GqlTest::Client.query(Query) endindex.html.erb<% @works.each do |work|%> <p><%= work.node.name%></p> <% end %>と書くことで目的のデータが表示できるようになります。
(夜中に殴り書きしたので色々誤字脱字あるかもしれません…)
- 投稿日:2020-08-20T01:48:44+09:00
【Rails 6.0】Action Cable を解説しながらプログレスバーを実装する
TL;DR
- Action Cable を使用してプッシュ通信のプログレスバーを実装した
- Action Cable について解説する
ProgressChannel.broadcast_to(current_user, percent: (index+1) * 100 / pictures.length)背景
旅行の思い出投稿サイトを作ったときに写真アップロードが遅くて、UX 的にアップロードの進捗状況を表すプログレスバーがあったほうが良いのではないかとなり、Action Cable の勉強も兼ねて実装しました
モノ
Action Cable 解説
Rails に Action Cable という WebSocket と Rails のその他の部分をシームレスに統合するための機能が存在します
Rails 公式なので Rails ガイドを見るのが良いと思ったのですが、Rails ガイドの解説がとても分かりづらかったので自分でまとめ直してみました
参考になれば幸いです図が一番わかり易い
Rails ガイドがわかりづらかった理由の一つが図がなかったことだと思ったので図を作りました
どこか間違ってたら教えてください
用語解説
Rails ガイドと順番が違うかもしれませんが、一番わかり易いと思う順番にしました
究極的にはパブリッシャからサブスクライバへ通信が行われる pub/sub リンクです
Rails アプリケーションの controller でブロードキャストを行い、チャネルを通して各タブ(サブスクライバ)に配信しますプログレスバーの実装
プログレスバーを主題に実際の実装を見ていきます
実行環境
- ruby:2.6.5 (Docker image)
- Rails 6.0.2
まずは
rails g channel <チャネル名>でひな形を作成します$ rails g channel progressサーバー側
チャネル
サーバー側のプログレスチャネルです
app/channels/progress_channel.rbclass ProgressChannel < ApplicationCable::Channel def subscribed stream_for current_user end def unsubscribed # Any cleanup needed when channel is unsubscribed end end
subscribedとunsubscribedのひな形は用意されていて、それぞれサブスクリプションができた/なくなったときの挙動を書きます
一般的には、stream_from "progress_#{params[:progress_id]}"のような形で受け取るブロードキャストを指定します
しかし、ここではstream_forを使用し、ユーザーごとに変わるブロードキャストを指定しています
これで、progress:Z2lkOi8vbWVtb3BpYy9Vc2VyLzE(ブロードキャスト名) のようなブロードキャストを受信することができますコネクション
サーバー側ではこのコネクションクラスをもとに、コネクションインスタンスを作成します
app/channels/application_cable/progress_channel.rbmodule ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end private def find_verified_user User.find(session['warden.user.user.key'][0][0]) rescue reject_unauthorized_connection end def session @session ||= cookies.encrypted[Rails.application.config.session_options[:key]] end end end
identified_byに指定したものを参考に、サーバーはコネクションを識別できるようになります
このコードでは、current_userに入る User オブジェクトをもとに識別しています
アプリケーションで Devise を使用しているので、ここの実装は
https://qiita.com/masuidrive/items/fd13ab8d9410206a1b91
を参考にしましたパブリッシャ
今回は model のところにブロードキャストするコードを書きます
app/models/album_form.rbpictures.each_with_index do |picture, index| album.pictures.new(picture_name: picture) ProgressChannel.broadcast_to( user, percent: (index+1) * 100 / pictures.length ) endここでの
userはcurrent_userに当たります(実装上、変数名が違います)
ここでクライアントにわたす data (percent: (index+1) * 100 / pictures.length) を指定してプログレスバーの進めるわけです
単純に枚数で進捗の%を計算しています
このブロードキャストがチャネルのstream_for current_userにつながり、current_userがいるクライアントにデータが送信されます以上で、サーバー側の実装は終わりです
クライアント側
好きな場所にプログレスバーを置きます
app/views/albums/index.html.erb<div class="progress-percentage text-xs-center">0%</div> <progress class="progress progress-striped" max="100" value="0"></progress>クライアント側でもコネクションを作成しないといけません
app/javascript/channles/consumer.js// Action Cable provides the framework to deal with WebSockets in Rails. // You can generate new channels where WebSocket features live using the `rails generate channel` command. import { createConsumer } from "@rails/actioncable" export default createConsumer()app/javascript/channels/progress_channel.jsimport consumer from "./consumer" consumer.subscriptions.create("ProgressChannel", { connected() { // Called when the subscription is ready for use on the server }, disconnected() { // Called when the subscription has been terminated by the server }, received(data) { // Called when there's incoming data on the websocket for this channel $('.progress-percentage').text(`${data.percent}%`) $('progress').prop('value', data.percent) } });
consumer.jsでコンシューマを作成し、progress_channel.jsでコネクション(サブスクリプション)を作成しています
今回はProgressChannelにつながるサブスクリプションです
connected()にコネクションができたときの動作、disconnected()にコネクションがなくなったときの動作を記述します
received()にはブロードキャストされたデータを受け取ったときの挙動を書きます
プログレスバーの進捗状況がサブスクライバにブロードキャストされて受け取るので、ここでプログレスバーの各要素を jQuery で書き換えます完成!
まとめ
以上のコードを実装することでユーザーごとにプッシュ通信にするプログレスバーが実装できます
Qiita 初投稿で ActionCable も学びたてなので間違っているところもあるかもしれません、その部分はご指摘・ご教授いただけるとありがたいです
また、記事のこの部分がわかりづらいというコメントもお待ちしています!参考記事
https://railsguides.jp/action_cable_overview.html
https://qiita.com/QUANON/items/0ba35cd0eb5d720f4237
https://qiita.com/masuidrive/items/fd13ab8d9410206a1b91P.S.
『memopic』 という旅行の思い出・アルバム・写真共有サービスを作っています。是非使ってみてください!
ご意見・ご要望などあれば @memopic_bambooまで DM お願いします!
- 投稿日:2020-08-20T01:37:15+09:00
【Rails 6.0】Action Cable を解説しながらプログレスバーを実装する
TL;DR
- Action Cable を使用してプッシュ通信のプログレスバーを実装した
- Action Cable について解説する
ProgressChannel.broadcast_to(current_user, percent: (index+1) * 100 / pictures.length)背景
旅行の思い出投稿サイトを作ったときに写真アップロードが遅くて、UX 的にアップロードの進捗状況を表すプログレスバーがあったほうが良いのではないかとなり、Action Cable の勉強も兼ねて実装しました
モノ
Action Cable 解説
Rails に Action Cable という WebSocket と Rails のその他の部分をシームレスに統合するための機能が存在します
Rails 公式なので Rails ガイドを見るのが良いと思ったのですが、Rails ガイドの解説がとても分かりづらかったので自分でまとめ直してみました
参考になれば幸いです図が一番わかり易い
Rails ガイドがわかりづらかった理由の一つが図がなかったことだと思ったので図を作りました
どこか間違ってたら教えてください
用語解説
Rails ガイドと順番が違うかもしれませんが、一番わかり易いと思う順番にしました
究極的にはパブリッシャからサブスクライバへ通信が行われる pub/sub リンクです
Rails アプリケーションの controller でブロードキャストを行い、チャネルを通して各タブ(サブスクライバ)に配信しますプログレスバーの実装
プログレスバーを主題に実際の実装を見ていきます
実行環境
- ruby:2.6.5 (Docker image)
- Rails 6.0.2
まずは
rails g channel <チャネル名>でひな形を作成します$ rails g channel progressサーバー側
チャネル
サーバー側のプログレスチャネルです
app/channels/progress_channel.rbclass ProgressChannel < ApplicationCable::Channel def subscribed stream_for current_user end def unsubscribed # Any cleanup needed when channel is unsubscribed end end
subscribedとunsubscribedのひな形は用意されていて、それぞれサブスクリプションができた/なくなったときの挙動を書きます
一般的には、stream_from "progress_#{params[:progress_id]}"のような形で受け取るブロードキャストを指定します
しかし、ここではstream_forを使用し、ユーザーごとに変わるブロードキャストを指定しています
これで、progress:Z2lkOi8vbWVtb3BpYy9Vc2VyLzE(ブロードキャスト名) のようなブロードキャストを受信することができますコネクション
サーバー側ではこのコネクションクラスをもとに、コネクションインスタンスを作成します
app/channels/application_cable/progress_channel.rbmodule ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end private def find_verified_user User.find(session['warden.user.user.key'][0][0]) rescue reject_unauthorized_connection end def session @session ||= cookies.encrypted[Rails.application.config.session_options[:key]] end end end
identified_byに指定したものを参考に、サーバーはコネクションを識別できるようになります
このコードでは、current_userに入る User オブジェクトをもとに識別しています
アプリケーションで Devise を使用しているので、ここの実装は
https://qiita.com/masuidrive/items/fd13ab8d9410206a1b91
を参考にしましたパブリッシャ
今回は model のところにブロードキャストするコードを書きます
app/models/album_form.rbpictures.each_with_index do |picture, index| album.pictures.new(picture_name: picture) ProgressChannel.broadcast_to( user, percent: (index+1) * 100 / pictures.length ) endここでの
userはcurrent_userに当たります(実装上、変数名が違います)
ここでクライアントにわたす data (percent: (index+1) * 100 / pictures.length) を指定してプログレスバーの進めるわけです
単純に枚数で進捗の%を計算しています
このブロードキャストがチャネルのstream_for current_userにつながり、current_userがいるクライアントにデータが送信されます以上で、サーバー側の実装は終わりです
クライアント側
好きな場所にプログレスバーを置きます
app/views/albums/index.html.erb<div class="progress-percentage text-xs-center">0%</div> <progress class="progress progress-striped" max="100" value="0"></progress>クライアント側でもコネクションを作成しないといけません
app/javascript/channles/consumer.js// Action Cable provides the framework to deal with WebSockets in Rails. // You can generate new channels where WebSocket features live using the `rails generate channel` command. import { createConsumer } from "@rails/actioncable" export default createConsumer()app/javascript/channels/progress_channel.jsimport consumer from "./consumer" consumer.subscriptions.create("ProgressChannel", { connected() { // Called when the subscription is ready for use on the server }, disconnected() { // Called when the subscription has been terminated by the server }, received(data) { // Called when there's incoming data on the websocket for this channel $('.progress-percentage').text(`${data.percent}%`) $('progress').prop('value', data.percent) } });
consumer.jsでコンシューマを作成し、progress_channel.jsでコネクション(サブスクリプション)を作成しています
今回はProgressChannelにつながるサブスクリプションです
connected()にコネクションができたときの動作、disconnected()にコネクションがなくなったときの動作を記述します
received()にはブロードキャストされたデータを受け取ったときの挙動を書きます
プログレスバーの進捗状況がサブスクライバにブロードキャストされて受け取るので、ここでプログレスバーの各要素を jQuery で書き換えます完成!
まとめ
以上のコードを実装することでユーザーごとにプッシュ通信にするプログレスバーが実装できます
学びたてなので間違っているところもあるかもしれません、その部分はご指摘・ご教授いただけるとありがたいです
また、記事のこの部分がわかりづらいというコメントもお待ちしています!参考記事
https://railsguides.jp/action_cable_overview.html
https://qiita.com/QUANON/items/0ba35cd0eb5d720f4237
https://qiita.com/masuidrive/items/fd13ab8d9410206a1b91P.S.
『memopic』 という旅行の思い出・アルバム・写真共有サービスを作っています。是非使ってみてください!
ご意見・ご要望などあれば @memopic_bambooまで DM お願いします!
- 投稿日:2020-08-20T01:15:02+09:00
C++とRubyで処理速度比較してみた 第一回
はじめに
C++の処理速度が速い、Ruby生産性が高いと巷でよく聞くので、
実際に処理速度を比較してみたいと思いました。
それぞれで1000万までの素数生成プログラムを作り比べてみようと思います。どちらもまだまだチューニングや測定方法などの改善の余地があると思うので、
こうやってみたら速くなるのでは
と気づいた方はコメントいただけるとすごく嬉しいですそんな感じで改善していければ面白いかもと思い「第一回」としてます。
アルゴリズムは「エラトステネスのふるい」を使用してます。
自然数を何回かふるいにかけていって素数でない数を消していくと言うやり方です。
詳細で正確な解説は下記参照下さい。
https://ja.wikipedia.org/wiki/エラトステネスの篩環境は下記になります。
macOS Catalina 10.15.6 [メモリ:8GB/CPU:Intel Core i5]
zsh
Apple clang version 12.0.0
ruby 2.6.3p62Ruby編:コードと測定結果
prime_number.rbdef create_prime_number(max_number) terms = Array.new puts(Time.now.strftime("%H:%M:%S.%N hurui start")) max_number.times {|x| terms.push x + 1} # natural number terms.shift # reject 1 last_number = Math.sqrt(max_number).floor # end of hurui puts(Time.now.strftime("%H:%M:%S.%N hurui start")) i = 0 current_number = terms[i] while current_number <= last_number do ary = terms.reject {|x| x % current_number == 0 && x != current_number} terms = ary i += 1 current_number = terms[i] end puts(Time.now.strftime("%H:%M:%S.%N hurui end")) # result # p terms end create_prime_number(10000000)実行結果は下記になります。
% ruby prime_number.rb 23:57:23.813912000 hurui start 23:57:24.487299000 hurui start 23:57:49.403797000 hurui endRuby編:気づいたこと
・破壊的メソッドでふるいにかけようとするとすごく遅くなりました。
prime_number.rbterms.reject! {|x| x % current_number == 0 && x != current_number}C++編:コードと測定結果
eratosthenes.cpp#include<iostream> #include <math.h> #include <list> #include <sys/time.h> #define MAX_NUMBER 10000000 using namespace std; int main() { unsigned long last_num = (unsigned long)sqrt(MAX_NUMBER); unsigned long cur_num; struct timeval tv; struct tm *time_fmt; list<unsigned long> *num_ls = new list<unsigned long>; list<unsigned long>::iterator num_ls_itr; unsigned long itr_skip; unsigned long i; /* ======== measurement ======== */ gettimeofday(&tv, NULL); time_fmt = localtime(&tv.tv_sec); cout << time_fmt->tm_hour << ":" << time_fmt->tm_min << ":" << time_fmt->tm_sec << "." << tv.tv_usec << endl; /* ======== measurement ======== */ /* create natural number */ for (unsigned long ntrl_num = 2; ntrl_num <= MAX_NUMBER; ntrl_num++) { num_ls->push_back(ntrl_num); } /* ======== measurement ======== */ gettimeofday(&tv, NULL); time_fmt = localtime(&tv.tv_sec); cout << time_fmt->tm_hour << ":" << time_fmt->tm_min << ":" << time_fmt->tm_sec << "." << tv.tv_usec << endl; /* ======== measurement ======== */ /* hurui */ itr_skip = 0; num_ls_itr = num_ls->begin(); cur_num = *num_ls_itr; while (cur_num <= last_num) { for (; num_ls_itr != num_ls->end();) { if ( (*num_ls_itr % cur_num == 0) && (*num_ls_itr != cur_num) ) { num_ls_itr = num_ls->erase(num_ls_itr); } else { num_ls_itr++; } } num_ls_itr = num_ls->begin(); itr_skip++; for (i = 0; i < itr_skip; i++) { num_ls_itr++; } cur_num = *num_ls_itr; } /* ======== measurement ======== */ gettimeofday(&tv, NULL); time_fmt = localtime(&tv.tv_sec); cout << time_fmt->tm_hour << ":" << time_fmt->tm_min << ":" << time_fmt->tm_sec << "." << tv.tv_usec << endl; /* ======== measurement ======== */ /* result */ #if (0) cout << "["; for (num_ls_itr = num_ls->begin(); num_ls_itr != num_ls->end(); num_ls_itr++) { cout << *num_ls_itr << ","; } cout << "]" << endl; #endif delete num_ls; return 0; }コンパイルと実行結果は下記になります。
% clang++ eratosthenes.cpp -o eratosthenes -O3 % ./eratosthenes 23:56:12.820103 23:56:13.458872 23:56:37.406918C++編:気づいたこと
・最適化オプション(-O3)を付けずにコンパイルすると10秒ちょっと遅くなりました。
% clang++ eratosthenes.cpp -o eratosthenes・listをヒープ領域からスタック領域に変更してもそんなに変わらなかった。
eratosthenes.cpplist<unsigned long> num_ls;終わりに
今回はC++の方がRubyよりもおよそ1秒ほど早かった形になります。
C++の方はiteratorループ内のeraseのやり方を誤ったせいで「segmentation fault」でしばらくハマりましたし、時間計測に使ったgettimeofday関数を後で調べてみると非推奨になっていたりしました(chronoを使うと良いらしい)。
やはり生産性はRubyの方が高かった印象です。
- 投稿日:2020-08-20T00:07:24+09:00
rails csv ファイル読み込み トライ
【Ruby on Rails】CSVインポート
Rails上で、CSVファイルを読み込んでみた
【Rails】CSVファイルからデータをインポートこれを参考にトライ
結果その通りにやったらできました。
(投稿者様、ありがとうございます)だが、rubyの文法でつまづく。
だれかわかる方いらっしゃたら助けていただけるとありがたいです。app/controllers/users_controller.rbdef index @users = User.all end def import # fileはtmpに自動で一時保存される User.import(params[:file]) redirect_to users_url endこのimportコントローラーUser.importのimportメソッドは、user Modelを操作するために、下記で、クラスメソッドとして定義されている、importメソッドであることはわかった。
app/model/user.rbclass User < ApplicationRecord def self.import(file) CSV.foreach(file.path, headers: true) do |row| # IDが見つかれば、レコードを呼び出し、見つかれなければ、新しく作成 user = find_by(id: row["id"]) || new # CSVからデータを取得し、設定する user.attributes = row.to_hash.slice(*updatable_attributes) # 保存する user.save end end # 更新を許可するカラムを定義 def self.updatable_attributes ["id", "name", "age"] end endまず、
user = find_by(id: row["id"]) || new
でつまづいた。
調べると、
https://www.sejuku.net/blog/19044
nil判定を行っていることは理解
find_by(id: row["id"])がnilなら newをuserに代入している。でも "new"とはなんだろう、インスタンスか?
公式document
https://docs.ruby-lang.org/ja/latest/class/CSV=3a=3aRow.html
をみると、
CSV::Rowクラスの、newメソッドなのではないかと予想
であれば、
row.newを省略して書いている??もう一個
row.to_hash.slice(*updatable_attributes)
がわからない。rowは、csvの特定の行
それをto_hashでハッシュにしている
sliceで ["id", "name", "age"]のみ取扱たいが、
to_hashメソッドではを使うと、["id", "name", "age"]がハッシュのキー(あるいは値)として使われるのだろうか?
https://docs.ruby-lang.org/ja/latest/class/CSV=3a=3aRow.htm謎は深まる。
あと、*updatable_attributes
引数にアスタリスク付きのメソッドを指定している記法がわからない。
- 投稿日:2020-08-20T00:00:19+09:00
Rubyで段落を作る
Rubyで段落を作るには、文字の後に\nをつける必要があります。
バックスラッシュ\の出し方はオプションキーと¥を押せば簡単に出すことが出来ます
おはよう\nこんにちは\nこんばんは\n
と記載すると...
下のように簡単に縦に文字を並べることができました。
おはよう
こんにちは
こんばんは





















