- 投稿日:2019-07-19T23:06:10+09:00
バーチャル脱出ゲーム:バリデーションからの脱出(問題編)
Rubyのサンドボックスを作って、evalするBotを作ったという記事をこの間読んだんですよ
この記事自体は大分前のものですが、最新版を突破できないかと試行錯誤している時に「もしバリデーションでサンドボックスを作ろうとしたら」とか考えまして
まあ、軽く考えてブラックリスト方式はもちろん、ホワイトリストでもまず無理だと思いましたが
今回の問題は、その際に軽く考えた内容をベースにしたものです
問題
quiz.rbclass Tester < Ripper::Filter def on_ident(name, *) raise end end code = gets Tester.new(code).parse eval(code)ルール
上記スクリプトの eval(code) に到達し、'escape!' を何らかの形で出力すればok
実は結構色々出来ます予備知識
RipperはRubyコードをパースするライブラリで、on_identはローカル変数っぽいもので呼ばれます
で、rubyのメソッドの呼び出し方はわりとローカル変数っぽいので、ここに投げ込まれます
つまり大まかに言えば、普通のメソッドっぽいものが書かれていた時点でエラーになります最後に
回答編は日曜夜を予定しています
ゆっくりかんがえていってね!!!
- 投稿日:2019-07-19T22:14:31+09:00
【Ruby】配列をループしている合間に二重配列にしていく
やろうとするたびに忘れるので,めも
sample.rb#wordの箇所は全て同じにしましょう,hogeはお好きなように array.map{|word|word=[word,hoge]}ループしているうちに配列が前の方から次々と二重配列になっていく.
適宜,書き直してください.
他の処理も追加できるよ.
コードが汚いので,もっといい方法あったら教えてください.
- 投稿日:2019-07-19T22:04:47+09:00
【備忘録】rails newでmysql2がインストールできないエラーの解消方法
【参考】mysql2 gemインストール時のトラブルシュート
長らく悩まされていたエラーがようやく解決できたので,備忘録として。
$ rails new アプリ名 -d mysqlを実行すると,毎回このエラーが発生していた。
Fetching mysql2 0.5.2 Installing mysql2 0.5.2 with native extensions Gem::Ext::BuildError: ERROR: Failed to build gem native extension. (中略) An error occurred while installing mysql2 (0.5.2), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.5.2' --source 'https://rubygems.org/'` succeeds before bundling. In Gemfile: mysql2 run bundle exec spring binstub --all Could not find gem 'mysql2 (>= 0.4.4, < 0.6.0)' in any of the gem sources listed in your Gemfile. Run `bundle install` to install missing gems.色々調べて試してみるも効果無し。
解決できたコマンドは【参考】に書かれていた次のコマンドだった。$ cd アプリ名 $ gem install mysql2 -v '0.5.2' --source 'https://rubygems.org/' -- --with-cppflags=-I/usr/local/opt/openssl/include --with-ldflags=-L/usr/local/opt/openssl/lib $ bundle installこの後,「rails new」でアプリを作成してもエラーが出なくなった。長かった……
- 投稿日:2019-07-19T21:18:52+09:00
【Rials】フォロー機能1~多対多のモデルの構造
Rialsでのフォロー機能についてのメモです。
ツイッタークローンのフォロー機能のモデルの説明です。こちらの続きです。
フォロー機能の概要
投稿機能は1対多で、
フォロー機能は、モデルの多対多の関係で成り立ちます。投稿機能では、ユーザ(1)に対して、投稿(多)という関係が成り立ちました。
フォロー機能では、ユーザ(多)に対して、ユーザ(多)という構造を取ります。
「ユーザって言っても、自分は一人やし、1対多なんじゃないの?」と疑問に思われる方もいらっしゃるかと思いますが、半分合ってます!前回も使わせてもらいましたが、こちらの記事を参考にしてみてください。めっちゃ分かりやすいです。
https://qiita.com/kazukimatsumoto/items/14bdff681ec5ddac26d1多対多のモデルの構造の場合、中間にテーブルを新しく作成し、相互のやり取りをするのが一般的です。
全体像
モデル
User(ユーザ)
name:string amail:string password_digest:stringRelationship (中間テーブル)
user:references follow:referencesユーザ同士のフォロー機能なので、中間テーブルをはさむことによって、
モデルの構造はUser-Relationship-Userのようになります。
User(フォローする側)-Relationship-User(フォローされる側)と分けることが出来ます。
もう一つ細かく分けると、
User(フォローする側)-(フォローするための中間テーブル)Relationship(フォローされるための中間テーブル)-User(フォローされる側)
となります。モデルの構成で表してみると、
User(フォローする側)-(user)Relationship(follow)-User(フォローされる側)
こうなります。User(フォローする側)-(user)Relationship
User(フォローされる側)-(follow)Relationship
とも捉えることができます。(捉え方多すぎ!笑)こうすると1対多+1対多となっているように見えてきませんか?
実は、多対多は、中間テーブル(多)を挟んだ1対多+1対多なのです!モデルの作成
上記の構成を参考に、モデルの作成をしましょう。
マイグレーションファイルでの設定
中間テーブルのモデルの下記のコードのマイグレーションファイルが出来上がっているかと思います。
db/migrate/年月日時_create_relationships.rbclass CreateRelationships < ActiveRecord::Migration[5.0] def change create_table :relationships do |t| t.references :user, foreign_key: true t.references :follow, foreign_key: true t.timestamps end end enduserもfollowもUserテーブルに紐づけたいです。
railsでは、別のテーブル名をモデルのカラム名に設定すると、自動で参照してくれます。なので、t.references :user は、Userテーブルを参照することが出来ます。t.references :followをUserテーブルに紐づけたい。。
そんなときは、
t.references :follow, foreign_key: { to_table: :users }
こう記述すればfollowをUserテーブルに紐づけることが出来ます!
下記のコードに書き換えます。db/migrate/年月日時_create_relationships.rbclass CreateRelationships < ActiveRecord::Migration[5.0] def change create_table :relationships do |t| t.references :user, foreign_key: true t.references :follow, foreign_key: { to_table: :users } t.timestamps t.index [:user_id, :follow_id], unique: true end end endここで、
t.index [:user_id, :follow_id], unique: true
というコードを追記しています。unique: trueを記述することで、フォローとフォロワーがごっちゃにならないよにしています。モデル
モデルのコードを見てみましょう。
まず、中間テーブルから。
app/models/relationship.rbclass Relationship < ApplicationRecord belongs_to :user belongs_to :follow, class_name: 'User' end多対多の構造は、細かく見ていくと
1対多+1対多
だと説明しました。なので、relationshipテーブル(中間テーブル)では、haa_manyではなく、belongs_to でUserテーブルと紐づけてます。Userテーブルで、has_manyでrelationshipテーブルと紐づけることで多対他になります。
また、Userテーブルと各カラムを紐づけたいのですが、このままの状態ではuserカラムしか紐づいていないです。(railsの機能でカラムと同じ名前のテーブルを探しにいくため)
belongs_to :follow, class_name: 'User'ですが、class_name: 'User'と追加で指定することで、followカラムもUserテーブルを参照することが出来るようになります。Userテーブルを見てみましょう!
app/models/user.rbclass User < ApplicationRecord has_many :microposts has_many :relationships has_many :followings, through: :relationships, source: :follow has_many :reverses_of_relationship, class_name: 'Relationship', foreign_key: 'follow_id' has_many :followers, through: :reverses_of_relationship, source: :user end
has_many :micropostsは、投稿機能を追加したときに作成したコードです。下の4つについて見ていきましょう。
has_many :relationshipsで、relationshipsテーブルから、データを取得してくることが出来ます。1対多の関係です。
自分がフォローしているユーザを取得します。
railsでは、同じ名前のテーブル・カラムがあると自動で取得してくれるので、RelationテーブルにUserカラムの情報を取得していることになります。一行飛ばして、
has_many :reverses_of_relationship, class_name: 'Relationship', foreign_key: 'follow_id'を見てみましょう。
reverses_of_relationshipというのは自由に決められる名前です。
class_name: 'Relationship'では、Relationshipテーブルからデータを取ってきてるということを表しています。
そして、foreign_key: 'follow_id'でfollow_idのデータですよ!というようになります。
has_many :reverses_of_relationship, class_name: 'Relationship', foreign_key: 'follow_id'
では、reverses_of_relationshipという名前の箱に、Relationshipテーブルを通して、follow_idのデータが入ると言えます。
では、
has_many :followings, through: :relationships, source: :follow has_many :followers, through: :reverses_of_relationship, source: :user endについて見ていきます。
throughとsourceがミソになってきます。
through: :中間テーブル, source: :カラム名
で、中間テーブルに設定されているカラムのデータを引っ張てくることが出来ます。今回のように、ユーザがフォローしているユーザを取得したければ、中間テーブルを通過することで、
has_many :followings, through: :relationships, source: :follow
は、ユーザ(user)がフォローしているユーザ(followings)を取得
has_many :followers, through: :reverses_of_relationship, source: :user
end
で、ユーザ(User)のことをフォローしているユーザ(followers)を取得することが出来ます。例えば、ユーザ(user)がフォローしているユーザ(followings)を取得したい場合、
@user = User.find(params[:id]) @followings = @user.followings.page(params[:page])というような記述の仕方も可能になります。
- 投稿日:2019-07-19T16:47:34+09:00
【Rails on Rails】.envの環境変数をjsファイル内で利用する方法
はじめに
Rails内で環境変数を.envファイル内に書いておくと、ENV["環境変数名"]で利用することが出来ます。
しかし、erbファイル内では利用することが出来ますが、jsファイルで利用することが出来ません。
そこで、jsファイル内で環境変数を利用する方法を解説します。
gemのインストール
以下の2つのgemをインストールします。
Gemfileに以下の2つのgemを記入し、
Gemfilegem 'gon' gem 'dotenv-rails'bundle installします。
bundle install.envファイルの作成作成
jsファイル内で利用したい、環境変数を.envファイルに記入します。
.envKEY = "xxx".gitignoreファイルを編集
念の為、GithubなどにAPI KEYなどの環境変数が公開されていように設定します。
.envに以下の1行を追加します。.gitignore/.envapplication.html.erbの編集
application.html.erbのhead内に以下の1行を追加します。
application.html.erb<%= include_gon %>
【注意】javascriptより上に書くこと
コントローラーの編集
環境変数を利用したいコントローラーのメソッドに追記。
xxx_controller.erbdef xxx gon.xxx_key = ENV['KEY'] end
KEYは.envに記述したもの
jsファイルの編集
xxx.jsconst KEY = gon.xxx_key;// 環境変数これでjsファイル内で環境変数を利用することが出来ます。
参考リンク
- 投稿日:2019-07-19T16:41:50+09:00
読みたい・読んで良かった本
前提として、筆者はRailsメインで開発やっていてプログラマ歴3年目です。
2年ちょっと受託開発の会社に勤めており、自社サービス企業に転職しました。役割や経験、バックグラウンドによって必要な知識は異なると思うので「ふーん」ぐらいに見ておいてください。
▼運用しやすいコードを書くために
●設計について
オブジェクト指向設計の成り立ちや有用性を知ることがまず第一歩。
そこから発展して拡張しやすい設計についてを知る必要があり、具体的な手法としてのドメイン駆動開発(DDD)についてという順序で学ぶと良さそう。
- オブジェクト指向でなぜ作るのか
- Clean Code アジャイルソフトウェア達人の技
- Clean Architecture 達人に学ぶソフトウェアの構造と設計
- エリック・エヴァンスのドメイン駆動設計
- 実践ドメイン駆動設計
●設計原則
オブジェクト指向設計実践ガイドが参考になる。
暗記というよりは「なんかこんな原則あったな。違反してないかな?」という確認のために軽く頭に入れておいて定着するまでなんどもコードを見直して改善するみたいな運用じゃないと定着しないと思います。
- SOLID原則
オブジェクト指向設計のガイドラインの集合体。
5つの原則の頭文字をとってSOLIDと呼ばれる。
英名 和名 概要 Single Responsibility Principle 単一責任の原則 一つのクラスが持つ責任は一つでなければならない Open/closed principle 解放閉鎖の原則 ソフトウェアのエンティティ(クラス、モジュール、関数)は拡張に対して開き、修正に対して閉じていなければならない Liskov substitution principle リスコフの置換原則 サブクラスは、そのスーパークラスで代用可能でなければならない Interface segregation principle インターフェース分離の原則 顧客に特化した細粒度のインタフェースを作れ。顧客は自分たちが使わないインターフェースに依存することを強いられるべきではない Dependency inversion principle 依存性逆転の原則 依存は具象から抽象に向かって行われなければならない - デメテルの法則
3つ目のオブジェクトにメッセージを送る際、異なる型の2つ目のオブジェクトを介することを禁じる
- DRY原則
Dot't Repeat Yourself
コードの定義は1ヶ所でのみ行い、複製ではなく参照せよ- YAGNI原則
You Ain't Going to Need it.
必要になるまで実装するな。
将来への予想を過度に行って先回りして実装するな。●デザインパターン
Gang of Fourと呼ばれる4人が定義した、コードの設計によく見られるパターン集。
いわゆるGoF本。
デザインパターンは23パターンあり、パターンの適用ができる場合には設計をより柔軟なものにすることができる。過度に適用しようとするのは危険だけどこれも原則同様知っておくと強力な武器になると思う。
●読みやすいコードを書く
Clean CodeやClean Architectureはこっちに被る気がしないでもない。
コード書く時間のほとんどは読んでる時間なので、拡張しやすさや開発の楽しさにおいて読みやすさは生命線。
コード規約とかベストプラクティスも併用するといいと思うのでRubocopやRails Best Practicesは積極的に活用すると良いです。●テストを書く
- どのようにテストを導入・運用していくのか
テストを意識していないコードに対してテスト書こうとするとだいたいはテストがコードに依存する(コードが複雑なコンテキストを要求するとか)状態になったり、そもそもプロセスの見直しが必要になったりとかで、問題として単体で完結しづらい。
テストが書きやすい設計手法を知る以外にも、導入方法について包括的に知る、あるべきプロセスについて学ぶなども必要そう。
▼効率のいいコードを書くために
●SQLの効率を高める
遅くなる原因のだいたいはSQL。アンチパターンを知って避けるだけでも改善はしやすい。
SQLアンチパターンが参考になる。
●コードの効率を下げる要因を知る
Rails Best Practicesがヒントになりそう。
gemで静的解析挟んでコードレビューとか。▼効率よくコードを書くために
- コーディングを支える技術
- エディタやIDEの使い方について学ぶ。ショートカットを学ぶとか、Qiitaの記事を見るとか。
▼Ruby・Railsについて学ぶ
- プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで
- メタプログラミングRuby 第2版
- Rubyベストプラクティス -プロフェッショナルによるコードとテクニック
- Effective Ruby
- 現場で使える Ruby on Rails 5速習実践ガイド
▼安全なWebアプリケーションを作る
▼Webアプリケーションの仕組みについて知る
▼プログラマとしての原理原則や心構えを学ぶ
エッセイ形式で読みやすいものが多い。
避けるべきパターンがわかればプロセスについて組み立てたり問題を事前に察知したりしやすい。▼効率のいい開発・運用体制を構築する
Docker、Kubernetes、CIツール、ChatOps、監視ツール(サーバー監視、アプリケーション監視、ログ監視)あたりを押さえておけばかなり効率化が図れるようになるんじゃないかと思います。
- Docker/Kubernetes 実践コンテナ開発入門
- SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム
- 入門 監視 ―モダンなモニタリングのためのデザインパターン
- Effective DevOps ―4本柱による持続可能な組織文化の育て方
▼開発手法・開発体制について学ぶ
個人の能力をさらに活かすためにはプロセスやチームのあり方が重要になる。
カイゼンジャーニーが一番実践的。
- カイゼン・ジャーニー たった1人からはじめて、「越境」するチームをつくるまで
- チーム開発実践入門 ~共同作業を円滑に行うツール・メソッド
- スクラム 仕事が4倍速くなる“世界標準”のチーム戦術
- SCRUM BOOT CAMP THE BOOK
- アジャイルサムライ達人開発者への道
- Fearless Change アジャイルに効く アイデアを組織に広めるための48のパターン
▼組織・プロセスについて学ぶ
開発チームの改善とかを考え始めると結局組織のあり方・プロセスのあり方そのものに行き着くなと思うので。
- 投稿日:2019-07-19T16:06:36+09:00
[Rails]C3.js × gonでチャートを表示する
できるだけ簡単に、かつクオリティの高いチャートを表示したい・・・。
そんなあなたにはC3.jsがオススメです。
また、データベースやコントローラーにあるデータを表示したい場合はgemgonを使うと便利です。今回は、C3.jsとgonを使った以下のような棒グラフの描画方法を紹介します。
環境
バージョン
$ ruby -v ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin18] $ rails -v Rails 5.2.3導入gem
gonを導入します。gem 'gon' gem 'haml-rails'ビューにはhamlを採用しています。
ライブラリとgonの読み込み
C3.jsはD3.jsのラッパーライブラリーなのでC3.jsと一緒にD3.jsを読み込みます。
今回はCDNを使って読み込むことにしました。(もちろん、gemとして読み込むこともできます。RailsでC3.jsを使う)
また、gonを適用するためにheadタグの中で以下のように追記します。application.html.haml# jsファイルよりも前に読み込む = Gon::Base.render_data %link{ href: "https://cdnjs.cloudflare.com/ajax/libs/c3/0.6.7/c3.css", rel: "stylesheet"} %script{src: "https://d3js.org/d3.v4.min.js", type: "text/javascript"} %script{src: "https://cdnjs.cloudflare.com/ajax/libs/c3/0.6.7/c3.js", type: "text/javascript"}ビュー
チャートを挿入したい要素にIDを指定しておきます。
html.haml.container #bar / ここにチャートを挿入コントローラー
gon.◯◯という変数に値を代入することでjsファイルで呼び出すことができます。controller.rbgon.x = ["x","07/13","07/14","07/15","07/16","07/17","07/18","07/19"] gon.cash = ["現金",2000000,1000000,1000000,1000000,1000000,1000000,1000000] gon.crypto = ["仮想通貨",8000000,9000000,10000000,8000000,6000000,4000000,2000000]わかりやすくするため値を直接代入していますが、
コントローラーでDBの情報を取得してjsに変数を渡したいときに利用すると便利です。jsファイル
同様に
gon.◯◯とすることでコントローラで定義した変数を取り出すことができます。window.onload = function() { // 棒グラフを挿入 var barGraph = c3.generate({ bindto: '#bar', // 挿入する要素のIDを指定(デフォルトは#chart) data: { x : 'x', columns: [ // コントローラーで定義した変数の呼び出し gon.x, //x軸のメモリに表示する文字列 gon.cash, // 値1 gon.crypto, // 値2 ], groups: [ // 値1と値2を連結させた棒グラフを表示 ['現金', '仮想通貨'] ], type: 'bar' // チャートタイプを指定(今回は棒グラフ) }, axis: { x: { type: 'category' // x軸のメモリ(今回は日付)を読み込むために必要 }, y: { label: { // y軸のラベルを追加 text: '価格 / 円', position: 'outer-middle' }, tick: { // 3桁ずつ「,」を挿入 format: d3.format(",") } } }, // チャートの余白を指定できる padding: { top: 10, bottom: 0 } }); }指定できるチャートタイプやその他設定は公式リファレンスがわかりやすいです。C3.js
gonの仕組み
application.html.hamlのheadタグの中で
Gon::Base.render_dataを記述した場所がどう表示されているかを確認してみます。html<script> //<![CDATA[ window.gon={}; gon.x=["x","07/13","07/14","07/15","07/16","07/17","07/18","07/19"]; gon.cash=["現金",2000000,1000000,1000000,1000000,1000000,1000000,1000000]; gon.crypto=["仮想通貨",8000000,9000000,10000000,8000000,6000000,4000000,2000000]; //]]> </script>
window.gon={}とすることでgonというグローバル変数を定義し、空のオブジェクトを代入して初期化しています。(グローバル変数はwindowオブジェクトのプロパティです)
そして、コントローラーで指定した値をgonのプロパティとして代入しています(windowは省略可能)。
これにより、jsファイルで変数を読み込むことが可能になります。参考
- 投稿日:2019-07-19T13:40:40+09:00
Selenium::WebDriver::Element#click が position: fixed のヘッダやフッタに邪魔されて失敗するときに回避する方法
事象
ヘッダとフッタが
position: fixed;で固定されたUIを持ったWebページのe2eテストとか書いてるとクリックできないよーってエラーがよく起きる。こんな感じのUIで:See the Pen fixed header and footer by oieioi (@oieioi) on CodePen.
このページに対してこんなスクリプトを実行すると:
test.rbrequire 'selenium-webdriver' driver = Selenium::WebDriver.for :chrome driver.navigate.to "http://127.0.0.1:8081/" element = driver.find_element(name: 'button') element.click driver.quitこんなエラーになる。 fixed された
<footer>とクリックしたい<button>が被っているためクリックできないという。Element <button name="button">...</button> is not clickable at point (40, 881). Other element would receive the click: <footer>...</footer>解決策
目標をセンターに入れてクリックする。
色々方法はあるが、今回はJavaScriptのElement.scrollIntoView() を使って解決した。以下を
clickする前に実行すれば目標の要素を真ん中になるようにスクロールするので、クリックできるようになる。driver.execute_script("arguments[0].scrollIntoView({behavior: 'auto', block: 'center', inline: 'nearest'});", element)で、この動作を
clickをモンキーパッチして行う Gem をつくりました。https://github.com/oieioi/selenium-webdriver-element-extend_click_again
test.rbrequire 'selenium-webdriver' require 'selenium/webdriver/element/extend_click_again' ...たまに便利です。
- 投稿日:2019-07-19T13:40:40+09:00
Selenium::WebDriver::Element#click が position: fixed のヘッダやフッタに邪魔されて not clickable になるとき回避する方法
事象
ヘッダとフッタが
position: fixed;で固定されたUIを持ったWebページのe2eテストとか書いてるとクリックできないよーってエラーがよく起きる。こんな感じのUIで;See the Pen fixed header and footer by oieioi (@oieioi) on CodePen.
このページに対してこんなスクリプトを実行すると;
test.rbrequire 'selenium-webdriver' driver = Selenium::WebDriver.for :chrome driver.navigate.to "http://127.0.0.1:8081/" element = driver.find_element(name: 'button') element.click driver.quitこんなエラーになる。 fixed された
<footer>とクリックしたい<button>が被っているためクリックできないという。Element <button name="button">...</button> is not clickable at point (40, 881). Other element would receive the click: <footer>...</footer>解決策
目標をセンターに入れてクリックする。
色々方法はあるが、今回はJavaScriptの
Element.scrollIntoView()を使って解決した。以下をclickする前に実行すれば目標の要素を真ん中になるようにスクロールするので、クリックできるようになる。driver.execute_script("arguments[0].scrollIntoView({behavior: 'auto', block: 'center', inline: 'nearest'});", element)で、この動作を
clickをモンキーパッチする Gem をつくりました。https://github.com/oieioi/selenium-webdriver-element-extend_click_again
使い方は
requireするだけです。test.rbrequire 'selenium-webdriver' require 'selenium/webdriver/element/extend_click_again' ...たまに便利です。
- 投稿日:2019-07-19T13:40:40+09:00
Selenium::WebDriver::Element#click がfixedのヘッダやフッタに邪魔されて失敗するとき回避するパッチを作った
事象
ヘッダとフッタが
position: fixed;で固定されたUIを持ったWebページのe2eテストとか書いてるとクリックできないよーってエラーがよく起きる。こんな感じのUIで:See the Pen fixed header and footer by oieioi (@oieioi) on CodePen.
このページに対してこんなスクリプトを実行すると:
test.rbrequire 'selenium-webdriver' driver = Selenium::WebDriver.for :chrome driver.navigate.to "http://127.0.0.1:8081/" element = driver.find_element(name: 'button') element.click driver.quitこんなエラーになる。 fixed された
<footer>とクリックしたい<button>が被っているためクリックできないという。Element <button name="button">...</button> is not clickable at point (40, 881). Other element would receive the click: <footer>...</footer>解決策
目標をセンターに入れてクリックする。
色々方法はあるが、今回はJavaScriptのElement.scrollIntoView() を使って解決した。以下を
clickする前に実行すれば目標の要素を真ん中になるようにスクロールするので、クリックできるようになる。driver.execute_script("arguments[0].scrollIntoView({behavior: 'auto', block: 'center', inline: 'nearest'});", element)で、この動作を
clickをモンキーパッチして行う Gem をつくりました。https://github.com/oieioi/selenium-webdriver-element-extend_click_again
test.rbrequire 'selenium-webdriver' require 'selenium/webdriver/element/extend_click_again' ...たまに便利です。
- 投稿日:2019-07-19T12:38:44+09:00
Rails6 のちょい足しな新機能を試す56(create_table if_not_exists編)
はじめに
Rails 6 に追加されそうな新機能を試す第56段。 今回は、
create_table if_not_exists編です。
Rails 6 では、create_tableに:if_not_existsオプションが追加されました。Ruby 2.6.3, Rails 6.0.0.rc1 で確認しました。Rails 6.0.0.rc1 は
gem install rails --prereleaseでインストールできます。$ rails --version Rails 6.0.0.rc1マイグレーションファイルを作る
$ bin/rails g migration CreateUser nameマイグレーションファイルを修正する
:if_not_existsオプションを指定します。db/migrate/20190713221959_create_user.rbclass CreateUser < ActiveRecord::Migration[6.0] def change create_table :users, if_not_exists: true do |t| t.string :name end end endデータベースを作成する
データベースを作成します。
$ bin/rails db:createpsql でテーブルを作ります
先に users テーブルを psql で作成します。
ちょっと手抜きで id カラムは省略します。app_development=# create table users ( name varchar(256) ) CREATE TABLEテーブルができたことを確認しておきます。カラムの情報も確認します。
app_development=# \dt List of relations Schema | Name | Type | Owner --------+----------------------+-------+---------- public | ar_internal_metadata | table | postgres public | schema_migrations | table | postgres public | users | table | postgres (3 rows) app_development=# \d users; Table "public.users" Column | Type | Collation | Nullable | Default --------+------------------------+-----------+----------+--------- name | character varying(256) | | |マイグレーションを実行する
マイグレーションを実行します。エラーは発生しません。
$ bin/rails db:migrate == 20190713221959 CreateUser: migrating ======================================= -- create_table(:users, {:if_not_exists=>true}) -> 0.0019s == 20190713221959 CreateUser: migrated (0.0019s) ==============================psql で users テーブルを確認すると id カラムが無いので、マイグレーションによってテーブルはできなかったことがわかります。
app_development=# \d users; Table "public.users" Column | Type | Collation | Nullable | Default --------+------------------------+-----------+----------+--------- name | character varying(256) | | |ロールバックする
ロールバックしてみます。
$ bin/rails db:rollback == 20190713221959 CreateUser: reverting ======================================= -- drop_table(:users, {:if_not_exists=>true}) -> 0.0031s == 20190713221959 CreateUser: reverted (0.0043s) ==============================psql で確認すると users テーブルが削除されていることがわかります。
app_development=# \dt List of relations Schema | Name | Type | Owner --------+----------------------+-------+---------- public | ar_internal_metadata | table | postgres public | schema_migrations | table | postgres (2 rows)マイグレーションを実行する
users テーブルが無い状態でマイグレーションしてみます。
$ bin/rails db:migratepsql で確認するとテーブルが作られていることがわかります。
app_development=# \dt List of relations Schema | Name | Type | Owner --------+----------------------+-------+---------- public | ar_internal_metadata | table | postgres public | schema_migrations | table | postgres public | users | table | postgres (3 rows)カラムの情報を確認すると id カラムも存在しており、 migration によって作られたことがわかります。
app_development=# \d users; Table "public.users" Column | Type | Collation | Nullable | Default --------+-------------------+-----------+----------+----------------------------------- id | bigint | | not null | nextval('users_id_seq'::regclass) name | character varying | | | Indexes: "users_pkey" PRIMARY KEY, btree (id)テーブルを削除してからロールバックする
psql で users テーブルを削除します。
app_development=# drop table usersロールバックするとエラーが発生します。
$ bin/rails db:rollback == 20190713221959 CreateUser: reverting ======================================= -- drop_table(:users, {:if_not_exists=>true}) rails aborted! StandardError: An error has occurred, this and all later migrations canceled: PG::UndefinedTable: ERROR: table "users" does not existエラーを回避するには
エラーを回避するには、
db/migrate/20190713221959_create_user.rbclass CreateUser < ActiveRecord::Migration[6.0] def change create_table :users, if_not_exists: true, if_exists: true do |t| t.string :name end end endとするか
db/migrate/20190713221959_create_user.rbclass CreateUser < ActiveRecord::Migration[6.0] def up create_table :users, if_not_exists: true do |t| t.string :name end end def down drop_table :users, if_exists: true end endとすれば良いようです。後者の方が直感的でわかりやすいので、個人的にはオススメです。
(というか前者は訳わからんし、将来、 ActiveRecord の動作が変わったりすると痛い目に合いそうです。)試したソース
試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try056_create_table_if_not_exists参考情報
- 投稿日:2019-07-19T11:56:18+09:00
An error occurred while installing mysql2 (0.5.2), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.5.2' --source 'https://rubygems.org/'` succeeds before bundling.と出てきたときの対処法
最近の勉強で学んだ事を、ノート代わりにまとめていきます。
主に自分の学習の流れを振り返りで残す形なので色々、省いてます。
Webエンジニアの諸先輩方からアドバイスやご指摘を頂けたらありがたいです!どういうエラーなのか
railsのリポジトリをクローンしてきてちゃんと動くか確かめようとした時に発生しました!
やったことはbundle installをしたら最後に以下のような感じのものが出てきました//省略 An error occurred while installing mysql2 (0.5.2), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.5.2' --source 'https://rubygems.org/'` succeeds before bundling.エラーに対応策が書いていたので
gem install mysql2 -v '0.5.2' --source'https://rubygems.org/'そのまま入力したのですが解決されず
なんなんやこれはって思っていたら以下の記事を見つけました。恐らくほとんど一緒のエラー?
なのかと思うのですが記事に書いてあるとの解決策を試したところ問題解決しました。
mysql2 gemインストール時のトラブルシュート$ gem install mysql2 -v '0.5.2' --source 'https://rubygems.org/' -- --with-cppflags=-I/usr/local/opt/openssl/include --with-ldflags=-L/usr/local/opt/openssl/lib Building native extensions with: '--with-cppflags=-I/usr/local/opt/openssl/include --with-ldflags=-L/usr/local/opt/openssl/lib'このように入力したらやっと問題なくbundle installできました。
Railsではこのようなエラーに遭遇することが多いのですがなぜ、そもそもこのようなエラーが起きているのかの原因究明を参考にした記事のようにできていなかったので全てできるかは分からないですが徐々にしていきたいと思います。
- 投稿日:2019-07-19T11:31:43+09:00
nokogiri が macOS でインストールできない
環境
- macOS High Sierra 10.13.6
- Ruby 2.3.8 (わけあって古い)
現象
macOS で nokogiri を
bundle installすると、事前にbrew install libxml2しているにもかかわらず libxml2 が見つからないと言われて失敗します。Gem::Ext::BuildError: ERROR: Failed to build gem native extension. current directory: 省略 checking if the C compiler accepts ... yes checking if the C compiler accepts -Wno-error=unused-command-line-argument-hard-error-in-future... no Building nokogiri using system libraries. Using pkg-config gem version 1.1.9 checking for libxml-2.0... no checking for libxslt... no checking for libexslt... no ERROR: cannot discover where libxml2 is located on your system. please make sure `pkg-config` is installed. *** extconf.rb failed ***対処
既知の対処法として、下記を事前に実行していましたが、うまくいきませんでした。いろいろググってみましたがわからず。
bundle config build.nokogiri --use-system-librariesが nokogiri 公式 を確認すると、以下を指定するようにとあり、そのとおりにしたらうまくいきました。
bundle config build.nokogiri --use-system-libraries \ --with-xml2-include=$(brew --prefix libxml2)/include/libxml2結論
公式は見よう。
- 投稿日:2019-07-19T10:32:58+09:00
Pay.jpを使用してクレジットカード登録削除機能の実装をしてみた
はじめに
軽く自己紹介から
TECH::EXPRETというプログラミングスクールに通っております。
53期で活動して、そろそろ終わりに近づいてきましたので、実装した部分くらいは
アウトプットするかーってことで、やります。
テストはやってないので、それ目的の方はごめんなさい。今回この記事を書く目的は3つほどあります。
1.チームメンバーに学んだ技術の共有の為
なんか作ったらわかりやすいかなーと思ってます
2.就活で学んだことをしっかりアウトプットできますよとアピールする為
話すよりも論理的に組み立てているか、全体像をみて考えているかをこういうところでみてもらう為
3.アウトプットを通して、今回学んだ内容をしっかり定着させる為
ぶっちゃけ忘れているところが多々あります笑 思い出すためにもしっかり書きます笑
開発環境
ruby 2.5.1
rails 5.2.1
mysqlゴール
以下のように登録をrails5でできるようにしましょう
前提準備
deviseでのユーザログイン機能の実装
処理の概要
1) pay.jpのgemをインストール
作業目的:Pay.jpを使用できるようにする為
Gemfilegem 'payjp'2) Pay.jpの公式のjavascriptを読み込ませられるようする
作業目的:Pay.jpの公式のjavascriptを読み込ませられるようする為
app/views/layouts/application.html.haml%script{src: "https://js.pay.jp/", type: "text/javascript"}3) クレジットカードテーブルを作成する
作業目的: クレジットカード情報を保存するテーブルを作成
Colun Type Options 意味 user_id references null: false, foreign_key: true ログインユーザ costomer_id integer null: false 顧客ID情報(pay.jpから返ってくるデータ) card_id integer null: false カードID情報(pay.jpから返ってくるデータ) costomer_id, card_idは、カード情報(16桁のやつ),有効期限年月,セキュリティーコードを渡すとpay.jpから返ってくるデータのこと
db/migrate/***************_create_credit_cards.rbclass CreateCreditCards < ActiveRecord::Migration[5.2] def change create_table :credit_cards do |t| t.references :user ,foreign_key: true, null: false t.string :customer_id, null: false t.string :card_id, null: false t.timestamps end end endなんで、DBにカード情報とか保存しないの?っと疑問に持たれる人もいると思いますので、ここで解説
以下のURLを参照していただければわかるのですが、
Pay.jp側の方で、
セキュリティーの観点から
クレジット情報は、勝手にDBに保存するな。俺が管理する。お前らにはIDやるから我慢しろ。っと言われているからなんですよねー
めんどいですが、クレジット情報や購入情報は毎回Pay.jpに問い合わせる必要があります。http://payjp-announce.hatenablog.com/entry/2017/11/10/182738
4) クレジットカード情報の追加と削除のコントローラを作っていきます
1つのget、3つのpostの実装
get
- edit
post
- create
payjpとCardのデータベース作成を実施します。- delete
payjpとCardのデータベースを削除する。- show
DBのCreditCard情報を、payjpに送りcustomer情報を取り出すために実装感想
まだ途中です。。。。。
- 投稿日:2019-07-19T07:11:01+09:00
【Programming News】Qiitaまとめ記事 July 18, 2019 Vol.4
筆者が昨日2019/7/18(木)に気になったQiitaの記事をまとめました。昨日のまとめ記事はこちら。
Java
Python
- Beginner
- Tips
- K近傍法
Node.js
Nuxt.js
Swift
Laravel
MySQL
- Beginner
Git
- Beginner
Visual Studio
AWS
- Beginner
- Tips
- Cognito
- S3
- EC2
- Elasticsearch
Docker
Google Apps Script
- Tips
Develop
Raspberry
- Tips
- Apps
UML
- PlantUML
Go言語
- Beginner
- Tips
R言語
awk
LaTex
Redmine
- Tips
更新情報
- 投稿日:2019-07-19T04:00:24+09:00
Railsでメタプログラミング(黒魔術)と呼ばれるsendメソッドを活用してみた
はじめに
この記事を書こうと思ったのはsendメソッドを活用するイメージが湧かない方に、僕はRailsでこんな感じで使う場面がありましたよー的な意味で紹介するために書きました。
sendメソッドを使うのが最善かどうかは置いといて...笑メタプログラミングとは?
まずメタプログラミングの意味を軽く紹介します。
Railsチュートリアルではこのように説明されてます。メタプログラミングを一言で言うと「プログラムでプログラムを作成する」ことです。メタプログラミングはRubyが有するきわめて強力な機能であり、Railsの一見魔法のような機能 (「黒魔術」とも呼ばれます) の多くは、Rubyのメタプログラミングによって実現されています。
プログラムでプログラムを作成する?ちょっとこの説明だけ読んでもよくわからないですね笑
Railsチュートリアルでは実際にsendメソッドというものを利用してメタプログラムを作成していますのでそちらを見るとよりイメージが湧くかも。
※この後実際に例を用いて説明しますsendメソッドとは?
Rubyにはsendというメソッドがあります。
どのように使うか簡単な例で説明します。例えば、upcaseという文字列を大文字に変換して出力するメソッドを例に使います。
string = "ruby" #=> "ruby" string.upcase #=> "RUBY" string.send(:upcase) #=> "RUBY" string.send("upcase") #=> "RUBY"このようにsendメソッドの引数に呼び出したいメソッド名のシンボルか文字列を渡すと通常通りメソッドの呼び出しが起こります。
では次に引数を持つメソッドを呼び出したい時にどのようにするか、splitメソッド例を出しましょう。
Railsチュートリアルではここまでは説明されていませんが意外と簡単です。splitメソッドは文字列などを引数に渡した区切りで分割して配列にしてくれます。
string = "aaaaaxbbbbbxccccc" #=> "aaaaaxbbbbbxccccc" string.split("x") #=> ["aaaaa", "bbbbb", "ccccc"] string.send(:split, "x") #=> ["aaaaa", "bbbbb", "ccccc"] string.send("split", "x") #=> ["aaaaa", "bbbbb", "ccccc"]引数をメソッドに渡したい場合sendメソッドの第二引数以降に渡します。
Railsでsendメソッド活用
では本題に入ります。
RailsでECサイトを作っていたのですが、3つのモデル(Artist, Label, Genre)のデータを一覧表示するviewがほぼ同じだったのでこれをパーシャル化してしまおうという場面で使いました。
どのようなレイアウトか一応簡単なモックアップを載せておきます。(かなり雑なのは気にしないでください笑)モックアップ
うん、これはもう表示するデータしかほぼ変わらないですね。これをパーシャル化せずに何をする?ということでパーシャル化しました。
コントローラ
app/controllers/artists_controller.rb# 中略 def index @artists = Artist.page(params[:page]) @artist = Artist.new end # 中略app/controllers/genres_controller.rb# 中略 def index @genres = Genre.page(params[:page]) @genre = Genre.new end # 中略app/controllers/labels_controller.rb# 中略 def index @labels = Label.page(params[:page]) @label = Label.new end # 中略ビュー
app/views/artists/index.html.erb<h2>アーティスト一覧</h2> <%= render 'layouts/object_list', objects: @artists, object: @artist %>app/views/genres/index.html.erb<h2>ジャンル一覧</h2> <%= render 'layouts/object_list', objects: @genres, object: @genre %>app/views/labels/index.html.erb<h2>レーベル一覧</h2> <%= render 'layouts/object_list', objects: @labels, object: @label %>app/views/layouts/_object_list.html.erb<div class="row"> <div class="col-sm-5"> <%= render 'layouts/error', object: object %> <%= form_with model: object, local: true do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.submit "登録" %> <% end %> </div> <div class="col-sm-7"> <div class="index-wrapper"> <% object_name = object.class.to_s.downcase # それぞれのオブジェクトのクラス名を変数に代入 %> <% objects.each do |data| %> <% edit_path = self.send("edit_#{object_name}_path", data) ### ここで使用!!### %> <% destroy_path = self.send("#{object_name}_path", data) ### ここで使用!!### %> <%= data.name %> <%= link_to "編集", edit_path %> <%= link_to "削除", destroy_path, method: :delete, data: { "confirm" => "本当に削除しますか?" } %> <% end %> </div> <%= paginate objects, class: "pagination" %> </div> </div>それぞれの
index.html.erbで書かれているパーシャルテンプレートの呼び出しは大丈夫だと思います。(わからない方はググってください)
_object_list.html.erbのコードで今回テーマのsendメソッドに関係するコードだけ説明していきます。解説
まずそれぞれ渡されたオブジェクトのクラス名(全部小文字)を変数に代入します。
何故かは後々わかります。<% object_name = object.class.to_s.downcase # それぞれのオブジェクトのクラス名を変数に代入 %>一つずつ説明すると、
.class→ 呼び出したオブジェクトのクラスを取得
.to_s→ 文字列に変換
.downcase→ 文字列を小文字に変換なのでまとめると
# 渡ってきた元のインスタンス変数 → object_nameに入る文字列 @artist → "artist" @genre → "genre" @label → "label"となります。
次に本題のsendメソッドのコード
<% edit_path = self.send("edit_#{object_name}_path", data) ### ここで使用!!### %> <% destroy_path = self.send("#{object_name}_path", data) ### ここで使用!!### %>ここでしたいことは、
edit_path,destroy_pathそれぞれにeachメソッドで取り出されるオブジェクト+パーシャルの呼び出しで渡ってきたオブジェクトの種類(artistかgenreかlabel)によって動的にパスを生成し、代入するということです。
つまり、呼び出すメソッドを動的に決めるということ。これがRubyのメタプログラムです。sendメソッドの実行結果は以下のようになります。
# artist edit_path = edit_artist_path(data) destroy_path = artist_path(data) # genre edit_path = edit_genre_path(data) destroy_path = genre_path(data) # label edit_path = edit_label_path(data) destroy_path = label_path(data)ここで、ん?
self.ってなに?てか、railsの~_pathってメソッドなの?メソッドって***.メソッドって感じで何かオブジェクトに対して呼び出すものじゃないの?
って思うかもしれません。特に他の言語(JavaScriptやPHPなど)をやったことがありrubyやrailsを始めたばかりの方なら~_pathは関数じゃないの?って思うかもしれませんが、メソッドです!!
というかRubyには関数がありません。この話をすると長くなってしまうので少し省略しますが、Rubyは全てがオブジェクトであり
~_pathもなんらかのオブジェクトのメソッドになるのです。そして自身のメソッド呼び出す際には
self.メソッドという感じで書きます。しかしこれはself.を省略できてメソッド名だけで呼び出すことが可能です。つまり、
~_pathは普段はself.が省略されていて、実際にはself.~_pathでも呼び出せます。じゃあsendメソッドを呼び出すときも
self.を省略できるのかと。
その通りです。上で紹介したコードはさらに短くこのようにできます。<% edit_path = send("edit_#{object_name}_path", data) ### ここで使用!!### %> <% destroy_path = send("#{object_name}_path", data) ### ここで使用!!### %>今回説明しやすいようにはじめは
self.をつけた状態にしておきましたが、これが短く書くとしたら完成系になります。まとめ
実際sendメソッドなんて使わなくてももっと簡単にできます。
prefix(~_path)を使わずに直接相対パスを書き、その相対パスに変数を入れ込めばそこまで難しいことではないでしょう。
しかし、ただただこの黒魔術と呼ばれるメソッドを使いたかったのです。笑
皆さんも遊び感覚であえて難しい実装方法を選んでみても面白いかもしれませんよ〜。







