20210621のRubyに関する記事は11件です。

webpackerでBootstrap4の導入

初めに Railsでwebpackerを使ってBootstrap4の導入方法のまとめ。 導入手順 Gemfileにwebpackerを追加して、インストール (今回は既に入っていたので、飛ばしてます。) Gemfile gem 'webpacker', '~> 3.5 bundle installの実行 Webpackerをプロジェクトにインストール  bundle exec rails webpacker:install これでWebpackerを実行するための各種ファイルが生成。 app/javascript以下にsrcというディレクトリを作成し、application.scssを作成。 ディレクトリはapp/javascript/src/application.scssとなる。 Bootstrapの導入 yarn add bootstrap@4.3.1 jquery popper.jsを実行 BootstrapのJSをapp/javascript/packs/application.jsで、 CSSをapp/javascript/src/application.scssでインポート app/javascript/packs/application.js import 'bootstrap' import '../src/application.scss' app/javascript/src/application.scss @import '~bootstrap/scss/bootstrap'; をそれぞれ追記。 アプリケーションがWebpackerでビルドしたJS/CSSを読み込むよう設定する。 ```app/views/layouts/application.html.erb <!DOCTYPE html> RailsBootstrapTemplate <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= javascript_pack_tag 'application' %> <%= stylesheet_pack_tag 'application' %> <%= yield %> ``` 以上。 参考資料 webpackerでBootstrap4の導入方法
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Carrierwaveの実装 

はじめに 画像登録の実装の際に、carrierwaveを用いて実装しました。 その時に調べたこと感じたことの記録です。 結論 実装自体は単純で、使い勝手が良い。 ドラッグ&ドロップなどにも対応可能。 手順 1. インストール ターミナル上で以下のコードを実行し、最新版のcarrierwaveをインストール $ gem install carrierwave Gemfile内に以下を追加 gem 'carrierwave', '~> 2.0' 2. 実装 uploaderの実装 以下のコードを実行して、uploaderを実装する。 terminal $ bundle exec rails generate uploader ### ###の部分はimageやavatorなど、その時のカラムに合わせて変えてください。 そうすると app/uploaders/###_uploader.rb というファイルが作成されている。 中身は「保存先の指定」「アップロードする画像の種類」などの指定ができる。 uploaderを作成したら、モデルとの関連付け 以下のコードを関連付けしたいモデルに追記する。 (今回で言うと、document_images.rb) mount_uploader :carrierwave用に作ったカラム名, carrierwaveの設定ファイルのクラス名 ファイルのアップロード viewで画像を登録するのに、以下をviewのファイル書く。 今回はnew.html.erbにコードを書いてます。 new.html.erb <%= f.label :image %> <%= f.file_field :image %> 注意点 複数枚の画像を登録する場合、まとめたファイルがdbに登録されます。 そのため、1枚1枚の写真を取り出すには別のメソッドを使用する必要があるそうです。 【Rails】画像を複数投稿したい 参考サイトにもありますが、アンチメソッドのようです・・・ また、 mount_uploader :carrierwave用に作ったカラム名, carrierwaveの設定ファイルのクラス名 のmount_uploaderを複数形にすると、carrierwave用に作ったカラムを配列形式で扱わないといけなくなるので、タイポ要注意です! さいごに 上記内容で、一通り実装は出来ます。 が、アンチメソッドを使った実装はまだしていないので、挙動は不明です。 ドキュメント自体に画像が登録されるかについても、show.html.erbにコードを書いていないので、別の記事で書きます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gem installでおかしいことになった

はじめに 100億年ぶりにgem installをしたらハマったのでメモ的なのでここに残しておきます 環境 MacOS 10.15.7 zsh ruby 2.6 rbenv <- ここ重要 ことの発端 gemでfluentdをインストールしようとしたら以下のようなエラーが出てしまいました。 $ gem install fluentd --no-doc Building native extensions. This could take a while... ERROR: Error installing fluentd: ERROR: Failed to build gem native extension. current directory: /Library/Ruby/Gems/2.6.0/gems/msgpack-1.4.2/ext/msgpack /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby -I /Library/Ruby/Site/2.6.0 -r ./siteconf20210621-4696-16rdbxa.rb extconf.rb checking for ruby/st.h... *** extconf.rb failed *** Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers. Check the mkmf.log file for more details. You may need configuration options. Provided configuration options: --with-opt-dir --without-opt-dir --with-opt-include --without-opt-include=${opt-dir}/include --with-opt-lib --without-opt-lib=${opt-dir}/lib --with-make-prog --without-make-prog --srcdir=. --curdir --ruby=/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/$(RUBY_BASE_NAME) /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:467:in `try_do': The compiler failed to generate an executable file. (RuntimeError) You have to install development tools first. from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:585:in `block in try_compile' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:534:in `with_werror' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:585:in `try_compile' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:1109:in `block in have_header' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:959:in `block in checking_for' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:361:in `block (2 levels) in postpone' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:331:in `open' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:361:in `block in postpone' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:331:in `open' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:357:in `postpone' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:958:in `checking_for' from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:1108:in `have_header' from extconf.rb:3:in `<main>' To see why this extension failed to compile, please check the mkmf.log which can be found here: /Library/Ruby/Gems/2.6.0/extensions/universal-darwin-19/2.6.0/msgpack-1.4.2/mkmf.log extconf failed, exit code 1 Gem files will remain installed in /Library/Ruby/Gems/2.6.0/gems/msgpack-1.4.2 for inspection. Results logged to /Library/Ruby/Gems/2.6.0/extensions/universal-darwin-19/2.6.0/msgpack-1.4.2/gem_make.out なんだこれと思い無邪気に「msgpack」ってやつがなんか悪さしてそうと思ったので調べてみました。 $ gem list | grep msgpack まあ案の定出てきませんでした。ここでふと以下のコマンドを叩いてみました $ which ruby /usr/bin/ruby あれなんかrbenvがちゃんと読まれてないぞと思ったので~/.zshrcをのぞいてみました。するとrbenvなんて一文字も書いてなかったので追加しました。 zshrc export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init - zsh)" 追加したあとに$HOME下に.rbenvすらなかったので作ります。 mkdir ~/.rbenv rbenvでrubyをインストールします $ rbenv install 2.6.6 Downloading openssl-1.1.1g.tar.gz... -> https://dqw8nmjcqpjn7.cloudfront.net/ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46 Installing openssl-1.1.1g... Installed openssl-1.1.1g to /Users/hogehoge/.rbenv/versions/2.6.6 Downloading ruby-2.6.6.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.6.tar.bz2 Installing ruby-2.6.6... ruby-build: using readline from homebrew Installed ruby-2.6.6 to /Users/hogehoge/.rbenv/versions/2.6.6 $ rbenv global 2.6.6 $ rbenv rehash $ which ruby /Users/hogehoge/.rbenv/shims/ruby これで多分大丈夫なので以下でインストールします $ gem install fluentd --no-doc Password: Fetching sigdump-0.2.4.gem Fetching serverengine-2.2.4.gem Fetching msgpack-1.4.2.gem Fetching http_parser.rb-0.6.0.gem Fetching yajl-ruby-1.4.1.gem Fetching cool.io-1.7.1.gem Fetching concurrent-ruby-1.1.9.gem Fetching tzinfo-2.0.4.gem Fetching tzinfo-data-1.2021.1.gem Fetching strptime-0.2.5.gem Fetching fluentd-1.13.0.gem Building native extensions. This could take a while... Successfully installed msgpack-1.4.2 Building native extensions. This could take a while... Successfully installed yajl-ruby-1.4.1 Building native extensions. This could take a while... Successfully installed cool.io-1.7.1 Successfully installed sigdump-0.2.4 Successfully installed serverengine-2.2.4 Building native extensions. This could take a while... Successfully installed http_parser.rb-0.6.0 Successfully installed concurrent-ruby-1.1.9 Successfully installed tzinfo-2.0.4 Successfully installed tzinfo-data-1.2021.1 Building native extensions. This could take a while... Successfully installed strptime-0.2.5 Successfully installed fluentd-1.13.0 11 gems installed なんでこんなことになったのか なんでこんなことになったのかちょっとだけ考えました。そういや僕最近mac買い替えたんだった。 mac買い替えてなぜかrbenvだけはインストールしていたようです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

