Rails
フォーム送信中にダブルクリックを止める
タグに直書きする時は data-disable-with="送信中"
コーディングパターン
パーシャルの書き方
<%= render "menu" %>同階層の_menu.html.erb
<%= render "shared/menu" %>app/views/shared/_menu.html.erb
code:rb
# respond_toを利用して、xlsxを求められたらviewを切り替える
respond_to do |format|
format.html
format.xlsx do
# rubyXLはRailsのView層にコンバート頑張って作る
workbook = RubyXL::Workbook.new
sheet = workbook.first
sheet.add_cell(0,0, "Hello Wrold!")
send_data workbook.stream.read, filename: 'test.xlsx', type: :xlsx
end
end
Gemインストール
code:gemfile
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
コンフィグファイル作成
rails g wicked_pdfでconfig/initializers/wicked_pdf.rbを作成、内容を編集(wkhtmltopdfのバイナリ置き場を指定
exe_path: Gem.bin_path('wkhtmltopdf-binary', 'wkhtmltopdf')
日本語フォントのインストール
code:sh
yum install -y ipa-gothic-fonts
yum install -y ipa-mincho-fonts
viewの作成
いくつかヘルパーが異なるので専用のレイアウトから作る
views/layouts/application.pdf.erb
views/[TARGET]/xxx.pdf.erb
controllerの作成
code:rb
respond_to do |format|
format.html
format.pdf do
render(
pdf: 'file_name', # 必須
encoding: 'UTF-8', # 日本語化に必須
layout: 'application.pdf.erb' # 上で作成したもの
)
end
end
docxの作成
あらかじめテンプレート用のdocxに、文章挿入ポイントとしてブックマークを仕込んだ物を作成しておく
code:rb
doc = Docx::Document.open('./tmp/テスト.docx')
doc.bookmarks'value'.insert_text_after("9876") doc.save('./tmp/test1.docx')
ただ、gem docxはテキストの挿入しかサポートしていないように思える
Pandoc
sudo yum install pandoc
Controllerの応答パターン
respond_toとかcsvダウンロードとかあの辺
CSV出力
リクエスト時にformat: :csvとするか、URLの末尾に".csv"をつける事で、Viewを変更できる
通常(HTML)のViewが prize.html.haml だとすると、CSVのViewは prize.csv.ruby にできる。
特に何もしない場合
リクエストの拡張子(URLあるいはContent-Typeヘッダ)に対応したViewテンプレートを自動で選択する
xxx.html.erbやxxx.csv.ruby、xxx.json.jbuilderなんかを勝手に選んでレンダリングを行う。
この場合、ファイル名は自動的にアクション名になってしまうので、ファイル名を変えたい場合は一手間必要。
render :new を使う場合
本来xxx.html.erbのテンプレートを見に行く所を、new.html.erbを見に行くように矯正する
render format: content, status: :ok を使った応答
formatで指定したものとして応答する。
respond_to とは違い、リクエストを参照しないので、一意に決め打つ時に。
formatの部分はいろいろ選べる
plain: プレーンテキスト
json: JSON
redirect_to xxx_path を使う場合
本来xxx.html.erbのテンプレートを見に行く所を、302応答を返してxxx_pathへリダイレクトする
respond_to を使う場合
要求されたファイル拡張子に応じた処理の振り分けと、応答可能な拡張子のホワイトリストができる
code:rb
respond_to do |format|
format.html
format.csv do
send_data render_to_string, filename: "hoge.csv", type: :csv
end
end
これで、hoge.csvのダウンロードができる。(あとhtmlとcsv以外のフォーマットを受け付けない)
ファイル名を作っているのはsend_dataの仕業。
render_to_stringは、Viewの出力結果を文字列で受け取る,renderメソッドの代わりになるものなので、viewにhoge.csv.erb的なファイルが必要
prize.csv.rubyの内容はこんな感じ
code:rb
CSV.generate do |csv|
csv << csv_column_names
@prizes.each do |prize|
csv_column_values = [
->{ "済み" if prize.prized }.call,
prize.user.email,
]
csv << csv_column_values
end
end.tosjis
Railsでタイトルを動的に
アップロードされたファイルの拡張子確認
raise I18n.t('import.not_csv') if file.content_type != Rack::Mime::MIME_TYPES['.csv']
リクエストURLから該当するControllerとActionを逆引き(アプリ内でRoutesを調査)
Rails.application.routes.recognize_path "users"
railsからHTMLタグの出力
文字列.html_safe
現在のDBの状態をschema.rbに書き込む
bundle exec rails db:schema:dump
マイグレーション使わずにテーブル作ったりした時に
cookies.signed[:user_id] = user.id
復号化
User.find_by(id: cookies.signed[:user_id])
app/assetsは、カスタム画像ファイル、JavaScript、スタイルシートなど、アプリケーション自身が保有するアセットの置き場所です。
lib/assetsは、1つのアプリケーションの範疇に収まらないライブラリのコードや、複数のアプリケーションで共有されるライブラリのコードを置く場所です。
vendor/assetsは、JavaScriptプラグインやCSSフレームワークなど、外部の団体などによって所有されているアセットの置き場所です。
rakeタスクの作り方
rails g task task_sample でスケルトンを作る
code:rb
namespace :inactive_uesr do
desc "usage task"
task :task_name => :environment do
end
end
自作のrakeタスクから、他のrakeタスクを呼び出し
Rake::Task["sample:hoge"].invoke
Ruby on Railsでapplication.html.erbを適用したくないページの設定
適応しない
render :layout => nil
別のレイアウト
render :layout => "second_layout"
コントローラー全体にレイアウトファイルを適用したい場合
code:rb
class UserController < ApplicationController
layout "second_layout"
def home
...
end
def about
...
end
end
Rack::Mime::MIME_TYPES
テストでMimeType付きのFileオブジェクトが欲しいとき
code:rb
f = ActionDispatch::Http::UploadedFile.new({
filename: 'test',
type: Rack::Mime::MIME_TYPES'.csv', tempfile: File.new('test/fixtures/files/employee.csv')
})
protect_from_forgeryを書く位置
こう書くとCSRFチェックで弾かれた
code:rb
class ApplicationController < ActionController::Base
default_form_builder ApplicationFormBuilder
before_action :set_paper_trail_whodunnit
before_action :sign_in_required
protect_from_forgery with: :exception
end
こう書く
code:rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
default_form_builder ApplicationFormBuilder
before_action :set_paper_trail_whodunnit
before_action :sign_in_required
end
binding.pryから脱出するコマンド
exit!
Railsのログをもう少しまともにする
受けたリクエストのURLを取得
request.original_url
railsでカスタムフォントを使う時
カスタムフォントはassets/fonts/配下に入れる(他の場所だと何かに定義しないと上手く動かない)
assets配下だとデプロイ後は置き場所が変わるので、font-url()を使用してパス指定する。
ただし、font-urlはコンパイルしないと機能しないので、CSSファイルに書いても動かない。
SCSSファイルにすること
どうも、ヘルパー扱いのようで、viewとcontrollerはt('hoge')で使える
Model周りではI18n.t('hoge')でつかえる。
tに入れるクエリは.区切りでパス指定できる。子持ちのパスの場合はハッシュで帰ってくる
ファイル名はコントローラ側で制御する
code:rb
respond_to do |format|
format.html
format.csv do
send_data render_to_string, filename: "ギフト券.csv", type: :csv
end
end
リクエストフォーマットの確認
request.format == :html
request.format == :csv
ルーティングヘルパーから、リクエストフォーマットの指定
prize_path(format: :csv)
言語設定の変更
code:application.rb
config.i18n.default_locale = :ja
をブロック内部に追加
このメソッドが呼ばれたらこのクラスに問い合わせてね、と指定する
code:rb
class Note < ActiveRecord::Base
belongs_to :user
delegate :name, to: :user
# => 最小構成、nameを呼ぶとuesr.nameが帰る。user == nilの場合は例外
delegate :name, to: :user, prefix: true, allow_nil: true
# => 基本構成、user_nameが使えて、user == nil の場合にnilが帰る
delegate :name, to: :user, prefix: :author, allow_nil: true
# => 応用構成、note.author_name が使えるようになる
end
基本の書式はこんな感じ
delegate [呼び出しメソッド], to: [移譲先]
prefixで、メソッドの接頭語を指定できる(プロパティ名が被ってる時などに)
allow_nilで、委譲先のオブジェクトが無い場合でもエラーにしない(nilを返す)
coffeeスクリプトを消すのに失敗
誤ってcoffiee-railsを入れっぱなしにしていた。
Gemfileから消してRailsを再起動すると、LoadError (cannot load such file -- coffee_script):とエラーが出るように
これはcoffee_scriptのキャッシュが残っているのが原因だったので、bin/rake tmp:cache:clearを実行することで解決
application.js, application.cssからnode_modulesへのrequire_tree
require_tree, require_directoryは、そのファイルからの相対パスになるので、3つくらい戻る必要がある
require_tree ../../../node_modules
でも、そもそもnode_modulesがデフォルトでルート扱いなので・・・
JavascriptからSprockets配下のアセットへのアクセス
ファイル名末尾に.erbをつける事で、<%= %>記法が使えるようになるので、
<%= image_tag("slickgrid/CheckboxY.png") %> なんかが使える
SCSSの場合はasset-pathやasset-path
railsのメールテンプレートの確認用URL
http://localhost:3000/rails/mailers
seeds.rb 書き方
code:rb
User.create!(name: "Example User",
email: "example@railstutorial.org",
password: "foobar",
password_confirmation:"foobar",
admin: true,
activated: true,
activated_at: Time.zone.now)
99.times do |n|
name = Faker::Name.name
email = "examle-#{n+1}@railstutorial.org"
password = "password"
User.create!(name: name, email: email,
password: password, password_confirmation: password,
activated: true,
activated_at: Time.zone.now)
end
Viewでプリントデバッグ(debugヘルパ)
debug(params) if Rails.env.development?
rails起動のふわっとした流れ
1. nginx起動する
2. unicorn起動する
3. Unixドメインソケットという概念でnginxとunicornが連携する
4. unicornがワーキングディレクトリからconfig.ruを探す
5. config.ruにRails起動のDSLが書かれているので、railsが起動
rubyのメソッド中において クラス名 と メソッド名 を取得する方法。
in-methodログなんかはp "#{self.class.name}##{__method__}"とか
ruby 例外情報からコールスタックの確認
e.backtrace
code:rb
module ConcernSmaple
extend ActiveSupport::Concern
included do
# wrire for scope
end
module ClassMethods
# write for Class methods
end
# write for instance methods
end
Railsでグラフとかを表示する
Viewから任意のタグ生成 3パターン
"<div></div>".html_safe
raw "<div></div>"
content_tag('div')
要求されたコントローラを取得
params[:controller]
要求されたアクションを取得
params[:action]
railsのproduction環境でpublic配下にアクセスできない
code:config/environments/production.rb
config.public_file_server.enabled = true
RailsデフォルトではENV['RAILS_SERVE_STATIC_FILES'].present?となっているので
環境変数定義してあげればOK
実環境ではS3とかに置きたいね
本番モードテスト
code:環境変数定義
RAILS_ENV=production
RAILS_SERVE_STATIC_FILES=1
SECRET_KEY_BASE=hogehogehogheo
code:プリコンパイル、DB準備
bundle exec rails assets:precompile
bundle exec rails db:setup
bundle exec rails db:migrate
code:起動
bundle exec rails s -e production
アセットのプリコンパイル試行錯誤
precompileしたものを削除
bundle exec rake assets:clobber RAILS_ENV=production
precompileする
bundle exec rake assets:precompile RAILS_ENV=production
間違えてassetをプリコンパイルした時(プリコンパイルしたassetsを削除
rake assets:clobber
ちなみにプリコンパイルはこう
rake assets:precompile
Modelの世代管理
変更履歴の一覧取得
code:変更履歴の一覧取得
<% @note.versions.sort{|a,b| b.index <=> a.index}.each do |version| %>
<% next if version.event != 'update' %>
<tr>
<td><%= version.index %></td>
<td><%= version.created_at %></td>
<td><%= User.find(version.whodunnit).name if version.whodunnit.present? %></td>
<td>
<%= form_with(model: version.reify, local: true) do |f| %>
<%= f.hidden_field :title %>
<%= f.hidden_field :body %>
<%= f.hidden_field :rollback, value: :true %>
<%= f.button '戻す', type: :submit, class: 'btn btn-primary' %>
<% end %>
</td>
</tr>
<% end %>
ApplicationControllerにbefore_action :set_paper_trail_whodunnitを入れておくこと
date_select型の内容解決
start_date(1i)なんかをDate型に変換
code:rb
#
# combine date params from date_select helper
# for example
# {'start_date(1i)' => '2018', 'start_date(2i)' => 09, 'start_date(3i)' => 01}
# combine to
# {'start_date' => 2018-9-1]} # is TimeWithZone
# after, send to super
#
#
def initialize(attribute = nil)
return super if attribute == nil
# find date_select params
date_parts = {}
attribute.each do |k, v|
if k.include?('(')
date_partsk = attribute.delete(k) end
end
# combine values
dates = {}
date_parts.each do |multi_key, value|
attribute_name = multi_key.split('(').first
parameter_value = nil
if value.present?
# 'start_date(1i)' to "i"
value = multi_key =~ /\(0-9*(if)\)/ ? value.send('to_'+ $1) : value # 'start_date(1i)' to "1"
position = multi_key.scan(/\((0-9*).*\)/).first.first.to_i end
end
# applying to params
dates.each do |name, values|
values = nil if values.all?(&:nil?)
type = name.split('_').last
if %w(date month).include?(type)
if values.size != 3
values = nil
else
values = "%s-%s-%s" % [values1, values2, values3] values = values.in_time_zone
end
end
end
super
end
ふわっとした理解
'('で引っ掛けて、date_selectによるものかどうかを判定
date_select由来のデータなら退避してparamsから削除
退避した各paramに対して、'('以前を取り出して属性名とする
この属性名に対応する箱を用意
param_keyに'(0-9i)'があれば to_i を呼ぶ param_keyに'(0-9f)'があれば to_f を呼ぶ 入力にも和名を使いたい
実データコードで持つ意味無いのでは案件
入力パート
バリデーション直前に入力された値とエイリアスマップとを突合して、コード値に置き換え
出力パート
エイリアスマップを舐めて、変換メソッドを製造する
code:rb
class BusinessClassification < ApplicationRecord
# エイリアスマップ
ALIASES = {
sbr_account_classification_code: {
'無形' => '01', '雑費' => '02'
},
sales_classification: {
'企画' => '01', '開発' => '02', '保守' => '03'
}
}.freeze
# 出力用メソッド生成
# define name getter
# ex.
# generate 'sbr_account_classification_code_name' method
ALIASES.each do |target, pairs|
define_method "#{target}_name" do
ALIASEStarget.key(self.send(target)) end
end
# 入力時に名称:コード変換
before_validation :apply_aliases
private
def apply_aliases
ALIASES.each do |target, pairs|
input = self.send(target)
if pairs.keys.include?(input)
# find alias
end
end
end
end
## SameSite
## RestAPIの雛形(スケルトン)作成
## vendor配下にbundle install
bundle install --path vendor/bundler
## Railsでファイルアップロード
ActiveStorageの話
## ElasticSearchのインストール
## scaffoldの生成ファイルカスタマイズ
`rb
Rails.application.config.generators do |g|
g.scaffold_stylesheet = false # ignore generate scaffold.scss
g.test_framework = false # ignore generate tests
g.jbuilder = false # ignore generate jbuilders
g.assets = false # ignore generate javascript and stylesheet
# g.stylesheets = false # ignore generate stylesheets
# g.javascript = false # ignore generate javascript
g.helper = false # ignore generate helpers
g.integration_tool = false # ignore unit_tests
g.system_tests = false # ignore system_tests
g.active_record = {
migration: false # ignore migration
}
end
`
## bootstrapとjquery-uiの競合
メソッド名が同じ・・・
後がちになるので読み込み順を操作する。
## RailsでWebAPI式のアプリを作る雛形
## Railsで簡易なあいまい検索
User.where('name LIKE ?', "%#{params[:word]}%")
## Ruby / Railsでrequireで使用するパスの指定方法と拡張方法
rubyの場合、ローカルファイルへのrequireは require './hoge'と書く
ActiveSupoort影響下では、フォルダパスを指定して自動でrequireしてくれる
`rb
require 'active_support/dependencies'
ActiveSupport::Dependencies.autoload_paths << "/path/to/my_library"
`
## erbで範囲コメント
どうにもうまい方法がない模様
`rb
<% if false %>
hogehoge
<% end %>
`
## Stimulus
Railsの親玉(Basecamp)が作成したjsライブラリ。
Railsと親和性が高くて、特にTurbolinks影響下でもちゃんと動いてくれる
立ち位置としてはVue.jsやReactなどと同じポジション
## RailsでDatatablesを真っ当に使う
- tbody内部はカラにしておく
- 画面表示用のパスとは別に、json受送信用のパスを切っておく(今回は拡張子切り替えで対応)
- DataTablesのイニシャライザに色々オプションをつける
- serverSide: true
- フィルタ、ページング、ソートの計算をサーバーで行う
- ajax: url
- データ取得元のパスを書く
- 応答クエリのフォーマットは以下を参照、デフォルトだとdataプロパティパターンの模様
- DataTablesがクエリを投げる際に、自前の検索条件も合わせて投げるよう修正
- クエリの追加を行う
- HTML向けには検索せずに応答を返す、JSON向けには検索を実施
- DataTables由来の検索パラメータ処理を書く
- やる事が多すぎるのでGemを投入
- DataTable側をこのgemに合わせるように修正(下記)
- view_columnsメソッドは、各カラムのメタデータを定義する。基本的にはクライアントJSに渡したcolumnsと同じものを定義
- sourceに書いた情報は検索やオーダーに使うSQL文に反映される
- 固定値としたい場合は、sourceにnilを入れて、orderableをfalse、searchableをfalse
- dataメソッドは、取ってきたレコードと応答JSONの対応関係を記述する
- 固定値を入れるのはここ
- get_raw_recordsに実際の検索処理を記述する
ajax-datatables-rails向けのDatatable初期化処理例
`js
// users.js
jQuery(document).ready(function() {
$('#users-datatable').dataTable({
"processing": true,
"serverSide": true,
"ajax": $('#users-datatable').data('source'),
"pagingType": "full_numbers",
"columns": [
{"data": "id"},
{"data": "first_name"},
{"data": "last_name"},
{"data": "email"},
{"data": "bio"}
]
// pagingType is optional, if you want full pagination controls.
// Check dataTables documentation to learn more about
// available options.
});
});
`
## redirect_toのバリエーション
## Railsでファビコン
## RailsのModelでコールバックメソッドの追加をする
define_model_callbacks :xxx でコールバックのフック名を定義する
(before_xxx みたいなメソッドが使えるようになる)
コールバックを叩くときは、
`rb
run_callbacks(:xxx) do
# doing
end
`
メソッドを呼び出すと、ブロック前後でコールバックメソッドがキックされるようになる
## Railsでflashメッセージ
some_controller.rb
`rb
`
some_view.html.haml
`rb
- flash.each do |type, msg|
%div{class: "alert alert-#{type}"}= msg
`