- 投稿日:2019-05-30T22:37:20+09:00
今日のリファクタリング -1
お腹ぐるぐるヨーグルト
どうも k1y0です。初めてのqiita投稿。今日のキニナルコード
before_action :set_content #@content def show if params[:type] == 'programming' @title = '速習 プログラミング!' @text = @content.programming_text elsif params[:type] == 'food' @title = '美味しい焼肉屋さん' @text = @content.food_text elsif params[:type] == 'money' @title = '爆速で100億稼ぐ方法' @text = @content.money_text end endこんなコードがありました。
どう変更したか
#before省略 def show @title, @text = case params[:type] when 'programming' '速習 プログラミング!', @content.programming_text when 'food' '美味しい焼肉屋さん', @content.food_text when 'money' '爆速で100億稼ぐ方法', @content.money_text end end課題
typeごとにhashとか作ってkeyで受け取った方がわかりやすそう。rubyむずかしい
- 投稿日:2019-05-30T20:49:51+09:00
Amazon Linux 2でrbenvを使う
環境
- Amazon Linux 2
インストール手順
install# 0. 依存ライブラリのインストール $ sudo yum install -y git gcc openssl-devel readline-devel zlib-devel # 1. レポジトリをクローン $ git clone https://github.com/rbenv/rbenv.git ~/.rbenv # 2. .bash_profileにパスを追記 $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile # 3. init $ ~/.rbenv/bin/rbenv init # 4. Restart your shell so that PATH changes take effect. $ exec $SHELL -l # 5. プラグイン用ディレクトリ $ mkdir -p "$(rbenv root)"/plugins # 6. rbenv installができるように $ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build # 7. 確認 $ curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash使い方
usage# インストールできるバージョンを確認 $ rbenv install -l # 指定バージョンのRubyをインストール $ rbenv install 2.5.1 # グローバルのバージョン指定 $ rbenv global 2.5.1 # インストールされているバージョンを確認 $ rbenv versions参考
- 投稿日:2019-05-30T20:47:08+09:00
【Ruby on Rails】resourcesで作られるルーティングの表記を作るアプリを作った。
タイトル見て「は?」って思った方もいるかなと思います。
Railsでアプリを作成する際に欠かせないルーティングですが、そこには resources っていう便利なメソッドがあります。
例えば、routes.rbに
routes.rbresources :usersって書くと
Prefix Verb URI Pattern Controller#Action users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy これらが一気に作られるわけですよ。(ああ便利)
ただ、たまに resources を使いたくない人とか使えない場合とかあったりなかったりします。
例えば、URI Patteren のところに users ではないコントローラーを使いたいときとか。
そんなとき用に完全個人用に Rails でアプリ作りました。
(個人用にサクッと作ったので見たらわかるやっすいやつやん)↓↓↓↓↓↓↓↓これです↓↓↓↓↓↓↓↓
https://resourcesrouting.herokuapp.com/使い方
Name と Name2 にそれぞれ何かしら文字列を入れて "Create Home" ボタン押すだけ。
あとはコピってペーストするだけ。
正直ワンパターンだけしかこんな今年ないならいらないですが、いくつもルーティングをこんな感じにする際は多少楽になるかなって思います。
後記
実際これ作るのに30分もかかってないですが、こんなの作ってる暇あったら素直に手打ちできたなと思っています(笑)
まぁでも、楽しいんでいいです(¬ω¬)b
ちなみにDBもセッションもなにも使ってないので何かが保存されることはないです。
- 投稿日:2019-05-30T19:30:37+09:00
Ruby アプリケーションの Cloud Native なモニタリング
What's Cloud Native?
- Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds.
- Resilient, Manageable, and Observable
- たくさんのアプリケーションを動かすことへの問題、はパブリッククラウド や Docker や Kubernetes がかなり解決してくれた
- それらをいい感じに強調させたり、問題が起きた時に正しく対処を行えるかどうかに大きな課題があるのが現在
Why Cloud Native? (in Quipper case)
- 一つのコードベースで様々なビジネスが行われ、組織的・ビジネス的な複雑性が課題となっている
- B2C/B2B, 小中高, 国内/グローバル, そして新規事業
- サービスがたくさん増えて、複雑なエコシステムを形成しようとしている
- Microservices
- 複数のクラスタ、環境上に、様々なプロダクト、アプリケーション
- 一貫した方法で Observability を提供したい
- https://ymotongpoo.hatenablog.com/entry/2019/03/25/084500
- SRE がボトルネックにならず、開発チームが自律的かつ高速にプロダクト開発を回せるようにしたい
- セルフサービス化の流れ
- Observability は大事だけど、そればかりに時間を取られては本末転倒
The Theree Pillars of Observability
- https://www.oreilly.com/library/view/distributed-systems-observability/9781492033431/ch04.html
- Event Logs
- Quipper では GCP の Stackdriver Logging を使っている (これは超絶便利!!)
- Metrics
- Quipper では主に Datadog を使っている
- 今日はこれの話
- Tracing
- Jaeger 試したりしているけどまだまだ活用できてない
- そもそも社内的にまだそんなに欲されている状況でもない
- とはいえ必要になるのも時間の問題なので今日の kawasy さんの発表楽しみです!!!
Collecting Ruby Metrics for Datadog
- 今日は prometheus_exporter という gem について紹介します
- Ruby プロセスや様々なフレームワーク (Rails, Puma, Sidekiq, etc) のための Prometheus の Exporter を提供する
- Prometheus? Exporter?
Prometheus
- 時系列データベースを基盤としたモニタリングシステム
- が、Quipper で使っている訳ではないので僕も触ったことがある訳ではない
- そもそも Datadog を使っているのに何故?
- そこで Exporter
Prometheus Exporter
- 監視対象のサーバ等で起動して、Prometheus から pull してメトリクスを取得するための Web API みたいなもの
- オープンソースで多数公開されている
- Datadog は Prometheus Exporter からのメトリクス収集もできる
# HELP ruby_rss Total RSS used by process. # TYPE ruby_rss gauge ruby_rss{type="master",pid="1",pod_name="api-6bcf6c8554-jntdq"} 390942720 ruby_rss{pod_name="api-6bcf6c8554-jntdq",type="web",pid="45"} 346877952 ruby_rss{pod_name="api-6bcf6c8554-jntdq",type="web",pid="42"} 347594752 ruby_rss{pod_name="api-6bcf6c8554-jntdq",type="web",pid="39"} 350285824 ruby_rss{pod_name="api-6bcf6c8554-jntdq",type="web",pid="50"} 347901952 # HELP ruby_unicorn_workers_total Number of unicorn workers. # TYPE ruby_unicorn_workers_total gauge ruby_unicorn_workers_total{pod_name="api-6bcf6c8554-jntdq"} 4 # HELP ruby_unicorn_active_workers_total Number of active unicorn workers # TYPE ruby_unicorn_active_workers_total gauge ruby_unicorn_active_workers_total{pod_name="api-6bcf6c8554-jntdq"} 0
Datadog and Kubernetes
- Datadog には Auto Discovery という仕組みがあり、Pod (コンテナの集合的なやつ) の annotations (メタデータみたいなやつ) に設定を記述するだけで、Datadog が勝手に収集してくれる
- コピペでメトリクスの収集が行われるようになるので簡単
- サービス作成時のテンプレート的なやつに含めて自動化とかもできそう
- Kubernetesにデプロイしたアプリケーションのメトリクスを自動収集する
Annotation
metadata: annotations: ad.datadoghq.com/api.check_names: | ["prometheus"] ad.datadoghq.com/api.init_configs: | [{}] ad.datadoghq.com/api.instances: | [ { "prometheus_url": "http://%%host%%:9394/metrics", "namespace": "prometheus_checks", "metrics": ["*"] } ]
今日の成果
Ruby クイズ: 何が問題だったでしょう
元のコード
def worker_process_count # ... # find all processes whose parent is the unicorn master # but we're actually only interested in the number of processes (= lines of output) result = `ps --no-header -o pid --ppid #{pid}` result.lines.count end
- 投稿日:2019-05-30T17:38:56+09:00
【Rails】Active Storage + S3 + Active JobでGoogle Cloud Visionセーフサーチ
個人開発のWebアプリ「まちかどルート」v6.0rc6への実装メモです。
Amazon S3に画像をダイレクトアップロード
Railsガイドをもとに実装しました。
セーフサーチを実装
前回の記事のとおりです。
【Rails】Active Storage環境下でGoogle Cloud Visionのセーフサーチを実装
S3アップロード画像のパブリックURLを取得
こちらの記事を参考にしました。
Rails ActiveStorage で PUBLIC な URL を表示する
セーフサーチを非同期処理化
※controllerのコードは省略します
model
post.rbhas_one_attached :image after_commit :annotate_self, on: [:create, :update] def annotate_self if image.attached? ImageAnnotateJob.set(wait: 10.second).perform_later(self) end endpostの新規投稿もしくは編集のときに画像
imageが添付されていたらImageAnnotateJobというActive Jobのキューを走らせます。10秒後に走らすようセットした理由は、Amazon S3へのアップロード完了までのタイムラグが発生するかなと思ったからです。job
image_annotate_job.rbclass ImageAnnotateJob < ApplicationJob queue_as :second def perform(target) tempfile = target.image.attachment.service.send(:object_for, target.image.key).public_url require "google/cloud/vision" image_annotator = Google::Cloud::Vision::ImageAnnotator.new response = image_annotator.safe_search_detection image: tempfile response.responses.each do |res| safe_search = res.safe_search_annotation if safe_search.adult.to_s == "VERY_LIKELY" || safe_search.adult.to_s == "LIKELY" target.destroy return elsif safe_search.violence.to_s == "VERY_LIKELY" || safe_search.violence.to_s == "LIKELY" target.destroy return elsif safe_search.medical.to_s == "VERY_LIKELY" || safe_search.medical.to_s == "LIKELY" target.destroy return end end end endいったん
tempfileにS3アップロード画像のパブリックURLを格納。それをGoogle Cloud Visionのセーフサーチにかけます。このコードはすべてActive Jobによってバックグラウンド(非同期)処理されます。もし不適切な画像と判断されれば投稿そのものを削除する流れです。Sidekiq
config\sidekiq.yml:concurrency: 5 :queues: - [default, 7] - [second, 5]じぶんはActive JobのライブラリとしてSidekiqを使用しています。上記
image_annotate_job.rbのqueue_as :secondによってキューが処理される優先順位を設定しました。あとがき
駆け足ですが以上です。Google Cloud Visionのセーフサーチは高精度ですがレスポンスに1~2秒ほど要するため、そのぶん前回の記事のような同期処理だと投稿完了までにユーザーを待たせてしまううえ、サーバーサイドに負荷がかかります。今回の記事はそれらの課題を解決するものとなります。
また、苦労したのがS3アップロード画像のパブリックURLを取得する部分でした。
url_forやrails_blob_urlといったActive Storageのメソッドを使うと時限的なものorリダイレクトされるURLしか取得できずセーフサーチにかけても「そんな画像はありませんよ」と言われてしまいます。というわけで試行錯誤を経たおかげでよりレスポンス性とセキュリティ性を兼ね備えた画像投稿機能を実装できました。
- 投稿日:2019-05-30T16:09:53+09:00
The Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrs
The Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrsThe Indian (Black Magic Astrologer) World Famous Vashikaran Expert{+91-7837546443} #Online24hrs[uploading-0]()
- 投稿日:2019-05-30T15:28:51+09:00
Travis CIからSlackに通知する際の認証情報の暗号化(travis encrypt)について
TL;DR
.travis.ymlに直トークン書きたくないからtravis encryptしたい- Travis「Travis CLI 使ってね」っ
gem install travis- このプロジェクト
rubyじゃないし、ローカルにもruby入ってない…というときに、
rubyのDockerイメージを使ってアプリケーションに影響なくサクッとやりたい準備
実践したのはmac + Docker for Mac環境ですが、Docker使えれば大丈夫なはずです
- macOS 10.14.5
- docker 18.09.2
想定プロジェクト
- GitHubで管理されている
- travis-ci.comでCIを回している
- アプリケーションルートに
.travis.ymlがある手順
$ cd /path/to/project $ docker run -it --rm --volume $(pwd)/:/app ruby bash # cd app # gem install travis # travis login --com --github-token XXXXXXXX # travis encrypt "xxxxxx:XXXXXXXX" --add notifications.slack --com # exit※
--comオプションについてtravis-ci.comを使ってるので
--comオプションつけましたが、travis-ci.orgな場合はオプション無しでよいのかもしれません(未検証)※
--github-tokenオプションについてGitHubで二段階認証を有効化している場合は、
Personal access tokenを発行して指定する必要があります
GitHub > Settings > Developer settings > Personal access tokens から発行できます
必要なスコープはrepo,user:emailだと思われます
有効化していない場合は--github-tokenオプションなしでやれば、普通にuser/passの認証がされるのでそれで大丈夫だと思われます(未検証)※
travis encryptコマンドの部分はSlackのTravisアプリ画面の「セットアップの手順」に書いてあるのでコピーしちゃえば大丈夫です上記うまくいけば
.travis.ymlに暗号化された認証情報が追記されているはずなので、コミットしちゃえばOK+notifications: + slack: + secure: XXXXXXXXXXXXXXXX...まとめ
Circle CIは設定ファイルじゃなくWeb上の設定画面でWebhookのURL指定するだけで通知できたので楽ちんだった
- 投稿日:2019-05-30T12:37:39+09:00
+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}
+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}+91-9501761003 Black magic Specialist Molvi ji in {{Mumbai}}
- 投稿日:2019-05-30T11:41:37+09:00
mac で rails new した後の bundle install 時に ffi 1.11.0 のインストールが失敗したのでバージョン固定で解決
バージョン
名前 バージョン macOS 10.13.6 ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin17] rails 5.2.3 起きたエラー内容
$ bundle exec rails new ../sample create create README.md create Rakefile create .ruby-version create config.ru create .gitignore create Gemfile (略) Fetching gem metadata from https://rubygems.org/............ Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Fetching rake 12.3.2 Your user account isn't allowed to install to the system RubyGems. You can cancel this installation and run: bundle install --path vendor/bundle to install the gems into ./vendor/bundle/, or you can enter your password and install the bundled gems to RubyGems using sudo. Password: Installing rake 12.3.2 Fetching concurrent-ruby 1.1.5 Installing concurrent-ruby 1.1.5 (略) Fetching coffee-rails 4.2.2 Installing coffee-rails 4.2.2 Fetching ffi 1.11.0 Installing ffi 1.11.0 with native extensions Gem::Ext::BuildError: ERROR: Failed to build gem native extension. current directory: /private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/bin/ruby -r ./siteconf20190519-47252-1at7a63.rb extconf.rb checking for ffi.h... no checking for ffi.h in /usr/local/include,/usr/include/ffi... yes checking for ffi_call() in -lffi... yes checking for ffi_closure_alloc()... no checking for shlwapi.h... no checking for rb_thread_call_without_gvl()... yes checking for ruby_native_thread_p()... yes checking for ruby_thread_has_gvl_p()... yes creating extconf.h creating Makefile To see why this extension failed to compile, please check the mkmf.log which can be found here: /var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/extensions/universal-darwin-17/2.3.0/ffi-1.11.0/mkmf.log current directory: /private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c make "DESTDIR=" clean current directory: /private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c make "DESTDIR=" mkdir -p "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c"/libffi-i386; (if [ ! -f "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c/libffi"/configure ]; then echo "Running autoreconf for libffi"; cd "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c/libffi" && /bin/sh /private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c/libffi/autogen.sh > /dev/null; fi); (if [ ! -f "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c"/libffi-i386/Makefile ]; then echo "Configuring libffi for i386"; cd "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c"/libffi-i386 && env CC=" xcrun clang" CFLAGS="-arch i386 " LDFLAGS="-arch i386" "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c/libffi"/configure --disable-static --with-pic=yes --disable-dependency-tracking --disable-docs --host=i386-apple-darwin > /dev/null; fi); env MACOSX_DEPLOYMENT_TARGET=10.4 /Applications/Xcode.app/Contents/Developer/usr/bin/make -C "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c"/libffi-i386 (略) libtool: link: ranlib .libs/libffi_convenience.a libtool: link: ( cd ".libs" && rm -f "libffi_convenience.la" && ln -s "../libffi_convenience.la" "libffi_convenience.la" ) /bin/sh ./libtool --tag=CC --mode=link xcrun clang -arch i386 -Wall -fexceptions -no-undefined -version-info `grep -v '^#' /private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c/libffi/libtool-version` '-arch' 'i386' -arch i386 -o libffi.la -rpath /usr/local/lib src/prep_cif.lo src/types.lo src/raw_api.lo src/java_raw_api.lo src/closures.lo src/x86/ffi.lo src/x86/sysv.lo libtool: link: xcrun clang -dynamiclib -o .libs/libffi.7.dylib src/.libs/prep_cif.o src/.libs/types.o src/.libs/raw_api.o src/.libs/java_raw_api.o src/.libs/closures.o src/x86/.libs/ffi.o src/x86/.libs/sysv.o -arch i386 -arch i386 -arch i386 -install_name /usr/local/lib/libffi.7.dylib -compatibility_version 9 -current_version 9.0 -Wl,-single_module ld: warning: The i386 architecture is deprecated for macOS (remove from the Xcode build setting: ARCHS) Undefined symbols for architecture i386: "___x86.get_pc_thunk.bx", referenced from: _ffi_call_i386 in sysv.o _ffi_closure_raw_SYSV in sysv.o _ffi_closure_raw_THISCALL in sysv.o "___x86.get_pc_thunk.dx", referenced from: _ffi_closure_i386 in sysv.o _ffi_closure_STDCALL in sysv.o ld: symbol(s) not found for architecture i386 clang: error: linker command failed with exit code 1 (use -v to see invocation) make[3]: *** [libffi.la] Error 1 make[2]: *** [all-recursive] Error 1 make[1]: *** [all] Error 2 make: *** ["/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c"/libffi-i386/.libs/libffi_convenience.a] Error 2 make failed, exit code 2 Gem files will remain installed in /var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0 for inspection. Results logged to /var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/extensions/universal-darwin-17/2.3.0/ffi-1.11.0/gem_make.out An error occurred while installing ffi (1.11.0), and Bundler cannot continue. Make sure that `gem install ffi -v '1.11.0'` succeeds before bundling. In Gemfile: spring-watcher-listen was resolved to 2.0.1, which depends on listen was resolved to 3.1.5, which depends on rb-inotify was resolved to 0.10.0, which depends on ffi run bundle exec spring binstub --all bundler: command not found: spring Install missing gem executables with `bundle install` $macOS が 10.13.6 なのがきっといけないのだろうと思うが、そこじゃない場所で解決を試みる。
解決後に
Gemfile.lockを見ると、ffiの依存指定は一箇所のみで、こうなっていた。rb-inotify (0.10.0) ffi (~> 1.0)
ffi (~> 1.0)とあるので、ffiの1.xの最新版を取ろうとして、それがインストールできていない。解決方法
~> 1.0ということは1.11.0が必要なわけじゃないはずので、Gemfileを書き換えて、インストールできるバージョンに固定した。1.9.21は、あくまで例ですgem 'ffi', '= 1.9.21'(これは要らないかもしれない) 事前にインストールしておく。
$ sudo gem install ffi -v '1.9.21' Fetching: ffi-1.9.21.gem (100%) Building native extensions. This could take a while... Successfully installed ffi-1.9.21 Parsing documentation for ffi-1.9.21 Installing ri documentation for ffi-1.9.21 Done installing documentation for ffi after 3 seconds 1 gem installed $で、改めて bundle install
$ bundle install --path vendor/bundle The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/............ Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Using rake 12.3.2 Using concurrent-ruby 1.1.5 (略) Using railties 5.2.3 Using coffee-rails 4.2.2 Fetching ffi 1.9.21 Installing ffi 1.9.21 with native extensionsうまくいった
バージョンの選び方
ffi 1.9.21を選ぶべきだというわけではないのですが、あらかじめ mac 側にインストールしてある可能性(他の ruby プログラムを動かすためにインストールしたことがあった、など)を考えれば、たとえばgem listで調べればインストール可能なバージョンがわかるはず$ gem list ffi *** LOCAL GEMS *** ffi (1.9.21) public_suffix (3.0.3)それ以外では、 https://rubygems.org/gems/ffi/versions を見て、一つ前のバージョンの最新を選ぶと良いと思います。今回だと、失敗したのは
1.11.0なので、1.10.xの中での最新の1.10.0を試す、それでもダメなら1.9.25を試す、...を繰り返す、など。時間がある人は、もちろん原因を特定してそれが解決できる最新のバージョンを選びましょう。
- 投稿日:2019-05-30T11:41:37+09:00
mac で (rails new した後の bundle install 時に) ffi 1.11.0 のインストールが失敗したのでバージョン固定で解決
バージョン
名前 バージョン macOS 10.13.6 ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin17] rails 5.2.3 コメントでご指摘あったように、問題そのものは Rails とは関係ないです。
起きたエラー内容
$ bundle exec rails new ../sample create create README.md create Rakefile create .ruby-version create config.ru create .gitignore create Gemfile (略) Fetching gem metadata from https://rubygems.org/............ Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Fetching rake 12.3.2 Your user account isn't allowed to install to the system RubyGems. You can cancel this installation and run: bundle install --path vendor/bundle to install the gems into ./vendor/bundle/, or you can enter your password and install the bundled gems to RubyGems using sudo. Password: Installing rake 12.3.2 Fetching concurrent-ruby 1.1.5 Installing concurrent-ruby 1.1.5 (略) Fetching coffee-rails 4.2.2 Installing coffee-rails 4.2.2 Fetching ffi 1.11.0 Installing ffi 1.11.0 with native extensions Gem::Ext::BuildError: ERROR: Failed to build gem native extension. current directory: /private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/bin/ruby -r ./siteconf20190519-47252-1at7a63.rb extconf.rb checking for ffi.h... no checking for ffi.h in /usr/local/include,/usr/include/ffi... yes checking for ffi_call() in -lffi... yes checking for ffi_closure_alloc()... no checking for shlwapi.h... no checking for rb_thread_call_without_gvl()... yes checking for ruby_native_thread_p()... yes checking for ruby_thread_has_gvl_p()... yes creating extconf.h creating Makefile To see why this extension failed to compile, please check the mkmf.log which can be found here: /var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/extensions/universal-darwin-17/2.3.0/ffi-1.11.0/mkmf.log current directory: /private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c make "DESTDIR=" clean current directory: /private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c make "DESTDIR=" mkdir -p "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c"/libffi-i386; (if [ ! -f "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c/libffi"/configure ]; then echo "Running autoreconf for libffi"; cd "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c/libffi" && /bin/sh /private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c/libffi/autogen.sh > /dev/null; fi); (if [ ! -f "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c"/libffi-i386/Makefile ]; then echo "Configuring libffi for i386"; cd "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c"/libffi-i386 && env CC=" xcrun clang" CFLAGS="-arch i386 " LDFLAGS="-arch i386" "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c/libffi"/configure --disable-static --with-pic=yes --disable-dependency-tracking --disable-docs --host=i386-apple-darwin > /dev/null; fi); env MACOSX_DEPLOYMENT_TARGET=10.4 /Applications/Xcode.app/Contents/Developer/usr/bin/make -C "/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c"/libffi-i386 (略) libtool: link: ranlib .libs/libffi_convenience.a libtool: link: ( cd ".libs" && rm -f "libffi_convenience.la" && ln -s "../libffi_convenience.la" "libffi_convenience.la" ) /bin/sh ./libtool --tag=CC --mode=link xcrun clang -arch i386 -Wall -fexceptions -no-undefined -version-info `grep -v '^#' /private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c/libffi/libtool-version` '-arch' 'i386' -arch i386 -o libffi.la -rpath /usr/local/lib src/prep_cif.lo src/types.lo src/raw_api.lo src/java_raw_api.lo src/closures.lo src/x86/ffi.lo src/x86/sysv.lo libtool: link: xcrun clang -dynamiclib -o .libs/libffi.7.dylib src/.libs/prep_cif.o src/.libs/types.o src/.libs/raw_api.o src/.libs/java_raw_api.o src/.libs/closures.o src/x86/.libs/ffi.o src/x86/.libs/sysv.o -arch i386 -arch i386 -arch i386 -install_name /usr/local/lib/libffi.7.dylib -compatibility_version 9 -current_version 9.0 -Wl,-single_module ld: warning: The i386 architecture is deprecated for macOS (remove from the Xcode build setting: ARCHS) Undefined symbols for architecture i386: "___x86.get_pc_thunk.bx", referenced from: _ffi_call_i386 in sysv.o _ffi_closure_raw_SYSV in sysv.o _ffi_closure_raw_THISCALL in sysv.o "___x86.get_pc_thunk.dx", referenced from: _ffi_closure_i386 in sysv.o _ffi_closure_STDCALL in sysv.o ld: symbol(s) not found for architecture i386 clang: error: linker command failed with exit code 1 (use -v to see invocation) make[3]: *** [libffi.la] Error 1 make[2]: *** [all-recursive] Error 1 make[1]: *** [all] Error 2 make: *** ["/private/var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0/ext/ffi_c"/libffi-i386/.libs/libffi_convenience.a] Error 2 make failed, exit code 2 Gem files will remain installed in /var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/gems/ffi-1.11.0 for inspection. Results logged to /var/folders/_h/xgr__5xn3kscjx0f8m7v3qbr0000gn/T/bundler20190519-47252-1smbwrlffi-1.11.0/extensions/universal-darwin-17/2.3.0/ffi-1.11.0/gem_make.out An error occurred while installing ffi (1.11.0), and Bundler cannot continue. Make sure that `gem install ffi -v '1.11.0'` succeeds before bundling. In Gemfile: spring-watcher-listen was resolved to 2.0.1, which depends on listen was resolved to 3.1.5, which depends on rb-inotify was resolved to 0.10.0, which depends on ffi run bundle exec spring binstub --all bundler: command not found: spring Install missing gem executables with `bundle install` $
macOS が 10.13.6 なのがきっといけないのだろうと思うが、そこじゃない場所で解決を試みる。コメントにて、OS も関係ないとのご指摘。ld: warning: The i386 architecture is deprecated for macOS (remove from the Xcode build setting: ARCHS)の辺りを調べればいいかもしれないのですが、それはまたの機会に。解決後に
Gemfile.lockを見ると、ffiの依存指定は一箇所のみで、こうなっていた。rb-inotify (0.10.0) ffi (~> 1.0)
ffi (~> 1.0)とあるので、ffiの1.xの最新版(当時は1.11.0)を取ろうとして、それがインストールできていない。解決方法
~> 1.0ということは1.11.0が必要なわけじゃないはずので、Gemfileを書き換えて、インストールできるバージョンに固定した。(1.9.21は、あくまで例です)gem 'ffi', '= 1.9.21'(これは要らないかもしれない) 事前にインストールしておく。
$ sudo gem install ffi -v '1.9.21' Fetching: ffi-1.9.21.gem (100%) Building native extensions. This could take a while... Successfully installed ffi-1.9.21 Parsing documentation for ffi-1.9.21 Installing ri documentation for ffi-1.9.21 Done installing documentation for ffi after 3 seconds 1 gem installed $で、改めて bundle install
$ bundle install --path vendor/bundle The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`. Fetching gem metadata from https://rubygems.org/............ Fetching gem metadata from https://rubygems.org/. Resolving dependencies... Using rake 12.3.2 Using concurrent-ruby 1.1.5 (略) Using railties 5.2.3 Using coffee-rails 4.2.2 Fetching ffi 1.9.21 Installing ffi 1.9.21 with native extensionsうまくいった
バージョンの選び方
ffi 1.9.21を選ぶべきだというわけではないのですが、あらかじめ mac 側にインストールしてある可能性(他の ruby プログラムを動かすためにインストールしたことがあった、など)を考えれば、たとえばgem listで調べればインストール可能なバージョンがわかるはず$ gem list ffi *** LOCAL GEMS *** ffi (1.9.21) public_suffix (3.0.3)それ以外では、 https://rubygems.org/gems/ffi/versions を見て、一つ前のバージョンの最新を選ぶと良いと思います。今回だと、失敗したのは
1.11.0なので、1.10.xの中での最新の1.10.0を試す、それでもダメなら1.9.25を試す、...を繰り返す、など。時間がある人は、もちろん原因を特定してそれが解決できる最新のバージョンを選びましょう。
- 投稿日:2019-05-30T11:22:23+09:00
find_or_initialize_byメソッド
find_or_initialize_byメソッドはfind_or_create_byと同様に動作しますが、createの代りにnewを呼ぶ点が異なります。つまり、モデルの新しいインスタンスは作成されますが、その時点ではデータベースに保存されていません。
- 投稿日:2019-05-30T10:36:53+09:00
【Rails】個人開発のアプリに5,000万円の交渉相手が現れました
個人開発のWebアプリ「まちかどルート」。
驚いたことに5,000万円の交渉話が飛び込んできました。
【速報】
— 西村 治久(ハル) (@west2538) May 29, 2019
個人開発のアプリ #まちかどルート に5,000万円の買い手が現れました。これから交渉しますhttps://t.co/I47lB4Z8pL pic.twitter.com/uIF322UZS6まだ交渉中ではありますが、ここに至る1年弱の所感をまとめたいと思いました。
RubyとRailsに入門して1年弱
プログラミングを学んだのは、40代でセミリタイア&山奥で隠居生活するようになってからというもの、新たなたしなみのひとつにしようと思ったのがきっかけです。
ソーシャルな隠居の、新たな“たしなみ”。プログラミングをはじめました
アイデアがどう生まれたか
世間と隔絶されずより一層ひととの関係性を高める自称 ソーシャルな隠居生活では、じぶんの時間がたくさん持てます。余白の時間に「あ、こんな機能を作ってみようかな」というアイデアがふと生まれやすいです。
アイデアとは、ひねり出すものではなく、それまで興味の赴くまま得てきた数多くのインプットと行動から余白の時間にふと思いつくものなのでしょうか。
よくローカル環境構築などと言いますが、ノートPC上だけでなく自身の環境構築も大事なんだと思いました。そういう意味ではじぶんの環境構築は恵まれていたかもしれません。
生活への変化
プログラミングをたしなみのひとつにしたおかげで、そうした余白の時間が充実したものになりました。
たとえばカフェなどで思いがけず待ち時間が長くなったとしても、ふとアイデアを思いついたらノートPCを開き、コードに落とし込みます。
いままで持て余していた時間が楽しいものとなりました。
どう学んできたか
書籍を読んだりスクールに通うということをしていません。ほとんどが必要に応じてインターネットで調べるという独学スタイルです。そのせいか基本的な知識がところどころ抜けているように思います。
反面、基本的な知識がないおかげなのか、参考文献をそのまま写すのではなく「もしかしてこんなふうに書けば動くのかな」という感じでコードを自由奔放に書き換えたりするんですけど、それが本当に動くとうれしいですね。いわゆる論理的思考が鍛えられているのでしょうか。
開発を続けられた理由
オープンソースのSNS「Mastodon」に触れていたことが大きかったと思います。だいぶお手本にさせていただきましたし、APIで連携することで個人開発の幅が広がりました。
Mastodonが日本に普及して2年。インスタンス運営と連携アプリの開発が楽しい
これから
5,000万円の交渉の成否はこれからのことなのでさておき、じぶんの生活を経済的というよりひととの関係性や精神的に豊かにするための趣味の個人開発ですから飽きるまで続けていこうと思います。
なによりも、スタートアップやエンジニア関係のつながりがこれまで以上に増えたことがうれしいです。やはりコミュニティとして日常的に学びあうことが、成長のスピードを加速させるんでしょうね。
- 投稿日:2019-05-30T09:57:38+09:00
[初心者]テストコードを書く際の原則
テストコードとは?
テストコードとは、本番環境に実装する前に、各メソッドがしっかりと自分が期待した値を取得し、保存しているのか確認することを指します。
もしなにもテストせずに本番環境に実装してしまったら、ユーザーからの苦情があり、サーバーを止めて修正しないといけないですよね??
それらの原因が結果的に、損失だったり、ユーザーの評価を下げてしまうことになる可能性があります?今回はテストコードを書く際の大切な原則について書いていきたいと思います!!
①各exampleで期待する値は1つだけ
期待する値を2つ以上含めてしまうと、どちらのエクスペクテーションでエラーが出たのか判別できず、正確なテストができないため、1つだけにしておきましょう!!
※エクスペクテーション(予想、期待、切望、待望)
②期待する結果をはっきりと分かりやすく記述
明快な記述にすることで、自身の確認やメンバーとの共有、顧客への仕様説明が楽になります。
結果としてコミュニケーションミスが低下し、スムーズに仕事を進めることができます。もし分かりづらい記述だったら、一々相手に確認をしてテストするという無駄な時間が生まれますよね?
③起きて欲しいことと起きてほしくないこと両方をテストする
これはどちらもテストで予期せぬ動作が残るのを防ぐためにどっちも行います!!
そうすればエラー発生率の低下に繋がると思っています?
④境界値をテストする
例えば、パスワード登録の際に8文字以上を保存するとしましょう。
テストする際には、8文字以上保存されているかの確認と、逆に7文字以下のパスワードは保存されていないかテストをする必要があります。
よくバリデーションとかに使われるので、しっかりチェックする必要がありますね?可読性を考えつつ、適度にDRYする
DRYとはDon't Repeat Yourselfの略になります。
テストコードにおいて何よりも相手に伝わる分かりやすさが大切です。
分かりづらくなってテストの見落としが起きるよりマシだと思いませんか?最後に
本番環境前には必ずテストコードを書き、
ちゃんとメソッドが定義されているのか、
値がしっかり取得されているのかを確認してしっかりとしたアプリケーションを作っていきましょう!!!
- 投稿日:2019-05-30T09:57:38+09:00
【スクール生】12日目:テストコードを書く際の原則
テストコードとは?
テストコードとは、本番環境に実装する前に、各メソッドがしっかりと自分が期待した値を取得し、保存しているのか確認することを指します。
もしなにもテストせずに本番環境に実装してしまったら、ユーザーからの苦情があり、サーバーを止めて修正しないといけないですよね??
それらの原因が結果的に、損失だったり、ユーザーの評価を下げてしまうことになる可能性があります?今回はテストコードを書く際の大切な原則について書いていきたいと思います!!
①各exampleで期待する値は1つだけ
期待する値を2つ以上含めてしまうと、どちらのエクスペクテーションでエラーが出たのか判別できず、正確なテストができないため、1つだけにしておきましょう!!
※エクスペクテーション(予想、期待、切望、待望)
②期待する結果をはっきりと分かりやすく記述
明快な記述にすることで、自身の確認やメンバーとの共有、顧客への仕様説明が楽になります。
結果としてコミュニケーションミスが低下し、スムーズに仕事を進めることができます。もし分かりづらい記述だったら、一々相手に確認をしてテストするという無駄な時間が生まれますよね?
③起きて欲しいことと起きてほしくないこと両方をテストする
これはどちらもテストで予期せぬ動作が残るのを防ぐためにどっちも行います!!
そうすればエラー発生率の低下に繋がると思っています?
④境界値をテストする
例えば、パスワード登録の際に8文字以上を保存するとしましょう。
テストする際には、8文字以上保存されているかの確認と、逆に7文字以下のパスワードは保存されていないかテストをする必要があります。
よくバリデーションとかに使われるので、しっかりチェックする必要がありますね?可読性を考えつつ、適度にDRYする
DRYとはDon't Repeat Yourselfの略になります。
テストコードにおいて何よりも相手に伝わる分かりやすさが大切です。
分かりづらくなってテストの見落としが起きるよりマシだと思いませんか?最後に
本番環境前には必ずテストコードを書き、
ちゃんとメソッドが定義されているのか、
値がしっかり取得されているのかを確認してしっかりとしたアプリケーションを作っていきましょう!!!
- 投稿日:2019-05-30T07:49:21+09:00
Rails6 のちょい足しな新機能を試す27(perform_deliveries 編)
はじめに
Rails 6 に追加されそうな新機能を試す第27段。 今回は、
perform_deliveries編です。
perform_deliveriesの値が false(nil) のとき、log への出力メッセージがメールの送信をスキップしたことを示す内容に変わりました。今回は、User を登録するときに
perform_deliveriesの値を画面から指定できるようにする(メールを送信する、しないを指定できるようにする)ことにより、動作を確認します。Ruby 2.6.3, Rails 6.0.0.rc1 で確認しました。Rails 6.0.0.rc1 は
gem install rails --prereleaseでインストールできます。$ rails --version Rails 6.0.0.rc1Rails プロジェクトを作る
$ rails new rails6_0_0rc1 $ cd rails6_0_0rc1User の CRUD を作る
User の CRUD を作ります。
$ rails g scaffold User nameUserMailer を作る
メールを送信できるように、UserMailer を作ります。
$ rails g mailer UserMailerView を変更する
_form.html.erbにperform_deliveriesを true にするか false にするか指定するチェックボックスを追加します。app/views/users/_form.html.erb<%= form_with(model: user, local: true) do |form| %> ... <div class="field"> <%= label_tag :perform_deliveries %> <%= check_box_tag :perform_deliveries %> </div> ... <% end %>User controller を変更する
UsersController#createメソッドを変更し、DBへの保存が成功したら、 メールを送信するようにします。
このとき、perform_deliveriesの値をパラメータとしてUserMailerに渡すようにします。app/controllers/users_controller.rbclass UsersController < ApplicationController ... def create @user = User.new(user_params) respond_to do |format| if @user.save perform_deliveries = params[:perform_deliveries].to_i == 1 UserMailer.with(user: @user, perform_deliveries: perform_deliveries).created_email.deliver_later format.html { redirect_to @user, notice: 'User was successfully created.' } format.json { render :show, status: :created, location: @user } else format.html { render :new } format.json { render json: @user.errors, status: :unprocessable_entity } end end end ... end以下の2行が追加した部分になります。
perform_deliveries = params[:perform_deliveries].to_i == 1 UserMailer.with(user: @user, perform_deliveries: perform_deliveries).created_email.deliver_laterこのあとは、
UserMailer側を修正していきます。UserMailer クラスにメソッドを追加する
メールを送信する
created_emailメソッド とperform_deliveriesを設定するコールバックを追加します。app/mailers/user_mailer.rbclass UserMailer < ApplicationMailer after_action :set_perform_deliveries def created_email @user = params[:user] mail(to: 'admin@example.com', subject: 'user created') end private def set_perform_deliveries mail.perform_deliveries = params[:perform_deliveries] end endメールのテンプレートを作成する
今回は手抜きで テキストメールのみとし、 HTML メールは作成しません。
app/views/user_mailer/created_email.text.erbUser created ============= User <%= @user.name %> was createdメールをファイルに保存する
今回は実際にメールを送信せず、ファイルに保存するように設定を変更します。
config/environments/development.rbRails.application.configure do ... config.action_mailer.delivery_method = :file ... enddb:migrate を実行する
$ bin/rails db:create db:migraterails server を実行してブラウザからUserを登録する
User を登録するときに、
perform_deliveriesチェックボックスをチェックします。
ユーザーが無事、登録できれば、メールは、tmp/mails ディレクトリ内に保存されます。
このときログを確認するとlog/development.log... Delivered mail 5ce9f3619b8f4_2f32ae97743a3b0258ac@8baf8e28fbad.mail (6.1ms)とメールが送信されたことが記録されています。
今度は、
perform_deliveriesチェックボックスをチェックしないで User を登録します。
tmp/mails ディレクトリの admin@example.com ファイルには何も追加されません。
このときログを確認するとDeliveredとはならずにlog/development.log... Skipped delivery of mail 5ce9f380b1ad6_2f32ae97743a3b025957@8baf8e28fbad.mail as `perform_deliveries` is falseとメールの送信をスキップしたことがログに記録されます。 (←これが今回のちょい足し機能です。)
試したソース
試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try027_perform_deliveries参考情報
- 投稿日:2019-05-30T05:14:01+09:00
ReactやVueなどのJavascriptフレームワークからRailsの多対多モデルに対して、関連付けも含めて一気に作ってみた
こんにちは!@hairgaiです。
突然ですが、とあるTeamを作成するときにMemberを一緒に所属させたい!と思うことって一日に一回くらいありませんか?
今回は、それを実装するときにJavascriptからRailsへのパラメータで若干悩んだので、それを共有しようかなと思います。前提
よくある多対多です。
- Teamモデル
has_many :team_membershas_many :members, through: :team_members- TeamMemberモデル
belongs_to :teambelongs_to :member- Memberモデル
has_many :team_members: :destroyhas_many :teams, through: :team_membersReact 16.8.6、Rails 5.2.2
実装
Memberが数人いて何かTeamを作るときに、一度作成して後から所属させるのは面倒なので、当然ながら「Teamを作るときにMemberを所属させつつ作れたらいいのに」と思うと思います。
なので、フロントでTeamを作成するときに、既に登録済みのMemberを一覧で表示して、それを選択させることでパラメータを同時に送らせることにします。こんな感じですね。
また、Railsでは、モデルを作成するときに、その多対多関係にあるモデルを一緒に関連付けることが可能です。
なので、上記のように「Teamを作るときにMemberを所属させつつ作れたらいいのに」と思ったら、pry(main)> Member.create!(email: 'a@example.com') => #<Member:0x00007fb1498f04d8 id: 1, email: "a@example.com", # ... pry(main)> team = Team.create!(name: 'hoge', member_ids: [1]) => #<Team:0x00007fb142722860 id: 1, name: "hoge", # ... pry(main)> team.members => [#<Member:0x00007fb149951af8 id: 1, email: "a@example.com", # ... ]のように、
[モデル名]_idsというパラメータに配列を渡すと関連付けることが出来ます。
なので、フロント側でこのパラメータが渡ってくるようにURLパラメータを生成すれば良いということですね。パラメータ送りたい
ググると色々な記事が出てくる通り、JavascriptからURLにパラメータ用のクエリを乗っけるときは、qsを使いました。
qsは、Javascriptの連想配列(Hash)からURLパラメータを生成してくれるライブラリで、例えばimport qs from 'qs'; // or var qs = require('qs'); const params = { team: { name: 'hoge' }}; qs.stringify(params) // => 'team%5Bname%5D=hoge'という感じで使います。
なので(今回はパラメータの話なので解説しませんが)、フロント側でいい感じにパラメータを連想配列として生成します。// 実際はフレームワーク上でユーザの入力に合わせて作りますが、下記のような連想配列が生成されます。 const params = { team: { name: 'hoge', member_ids: [2, 3] } } qs.stringify(params) // => 'team%5Bname%5D=hoge&team%5Bmember_ids%5D%5B0%5D=2&team%5Bmember_ids%5D%5B1%5D=3'これをライブラリ等を用いてRailsに送ってみると…(axiosを使いました)
15: def update 16: binding.pry => 17: @team.update!(team_params) # ... [1] pry(#<Api::V1::TeamsController>)> params => <ActionController::Parameters {"team"=>{"id"=>"1", "name"=>"hoge", "member_ids"=>{"0"=>"1", "1"=>"2", "2"=>"3"}}, "format"=>"json", "controller"=>"api/v1/teams", "action"=>"update", "id"=>"1"} permitted: false>あれ?なんか変な形で送られてますね。
クエリの生成方法のオプションがあった
qsが生成したURLパラメータをデコードしてみると…team[name]=hoge&team[member_ids][0]=2&team[member_ids][1]=3ということで、
team[member_ids][0]=2&team[member_ids][1]=3のように指定されています。これをRailsでは"member_ids"=>{"0"=>"2", "1"=>"3"}と解釈してしまうようですね。(確かにHashっぽいです)
では、Railsではどう記述すればArrayと解釈されるのか、ということで、逆にArrayからクエリを生成してみましょう。pry(main)> { name: 'hoge', member_ids: [2, 3] }.to_query('team') => "team%5Bmember_ids%5D%5B%5D=2&team%5Bmember_ids%5D%5B%5D=3&team%5Bname%5D=hoge" # => team[member_ids][]=2&team[member_ids][]=3&team[name]=hoge順番は違いますが、若干指定の仕方が違いますね。Arrayのクエリでは、
[]のように数字を入れずに指定すれば良いようです。
現在はその数字の処理はqs側にまかせています。qsのGitHubをよく読んでみると…You may use the arrayFormat option to specify the format of the output array:
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }) // 'a[0]=b&a[1]=c' qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }) // 'a[]=b&a[]=c' qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }) // 'a=b&a=c' qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' }) // 'a=b,c'とのことです。
なので、フロント側でURLパラメータを生成する際に、const params = { team: { name: 'hoge', member_ids: [2, 3] } } qs.stringify(params, { arrayFormat: 'brackets' }) // => 'team%5Bname%5D=hoge&team%5Bmember_ids%5D%5B%5D=2&team%5Bmember_ids%5D%5B%5D=3'とオプションを追加してみると…
[1] pry(#<Api::V1::TeamsController>)> params => <ActionController::Parameters {"team"=>{"id"=>"1", "name"=>"hoge", "member_ids"=>["2", "3"]}, "format"=>"json", "controller"=>"api/v1/teams", "action"=>"update", "id"=>"1"} permitted: false>出来ました!レコードも正常に作られます。
結論
Railsは、Railsが提供するWayに沿えば非常に強力なフレームワークですが、その分色々な決まりがあるので、こういった細かいところもキチンと調べていきたいですね。
この記事が誰かの参考になれば幸いです。
参考
ありがとうございました!
- 投稿日:2019-05-30T01:05:48+09:00
~勉強会~ヘルスケアwebサービスを自分で作る医者の日記
やっぱ勉強会に参加は有意義
1人でやると煮詰まるし、上級者や他の人の視点で、視界や開ける。rails チュートリアル4章まで終了
頑張ろう
- 投稿日:2019-05-30T00:55:37+09:00
Rubyのインクリメント速度のバージョンごとの比較
TL;DR
Rubyでは
i++ができない。
たまたまi++しようとしたらエラーになったのでそういえばインクリメント演算子がRubyだと出来なかった気がするなと思い、念のために調べてみた。公式ドキュメントじゃないけど、割と自明な話しだと思うので公式ではなく個人のテックブログを参照した。詳細に調べられていたので問題ないかなと思う。
そして恐らくだが以前も同じブログを参照した気がする。選択肢としては
i += 1とi.succ、i.nextの3パターンあるがなにが違うのか気になったので調べたところ先人がすでに調べていた。ただこの記事も前述の記事も2014年〜2015年なので現在のRubyバージョンでは状況が変わっている可能性があるし、なんなら自分が問題を起こしたバージョン(ruby@2.4.1p111)とで結果が異なる可能性があるのではないか?と思い、せっかくなので調べてみた。
結論:
3つの中で
succが比較的安定、速い。
次点でi += 1、nextはFixnumで他に比べて遅い。Rubyでインクリメントしたいとき、普段は
i += 1かi.succを使う。
パフォーマンスが気になる箇所ではi.succを使うのがベター。次点でi += 1、i.nextを使ってたらレビューで指摘してあげたほうがいいかもしれない……くらいかな。2つ目のブログで書かれている状態とほぼ同じ結果になったのでおそらくは原因も同じと推測される。
各バージョンのコードまでは検証するつもりがなかったので省略してるけど多分同じなんだろうと思う。検証環境:
MacOS version 10.14.5(18F132) MacBook Pro (Retina, 15-inch, Late 2013) プロセッサ 2.3 GHz Intel Core i7 memory 16 GB 1600 MHz DDR3検証項目:
各バージョンの最新 + 問題を起こしたバージョンで検証。
本来は何度か実行してその平均値で出すんだろうけどメンドイので1回しかやってないので何かの検証目的でいる場合はサンプルデータ1くらいの扱いでお願いします。検証コードは2つ目のブログの検証コードをそのまま流用。
理由としては計測する方法が変わると期待する結果も変わってしまう可能性があるため、できるだけ条件を同じにしたかったから。実行したコード(ファイル名が違うだけ)
example.rubyrequire 'benchmark' Benchmark.bm do |x| cnt = 100000000 fixnum = 1 bignum = 1 << 64 x.report("+=1 Fixnum") do a = fixnum cnt.times{ a += 1 } end # Fixnum#succ x.report("Fixnum succ") do a = fixnum cnt.times{ a = a.succ } end # Fixnum#next x.report("Fixnum next") do a = fixnum cnt.times{ a = a.next } end x.report("+=1 Bignum") do a = bignum cnt.times{ a += 1 } end # Bignum#succ x.report("Bignum succ") do a = bignum cnt.times{ a = a.succ } end # Bignum#next x.report("Bignum_next") do a = bignum cnt.times{ a = a.next } end endruby@2.4.1p111
Fixnumのときの
nextだけが遅い。ブログの現象と同じ。$ ruby example.rb user system total real +=1 Fixnum 4.720000 0.000000 4.720000 ( 4.724997) Fixnum succ 4.720000 0.000000 4.720000 ( 4.727951) Fixnum next 5.870000 0.010000 5.880000 ( 5.879860) +=1 Bignum 10.570000 0.000000 10.570000 ( 10.582265) Bignum succ 10.340000 0.010000 10.350000 ( 10.354154) Bignum_next 10.110000 0.010000 10.120000 ( 10.121698)ruby@2.6.3
Fixnumで
nextだけが遅いという現象が発生してたのでこれはなにか別のアプリケーションが影響してるのか?と思ってもう一回実行したんだけど結果ほぼ変わらずだったのでどうやらnextがFixnumのときだけ遅いっぽい。$ ruby example.rb user system total real +=1 Fixnum 5.052053 0.007104 5.059157 ( 5.068701) Fixnum succ 4.829195 0.004266 4.833461 ( 4.840140) Fixnum next 6.159282 0.002336 6.161618 ( 6.165148) +=1 Bignum 10.592028 0.004909 10.596937 ( 10.601052) Bignum succ 10.377451 0.003914 10.381365 ( 10.384787) Bignum_next 10.320459 0.003760 10.324219 ( 10.327414)ruby@2.5.5
こちらもブログの現象と同じといえる。
$ ruby example.rb user system total real +=1 Fixnum 4.719072 0.001677 4.720749 ( 4.722440) Fixnum succ 4.651602 0.001740 4.653342 ( 4.654881) Fixnum next 5.748883 0.001787 5.750670 ( 5.752244) +=1 Bignum 10.454205 0.007352 10.461557 ( 10.471455) Bignum succ 10.872434 0.028426 10.900860 ( 10.935718) Bignum_next 10.624259 0.024823 10.649082 ( 10.674851)ruby@2.4.6
ブログの(ry
ruby example.rb user system total real +=1 Fixnum 4.430000 0.000000 4.430000 ( 4.437439) Fixnum succ 4.570000 0.020000 4.590000 ( 4.615233) Fixnum next 5.950000 0.020000 5.970000 ( 6.008219) +=1 Bignum 10.600000 0.030000 10.630000 ( 10.660327) Bignum succ 10.050000 0.020000 10.070000 ( 10.093179) Bignum_next 10.370000 0.020000 10.390000 ( 10.410649)ruby@2.3.7
たまたまなのかもしれないけど一番古いRubyのバージョンが一番遅いだろうとなんとなく考えていたが結果が真逆だったのが面白い。
昔のほうが特定の条件下では高速に動作するのか。
とはいえそのためにあえて2.3.7使うひとはいないと思うけど、どうしても速度がほしいなら別の言語選ぶだろうしな。ruby example.rb [05 30 00:37:43] user system total real +=1 Fixnum 4.170000 0.000000 4.170000 ( 4.173693) Fixnum succ 4.150000 0.000000 4.150000 ( 4.154439) Fixnum next 5.280000 0.000000 5.280000 ( 5.282451) +=1 Bignum 9.170000 0.000000 9.170000 ( 9.176064) Bignum succ 9.230000 0.010000 9.240000 ( 9.233617) Bignum_next 9.420000 0.000000 9.420000 ( 9.428198)
- 投稿日:2019-05-30T00:18:27+09:00
Rubyのdefine_methodについて
はじめに
以下のようなコードを見て、difine_methodの使われ方が良く分からなかったため、調べてみました。
特に、ブロックパラメーターがたくさん使われているため、define_methodの後にあるブロックパラメーター i が何を意味しているのか理解できませんでした。【訂正】当初の記事ではブロック内の| |で囲まれた部分(下記のコード例ではwordやnum、iのこと)を「ブロック引数」と表現していましたが、正しくは「ブロックパラメーター」でした。本文内の記述を訂正させて頂きました。
コメントを頂いた@scivolaさん、ありがとうございました!合わせて、初学者の方の参考になればと思い、コード内で使われている記法やメソッドについて初心者目線で解説してみました。コードを読んでも何をしているか分からない方の参考になればと思います。
sample.rbNUMBERS = %w(zero one two three four five six seven eight nine) NUMBERS.each_with_index do |word, num| define_method word do |i = nil| i ? num * i : num end end p two #=> 2 p two(2) #=> 4 p nine #=> 9 p nine(3) #=> 27%wについて
リファレンス
https://docs.ruby-lang.org/ja/2.5.0/doc/spec=2fliteral.html#percentRubyには%記法というものがあり、文字列、配列やハッシュ等々を%記号を使って記載することができます。
# 通常の配列の書き方 p ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] #=> ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] # %記法を使った書き方 p %w(zero one two three four five six seven eight nine) #=> ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]どちらも同じ結果を得られますが、%記法を使用した方が簡潔に記載することが出来ます。
上記のコードでは%wを使用して、zeroからnineまでの配列を作成し定数NUMBERSに代入しています。Enumerable#each_with_indexについて
リファレンス
https://docs.ruby-lang.org/ja/2.5.0/method/Enumerable/i/each_with_index.htmleach_with_index -> Enumerator
each_with_index {|item, index| ... } -> selfeach_with_indexメソッドは通常のeachメソッドのように繰り返しを行いながら、各要素にインデックス番号を付番することが出来ます。ブロックを渡す場合はブロックパラメーターの第一引数(item)に各要素が順番に入り、第二引数(index)にインデックス番号が0番から自動で代入されていきます。
NUMBERS = %w(zero one two three four five six seven eight nine) NUMBERS.each_with_index do |word, num| # 省略 end # word num # "zero" 0 # "one" 1 # 省略 # "nine" 9上記のコードでは%w記法を使って定義した配列NUMBERSに対してeach_with_indexを使用しているため、ブロックパラメーターの第一引数(word)に配列NUMBERSの各要素("zero"から"nine"まで)が順番に代入され、それぞれの要素に対して第二引数(num)のインデックス番号が0から順番に代入されています。
従って、"zero"には0のインデックスが、"one"には1のインデックスが付番され、最後の"nine"まで繰り返し行われます。Module#define_methodについて
リファレンス
https://docs.ruby-lang.org/ja/2.5.0/method/Module/i/define_method.htmldefine_method(name, method) -> Symbol
define_method(name) { ... } -> Symboldefine_methodは第一引数(name)に指定した名称をメソッド名とし、第二引数(method)にそのメソッドの処理内容を指定します。
また、第二引数にブロックを渡した場合は、ブロック内の処理が第一引数(name)で指定したメソッドの処理内容になります。このブロック内の処理については、第一引数で作成したメソッドを呼び出した際に初めて実行されます。NUMBERS = %w(zero one two three four five six seven eight nine) NUMBERS.each_with_index do |word, num| define_method word do |i = nil| # 省略 end end上記のコードの場合は、define_methodの第一引数としてwordを指定しています。このwordが何かと言うと、前述したeach_with_indexの第一引数に指定されていたwordです。each_with_indexのwordには配列NUMBERSの各要素が順番に代入されていました。従って、define_methodの第一引数にwordが指定されていると言うことは、配列NUMBERSの各要素の名称をメソッド名として定義している事になります。つまり、"zero"と言う名称のメソッドから"nine"と言う名称のメソッドがここで順番に定義されています。
各メソッドの処理内容についてはブロックを渡しています。前述したように、ブロック内の処理は定義したメソッドが呼び出されるまで実行はされません。(例えばtwoメソッドを呼び出した時に初めてブロック内の処理が実行されます)。またブロックパラメーターとしてiを指定しており、そのデフォルト値をnilにしています。
最後にブロック内の処理内容を確認します。条件演算子(三項演算子)について
リファレンス
https://docs.ruby-lang.org/ja/2.5.0/doc/spec=2foperator.html#cond
Rubyには条件演算子と言うものがあります(他の言語にもあります)文法:
式1 ? 式2 : 式3
式1の結果によって式2または式3を返します。
if 式1 then 式2 else 式3 end
とまったく同じです。リファレンスに書いてある通りなのですが、if文をこのように書くことが出来ます。
?と:で式が分けられています。式1は条件式に該当するので、真(true)か偽(false)を返す式を記載します。
式1が真なら式2が実行されます。式1が偽なら式3が実行されます。NUMBERS = %w(zero one two three four five six seven eight nine) NUMBERS.each_with_index do |word, num| define_method word do |i = nil| i ? num * i : num end end上記のコードではdefine_methodのブロック内で条件演算子が使用されています。
i と (num * i) と num が?と:で区切られているのが分かると思います。
ここで、Rubyでは「nilとfalse以外は全て真とされる」という決まりがあります。
参考
https://docs.ruby-lang.org/ja/2.5.0/class/FalseClass.htmlfalse は nil オブジェクトとともに偽を表し、 その他の全てのオブジェクトは真です。
上記の条件演算子をもう一度見てみると、式1にはブロックパラメーターのiしか記載されていませんが、iにfalseかnil以外の値が代入されていれば(真ならば)式2(num * 1)が実行され、iにfalseかnilが代入されていれば(偽ならば)式3(num)が実行されます。ここで、iにはデフォルト値としてnilが設定されているため(ブロックパラメーター内で i = nilとなっている部分)今回の条件演算子は、
(式1が真)iにfalse以外の値が入れば
(式2を実行)num(each_with_indexで付番したwordに対応するインデックス番号)にiを掛けた数値を返す。
(式1が偽)iに何も指定がなければ(デフォルト値のままなら)
(式3を実行)numをそのまま返す事になります。まとめ
sample.rbNUMBERS = %w(zero one two three four five six seven eight nine) NUMBERS.each_with_index do |word, num| define_method word do |i = nil| i ? num * i : num end end p two #=> 2 p two(2) #=> 4 p nine #=> 9 p nine(3) #=> 27最初のコードをもう一度見てみます。特に最後の4行。
twoを呼び出すと2が返ってきます。これは、define_method wordの部分で定義したtwoメソッドを引数なしで呼び出しています。引数の指定が無いため、ブロックパラメーターiにはデフォルト値のnilが代入されます。するとブロック内の条件演算子ではiは偽と判断されるので、twoメソッドに対応するインデックスの2がそのまま返ってきています。
two(2)を呼び出すと4が返ってきます。今回は引数2を指定してtwoメソッドを呼び出しています。この引数2はdefine_methodの部分でwordのブロックパラメーターであるiに代入されます。twoというメソッドはwordの部分で定義されており、その処理内容は続くブロックの中に記載されます。考え方は通常のメソッドの引数と同じです。
# (注)ここではdefine_methodで作成されるtwoメソッドを、普通に(?)定義した場合を示しています。 # 便宜上num=2としています。ブロックによる記述も、通常のメソッドの定義方法とあまり変らないのか、 # と感じて頂ければと思います。 def two(i = nil) num = 2 i ? num * i : num endnineについても全く同じ考え方になります。
ちなみにsample.rbに関しては、twoメソッドやnineメソッドの引数に数値以外を渡すとエラーになります(例えばtwo("2")とか)。理由については試して頂ければ分かると思います。
【訂正】
当初の記事では上記のコードを下記のように記載していましたが、下記のコードではエラーが発生します。ローカル変数やスコープに関する認識が不足していました。
コメントを頂いた@scivolaさんには重ねてお礼申し上げます!# (注)誤ったコードです。 num = 2 def two(i = nil) i ? num * i : num end終わりに
だんだん何について書いているのか分からなくなり、また、説明が冗長で逆にわかりづらかったら申し訳ありま温泉。
個人的にはブロックパラメーター i にはdefine_methodで作成したメソッドを呼び出す際の引数が代入される。という部分が、なるほどポイントでした。
自分と同じような初学者の方に、一部分でも参考になる点があれば幸いです。
また、誤り・改良すべき点等あればご指摘いただければありがたいがーです。