1人目が2人目をフォローする、2人目が3人目をフォローする... の様なズレと組み合わせを生成するアルゴリズム[Ruby](zip, rotate)

1人目のユーザーが2人目のユーザーをフォローする。 2人目のユーザーが3人目のユーザーをフォローする。...など、 少しずれた関係性を生み出す際に利用可能なアルゴリズムを考えついたのでここで共有します。 2つのメソッドを組み合わせる事で、生成することが可能です。 結論 users = User.all users.zip(users.rotate) do |following_user, followerd_user| following_user.follow(followerd_user) end 省略形 User.all.zip(User.all.rotate) { _1.follow(_2) } 恐らくこれで動作すると思います!followというtwitterなどで用いられる関係性を生み出す独自メソッドです。省略形はruby2.7以降で動作すると思います! rotate メソッド a = [1, 2, 3, 4] a.rotate # => [2, 3, 4, 1] a # => [1, 2, 3, 4] a.rotate(2) # => [3, 4, 1, 2] a.rotate(-1) # => [4, 1, 2, 3] こちらはメソッド名から想像が着くように、引数に渡した数字分だけ配列の要素をズラすメソッドになります。 zip メソッド p [1, 2, 3].zip(["A", "B", "C"], ["a", "b", "c"]) # => [[1, A, a], [2, "B", "b"], [3, "C", "c"]] p [1,2].zip([:a,:b,:c], [:A,:B,:C,:D]) # => [[1, :a, :A], [2, :b, :B]] こちらのメソッドは痒いところに手が届くようなメソッドです。 self配列に対して、引数の配列の同じインデックスに対応する要素を抽出し、それらを組み合わせて新たな配列を生成するメソッドです。 個人的にはメソッド名が好きです。 まとめ 上記2つのメソッドを活用することで、 ズラした配列を生成し、 元の配列とズラした配列を組み合わせた配列を生成する 新たな配列に対して、2つの要素を引数に取る繰り返し処理 といった流れで、対応していきます。以下動作反応。 users = User.all => TRANSACTION (0.4ms) BEGIN User Load (15.5ms) SELECT `users`.* FROM `users` [#<User id: 1, name: "Test_User1", created_at: "2021-06-20 04:34:55.577201000 +0900", updated_at: "2021-06-20 04:34:56.195767000 +0900">, #<User id: 2, name: "Test_User2", created_at: "2021-06-20 04:34:56.513694000 +0900", updated_at: "2021-06-20 04:34:56.607071000 +0900">, #<User id: 3, name: "Test_User3", created_at: "2021-06-20 04:34:57.040755000 +0900", updated_at: "2021-06-20 04:34:57.147144000 +0900">, #<User id: 4, name: "Test_User4", created_at: "2021-06-20 04:34:57.636523000 +0900", updated_at: "2021-06-20 04:34:57.876562000 +0900"] users.zip(users.rotate) do |following_user, followerd_user| following_user.follow(followerd_user) end TRANSACTION (0.4ms) SAVEPOINT active_record_1 User Load (0.7ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1 Relationship Create (2.4ms) INSERT INTO `relationships` (`follower_id`, `followed_id`, `created_at`, `updated_at`) VALUES (1, 2, '2021-06-21 13:29:30.279783', '2021-06-21 13:29:30.279783') : 余談 これらのメソッドは、Qiitaを通してあるユーザー様にご教授いただきました。 自分の記事に色々とコメントしてくださり、いつも助けられております。 アウトプットする事で視野が広がるいい経験でしたっ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby]スキルチェックDの足し算問題/配列のデータ型を変更

足し算 (paizaランク D 相当)の問題を解いてみました。(プログラミング初心者です。) ちなみに練習問題なので、コードの公開はokのようです。 順を追って答えを導きたいと思います。 もっと綺麗な答えがあるように思いますが、今ある知識で回答を導きたいと思います。 ①値を配列に格納 まず横1行で与えらえる値2つ(今回1と1とします)を受け取り、配列に入れてしまいます。 line=gets.split(" ") ②失敗!普通に足してみた  これで、p numberで出力すると、["1", "1"]と配列に格納されます。 配列を取り出して足せばいいかなと思ったのですが、 配列の中身が文字列stringになっているため、 ↓こうしてしまうと、、11と出力されます。 puts line[0]+line[1] 配列のデータ型を変えるには?? わからなかったので調べました。 以下のコードを追加すれば整数型になります。 line.map!(&:to_i) 他にもいくつかやり方はあるようで、今回以下の記事を参考にしました。 解答コード 一応正解できました。 line=gets.split(" ").map!(&:to_i) puts line[0]+line[1] 頑張れ、自分。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails Tutorialをローカル環境で始めるためのセットアップ例-for windows10

