カリキュラム1日ビハインドのままだが気にしない。
非同期通信関係でjqueryやajaxなど横断的な内容が多い。
1日振り返って考えると大した分量ではないのだが、脳みそに定着させるには時間がかかる。
ここ数日の吸収量が多いので今日はちょっとカリキュラムは止めて、まとめ作業へ。
普段はつくばの自宅に戻った時にやるんだけれど、あと1週間は戻る予定がないのでここらで纏めないとオーバーフローしちゃう。
クエリ文字列(URLパラメーター)
URLの後ろにつける ?hoge=fugafufga&hoge2=fuagfua のこと。
アクセス解析に使うようなパッシブのものと、
表示内容に影響を与えるアクティブなものがある。
rails
AddIndexToTable
def change
add_index :products, :title
end
test code
changeマッチャ
引数が変化したかどうかを確かめる。
change(Message, :count).by(1)と記述し、Messageモデルのレコードの総数が1個増えたかどうかを確かめる
.to .not_to
以下と同じことを示す場合は.toを。
違うことを示す場合には.not_toを。
it 'does not count up' do
expect{ subject }.not_to change(Message, :count)
end
メモ
冗長ですがcontrollerのテストの雛形を作成するためにベタ張り。
後ほど切り出して分解します。
require 'rails_helper'
describe MessagesController do
let(:group) { create(:group) }
let(:user) { create(:user) }
describe '#index' do
context 'log in' do
before do
login user
get :index, params: { group_id: group.id }
end
it 'assigns @message' do
expect(assigns(:message)).to be_a_new(Message)
end
it 'assigns @group' do
expect(assigns(:group)).to eq group
end
it 'redners index' do
expect(response).to render_template :index
end
end
context 'not log in' do
before do
get :index, params: { group_id: group.id }
end
it 'redirects to new_user_session_path' do
expect(response).to redirect_to(new_user_session_path)
end
end
end
describe '#create' do
let(:params) { { group_id: group.id, user_id: user.id, message: attributes_for(:message) } }
context 'log in' do
before do
login user
end
context 'can save' do
subject {
post :create,
params: params
}
it 'count up message' do
expect{ subject }.to change(Message, :count).by(1)
end
it 'redners index' do
subject
expect(response).to redirect_to(group_messages_path(group))
end
end
context 'can not save' do
let(:invalid_params) { { group_id: group.id, user_id: user.id, message: attributes_for(:message, body: nil, image: nil) } }
subject {
post :create,
params: invalid_params
}
it 'does not count up' do
expect{ subject }.not_to change(Message, :count)
end
it 'renders index' do
subject
expect(response).to render_template :index
end
end
end
context 'not log in' do
before do
get :index, params: { group_id: group.id }
end
it 'redirects to new_user_session_path' do
post :create, params: params
expect(response).to redirect_to(new_user_session_path)
end
end
end
end
FactoryBot
attributes_for
FactoryBotによって定義されるメソッドで、オブジェクトを生成せずにハッシュを生成する
let(:invalid_params) { { group_id: group.id, user_id: user.id, message: attributes_for(:message, body: nil, image: nil) } }
統合テスト
フィーチャスペック
Rspecを使って統合テストを行うためのスペック。
アプリケーションの肝となる動作を総合的にテストしたい場合に用いる。
一部の記述の名前が単体テストと異なる
フィーチャスペックでは、
itの代わりにscenarioを、
beforeをbackground、
describeをfeature、
letをgivenと記述する慣習がある。
これらは全てエイリアスメソッドなので、名前が異なるだけで内部的な実装は全く同じ。
フィーチャスペックでもit・before・describe・letを使える。
どっちで書くかは現場次第。
同一テスト内に複数のexpectを記述。
have_content / have_no_contentマッチャ
引数に指定したバリューを持つHTML要素がそのページに存在する/しないことを確かめるためのマッチャ
Capybara
gem。
ブラウザの操作を再現するのに必要。
特定の要素をクリックしたり、フォームに値を入力したり、特定の要素が画面に表示されているかなど、様々なブラウザ上の動きをテストできる。
Capybaraを導入することで、テストを記述するためのヘルパーメソッドが複数追加される。
visitメソッド
引数にURL、 もしくはプレフィックスを指定し、そのページに移動する。
visit("/hogehoge")
visit hogehoge_path
click_onメソッド
指定したHTML要素をクリックする。
引数にはHTML要素のvalue属性を指定する。
find('input[type="submit"]').click
fill_inメソッド
値を入力する。
入力したいvalueを指定し、with以下に入力する値を入れる。
fill_in 'user_email', with: user.email
fill_in 'text', with: 'フィーチャスペックのテスト'
capybaraの使い方
gemファイルに以下を記述してbundle install。
group :test, :development do
gem 'capybara'
end
spec/rails_helper.rbに以下を追記。
require 'capybara/rspec'
featureSpec用のディレクトリを準備し、以下を作成。
spec/features/tweet_spec.rb
require 'rails_helper'
feature 'tweet', type: :feature do
# このブロックの内部にscenarioを記述していく
end
APIとは
必要なデータだけをJSONなどの形式で返すサーバの仕組みのことをAPI、もしくはJSON APIと呼ぶ。
APIの作り方(rails)
- HTMLを返すためのコントローラーを共用してAPIとして使えるようにする
- API専用のコントローラーを作成する
- APIを作成するためのgemを利用する
コントローラーでの振り分け
Railsには、コントローラーのアクションの中でHTMLとJSONといったフォーマット毎に条件分岐できる仕組みがある。
respond_to
コントローラーで利用できる。
JSONを返す場合はRubyのハッシュの状態のままrenderメソッドに渡すだけでJSONに変換してくれる。インスタンス単体ならそのまま渡してもOK。
respond_to do |format|
format.html { render ... } # この中はHTMLリクエストの場合に呼ばれる
format.json { render ... } # この中はJSONリクエストの場合に呼ばれる
end
respond_to do |format|
format.json {
render json: { id: @user.id, name: @user.name }
}
end
def create
@todo = Todo.new(todo_params)
if @todo.save
respond_to do |format|
format.html { redirect_to :root }
format.json { render json: @todo}
end
else
render :index
end
end
jbuilder
rails newした際にgemfileにデフォルトで記述されているgem。
入力データをJSON形式で出力するテンプレートエンジン。
jbuilderを使用してJavaScriptファイルに返すデータを作成
viewを同じように該当するアクションと同じ名前にする必要がある。
viewと同じように該当コントローラの該当アクション名で.json.jbuilderファイルを作成する。
例
hogehoges_controllerの#createアクションに対応するjbuilderファイルを
view/hogehoges/create.json.builderに作成する。
json.jbuilderファイル内の記法
基本的にjson.KEY VALUEと記述する。
json.text @comment.text
json.user_id @comment.user.id
json.user_name @comment.user.nickname
ちなみにcontroller側の記述で
@comment = Comment.new(params) とせずに
comment = Comment.new(params)
@抜きにした場合、
json.jbuilderファイル内でjson.text comment.textのように記述しても出力できない。
createは通るけれど、viewに投げるには@が符号になっているためこのような処理は通らない。
viewファイルを経由せずにcontroller側でハッシュにしてしまえば通る。
これは通る
@comment = Comment.new()
略
format.json
@をつけない場合。コントローラ内でハッシュにして渡せば通る。
view経由させるなら@は必須。
def create
comment = Comment.create(comment_params)
hoge = {text: comment.text, user_id: comment.user_id }
respond_to do |format|
format.html { redirect_to tweet_path(params[:tweet_id]) }
format.json { render json: hoge}
end
end
json.array!
json形式のデータを配列にしたい場合に使う。
json.array! @products do |product|
json.id product.id
json.title product.title
json.image product.image_url
json.detail product.detail
end
Ajax
$.ajax()は以下の4つの要素を送信する。
この要素の記載に続けて、.doneと.failを記載して送信後の処理を行う。
type | HTTP通信の種類を記述する。通信方法は、GETとPOSTの2種類がある。 |
url | リクエストを送信する先のURLを記述する。 |
data | サーバに送信する値を記述する。 |
dataType | サーバから返されるデータの型を指定する。 |
$(function() {
function buildHTML(todo) {
var html = $('<li class="todo">').append(todo.content);
return html;
}
$('.js-form').on('submit', function(e) {
e.preventDefault();
var textField = $('.js-form__text-field');
var todo = textField.val();
$.ajax({
type: 'POST',
url: '/todos.json',
data: {
todo: {
content: todo
}
},
dataType: 'json'
})
.done(function(data) {
var html = buildHTML(data);
$('.todos').append(html);
textField.val('');
})
.fail(function() {
alert('error');
});
});
});
dataは基本的にハッシュで渡す。
formの場合はハッシュのネストで渡すと通常のフォームと同じ形式になるが、
FormDataオブジェクトで渡すのが普通。
data: { keyword: input },
$.ajax({})の後の.done(function(DATA){})のDATA部分はajaxでリクエストを投げた結果、返ってきた値が入る。
入れる値を指定しているのではなくて、返ってきた値をDATAと命名し、以降の関数内でDATAという変数名で用いる。
通常の引数を渡すという感覚ではなく、引数はすでに持っていてそれに名前をつける感覚。
$.ajax({})
.done(function(DATA){})
JavaScript
window.location.href
現在のURLを返す。
テンプレートリテラル記法
バックティック文字で囲むことで、複数行文字列や文字列内挿入機能を使用できる。
function buildHTML(comment){
var html = `<p>
<strong>
<a href=/users/${comment.user_id}>${comment.user_name}</a>
:
</strong>
${comment.text}
</p>`
return html;
}
較的新しい記述の仕方のため、アプリケーションをデプロイする際にエラーになる。
その場合、config/environments/production.rb
の下記の設定をコメントアウトする。
config.assets.js_compressor = :uglifier
↓
# config.assets.js_compressor = :uglifier
jQuery
gemfileに以下を追記してbundle install
# Use jquery as the JavaScript library
gem 'jquery-rails'
application.jsに以下の記述があるかを確認。
//= require jquery
//= require rails-ujs
注意点として、require tree .より上にjqueryを書くこと。
require tree以下でjsファイルが読み込まれるため、下に書くとjqueryがロードされる前にjqueryの内容を含むjsファイルが読まれ、Uncaught ReferenceErrorを吐く。
FormData
フォームのデータの送信に使用できる。フォームの情報を取得するのに使う。
フォームとは独立して使用することもできます。
$(function(){
$('#new_comment').on('submit', function(e){
e.preventDefault();
var formData = new FormData(this);
console.log(formData)
var url = $(this).attr('action')
$.ajax({
url: url,
type: "POST",
data: formData,
dataType: 'json',
processData: false,
contentType: false
})
})
})
attrメソッド
要素が持つ指定属性の値を返す。
要素が指定属性を持っていない場合、関数はundefinedを返す。
processDataオプション
デフォルトはtrue。
dataに指定したオブジェクトをクエリ文字列に変換する役割がある。
FormDataを扱う場合にはfalseにする。
contentTypeオプション
サーバにデータのファイル形式を伝えるヘッダ。
デフォルトでは「text/xml」でコンテンツタイプをXMLとして返す。
ajaxのリクエストがFormDataのときはどちらの値も適切な状態で送ることが可能なため、falseにすることで設定が上書きされることを防ぐ。
FormDataで扱う場合にはfalseにする。
$(function(){
function buildHTML(comment){
var html = <div> hoge </div>
return html;
}
$('#new_comment').on('submit', function(e){
e.preventDefault();
var formData = new FormData(this);
var url = $(this).attr('action')
$.ajax({
url: url,
type: "POST",
data: formData,
dataType: 'json',
processData: false,
contentType: false
})
.done(function(data){
var html = buildHTML(data);
$('.comments').append(html)
$('.textbox').val('')
})
.fail(function(){
alert('error');
})
})
});
Uncaught ReferenceError: $ is not defined
jqueryを読み込む前にjqueryの記法を使っている場合に起こるエラー。
javaファイルの読み込み順の上位にjqueryを読み込ませると解決する。
emptyメソッド
指定したDOM要素の子要素のみを削除する。
removeメソッドは指定した要素を削除する。
forEachメソッド
配列に含まれる各要素に対して一度ずつ呼び出して処理をする。
products.forEach(function(product){
appendProduct(product);
});
インクリメンタルサーチ
jQueryを有効にする。
jsファイルにonメソッドでアクションを起こし、(form系の処理ならe.priventDefault)
ajaxでurl, type, data, datatypeを投げる。
controllerでrespond_to do |format|で条件分岐。
json.jbuolderファイルが必要なら作成。
viewファイルを参考に返ったデータを再びjqueryで処理するための記述を追加。done
・表示内容を.empty()で初期化し、新たな内容を表示させる。append
・適合内容の有無を条件判定させる。array.length!==0
エラー時の挙動を追加。fail
bundler
can’t find gem bundler (>= 0.a) with executable bundle (Gem::GemNotFoundException)
bundlerのバージョン違いで出るエラー。
bunler2系のエラーだそう。
gem.lockファイルに記載のbundled_withのバージョンを確かめるか、以下でバージョンを確認。
cat Gemfile.lock | grep -A 1 "BUNDLED WITH"
あとはバージョンを指定してgemをインストールすればOK
gem install bundler -v '2.0.1'
コメント