- 投稿日:2020-07-08T23:16:27+09:00
gemでインストールしたrailsコマンドがなぜ使えるのか??
はじめに
※windowsのローカル環境下です。※
表題の通り、気になったので調べました。
bin/railsで実行するかrailsで実行するか??
まずは事前知識として、bin/railsで実行するかrailsで実行するか??結論、どちらを使っても基本問題ないですが、参照元が違います。
binをつけるとプロジェクトディレクトリを参照。
binを付けないとローカル(グローバル)のrailsを参照。
続いて本題。
gemでインストールしたrailsコマンドがなぜ使えるのか??
railsコマンドはローカル環境変数でPath通してるはず。。。→これはとても浅い勘違い!!
railsは以下のようにgemでインストールしています。
$ gem install rails -v "5.2.3" $ rails -v Rails 5.2.3
では、なぜgemでインストールしたrailsでrailsコマンドが通るんだろう??調べました。
環境変数には以下があります。
C:\Ruby26-x64\bin
ここを確認するとこんな感じ。
$ ls Ruby26-x64/bin _guard-core* coderay.bat nokogiri* rails.bat ruby_builtin_dlls/ spring* _guard-core.bat erb* nokogiri.bat rake* rubyw.exe* spring.bat bundle.cmd erb.cmd pry* rake.bat sass* sprockets* bundler.cmd gem* pry.bat rake.cmd sass.bat sprockets.bat byebug* gem.cmd puma* rdoc* sass-convert* sqlite3.def byebug.bat guard* puma.bat rdoc.cmd sass-convert.bat sqlite3.dll* chromedriver-helper* guard.bat pumactl* ri* scss* thor* chromedriver-helper.bat irb* pumactl.bat ri.cmd scss.bat thor.bat chromedriver-update* irb.cmd rackup* ridk.cmd setrbvars.cmd tilt* chromedriver-update.bat listen* rackup.bat ridk.ps1 slimrb* tilt.bat coderay* listen.bat rails* ruby.exe* slimrb.bat x64-msvcrt-ruby260.dll*
railsがありました。これを参照しているっぽい。ですが確証が得られないのでさらに調べる。
gemsでインストールした各種ライブラリは以下にあります。
C:\Ruby26-x64\lib\ruby\gems\2.6.0\gems
rails 5.2.3の中身を確認
$ ls Ruby26-x64/lib/ruby/gems/2.6.0/gems/rails-5.2.3 README.md
あれ??どうやって参照している??whichコマンドを使ってみる。
$ which rails /c/Ruby26-x64/bin/rails
解決しました◎やはりRuby直下のbinディレクトリでrailsは参照されていました。
ちなみに、なんで参照されるか。。。
どうやらwhichコマンドは以下のルールらしい。
whichコマンドは、環境変数のPATHに設定されているディレクトリ順に調べ、最初に見つかったコマンドを表示
whichコマンドがこのような参照方法であればコマンドもそうであるはず!!合点✌
備考:bundle installする際にパスを指定する
指定しないで実行すると前述のとおり、以下にインストールされます。
C:\Ruby26-x64\lib\ruby\gems\2.6.0\gems
パスを指定する方法は以下
bundle install --path vendor/bundle
これでプロジェクト直下のvendor/bundleにインストールできます。ちなみにrails new時デフォルトでbundle installも実行されますが、Bオプションを付けることで実行スキップできます。
rails new rails_app -B
一度オプションを付けてインストールすれば、以下に設定が保存されて以降は指定不要。プロジェクト/.bundle/config
--- BUNDLE_PATH: "vendor/bundle"
参考
Rails 4.1以降のコンソールコマンドは必ず bin/ を付けなきゃいけないの?railsでbundle installする時にインストールパスを指定
おわりに
日ごろからローカル環境の扱いにはもっと意識を向けていきたいと思いました。
- 投稿日:2020-07-08T23:00:06+09:00
Railsから入った自分は【attr_accessor】が何か全くわからなかった
Rubyの基礎文法などを見ているとちょくちょく登場するattr_accessorというメソッド。
Ruby on Railsから入った自分は、最初見たときには全く使用用途が分かりませんでした。
そこからいろいろ調べてみるも
- セッターとゲッターの両方を担うメソッド
- インスタンス変数の値を参照、変更する
などなど正直理解不能です…
わかっている方には当た有前すぎて何言ってんだこいつ状態かもしれませんが、もし自分と同じところでつまずいた方がいらっしゃれば参考にしていただけると幸いです。
attr_accessorの使用用途
まずは問題のattr_accessorメソッドですが一言でいうとインスタンスに外部から参照、更新可能な属性を持たせるメソッドです。
一つ一つ解説していきます。
インスタンスの値には直接アクセスできない
Railsを使っていると忘れがちなのですが、大前提としてインスタンスの値には直接アクセスすることはできません。
例として以下にHogeクラスを用意しました。インスタンス変数としてnameとageの2つがあります。
attr_accessor.rbclass Hoge def initialize(name, age) @name = name @age = age end def hello p "Hello! I'm #{@name} " p "I'm #{@age} old" end end hoge = Hoge.new("hogehoge",12) hoge.hello p hoge.nameこの状態でHogeのインスタンスであるhogeの変数であるnameを出力しようとしてみると…
"Hello! I'm hogehoge " "I'm 12 old" Traceback (most recent call last): attr_accessor.rb:33:in `<main>': undefined method `name' for #<Hoge:0x0000000004e68eb8 @name="hogehoge", @age=12> (NoMethodError)とNoMethodErrorが発生します。
それもそのはず。Hogeクラスにはnameというメソッドが存在していないためです。
こういったインスタンスの値にアクセスできない問題を解決するためにセッターとゲッターというメソッドが出てきます。
ゲッター
先ほどのようにインスタンスの値を参照したいときに使われるのがゲッターです。ゲッターの設定方法には2種類あり、メソッドを定義する方法とアクセスメソッドを使う方法です。
メソッドを定義する方法は実にシンプルで
class Hoge def initialize(name, age) @name = name @age = age end # インスタンス変数と同じ名前にすることで値の参照を実現させる def name name = @name end def hello p "Hello! I'm #{@name} " p "I'm #{@age} old" end endと実行した際にインスタンス変数を引っ張ってくるメソッドを定義します。
ただこれだと少しかっこ悪いのでアクセスメソッドを使った方がまとまりが良いです。
class Hoge attr_reader :name def initialize(name, age) @name = name @age = age end def hello p "Hello! I'm #{@name} " p "I'm #{@age} old" end endattr_readerメソッドを使いことで上のようにインスタンス変数の値を参照できます。この際定義する値はインスタンス変数で定義したものと同じでなければいけません。
これが値を参照するためのゲッターという役割です。
セッター
それでは値の参照ができるようになったので、Railsと同じような感覚でインスタンスの値に別の値を代入してみると…
class Hoge attr_reader :name def initialize(name, age) @name = name @age = age end def hello p "Hello! I'm #{@name} " p "I'm #{@age} old" end end hoge = Hoge.new("hogehoge",12) hoge.hello p hoge.name hoge.name = "fugafuga" -------------------------------------------------------------------------------- "Hello! I'm hogehoge " "I'm 12 old" "hogehoge" Traceback (most recent call last): attr_accessor.rb:37:in `<main>': undefined method `name=' for #<Hoge:0x0000000004fbb040 @name="hogehoge", @age=12> (NoMethodError) Did you mean? nameとまたもや怒られてしまいます。
これはゲッターでは値を参照することができても、更新する機能は含まれていないので新しい値を代入できないためです。
この問題を解決するために使われるのがセッターと呼ばれるものです。
セッターもメソッドをそのまま定義する方法とアクセサメソッドを使う方法の2種類があります。
def name=(name) @name = name endメソッドの後に=を付けるとセッターになります。
内容はシンプルで受け取った引数をインスタンス変数に反映するだけです。アクセサメソッドを使う方法。
class Hoge attr_reader :name attr_writer :name def initialize(name, age) @name = name @age = age end def hello p "Hello! I'm #{@name} " p "I'm #{@age} old" end endアクセサメソッドはattr_writerを使います。
hoge = Hoge.new("hogehoge",12) hoge.hello p hoge.name hoge.name = "fugafuga" hoge.hello ------------------------------------------------------ "Hello! I'm hogehoge " "I'm 12 old" "hogehoge" "Hello! I'm fugafuga " "I'm 12 old"と確かにnameの値が更新されています。
attr_accessor
いよいよattr_accessorの解説ですが上記のゲッターとセッターの両方を担うのがこのメソッドの役割ということになります。
つまりattr_reader, attr_writerの2種類を書かなくても
class Hoge # この一行でゲッターとセッターの役割を果たしている attr_accessor :name def initialize(name, age) @name = name @age = age end def hello p "Hello! I'm #{@name} " p "I'm #{@age} old" end end hoge = Hoge.new("hogehoge",12) hoge.hello p hoge.name hoge.name = "fugafuga" hoge.hello ---------------------------------------------------------- Hello! I'm hogehoge " "I'm 12 old" "hogehoge" "Hello! I'm fugafuga " "I'm 12 old"とかなりシンプルに書くことができます。
ここまで読んでいて不思議に思ったかもしれませんが、Railsでは通常このアクセサメソッドは出てきません。
ではなぜメソッドのようにテーブルの値を参照、更新できているのかというと、ActiveRecord::Baseで自動機的に設定されているからです。
よければそのことについても記事を書いているのでご覧下さい
>> rails db:migrateとモデルという存在についてここまでがattr_accessorの解説になります。
まとめ
メソッド 機能 役割 attr_reader ゲッター インスタンス変数の値を参照できるようになる attr_writer セッター インスタンス変数の値を更新できるようになる attr_accessor ゲッター・セッター インスタンス変数の値を参照、更新できるようになる Ruby on Railsは非常に便利なフレームワークですが、時々Rubyの基礎を勉強し直さないといけないなと強く思いました…
皆様も是非一度この辺りの内容を自分でコードを書いてみることをおすすめします!
それではー
- 投稿日:2020-07-08T22:33:17+09:00
☆初投稿:TECH CAMP学習、個人アプリ作成①、駆け出しエンジニア
TECH CAMPに通い出して
初めて投稿させていただきました。
TECH CAMPの夜間コースに通い出して3ヶ月が経ちました。
カリキュラムではわからないことも沢山あり、その都度Googleで調べたり、メンターさんに質問したりと、苦労も沢山ありました。エラーを繰り返しながらも、無事に動いたときというのは何とも言えない喜びもありました。諸々、何とかこなし、いよいよ個人アプリ開発となりました。
正直、現在も試行錯誤の連続でありますが、やはり自分のやっていることというのは、時間がたつと忘れてしまうこともありますので、自分が苦労して解決したことなどは記録していった方がよいと考え、投稿させていただきました。
まだまだ不慣れであり、かつ知識も未熟なため、書いてあることが間違っていることもあるかもしれませんが、はっきりいって投稿することに意味があるのだと思いましたので、間違っているところははっきり指摘していただけたら嬉しいです。
個人アプリの作成時、viewを表示させようとしたのですが
まずは以下のエラーがありました。原因はhamlの導入ができていませんでした。
なのでGemfileに『gem 'haml-rails'』
を入力して
ターミナルにて『bundle install』
を実行、その後すでにあるerbをhamlに変換
『rails haml:erb2haml』
すると「would you like〜」と出てくるので「y」を入力
これでエラーは解決しました。
- 投稿日:2020-07-08T22:30:53+09:00
Railsでdeviseを追加したのに反映されなかったときの対処法
dockerで環境構築を行いgemの追加をした際にエラーが発生しましたので
備忘録として情報共有しようと思います!開発環境
docker
rails (5.2.0)
ruby (2.7.1)
mysql (5.7)
nginx
puma※docker-compose buildでgemの更新をしますが時間がかかるので下記の記事を参考に
gemの更新を素早くする設定をしています。
https://qiita.com/neko-neko/items/abe912eba9c113fd527eエラー内容
Gemfileでdeviseのgemを追加し、docker-compose run --rm rails bundle installをしたところ以下のエラーが発生
~ RailsApp % docker-compose run --rm rails bundle install #bundle installを実行 Starting railsapp_db_1 ... done Fetching gem metadata from https://rubygems.org/............. Fetching gem metadata from https://rubygems.org/. Resolving dependencies.... Bundler could not find compatible versions for gem "railties": # エラー発生 In snapshot (Gemfile.lock): railties (= 5.2.4.3) In Gemfile: devise (= 4.1.0) was resolved to 4.1.0, which depends on railties (< 5.1, >= 4.1.0) rails (~> 5.2.2) was resolved to 5.2.4.3, which depends on railties (= 5.2.4.3) Running `bundle update` will rebuild your snapshot from scratch, using only the gems in your Gemfile, which may resolve the conflict.何が原因なのかわからなかったが、とりあえず「bundle updateしたら解決するかもよ」と書いてあったので
docker-compose run --rm rails bundle updateを実行↓↓↓↓↓↓↓↓
~ RailsApp % docker-compose run --rm rails bundle update #bundle updateを実行 Starting railsapp_db_1 ... done Fetching gem metadata from https://rubygems.org/............. Fetching gem metadata from https://rubygems.org/. Resolving dependencies........................... Bundler could not find compatible versions for gem "railties": # 同じエラーが発生 In snapshot (Gemfile.lock): railties (= 5.2.4.3) In Gemfile: devise (= 4.1.0) was resolved to 4.1.0, which depends on railties (< 5.1, >= 4.1.0) rails (~> 5.2.2) was resolved to 5.2.4.3, which depends on railties (= 5.2.4.3)ダメでしたね・・・
原因
結論から申し上げるとdeviseというgemがlistにないことがエラーの原因だったみたいです。
下記の記事を参考にさせていただきました。
https://qiita.com/hatorijobs/items/2928e152f22d009b07d0こちらの記事をそのまま実行すれば解決しますが、dockerで作業している方は若干、コマンドが
異なりますのでこちらで説明させていただこうと思います。対処方法
以下の順に操作をしていきます。
1. docker-compose exec [任意サービス名] gem list でGemの一覧を確認し、deviseのgemがないことを確認する。 2. docker-compose exec [任意サービス名] gem install devise でdeviseをインストールする。 3. もう一度docker-compose exec [任意のサービス名] gem list でGemの一覧を確認し、devise(4.7.2)があることを確認する。 4. Gemfileに**gem 'devise','4.7.2'を記述する 5. docker-compose run --rm rails bundle updateを実行順番に見ていきましょう。
↓↓↓↓↓↓↓↓1. docker-compose exec [任意サービス名] gem list でGemの一覧を確認し、deviseのgemがないことを確認する。
~ RailsApp % docker-compose exec app gem list *** LOCAL GEMS *** actioncable (5.2.4.3, 5.2.2) actionmailer (5.2.4.3, 5.2.2) actionpack (5.2.4.3, 5.2.2) actionview (5.2.4.3, 5.2.2) actionjob (5.2.4.3, 5.2.2) ~省略~ web-console (3.7.0)2. docker-compose exec [任意サービス名] gem install devise でdeviseをインストールする。
~ RailsApp % docker-compose exec app gem install devise Fetching warden-1.2.8.gem Fetching bcrypt-3.1.13.gem ~省略~ Successfully installed devise-4.7.2 5 gems installed3. もう一度docker-compose exec [任意のサービス名] gem list でGemの一覧を確認し、devise(4.7.2)があることを確認する。
~ RailsApp % docker-compose exec app gem list *** LOCAL GEMS *** actioncable (5.2.4.3, 5.2.2) actionmailer (5.2.4.3, 5.2.2) actionpack (5.2.4.3, 5.2.2) actionview (5.2.4.3, 5.2.2) actionjob (5.2.4.3, 5.2.2) ~省略~ devise (4.7.2) ~省略~ web-console (3.7.0)4. Gemfileにgem 'devise','4.7.2'を記述する
Gemfile.source 'https://rubygems.org' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.0.0', '>= 5.0.2.1' # Use mysql as the database for Active Record gem 'mysql2', '>= 0.3.18', '< 0.5' # Use Puma as the app server ~省略~ #ログイン機能の追加 gem 'devise','4.7.2' ~省略~5. docker-compose run --rm rails bundle updateを実行
~ RailsApp % docker-compose run --rm rails bundle updateこれでエラーが出なければ我々の勝ちです
気づいたこと
エラー対応している際に気づいたことがいくつかあったので共有させていただきます
docker-compose exec app gem install deviseを実行すると最新版のgemがインストールされます。 最新版でも特に問題ないと判断したため、バージョン指定する方法の説明は省略させていただきました。 ※逆に方法がわかる方は教えてください!ちなみにdeviseの全バージョン履歴が掲載されたサイトを見つけましたのでURLを連携させていただきます。
https://rubygems.org/gems/devise/versions
※2020.06.10時点での最新バージョンは4.7.2
[任意のサービス名]とはdocker-compose.ymlで指定しているサービス名のことです。 任意で決めることができますが、どのサイトでもdbとかappとかwebて設定している方が多い印象です。※わからないて方は、「docker-compose.yml サービス名」で調べると記事がヒットすると思います。
終わりに
Railsやdockerについては今後もアウトプットのためにエラーに関する記事等を定期的に投稿していこうと思っております。
また、初めての投稿で至らない点があったかと思いますが、最後まで読んでいただきありがとうございました!!!以上
- 投稿日:2020-07-08T22:30:53+09:00
Railsでdeviseを追加したいのにbundle installができない
dockerで環境構築を行いgemの追加をした際にエラーが発生しましたので
備忘録として情報共有しようと思います!開発環境
docker
rails (5.2.0)
ruby (2.7.1)
mysql (5.7)
nginx
puma※docker-compose buildでgemの更新をしますが時間がかかるので下記の記事を参考に
gemの更新を素早くする設定をしています。
https://qiita.com/neko-neko/items/abe912eba9c113fd527eエラー内容
Gemfileでdeviseのgemを追加し、docker-compose run --rm rails bundle installをしたところ以下のエラーが発生
~ RailsApp % docker-compose run --rm rails bundle install Starting railsapp_db_1 ... done Fetching gem metadata from https://rubygems.org/............. Fetching gem metadata from https://rubygems.org/. Resolving dependencies.... Bundler could not find compatible versions for gem "railties": In snapshot (Gemfile.lock): railties (= 5.2.4.3) In Gemfile: devise (= 4.1.0) was resolved to 4.1.0, which depends on railties (< 5.1, >= 4.1.0) rails (~> 5.2.2) was resolved to 5.2.4.3, which depends on railties (= 5.2.4.3) Running `bundle update` will rebuild your snapshot from scratch, using only the gems in your Gemfile, which may resolve the conflict.何が原因なのかわからなかったが、とりあえず「bundle updateしたら解決するかもよ」と書いてあったので
docker-compose run --rm rails bundle updateを実行↓↓↓↓↓↓↓↓
~ RailsApp % docker-compose run --rm rails bundle update Starting railsapp_db_1 ... done Fetching gem metadata from https://rubygems.org/............. Fetching gem metadata from https://rubygems.org/. Resolving dependencies........................... Bundler could not find compatible versions for gem "railties": In snapshot (Gemfile.lock): railties (= 5.2.4.3) In Gemfile: devise (= 4.1.0) was resolved to 4.1.0, which depends on railties (< 5.1, >= 4.1.0) rails (~> 5.2.2) was resolved to 5.2.4.3, which depends on railties (= 5.2.4.3)ダメでしたね・・・
原因
結論から申し上げるとdeviseというgemがlistにないことがエラーの原因だったみたいです。
下記の記事を参考にさせていただきました。
https://qiita.com/hatorijobs/items/2928e152f22d009b07d0こちらの記事をそのまま実行すれば解決しますが、dockerで作業している方は若干、コマンドが
異なりますのでこちらで説明させていただこうと思います。対処方法
以下の順に操作をしていきます。
1. docker-compose exec [任意サービス名] gem list でGemの一覧を確認し、deviseのgemがないことを確認する。 2. docker-compose exec [任意サービス名] gem install devise でdeviseをインストールする。 3. もう一度docker-compose exec [任意のサービス名] gem list でGemの一覧を確認し、devise(4.7.2)があることを確認する。 4. Gemfileに**gem 'devise','4.7.2'を記述する 5. docker-compose run --rm rails bundle updateを実行順番に見ていきましょう。
↓↓↓↓↓↓↓↓1. docker-compose exec [任意サービス名] gem list でGemの一覧を確認し、deviseのgemがないことを確認する。
~ RailsApp % docker-compose exec app gem list *** LOCAL GEMS *** actioncable (5.2.4.3, 5.2.2) actionmailer (5.2.4.3, 5.2.2) actionpack (5.2.4.3, 5.2.2) actionview (5.2.4.3, 5.2.2) actionjob (5.2.4.3, 5.2.2) ~省略~ web-console (3.7.0)2. docker-compose exec [任意サービス名] gem install devise でdeviseをインストールする。
~ RailsApp % docker-compose exec app gem install devise Fetching warden-1.2.8.gem Fetching bcrypt-3.1.13.gem ~省略~ Successfully installed devise-4.7.2 5 gems installed3. もう一度docker-compose exec [任意のサービス名] gem list でGemの一覧を確認し、devise(4.7.2)があることを確認する。
~ RailsApp % docker-compose exec app gem list *** LOCAL GEMS *** actioncable (5.2.4.3, 5.2.2) actionmailer (5.2.4.3, 5.2.2) actionpack (5.2.4.3, 5.2.2) actionview (5.2.4.3, 5.2.2) actionjob (5.2.4.3, 5.2.2) ~省略~ devise (4.7.2) ~省略~ web-console (3.7.0)4. Gemfileにgem 'devise','4.7.2'を記述する
Gemfile.source 'https://rubygems.org' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.0.0', '>= 5.0.2.1' # Use mysql as the database for Active Record gem 'mysql2', '>= 0.3.18', '< 0.5' # Use Puma as the app server ~省略~ #ログイン機能の追加 gem 'devise','4.7.2' ~省略~5. docker-compose run --rm rails bundle updateを実行
~ RailsApp % docker-compose run --rm rails bundle updateこれでエラーが出なければ我々の勝ちです
気づいたこと
エラー対応している際に気づいたことがいくつかあったので共有させていただきます
docker-compose exec app gem install deviseを実行すると最新版のgemがインストールされます。 最新版でも特に問題ないと判断したため、バージョン指定する方法の説明は省略させていただきました。 ※逆に方法がわかる方は教えてください!ちなみにdeviseの全バージョン履歴が掲載されたサイトを見つけましたのでURLを連携させていただきます。
https://rubygems.org/gems/devise/versions
※2020.06.10時点での最新バージョンは4.7.2
[任意のサービス名]とはdocker-compose.ymlで指定しているサービス名のことです。 任意で決めることができますが、どのサイトでもdbとかappとかwebて設定している方が多い印象です。※わからないて方は、「docker-compose.yml サービス名」で調べると記事がヒットすると思います。
終わりに
Railsやdockerについては今後もアウトプットのためにエラーに関する記事等を定期的に投稿していこうと思っております。
また、初めての投稿で至らない点があったかと思いますが、最後まで読んでいただきありがとうございました!!!以上
- 投稿日:2020-07-08T22:08:36+09:00
【Ruby】Ubuntu での Ruby 開発環境構築
はじめに
2020/7/7 時点で Ruby の環境構築をしてみたので、書き起こします。けっこうたくさんの方が記事にしていらっしゃいますが、自分でアウトプットするとそれだけでも勉強になるので。
導入環境
- Ubuntu 20.04 LTS (64bit)
- Windows 10 Virtual Box内にインストールしています。
(注意)同じような Ruby バージョン管理ツールに、「RVM」がありますが、rbenv と RVM は非互換なので、「RVM」が入っていない環境を準備します。
入れるもの
複数のRubyバージョンの管理ができる rbenv というツールも一緒に入れます。複数のRubyバージョンを入れられるだけでなく、プロジェクト毎に使用するRubyのバージョンを指定することができるようになります。
また、rbenv のオプション扱い(?)ですが、ruby-build も導入します。
- rbenv 1.1.2-30-gc879cb0
- ruby-build
- Ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]
※どちらも、2020/7/7 時点で最新の安定バージョン、だと思います。
大きな流れ
流れはこんな感じです。
0. git をインストールしておく。
1. 事前に必要なツール、パッケージを導入する。
2. rbenv のインストールと環境設定
3. ruby-build のインストール
4. Rubyのインストール手順0. git のインストール
git 自体は環境構築とは直接関係がないのですが、rbenv, ruby-build のインストールの際、github からダウンロードします。このため、git をあらかじめ入れておいてください。
インストール方法は、こちら に書かれていますが、apt-get
コマンド一つで完了です。
※必要に応じて、sudo
を頭につけてください。apt-get install git手順1. 必要なツール、パッケージの導入
詳しくはこちらに書かれていますが、英語なので、、、
rbenv+ruby-build で必要な Ruby のバージョンをダウンロード、インストールする際、環境の違いでコンパイルエラーが起こったりする場合がある、とのことで、少なくとも下記パッケージは事前に入れておいた方が良いよ、ということです。一気に入れる場合は、下の長いコマンドをコピペして実行してください。
※必要に応じて、sudo
を頭につけてください。apt-get install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm6 libgdbm-dev libdb-dev「libgdbm6」が使えない環境の場合は、「libgdbm5」でやってみて、とのことです。
それぞれのパッケージが何か、については、自分でも調べてみてもよく分からないものが多い、というより、必要になって使う場面にならないと分からなそうなので、興味がある方はそれぞれ調べてみてください。
【補足】事前に必要なパッケージの一覧(自分なりに調べてみた)
※ あくまで2020/7/7 時点で ruby-build の説明に記載のあったものです。
パッケージ名 これって何? 自分の環境に個別に入れてみたときの反応 autoconf configure スクリプトの自動生成ツール bison パーサジェネレータの一種。コンパイルのときに使われるみたい build-essential 名前の通り、開発に必要なビルドツール一式が入ってるみたい。 libssl-dev SSL関連の開発用ツールキット libyaml-dev YAML関連の開発用ライブラリ libreadline6-dev GNU readline のライブラリらしいが、何の機能かよく分からない・・・ libreadline-dev をすすめられ、libncurses-dev が依存でインストールされました zlib1g-dev 圧縮(gzip, pkzip )に関するライブラリ libncurses5-dev ncurses関連の開発用ライブラリ。端末に依存しない形式でテキストユーザインタフェース (TUI) を作成するためのAPIを提供するそうです libffi-dev Foreign Function Interface に関するライブラリ。他の言語で書かれたメソッドを呼び出す際とかに使われるんだと思います。 libgdbm6 GNU dbm データベースルーチン (ランタイム版) libgdbm-dev GNU dbm データベースルーチン (開発用ファイル) libdb-dev Berkeley Database Libraries [development] → libdb5.3-dev が追加でインストールされました。 手順2. rbenv のインストールと環境設定
2-1. rbenv のインストール
rbenv のインストールは、
git clone
を使って github からダウンロードし、ホームディレクトリの.rbenv
フォルダーに設置します。git clone https://github.com/sstephenson/rbenv.git ~/.rbenv2-2. rbenv の環境設定1 パスを通す
rbenv の環境設定として、
~/.rbenv/bin
へのパスを通します。公式だと、Ubuntu desktop 環境の場合は、.bashrc にパス設定を追加するように、と記載されています。ので、下のようにコマンド実行すればOKです。echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
【補足】.bashrcではなく、.profile に書く場合
個人的に .bashrc にパスの設定を書く、というのがなんだか筋が悪いような気がしています。環境変数を書くなら .profile か、.bash_profile じゃないのかなぁ、と思うので、自分の環境では .profile に書き込みました。.profile にパス設定を記述する場合は、.profile をエディタで開いて、下記コードを追記して下さい。
if [ -d "$HOME/.rbenv/bin" ] ; then PATH="$HOME/.rbenv/bin:$PATH" fi
2-3. rbenv の環境設定2 rbenv init - を .bashrc に入れる。
rbenv init -
を .bashrc 内で実行するように、下記コマンドを実行します。echo 'eval "$(rbenv init -)"' >> ~/.bashrcその後、設定を反映させます。
source ~/.bashrc
【補足】rbenv init - を .bashrc に入れることについて
rbenv init -
コマンドは、shimsディレクトリの再作成やら、rehash処理なんかをしているのですが、こちらのコマンド内でも $PATH に shims へのパスを追加しています。なので、.bashrc に入れるのはなんだか気持ち悪いのですが、他にもいろいろ処理をしているので、こちらは公式の指示に従いました。※ .bashrc に入れると、なにか変更して
source ~/.bashrc
で設定反映する度に、パスが2重、3重と追加されてしまうんですよね、、、かといって.bashrc を変更したのに、source ~/.profile
するのもなんか変ですし。
手順3. ruby-build のインストール
ruby-build をインストールします。ruby-build も github 上に公開されていますので、rbenv と同じように、
git clone
でダウンロードします。設置する場所は、~/.rbenv/plugins/
ディレクトリの下です。git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-buildこれで、
rbenv install
コマンドが使えるようになります。手順4. rubyのインストール
rbenv install
コマンドで、ruby の各バージョンのインストールができるようになっています。まず、
rbenv install -l
で、インストールできる安定バージョンを確認します。$ rbenv install -l 2.5.8 2.6.6 2.7.1 jruby-9.2.12.0 maglev-1.0.0 mruby-2.1.1 rbx-5.0 truffleruby-20.1.0 Only latest stable releases for each Ruby implementation are shown. Use 'rbenv install --list-all' to show all local versions.通常版Ruby の 2020/7/7 時点での最新安定版は 2.7.1 のようなので、そちらをインストールする場合は、バージョンを指定します。
rbenv install 2.7.1インストール後、通常使う ruby のバージョンとして設定するには、
rbenv global
を使います。rbenv global 2.7.1最後に、インストールしたrubyのバージョンをshimsに反映させるために、
rbenv rehash
を実行します。今後、ruby の別バージョンを入れたり、gem を追加したりした時は、rbenv rehash
を行った方がいいそうです。rbenv rehashエラーが出ていなければ、これで ruby の実行ができるようになっています。
'ruby -v' でバージョンの確認ができます。$ ruby -v Ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]また、
irb
を使って "Hello, World!" を表示させてみます。$ irb irb(main):001:0> puts "Hello, World!" Hello, World! => nil irb(main):002:0>
- 投稿日:2020-07-08T22:06:51+09:00
Railsでhtml.erbのclassを条件付きで追加する方法
概容
下記のクラスに「ある条件」の時だけクラスを追加したいとする。
<div class="container"> </div>例えば今回は
users_controller
の時だけクラスを追加するとする。
その場合の条件式は下記のようになる。<div class="container<%= ' user-container' if controller_name = 'users' %>"> </div>※注意
追加したいクラス名の先頭文字の前はスペースを開けること。
正しい: <%= ' user-container' if・・・ 間違い: <%= 'user-container' if・・・このような条件付きで
class
を追加することによってその条件下のみでCSSでスタイルの変更や上書きができるようになるのでぜひ覚えておきたい。
- 投稿日:2020-07-08T22:03:24+09:00
初心者が書く!macOS catalina に postgreSQL をインストールしてrails6.0.3.2で使えるようにするまで
プログラミングとmacの初心者です。
わからないなりに、色々調べて非常に時間を要したので
自分の覚書として念の為に書いておこうと思った次第ですが、
もし間違った記載など有りましたら玄人の方にご指摘いただけると本当に助かりますm(-_-)m
玄人以外の方も、出来ないなどご相談があれば、可能な限り対応します。※可能な限りですよ^^;
宜しくおねがいします。
まずはhomebrewでpostgresqlをインストールして初期設定
ターミナルで以下を実行。場所はどこでも良いみたいです。
私は前に作ったprojectsフォルダで実行しました。
この後の作業は特に記載がなければ全てターミナルで行っています。brew install postgresqlそして以下が初期設定らしいです。
initdb /usr/local/var/postgres -E utf8起動して設定確認
まずは起動。
brew services start postgresql以下でEncodingがUTF8になってればOKだそうです。ひょっとしたら初期設定しなくてもなってるのかも??
psql -lそして終了。
brew services stop postgresqlここはpostgreを使いやすくする設定(だそうです。)
ホームフォルダ直下にある .zshrcをvscodeで編集しました。
具体的には以下を追記。export PATH="/usr/local/opt/postgres:$PATH" export PGDATA="/usr/local/var/postgres"そしてターミナルから以下を実行すればpostgreが起動します。
source ~/.zshrc #変更した設定を適応。 postgreLOG: 〜
LOG: 〜
LOG: 〜
みたいな画面ならOKだそうです。
control+cで止まります。ここからはrailsの設定です。
rails 6.0.3.2 new コマンドで好きなアプリを適当に作って下さい。ただしもちろん
-d postgresql を忘れずに^^database.ymlの設定とデータベース作成権限を持ったuserの作り方
※<任意のusername>は好きな名前です。私は今回、rootにしました。# database.yml内 default: &default adapter: postgresql encoding: utf8 #utf8に変更 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: <任意のusername> #追加 password: #追加 host: localhost #追加# ターミナル createuser <任意のusername> -d # 消すとときは dropuser <任意のusername>ここまで終わったらrails db:create (もちろん任意のフォルダで)でできると思います。
- 投稿日:2020-07-08T20:43:04+09:00
Kinx ライブラリ - パーサ・コンビネータ(その2)
Kinx ライブラリ - パーサ・コンビネータ(その2)
はじめに
「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。今回もパーサ・コンビネータです。
- 参考
- 最初の動機 ... スクリプト言語 KINX(ご紹介)
- 個別記事へのリンクは全てここに集約してあります。
- リポジトリ ... https://github.com/Kray-G/kinx
- Pull Request 等お待ちしております。
前回 Kinx ライブラリ - パーサ・コンビネータ(その1) で AST を作るところまで実施しました。
今回はこれを解釈して実行させます。最後には JIT コンパイルして実行するところまでいきます。
その前に
おさらい
前回の最終的なプログラムを再確認しておきましょう。
using Parsek; function makeAST(first, rest) { var expr = first; for (var i = 0, l = rest.length(); i < l; ++i) { expr = { lhs: expr, op: rest[i][0], rhs: rest[i][1] }; } return expr; } var $ = new Parsek(); var number = $.regex(/[1-9][0-9]*|[0-9]/).map(Integer.parseInt); var addsub = $.oneOf("+-"); var muldiv = $.oneOf("*/%"); var lbr = $.string("("); var rbr = $.string(")"); var expression; var factor = $.lazy(&() => $.alt(number, $.seq(lbr, expression, rbr).map(&(value) => value[1]))); var term = $.seqMap(factor, $.seq(muldiv, factor).many(), makeAST); expression = $.seqMap(term, $.seq(addsub, term).many(), makeAST); // test System.println(expression.parseAll("1+2*3+2*(14-2)").value.toJsonString(true));これです。これについて 1 つだけ問題点を直しておきましょう。問題というほどではないですが。
実はこのパーサー、空白文字があると失敗します。当然ですね、空白を解釈するところが無いので、空白に遭遇すると想定した文字でないため「文法あってないぜ!」と自信満々に答えてくるのです。
空白文字の読み飛ばし処理
普通のプログラミング言語では、トークンの切れ目が任意の数の空白文字になっており、空白は読み飛ばす対象であることが一般的です。そこで空白文字は読み飛ばすようにしましょう。そこで使うのが以下の 2 つです。
$.optWhitespace
は 0 個以上の任意の空白文字を解釈するパーサーです。具体的には$.regex(/\s*/)
と同じです。したがって、空白、改行(CR/LF)、タブにマッチします。尚、$.whitespace
というのもあり、こちらは$.regex(/\s+/)
で 1 個以上の空白文字にマッチします。parser.skip(anotherParser)
でparser
にマッチした後anotherParser
にマッチするか試し、マッチしたらparser
の結果を返すといった動作をします。つまり、anotherParser
のマッチ結果をスキップします。この 2 つを使って、
.skip($.optWhitespace)
とすることで特定のパーサー・マッチングの後で空白文字を読み飛ばすことが可能になります。ここでは以下のように
lexeme()
という関数を用意し、空白文字を読み飛ばす処理を追加しましょう。ignore
は無視するものが増えたときにここに追加すればよいのと、最初の出だしでも空白文字のスキップが必要なので定義しておきます。var ignore = $.optWhitespace; var lexeme = &(p) => p.skip(ignore);この
lexeme()
関数は、トークンの切れ目にあれば良いので、number
、addsub
、muldiv
、lbr
、rbr
の最小単位のパーサーにあれば良いですね。最初のプログラムを書き換えると次のようになります。using Parsek; function makeAST(first, rest) { var expr = first; for (var i = 0, l = rest.length(); i < l; ++i) { expr = { lhs: expr, op: rest[i][0], rhs: rest[i][1] }; } return expr; } var $ = new Parsek(); var ignore = $.optWhitespace; var lexeme = &(p) => p.skip(ignore); var number = lexeme($.regex(/[1-9][0-9]*|[0-9]/)).map(Integer.parseInt); var addsub = lexeme($.oneOf("+-")); var muldiv = lexeme($.oneOf("*/%")); var lbr = lexeme($.string("(")); var rbr = lexeme($.string(")")); var expression; var factor = $.lazy(&() => $.alt(number, $.seq(lbr, expression, rbr).map(&(value) => value[1]))); var term = $.seqMap(factor, $.seq(muldiv, factor).many(), makeAST); expression = $.seqMap(term, $.seq(addsub, term).many(), makeAST); // test System.println(expression.parseAll("1+2*3+2*(14-2)").value.toJsonString(true)); System.println(ignore.then(expression).parseAll(" 1 + 2 * 3 + 2 * ( 14 - 2 ) ").value.toJsonString(true)); /* Both results are the same. { "lhs": { "lhs": 1, "op": "+", "rhs": { "lhs": 2, "op": "*", "rhs": 3 } }, "op": "+", "rhs": { "lhs": 2, "op": "*", "rhs": { "lhs": 14, "op": "-", "rhs": 2 } } } */どちらも同じ結果が返ります。
最初の
ignore.then()
が先頭の空白を無視する部分です。parser.then(anotherParser)
は.skip()
とは違ってanotherParser
のほうを返します。これによって、parser
のほうが無視されます。では、本題に入りましょう。
インタプリタ
まず JIT の前にインタプリタを実装してみましょう。こっちのほうが簡単です。インタプリタは AST をトラバースしながら逐次実行していきます。左辺を評価し、右辺を評価し、その結果で演算すれば OK。
Interpreter
クラスは以下のようになります。class Interpreter(opts_) { private sequence(r, op, lhs, rhs) { return if (!opts_.enableSequence); System.println("%d %s %d -> %d" % lhs % op % rhs % r); } public eval(ast) { var lhs = ast.lhs.isObject ? eval(ast.lhs) : ast.lhs; var rhs = ast.rhs.isObject ? eval(ast.rhs) : ast.rhs; var r = 0; switch (ast.op) { case '+': r = lhs + rhs; break; case '-': r = lhs - rhs; break; case '*': r = lhs * rhs; break; case '/': r = lhs / rhs; break; case '%': r = lhs % rhs; break; default: throw RuntimeException('Invalid operator'); } sequence(r, ast.op, lhs, rhs); return r; } }計算過程も見えるようにしてみました(コンストラクタに
{ enableSequence: true }
を渡すと計算過程を表示します)。こんな感じで実行してみましょう。System.println( "Result = ", new Interpreter({ enableSequence: true }) .eval(ignore.then(expression) .parseAll(" 1 + 2 * 3 + 2 * ( 14 - 2 ) ").value) ); /* 2 * 3 -> 6 1 + 6 -> 7 14 - 2 -> 12 2 * 12 -> 24 7 + 24 -> 31 Result = 31 */ちゃんと優先順位に従って計算されてますね! AST が優先順位の通りに構築されているので当然です。別の式でもやってみましょう。
System.println( "Result = ", new Interpreter({ enableSequence: true }) .eval(ignore.then(expression) .parseAll("(( 123 ) * 2 * 4 - 3 * ( 12 + 10 )) % 100").value) ); /* 123 * 2 -> 246 246 * 4 -> 984 12 + 10 -> 22 3 * 22 -> 66 984 - 66 -> 918 918 % 100 -> 18 Result = 18 */あってますねー。
JIT コンパイル
お待ちかねの JIT です。ただし、JIT 技術自体は素晴らしいのですが、正直このレベルの計算量(ループも条件分岐もない)だと素直に上記のインタプリタのほうが速いのでご了承ください。インタプリタと同じルートでコンパイルしてるのと、インタプリタ自体が大した事やってないので、JITコンパイル時間がインタプリタ時間(+α)みたいなものになってしまってます。むむ。
あくまで技術ベースということで。もっと速さに差が出る例題にすればよかったな。今度 ここ の記事を参考にして Brainf*ck を題材に書き直すか。
さて、
Interpreter
では直接計算式を実行してましたが、計算を実行する代わりに同等のマシン語コードを出力するように書き換えれば OK です。JIT ライブラリ を見ながら、マシン語コードに変換するCompiler
クラスは以下のようになるでしょう。ちょっと長いですが、そのまま載せます。今回も コンストラクタに{ enableListing: true }
とすることで中間形式のコンパイルコードを出力できます。class Compiler(opts_) { var regs_, regsLen_; enum { MOV, BOP, DIVMOD, RET } private initialize() { regs_ = [ // Jit.R0 and Jit.R1 is used as a work register when it is division. { reg: Jit.R2, used: false, name: "R2" }, { reg: Jit.R3, used: false, name: "R3" }, { reg: Jit.R4, used: false, name: "R4" }, { reg: Jit.R5, used: false, name: "R5" }, { reg: Jit.S0, used: false, name: "S0" }, { reg: Jit.S1, used: false, name: "S1" }, { reg: Jit.S2, used: false, name: "S2" }, { reg: Jit.S3, used: false, name: "S3" }, { reg: Jit.S4, used: false, name: "S4" }, { reg: Jit.S5, used: false, name: "S5" }, ]; regsLen_ = regs_.length(); } private listing(type, op, dst, op1, op2) { return if (!opts_.enableListing); switch (type) { case MOV: System.println("%s <- %s" % dst % op1); break; case BOP: System.println("%s <- %s %s %s" % dst % op1 % op % op2); break; case DIVMOD: System.println("R0 <- %s" % op1); System.println("R1 <- %s" % op2); System.println("%s <- R0 %s R1" % dst % op); break; case RET: System.println("ret %s" % dst); break; } } private getReg() { for (var i = 0; i < regsLen_; ++i) { if (!regs_[i].used) { regs_[i].used = true; return i; } } throw RuntimeException("Not enough register"); } private releaseReg(i) { regs_[i].used = false; } private compileLeaf(c, leaf) { var r = getReg(); c.mov(regs_[r].reg, Jit.IMM(leaf)); listing(MOV, null, regs_[r].name, leaf); return r; } private compileNode(c, ast) { var rl = ast.lhs.isObject ? compileNode(c, ast.lhs) : compileLeaf(c, ast.lhs); var rr = ast.rhs.isObject ? compileNode(c, ast.rhs) : compileLeaf(c, ast.rhs); var r = getReg(); switch (ast.op) { case '+': c.add(regs_[r].reg, regs_[rl].reg, regs_[rr].reg); listing(BOP, ast.op, regs_[r].name, regs_[rl].name, regs_[rr].name); break; case '-': c.sub(regs_[r].reg, regs_[rl].reg, regs_[rr].reg); listing(BOP, ast.op, regs_[r].name, regs_[rl].name, regs_[rr].name); break; case '*': c.mul(regs_[r].reg, regs_[rl].reg, regs_[rr].reg); listing(BOP, ast.op, regs_[r].name, regs_[rl].name, regs_[rr].name); break; case '/': c.mov(Jit.R0, regs_[rl].reg); c.mov(Jit.R1, regs_[rr].reg); c.sdiv(); c.mov(regs_[r].reg, Jit.R0); listing(DIVMOD, ast.op, regs_[r].name, regs_[rl].name, regs_[rr].name); break; case '%': c.mov(Jit.R0, regs_[rl].reg); c.mov(Jit.R1, regs_[rr].reg); c.sdivmod(); c.mov(regs_[r].reg, Jit.R1); listing(DIVMOD, ast.op, regs_[r].name, regs_[rl].name, regs_[rr].name); break; default: throw RuntimeException('Invalid operator'); } releaseReg(rl); releaseReg(rr); return r; } public compile(ast) { var c = new Jit.Compiler(); c.enter(); var r = compileNode(c, ast); c.ret(regs_[r].reg); listing(RET, null, regs_[r].name); return c.generate(); } public run(ast) { var code = compile(ast); return code.run(); } }レジスタは
R2
~R5
、S0
~S5
が利用可能です。R0
とR1
は除算で使う必要があるのでそのために取っておきます。レジスタの割り当て方は「使うときに予約して、使い終わったら返す」です。尚、
R
系のレジスタは関数呼び出し後に破壊される可能性がありますが、今回は演算するだけで関数呼び出しをしないのでS
系と同様の扱いで良いでしょう。使えるレジスタを使い切るとエラーしますが、中間値の多くは短命なので問題ないでしょう。もし保存すべき値が増えたら、メモリへ退避させるコードを出します。また、Kinx の JIT ライブラリにはスタック上にローカル変数領域を持てるので、足りなくなったらそこに割り当てる、というのが一番楽です。本格的にレジスタ割り付けとかを考えるとかなり奥が深いですが、JIT の場合は 最適化にはあまり時間をかけない というのがセオリーですので、この程度で十分でしょう。なぜならば、最適化の時間がもったいないからです。事前コンパイル(AOT Compilation)のときはコンパイル時間は気にせずに実行時に速ければ速いだけよいので最適化をガンガンしますが、JIT ではコンパイル時間も実行時間のうちなのでそうもいかないのです。
では早速計算させてみます。
using Jit;
を忘れずに。using Parsek; using Jit; /* 省略 */ System.println( "Result = ", new Compiler({ enableListing: true }) .run(ignore.then(expression) .parseAll(" 1 + 2 * 3 + 2 * ( 14 - 2 ) ").value) ); /* R2 <- 1 R3 <- 2 R4 <- 3 R5 <- R3 * R4 R3 <- R2 + R5 R2 <- 2 R4 <- 14 R5 <- 2 S0 <- R4 - R5 R4 <- R2 * S0 R2 <- R3 + R4 ret R2 Result = 31 */あってますねー。もう一つの例もやってみましょう。
System.println( "Result = ", new Compiler({ enableListing: true }) .run(ignore.then(expression) .parseAll("(( 123 ) * 2 * 4 - 3 * ( 12 + 10 )) % 100").value) ); /* R2 <- 123 R3 <- 2 R4 <- R2 * R3 R2 <- 4 R3 <- R4 * R2 R2 <- 3 R4 <- 12 R5 <- 10 S0 <- R4 + R5 R4 <- R2 * S0 R2 <- R3 - R4 R3 <- 100 R0 <- R2 R1 <- R3 R4 <- R0 % R1 ret R4 Result = 18 */こちらもあってます。
実際のマシン語コードもダンプできるのでやってみましょう。
var code = new Compiler() .compile(ignore.then(expression) .parseAll(" 1 + 2 * 3 + 2 * ( 14 - 2 ) ").value); code.dump(); System.println(code.run());Linux での出力です。
0: 53 push rbx 1: 41 57 push r15 3: 41 56 push r14 5: 48 8b df mov rbx, rdi 8: 4c 8b fe mov r15, rsi b: 4c 8b f2 mov r14, rdx e: 48 83 ec 10 sub rsp, 0x10 12: 48 c7 c7 01 00 00 00 mov rdi, 0x1 19: 48 c7 c1 02 00 00 00 mov rcx, 0x2 20: 49 c7 c0 03 00 00 00 mov r8, 0x3 27: 49 89 cb mov r11, rcx 2a: 4d 0f af d8 imul r11, r8 2e: 4a 8d 0c 1f lea rcx, [rdi+r11] 32: 48 c7 c7 02 00 00 00 mov rdi, 0x2 39: 49 c7 c0 0e 00 00 00 mov r8, 0xe 40: 49 c7 c3 02 00 00 00 mov r11, 0x2 47: 4c 89 c3 mov rbx, r8 4a: 49 2b db sub rbx, r11 4d: 49 89 f8 mov r8, rdi 50: 4c 0f af c3 imul r8, rbx 54: 4a 8d 3c 01 lea rdi, [rcx+r8] 58: 48 89 f8 mov rax, rdi 5b: 48 83 c4 10 add rsp, 0x10 5f: 41 5e pop r14 61: 41 5f pop r15 63: 5b pop rbx 64: c3 ret 31ざっくり読み解いてみましょう。
- 0xe 行目までがプロローグですね。
- 0x12 行目で
rdi
レジスタに 0x01 を代入- 0x19 行目で
rcx
レジスタに 0x02 を代入- 0x20 行目で
r8
レジスタに 0x03 を代入- 0x27-0x2a 行目で
r11
レジスタにrcx * r8
の結果を代入- 0x2e 行目で
rcx
レジスタにrdi + r11
の結果を代入- 0x32 行目で
rdi
レジスタに 0x02 を代入- 0x39 行目で
r8
レジスタに 0x0e を代入- 0x40-0x4a 行目で
rbx
レジスタにr8 - r11
の結果を代入- 0x4d-0x50 行目で
r8
レジスタにrdi * rbx
の結果を代入- 0x54 行目で
rdi
レジスタにrcx + r8
の結果を代入- 0x58 行目で
rax
レジスタにrdi
を代入- 0x5b 行目からはエピローグです。
関数の復帰値は x64 では
rax
レジスタと決まっているので、これで結果が返ります。ではもう一つの例で。
var code = new Compiler() .compile(ignore.then(expression) .parseAll("(( 123 ) * 2 * 4 - 3 * ( 12 + 10 )) % 100").value); code.dump(); System.println(code.run());こちらも Linux での出力です。
0: 53 push rbx 1: 41 57 push r15 3: 41 56 push r14 5: 48 8b df mov rbx, rdi 8: 4c 8b fe mov r15, rsi b: 4c 8b f2 mov r14, rdx e: 48 83 ec 10 sub rsp, 0x10 12: 48 c7 c7 7b 00 00 00 mov rdi, 0x7b 19: 48 c7 c1 02 00 00 00 mov rcx, 0x2 20: 49 89 f8 mov r8, rdi 23: 4c 0f af c1 imul r8, rcx 27: 48 c7 c7 04 00 00 00 mov rdi, 0x4 2e: 4c 89 c1 mov rcx, r8 31: 48 0f af cf imul rcx, rdi 35: 48 c7 c7 03 00 00 00 mov rdi, 0x3 3c: 49 c7 c0 0c 00 00 00 mov r8, 0xc 43: 49 c7 c3 0a 00 00 00 mov r11, 0xa 4a: 4b 8d 1c 18 lea rbx, [r8+r11] 4e: 49 89 f8 mov r8, rdi 51: 4c 0f af c3 imul r8, rbx 55: 48 89 cf mov rdi, rcx 58: 49 2b f8 sub rdi, r8 5b: 48 c7 c1 64 00 00 00 mov rcx, 0x64 62: 48 89 f8 mov rax, rdi 65: 48 89 ce mov rsi, rcx 68: 48 99 cqo 6a: 48 f7 fe idiv rsi 6d: 48 89 d6 mov rsi, rdx 70: 49 89 f0 mov r8, rsi 73: 4c 89 c0 mov rax, r8 76: 48 83 c4 10 add rsp, 0x10 7a: 41 5e pop r14 7c: 41 5f pop r15 7e: 5b pop rbx 7f: c3 ret 18計算結果もあってますね!
おわりに
通常、スクリプト言語系では AST から独自の中間形式コード(バイトコード)に変換して、それを実行します。Kinx もそうです。ただ、やってることは上記と大差ありません。どちらかというと、
Compiler
と似たようなことをやって、独自のInterpreter
で実行する、といった感じですね。ネイティブなマシン語コードに変換できればIntrpreter
は不要です。普通は多倍長演算とかクラス・オブジェクトとかを扱うし、動的型付け言語は型が実行時まで分からないので、マシン語にコンパイルするのが面倒なだけです。特に、x64(Windows/Linux)/ARM とか色々対応しようとするとそれぞれごとにコンパイラを作らなくてはなりません。なのでバイトコード化がベスト・プラクティスな感じではあります。そうは言っても本気のプロダクトは JIT 頑張ってますよね。Kinx も native 関数などで一部 JIT 化可能です。しかも抽象化アセンブラ(SLJIT)で表現可能な範囲で実現しているので、基本的には色々なプラットフォームへの移植性は高いはず。
いずれにしても、JIT は言語処理系では昔からホットな話題なので、Kinx の JIT ライブラリはちょっとしたオモチャとしては楽しいかなー、と自画自賛してます。しかも今回頑張ってパーサ・コンビネータなんて実装してしまったので、手軽に独自の DSL とか作ったりできるのではないでしょうか。結構楽しいと思いますがいかがでしょう。
Kinx JIT ライブラリのキャッチフレーズは 「気軽に JIT」、ですね。
ということで、では、また次回。
- 投稿日:2020-07-08T20:12:07+09:00
Rubyで配列の中の文字列を数値に変更する方法
array = ["1", "2", "3", "4", "5"]のように配列の中に文字列で数字の値がある時に、配列の文字列を数値に変更する方法を書きます。
for文の方法
array = ["1", "2", "3", "4", "5"] intArray = [] for n in array intArray.push(n.to_i) end # intArray => [1, 2, 3, 4, 5]
array
とは別の配列intArray = []
を作成します。
array
の値をn
で取得して、to_i
で文字列を数値に変更、push
でintArray
に値を代入していくという処理を繰り返します。mapメソッドの方法
array = ["1", "2", "3", "4", "5"] intArray = array.map{ |n| n.to_i } # intArray => [1, 2, 3, 4, 5]または
array = ["1", "2", "3", "4", "5"] intArray = array.map(&:to_i) # intArray => [1, 2, 3, 4, 5]
map
メソッドで配列array
を繰り返し処理して値をn
という変数に代入して、実行したい処理としてn.to_i
または&:to_i
という処理を実行しています。
実行した処理をintArray
に代入していきます。
こちらの場合は予めintArray = []
のように定義しておく必要はありません。
map!
を使用する方法もあり、こちらの場合はarray = ["1", "2", "3", "4", "5"] array.map!{ |n| n.to_i } # array => [1, 2, 3, 4, 5]のように
array
の配列を書き換えることができます。参考サイト
https://techacademy.jp/magazine/19868
https://uxmilk.jp/21695
- 投稿日:2020-07-08T19:42:38+09:00
form_with を使った検索フォームに初期値 value を設定する方法
筆者はRails6.0ですが、Rails5(Rails4も?)でも動くはずです。またSearchkick(Elasticsearchを簡単にするGem)を使っているので、もしかすると環境依存があるかもしれません。
最新のRailsでは
form_with
を使った記法が推奨されています。しかしこと検索フォームの作成においては、未だに情報が少なく苦戦したのでメモ。正解のコード
# index.html.erb <%= form_with model: @post, method: "get", local: true do %> <%= text_field_tag :search, params[:search], class: "form-control ds-input", placeholder: "検索‥" %> <% end %>初期値valueを入れる場合は
text_field
は使えないようです。form_withを使っているのでURLの指定は不要ですがmethod: "get", local: true
の記述は必要でした。
local: true
というのは form_with のデフォルト設定でAjaxによるHTTPリクエストを送ってしまうのをやめさせているようです(Ajaxだと画面遷移してくれないらしい)。
text_field_tag
の引数たちをかっこやハッシュ?({}
)で囲んでる記述も見かけましたが、よくわからんがこれでいいようです。post_controller.rbdef index @posts = self.query end private def query if params[:search] # ここの検索方法の記述はSearchkickを使っているためこうなので、標準の検索方法に置き換えるなどしてください Post.search params[:search], fields: [:name, :description, :category_name] else Post.all end end前提知識
筆者はProgateあがりですが、RubyやRailsの公式チュートリアルをやっていないせいかformまわりの基礎知識がバラバラです?もしかすると当たり前かもしれませんが、知らなかった知識を書いときます。
text_field は text_field_tag のヘルパー
フォームの中身に使う
text_field
はtext_field_tag
のヘルパーであり、ヘルパーとは簡単にいうと記述を省略して書けるようにしてくれるものらしい?フォームのコントロールの命名法や大量の属性を扱わなければならず、フォームのマークアップは作成もメンテナンスも退屈な作業になりがちです。そこでRailsでは、フォームのマークアップを生成するビューヘルパーを提供し、こうした煩雑な作業を行わないで済むようにしました
https://railsguides.jp/form_helpers.html最初の引数はインスタンス変数名、2番目の引数はオブジェクトを呼び出すためのメソッド名
たとえば、コントローラで
@person
が定義されており、その人物の名前がHenryの場合<%= text_field(:person, :name) %> # こうなる? <input id="person_name" name="person[name]" type="text" value="Henry"/>なので
<%= text_field :search, params[:search] %> # と書いてしまうと、こうなってしまう? <input id="search_" name="search[]" type="text">Railsの検索に search.html.erb はいらない
検索をする場合は search.html.erb を作成して、ルーティングにGETで追加して、index.html.erb と同じコードを毎回貼り付けてました(笑)コントローラー側でキーワードがあるかないかでif分岐してやればスッキリ書けます。
参考
- 投稿日:2020-07-08T18:54:52+09:00
Rails5でECサイトを作る⑤ ~Customerモデル編~
はじめに
架空のベーカリーで買い物できるECサイトを作るシリーズ、Rails5でECサイトを作る④の続きです。
ようやく機能の実装に入ります。まずは顧客サイトの親モデル周辺機能から順番に……ということで、今回はCustomerモデルのCRUDを中心に作ります。ソースコード
https://github.com/Sn16799/bakeryFUMIZUKI
deviseのコントローラ
deviseで新規登録できず、何でだろうと思ったらコントローラ側にパラメータの許可を与えていなかったためと判明しました。
app/controllers/customers/registrations_controller.rbclass Customers::RegistrationsController < Devise::RegistrationsController before_action :authenticate_customer! before_action :configure_permitted_parameters, if: :devise_controller? protected def after_sign_up_path_for(resource) customer_top_path end def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:is_active, :first_name, :first_name_kana, :family_name, :family_name_kana, :post_code, :address, :tel]) end protected def update_resouce(resource, parans) resource.update_without_password(params) end endついでにsessions_controllerもログイン、ログアウト後の遷移先を指定しておきました。
app/controllers/customers/sessions_controller.rbclass Customers::SessionsController < Devise::SessionsController protected def after_sign_in_path_for(resource) customer_top_path end def after_sign_out_path_for(resource) customer_top_path end endBootstrapのflashを使えるようにする
Bootstrapでは、class指定だけできれいに整ったflashメッセージを使うことができます。
部分テンプレートを作っておくとrenderで呼び出せて便利です。app/controllers/application_controller.rbadd_flash_types :success, :danger, :infoapp/helpers/application_helper.rbdef flash_class_for flash_type case flash_type when 'success' then 'alert-success' when 'danger' then 'alert-danger' when 'info' then 'alert-info' end endapp/views/layouts/_flash.html.erb<% flash.each do |type, msg| %> <div class="alert <%= flash_class_for(type) %> row" role="alert"> <a class="close" data-dismiss="alert">×</a> <%= msg %> </div> <% end %>app/views/layouts/application.html.erb<body> <%= render 'layouts/header' %> <div class="container-fluid"> <%= render 'layouts/flash', flash: flash %> <div class="row"> <%= yield %> </div> </div> <%= render 'layouts/footer' %> </body>Controller
多くの機能は基本的なCRUDですが、退会処理のみ異なります。
編集画面にて「退会する」ボタンを押下(withdrawアクション呼出) ↓ 退会確認画面に遷移 ↓ 「退会する」ボタンを押下(alert出現) ↓ 「はい」を押すと退会処理が行われる(withdraw_doneアクション呼出)以上のような流れになっています。
また、退会処理はCustomerデータの削除ではなく、is_activeカラムをtrueからfalseに変更する処理としています。app/controllers/customers_controller.rbclass CustomersController < ApplicationController before_action :authenticate_customer! before_action :set_customer before_action :baria_customer def edit end def show end def update @customer = current_customer if @customer.update(customer_params) redirect_to customer_path(@customer), success: 'お客様情報が更新されました!' else flash[:danger] = 'お客様の情報を更新出来ませんでした。空欄の箇所はありませんか?' render :edit end end def withdraw end def withdraw_done @customer = current_customer @customer.update(is_active: false) reset_session redirect_to customer_top_path, info: 'ありがとうございました。またのご利用を心よりお待ちしております。' end private def customer_params params.require(:customer).permit(:is_active, :first_name, :first_name_kana, :family_name, :family_name_kana, :post_code, :address, :email, :tel, cart_items_attributes: [:_destroy]) end def set_customer @customer = current_customer end # 他者のページにアクセスしようとすると直前のページに戻る def baria_customer if params[:id].to_i != current_customer.id redirect_back(fallback_location: root_path) end end endView
Sign_up
app/views/customers/registrations/new.html.erb<div class='col-lg-6 offset-lg-3 offset-2 space'> <div class='row'> <h2>新規会員登録</h2> </div> <div class='row'> <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= render 'devise/shared/error_messages', resource: resource %> <div class='form-group row space'> <div class='col-lg-4'> <h5><%= f.label :名前 %></h5> </div> <div class='col-lg-8'> <div class="row"> <div class="col-lg-6"> (姓)<%= f.text_field :family_name, autofocus: true, autocomplete: 'family_name', class: 'form-control' %> </div> <div class="col-lg-6"> (名)<%= f.text_field :first_name, autofocus: true, autocomplete: 'first_name', class: 'form-control' %> </div> </div> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :フリガナ %></h5> </div> <div class='col-lg-8'> <div class="row"> <div class="col-lg-6"> (セイ)<%= f.text_field :family_name_kana, autofocus: true, autocomplete: 'family_name_kana', class: 'form-control' %> </div> <div class="col-lg-6"> (メイ)<%= f.text_field :first_name_kana, autofocus: true, autocomplete: 'first_name_kana', class: 'form-control' %> </div> </div> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :Eメール %></h5> </div> <div class='col-lg-8'> <%= f.text_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :郵便番号(ハイフンなし) %></h5> </div> <div class='col-lg-8'> <%= f.text_field :post_code, autofocus: true, autocomplete: 'post_code', class: 'form-control' %> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :住所 %></h5> </div> <div class='col-lg-8'> <%= f.text_field :address, autofocus: true, autocomplete: 'address', class: 'form-control' %> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :電話番号(ハイフンなし) %></h5> </div> <div class='col-lg-8'> <%= f.text_field :tel, autofocus: true, autocomplete: 'tel', class: 'form-control' %> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :パスワード(6文字以上) %></h5> </div> <div class='col-lg-8'> <%= f.password_field :password, autocomplete: 'new-password', class: 'form-control' %> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :パスワード確認用 %></h5> </div> <div class='col-lg-8'> <%= f.password_field :password_confirmation, autocomplete: 'new-password', class: 'form-control' %> </div> </div> <div class='row'> <div class='col-lg-2 offset-lg-5 actions'> <%= f.submit '新規登録', class:'btn btn-danger' %> </div> </div> <% end %> </div> <div class="space"> <h4>すでに登録済みの方</h4> <h5><%= link_to 'こちら', new_customer_session_path %>からログインしてください</h5> </div> </div>Sign_in
html/app/views/customers/sessions/new.html.erb<div class='col-lg-6 offset-lg-4 offset-1 space'> <div class="row"> <h3> <span style="display: inline-block;">会員の方は</span> <span style="display: inline-block;">こちらからログイン</span> </h3> </div> <div class="row"> <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> <%= render 'devise/shared/error_messages', resource: resource %> <div class="form-group row space"> <div class="col-lg-4"> <h5><%= f.label :Eメール %></h5> </div> <div class="col-lg-8"> <%= f.text_field :email, autofocus: true, autocomplete: "email", class: 'form-control' %> </div> </div> <div class="form-group row"> <div class="col-lg-4"> <h5><%= f.label :パスワード %></h5> </div> <div class="col-lg-8"> <%= f.password_field :password, autocomplete: "current-password", class: 'form-control' %> </div> </div> <div> <%= link_to "=>パスワードを忘れた方はこちら", new_password_path(resource_name) %> </div> <div class="form-group row space"> <div class="col-lg-2 offset-lg-5"> <%= f.submit "ログイン", class:"btn btn-danger" %> </div> </div> <% end %> </div> <div class="space"> <h4>登録がお済みでない方</h4> <h5><%= link_to "こちら", new_customer_registration_path %>から新規登録してください。</h5> </div> </div>編集
app/views/customers/edit.html.erb<div class='col-lg-10 offset-lg-1 space'> <div class='row'> <h2>登録情報編集</h2> </div> <div class='row'> <%= form_with(model: @customer, local: true, class: 'container') do |f| %> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :名前 %></h5> </div> <div class='col-lg-8'> <span style='display: inline-block;'> (姓)<%= f.text_field :family_name, autofocus: true, autocomplete: 'family_name', class: 'form-control' %> </span> <span style='display: inline-block;'> (名)<%= f.text_field :first_name, autofocus: true, autocomplete: 'first_name', class: 'form-control' %> </span> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :フリガナ %></h5> </div> <div class='col-lg-8'> <span style='display: inline-block;'> (セイ)<%= f.text_field :family_name_kana, autofocus: true, autocomplete: 'family_name_kana', class: 'form-control' %> </span> <span style='display: inline-block;'> (メイ)<%= f.text_field :first_name_kana, autofocus: true, autocomplete: 'first_name_kana', class: 'form-control' %> </span> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :Eメール %></h5> </div> <div class='col-lg-8'> <%= f.text_field :email, autofocus: true, autocomplete: 'email', class: 'form-control' %> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :郵便番号(ハイフンなし) %></h5> </div> <div class='col-lg-8'> <%= f.text_field :post_code, autofocus: true, autocomplete: 'post_code', class: 'form-control' %> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :住所 %></h5> </div> <div class='col-lg-8'> <%= f.text_field :address, autofocus: true, autocomplete: 'address', class: 'form-control' %> </div> </div> <div class='form-group row'> <div class='col-lg-4'> <h5><%= f.label :電話番号(ハイフンなし) %></h5> </div> <div class='col-lg-8'> <%= f.text_field :tel, autofocus: true, autocomplete: 'tel', class: 'form-control' %> </div> </div> <div class="form-group row"> <div class="col-lg-4 offset-lg-8"> <%= f.submit '編集内容を保存する', class:'btn btn-danger' %> <%= link_to '退会する', customer_withdraw_path(@customer), class:'btn btn-danger' %> </div> </div> <% end %> </div> </div>詳細
app/views/customers/show.html.erb<div class="col-lg-6 offset-lg-3 space"> <div class="container"> <h2>マイページ</h2> </div> <!-- お客様情報 --> <div class="container space"> <div class="row"> <div class="col-lg-5"><h3>お客様情報</h3></div> <div class="col-lg-2"> <%= link_to '編集', edit_customer_path, class: 'btn-sm btn-danger' %> </div> <div class="col-lg-4"> <%= link_to 'パスワード変更', new_customer_password_path, class: 'btn-sm btn-danger' %> </div> </div> <div class="row"> <div class="col-lg-3"> <h4>氏名</h4> </div> <div class="col-lg-9"> <%= @customer.full_name %> </div> </div> <div class="row"> <div class="col-lg-3"> <h4>カナ</h4> </div> <div class="col-lg-9"> <%= @customer.family_name_kana %> <%= @customer.first_name_kana %> </div> </div> <div class="row"> <div class="col-lg-3"> <h4>郵便番号</h4> </div> <div class="col-lg-9"> 〒 <%= @customer.post_code %> </div> </div> <div class="row"> <div class="col-lg-3"> <h4>住所</h4> </div> <div class="col-lg-9"> <%= @customer.address %> </div> </div> <div class="row"> <div class="col-lg-3"> <h4>電話番号</h4> </div> <div class="col-lg-9"> <%= @customer.tel %> </div> </div> <div class="row"> <div class="col-lg-3"> <h4>Eメール</h4> </div> <div class="col-lg-9"> <%= @customer.email %> </div> </div> </div> <!-- その他のリンク --> <div class="container space"> <div class="row"> <div class="col-lg-3"> <h4>配送先</h4> </div> <div class="col-lg-9"> <%= link_to '一覧を見る', addresses_path, class: 'btn btn-danger' %> </div> </div> <div class="row"> <div class="col-lg-3"> <h4>注文履歴</h4> </div> <div class="col-lg-9"> <%= link_to '一覧を見る', orders_path, class: 'btn btn-danger' %> </div> </div> </div> </div>退会確認
app/views/customers/withdraw.html.erb<div class="col-lg-10 offset-lg-1 space"> <div class="row space"> <h2>本当に退会しますか?</h2> </div> <div class="row space"> <h4>退会すると、会員登録情報や <br>これまでの購入履歴が閲覧できなくなります。 <br>退会する場合は「退会する」をクリックしてください。 </h4> </div> <div class="row space"> <%= link_to "退会しない",customer_path(current_customer),class: "btn btn-danger" %> <%= link_to "退会する", customer_withdraw_done_path(current_customer), method: :put, "data-confirm" => "本当に退会しますか?", class: "btn btn-danger" %> </div> </div>SCSS
app/assets/stylesheets/application.scss// initialize *{ margin:0; padding:0; box-sizing:border-box; color: #120136; } a{ text-decoration: none; } .space { padding: 50px 0 30px; }後記
今回、機能そのものは難しくないのですが、画面をレスポンシブ対応にしようとするとやはり手間取りました。ボタンは後ほどテーマカラーの色に変更する予定で、今はとりあえず全て赤にしてあります。目立つので。
また、Viewファイルに書いたリンクも一応は機能しますが、飛んだ先のページにまだ何も書いていないので、例の「Find me in ...」という文言が出るだけです。メインの画面を作ったことで、ようやくサイトらしい見た目になってきましたね。機能はまだまだですが、これから実装していきます。気になっていたIK●Aカラーも、進めるうちに「これはこれでアリなんじゃないか?」と思えるようになってきました。単に見慣れただけかも知れませんが。
写真を載せればきっとパン屋さんのサイトになると信じています。次回へ続く!
参考
RailsでTwitterBootstrapを使ったflashメッセージ
データ登録の際、適当な名前や住所が思い付かない時に便利。「文月太郎」とかでは流石に味気ないので……。
日本人名前自動生成機
ランダム日本地名ジェネレータ
- 投稿日:2020-07-08T18:04:45+09:00
ジョブカンスクレイピング
必要なもの
- selenium-webdriver
- chromedriver
chromedriver
- 下記からChromeのバージョンと同じものをDL http://chromedriver.storage.googleapis.com/index.html
- 解凍したらファイルを移動
% sudo mv 解凍したchromedriver /usr/local/bin3.移動したファイルにパスを通す
% export PATH="/usr/local/bin:$PATH"selenium-webdriver
gemをインストールする。
% gem install selenium-webdriverプログラムについて
リポジトリからgit clone
git clone https://github.com/ryumaanai/jobcan.gitプログラムを編集
1.emailとpasswordを編集する。アスタリスクのところ。
2.出勤の打刻に関しては時間で判別してる。unlessとifのところ。読めばわかると思う。不明点があれば聞いて◎エイリアスを環境変数に登録しておくと簡単にプログラムを呼び出せる。
- 投稿日:2020-07-08T17:08:08+09:00
rubyのエラーの解決策がわかりません...
今日初めてqiitaを使うのでもし使い方間違っていたらすみません.
下のようなエラーが出ていて困っています.Traceback (most recent call last):
1: from othello.rb:46:in<main>'
make_bord': undefined local variable or method `max_row' for main:Object (NameError)
othello.rb:14:in色々調べてみたのですが、後ろから順番に実行されてるなど、どう解決したらいいのかわからない文がでてきて困惑しています.わかる方どうかよろしくお願いします.
rubyのverはruby2.6.6p146 (2020-03-31 revision 67876) [x86_64-darwin19]
です
- 投稿日:2020-07-08T16:04:42+09:00
Ruby: Starttls でメールの送信
こちらと同じことを Ruby で行いました。
Python3: Starttls でメールの送信
hi-ho.ne.jp で試しました。ライブラリーのインストール
sudo gem install mailhi-ho.rb#! /usr/bin/ruby # -*- encoding: utf-8 -*- # # hi-ho.rb # # Jul/08/2020 # # --------------------------------------------------------------------- require "mail" require "dotenv" STDERR.puts "*** 開始 ***" Dotenv.load server = ENV['SERVER'] port = ENV['PORT'] usr = ENV['USR'] password = ENV['PASSWORD'] from = ENV['FROM'] to = ENV['TO'] # puts server puts port str_out = "Good Morning\n" str_out += "こんにちは。\n" str_out += "Jul/08/2020\n" str_out += "PM 15:58\n" mail = Mail.new do from from to to subject "Hello from Hi-ho PM 15:58" content_type 'text/plain; charset=UTF-8' body str_out end mail.delivery_method(:smtp, address: server, port: port, authentication: :login, us er_name: usr, password: password ) mail.deliver STDERR.puts "*** 終了 ***" # ---------------------------------------------------------------------.envSERVER = 'hi-ho.mose-mail.jp' PORT = 587 USR = '****@hi-ho.ne.jp' PASSWORD = '****' FROM = '****@hi-ho.ne.jp' TO = 'sample@example.com'実行結果
$ ./hi-ho.rb *** 開始 *** hi-ho.mose-mail.jp 587 *** 終了 ***次のバージョンで確認しました。
$ ruby --version ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux-gnu]
- 投稿日:2020-07-08T15:50:05+09:00
ハッシュの使い方
これまで配列を多用していたのですが、
ある問題を解いていた時に配列だけでは解決できず、
ハッシュで解決できました。
その学びについてのメモ。前提
Aさん、 Bさん、 Cさんの書籍購入費ランキングを作成する。
Aさん、 Bさん、 Cさんという名前の 3人のエンジニアがおり、
それぞれ 1000円, 3000円, 2000円 の書籍購入費を消費。
この場合、 Bさんが 1位となり、 Cさんが 2位、そして Aさんが 3位となる。それぞれのエンジニアの名前と、書籍の購入情報が与えられるので、
この情報から書籍購入費ランキングを作成する。入力
3 -> エンジニアの人数( number_of_engineers )
A B C -> エンジニアの名前( name_of_engineers )
4 -> エンジニアたちが購入した本の数( number_of_books )
A 1000 -> 本を購入したエンジニアの名前とその本の金額( name, price )
B 1000
B 2000
C 2000出力
B
C
A解き方
number_of_engineers = gets.to_i name_of_engineers = gets.to_s.split(" ") number_of_books = gets.to_i name_n_price = {} name_of_engineers.each{|name| name_n_price[name] = 0} #解説1 number_of_books.times do name, price = gets.to_s.split(" ") name_n_price.each_key{ |i| name_n_price[i] += price.to_i if i == name} #解説2 end puts name_n_price.sort_by{ |name, price| -price}.to_h.keys #解説3解説
解説1
事前に"name_n_price"で空のハッシュを生成しています。
name_of_engineersは現時点で以下のようになっています。name_of_engineers = ["A", "B", "C"]この配列の要素(A, B, C)をeachメソッドで取得し、name_n_priceの中のキーとして加えていきます。
また、この時、name_n_price[name] = 0としているので、各キーには 0というバリューが入ります。
この後、"A 1000"などが入力されていくため、初期値の設定として、上記のようにしています。
上記操作によって、"name_n_price"は以下のようになります。name_n_price = {"A"=> 0, "B"=> 0, "C"=> 0}解説2
number_of_books.times do ~ end で "A 1000"など計4回の入力を行わせます。
(今回はnumber_of_books = 4)
name, price = gets.to_s.split(" ")にて以下のような配列を生成させます。name, price = ["A", "1000"]続いて、name_n_priceのハッシュ内の各キーに対して、値を追加していきます。
そのため、name_n_price内の各要素をeachメソッドで取得していくのですが、
今回はキーのみの取得を行っていきます。
理由はハッシュのキーに値を追加していく事を理解できていれば、
おさらいしておきます。irb(main):011:0> h = {} => {} irb(main):012:0> h[:name] = "suzuki" => "suzuki" irb(main):013:0> p h {:name=>"suzuki"} => {:name=>"suzuki"}要はキー(今回で言う"name")があれば、値(今回で言う"suzuki")を指定して追加が可能です。
そのため、" name_n_price.each_key{} "とします。
他にはバリューだけ取り出すeach_valueもあります。続いて、name_n_price = {"A"=> 0, "B"=> 0, "C"=> 0}に対して、
Aさんに該当した場合に値を追加していきたいので、if分で条件分岐を行います。
以下の通りです。if i == nameここでの " i "には A, B, Cが入ります。
( each_key でキーを取得しており { |i| ~ }としているため)最後に入力された金額(price)を加えていきます。
name_n_price[i] += price.to_i上記を分解して書くと、
name_n_price[i] = name_n_price[i] + price.to_i代入値で書くと
name_n_price[A] = 0 + 1000なので、
name_n_price = {"A"=> 0, "B"=> 0, "C"=> 0}が
name_n_price = {"A"=> 1000, "B"=> 0, "C"=> 0}になります。
これをあと三回繰り返すと結果的に
name_n_price = {"A"=> 1000, "B"=> 3000, "C"=> 2000}になります。
解説3
最後にname_n_priceに対して、金額が大きい人からそのキー(名前)を順番に出力します。
ハッシュ内の金額を大きい順(降順)にしたいので、sort_byを使用します。
sortもありますが、こちらはキーでの並び替えであるため、今回は使えません。ちなみに、使うとこんな感じになります。
irb(main):017:0> h = {"z" => 1, "f" => 3, "a" => 5} => {"z"=>1, "f"=>3, "a"=>5} irb(main):018:0> h.sort => [["a", 5], ["f", 3], ["z", 1]]キーがアルファベット順で並び替えられています。
以下のようにする事で、値を降順にできます。
name_n_price.sort_by{ |name, price| -price}irb(main):019:0> name_n_price = {"A"=> 1000, "B"=> 3000, "C"=> 2000} => {"A"=>1000, "B"=>3000, "C"=>2000} irb(main):020:0> name_n_price.sort_by{ |name, price| -price} => [["B", 3000], ["C", 2000], ["A", 1000]]これで並び替えができましたが、ハッシュから配列に要素が格納されています。
これは、sort_byやsortを実行すると、なってしまいます。
なぜかは不明です。今回の条件ではキーだけを取得したいので、
配列をハッシュに戻して、キーを取得します。配列をハッシュに変換するには、to_hメソッドを使用します。
name_n_price.sort_by{ |name, price| -price}.to_h => {"B"=>3000, "C"=>2000, "A"=>1000}また、キーだけ欲しいので、keysメソッドを使用します。
name_n_price.sort_by{ |name, price| -price}.to_h.keys => ["B", "C", "A"]putsメソッドでは改行されるので、上記をputsで出力してあげれば、
期待通りの答えになります。以上です。
- 投稿日:2020-07-08T15:12:38+09:00
?Gem::LoadErrorが出て、sqlite3をバージョン指定したが改善されない。
初めまして、Macを使っており、最近ターミナルを初めて触り、Rubyをやり始めた者です。
開発環境を整える際につまづいていることについて質問をさせていただきたいです。現在、Gemfileに'sqlite3'のみ書かれていたものを、
gem 'sqlite3', '~> 1.3.13'
と書き換えたりしましたが、ローカルホストにアクセスしようとした時に以下の表示が出てしまい、
改善されません。全てが初めてで、恥ずかしくなるくらい初歩的な質問になっていると思います。
ですが、どうしても自分で作っていかなければならない状況でして、回答いただけますと嬉しいです。
どうぞよろしくお願いします。
- 投稿日:2020-07-08T11:04:20+09:00
form_withのform.〇〇について
form_withのform.〇〇について
form.withのform.〇〇について、わからないところがあったので、備忘録としてまとめていく。
form.text_field
<%= form.text_field :title %>幅20文字の入力欄が設定される
form.text_area
<%= form.text_area :content %>40×20の入力欄が設定される
form.email_filed
<%= form.email.field :email %>投稿内容に@が含まれていないとメールアドレスとみなされず、エラーになる。
テスト時に注意!他にもいろいろあるが今回はメールの入力欄にてテスト時にエラーが出たので、備忘録としてまとめた。
- 投稿日:2020-07-08T09:09:21+09:00
i18nのロケールをJavaScriptにわたす
多言語対応のアプリを作成中に詰まった
jQueryとAjaxを使って、親カテゴリを選択すると子カテゴリの候補がプルダウンで表示されるようにしました。
ところが、子カテゴリだけ、デフォルト表記のベトナム語でしか翻訳されてませんでした。例:親カテゴリ => 質問
子カテゴリ => 1,Ngôn ngữ và văn hóa
2,Tập huấn kỹ thuật
3,Ứng dụng và thủ tục原因
Ajaxで子カテゴリを取得する際、コントローラにロケールを渡してないことが原因でした。
category_pulldown.js$("#parent").on("change", function () { // 選択した親カテゴリからIDを取得 var id = document.getElementById("parent").value; // 中略 $.ajax({ type: 'GET', data: { parent_id: id }, // IDをparams[:parent_id]に入れて送信 url: '/categories/pulldown', // categoriesコントローラにて、受け取ったIDで子カテゴリを取得 dataType: 'json', }).done(function (children) { // 取得した子カテゴリを表示させる処理...一部抜粋ですがロケールの取得は以下の方法でやってました。
application_controller.rbbefore_action :set_locale private def set_locale I18n.locale = locale end def locale @locale ||= params[:locale] ||= I18n.default_locale end def default_url_options(options = {}) options.merge(locale: locale) end現在のロケールをJava Scriptへわたす
まずは、inputタグを使って現在のロケールを保持させます。
以下のようにtype="hidden"
と書くと、ユーザーから見ても画面には表示されません。hoge.html.erb<input type="hidden" class="current_locale" value="<%= I18n.locale %>">次に
locale: $('.current_locale').val()
を追加すると
親子カテゴリの翻訳言語が統一されました。category_pulldown.js$("#parent").on("change", function () { // 選択した親カテゴリからIDを取得 var id = document.getElementById("parent").value; // 中略 $.ajax({ type: 'GET', data: { parent_id: id, locale: $('.current_locale').val() }, // IDをparams[:parent_id]に入れて送信 url: '/categories/pulldown', // categoriesコントローラにて、受け取ったIDで子カテゴリを取得 dataType: 'json', }).done(function (children) { // 取得した子カテゴリを表示させる処理...HTMLはRubyからもJavaScriptからも情報の受け渡しができるので便利ですね
- 投稿日:2020-07-08T09:01:12+09:00
【Ruby on Rails】Chartkickでカラム別集計の円グラフを作成
はじめに
railsでグラフを挿入したかったのでchartkickを使用しました。
導入自体も容易なので、集計結果の円グラフ化の方法を記載します。参考にしたページ
-chartkick公式ドキュメント
-Railsでシンプルなグラフを扱うならchart-js-rails よりchartkickを使うべし
-爆速で円グラフを実装する[5分]環境
ruby 2.5.1
Rails 5.2.4.3前提と目標
ユーザの意見をまとめたvotesテーブルのopinionカラム(integer)から円グラフを作成する。
opinionカラムの値が1の場合は賛成、0の場合は反対で集計しますが、他の値やNULL値が混ざっている状態で作成します。votesテーブル
id opinion 1 1 2 1 3 0 4 1 5 0 6 2 7 NULL 8 3 9 NULL 10 0 完成図
※実機では円グラフの各要素にマウスカーソルを当てると要素数が表示されます。Chartkickの導入
参考ページに記載されていますが改めて記載します。
Gemfilegem "chartkick" #追記するGemfile$ bundle installapp/javascript/packs/application.js//= require chartkick //= require Chart.bundle #2行追記するviewページへの反映
表示させるページはどこでもいいですが今回はpostsコントローラーのindex.html.erbにグラフを表示させたいと思います。
app/views/posts/index.html.erb<%= pie_chart Vote.group(:opinion).count%>これだけでは何を表しているのかよくわからないグラフになっています。。
そこでコントローラーファイルに変数とグラフ用のメソッドを定義します。
app/controllers/posts_controller.rbdef index @opinion = Vote.pluck(:opinion) @aggregate = aggregateOpinion(@opinion) @sum = sumOpinion(@opinion) end def aggregateOpinion(array) result = [["賛成",0],["反対",0],["どちらでもない",0],["無回答",0]] array.each do |i| if i == 1 result[0][1] += 1 elsif i == 0 result[1][1] += 1 elsif i == nil result[3][1] += 1 else result[2][1] += 1 end end return result end def sumOpinion(array) result = [["総投票数",0],["有効投票数",0],["無効投票数",0]] array.each do |i| if i == nil result[2][1] += 1 else result[1][1] += 1 end end result[0][1] = array.length return result end解説
pluckメソッドで対象のカラムだけを配列で取得しています。
その後、aggregateOpinionメソッドで円グラフ用のデータに整形しています。result配列のインデックスがそのまま円グラフの表示順になるのでパラメータが
NULL値のものを最後にしてその次に所謂"その他"(今回の場合は[どちらでもない]という名前)が来るようにしています。sumOpinionメソッドは直接グラフには関係ありませんが総投票数の情報をグラフに載せることが難しいので別途作成しています。
以上の内容を踏まえ、改めてビューページに反映させたいと思います。
app/views/posts/index.html.erb<%= pie_chart @aggregate, width: "500px"%>これでも問題ないですが最後にレイアウトとsumOpinionメソッドで取得した内容をtable要素で表示させて完成です。
app/views/posts/index.html.erb<%= pie_chart @aggregate,colors: ["#3333cc","#cc3333","#339966","333"], donut: true , width: "500px"%> <table border="1" width="500"> <tr> <th>総投票数</th> <th>有効投票数</th> <th>無効投票数</th> </tr> <tr> <th><%=@sum[0][1]%></th> <th><%=@sum[1][1]%></th> <th><%=@sum[2][1]%></th> </tr> </table>終わりに
いかがでしたでしょうか。もう少しメソッドの部分に応用を効かせられたなぁと自分でも思いますね。。
もちろんchartkickが対応しているのは円グラフだけではないので是非調べてみてください。
見て頂いてありがとうございました!
- 投稿日:2020-07-08T00:03:01+09:00
Stack Overflow Developer Survey 2020の要点整理
この記事の目的
世界のディベロッパーのトレンドを追う上で、非常に重要な資料であるStack Overflow Developer Surveyの最新版である2020年版の要点を整理すること。自分の理解のためだけでなく、毎年出されているにもかかわらず、日本語での情報共有が少ない本調査の要点を日本語で整理することで、多くの日本人の開発者が世界のトレンドを理解することを期待する。
原文
https://insights.stackoverflow.com/survey/2020調査概要
・2020年2月に世界中の65,000人以上のディベロッパーにアンケートを実施(注:回答者はアメリカ、インド、ヨーロッパに集中)。
・コロナウイルスが流行る前に調査が実施されたため、仕事や賃金のデータは現時点での情報と異なる可能性があることに注意。キーポイント
(1)最も愛された言語はRust, Typescript, Pythonの順番。Rustは五年連続で一位。
(2)回答者の8割がDevOpsが重要だと考えている。
(3)回答者の9割が、プログラミングで壁に当たった時、Stack Overflowを参照している。職種
・複数回答ありで、55%がバックエンド、フルスタックと答え、37%がフロントエンドのエンジニアと回答。
・プログラミング経験年数の層の中で一番高い割合が5年から9年で回答者の30%を占めた。5年以下と答えたのは17%。
・一方で、仕事としてプログラミングした経験は5年以下が最多で回答者の40%を占めた。
・回答者の85%は10代でプログラミングを開始した。学歴
・46%が学士号を、23%が修士号を取得。また回答者の6割以上が学部時代にコンピューターサイエンスを専攻していた。
言語その他のランキング
- 投稿日:2020-07-08T00:01:49+09:00
Rubyで繰り返し処理の中で任意の要素数で処理を差し込む方法
この記事で書くこと
Rubyで数字を整数かどうか判定する文を書こうとしたら、
すぐに自分の中から出てこなかったので
その内容を学びとしてアウトプットする。繰り返し処理の中で任意の要素数で処理を差し込む方法について
学びとしてアウトプットする。(記述する処理と内容が適さないため、修正しました。 )
特定の数字の倍数かどうかで処理を分ける
pry(main)> test_num = 1 => 1 pry(main)> test_num.to_f => 1.0 pry(main)> test_num.to_f.to_s.split('.')[1] == '0' => true pry(main)> test_num2 = 1.05 => 1.05 pry(main)> test_num2.to_f => 1.05 pry(main)> test_num2.to_f.to_s.split('.')[1] == '0' => false (0..1000).to_a.each do |num| if num.fdiv(100).to_s.split('.')[1] == '0' && num != 0 p '100の倍数です' end end
- 投稿日:2020-07-08T00:01:49+09:00
Rubyで数字が整数か分数かどうかを判定するやり方
この記事で書くこと
Rubyで数字を整数かどうか判定する文を書こうとしたら、
すぐに自分の中から出てこなかったので
その内容を学びとしてアウトプットする。最初に調べた時に見つかって読ませていただいたQiita記事は以下のものです
Rubyで整数かどうか判定する(正規表現、integer?、to_i、Integer())
自分なりに考えてみたかったので、今回は別の方法を試しています。
判定する
数字のまま、判定する方法はなかなか見つからなかったので、文字列で考えることにしました。
pry(main)> test_num = 1 => 1 pry(main)> test_num.to_f => 1.0 pry(main)> test_num.to_f.to_s.split('.')[1] == '0' => true # 整数 pry(main)> test_num2 = 1.05 => 1.05 pry(main)> test_num2.to_f => 1.05 pry(main)> test_num2.to_f.to_s.split('.')[1] == '0' => false # 分数