初投稿です。 普段はJavascriptを中心にフロントエンドの勉強をしているのですが、Web開発について体系的に勉強していきたいなと思い、Rails Tutorialをやってみることにしました。ボリューム感もお手頃だし。 はじめに Rails Tutorialの第6版-1章ではAWSのCloud9(クラウドIDE)を用いた実行環境のセットアップが紹介されており、ややこしい環境構築をすっ飛ばしてすぐに学習が始められるよう配慮がなされています。(素晴らしい) そのまま進めるのが安牌なのは分かっているのですが、変なところで凝り性な私は「どうせやるならローカル環境でやりてえよ!!」と思い、色々調べながら環境構築を進めました。 結論何とかなったのですが、案の定色々とハマってしまったので、これからローカル環境でRuby on rails(Rails Tutorial)を始めようとしている初学者の人(よしときゃいいのに)の一助になればと思い筆を執った次第です。 こんな記事を書いておいてなんですが、初学者の方にとってはかなり煩雑な作業になってくるかと思います(爆)ムリだと思ったら今回はローカル環境構築は潔く諦めるのが吉です。無駄に時間食われるので。 ある程度ご経験のある方にとっては、「そんなの知ってるぜ?」という内容になってしまうかと思います。 また私はゴリゴリのプログラミング初学者+Ruby on railsエアプ勢なので、おかしいところがあればご指摘いただければと思います。 この記事で取り扱う内容 Ruby on rails のローカル環境構築(Win10) 前提条件 OSはWindows10 (macは触ったことが無いので何とも言えない) テキストエディタはVSCodeを使用(持っていない人はインストールを。その他のエディタは今回カバー外です。) ターミナルで最低限のコマンドが叩ける(ターミナル?なにそれ?まったくわからん!という場合は少し厳しいかも。) 無限インストールしていく ローカル環境を構築するには、色々なモジュール*のインストールが必要になってきます。 node.jsやgitなど、すでにインストールしているものがあれば飛ばしてもらって大丈夫です。 ※それぞれのインストール方法やセッティングについて詳しく解説しているとキリがないので、最低限度の説明に留めています。私の説明が足りないと感じたら、随時ググってほかのリソースをあたってください。 1. まずは今回作業するディレクトリを作成しておく VSCodeからターミナルを開いてください。(わからない場合は調べてね) 任意の場所に、今回のプロジェクト用の空ディレクトリをmkdirコマンドで作成します。(名前は自由です) それが終わったら、cdコマンドで作成したディレクトリに移動しておきましょう。 とりあえず開いたまま置いときましょう。 2. Rubyのインストール 次にRubyをインストールします。一番最初にこれをやっておいてください。 プログラミング学習ツールで有名なprogateが、下記に添付したリンク先で非常にわかりやすくRubyのインストール方法をまとめてくれています。今回の場合もこちらの手順で問題ありませんので、ここでの説明は省きます。 余裕のある人は、この記事で紹介されているRubyの実行も試してみると良いかも。キチンとインストールされているか確認する意味合いも含めて。 3-1. SQLite3のインストール ローカル用のデータベースとしてSQlite3(えすきゅーらいとすりー)をインストールします。ここが一番面倒です。 まずはSQLite3のサイトへアクセス。 https://www.sqlite.org/index.html 「Download」をクリックします Downloadを押すと、ファイルがたくさんあるページに遷移します。今回必要なのは、 Precompiled Binaries for Windows の欄にある、以下の二つのファイルです。赤枠で囲ってます。(お使いのPCがもし32bitだった場合は、一番上のファイルと一番下のファイルをDLしてください。ほとんどの人が64bitだと思いますが。) SQLite3周りの準備はもう一仕事あります() 以下の手順を行ってください。 3-2.SQLite3のファイルを、Rubyフォルダへ移動する 1.まずはDLした2つのzipをそれぞれ解凍しておく 2.次に、ファイルの移動先である、Rubyのフォルダを開いておきましょう。 ひとつ前に説明したRubyのインストールを行っていると、画像のようなファイルが見つかるかと思います。 これからSQlite3のファイルを置くのは、この中にある「bin」フォルダなので、移動しておきましょう。 Ruby27-x64 > bin というディレクトリ構成になっているはずです。 3.ファイルをRuby27-x64 > binにコピー 全てのファイルを移動するわけではないです。 これから説明する2つのファイルをコピーして、binフォルダの中に張り付けてください。 sqlite-dll-win64-x64-3360000 の中にある「sqlite3.dll」 sqlite-tools-win32-x86-3360000の中にある「sqlite3.exe」 binファイルの中身に、上の画像のようにファイルをコピーできていたら大丈夫です。 3-3.プロジェクトファイルにSQLite3をインストール 1で作業したVSCodeの画面に戻ります。 下記のコマンドでインストールを開始 $ gem install sqlite3 インストールが終わったら、バージョンを確認。 3.35.5 20~などバージョンが返ってくればサクセスです! $ sqlite3 --version 4. Bundlerのインストール ターミナルにて、下記のコマンドでインストールを開始。 $ gem install bundler 下記コマンドを入力、バージョンが帰ってくればインストール成功です! $ bundler -v 5. Node.jsのインストール https://qiita.com/echolimitless/items/83f8658cf855de04b9ce こちらの記事でNode.jsのインストール方法が解説されています。 まだインストールしていない人は、この方法でゲットしちゃいましょう。 インストールが終わったら、一応ターミナルから $ node --version を入力して正しくインストールされているか確認しておきましょう。もしバージョンが返ってこない場合は、一度VSCodeを再起動してみるといけるかもです。 7. yarnのインストール Node.jsをインストールすると、同時にnpmもインストールされているはずです。下記の段取りでインストール&バージョン確認を行ってください。 $ npm install -g yarn $ yarn -v 8. railsのインストール 下記コマンドでrailsをインストールします。 $ gem install rails バージョンを指定してインストールしたい場合は、以下のようにコマンドに追記を行ってください。 $ gem install rails -v バージョン名(6とか) バージョン確認も忘れずに。 $ rails -v 9. vscodeの拡張機能「Rails」「Ruby」のインストール VSCodeで快適にRuby on railsを書くために、拡張機能をインストールします。 この二つですね。 10. webpackerのインストール インストールには以下のコマンドを使用します。 $ rails webpacker:install ダーッとコードが流れればインストール完了です! 11. Gitのインストール こちらもprogateさんによる記事を抜粋したいと思います。 ここに書いてある手順でWindowsにGitをインストールしましょう。 Git,Githubの具体的な使い方については、Rails Tutorial本編で詳しく解説されているので、その手順通りに進めましょう。 環境構築、とりあえず終了。 ここまで進んできた方は、環境構築が完了してます。 railsアプリの作成 アプリを作るには、下記のようなコマンドを使用します。 バージョン名をアンダースコアで囲む + new + アプリの名前(任意) といった感じです。スペースに気を付けて入力してください。 $ rails _バージョン_ new アプリの名前 railsのバージョンが分からない場合は、下記のようにrails -vコマンドで調べてあげればOK。 吐き出されたバージョン番号を全部そのまま入力してください。 $ rails -v Rails 6.1.3.2 railsのアプリ作成を始めるとしばらくターミナルにログが流れるので、煙草でも吸いながら気長に待ちましょう。 とりあえず、ここまで! 少し長くなってしまったので、いったん記事をここで終わります! 走り書きなので、間違っている部分も少なからずあるかと思います。ご了承ください。 あとはRails Tutorialで紹介されているcloud9での環境構築の部分を飛ばして進めていけばOKです。 Herokuへのデプロイの項目で若干本編と異なる作業が必要になる場合があるので、それはまた追ってご紹介できればと思います。では。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

