- 投稿日:2019-05-03T23:20:55+09:00
Day002 webエンジニアへの道 - Ruby on Railsの基礎を学ぶ -
こんにちは。
webエンジニアを目指すtomoです。エンジニアの勉強をしている日々の学びをQiitaに記録しています。
私自身の頭の整理と今後迷った時のリファレンスも兼ねて書き連ねていきますが、同じようにエンジニア転職を考えている方の参考にもなればと考えています。
また、「ここ間違ってる!こっちが正しい!」といったご指摘もあれば頂けると嬉しいです?
前回は仮想マシンの環境設定を行いました。
しかし、実際にRuby on railsを使い始めたところ必要な関連ソフトが足りずエラーが連発しました。
必要な関連ソフトは何かなどちゃんと把握した上で進めたいのですが、それでは時間がいくらあっても足りなさそうなので、関連ソフトの理解は後にしてまずはdot-installのレッスン通りCentOS6を用いた環境で学習を進めることにします。
また、ひとつひとつのコードを全て書き写しているとキリがないので、学習を進める中で調べたことやエラー対応したことに絞って記述していきます。
railsサーバの起動
commandlinerails server -b [ipアドレス] -d-b オプションでバインドするipアドレスを指定。
-d オプションでデーモンとしてサーバを起動。
デーモンとはメモリ上に常駐し、いつでも処理できるよう待機するプログラムのこと。[参考]
railsコマンド(rails)
デーモン (daemon)rails(5.1.4)以降ではリンク先が表示されないため、ブラウザに直接
http://[ipアドレス]:3000
と入力して動作確認する。scaffoldで作成したファイルを削除する
commandlinerails destroy scaffold [name][name]は削除したいscaffoldの名前。
rails g scaffold [name]
で要素を間違えて指定した時に有用Modelの作成
rails g model Post title:string body:textPost → Modelは個々のデータ構造を定義するので単数形で指定する。
string → 1行テキスト
text → 複数行のテキスト追加した変更が反映されない時の対応
Transmit5を利用して仮想マシンとのファイル転送を行っていたが、エディタで保存したはずの変更がブラウザに反映されなかった。
↓
原因:Transmitとの接続が途中で切れていたため。(Transmit上で「サーバから切断」を行うと、再接続しても既に開いているエディタのファイルとのリンクが切れてしまう)tips:ブラウザまたはコンソール上で見える簡単な変更(htmlの文章やplaceholderなど)を加えてみると、どこに不具合があるか特定しやすい。目で見て確認しづらい処理の不具合をチェックする場合は処理の内容を簡素化(文字列の出力など)すると特定しやすい。
★メモ
*後日調べること & 気づいたこと
- kill -9 [プロセス番号] → killコマンドで-9以外の場合どうなる?
- Convention over Configuration → どのファイルがどこのディレクトリにあるか、指定は単数形か複数形かといった細かいルールがrailsには設定されている。ということらしいので調べる。
- Active Recordとは何か?
- MVCのフローを理解するため整理して図に書き出す。
- 開発環境構築が始めの1歩だと思っていたが、「どのパッケージをどのバージョンで入れるか」というのは難易度が高いので詳しくは後で学んだ方が良い...
さいごに
2日目にして完全に自分用の学習メモと化してきました...
続けていった後で、「うわ、このとき自分はこんな簡単なことで悩んでたのかよ...」ってなれるよう精進します。
twitterもやっているので、宜しければフォローお願いします!
@tomo_tech_
- 投稿日:2019-05-03T21:20:44+09:00
37日目。Rubyでクラスの継承など。
クラスの継承
RubyⅢ(8分)
RubyⅣ(18分)やっばりちょっと難しいですね・・・。
2周目だけどてこずりました。クラスの継承
class Food < Menu end継承されるもの
親クラスのインスタンスとメソッド
子クラスにインスタンスを追加
クラス継承のところで
attr_accesso
にて追加できる。class Food < Menu attr_accessor :calorie end子クラスにメソッドを追加
同様に、クラス継承のところで
def
にて追加できる。class Food < Menu attr_accessor :calorie def calorie_info return "#{self.name}は#{self.calorie}kcalです" end endメソッドのオーバーライド(上書き)
子クラスと親クラスに同じ名前のメソッドがある場合、子クラスが優先される。
親クラスの同名のメソッドを呼び出す(super)
super(name: name, price: price)
日付を扱う(Dateクラス)
Rubyにて事前定義済みなので呼ぶだけでいい
# requireを用いて、Dateクラスを読み込んでください require "date" # 変数birthdayに、Dateクラスのインスタンスを代入してください birthday = Date.new(2019,5,1) # 変数birthdayをputsしてください puts birthday # 変数birthdayにsunday?メソッドを用いた結果をputsしてください puts birthday.sunday? # 変数todayに、Date.todayの戻り値を代入してください today =Date.today # 変数todayをputsしてください puts today # 変数todayに対してsunday?メソッドを用いた結果をputsしてください puts today.sunday?クラスメソッド
Date.today
のようちクラス名に対して呼び出すメソッドのことclass A def A.B? end endインスタンスメソッドの中でクラスメソッドを呼び出す
class A def B if A.C end end end A.C完成!
さらに頭がこんがらがっています。
インスタンスメソッド、クラスメソッド。
よく似ていますよね。。。実際どうやって使い分けるんでしょう。
アルゴリズム体操ですかね。。。(所要時間 52分)
(2周目 21分)
- 投稿日:2019-05-03T21:17:26+09:00
【Ruby】配列内の配列のkeyをで重複をなくす方法
やりたかったこと
配列内に配列があり、その配列内の1要素をkeyとして重複を消したい。
環境
ruby 2.5.1p57 (2018-03-29 revision 63029)
sandbox.rb
sandbox.rbdef test() array = [["id01",111,"1"],["id01",222,"2"],["id02",111,"1"],["id02",222,"2"],["id01",333,"3"]] p array.group_by{ |e| e.first }.select { |k, v| v}.map{|m| m.last.last} end test()
内容
Ruby: 配列で重複してるものを探す - 宇宙船サンドボックスを参考に記述を変更した。
業務上1億件近くのデータを扱うため速度は重要だった。
配列で重複した場合は後勝ちとした。
結果
これが[["id01", 111, "1"], ["id01", 222, "2"], ["id03", 111, "1"], ["id02", 111, "1"], ["id02", 222, "2"], ["id01", 333, "3"]]これに[["id01", 333, "3"], ["id02", 222, "2"], ["id03", 111, "1"]]これで早くなった。
もっといい方法があれば教えてほしい。
コメントいただいてありいがとうございます!※備忘録に近いが記載する。
- 投稿日:2019-05-03T20:29:25+09:00
docker で rspec の system spec を実行するための設定メモ
はじめに
docker 環境で rspec の system spec を実行させるための設定に手間取ったので、メモ代わりに書いておきます。
railsが動くdocker の image に chrome をインストールするのもちょっとなあと思ったので、 selenium/standalone-chrome-debug
を使う方法です。確認した Rails 環境は、5.2.2 です。
ちなみに Gemfile でバージョン指定はしていませんが、Gemfile.lock を確認したところ
capybara (3.14.0) rspec-rails (3.8.2) selenium-webdriver (3.141.0)となってました。
docker-compose.yml の編集
chrome が動作するように設定を追加します。
Rails はweb
で動作します。docker-compose.ymlversion: '3' services: web: build: . ports: - "3000:3000" volumes: - .:/app - bundle:/usr/local/bundle tty: true environment: # この環境変数を追加 - "SELENIUM_DRIVER_URL=http://selenium_chrome:4444/wd/hub" # 以下の4行を追加 selenium_chrome: image: selenium/standalone-chrome-debug logging: driver: none db: image: postgres:10.7-alpine volumes: - pgsqldb:/var/lib/postgresql/data environment: - "POSTGRES_USER=xxxx" - "POSTGRES_PASSWORD=xxxx" volumes: pgsqldb: bundle:chrome を動作させるために、selenium_chrome を追加しています。
logging
の設定をdriver: none
にしているのは、不要なログ出力を抑制するためです。rspec は web で実行しますが、 chrome は、selenium_chrome で動作させるため、web 側からアクセスできるように
環境変数SELENIUM_DRIVER_URL
を web 側に追加しています。Gemfile を編集する
gem ファイルに rspec-rails を追加します。
chromedriver-helper を削除します。webdrivers gem は追加しません。Gemfilegroup :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '>= 2.15' gem 'selenium-webdriver' gem 'rspec-rails' enddocker 環境で、bundle install を実行する
docker 環境で追加した gem をインストールします。
$ docker-compose up -d $ docker-compose exec web bash # 以下は web コンテナ内で実行します。 $ bundle installrspec の初期設定をする。
続けて、rspec の初期設定をします。
# 以下は web コンテナ内で実行します。 $ bin/rails g rspec:installsystem spec の設定をする。
system spec の設定をするために、
spec/rails_helper.rb
を編集し、spec/support/capybara.rb
を追加します。spec/rails_helper.rb# 以下の1行を有効にします。 Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines.spec/support/capybara.rbrequire 'capybara/rspec' RSpec.configure do |config| config.before(:each, type: :system) do |config| driven_by :selenium, using: :headless_chrome, options: { browser: :remote, url: ENV.fetch("SELENIUM_DRIVER_URL"), desired_capabilities: :chrome } Capybara.server_host = 'web' Capybara.app_host='http://web' end end
spec/support/capybara.rb
内でselenium_chrome
側の headless chrome を利用するための設定をしています。
web コンテナ側から selenium_chrome コンテナ の chrome を使用するために、options
の中で、url
にSELENIUM_DRIVER_URL
環境変数の値を設定しています。
headless chrome からは、ローカル環境ではなく、web
コンテナ側の rails アプリを表示してテストする必要があるため、
Capybara.server_host
とCapybara.app_host
を設定しています。参考情報
- 投稿日:2019-05-03T20:29:25+09:00
Docker で RSpec の System Spec を実行するための設定メモ
はじめに
docker 環境で rspec の system spec を実行させるための設定に手間取ったので、メモ代わりに書いておきます。
railsが動くdocker の image に chrome をインストールするのもちょっとなあと思ったので、 selenium/standalone-chrome-debug
を使う方法です。確認した Rails 環境は、5.2.2 です。
ちなみに Gemfile でバージョン指定はしていませんが、Gemfile.lock を確認したところ
capybara (3.14.0) rspec-rails (3.8.2) selenium-webdriver (3.141.0)となってました。
docker-compose.yml の編集
chrome が動作するように設定を追加します。
Rails はweb
で動作します。docker-compose.ymlversion: '3' services: web: build: . ports: - "3000:3000" volumes: - .:/app - bundle:/usr/local/bundle tty: true environment: # この環境変数を追加 - "SELENIUM_DRIVER_URL=http://selenium_chrome:4444/wd/hub" # 以下の4行を追加 selenium_chrome: image: selenium/standalone-chrome-debug logging: driver: none db: image: postgres:10.7-alpine volumes: - pgsqldb:/var/lib/postgresql/data environment: - "POSTGRES_USER=xxxx" - "POSTGRES_PASSWORD=xxxx" volumes: pgsqldb: bundle:chrome を動作させるために、selenium_chrome を追加しています。
logging
の設定をdriver: none
にしているのは、不要なログ出力を抑制するためです。rspec は web で実行しますが、 chrome は、selenium_chrome で動作させるため、web 側からアクセスできるように
環境変数SELENIUM_DRIVER_URL
を web 側に追加しています。Gemfile を編集する
gem ファイルに rspec-rails を追加します。
chromedriver-helper を削除します。webdrivers gem は追加しません。Gemfilegroup :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '>= 2.15' gem 'selenium-webdriver' gem 'rspec-rails' enddocker 環境で、bundle install を実行する
docker 環境で追加した gem をインストールします。
$ docker-compose up -d $ docker-compose exec web bash # 以下は web コンテナ内で実行します。 $ bundle installrspec の初期設定をする。
続けて、rspec の初期設定をします。
# 以下は web コンテナ内で実行します。 $ bin/rails g rspec:installsystem spec の設定をする。
system spec の設定をするために、
spec/rails_helper.rb
を編集し、spec/support/capybara.rb
を追加します。spec/rails_helper.rb# 以下の1行を有効にします。 Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines.spec/support/capybara.rbrequire 'capybara/rspec' RSpec.configure do |config| config.before(:each, type: :system) do |config| driven_by :selenium, using: :headless_chrome, options: { browser: :remote, url: ENV.fetch("SELENIUM_DRIVER_URL"), desired_capabilities: :chrome } Capybara.server_host = 'web' Capybara.app_host='http://web' end end
spec/support/capybara.rb
内でselenium_chrome
側の headless chrome を利用するための設定をしています。
web コンテナ側から selenium_chrome コンテナ の chrome を使用するために、options
の中で、url
にSELENIUM_DRIVER_URL
環境変数の値を設定しています。
headless chrome からは、ローカル環境ではなく、web
コンテナ側の rails アプリを表示してテストする必要があるため、
Capybara.server_host
とCapybara.app_host
を設定しています。参考情報
- 投稿日:2019-05-03T18:54:29+09:00
36日目。Rubyのクラスとメソッドの使い方。
今日は一週間ぶりにProgateを開きました。
まずは前回までの復習です。RubyⅠ(10分)
RubyⅡ(14分)
RubyⅢ(9分)若干忘れてましたが、なんとか思い出しました!
ではRubyⅣクラスとメソッドです。クラスを作る
Class Menu endインスタンス変数を作る
Class Menu attr_accessor :name attr_accessor :price endattr_accessorって、なんて読むんだろう? アトリアクセソーかな?
なんでattr_accessor
で追加できるんだろう。謎。インスタンスの生成
menu1=Menu.newインスタンス変数の使い方
menu1.price = 800複数のインスタンス
menu1=Menu.new menu2=Menu.new menu3=Menu.new こんな感じでどんどんふやせる。クラスの中でメソッドを定義
できる。ある程度まとめた方が便利なんだろうな。
インスタンスメソッドという。ややこしい。インスタンスメソッドの中でreturnする
putsもreturnも使える。
メソッドの中のインスタンス変数
自分自身に設定した値を見るには
self
を使うself.変数ややっこしいんだけど、インスタンス変数を呼ぶ、=
self.変数
「 Aに対してBメソッドを呼び出して戻り値を出力 」
これはputs A.B
何回も出てくるよ。ややっこしい!initializeメソッド
インスタンスを生成したとき実行されるメソッド
ファイルを分割する
①区切りのいいところでファイルを分ける
②require <なにがし>で呼び出すコンソールから入力を受け取る
①gets.chompを実行、入力待機状態になる
②変数 = gets.chomp で入力された内容を変数で受け取れる
※数値の場合は'gets.chomp.to_i'とする
これも読み方がわからない。ゲッツチョップ、ゲッツチョップトォーアイ???完成!
ファイルを分割してメソッドでインスタンスで頭がこんがらがってますがなんとかゴールできました!
(所要時間 1時間)追記) Ruby語の読み方
コメントいただきました!
> attr_accessorって、なんて読むんだろう? アトリアクセソーかな? 正式な呼び方もしくは広く通用している読み方があるのかどうか私は知りませんが,attr は attribute(属性)から来ているのでしょう。 「アトリビュート・アクセッサー」とかでいいんじゃないですかね。> gets.chomp、gets.chomp.to_i。これも読み方がわからない。 ゲッツチョップ、ゲッツチョップトォーアイ??? gets は「ゲット・ストリング」だと長いので,私は「ゲット・エス」かなあ。 同じく puts は「プット・エス」。 (中略) いろんな方のご意見が聞いてみたい。
- 投稿日:2019-05-03T17:18:19+09:00
Railsのclass_methodsがやっていること
module ModuleA extend ActiveSupport::Concern class_methods do def method_a puts "method_a" end end end class ClassA include ModuleA end ClassA.method_a #=> "method_a"Moduleのメソッドをクラスメソッドとして使用できるようにするために、class_methodsを使う。
class_methodsで囲んだ箇所をActiveSupportのConcernがextendしてくれる。class_methodsがないと‥
module ModuleA def method_a puts "method_a" end end class ClassA include ModuleA end ClassA.method_a #=> "NoMethodError: undefined method `method_a' for ClassA:Class" instance_a = ClassA.new instance_a.method_a #=> "method_a"ModuleAのメソッドはClassAのインスタンスメソッドになる。
ちなみに
module ModuleA extend ActiveSupport::Concern class_methods do def self.method_a puts "self_method_a" end end end class ClassA include ModuleA end当たり前だが、ModuleAのself.method_aはあくまでもModuleAの特異メソッドだからClassAからもインスタンスからも呼び出すことはできない。
- 投稿日:2019-05-03T16:32:28+09:00
Hanamiのcircle ci設定例
ほぼほぼcirciel ci公式のrubyの例のコピペですが覚書としておいておきます。
version: 2 # use CircleCI 2.0 jobs: # a collection of steps build: # runs not using Workflows must have a `build` job as entry point parallelism: 1 docker: # run the steps with Docker - image: circleci/ruby:2.6.1 # ...with this image as the primary container; this is where all `steps` will run environment: # environment variables for primary container BUNDLE_JOBS: 3 BUNDLE_RETRY: 3 BUNDLE_PATH: vendor/bundle PGHOST: 127.0.0.1 PGUSER: postgres HANAMI_ENV: test - image: circleci/postgres:9.5-alpine # database image environment: # environment variables for database POSTGRES_USER: postgres POSTGRES_DB: bookshelf_test POSTGRES_PASSWORD: "postgres" steps: # a collection of executable commands - checkout # special step to check out source code to working directory # Which version of bundler? - run: name: Which bundler? command: bundle -v # Restore bundle cache # Read about caching dependencies: https://circleci.com/docs/2.0/caching/ - restore_cache: keys: - bookshelf-v2-{{ checksum "Gemfile.lock" }} - bookshelf-v2- - run: # Install Ruby dependencies name: Bundle Install command: bundle check || bundle install - run: sudo apt update - run: sudo apt install -y postgresql-client || true # Store bundle cache for Ruby dependencies - save_cache: key: bookshelf-v2-{{ checksum "Gemfile.lock" }} paths: - vendor/bundle - run: name: Wait for DB command: dockerize -wait tcp://localhost:5432 -timeout 1m - run: name: Database setup command: bundle exec hanami db prepare - run: name: Run rspec command: bundle exec rspec # Save test results for timing analysis - store_test_results: # Upload test results for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/ path: test_results # See https://circleci.com/docs/2.0/deployment-integrations/ for example deploy configs参考 : https://circleci.com/docs/2.0/language-ruby/#sample-configuration
- 投稿日:2019-05-03T15:34:32+09:00
windowsでrails + elasticsearch環境を構築する
はじめに
最近、個人的に作っているアプリで全文検索を使ってみたくなり、Elasticsearchの環境を構築しました。
しかし、家のPCのOSはWindowsで、Qiitaや開発ブログなどでは大体Mac(Linux)で書かれていて、Windowsを使っている身としては肩身が狭いです。。。そして、Elasticsearchを取り入れる時にも、エラーが結構出ました。
Windows + Railsでelasticsearchを取り入れた時に躓いた部分などを備忘録的に投稿します。elasticsearch初心者なので、初歩的なエラーが多いです。
アプリにelasticsearchをセットアップ
なお、セットアップにはこちらを参考にさせて頂きました。
gem 'elasticsearch-model', git: 'git://github.com/elasticsearch/elasticsearch-rails.git' gem 'elasticsearch-rails', git: 'git://github.com/elasticsearch/elasticsearch-rails.git'class PoolHall < ActiveRecord::Base include Elasticsearch::Model end以上をGemfileに記載して、bundle installを実行。Elasticsearch::Modelをincludeして、ためしにPoolHall.search 'test'をしてみました。
検索は出来ているように思えましたが、検索結果を出力してみようと思い、resultsのeachやmapを呼び出すと以下のようなエラーが出ました。
Faraday::ConnectionFailed in PoolHallsController#new Failed to open TCP connection to localhost:9200 (Connection refused - connect(2) for "localhost" port 9200) Extracted source (around line #6):調べたところ、elasticsearchをlocalhost:9200で起動しないと駄目だと知りました。
elasticsearchをインストール
elasticsearchはJavaを実行できる環境が必要なので、こちらからJREをダウンロード。
Javaはインストールしていたので、実行できるかと思いきや、elasticsearchを実行するとエラーが出ました。こちらは、Javaのバージョンが古かったので、アップデートしたら実行できました。 Java SE Runtime Environment 8u211のWindows x86 Offlineをダウンロードし、インストールしました。
こちらのDownloadsのWindowsから、elasticsearchをダウンロード。バージョンは7.0.1です。
elasticsearchをダウンロードし、解凍したら、elasticsearch-7.0.1-windows-x86_64.zip\elasticsearch-7.0.1\bin\elasticsearch.batを実行すると、elasticsearchが起動できます。
インデックスを作成
以下のコマンドを実行。
rails celasticsearchを起動し、インデックスを作成し、登録。
PoolHall.__elasticsearch__.create_index! force: true PoolHall.importいちいち、コンソールを起動してやるのも面倒なので、こちらを参考にタスクを作成。
# rails g task elasticsearchで作成 namespace :elasticsearch do desc 'Elasticsearch のindex作成' task create_index: :environment do PoolHall.__elasticsearch__.create_index! force: true end desc 'Article を Elasticsearch に登録' task import_hall: :environment do PoolHall.import end endこれで以下のコマンドでインデックスを作成できる。
rake elasticsearch:create_index rake elasticsearch:import_hall検索結果をactiverecordとして扱いたい
これで、検索の準備が整い、検索をしてみました。
@hall = PoolHall.search(params[:word)viewで、検索結果を表示しようとしたところ、インスタンスメソッドを呼び出すところでメソッドが定義されていないとエラーが発生しました。
thumbnailというメソッドは、PoolHallモデルのインスタンスメソッドで、サムネイルを返すメソッドでした。
<%= @hall.each do |h| %> <%= image_tag h.thumbnail %> <% end %>調べてみると、検索結果はElasticSearchのオブジェクトとして返ってきていたので、当然の結果でした。activerecordとして扱いたい場合は以下の通り。
@hall = PoolHall.search(params[:word).recordsおわりに
elasticsearchは非常に便利ですね。
今後はkibanaなどにも手を出していきたいです。
- 投稿日:2019-05-03T14:58:42+09:00
RubyとSlackBotを使って、誰でもIssueを作成できるBotを作ってみた。
はじめに
先日、このTweetを見かけてしまいました。
Slackで絵文字つけるだけでGitHubにissue化できるようにするとめっちゃ便利…
— uiu (@uiu______) 2019年4月18日
スマホからでもいつでもすぐissue作れて、あとから文脈をSlackのリンクから辿れるので内容を思い出せる pic.twitter.com/LWsy4pXkZg「これは...圧倒的に便利すぎる!」 と衝撃を受け、有り余る好奇心で開発を試みたのが始まりです。(しかもGWで割と時間もあったので)
今回はRubyとSlack Bot、Github APIを使用して、Slackのリアクションでissueを作成してみました。ちなみにSlack Botほぼ初心者でも2日くらいでだいたい動くものは完成したので、サクッと作れます。
完成イメージ
この記事を読んでできること
- RubyとSlack Botを使って、SlackのリアクションでGithubのissueが作成できる。
- Slack上の特定のメッセージ、リアクションに対して、決まった返答を返すことができる。
準備
言語:Ruby
API:Github API
Bot:SlackSlack Botを使ったことのない方推奨の記事:
http://studio-andy.hatenablog.com/entry/ruby-bot
一度手を動かしてみると挙動が把握で切るのでオススメです!SlackBotを作成しよう
https://amdlaboratory.com/amdblog/slack-bot%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9%E3%80%82/
こちらのSlack Botの作り方を参考にBotを作成してください。APIの挙動確認、パラメータ、レスポンスなどはこちらからデモ操作できます。
https://api.slack.com/methods/chat.postMessage/testGithub APIを取得
こちらも公式APIドキュメントから細かいパラメータの設定や、レスポンスなどが確認できます。
https://developer.github.com今回はissueを作成したいので、
https://developer.github.com/v3/issues/#create-an-issueRubyファイルを作成
作成するファイル構成
slack_bot
┣ slack_bot.rb
┣ Gemfile
┗ Gemfile.lockGemfile# frozen_string_literal: true source "https://rubygems.org" ruby "2.6.3" gem 'http' gem 'json' # websocketを使用する gem 'faye-websocket' gem 'eventmachine'
bundle install
しておく。slack_bot.rbrequire 'http' require 'json' require 'eventmachine' require 'faye/websocket' require 'uri' require 'net/http' require 'net/https' # 環境変数を設定する # export SLACK_API_TOKEN=xoxo-hogehoge-api # export GITHUB_USERNAME=hoge # export GITHUB_PASSWORD=hogepass SLACK_RTM_URL="https://slack.com/api/rtm.start" SLACK_REACTION_URL="https://slack.com/api/reactions.get" response = HTTP.post(SLACK_RTM_URL, params: { token: ENV['SLACK_API_TOKEN'] }) rc = JSON.parse(response.body) url = rc['url'] EM.run do # Web Socketインスタンスの立ち上げ ws = Faye::WebSocket::Client.new(url) # 接続が確立した時の処理 ws.on :open do p [:open] end # RTM APIから情報を受け取った時の処理 ws.on :message do |event| data = JSON.parse(event.data) p [:message, data] if data['text'] == '疲れた' ws.send({ type: 'message', text: "お疲れ様です! <@#{data['user']}> さん", channel: data['channel'] }.to_json) elsif data['text'] == ':github_issue:' ws.send({ type: 'message', text: "<@#{data['user']}> さん!メッセージに、:github_issue:リアクションをするとissueが簡単に作成できますよ。", channel: data['channel'] }.to_json) elsif data['text'] == 'グッときた' ws.send({ type: 'message', text: "<@#{data['user']}> さん、:最高かよ:", channel: data['channel'] }.to_json) end if data['reaction'] == 'github_issue' slack_response = HTTP.post(SLACK_REACTION_URL, params: { token: ENV['SLACK_API_TOKEN'], channel: data['item']['channel'], timestamp: data['item']['ts'] }) slack_response = JSON.parse(slack_response) https = Net::HTTP.new('api.github.com', '443') https.use_ssl = true https.start do |https| # Issue を作る API: http://developer.github.com/v3/issues/#create-an-issue req = Net::HTTP::Post.new('/repos/githubアカウント名/リポジトリ名/issues') req.basic_auth ENV['GITHUB_USERNAME'],ENV['GITHUB_PASSWORD'] issue_info = { 'title': "#{slack_response['message']["text"]}", 'body': "ヘルプです", "labels": [ "help wanted" ] } # githubAPIを叩いて、issue作成 req.body = JSON.generate issue_info github_response = https.request(req) # JSON.parseとは、JSON形式の文字列をRubyのHash形式に変換するためのメソッド github_response = JSON.parse(github_response.body) ws.send({ type: 'message', text: "<@#{data['user']}> さんのために、issueを作成しました!issueボードを確認してください。 #{github_response["html_url"]}", channel: data['item']['channel'] }.to_json) end elsif data['reaction'] == 'fish' slack_response = HTTP.post(SLACK_REACTION_URL, params: { token: ENV['SLACK_API_TOKEN'], channel: data['item']['channel'], timestamp: data['item']['ts'] }) slack_response = JSON.parse(slack_response) ws.send({ type: 'message', text: "魚のリアクションしましたね?", # text: "#{slack_response['message']['text']}に、魚のリアクションしましたね?", channel: data['item']['channel'] }.to_json) elsif data['reaction'] == 'gyozabu' ws.send({ type: 'message', text: "なるほど!<@#{data['user']}> さんは震えるほど餃子が食べたいみたいですよ! <@here> 今日は、餃子活動しないんですか?", channel: data['item']['channel'] }.to_json) end end # 接続が切断した時の処理 ws.on :close do p [:close, event.code] ws = nil EM.stop end endrubyスクリプト実行。
bundle exec ruby slack_bot.rb
作成したSlackBotの入っているチャンネルで、メッセージを送信したり、リアクションしてみるとBotが動いて、挙動が確認できると思われます。
まとめ
これでエンジニアではなくても、困っているメンバーが簡単にIssueを作成できるようになりました。(使われると嬉しい。。)Issue Boadは毎週エンジニアMTGで確認して、社内メンバーの困っているIssueを改修して参ります。
おまけ
折角SlackBotを作ったので、多少ユーモアを入れてみました。
(割と開発の醍醐味だと思っています)
非常に共感したので引用させて頂きました。↓くだらないBotを運用する一番のメリットは、楽しく技術の勉強ができるという点です。
失敗をおそれず色々な技術を試せる
工夫次第でどんな技術でも自由に取り入れられる
得た知見を業務に役立てられる
車輪の再開発/overkillが気にならない
フロント側のコードを書かなくても目に見える楽しいアウトプットがある
https://tech.dely.jp/entry/slack_bot_philosophy今回は餃子が震えているリアクションをするとBotが反応してくれます。
(@here
がメンションになっていない部分の課題感は残りつつ)
SlackBotはアイディア次第でもっと面白くできそうです!何か間違えている部分など気になる点があれば、優しく教えて頂けると励みになります。
推奨記事
- 投稿日:2019-05-03T13:48:08+09:00
VSCode公式の機能で、リモートサーバにSSHして編集する【Insider Preview】
Remote Development with Visual Studio Code という機能のプレビュー版がリリースされました。
VSCode公式リモート開発機能。
— s2terminal /suzuki.sh (@suzukiterminal) May 3, 2019
サーバにはSSHでアクセスするが、リモートサーバー上でVSCode拡張機能を動かすので、リモート上のソースコード静的解析とかが効く。VSCode本体とUI関連の拡張はローカルにあるので、エディタとしては軽快に動く、という事らしい。これ最強ではhttps://t.co/qFcDKBdmOE※以下、本稿の内容は2019年5月3日現在 Insider Preview (要するにベータ版みたいなの)であり、通常バージョンのVSCodeでは動作しません。
VSCode Remote 機能の概要
- ローカルマシンでは通常通り、VScode本体とUI関連の機能等を動かしている
- リモートサーバ上のファイルにアクセスして編集できる
- リモートサーバ上で言語の拡張機能等を実行する
- リモートとしては「SSH」のほかに「Container」「WSL」が選べる
今までもいくつかサードパーティのリモート編集の拡張機能は存在しましたが、「リモートサーバ上で言語の拡張機能等を実行する」という点が異なる点だと思います。これにより、リモート上の言語実行環境等を使ってローカルで開発できます。
例えば「リモートサーバ上のRubyファイルをVScodeで編集するために、ローカルにもRubyをインストールする」といった事が必要無くなるようです。リモート上のRubyを使ってローカルのVSCodeで開発できるようになります。
環境
以下、下記環境で実際に試してみます。
- ローカル環境
- Windows 10 Pro 1809
- Visual Studio Code 1.34.0-insider
- リモート環境
- CentOS Linux release 7.6.1810
- ※VSCode無し
手順
ここからは実際に、SSH でリモートサーバ上に接続して、リモート上で Ruby 拡張機能を動かしてみます。
だいたい Developing on Remote Machines using SSH and Visual Studio Code に書いてある通りですが。まず下記から、InsidersビルドのVScodeをインストールします。
https://code.visualstudio.com/insiders/
Insiders buildは、緑色のアイコンが目印です。青色アイコンの安定バージョンVSCodeとは共存できますので、普段安定板を使っていても特に問題はありません。次に、Microsoft が提供している Remote Development 拡張機能パックをインストールします。下記コマンドを使うのが早いと思います。
PowerShell> code-insiders --install-extension ms-vscode-remote.vscode-remote-extensionpack
> code-insiders
コマンドを打つか何かして Insiders Build 版の VSCode を起動します。SSHの設定
SSH 設定は、OpenSSH の ssh_config の記法が利用できます。VSCode のコマンドパレットから
>Remote-SSH: Open configuration File
を実行すると設定ファイルが開くので、SSH 接続情報を記入しておきます。たとえば
> ssh your.name@your-remote-server.example.com -i C:\Users\your\secret\key\path\.ssh\id_rsa
で接続できるサーバの場合、下記のような感じです。Host your-remote-server HostName your-remote-server.example.com User your.name IdentityFile C:\Users\your\secret\key\path\.ssh\id_rsaVSCode のコマンドパレットから
>Remote-SSH: Connect to Host
を実行し、先ほど設定したホスト情報を選択します。うまくいけば VSCode のウィンドウがもうひとつ開いて、リモート上のファイルが実行可能になります。
リモート上の Ruby Extension を使ってみる
リモートで開いた VSCode Window で、
>Extensions: Install Extensions
から、Ruby 拡張機能をインストールします。
インストールボタンが「install on SSH:your-remote-server
」となっているのを確認してください。通常は、単にインストールと書かれているだけだと思います。VSCode を
>Developer: Reload Window
でリロードして、Open Folder ボタンから適当なフォルダを開き、index.rb
みたいな適当なファイルを開いてみましょう。下記のように、 Ctrl+Space で Ruby の補完がかかれば成功です。
失敗時は「No Suggestions.」になると思います。実際にリモート接続していない VSCode ウィンドウで試すと下記のようになり、リモート上でしか Ruby 拡張機能が動いていない事が確認できると思います。
なおインストールされた拡張機能はどこにあるのかというと、リモートサーバのホームディレクトリ以下に
.vscode-remote
というディレクトリが作成されており、そこに保存されるようでした。$ ls ~/.vscode-remote/extensions/ rebornix.ruby-0.22.3まとめ
以上より、ローカルにある VSCode を使ってリモート上の開発をすることができるようになりました。
まだプレビュー版であり、実際どれくらい便利に使えるのか分かりません。あまりにもあっさり動いたので、リモート上で拡張機能がどれくらい機能しているのか、どんな拡張機能でも動くのか、など不明点はありますが、少なくとも VSCode 公式の機能で、リモート上の開発が簡単にできるようになったというのは事実です。
これが実現すれば、ローカルに言語実行環境などをインストールしていない環境下でも、VSCode さえあれば開発ができるようになります。また、初学者や非エンジニアなど開発環境をローカルに構築するのが難しいケースにおいても有効に使えると思います。正式リリースが非常に楽しみです。
参考
- 投稿日:2019-05-03T13:48:08+09:00
VSCode公式の機能で、リモートサーバにSSHして編集する【Insiders Preview】
Remote Development with Visual Studio Code という機能のプレビュー版がリリースされました。
VSCode公式リモート開発機能。
— s2terminal /suzuki.sh (@suzukiterminal) May 3, 2019
サーバにはSSHでアクセスするが、リモートサーバー上でVSCode拡張機能を動かすので、リモート上のソースコード静的解析とかが効く。VSCode本体とUI関連の拡張はローカルにあるので、エディタとしては軽快に動く、という事らしい。これ最強ではhttps://t.co/qFcDKBdmOE※以下、本稿の内容は2019年5月3日現在 Insider Preview (要するにベータ版みたいなの)であり、通常バージョンのVSCodeでは動作しません。
VSCode Remote 機能の概要
- ローカルマシンでは通常通り、VScode本体とUI関連の機能等を動かしている
- リモートサーバ上のファイルにアクセスして編集できる
- リモートサーバ上で言語の拡張機能等を実行する
- リモートとしては「SSH」のほかに「Container」「WSL」が選べる
今までもいくつかサードパーティのリモート編集の拡張機能は存在しましたが、「リモートサーバ上で言語の拡張機能等を実行する」という点が異なる点だと思います。これにより、リモート上の言語実行環境等を使ってローカルで開発できます。
例えば「リモートサーバ上のRubyファイルをVScodeで編集するために、ローカルにもRubyをインストールする」といった事が必要無くなるようです。リモート上のRubyを使ってローカルのVSCodeで開発できるようになります。
環境
以下、下記環境で実際に試してみます。
- ローカル環境
- Windows 10 Pro 1809
- Visual Studio Code 1.34.0-insider
- リモート環境
- CentOS Linux release 7.6.1810
- ※VSCode無し
手順
ここからは実際に、SSH でリモートサーバ上に接続して、リモート上で Ruby 拡張機能を動かしてみます。
だいたい Developing on Remote Machines using SSH and Visual Studio Code に書いてある通りですが。まず下記から、InsidersビルドのVScodeをインストールします。
https://code.visualstudio.com/insiders/
Insiders buildは、緑色のアイコンが目印です。青色アイコンの安定バージョンVSCodeとは共存できますので、普段安定板を使っていても特に問題はありません。次に、Microsoft が提供している Remote Development 拡張機能パックをインストールします。下記コマンドを使うのが早いと思います。
PowerShell> code-insiders --install-extension ms-vscode-remote.vscode-remote-extensionpack
> code-insiders
コマンドを打つか何かして Insiders Build 版の VSCode を起動します。SSHの設定
SSH 設定は、OpenSSH の ssh_config の記法が利用できます。VSCode のコマンドパレットから
>Remote-SSH: Open configuration File
を実行すると設定ファイルが開くので、SSH 接続情報を記入しておきます。たとえば
> ssh your.name@your-remote-server.example.com -i C:\Users\your\secret\key\path\.ssh\id_rsa
で接続できるサーバの場合、下記のような感じです。Host your-remote-server HostName your-remote-server.example.com User your.name IdentityFile C:\Users\your\secret\key\path\.ssh\id_rsaVSCode のコマンドパレットから
>Remote-SSH: Connect to Host
を実行し、先ほど設定したホスト情報を選択します。うまくいけば VSCode のウィンドウがもうひとつ開いて、リモート上のファイルが実行可能になります。
リモート上の Ruby Extension を使ってみる
リモートで開いた VSCode Window で、
>Extensions: Install Extensions
から、Ruby 拡張機能をインストールします。
インストールボタンが「install on SSH:your-remote-server
」となっているのを確認してください。通常は、単にインストールと書かれているだけだと思います。VSCode を
>Developer: Reload Window
でリロードして、Open Folder ボタンから適当なフォルダを開き、index.rb
みたいな適当なファイルを開いてみましょう。下記のように、 Ctrl+Space で Ruby の補完がかかれば成功です。
失敗時は「No Suggestions.」になると思います。実際にリモート接続していない VSCode ウィンドウで試すと下記のようになり、リモート上でしか Ruby 拡張機能が動いていない事が確認できると思います。
なおインストールされた拡張機能はどこにあるのかというと、リモートサーバのホームディレクトリ以下に
.vscode-remote
というディレクトリが作成されており、そこに保存されるようでした。$ ls ~/.vscode-remote/extensions/ rebornix.ruby-0.22.3まとめ
以上より、ローカルにある VSCode を使ってリモート上の開発をすることができるようになりました。
まだプレビュー版であり、実際どれくらい便利に使えるのか分かりません。あまりにもあっさり動いたので、リモート上で拡張機能がどれくらい機能しているのか、どんな拡張機能でも動くのか、など不明点はあります。(たとえば Ruby 拡張機能でも Language Server 機能はエラーで動いておらず、まだ動かし方は調べていません)
ですが少なくとも VSCode 公式の機能で、リモート上の開発が簡単にできるようになったというのは事実です。これが実現すれば、ローカルに言語実行環境などをインストールしていない環境下でも、VSCode さえあれば開発ができるようになります。また、初学者や非エンジニアなど開発環境をローカルに構築するのが難しいケースにおいても有効に使えると思います。正式リリースが非常に楽しみです。
参考
- 投稿日:2019-05-03T13:21:20+09:00
ruby オブジェクト設計
単一責任
変更に対応できるように
- wheelの切り出し 単一責任の為に - initailizeの while=nil 変動に対応する class Gear attr_reader :chainring, :cog, :wheel def initialize(chainring, cog, wheel=nil) @chainring = chainring @cog = cog @wheel = wheel end def ratio chainring / cog.to_f end def gear_inches ratio * wheel.diameter end end class Wheel attr_reader :rim, :tire def initialize(rim, tire) @rim = rim @tire = tire end def diameter rim + (tire * 2) end def circumference diameter * Math::PI end end @wheel = Wheel.new(26, 1.5) p @wheel.circumference gear2 = Gear.new(11,11,@wheel) p gear2.gear_inches引数のコントロール
- ハッシュで格納
arges = ハッシュ
- 引数の格納順番への依存はなくなる
class Gear attr_reader :chinring, :cog, :wheel def initialize(arges) @chinring = arges[:chinring] @cog = arges[:cog] @wheel = arges[:wheel] end def fog chinring + cog + wheel end end class Wheel attr_reader :ring def initialize(ring) @ring = ring end def ee ring end end p Gear.new(chinring: 52, cog: 11, wheel: Wheel.new(11).ee ).fog
- 投稿日:2019-05-03T12:49:15+09:00
Ruby on railsで引っかかったところ
railsでつまづいたところをメモしていこうかなと思います。
- 投稿日:2019-05-03T08:01:52+09:00
Rails6 のちょい足しな新機能を試す10(year_format(令和ネタ)編)
はじめに
Rails 6 に追加されそうな新機能を試す第10段。 今回のちょい足し機能は、
date_select の year_format
編です。
datetime_select, date_select で year_format オプションを指定することで、年の書式を指定できるようになりました。記載時点では、Rails は 6.0.0.rc1 です。
gem install rails --prerelease
でインストールできます。今回は、せっかくなので、「令和」に対応した Ruby 2.6.3 を使ってます。
$ rails --version Rails 6.0.0.rc1プロジェクトを作成する
rails プロジェクトを作成します。
$ rails new sandbox6_0_0rc1
scaffold で CRUD を作成する
$ cd sandbox6_0_0rc1 $ bin/rails g scaffold User name birth_at:datetime $ bin/rails db:create db:migrate
app/views/users/_form.html.erb
を編集する
:year_format
オプションを追加して年の書式を変更してみます。app/views/users/_form.html.erb... <%= form.datetime_select :barth_at, year_format: ->year { "#{Date.new(year, 12, 31).jisx0301.split('.').first}(#{year})" } %>登録画面を表示
User の登録画面を表示してみると、年の書式が変わっていることがわかります。
参考情報
- 投稿日:2019-05-03T07:21:16+09:00
Rails6 のちょい足しな新機能を試す9(エラーメッセージのフォーマット編)
はじめに
Rails 6 に追加されそうな新機能を試す第9段。 今回のちょい足し機能は、
full message
編です。
ymlファイルで指定できるエラーフォーマットが拡張されてます。記載時点では、Rails は 6.0.0.rc1 です。
gem install rails --prerelease
でインストールできます。$ rails --version Rails 6.0.0.rc1プロジェクトを作成する
rails プロジェクトを作成します。
$ rails new sandbox6_0_0rc1
model を作る
$ cd sandbox6_0_0rc1 $ bin/rails g model User name email $ bin/rails db:create db:migratemodel に validation を追加する
app/models/user.rbclass User < ApplicationRecord validates :name, presence: true validates :email, presence: true end何もymlファイルに指定がないとき
何もymlファイルに指定がないときには、今までと同じ
Name can't be blank
とEmail can't be blank
になります。$ bin/rails c Running via Spring preloader in process 653 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> irb(main):002:0> user.validate => false irb(main):003:0> user.errors.full_messages => ["Name can't be blank", "Email can't be blank"] irb(mail):004:0> exityml ファイルでエラーフォーマットを指定
エラーメッセージのフォーマット全体を指定してみます。
config/locales/en.ymlen: errors: format: '%{message}'指定されたフォーマット (
can't be blank
だけ) にエラーメッセージが変わります。$ bin/rails c Running via Spring preloader in process 696 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> irb(main):002:0> user.validate => false irb(main):003:0> user.errors.full_messages => ["can't be blank", "can't be blank"] irb(main):004:0> exit実はここまでは、Rails 5.2.3でも同じです。
yml ファイルで model レベルでエラーフォーマットを指定
ここからが、Rails6のちょい足しです。
エラーメッセージのフォーマットを model レベルで指定してみます。config/locales/en.ymlen: errors: format: '%{message}' activerecord: errors: models: user: format: '%{attribute} : %{message}'では、エラーメッセージを確認してみましょう。
このとき、ActiveModel::Errors.i18n_customize_full_message = true
にします。$ bin/rails c Running via Spring preloader in process 1338 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> ActiveModel::Errors.i18n_customize_full_message = true => true irb(main):002:0> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> irb(main):003:0> user.validate => false irb(main):004:0> user.errors.full_messages => ["Name : can't be blank", "Email : can't be blank"] irb(main):007:0> exitなお
ActiveModel::Errors.i18n_customize_full_message
はデフォルトでは、false
です。
ActiveModel::Errors.i18n_customize_full_message = false
のときは、modelレベルの設定は無視されます。$ bin/rails c Running via Spring preloader in process 1343 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> ActiveModel::Errors.i18n_customize_full_message => false irb(main):002:0> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> irb(main):003:0> user.validate => false irb(main):004:0> user.errors.full_messages => ["can't be blank", "can't be blank"] irb(main):005:0> exityml ファイルで attribute レベルでエラーフォーマットを指定
今度は、attribute レベルでエラーフォーマットを指定してみます。
config/locales/en.ymlen: errors: format: '%{message}' activerecord: errors: models: user: format: '%{attribute} : %{message}' attributes: name: format: '%{message} - %{attribute}'では、試してみましょう。
name
は、'%{message} - %{attribute}'
のフォーマットが適用され
'%{attribute} : %{message}'
のフォーマットが適用されます。$ bin/rails c Running via Spring preloader in process 1383 Loading development environment (Rails 6.0.0.rc1) irb(main):001:0> ActiveModel::Errors.i18n_customize_full_message = true => true irb(main):002:0> user = User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> irb(main):003:0> user.validate => false irb(main):004:0> user.errors.full_messages => ["can't be blank - Name", "Email : can't be blank"] irb(main):005:0>
i18n_customize_full_message
の設定今回、irb の中で動的に設定しましたが、
i18n_customize_full_message
は、 config ファイルで設定できます。config/application.rbmodule Sandbox600rc1 class Application < Rails::Application ... config.active_model.i18n_customize_full_message = true end end参考情報
- 投稿日:2019-05-03T05:34:49+09:00
【Rails5.2.1】credentials.ymlを使用してアプリをHerokuにデプロイできなかった話
- 投稿日:2019-05-03T05:34:12+09:00
【10日間でポートフォリオ作成に挑戦】6日目:テストコードの実装
概要
今回は、2019年のGW期間(10日間)を全て費やして取り組む
ポートフォリオの製作過程
を取りまとめた内容を投稿させて頂きます。(投稿は毎日行う予定)全体通した取り組みの詳細については、前回までの記事をご参照ください。
【10日間でポートフォリオ作成に挑戦】1日目:要件定義〜記事投稿のCRUD
【10日間でポートフォリオ作成に挑戦】2日目:アクセス制限〜コメントのCRUD機能
【10日間でポートフォリオ作成に挑戦】3日目:ページネーション~CKEditorの導入
【10日間でポートフォリオ作成に挑戦】4日目:テーブル分割〜CKEditorのフォームへの反映
【10日間でポートフォリオ作成に挑戦】5日目:CKEditorへ画像アップロード機能を追加今日一日の作業内容
ここからは、今日1日で取り組んだ作業内容をご説明します。
テストコードの記述
これまで実装した各機能のテストコードを記述して行きました。
- アカウント認証機能(devise)
- 記事のCURD機能(一覧、新規作成、編集、削除)
- 記事へのコメントのCRUD機能(新規作成、編集、削除)
- 記事一覧のページネーション(kaminari)
- 画像のアップロード機能(Shrine)
- CKEditorによる記事の編集機能
テストで利用したgemは下記の通りです。
group :development, :test do (中略) gem 'rspec-rails' gem 'factory_bot_rails' gem 'rails-controller-testing' end group :test do gem 'capybara', '>= 2.15' gem 'selenium-webdriver' gem 'chromedriver-helper' endなお、テストコードの実装にあたっては、Everyday Railsをかなり参考にさせて貰いました。
Everyday Rails - RSpecによるRailsテスト入門今日の失敗
ここからは、今日の失敗をまとめます。
テストコードを通す為に無理矢理な処理を記述する
※追記(5/3):こちらのコードは、伊藤さん(@jnchito)がコメントにて、適切な修正案を提示して頂いているので、そちらもご参考にしてください。
まずは、下記のコードをご覧ください。
spec/system/post_spec.rbrequire 'rails_helper' RSpec.describe 'post_comment', type: :system do let!(:user) { create(:user) } let(:post) { create(:post, id: 1, user_id: 1) } let(:post_description) { create(:post_description, post_id: 1) } before { login_user(user) } it 'creates post' do visit posts_path click_on(I18n.t('common.button.new')) fill_in 'post[title]', with: 'テストタイトル1' fill_in 'post[post_description_attributes][description]', with: 'テスト詳細1' click_button(I18n.t('common.button.submit')) expect(page).to have_content('テストタイトル1', 'テスト詳細1') end it 'edits post' do post create(:post_description, post_id: 1) visit edit_post_path(post) fill_in 'post[title]', with: 'テストタイトル2' fill_in 'post[post_description_attributes][description]', with: 'テスト詳細2' click_button(I18n.t('common.button.submit')) expect(page).to have_content('テストタイトル2', 'テスト詳細2') end end上記のコードは二つの欠点があります。
I18n
辞書ファイルの名称と一致するかチェックしてsubmitボタンを押す処理ですが、下記の様に
I18n
を外すと、エラーになってしまいます。click_button(t('common.button.submit'))NoMethodError: undefined method `t' for #<RSpec::ExampleGroups::Post:0x00007fb64b90cac8>インターネットで調べると、stackoverflowに下記の様な回答があったので、それに沿って、
I18n
を付与すると、エラーは解消されました。Use I18n.t instead of just t.
undefined method `t' for Admin::FaqsController:Classしかし、
I18n
無しでも本来は実装出来るはずです。
その方法が見当たらなかった為、一旦この方法で実装しています。editでのpostの定義
editアクションのテストにおいて、
post_description
を新たに作成していますが、本来であれば、let
で事前に作成しているはずなので、不要です。しかし、このコードを外すとエラーになってしまいます。
Failures: 1) post edits post Failure/Error: fill_in 'post[post_description_attributes][description]', with: 'テスト詳細2' Capybara::ElementNotFound: Unable to find field "post[post_description_attributes][description]" that is not disabledこちらも原因が特定できませんでした、テストは正常にできている様なので、一旦はこの状態で実装し、後々対策を調査しようと考えています。
流石に疲れて来た
開発を進めながら、個人ブログとQiitaを両方毎日投稿するのは結構疲れます・・・
(特にQiitaは多く目に触れるので、かなり神経を擦り減らす・・・)その影響で集中力が持続せず、作業効率がかなり落ちてしまっている様に感じます。
適宜息抜きも必要だと、改めて実感しました。明日の予定
- 記事の検索機能(ransack)
- 記事へのいいね機能(別ユーザーのみ)
- 記事のストック機能(自身の記事も可)
明日までにここを完成させないと、いよいよ後が無い・・・
デスマーチで乗り切るほかあるまい・・・おまけ
最後になりますが、現在、私は下記の目標を立てて学習に取り組んでいます。
- 3年間で「10,000時間」をプログラミングに費やす
- その間、毎日ブログの投稿を行う
Twitterでは、その過程で学んだ事などを発信しています。
もし宜しければフォローしてみてください。
- 投稿日:2019-05-03T03:59:43+09:00
railsのbxsliderのaタグが効かないときの対処法
はじめに
スライドショーのgem「bxslider」を使用していた際に、スライドショー内のリンクをクリックしても遷移しなかったので、直接コードをいじってなんとか解決しました。
そのフローを残しておきたいと思います。開発環境
- windows7
- ruby 2.4.5p335 (2018-10-18 revision 65137) [x64-mingw32]
- Rails 5.2.3
- bxslider-rails 4.2.5.1
- jquery-rails 4.3.3
事象・検証状況
- スライドショー内のリンクをクリックしても遷移しない
- aタグをマウスオンすると遷移先URLが表示される
- aタグを覆う要素は存在しない
- レンダリング時のエラーなし
- 自身のコーディングに不備はない可能性が高い
- デベロッパーツールのコンソール上でもエラーなし
- JQueryでもエラーはない
- めっちゃ調べた。
- これっぽい。
カミナリ|bxSliderのスライド内のリンクが効かなくなった際の対処方
https://kaminarimagazine.com/web/2019/03/29/bxslider%E3%81%AE%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%89%E5%86%85%E3%81%AE%E3%83%AA%E3%83%B3%E3%82%AF%E3%81%8C%E5%8A%B9%E3%81%8B%E3%81%AA%E3%81%8F%E3%81%AA%E3%81%A3%E3%81%9F%E9%9A%9B%E3%81%AE%E5%AF%BE/解説
原因
スマホのタッチ・スワイプに対応させるためのオプションで、デフォルトが
true
である、「touchEnabled」オプションにより、マウスオン(タッチ)時にドラッグ(スワイプ)操作判定となる。つまり、clickイベントとmouseleaveイベントが発火しない。
もう少し詳しく話すと、ページをスライドさせない(ドラッグ(スワイプ)距離がないor短い)とき、クリック処理がされないような記述になっている。参考サイトの対応策
- 「mousedown」で対策する
- bxSliderのオプションの「touchEnabled」を’false’に設定する
感想
どっちもやだ。
理由
- 操作感が変わる→気持ち悪い
- スマホ上でフリックによるスライドはさせたい
解決するにいたった手順
- gemのソースから
touchEnabled
を探すinitTouch
呼び出してた。探すonTouchStart
をbindしてた。探す- いっぱい何か書いてる...。でも、
onTouchStart
というよりはonTouchEnd
みたいな関数が関係してそう。それ、bindされてた。探すdistance
計算してるし、ここだ!- 無理矢理クリックイベントを発火させる方法調べる → 発見!(参考サイト参照) → 完成!
※
onTouchStart
あたりから逐次console.log(hoge);
で呼び出されている関数を確認していました。該当コード
app_name\vendor\bundle\ruby\2.4.0\gems\bxslider-rails-4.2.5.1\vendor\assets\javascripts\jquery.bxslider.jsvar onTouchEnd = function(e) { if (slider.settings.mode === 'fade') { # # スライドショーのスタイルがfadeの時の処理 # } else { # # 横スライド(horizontal)か縦スライド(vertical)の判定処理 # if (!slider.settings.infiniteLoop && ((slider.active.index === 0 && distance > 0)|| (slider.active.last && distance < 0))) { setPositionProperty(value, 'reset', 200); } else { if (Math.abs(distance) >= slider.settings.swipeThreshold) { # # ドラッグ距離 or スワイプ距離がスライド有効距離以上の場合のスライド処理 # } else { + document.elementFromPoint(e.clientX, e.clientY).click(); setPositionProperty(value, 'reset', 200); } } } };まとめ
初心者によるChromeのアップデートに対する不具合解消ですので、かなり付け焼刃的な解決法だったのかな、と思っています...。ちなみに、今回はfade形式のスライドショーはを使う予定がなかったので、コードを変更していませんが、
# スライドショーのスタイルがfadeの時の処理
部分の類似部分を変更すればfadeでも正しく動作するはずです。参考サイト
- エラーの概要をつかみました。
カミナリ|bxSliderのスライド内のリンクが効かなくなった際の対処方
cly7796.net|スワイプする要素の中にリンクがある場合の対応方法document.elementFromPoint(x, y).click();
で任意の座標でクリックイベントを発火させることを知りました。
Stack Overflow|How to simulate a click by using x,y coordinates in JavaScript?- 座標の取得方法はここで見つけました
gaaamiiのブログ|function(e)のeってなんだ?
- 投稿日:2019-05-03T03:52:45+09:00
Ruby 範囲と配列についてもっと〜チェリー本4章〜
Ruby 範囲と配列についてもっと〜チェリー本4章〜
※この記事は初心者の私がチェリー本で学習するにあたって新しく知ったことや考えたことアウトプットするための場として書いています。
この記事は以下の情報を参考にして記述し、一部引用しています。
プロを目指す人のためのRuby入門
配列の便利メソッドあれこれ範囲から値が連続する配列を作成する
(1...4).to_a => [1, 2, 3] [*'x'..'z'] => ["x", "y", "z"]
.to_a
メソッドで配列化することができます。
2つ目は[]の中に*
を範囲の頭につけても配列化することができます。
配列の要素を取得するメソッド
a = [1, 2, 3, 4, 5] a.values_at(0,1,3) => [1, 2, 4] a[-1] => 5 a.last(3) => [3, 4, 5] a.first(2) => [1, 2]配列の添字から要素を複数指定して取得するときは
value_at
メソッドを使用します。
要素を1つ指定したい場合は配列[]
で指定します。
配列の前後から複数指定する場合は.last
、.first
メソッドを使用します。配列のメソッドについては配列の便利メソッドあれこれがとても参考になります!
配列に初期値を設定する場合の注意点-4.7.13より引用
a = Array.new(5, 'default') => ["default", "default", "default", "default", "default"] str = a[0].upcase! => "DEFAULT" a => ["DEFAULT", "DEFAULT", "DEFAULT", "DEFAULT", "DEFAULT"] a = Array.new(5){'default'} => ["default", "default", "default", "default", "default"] str = a[0].upcase! a => ["DEFAULT", "default", "default", "default", "default"]上のコードは同じ参照値の要素が同時に作られるため、1つ変更すると5つとも変更されてしまいます。
下のコードはブロックによって一つずつ別のオブジェクトの要素が作成されるため1つ変更してもその他の要素には影響を与えません。
あとがき
今日は@jnchitoさんのQiitaで記事を公開するときに気を付けるべきマナーについて 〜無断でネットや書籍の内容を丸写しするのはやめよう〜という記事を読んで少なからず当てはまる点があるのでは無いかと思い、記事の書き方を改めるのと過去の記事に注釈を追記しました。
先程の記事内でもリンクが貼ってあった”ブログに技術書の内容を丸写しする問題点と、オリジナルなコンテンツを書くためのアイデア”がとても参考になりました!
最近コードを写経とは別に実際に書く機会が無いとQiitaなどに記事を上げるだけではアウトプットの質が低いなと思えて来ました...(何かコードの問題集みたいなのないかな)
- 投稿日:2019-05-03T02:23:29+09:00
イテレータでインデックスを取ってみる
前提
Rubyのバージョン: 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin18]
インデックス取れないの? -> 取れた!
「たのしいRuby」でお勉強中。
26ページの繰り返しにtimesメソッドの記載がありました。iterator_sample.rb5.times do print '(☝︎ ՞ਊ ՞)☝︎' end #=> (☝︎ ՞ਊ ՞)☝︎(☝︎ ՞ਊ ՞)☝︎(☝︎ ՞ਊ ՞)☝︎(☝︎ ՞ਊ ՞)☝︎(☝︎ ՞ਊ ՞)☝︎これを書きながらふと「これのインデックス取れんの?」と疑問に思ったわけですな。
doの後ろで取ろう
doの後ろに変数を置いてあげると勝手にブチこんでくれます。
(下記例では「i」)index_hosii.rb5.times do |i| print i end #=> 01234おまけ
2つ目を指定してもなんにもでません。
j_missing.rb5.times do |i, j| print j end #=> #何も出ない。ひたすらに虚無。
- 投稿日:2019-05-03T01:35:46+09:00
【Ruby】caseでFizzBuzzを解いてみよう!
FizzBuzzとは
1から100の数字に対して、以下の制御を行うアルゴリズムです。
- 3で割り切れる場合は、「Fizz」と表示
- 5で割り切れる場合は、「Buzz」と表示
- 15で割り切れる場合は、「FizzBuzz」と表示
- それ以外の場合は、その数値を表示
シンプルにifを使って書くのがオーソドックスかと思います。
今回は、別解としてcaseを使って表現してみます。まずは、結果から。
(1..100).each do |num| puts case 0 when num % 15 then :FizzBuzz when num % 3 then :Fizz when num % 5 then :Buzz else num end endポイント1:「case 式」「when 式」という構造
caseでは、「case 式」と「when 式」という形を取り、それぞれの式の結果が等しいかどうかを判定して制御を行っています。
ここで、「式」とは「num % 15」のような計算式以外にも「0」や「"AAA"」のようなオブジェクトも含まれます。
したがって、caseの隣には必ずしも「num % 15」のような計算式を記載するとは限りません。case "AAA" # whenに計算式を書くのもアリ when "AA" + "A" then puts "same" else puts "different" end # => same上記の例では、
「"AAA" === "AA" + "A"」という比較を行った結果、等しいと判定して
「puts "same"」の処理が実行されています。ポイント2:caseは該当のwhenに対するthen以降の値を返す
判定の結果、該当のwhen(else)のthen以降の結果をcaseの返り値として扱います。
試しに、putsをpメソッドに変えてみると、
それぞれ「:Fizz」、「:Buzz」、「:FizzBuzz」が表示されていることが確認できます。
以下の記述でも同様の結果が得られますが、少し冗長ですよね。(1..100).each do |num| case 0 # 判定ごとにputsを記載 when num % 15 then puts :FizzBuzz when num % 3 then puts :Fizz when num % 5 then puts :Buzz else puts num end endポイント3:whenの記載順に注意!(上から順に判定)
if同様に、判定は上から順に行い、該当したところで値を返します。
したがって、仮に以下の順にした場合は、「FizzBuzz」は表示されません。
(15の倍数は先に「num % 3」に該当してしまうため「Fizz」が表示される)(1..100).each do |num| puts case 0 # 15の倍数はここに該当してしまう when num % 3 then :Fizz when num % 5 then :Buzz when num % 15 then :FizzBuzz else num end end本記事があなたのcaseの理解に少しでも貢献できれば幸いです。
- 投稿日:2019-05-03T00:38:41+09:00
Ruby Gold 勉強1(メソッド探索)
- 投稿日:2019-05-03T00:13:12+09:00
Rails ~会員登録までの道のり~
これまでのあらすじ
前回の記事よりActionMailerの機能でメールを飛ばせることを検証しました。
今回は、メールアドレスを認証させるために仮ユーザ登録させてからユーザ登録させるための仮ユーザ登録機能を作ってみます。
ユーザ認証に関しては、deviseなどのGemの利用がRailsでは、一般的(というか便利)らしいですが、Ruby・Railsの勉強をするために純粋にRuby・Railsで構築してきたいと思います。Top画面の作成
まずトップ画面を作成します。
top_cotroller.rb
を作成します。terminal$ rails g cotroller top
top_controller.rbclass TopController < ApplicationController def index end endindexアクションを作成し、
/views/top/index.html.erb
を作成します。
config/routes.rb
にルートを追加する。rootパスにtop#indexアクションを指定します。
routes.rbRails.application.routes.draw do root 'top#index' # top_controller get 'top', to: 'top#index' endTOP画面はこんな感じにBootstrapを駆使して作りました。
(管理者とかあるのはご愛嬌で...)
背景に動画を埋め込んで「イマドキ」っぽいおしゃれなWebサイトっぽくしました。
仮ユーザのModel作成
仮ユーザ(temp_user)のModel(DBのテーブル)を作成します。
schema.rbcreate_table "temp_users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| t.string "mail_address", null: false t.string "last_name", null: false t.string "first_name", null: false t.string "token", limit: 64, null: false t.datetime "expired_at", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["mail_address"], name: "index_temp_users_on_mail_address", unique: true t.index ["token"], name: "index_temp_users_on_token", unique: true end仮ユーザ情報として、ログインに使用するmail_addressとユーザの氏名、仮ユーザを認証するためのtoken、仮ユーザ情報の有効期限のためのexpired_atで構成されています。
仮ユーザ登録までの流れ
- TOP画面のsign onボタンを押下し、仮登録画面を表示。
- 仮登録画面で氏名・メールアドレスを入力。
- メールアドレス登録完了と同時に、本登録画面のurlを記載したメール送信。
- 仮登録が完了した旨のメッセージ表示。
1. TOP画面のsign onボタンを押下し、仮登録画面を表示。
以前の記事で紹介したモーダルウィンドウを起動し、仮登録画面を表示しました。
top_controller#sign_onで表示しています。
form_with用の@temp_userをnewしている以外は特別なことはしていません。top_controller.rbclass TopController < ApplicationController def sign_on @temp_user= TempUser.new end end2. 仮登録画面で氏名・メールアドレスを入力。
temp_user.rbにバリデーションを追加しました。
temp_user.rbclass TempUser < ApplicationRecord VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :last_name, presence: true validates :first_name, presence: true validates :mail_address, presence: true validates :mail_address, format: { with: VALID_EMAIL_REGEX } end氏名は必須。
メールアドレスは必須とメールアドレス正規表現によるフォーマットチェックをしています。3. メールアドレス登録完了と同時に、本登録画面のurlを記載したメール送信。
top#sign_onでformを表示し、formのsubmitでtop#createを読んでいます。
本処理は
- mail_addressがすでに使用されている(temp_userに登録済み)の場合は、UPDATE、未使用の場合は、INSERTをする。(UPSERT処理)
- UPDATE時、入力された姓・名・有効期限は後勝ちとする。
- tokenはランダム文字列で生成。
find_or_initialize_byで条件にformで入力されたmail_addressを指定しています。
find_or_initialize_byは取得できた場合、取得結果をModelに詰め、できない場合は、ただ単純にnewします。その後、姓・名をセットします。
tokenですが、SecureRandom.urlsafe_base64でランダム文字列を作成しました。なお、重複登録を防ぐために、token = 生成文字列でtemp_userを検索し、存在した場合、再度生成しています。
有効期限はとりあえず現在日時をセットし、saveしています。
find_or_initialize_byで生成しているため、すでに存在している場合は、UPDATE・新規の場合はINSERTをrailsで判断しています。(便利ですね!!)
top_controller.rbdef create # 入力されたメールアドレスは登録済みか @temp_user = TempUser.find_or_initialize_by(mail_address: temp_users_params[:mail_address]) # 入力された姓・名をセット @temp_user.last_name = temp_users_params[:last_name] @temp_user.first_name = temp_users_params[:first_name] # トークンを生成 @temp_user.token = create_token # 有効期限に現在日時をセットする @temp_user.expired_at = DateTime.now respond_to do |format| # UPSERT実行 if @temp_user.save format.js { @status = "success" } else format.js { @status = "fail" } end end end private def create_token token = nil loop do token = SecureRandom.urlsafe_base64 break if TempUser.find_by(token: token).nil? end return token end4. 仮登録が完了した旨のメッセージ表示。
成功した場合は、完了した旨のメッセージを表示しました。
これもModalで表示していますが、formのモーダルを消してから、完了のモーダルをshowするため
遅延実行しています。create.js.erb<% if @status == 'success' %> $('#top_modal').modal('hide'); setTimeout(function(){ $('#top_modal').html('<%= j(render 'completed_sign_on') %>'); $('#top_modal').modal('show'); }, 500); <% elsif @status == 'fail' %> $('#sign_on_errors').html('<%= j(render 'layouts/error_messages', model: @temp_user) %>'); <% end %>こんな感じ...
今後は…
これで仮登録までできました。
とりあえず、前回の記事の検証と合わせて、メールを飛ばすところまではつくりたいですね。
(こうしたほうがいいとか、ご意見あったらぜひぜひいただけると嬉しいです。)