素朴な自作言語Pricのコンパイラをセルフホストした

自作言語 Pric のコンパイラがセルフホストできました! 概要 これまでのあらすじ これまで vm2gol(virtual machine to game of life)というプロジェクト名で以下のようなことをやってきました。 (2018) RubyでオレオレVMとアセンブラとコード生成器を2週間で作ってライフゲームを動かした話 vm2gol-v1 高水準言語の代わりに構文木を直接書き、それをコンパイルして VM で動かすところまで ライフゲームだけコンパイル + 実行できればOK 雑、汚い (2019〜) Rubyで素朴な自作言語のコンパイラを作った vm2gol-v2 v1 のリライト版(機能的にはほぼ同じ)にフロントエンド部分を追加したもの (2020〜) vm2gol-v2 をいろんな言語に移植 アセンブラ・VM は除いてコンパイラ部分のみ TypeScript / Python / Dart / Java / C / Perl / C♭ / PHP / Go / LibreOffice Basic / Zig / Kotlin / Crystal / Rust / Julia / Pascal / OCaml ... 気が向けば続く予定 今回のセルフホスト編は何もないところから全部書いたわけではなく、すでに v2 がある状態から出発しています。 概観 ※ 独自VM向けなので「仮想機械語」のように呼ぶべきかもしれませんが、この記事では単に「機械語」とします。 ※ 図ではコンパイラが直接機械語を出力しているように見えますが、実際はアセンブルの過程があります。説明の簡略化のために省略しています。 T図式でも描いてみました。図の「Pric M」は Pric VM 向けの機械語。 期間 2021-01-12 〜 2021-02-21 規模 第2世代コンパイラのサイズ(行数)はこのくらい(2021-06-21 現在): $ wc -l lexer.pric parser.pric codegen.pric \ > lib/json.pric lib/std.pric lib/types.pric lib/words.pric 250 lexer.pric 973 parser.pric 1325 codegen.pric 170 lib/json.pric 536 lib/std.pric 461 lib/types.pric 305 lib/words.pric 4020 合計 # 補助ツール的なもの $ wc -l check.rb preproc.rb 110 check.rb 19 preproc.rb 129 合計 バージョンと世代 以下、v2/v3 と 第1世代/第2世代 という表現が出てくるので先に説明しておきます。 バージョン: 言語仕様=機能セットの違い Pric v2: ライフゲームはコンパイルできる。セルフホストはできない。 Pric v3: セルフホストに必要な機能を v2 に足したもの 世代: 記述言語の違い 第1世代コンパイラ: Ruby で書いたもの 第2世代コンパイラ: Pric で書いたもの ※ 今回作った v3 ではライフゲーム(game of life)専用ではなくなるのと、やっぱり呼びやすい名前がほしくなって Pric という名前を付けました。 vm2gol = Pric (プロジェクト名・言語名)という雑な捉え方でだいたい大丈夫です。 vm2gol がコードネームで Pric が正式名みたいな雰囲気。 工程 ステップ (1) 第1世代 v2 コンパイラにセルフホストに必要な機能を追加 第1世代なのでここは Ruby のコードの修正です。どのような機能を追加したかについては後述。 できたもの: 第1世代-v3 なコンパイラ ステップ (2) Pric 言語 v3 でコンパイラ v2 を書く 別の言い方でいえば: コンパイラ v2 を Pric 言語 v3 に移植 ステップ (1) で作った第1世代-v3 なコンパイラで第2世代-v2 なコンパイラをコンパイルできるようにする ここからは Pric 言語でもりもり書いていきます。 第2世代コンパイラはこの時点では v2 の機能しかサポートしておらず、ライフゲームはコンパイルできますがセルフホストはまだできません。 v2 コンパイラの移植作業はこれまで何度もやってきていてだいぶ慣れています。Pric 版に一番近い C 言語版もすでにありますし、実装で悩んだら C言語版を見ながら移植していけばOK。また、これまでの移植の過程で整備されてきたテスト用データもそのまま使えます。 できたもの: 第2世代-v2 なコンパイラ ステップ (3) 第2世代コンパイラ v2 を v3 にしていく やることはだいたいステップ (1) と同じ。 できたもの: 第2世代-v3 なコンパイラ … これができれば完成 ステップ (2) の移植作業はこれまでやってきた移植作業と同じようにやればいい。 ステップ (3) は (1) でRuby でやったことを繰り返して Pric で同じように書けばいい。 というわけで、全体の中ではステップ (1) が今回の目玉でした。今回自分にとって一番収穫の大きかったのがステップ (1)。 (1) ができた時点で (3) で何をやればいいか大体の目星が付き、後はやっていけば終わるなという感じで割と気楽に進めていきました。とはいえ、退屈な作業というわけではなく、自作の言語でこれくらいの分量のプログラムを書くのは初めての経験でしたから、 (2) も (3) も十分新鮮でおもしろかったです。 方針 セルフホストに必要かどうかで機能を選ぶ v1 では「ライフゲームのコンパイルに必要か?」で決めていたが、 今回も同様に「セルフホストに必要か?」で判断する。 なるべく寄り道しない / まずは曳光弾を通す / 後回しにできるものは後回し ここらへんも v1 のときと同じ。目標を絞って集中し、長期化させないように。 「とにかくセルフホストできれば細かいところはどうでもいい」くらいの気持ちで セルフホストすることが目的で機能追加はそのための手段 後回しにした機能については後述の「TODO」の節を参照 なるべく少ない機能で ミニマル・シンプルに作りたい気持ち 禁欲的に / ケチケチと (1) で機能を盛りすぎると (3) が大変になりそう ステップ (1) で機能を追加するとき、つねに「これを追加するとステップ(3) で大変にならないか?」と考える。ステップ (3) の負担がなるべく小さくなるように意識する。 DQNEO さんの minigo のセルフホストの話などで大変そうな話を見聞きしていたのが影響していると思います。先達の経験談、ありがたや……。 C言語を参考にするが、あくまで参考程度。C への準拠にはこだわらない。 自作言語でセルフホストするのは今回初めてなので、無理しない。適宜ハードルを下げる。 空間効率、速度効率は気にせず富豪的に。開発効率的に辛くなってきたらそのときなんとかする。先回りしすぎないように。 なるべく凝ったことをせず、多少冗長な書き方だったり遅い書き方でもよいので確実に動きそうな方に倒す(そこらへんの改善はセルフホストできた後でやればよい) セルフホストに必要な機能 はっきり決めていたわけではありませんが、基本的には C言語版に近い感じになるのかなとイメージしていました。 そうすると、 C言語版では使っていて Pric v2 にない機能をリストアップしていけば、それが v3 で追加すべき機能の候補ということになります。 入出力 ポインタ ヒープ / malloc 配列 文字列 構造体 グローバル変数 プリプロセッサ ファイル分割 / #include マクロ / #define ざっとこんなところかなあとぼんやり考えて、後は作りながら決めていきました。 以下、思い出しながらそれぞれについて書いてみました。 ※ 以下は説明のために整理していて、実際に考えたり作った順番とは多少違いがあります。 ※ ちなみに、私はC言語はずっと前にちょっとやった程度でだいぶ忘れています。素人が書いていると思って読んでいただきたく……勘違い・浅い理解・不正確な記述などあると思います。 入出力 1文字ずつ読み書き。これは必要。 VM に命令を追加: read, write 入り口と出口で文字と数(アスキーコード)との変換を行い、コンパイル処理本体で扱っているのはすべて単なる数(整数)となるイメージです。 "def main()\n ..." ↓ read 100 101 102 32 109 97 105 110 40 41 10 ... ↓コンパイル 32 32 99 97 108 108 32 109 97 105 110 ... ↓ write " call main\n exit\n ..." (アセンブリコード) こうやって見てみると、コンパイラがやっていることは「ある数の並びを別の数の並びに変換する」処理だと見ることができます。おもしろいですね。結局それしかやっていないというか。 それはさておき、出力ができるようになると print デバッグができるようになります。また、出力を検証する形でテストできるようになり、これだけでもだいぶ進歩を感じるものです。「おっ、この機能ができたらアレとアレができるようになるじゃん!」という、ブートストラップの楽しさ。 hello world はこんな感じ。こんなのでも動くと嬉しい。 def putchar(c) write(c, 1); end def main() putchar( 72); # H putchar(101); # e putchar(108); # l putchar(108); # l putchar(111); # o putchar( 44); # , putchar( 32); # (SPACE) putchar( 87); # W putchar(111); # o putchar(114); # r putchar(108); # l putchar(100); # d putchar( 33); # ! putchar( 10); # (LF) end 比較演算子 演算子としては小なり < だけ追加しました。これだけ追加すれば、 v2 からある ==、!= と組み合わせてあれこれできます。 VM にレジスタを追加: sf (sign flag) VM に命令を追加: jump_g ... x86 の jg 命令を参考に ファイル分割 #include lib/std.pric のような行を探し、指定されているファイルを読み込んでその場に埋め込むスクリプトを Ruby で書きました。 が、これは単に cat コマンドで繋げるだけもよかったですね。そうしておけばさらに簡略化できた。 ※ 機能として挙げましたが、この部分はコンパイラの本体に含めず、補助ツールの類である(セルフホストの対象外)ということにしました。 アドレス演算子・デリファレンス演算子 C の & と * ステップ (1) の中ではここがキモ キモといいつつちょっと怪しいので改めて整理したい…… 「ふつうのコンパイラをつくろう」を読み返す デリファレンス演算が左辺にあるときと右辺にあるときの違い chibicc や cbc の出力するアセンブリを観察 なるべく最適化されていないアセンブリ出力を見たい Compiler Explorer もお手軽・便利 アドレス演算のために VM に lea 命令を追加 x86 の lea 命令を参考に 「ポインタがある」というか「アドレス演算とデリファレンス演算と、アドレスの入った変数がある」といった雰囲気(一緒?) デリファレンスの括弧の中の部分は単なる式ということにしています。 *( some_addr_ + 2 ) ^^^^^^^^^^^^^^ この部分は単なる式 途中紆余曲折があって *(some_addr_, 2) のような書き方を試したりしましたが、括弧の中にはただの式を書くことにして単純化し、C っぽい見た目に落ち着きました。 デリファレンス演算の視点(?)では「とにかく何か値(式の評価結果=アドレス)が渡されるので、そのアドレスが指している場所を操作対象とする」のように考えればよい、ということに。 (余談)乗算の * と被るのでデリファレンス演算子には別の記号を使うのはどうかと考えて ^ にしてみたり、前置じゃなくて後置にするのはどうだろう? と考えて ( ... )^ にしてみたり……という試みが途中ありました。 下手な考え休むに似たりかと思いきや、Pascal ではそのように書くのだと後から知りました。図らずも似たところに辿りついていたようでちょっとおもしろかったです。 他には addr(some_var), deref(addr_expr) のように普通の関数っぽい構文にしてしまう、という案もありました。パースは少し楽になりそうですが、視認性が悪そう(普通の関数と見分けにくそう)な気がしてボツに。 ヒープ / malloc メモリのスタック領域として使っていた部分のうち、アドレスの小さい方の端をヒープとしました。 malloc もまじめに作らないといけないのかなと思ってあれこれ調べていたんですが、単に確保した分だけカーソルを動かすだけなら簡単で私でもすぐ作れそうね、じゃあそれで、ということになりました。 ※ bump allocator とか stack allocator と呼ばれている方式だそうです。 参考: Allocator Designs | Writing an OS in Rust というわけで allocate 関数の中身はこれだけ。 def allocate(g_, size) # 変更前のカーソル位置 var head_addr = *(g_ + GO_ALLOC_CURSOR()); # 変更後のカーソル位置 var next_addr = *(g_ + GO_ALLOC_CURSOR()) + size; # カーソル位置を更新 *(g_ + GO_ALLOC_CURSOR()) = next_addr; # スタックと衝突していないかチェック check_heap_stack_overlap(g_); # 確保した領域の始点アドレスを返す return head_addr; end (g_ はグローバル変数構造体のアドレス。後述。) free はありません(C言語版でも free してなかった)。ここは富豪的に。 富豪的にやってどのくらいメモリが必要だったかというと、最終的には番地の数で 2,600,000 となりました。1 byte/番地で換算すると約 2.6 MB。 あと、ヒープとスタックの衝突を Pric コードのレイヤーで検出する作りにしていて、そのためにレジスタ sp の値を取得する組み込み関数を追加しました。ここはちょっとやっつけ。 配列 配列は作る前からイメージがあって特に詰まらずに作れました。 「低レイヤを知りたい人のためのCコンパイラ作成入門」にならって、普通のローカル変数と同じようにスタックに置きます。 構造体の配列を使いたい場合は構造体のデータそのものは持たせず、ポインタというかアドレスを持たせる形で使います。もしデータそのものを持たせるとしたら各要素のオフセットの計算は自前でやらないとダメ(言語によるサポートはない)。 配列変数名[添字] で配列の要素にアクセスする構文を一度用意して、ステップ (1) では実装までしましたが、ステップ (3) でやることが増えるので取りやめにして実装も削除しました。 デリファレンス演算子があれば a = *(xs_ + 1); *(xs_ + 1) = 2; のように配列の要素を読み書きできます。つまりポインタ演算のシンタックスシュガーなわけですね。だったらなくてもいいかなと。 ただし、「これは配列を扱っているんだぞ」という意図を表すために実際は下記のような関数を介して操作するようにしています。 def aset(array_, i, val) *(array_ + i) = val; end def aget(array_, i) return *(array_, i); end Pric 言語で書けるものに関してはこのようにユーティリティ関数を作っていきました。 それで、結局配列のために手を加える必要があったのは 変数宣言時に幅を考慮(スタックポインタを幅1つ分ではなく配列の長さの分ずらす) 変数名からスタック上の位置を割り出す際に配列変数の幅を考慮 これだけといえばこれだけですね。 「配列という機能を追加した」というよりは「変数の幅の考慮とポインタ演算を組み合わせることによって配列の操作に相当する操作ができるようになった」という感覚です。 文字列 配列があればOK。プログラマ(私)が「これは文字列なのだ」と思い込むことにより文字列として扱うことができます。 たとえば C だったらこのように書くところを、 char str[] = "hello"; Pric では次のように書きます。 var [6]str; aset(&str, 0, 104); # h aset(&str, 1, 101); # e aset(&str, 2, 108); # l aset(&str, 3, 108); # l aset(&str, 4, 111); # o aset(&str, 5, 0); # (NUL) さすがに毎回手で書くのは面倒なので適当なスクリプトで生成してターミナルからエディタに貼り付けていました。 また、コード上での行数が多くて邪魔なのと、複数箇所で同じ文字列を使うケースがあることから、実際は次のように文字列に値を詰める処理を関数にして使っています。 var [6]s_hello; set_s_hello(&s_hello); ちなみに、文字列関連の何かを調べていたときに Pascal の文字列(先頭に文字列長を持つ)について知りました。Pascal 方式の方がバグが発生しにくそうな感じがしてちょっとなびきかけましたが、ひとまず C に寄せました(Pascal よりは C の方がなじみがあるため)。 構造体 構造体のサイズ分の領域の確保ができて、 起点と、各メンバのオフセットと幅が扱えて、 あとはポインタ演算ができれば読み書きできる 心の目で見ると構造体のようなものに見える何か。これもすでにある機能でまかなえてしまいます。 構造体のデータはヒープに置いていていますが、これはそういう制約がある訳ではなくとりあえずのルールです。たぶんスタックにも置けるはず。 some_struct.some_member や some_struct->some_member のような構文は用意せず、操作用のインターフェイスとなる関数を作ってその中でごにょごにょしています。 例を挙げると以下は List 構造体の size メンバの値を返す getter 関数です。アドレスがあればそれで操作できるぞい、というノリ。 def List_size(self_) var size = *(self_ + LIST__SIZE()); return size; end # 呼び出し側 # C でいえば list_->size に相当 var list_size = List_size(list_); 結局、メモリ上の位置とデータがあって操作がある、ということでしょうか(それはそう)。 グローバルな定数 関数はどこ(他のどの関数)からでも呼び出せますから、固定値を返す関数があればグローバルな定数と同等なものとして扱えます。マクロのような仕組みを追加しなくても、すでにある関数を使えばOK。じゃあそれで。 ※ ということにしましたが、コンパイラの機能に含めないことにしてプリプロセッサでやってしまってもよかったかも。 たとえば 1つ前の「構造体」の節のコード例に出てくる LIST__SIZE() がこれです。 グローバル変数 たとえば ELF だと .data セクションや .bss セクションに置く、という話があります。 しかし、その方向で進めるとなるとアセンブリ・機械語のファイルのフォーマットやメモリレイアウトも考えないといけなくなります。話が大きくなってハードルが上がってしまう(完成までの道のりが長くなってしまう)ように思われました。 そこで、グローバル変数に関してもすでにある機能でなんとかできないかと。 要するに どこでも使えて 読み書き両方できる という要件を満たせれば、その何かをグローバル変数相当のものとして使えます。 そこで思いついたのが、エントリポイントである main 関数で構造体を1つ用意して、そのアドレスを関数の引数でバケツリレーしていく方法です。よし、じゃあそれで。 機能追加まとめ 入出力: 追加した 比較演算子: < だけ追加した ファイル分割: セルフホストの対象外とした アドレス演算子・デリファレンス演算子: 追加した ヒープ / malloc: ほぼ追加なし 配列: 幅を考慮して変数を扱えるようにした 文字列: 追加なし 構造体: 追加なし グローバルな定数: 追加なし グローバル変数: 追加なし こうして見てみると、今回は アドレス演算子・デリファレンス演算子を使って間接参照ができるようになった メモリ上のある範囲をまとめて扱えるようになり、間接参照と組み合わせて配列・文字列・構造体相当のデータが扱えるようになった ことの比重が大きく、これらを利用することでできることの幅がかなり広がった、と振り返ることができそうです。 また、グローバルな定数や配列・構造体操作用の関数に加えて -, <=, &&, || などの演算子も関数でなんとかしていて、関数があることでなんとかなっている部分も多いですね。関数えらい。抽象化の力。 感想など v1 のときほどのがんばりは必要なく(そういう意味では盛り上がりに若干欠け)比較的落ち着いて進めた感じでしたが、 それはそれでOK。十分楽しく、達成感もありました! 先にC言語版を作っていたのが結果として良かった。ここで悩まずに済んだのも大きかった。 今回セルフホストまで達成できましたが、思い返してみれば 一度 v1 を作ったことが足がかりとなり、視界が開けて「自走できるようになった感」が得られたように思います。 がんばって v1 作って良かったなあとしみじみ。 ステップ (1) の部分は単純に知識・経験が得られてよかった(低レイヤーの知識がまたちょっと増えた) メモリ操作用言語でメモリ操作している、みたいな感覚が体験できた (今回は)構造体や文字列など、いろんなものを「見立て」でなんとかしている。つまり、概念は開発者(=自分)の頭の中にある。「これ不便だからこうしたら便利じゃね?」みたいなアイデア(シンタックスシュガーなど)も同様。 現在使われている言語の機能も、かつて開発者の頭の中にあったものが歴史を経て少しずつコードに落とされてきたものの蓄積……と考えると感慨深いものがありますね。今もいろんな人の頭の中に未来の便利機能のアイデアが眠っているのでしょう。 節目で何度か言語処理系Slackで進捗報告してリアクションをいただけたのがたいへん励みになりました。ありがとうございました。 TODO まずは文字列リテラル(に相当する部分)を静的なデータとして使いまわせるようにするところから手を付けたいですね。 おそらくここが速度的に一番のボトルネックになっているので、ここをなんとかするだけでかなり速くなって、後が楽になるんじゃないかと。 パッと思いつくもの、やりたいなーと思いつつ自重していたのはこのあたり: 文字列リテラル 配列アクセス演算子 [] 演算子の優先順位 明示的に ( ) で囲めば済むので結局今回もお流れとなった 演算子の追加 -, /, %, >, !, &&, etc. &&, || の短絡評価 まともな構造体 まともなグローバル変数 型 関数の途中での return break/continue その他、雑多な改善: リファクタリング・パフォーマンス改善 あんまり詳しく考えてないですが、C に近づいていくのかなあ。 v1, v2 では初心者向け教材っぽくしたくて(想定読者=どうやってコンパイラを作ればいいのか分からなかった頃の自分)「なるべくベタな仕様・見た目にしたい」「高度な機能や独自色をなるべく入れない」という気持ちがあったのですが、v3 ではその縛りは外してしまってもいいかなと思っています。 というわけで本体は以上で終わりです。以下、苦労話や枝葉な部分のメモです。 苦労話 遅い lexer, parser, codegen を全部コンパイルすると現状では 15分くらいかかります(第2世代コンパイラが育つに連れて伸びていき、最終的にこのくらい)。 ふつうに辛いのですが、終盤ではゴールが近いと分かっていたので下手にジタバタせず逃げ切りました。特に対策せず、実行して待ってる間に部屋の掃除、実行して部屋の掃除、を繰り返していました。部屋がちょっときれいになりました。 ※ 「TODO」の節で書いたようにこれから速くしていく予定です 配列の範囲外アクセス プログラムが大きくなってくるとアセンブリや機械語やメモリのダンプを見てもさっぱり分からん……ので git reset --hard して歩幅を小さくしてやり直して、という対処で8割くらいはなんとかなった気がします。残りの2割はがんばってなんとか。gdb とか便利なものがないのでここはちょっと辛いですね。 ただ、長くても数時間くらいでなんとかなるレベルで、バグの原因が判明するまで何日もかかるということはありませんでした。 なるべく複雑なことをしないように作っていたのが良かったかもしれません。 エラー発生時にスタックトレースが見れない print デバッグなどでがんばる……。 ある関数で配列の範囲外アクセスが原因と思われるエラーが発生し、おそらく呼び出し元で範囲指定をミスしており、そして呼び出し元がたくさんあり、まず呼び出し元の特定が必要……のようなパターン。 セルフホスト達成後に試したところ、VM などにちょいと細工をするだけでスタックトレースを見られるようにできました。若干チート感がありますが、これはさっさとやっておけばよかったかも。 関数呼び出し時の引数の個数のまちがい これは結構鬱陶しくて、よく間違えるのと、個数が違うのが原因だと分かりにくく不毛だったため、さすがに対策しました。 構文木の段階で関数の定義と呼び出しを突き合わせてチェックする数十行程度のスクリプトを、これは補助ツールだから……と言い訳しつつ Ruby で書き、パース処理とコード生成処理の間に挟みました。 すごく原始的な静的解析でしょうか。 おまけ・細かい話 v2 から v3 への変更のうち、セルフホストにはそんなに関係ない部分、その他。 文頭の set, call, call_set を廃止 v2 では文頭に set, call, call_set を明示的に書く方式にしていましたが、これを不要にしました。ある程度量を書くとなると、やはり鬱陶しいので。これは割と小さめの修正でOK。 -set a = 123; +a = 123; -call foo_func(); +foo_func(); -call_set a = foo_func(); +a = foo_func(); 関数の引数として任意の式を書けるようにし、また、関数呼び出しを式として扱うようにしました。これにより、たとえば次のように関数呼び出しを関数の引数として書けるようになります。 foo_func(bar_func()); だいぶ普通の言語っぽくなりました。 case 文の else v2 には else がなく case when ... # ... when (0 == 0) # else の代わり # ... end のように書いていたところを、次のように else で書けるようにしました。 case when ... # ... else # ... end if 文 case文があれば if文はなくていいかなと思っていたのですが あればあったでちょっと書きやすい 少しの手間(数行の変更)で実現できそう ということで書けるようにしました。 レキサで処理しています。if が来たら case when の2つのトークンに変換するだけ。 if ( ... ) ... end ↓ 変換 case when ( ... ) ... end これだけで else 節のある if文も書けるようになります。 if ( ... ) # ... else # ... end ↓ 変換 case when ( ... ) # ... else # ... end Ruby っぽい見た目に変更 すでにここまで貼ったコード片で見てきた通りで、 Ruby っぽい見た目に変えてみました。見た目は Ruby っぽいけど中身は C っぽい、ちょっと不思議な書き味です。 これはなんとなくできそうだったので、試してみて、なんとなくこうなったというものです。 v2 とのギャップを小さくするために JavaScript っぽい構文に戻そうかなとも考えていましたが、元に戻すのが億劫になるところまで進めてしまって、めんどくさくなって結局そのままという。 アドレス演算子、デリファレンス演算子があるため Ruby と文法コンパチにはなっていないと思います。 ポインタ変数 コードの見た目だけでは通常の変数なのかアドレスが入ってる変数なのか、使用箇所を見ても宣言を見ても分かりません。 var a; # 普通の整数が入る var b; # アドレスが入る そこで、見た目で瞬時に判別できるように、アドレスが入っている変数には末尾に _ を付けています。 def foo( arg1, # 普通の変数 arg2_ # アドレスが入っている ) # ... end foo( a, # 普通の整数を渡している b_ # アドレスを渡している ); というわけでおまけコーナーは以上です。また何か思い出したら追記するかもしれません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Active Storageを使ってプロフィールに画像をつける

はじめに オリジナルアプリの新規登録の際に、画像を入力するフォームを追加して、プロフィールに画像が入れ込もうと思いました。 今回はActive Storageを使っていきたいと思います。 Active Storageとは ファイルアップロード機能を簡単に実装できるGemです。 Active Storageを使うと、画像などのファイルのアップロードを簡単にするメソッドが使用できたり、画像を保存するテーブルを簡単に作成できます。 使用するツールたち ImageMagick 画像に処理を加えることができるツールです。 画像の作成やサイズ変更、保存形式の変更などがあります。 Railsでは単体で使用する事はできず、MiniMagickというgemが必要です。 MiniMagick ImageMagickの機能をRubyで扱えるようにしてくれるGemです。 RailsでImageMagickを扱うために必要となります。 ImageProcessing MiniMagickでは提供できない、画像サイズを調整する機能を提供するGemです。 実施に導入 まずはmageMagickをインストールします。 brew install imagemagick 注意 VSCode内のターミナル内でインストールをしたらエラーが発生する可能性があるみたいなのでターミナルアプリから実行して下さい。 実際のエラー ↓↓↓↓↓ Error: No available formula or cask with the name "imagemagic". ==> Searching for a previously deleted formula (in the last month)... Error: No previously deleted formula found. ==> Searching taps on GitHub... Error: No formulae found in taps. 続いて、MiniMagickとImageProcessingをインストールします。 Gemfile gem 'mini_magick' gem 'image_processing', '~> 1.2' しっかりターミナルでインストール! bundle install 最後にActive Storageをインストール ターミナルで実行します。 rails active_storage:install コマンドを実行すると、Active Storageに関連したマイグレーションが作成されます。続けてマイグレートします。 rails db:migrate ここまではポチポチして頂ければできると思います。 DBにはこのようなテーブルが追加されます。 簡単に説明すると active_storage_blobsは実施に写真などが保存されるテーブルで、active_storage_attachmentsは中間テーブルになります。 画像を保存する 準備が整ったので実施に保存していきます。 画像を保存する際の手順は2つです。 Active StorageのテーブルとUserテーブルのアソシエーションを定義 application.rbにて、画像カラムの保存を許可 まずはUserテーブルに紐づけるためにhas_one_attachedというメソッドを利用します。 has_one_attachedとは各レコードとファイルを1対1の関係で紐づけるメソッドです。 has_one_attachedメソッドを記述したモデルの各レコードは、それぞれ1つのファイルを添付できます。 user.rb class User < ApplicationRecord has_one_attached :avatar #ファイル名はなんでも大丈夫です end 次にストロングパラメーターで保存の許可をします。 application_controller.rb class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? private def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :company_name, :profession_id, :avatar]) end end 新規登録フォームに記述します。 new.html.erb <div class="form-group"> <label>プロフィール写真</label> <%= f.file_field :avatar, class:"input-default" %> </div> このような形でファイルを開く事ができます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby] AtCoder Beginner Contest 205 B問題

はじめに AtCoderの過去問にハマってしまいました 笑 今回はAtCoder Beginner Contest 205 B問題「B - Permutation Check」にRubyで挑戦です!よろしくお願いします。 問題文は下記リンクから飛んで確認してください。 問題 B - Permutation Check まずは入力を受け取る記述です。 n = gets.to_i a = gets.split.map(&:to_i) 正直のところ、入力値の受け取り方からかなり苦労してます。 慣れみたいなところはあるのでしょうが、まだまだ記述に困ってしまいます。 入力を受け取る記述ができたらまず、空の配列を作ります。 受け取った、nの数字分1からこの配列に入れていきます。 例えばnが6だったら、1, 2, 3, 4, 5, 6 という配列が欲しいので、その配列を作成するための記述を書いていきます。 n = gets.to_i a = gets.split.map(&:to_i) ary =[] n.times do |nn| ary << nn + 1 end ブロック変数の名前が思いつかなくて、めちゃくちゃ悪い例だと思います。。。 こういう時のわかりやすい名前の付け方とかあればぜひコメントにて教えていただけると幸いです。 nn + 1 としているのは、もし+ 1していないと、0 ~ 5になってしまうからです。(配列は添字で要素を管理しているため) ここまでくれば、あとはこの配列aryの中の数字が、aの中の数字と一致しているか(順番は関係ない)を調べて、全て一致していればYesを出して、一つでも違えばNoを出力してやれれば完成です。 n = gets.to_i a = gets.split.map(&:to_i) ary =[] n.times do |nn| ary << nn + 1 end if ary.all? { |i| a.include?(i) } puts "Yes" else puts "No" end これで完成です。ポイントはこのall?メソッド。 このall?メソッドとinclude?メソッドの組み合わせを見つけるのに苦労しました。 おまけ all?メソッドの使い方を説明します。まず、書き方は下記のとおりです。 オブジェクト.all? { |要素| ブロック処理 } all?メソッドは配列の中身を全て取り出し、ブロック変数に一つずつ入れていきます。そして、{}のなかに書かれている処理の返り値が全て真の時にtrueを返すメソッドです。 include?メソッドは()の中の要素が、配列の中にあるかを調べ、存在すればtrueを返します。 つまり、先ほどの回答の記述は、all?メソッドで配列aryの中の要素全てがブロック変数iに一つずつ代入され、include?メソッドの()の中に入ることによって、変数aの中に、aryから取り出した要素があるかどうかを調べています。 そして、全てのaryの要素が配列aの中に存在すると判断されればall?メソッドが初めてtrueを返します。 最後に これまであまりアルゴリズムには触れてこなくて、初めて見るメソッドが多かったりします。何回も繰り返して使いこなしたいと思いました。 今回のall?メソッドは大きな収穫です!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby on Rails] jscrollを使って無限スクロール

完成GIF動画 1.gem導入 ターミナル gem 'kaminari' gem 'jquery-rails' $ bundle install 2.jscrollのHPを確認 jscroll公式サイトが以下になります。 https://jscroll.com/#/ デバックの方法なども書いてあるので、一度確認しておいた方がいいです。 githubからjquery.jscroll.min.jsをダウンロードしてassets/javascripts直下に入れます。 [code]クリック → [Download Zip]クリック 3.jqueryを使用できるようにする app/assets/javascripts/application.js //= require jquery3 //= require jquery.jscroll.min.js 4.controllerの編集する 今回はPost/indexアクション内で無限スクロールさせます。 controllers/posts_controller.rb def index @posts = Post.page(params[:page]).per(12) //一度に12件表示させる end 5.viewsの編集 views/posts/index.html.erb <div class = "jscroll"> <%= render "posts/index", posts: @posts%> <%= paginate @posts %> //kaminari導入で使用可能 </div> 必ずpaginate @postsを、jscroll内に入れること。 6.javascriptsの編集 assets/javascripts/application.js $(document).on('turbolinks:load', function() { $('.jscroll').jscroll({ nextSelector: 'a[rel=next].page-link', //次に表示させたいページのリンク先 contentSelector: '.jscroll' //[jscroll]に追加表示させる }); }); おまけ 「言われた通りにやってるのに、なぜできないんだ!」なんて時にデバック出来たらいいですよね。 ちゃんと、jscrollにもデバック機能がついています。 When set to true, outputs useful information to the console display if the console object exists. nextSelector: hoge, contentSelector: hoge, debug: true debugをtrueとすることで、検証ツールにwarningなどを表示させてくれます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CakePHP v3.6.x から v4.5.x へのバージョンアップについてのメモ

CakePHP 3.5.x から 4.2.x へバージョンアップする際に、苦労した点などをマトメていきます。 この記事は徐々に、更新していく予定です。 私が携わった案件では、サーバをインターネット環境に接続できないという制約があったため、composerは使用せず、全て手動でバージョンアップしています。 CakePHP4.xの移行ガイドに書かれていることについては、この記事では触れません。 移行ガイドだけ読んでいても気づきづらいことや、予め一覧化されていた方がよい変更点などをマトメていきます。 ↓ v4.2の移行ガイドはこちら ↓ no link next time link post
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む