- 投稿日:2019-07-08T23:32:28+09:00
カスタムバリデーションで他のモデルのカラムを使用する
About
カスタムバリデーションで他のモデルのカラムを使いたい場合の記載方法について記載しています。
Environment
この記事ではmacbook(unix)にインストールしたruby 2.5.1p57, Rails 5.2.3を使用しています。
Validationとは
「バリデーション」とは、「検証、実証、認可、妥当性」を意味する英単語
「質的な良し悪し」を判断するのではなく、「システム的な適合不適合」を判断するための言葉小難しく書きましたが、意図しないデータをDBに登録できない様、バリデーションをかけることが一般的であるそうです。
カスタムバリデーション
通常、バリデーションは以下の様な形式で設定しますが、複雑なバリデーションを設定したい場合、自作のバリデーション(= カスタムバリデーション)を作成します。
通常class User < ApplicationRecord validates :user_id, presence: true validates :email, presence: true end基本的には、以下の様に定義したメソッド名をvalidate に続けて記載することで、設定可能です。
カスタムバリデーションclass User < ApplicationRecord validates :user_id, presence: true validates :email, presence: true validate :if_user_does_not_have_nickname def if_user_does_not_have_nickname return if nickname.present? errors.add(:nickname, "Nickname is absent") end end他のモデルのカラムも組み合わせたい場合
validationを考えていると、他のモデルのカラムを条件として加えたい時があるかと思います。
その様な場合、モデル間でアソシエーションを組むことで他のモデルのカラムを使用することができます。(例)睡眠時間を記録するアプリ
・日付がデータとして渡されている
・もしユーザの睡眠記録が一つもなければ、睡眠記録の新規作成が可能
・睡眠記録がすでにある場合は、渡されたデータが既存の睡眠記録の最終日付の翌日にマッチしているかどうか調べる
・ミスマッチの場合、渡されたデータを新規登録しないsleep.rubyclass Sleep < ApplicationRecord # アソシエーションの設定。読み込み順の関係で、カスタムバリデーションより必ず上に記載してください。 belongs_to :user validates :slept_time, presence: true validates :wakeup_time, presence: true # カスタムバリデーションの呼び出し(コントローラでnew, createメソッドの場合のみ) validate :dates_cannnot_be_registered_if_there_is_no_yesterdays_date, on: [:new, :create] # カスタムバリデーションの作成 def dates_cannnot_be_registered_if_there_is_no_yesterdays_date # もし日付があり、ユーザの睡眠記録がない場合は処理を抜ける(= そのまま新規登録する。) return if date.present? && user.sleeps.blank? # もし日付がユーザの最新の睡眠記録の日付の翌日でない場合は、データを新規登録しない。 if date != user.sleeps.last.date.tomorrow errors.add(:date, "You can't register that there is no data about before days.") end end end最後に
いかがでしょうか。アソシエーションを組めていれば、意外と簡単に実装できてしまいます。
私はbelongs toの記述位置がカスタムバリデーションの下になっていたため、エラーと何時間も格闘する羽目になりましたが...。
参考になれば幸いです!筆者について
TECH::EXPERTにて4月よりruby, railsを学習している未経験エンジニアです。
記載内容に不備・不足があればご指摘いただけると幸いです。
至らぬ点ばかりですので、改善点がありましたらどんどんご指摘下さい!
- 投稿日:2019-07-08T23:08:57+09:00
【Ruby】モジュールについて(ネームスペースの活用)
はじめに
今回はモジュールによるネームスペース(名前空間)の作成に関してまとめました!
モジュールには様々な用途があります。
主に以下のような用途です。・クラスにインスタンスメソッドを追加する(ミックスイン)
・クラスにクラスメソッドを追加する(ミックスイン)
・クラス名などの重複を防ぐためにネームスペース(名前空間)を作成する
・ミックスインせずにモジュール単体としてメソッドを定義
など。今回はこれらの機能のうち、上から3つ目のネームスペースに関してのみまとめています。
1つ目及び2つ目の機能(ミックスイン)に関しては別記事にてまとめていますので、ぜひこちらでご覧ください
→ https://qiita.com/tomokichi_ruby/items/b348d7a44928262cc7e4ネームスペース(名前空間)とは
各要素に一意の異なる名前をつけた範囲のことです。
また要素の名前が衝突しても識別できるように、要素の集合に対して付与された名前のこと。例えば複数名で同じアプリケーションを作成する場合などは特に、クラス名やメソッド名が被ってしまう危険性があります。
この名前の衝突を防ぐために活用するのがネームスペースであり、モジュールにてこのネームスペースを作ることができます!モジュールによるネームスペースの付け方
test.rbmodule Admin class User #メソッドなど end end module Guest class User #メソッドなど end end上記の例では、Userクラスが2つ存在していますが、区別するためにそれぞれAdminとGuestモジュール内に入れています。
このようにモジュール内のクラスを参照する場合はモジュール名::クラス名
とすればOKです!
例えば、クラスからインスタンスを作成する時はtest.rbGuest::User.newこのようにするだけです!
ネームスペースの定義の応用
すでにモジュールが定義されている場合で、そのモジュール内にクラスを追加したい場合は、
以下のようにclass モジュール名::クラス名
と宣言することもできます。test.rbmodule Admin end class Admin::User #メソッドなど endさいごに
ネームスペースについて解説しました!
大規模なプロジェクトになるほど、モジュールによるネームスペースの活用は必要になってきます。
モジュールを有効活用していきたいですね
最後までご覧いただきありがとうございました!
- 投稿日:2019-07-08T22:35:11+09:00
amcharts 4 Demos を使ってグラフを作成(piechart編)
About
amcharts関連の日本語文献のあまりの少なさから、誰かのお役に立てればと思い記載しています!
Environment
この記事ではmacbook(unix)にインストールしたruby 2.5.1p57, Rails 5.2.3を使っています。
amchartsとは
前回の記事をご覧ください!
Piechartとは
円グラフです。今回はこちらを作成します。
導入方法
こちらのサイトのページ内を下にスクロールすると、Demo sourceが置いてありますので、コピペで使用可能です。
基本的にはそのままコードを触らずとも、実装可能です。Demo_source<!-- Styles --> <style> #chartdiv { width: 100%; height: 500px; } </style> <!-- Resources --> <script src="https://www.amcharts.com/lib/4/core.js"></script> <script src="https://www.amcharts.com/lib/4/charts.js"></script> <script src="https://www.amcharts.com/lib/4/themes/animated.js"></script> <!-- Chart code --> <script> am4core.ready(function() { // Themes begin am4core.useTheme(am4themes_animated); // Themes end // Create chart instance var chart = am4core.create("chartdiv", am4charts.PieChart); // Add data chart.data = [ { "country": "Lithuania", "litres": 501.9 }, { "country": "Czech Republic", "litres": 301.9 }, { "country": "Ireland", "litres": 201.1 }, { "country": "Germany", "litres": 165.8 }, { "country": "Australia", "litres": 139.9 }, { "country": "Austria", "litres": 128.3 }, { "country": "UK", "litres": 99 }, { "country": "Belgium", "litres": 60 }, { "country": "The Netherlands", "litres": 50 } ]; // Add and configure Series var pieSeries = chart.series.push(new am4charts.PieSeries()); pieSeries.dataFields.value = "litres"; pieSeries.dataFields.category = "country"; pieSeries.slices.template.stroke = am4core.color("#fff"); pieSeries.slices.template.strokeWidth = 2; pieSeries.slices.template.strokeOpacity = 1; // This creates initial animation pieSeries.hiddenState.properties.opacity = 1; pieSeries.hiddenState.properties.endAngle = -90; pieSeries.hiddenState.properties.startAngle = -90; }); // end am4core.ready() </script> <!-- HTML --> <div id="chartdiv"></div>編集方法
style, htmlについては割愛し、scriptについて筆者がわかる範囲で記載していきます。
index.html.erb<!-- Chart code --> <script> am4core.ready(function() { // Themes begin // テーマです。 am4core.useTheme(am4themes_animated); // Themes end // Create chart instance // 始めにインスタンスを作成します。Piechart形式であることをここで指定しています。 var chart = am4core.create("chartdiv", am4charts.PieChart); // Add data // ここにデータを入力していきます。 chart.data = [ { "country": "Lithuania", "litres": 501.9 }, { "country": "Czech Republic", "litres": 301.9 }, { "country": "Ireland", "litres": 201.1 }, { "country": "Germany", "litres": 165.8 }, { "country": "Australia", "litres": 139.9 }, { "country": "Austria", "litres": 128.3 }, { "country": "UK", "litres": 99 }, { "country": "Belgium", "litres": 60 }, { "country": "The Netherlands", "litres": 50 } ]; // Add and configure Series // 作成したインスタンスに設定を追加していきます。 var pieSeries = chart.series.push(new am4charts.PieSeries()); // データはlitres(リットル) pieSeries.dataFields.value = "litres"; // 単位はcountryです pieSeries.dataFields.category = "country"; pieSeries.slices.template.stroke = am4core.color("#fff"); pieSeries.slices.template.strokeWidth = 2; pieSeries.slices.template.strokeOpacity = 1; // This creates initial animation pieSeries.hiddenState.properties.opacity = 1; pieSeries.hiddenState.properties.endAngle = -90; pieSeries.hiddenState.properties.startAngle = -90; }); // end am4core.ready() </script>実装例
最後に、私が作成した"sommeil"というアプリケーションのコードの内、一部のamcharts部分を参考までに記載します。
_piechart.html.erb<!-- Styles --> <style> #chart2div { width: 100%; height: 300px; } </style> <!-- Resources --> <script src="https://www.amcharts.com/lib/4/core.js"></script> <script src="https://www.amcharts.com/lib/4/charts.js"></script> <script src="https://www.amcharts.com/lib/4/themes/animated.js"></script> <!-- Chart code --> <script> am4core.ready(function () { // Themes begin am4core.useTheme(am4themes_animated); // Themes end // Create chart instance var chart2 = am4core.create("chart2div", am4charts.PieChart); // Add data var sleeping_time = '<%= @sleeping_time %>'; chart2.data = [{ "time": "Sleep", "amount": sleeping_time }, { "time": "Awake", "amount": (24 - sleeping_time) }]; // Add and configure Series var pieSeries = chart2.series.push(new am4charts.PieSeries()); pieSeries.labels.template.disabled = true; pieSeries.dataFields.value = "amount"; pieSeries.dataFields.category = "time"; pieSeries.slices.template.stroke = am4core.color("#fff"); pieSeries.slices.template.strokeWidth = 2; pieSeries.slices.template.strokeOpacity = 1; // This creates initial animation pieSeries.hiddenState.properties.opacity = 1; pieSeries.hiddenState.properties.endAngle = -90; pieSeries.hiddenState.properties.startAngle = -90; }); // end am4core.ready() </script> <!-- HTML --> <div id="chart2div"></div>最後に
注釈や解説できない部分がまだまだありますので、今後検証して追記していきます。
ご覧いただき、ありがとうございました。筆者について
TECH::EXPERTにて4月よりruby, railsを学習している未経験エンジニアです。
記載内容に不備・不足があればご指摘いただけると幸いです。
至らぬ点ばかりですので、改善点がありましたらどんどんご指摘下さい!
- 投稿日:2019-07-08T20:50:29+09:00
MaterializecssのCarouselを使用して、3秒ごとに画像が自動で切り替わるページを作る
概要
TECH::EXPERTのカリキュラムでオリジナルのミニアプリを作成する機会があり、
その一部のページでMaterializecssのCarouselを使用し、3秒ごとに画像が切り替わるページを作成したので紹介します。MaterializecssのCarouselとは
画像をくるくると回せる機能です。
https://materializecss.com/carousel.html自分が作成したページ紹介
作成する前提
MaterializecssがCDNで読み込めている
編集するファイル
・ビューファイル
・CSSファイル
・jsファイルビューファイル
about.html.erb<section class="about-main" > <div class="carousel carousel-slider" data-indicators="true" id="big3" > <div class="carousel-fixed-item"> <div class="container"> <h1 class="white-text">Work Hard See Result</h1> <% if user_signed_in? %> <a class="btn waves-effect white black-text darken-text-2" href="/" target="_blank">HOME</a> <%else%> <a class="btn waves-effect white black-text darken-text-2" href="/users/sign_in" target="_blank">Log in</a> <%end%> </div> </div> <div class="carousel-item big3pic" id="benchpress"> <div class="container"> <h3 class="white-text">Bench Press</h3> <p class="white-text">chest</p> </div> </div> <div class="carousel-item big3pic" id="deadlift"> <div class="container"> <h3 class="white-text">Dead Lift</h3> <p class="white-text">back</p> </div> </div> <div class="carousel-item big3pic" id="squat"> <div class="container"> <h3 class="white-text">Squat</h3> <p class="white-text">legs</p> </div> </div> </div> </section>"carousel carousel-slider" を用いました。
"carousel-fixed-item"は固定して表示したいものを書きます。
"carousel carousel-item"の部分がそれぞれの画像のクラスです。CSSファイル
style.css#big3{ height: 100vh; } #benchpress{ background-image: url('benchpress.jpg'); background-size: 100%; } #deadlift{ background-image: url('deadlift.jpg'); background-size: 100%; } #squat{ background-image: url('squat.jpg'); background-size: 100%; }それぞれidを付与してるので、idによって画像を変更してください。
jsファイル
about.js$(document).ready(function(){ $('#big3').carousel( { dist: 0, padding: 0, fullWidth: true, indicators: true, duration: 100, } ); autoplay() function autoplay() { $('#big3').carousel('next'); autoplay: true, setTimeout(autoplay, 3000); } });autoplayの関数を定義してます。
id=big3に対して3000ミリ秒で次のcarousel-itemを
表示するように設定してます。オプションは下記を参照しました。
- [1] dist: 0, => 遠近ズーム0
- [2] padding: 0, => 中央以外の項目の余白0
- [3] fullWidth: true, => carouselを全幅のスライダーへ
- [4] indicators: true, => インジケータを表示
- [5] duration: 100, => 次のスライドへ移動しきるまでの時間100ミリ秒
最後に
この記事を書いた目的
・自分なりに工夫した点をアウトプットして、理解を深める。
・あわよくば有識者にフィードバックをもらいたい。
・私と同じ初学者からも奇譚のない意見をもらいたい。(自分だったらどうこうする的な)筆者について
TECH::EXPERTにて4月27日より52期夜間・休日コースでruby/railsを学習している未経験エンジニアです。
ご不備等ありましたら、ご指摘ください。ちなみに本記事が初投稿になります。
言わずもがなかもしれませんが、趣味はボディメイク・筋トレでございます。
余談ですが、120kg⇨66kgまで減量して大会出場した経験があり
ダイエットについての質問はなんでも答えられるかと思います
- 投稿日:2019-07-08T20:17:39+09:00
rails スラッシュを複数挟んでもnot foundにならない
同僚から
Rails のルーティングは連続するスラッシュは単一扱いされるらしい。
localhost:3000/utilities///////hoge/39
の様にスラッシュを複数挟んでもnot foundにならないのはなんでなん?という質問を受けたので、備忘録として書いておきます。
僕が調べた感じ、railsの名前解決の流れは
https://qiita.com/kkyouhei/items/1203f5aa521c065a7097
らしい。そこで通る
Journey::Router::Utils.normalize_path(path)
にてdef self.normalize_path(path) path = "/#{path}" path.squeeze!('/') path.sub!(%r{/+\Z}, '') path = '/' if path == '' path endhttps://github.com/rails/journey/blob/master/lib/journey/router/utils.rb#L14
引数に含まれる文字が複数並んでいたら 1 文字にまとめる
.squeeze!('/')
をしているからhttps://docs.ruby-lang.org/ja/latest/method/String/i/squeeze.html
履歴にも残してはいけないものを書いていたので上げ直しです。
ごめんなさい
- 投稿日:2019-07-08T19:50:55+09:00
クラス継承の名前変更、削除(aliasとundef )
aliasについて
クラスの継承において、既に存在するメソッドに別の名前を割り当てたいときにaliasを使う。書き方としては、引数にメソッド名かシンボル名を指定する。
alias 別名 元の名前 または alias :別名 :元の名前実際のコードで確認すると、
class Class1 def hello "hello" end end class Class2 < Class1 alias old_hello hello def hello "#{old_hello}, again" end end obj = Class2.new p obj.old_hello #=> "hello" p obj.hello #=> "hello, again"undefについて
定義されたメソッドをなかったことにしたいときに、undefを使う。書き方として、aliasと同様に引数にメソッド名かシンボル名を指定する。
undef メソッド または undef :メソッドclass Class1 def hello "hello" end end class Class2 < Class1 undef hello end obj1 = Class1.new obj2 = Class2.new p obj1.hello #=>"hello" p obj2.hello #=>エラー(undefined method `hello')同じクラス内に用いても、
class Class1 undef hello def hello "hello" end end obj1 = Class1.new p obj1.hello #=> エラー(undefined method `hello' for class `Class1')
- 投稿日:2019-07-08T16:47:08+09:00
Rubyの整数から下1桁や2桁取る方法
コード
x = 123456789 puts x % 10 puts x % 100 puts x % 1000 puts x % 10000出力結果↓
9 89 789 6789あまりで下1桁、や2桁を取ってます。
ただ、ピンポイントでほしい桁だけを取ることが出来ません。ピンポイントでほしい桁だけを取るときは
x = 123456789 puts x.to_s.split("")[-1] puts x.to_s.split("")[-2] puts x.to_s.split("")[-3] puts x.to_s.split("")[-4]出力結果↓
9 8 7 6訂正しました
もっと簡単に取れる方法を教えて頂いたので、訂正します。
ttakuru88さんありがとうございました。x = 123456789 p x.digits出力結果↓
[9, 8, 7, 6, 5, 4, 3, 2, 1]
- 投稿日:2019-07-08T15:11:03+09:00
[ruby]バッチ処理について
バッチ処理とは?
バッチ処理とは“一定量の(あるいは一定期間の)データを集め、一括処理するための処理方法”
私は、大量のデータを処理すること全般をイメージしてる。
引用元
https://www.imkk.jp/blog/what-is-batch-processing.htmlバッチ処理のフロー
私の業務では、DBに一括登録するデータをスプレッドシートから取り組むバッチ処理でした。
以下では、データベースに商品を一括にスプレッドシートから取り込んで追加するという場合。フローは以下の通り
まずrakeファイルにそのバッチ処理がどういったものなのかをdescにかく。
そのあと走らせる。
以下がそのコードimport_new_products_information.rakenamespace :batch do desc '新規商品の追加' task import_new_products_information: :environment do require 'batches/import_new_products_information' Batches::ImportNewProductsInformation.run end end走らせるファイル。
一応シート内に商品名と商品コードというカラムがあることが前提。import_new_products_informations.rbrequire 'batch/base' require 'google_spreadsheet' module Batches class ImportrProductsInformations < Batch::Base SPREADSHEET_KEY = '追加する商品のスプレッドシートキー'.freeze SHEET_NAME = '追加する商品一覧があるシートの名前 例)取り込み用など'.freeze def execute(_args) gs = GoogleSpreadsheet.new(SPREADSHEET_KEY) gs.spreadsheet.worksheet_by_title(SHEET_NAME).tap do |sheet| sheet.list.each do |row| products_informations.create!( product_name: row['商品名'], product_number: row['商品コード'] ) end end end end endgoogle_spredsheet.rbdef worksheet_by_title(sheet_name) spreadsheet.worksheet_by_title(sheet_name) end
- 投稿日:2019-07-08T14:49:09+09:00
Thorのコマンドの中でrailsのActiveRecordを使う
以前railsのタスクの書式という記事を書きましたが、railsのタスクが引数がややこしくて、オプションもスマートに使えず余り好きではありません。
Thorでコマンドを書く方が好きなのですが、Thorの中でActiveRecordを使ってDBの操作が出来たのでメモしておきます。
以下はrailsアプリケーションの直下に
thor
というディレクトリを作りthor/foobar
にコマンドがある想定のコードです。railsのautoloadも効くのでthorをGemfileに追加します。
gem 'thor', '~> 0.20'* @scivola さんにコメントいただきましたが
railties
がthorに依存しているのでGemfileには書かなくても読み込めます。ただし私の環境で試したところrequire 'thor'
を書かないとuninitialized constant Thor
で動きませんでした。#!/usr/bin/env ruby require_relative '../config/application' Rails.application.initialize! class Main < Thor desc "exec", "foobar" def exec binding.pry end end Main.start(ARGV)これで
thor/foobar execで実行可能です。なんてことは無くて
config/environment.rb
のコードを丸っと持ってきただけです。なのでRAILS_ENV=production thor/foobar execで実行すると
production
環境で実行可能です。spring経由で起動してないので起動が重いのが玉に傷です。やり方をちょっと調べてみて、ちなみにspring経由で起動するのにこんなタイムリーな記事があったのですが、残念ながら記事内のリンク先のgithubのリンクが死んでいてコードを見ることが出来ませんでした。spring-commands-rspecも参考になると思ってみてみましたが、私がspringの仕組み自体全く理解してないのでイマイチでよく分かりませんでした。その内調べて分かったらまた共有します。何かご存じの方いらっしゃいまたらヒントでも結構ですのでコメントお願いします!
- 投稿日:2019-07-08T13:13:39+09:00
複数のherokuアカウント上のRailsアプリを管理・デプロイするための情報まとめ
一台のデバイスで複数のherokuアカウントを管理・デプロイするための情報
余裕があるときに随時加筆修正します。
heroku で複数アカウントを管理するCLIのプラグインを追加する
$ heroku plugins:install heroku-accounts$ heroku accounts:add any_account_name $ heroku accounts:set added_account_nameheroku のssh鍵を追加する
heroku keys:add # 自動で公開健がherokuにupされるheroku にRailsアプリケーションをデプロイする
herokuにアプリケーションを作成する
heroku create app_nameapp_nameは省略可能
アプリ名を省略した場合、heroku側で自動に生成されるこの時自動でremoteのリポジトリ名がherokuに設定される。
もし、違う名前にしたい場合は、heroku git:remote -a app_name -r <any_repository_name>以降、pushするときは
git push <any_repository_name> masterRails アプリケーションの設定
Railsアプリケーションのデータベースをsqlite3からpostgreSQLに変更する。
TIPS
herokuアプリケーションを削除する
herokuのデプロイに失敗したとき、作成したアプリケーションをいったん削除したい場合がある。
その場合は
heroku apps:destroy --app app_nameを実行すればいい。
herokuにRailsアプリケーションをデプロイしたとき、rake taskに失敗するときの対処法
考慮した可能性
- assets precompile関係
- bundler関係 〇
- gem関係
- その他bundler 2.0.1を利用したrailsアプリケーションがherokuにデプロイできなくなったので原因を探ってみたらbundler 2.0.2対応が原因らしい。
つい最近まで、bundler のバージョンが2.0.1でも問題なく動作したが、おそらく2019年6月13日にリリースされたbundler 2.0.2に対応した影響のためか、2.0.1だと
could not find bundler(2.0.1) required by your /tmp/build_xxxxxx....xxxxx/Gemfile.lockとエラーが出る。
この場合、まずローカル環境にあるGemfile.lockを一度削除したのちに、以下のコマンドを打ち
gem install bundler -v 2.0.2bundler(2.0.2)をインストールし、bundle install (--path vendor/bundle)、bundle updateする。
- 投稿日:2019-07-08T12:27:48+09:00
Rails6 のちょい足しな新機能を試す48(Duration編)
はじめに
Rails 6 に追加されそうな新機能を試す第48段。 今回は、
Duration
編です。
Rails 6 では、Duration
の計算で小数点以下が丸められることが無くなりました。Ruby 2.6.3, Rails 6.0.0.rc1, Rails 5.2.3 で確認しました。Rails 6.0.0.rc1 は
gem install rails --prerelease
でインストールできます。$ rails --version Rails 6.0.0.rc1簡単なスクリプトを作る
今回は、動作確認用の簡単なスクリプトを書いて確認します。
bin/duration.rbduration = 0.4.seconds d = DateTime.parse('2019-01-01') d += duration d += duration d += duration d += duration d += duration p d > DateTime.parse('2019-01-01') p d == DateTime.parse('2019-01-01')Rails 5 では
0.4.seconds
を足すとき 整数に丸められて 0.seconds として加算されてしまいます。結果、
0.4.seconds
を5回足す前も後も変化がありません。$ bin/rails runner bin/duration.rb false trueRails 6 では
整数に丸められることが無くなったので、より正確な値が得られるようになります。
$ bin/rails runner bin/duration.rb true false試したソース
試したソースは以下にあります。
https://github.com/suketa/rails6_0_0rc1/tree/try048_duration参考情報
- 投稿日:2019-07-08T11:46:51+09:00
ブロックとスコープを使いこなしたい【メタプログラミングRuby】
はじめに
この記事では
ブロックって何?
からinstance_evalってやつすげー!
までやります。
ブロックとスコープの知識をスッキリさせたい方
にはぴったりだと思っています。ブロックとは
Rubyでは、
do ~ end
、または{ ~ }
の処理の塊のことをブロックと言います。一行で記述されるブロックは
{ ~ }
を使い、複数行のブロックにはdo ~ end
を使う慣習があります。ブロックを定義できるのは、メソッドを呼び出す時だけです。
ブロックはメソッドに渡され、メソッド内でyield
キーワードを使ってブロックをコールバックできます。block.rbdef my_method(a, b) a + yield(a, b) end p my_method(1, 2){ |x, y| x + y } # => 4ブロックの基本的な説明はここまでにして、早速内容に入っていきましょう。
内容
ブロックと束縛
ブロックを呼び出すためには、ローカル変数、インスタンス変数、selfといった
環境
が必要になります。
このオブジェクトに紐づけられている環境の事を束縛
とも言います。つまり、ブロックとは
コードと束縛両方の集まり
という事になります。ブロックにおいて、コードは
ブロック内にあるコード
を使用し、束縛はブロックを定義した場所にある束縛
を使用します。そして、ブロックをメソッドに渡した時は、その束縛も一緒に運ばれていきます。
メソッドにある束縛はブロックからは見る事が出来ません。blocks.rbdef my_method my_var = "my_methodの変数" yield end my_var = "トップレベルの変数" p my_method { my_var } # => "トップレベルの変数"ブロックと束縛を理解するためには、スコープに関する知識を深める必要があります。
スコープゲート
スコープゲートとは、
スコープが変化する境界線
の事です。(スコープに関しての基本的な説明は省きます)
Rubyでは、下記の3つがスコープゲートになります。
- クラス定義
- モジュール定義
- メソッド
実際に下記のコードを見ながら確認していきましょう。
scope_gate.rbtop_level_var = "トップレベルの変数" local_variables # => [:top_level_var, :obj] class MyClass #クラス定義 my_class_var = "マイクラスの変数" local_variables # => [:my_class_var] def my_method # メソッド定義 my_method_var = "マイメソッドの変数" local_variables # => [:my_method_var] end local_variables # => [:my_class_var] end local_variables # => [:top_level_var, :obj]このような感じですね。
それぞれのスコープ内で束縛が変わっている事が確認できると思います。
思わぬ所で、予期せぬ変数が参照されるエラーが発生し難くなり便利ですね。しかし、Rubyを使っていると、スコープゲートを超えて束縛を渡したい局面に遭遇する事があります。
そういう時は
フラットスコープ
を使っていきましょう。スコープゲートを超えて束縛を渡す(フラットスコープ)
クラス定義ではなく、メソッド呼び出しを使用する
事でスコープゲートを超えることが可能になります。
(正確には、スコープゲートを超えているわけではなく、スコープゲートを使用していないだけです)先程の
scope_gate.rb
を書き換えていきましょう。
もしMyClass内でtop_level_var
を使いたい時はこのように書きます。scope_gate.rbtop_level_var = "トップレベルの変数" local_variables # => [:top_level_var, :obj] MyClass = Class.new do #クラス定義 my_class_var = "マイクラスの変数" local_variables # => [:my_class_var, :top_level_var, :obj] def my_method # メソッド定義 my_method_var = "マイメソッドの変数" local_variables # => [:my_method_var] end local_variables # => [:my_class_var, :top_level_var, :obj] end local_variables # => [:top_level_var, :obj]my_method内でmy_class_varを使いたい場合も、先程と同じように、メソッド呼び出しに書き換えます。
scope_gate.rbtop_level_var = "トップレベルの変数" class MyClass #クラス定義 my_class_var = "マイクラスの変数" local_variables # => [:my_class_var] define_method :my_method do # メソッド定義 my_method_var = "マイメソッドの変数" local_variables # => [:my_method_var, :my_class_var] end local_variables # => [:my_class_var] end local_variables # => [:top_level_var, :obj]技術的には、この方法の事を
入れ子構造のレキシカルスコープ
と呼びます。
しかしRubyに限ってはフラットスコープ
と呼ばれる事が多いようです。フラットスコープを応用すれば、自由自在にスコープを組み合わせる事ができます。
共有スコープ
という方法もありますが、上記の2つ(スコープゲート、フラットスコープ)を理解していれば自然と使えるようになる方法なので省きます。(気になる方は調べてみてください)コードと束縛を好きなように組み合わせる(instance_eval)
instance_evalというメソッドがあります。
instance_evalに渡したブロックは、オブジェクトのコンテキストで評価されます。
つまり、レシーバがselfにしてから評価されるので、レシーバのprivateメソッドやインスタンス変数などにもアクセスが可能になるという事です。また、
他のブロックと同じように、instance_evalを定義した時の束縛も見ることが出来ます。
それでは、コードを見ていきましょう。
instance_eval.rbtop_level_var = "トップレベルの変数" class MyClass def initialize @my_instance_var = "マイクラスのインスタンス変数" end end my_class = MyClass.new my_class.instance_eval do self # => #<MyClass:0x00007fc4b08bdcc0 @my_instance_var="マイクラスのインスタンス変数"> top_level_var # => "トップレベルの変数" @my_instance_var # => "マイクラスのインスタンス変数" endこのような感じです。
instance_evalに渡したブロックのことは、
インスタンス探査機
と呼ばれます。
オブジェクトの内部を探査して、そこで実行するコードだからです。まとめ
ブロック
を上手く扱うためには、束縛
を理解する必要がありました。
そして、束縛の住処であるスコープ
も学習しました。
instance_eval
はおまけのような感じです。気になる所や、間違っている所等ありましたらコメントください!!
- 投稿日:2019-07-08T11:20:07+09:00
#Ruby の pry のドルマークで元のコードを読む的なアレはなんなのか? show-source ですよ。
What is the purpose of reading the original code at Ruby's pry's dollar mark? It is a show-source.
[3] pry(main)> $ Class From: /Users/yumainaura/.rbenv/versions/2.6.1/lib/ruby/2.6.0/json/common.rb @ line 448: Class name: Class Number of lines: 9 class ::Class # Returns true if this class can be used to create an instance # from a serialised JSON string. The class has to implement a class # method _json_create_ that expects a hash as first parameter. The hash # should include the required data. def json_creatable? respond_to?(:json_create) end end[2] pry(main)> show-source Class From: /Users/yumainaura/.rbenv/versions/2.6.1/lib/ruby/2.6.0/json/common.rb @ line 448: Class name: Class Number of lines: 9 class ::Class # Returns true if this class can be used to create an instance # from a serialised JSON string. The class has to implement a class # method _json_create_ that expects a hash as first parameter. The hash # should include the required data. def json_creatable? respond_to?(:json_create) end endOriginal by Github issue
- 投稿日:2019-07-08T02:18:25+09:00
amcharts 4 Demos を使ってグラフを作成
About
amcharts関連の日本語文献のあまりの少なさから、誰かのお役に立てればと思い記載しています!
Environment
この記事ではmacbook(unix)にインストールしたruby 2.5.1p57, Rails 5.2.3を使っています。
amchartsとは
amcharts
javascriptでチャートを描画するためのフレームワークです。 棒、エリア、列、バー、パイ、XY、散布、ローソク足のようなチャートを描画することが出来ます。(参考文献)
http://mikawatan.hatenablog.com/entry/2016/10/09/171130Demosとは
amchartsが提供しているサンプルの一覧です。
実際にご覧いただいた方が早いと思います。下記よりご確認ください。
https://www.amcharts.com/demos/使用方法
HTML内でResources(CDN)を記載することで利用できます。
商用利用の場合も、チャート内のamchartsアイコン(amchartsへのリンク)を削除しなければそのまま使用可能です。(参考文献)
https://www.amcharts.com/online-store/
Free license
"Use anywhere you want as long as you don't mind a small amCharts attribution on charts"Rails環境ですが、Demoをわかりやすく、そのまま使用するために、
今回は各viewファイル内に直接記載していきます。index.html.erb<!-- Resources --> <script src="https://www.amcharts.com/lib/4/core.js"></script> <script src="https://www.amcharts.com/lib/4/charts.js"></script> <script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>これで準備完了です。
導入方法
今回は、こちらのチャートを扱います。
ページ内で下にスクロールすると、Demo sourceが置いてありますので、コピーします。Demo_source<!-- Styles --> <style> #chartdiv { width: 100%; height: 500px; } </style> <!-- Resources --> <script src="https://www.amcharts.com/lib/4/core.js"></script> <script src="https://www.amcharts.com/lib/4/charts.js"></script> <script src="https://www.amcharts.com/lib/4/themes/animated.js"></script> <!-- Chart code --> <script> am4core.ready(function() { // Themes begin am4core.useTheme(am4themes_animated); // Themes end // Create chart instance var chart = am4core.create("chartdiv", am4charts.XYChart); chart.scrollbarX = new am4core.Scrollbar(); // Add data chart.data = [{ "country": "USA", "visits": 3025 }, { "country": "China", "visits": 1882 }, { "country": "Japan", "visits": 1809 }, { "country": "Germany", "visits": 1322 }, { "country": "UK", "visits": 1122 }, { "country": "France", "visits": 1114 }, { "country": "India", "visits": 984 }, { "country": "Spain", "visits": 711 }, { "country": "Netherlands", "visits": 665 }, { "country": "Russia", "visits": 580 }, { "country": "South Korea", "visits": 443 }, { "country": "Canada", "visits": 441 }]; // Create axes var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis()); categoryAxis.dataFields.category = "country"; categoryAxis.renderer.grid.template.location = 0; categoryAxis.renderer.minGridDistance = 30; categoryAxis.renderer.labels.template.horizontalCenter = "right"; categoryAxis.renderer.labels.template.verticalCenter = "middle"; categoryAxis.renderer.labels.template.rotation = 270; categoryAxis.tooltip.disabled = true; categoryAxis.renderer.minHeight = 110; var valueAxis = chart.yAxes.push(new am4charts.ValueAxis()); valueAxis.renderer.minWidth = 50; // Create series var series = chart.series.push(new am4charts.ColumnSeries()); series.sequencedInterpolation = true; series.dataFields.valueY = "visits"; series.dataFields.categoryX = "country"; series.tooltipText = "[{categoryX}: bold]{valueY}[/]"; series.columns.template.strokeWidth = 0; series.tooltip.pointerOrientation = "vertical"; series.columns.template.column.cornerRadiusTopLeft = 10; series.columns.template.column.cornerRadiusTopRight = 10; series.columns.template.column.fillOpacity = 0.8; // on hover, make corner radiuses bigger var hoverState = series.columns.template.column.states.create("hover"); hoverState.properties.cornerRadiusTopLeft = 0; hoverState.properties.cornerRadiusTopRight = 0; hoverState.properties.fillOpacity = 1; series.columns.template.adapter.add("fill", function(fill, target) { return chart.colors.getIndex(target.dataItem.index); }); // Cursor chart.cursor = new am4charts.XYCursor(); }); // end am4core.ready() </script> <!-- HTML --> <div id="chartdiv"></div>お気づきでしょうか?既に使用方法欄で記載した内容は、こちらのDemo sourceに必ず記載されているものです。なので、コピーをそのまま利用される方は、CDNを記載する必要はありません。
それでは、コピーした内容をそのままhtmlにペーストします。
index.html.erb<!--コピー内容をそのままペースト-->
編集方法
このままでは当然ですが、サンプルデータそのままになってしまいます。
各機能について(理解している部分)を記載していきます。Styles
cssです。フォントのサイズ等を指定できますが、指定できる項目には限りがあります。
index.html.erb<!-- Styles --> <style> #chartdiv { width: 100%; height: 500px; font-size: 20px; } </style>chart code
本体です。以下、コメントアウトにて詳細を記載します。
index.html.erb<!-- Chart code --> <script> am4core.ready(function() { // Themes begin // テーマです。 am4core.useTheme(am4themes_animated); // Themes end // Create chart instance //始めにインスタンスを作成しています。 var chart = am4core.create("chartdiv", am4charts.XYChart); //X軸方向のスクロールバーです。私はいらなかったのでコメントアウトしました。 chart.scrollbarX = new am4core.Scrollbar(); // Add data // ここにデータを入力していきます。 chart.data = [{ "country": "USA", "visits": 3025 }, { "country": "China", "visits": 1882 }, { "country": "Japan", "visits": 1809 }, { "country": "Germany", "visits": 1322 }, { "country": "UK", "visits": 1122 }, { "country": "France", "visits": 1114 }, { "country": "India", "visits": 984 }, { "country": "Spain", "visits": 711 }, { "country": "Netherlands", "visits": 665 }, { "country": "Russia", "visits": 580 }, { "country": "South Korea", "visits": 443 }, { "country": "Canada", "visits": 441 }]; // Create axes //ここからはX軸方向について var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis()); //データフィールドのカテゴリーです。今回はcountryですね。 categoryAxis.dataFields.category = "country"; //この辺りはあまり手を加える必要はないかと思います。 categoryAxis.renderer.grid.template.location = 0; categoryAxis.renderer.minGridDistance = 30; categoryAxis.renderer.labels.template.horizontalCenter = "right"; categoryAxis.renderer.labels.template.verticalCenter = "middle"; //文字の向きです。このままだと270度回転して縦方向の文字列になりますので、 //横向き希望の方は0に設定すると良いです。 categoryAxis.renderer.labels.template.rotation = 270; categoryAxis.tooltip.disabled = true; categoryAxis.renderer.minHeight = 110; //ここからはY軸方向について var valueAxis = chart.yAxes.push(new am4charts.ValueAxis()); valueAxis.renderer.minWidth = 50; // Create series // ようやくチャートの設定です。上記のデータフィールドの項目を使用します。 var series = chart.series.push(new am4charts.ColumnSeries()); series.sequencedInterpolation = true; // Y軸方向はvisits series.dataFields.valueY = "visits"; // X軸方向はcountry series.dataFields.categoryX = "country"; // tooltipはグラフにカーソルを当てると上部に表示される内容です。 series.tooltipText = "[{categoryX}: bold]{valueY}[/]"; series.columns.template.strokeWidth = 0; series.tooltip.pointerOrientation = "vertical"; series.columns.template.column.cornerRadiusTopLeft = 10; series.columns.template.column.cornerRadiusTopRight = 10; series.columns.template.column.fillOpacity = 0.8; // on hover, make corner radiuses bigger // ホバーした際の設定です。 var hoverState = series.columns.template.column.states.create("hover"); hoverState.properties.cornerRadiusTopLeft = 0; hoverState.properties.cornerRadiusTopRight = 0; hoverState.properties.fillOpacity = 1; series.columns.template.adapter.add("fill", function(fill, target) { return chart.colors.getIndex(target.dataItem.index); }); // Cursor // カーソル設定です。非表示にしたい場合は、以下のコメントアウトのように記載します。 chart.cursor = new am4charts.XYCursor(); // chart.cursor.lineX.disabled = true; // chart.cursor.lineY.disabled = true; }); // end am4core.ready() </script>HTML
最後に、HTMLを記載します。
index.html.erb<!-- HTML --> <div id="chartdiv"></div>リンクの貼り方
chartにリンクを貼りたい!という方、必見です。
下記の様な記載で実装できます。index.html.erb<script> // データの項目にidを追加 chart.data = [{ "country": "USA", "visits": 3025, "id": 0 }, { "country": "China", "visits": 1882, "id": 1 }, { "country": "Japan", "visits": 1809, "id": 2 }, { "country": "Germany", "visits": 1322, "id": 3 }, { "country": "UK", "visits": 1122, "id": 4 }]; // script内で下記記載。最後の{}内の記載方法でid毎のページを呼び出せます。 series.columns.template.url = `http://hoge/users/huga/sleeps/{id.urlEncode()}`;実装例
最後に、私が作成した"sommeil"というアプリケーションのコードの内、一部のamcharts部分を参考までに記載します。
こちらのコードでは、オリジナル要素としてチャートのグラフ内から詳細ページに飛べる仕組みを導入しています。
この部分に関して、いくらGoogle先生に聞いても出てこなかったので、記事を執筆しました...。全編はgithubをご覧下さい。
実装イメージについては、グローバルIPで恐縮ですが公開中です。(sommeil)_chart2.html.erb</style> <!-- Resources --> <script src="https://www.amcharts.com/lib/4/core.js"></script> <script src="https://www.amcharts.com/lib/4/charts.js"></script> <script src="https://www.amcharts.com/lib/4/themes/animated.js"></script> <!-- Chart code --> <script> am4core.ready(function () { // Themes begin am4core.useTheme(am4themes_animated); // Themes end // Create chart instance var chart = am4core.create("chartdiv", am4charts.XYChart); //chart.scrollbarX = new am4core.Scrollbar(); // Add data var sleeping_times = '<%= @sleeping_times %>'.replace(/\[/g, "").replace(/\]/g, "").split(','); var dates = '<%= @dates %>'.replace(/\[/g, "").replace(/\]/g, "").replace(/\s/g, "").split(','); var sleep_ids = '<%= @sleep_ids %>'.replace(/\[/g, "").replace(/\]/g, "").replace(/\s/g, "").split( ','); console.log(sleep_ids); chart.data = [{ "day": dates[0], "time": sleeping_times[0], "id": sleep_ids[0] }, { "day": dates[1], "time": sleeping_times[1], "id": sleep_ids[1] }, { "day": dates[2], "time": sleeping_times[2], "id": sleep_ids[2] }, { "day": dates[3], "time": sleeping_times[3], "id": sleep_ids[3] }, { "day": dates[4], "time": sleeping_times[4], "id": sleep_ids[4] }, { "day": dates[5], "time": sleeping_times[5], "id": sleep_ids[5] }, { "day": dates[6], "time": sleeping_times[6], "id": sleep_ids[6] }, { "day": dates[7], "time": sleeping_times[7], "id": sleep_ids[7] }, { "day": dates[8], "time": sleeping_times[8], "id": sleep_ids[8] }, { "day": dates[9], "time": sleeping_times[9], "id": sleep_ids[9] }, { "day": dates[10], "time": sleeping_times[10], "id": sleep_ids[10] }, { "day": dates[11], "time": sleeping_times[11], "id": sleep_ids[11] }, { "day": dates[12], "time": sleeping_times[12], "id": sleep_ids[12] }, { "day": dates[13], "time": sleeping_times[13], "id": sleep_ids[13] }]; // Create axes var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis()); categoryAxis.dataFields.category = "day"; categoryAxis.renderer.grid.template.location = 0; categoryAxis.renderer.minGridDistance = 30; categoryAxis.renderer.labels.template.horizontalCenter = "middle"; categoryAxis.renderer.labels.template.verticalCenter = "middle"; categoryAxis.renderer.labels.template.rotation = 0; categoryAxis.renderer.labels.template.fill = am4core.color("#fff"); categoryAxis.tooltip.disabled = true; categoryAxis.renderer.minHeight = 110; var valueAxis = chart.yAxes.push(new am4charts.ValueAxis()); valueAxis.renderer.minWidth = 50; valueAxis.renderer.labels.template.fill = am4core.color("#fff"); // Create series var series = chart.series.push(new am4charts.ColumnSeries()); series.sequencedInterpolation = true; series.dataFields.valueY = "time"; series.dataFields.categoryX = "day"; series.tooltipText = "[{categoryX}: bold]{valueY}[/]"; series.columns.template.strokeWidth = 0; series.tooltip.pointerOrientation = "vertical"; //Go to detail page var current_user_id = '<%= @user.id %>' series.columns.template.url = `http://52.194.48.235/users/${current_user_id}/sleeps/{id.urlEncode()}`; series.columns.template.column.cornerRadiusTopLeft = 10; series.columns.template.column.cornerRadiusTopRight = 10; series.columns.template.column.fillOpacity = 0.8; // on hover, make corner radiuses bigger var hoverState = series.columns.template.column.states.create("hover"); //hoverState.properties.cornerRadiusTopLeft = 0; //hoverState.properties.cornerRadiusTopRight = 0; hoverState.properties.fillOpacity = 1; series.columns.template.adapter.add("fill", function (fill, target) { return chart.colors.getIndex(target.dataItem.index); }); // Cursor chart.cursor = new am4charts.XYCursor(); chart.cursor.lineX.disabled = true; }); // end am4core.ready() </script> <!-- HTML --> <div id="chartdiv"></div>最後に
合わせてpiechartの作成方法なども近日公開予定です。
ご覧いただき、ありがとうございました。筆者について
TECH::EXPERTにて4月よりruby, railsを学習している未経験エンジニアです。
記載内容に不備・不足があればご指摘いただけると幸いです。
至らぬ点ばかりですので、改善点がありましたらどんどんご指摘下さい!
- 投稿日:2019-07-08T02:02:21+09:00
capistrano3でpumaの運用ー設定ファイルの管理
railsユーザの多くがつかってるのではないかと思われるcapistranoとpuma。この組み合わせで、どうやってpuma起動時に任意の設定を指定するか、意外とわけわからんのでまとめた。
pumaのデフォルト仕様
なにも指定せずにpuma起動すると、
config/puma/{env}.rb
,config/puma.rb
, の優先順位で設定ファイルが読まれる。
puma/configuration.rb at 0cd28f373a245eca4e717bff1415b97bbc2356d2 · puma/puma · GitHubちなみに、ローカル環境などで
rails server
したときはどうも、puma
,thin
,webrick
の順にrackハンドラを探して見つかったやつで起動するっぽい。最近のrailsではrails new
するとpumaが入るので、特にいじらないかぎりrails server
したときにはpumaが起動すると思っていい。
rack/handler.rb at 2.0.7 · rack/rack · GitHub% rails server => Booting Puma => Rails 5.2.3 application starting in development => Run `rails server -h` for more startup options Puma starting in single mode...つまり、ローカルで起動するpumaの挙動を変更したい場合は、
config/puma/development.rb
をつくるか、config/puma.rb
をつくってrails server
起動すればいい。capistranoをつかう場合
しかし、capistrano3-pumaをデフォルト設定でつかう場合、
config/puma/{env}.rb
,config/puma.rb
は完全無視される。1
代わりに、capistrano3-pumaが持つ設定ファイル生成機能(後述)をもとに新規設定ファイルを作成し、デプロイサーバにアップロードし、それをpuma起動時に読むようになる。ここでcapistrano & pumaの設定ファイルの運用方針が、大きく2つの道にわかれる。
pumaオリジナル
config/puma/{env}.rb
,config/puma.rb
で運用するcapistrano3-pumaが提供する設定ファイル機能をつかわずに、従来のpumaの運用方法でやる方法。設定を緻密に詳細に指定したいときはこっちのほうがたぶんやりやすい。
必要な設定を逐一記述して、リポジトリにpushしておく。2
あとは、deploy.rb
またはdeploy/{env}.rb
に、設定ファイルのパスpuma_conf
を設定すればいい。
Why it builds a new puma.config instead of using config/puma/production.rb? (question) · Issue #244 · seuros/capistrano-puma · GitHub具体的にはこういう設定。
set :deploy_to, '/path/to/app' set :puma_conf, "#{current_path}/config/puma/staging.rb" # `current_path`の値は`deploy_to`によって決まるので、 # `puma_conf`は`deploy_to`よりあとに指定する点に注意!こうすると、capistrano経由でpumaを起動するときに、
-C /path/to/app/current/config/puma/staging.rb
が指定されてくれる。% bundle exec cap staging puma:start 00:00 puma:start using conf file /path/to/app/current/config/puma/staging.rb 01 bundle exec puma -C /path/to/app/current/config/puma/staging.rb --daemon ...capistrano3-pumaの設定ファイル生成機能をつかって運用する
capistrano3-pumaには設定ファイル生成機能
puma:config
がある。pumaの設定をがっちりかけなくてもそれなりの設定で動いてくれる。
cap {env} deploy:check
が実行されるときに、存在しなければ/path/to/app/shared/puma.rb
(またはpuma_conf
に指定されたパス)を自動生成して各サーバにアップロードする。3具体的にはこのとき、cap {env} puma:config
というタスクが実行されている。
cap {env} puma:config
を単体で実行すると、既存ファイルが存在するしないに関係なくcapistrano管理上の設定で上書きされる。ふだんcapistrano運用者がつかうコマンドは
cap {env} deploy
(内部的にdeploy:check
を実行する)のはずなので、設定ファイルははじめの一回だけ自動生成され、その後都度更新されることはない。
だから、サーバ上の設定を手動で直接かきかえる運用も可能。ただし、そういう運用をおこなう場合は、cap {env} puma:config
の実行に注意。手動で加えた設定は消されてしまうので。% bundle exec cap staging puma:config 00:00 puma:config Uploading /path/to/app/shared/puma.rb 100.0% # /path/to/app/shared/puma.rb # を確認すると、設定ファイルが追加されてたり、上書きされてるのがわかる # /path/to/app/shared/puma.rb # ちなみに、このパスを変えたい場合は、前述した要領で`puma_conf`を指定すればいいcapistrano管理上のpuma設定を上書きする
cap {env} puma:config
が実行されたときに生成されるファイルは、デフォルトでは以下に定義された値がコピーされる。capistrano-puma/puma.rb at v3.1.1 · seuros/capistrano-puma · GitHub
これらの設定値はもちろんcapistrano設定で全部上書きできる。
config/deploy.rb
やconfig/deploy/{env}.rb
に以下のように記述する。(すでに紹介したpuma_conf
の上書きと同じ要領)set :puma_env, 'staging' set :puma_threads, [4, 4] set :puma_workers, 0 set :puma_preload_app, false設定可能な設定値はこちらのリストも参考
GitHub - seuros/capistrano-puma: Puma integration for Capistrano念のためもう一度言うと、ここで設定した設定値はpuma起動時に直接読まれるわけではない。
puma:config
が実行されて、shared/puma.rb
(またはpuma_conf
で指定されたパス)ファイルにその値がハードコードされ、それが起動時(cap {env} puma:start
)に読み込まれることによってpumaに渡る。
つまり、puma_threads
,puma_workers
などのcapistrano管理上のpuma設定は、cap {env} puma:config
が実行されてはじめてリリースされる。capistrano管理上のpuma設定をプログラムする
ここまで見ると、設定ファイル生成機能はライトにつかえる。しかしpumaのチューニングに余念がない人々にとっては、これだけでは足りない点がある。
puma設定には、設定の「値」だけではなく、「プログラム」がかけるのである。たとえば、pumaプロセスがフォークしたときのコールバックなどをカスタム設定できる仕組みになっている。
GitHub - puma/puma: A Ruby/Rack web server built for concurrencyこういうかんじで。
on_worker_boot do # configuration here end before_fork do # configuration here endこういうことをやりたくなったら4、はじめのpumaオリジナル設定ファイルの運用に切り替えるのをおすすめするが、一応設定ファイル生成機能をつかってもできる。
cap {env} puma:config
が実行されるときに、生成される設定ファイルのテンプレートがこれ。
capistrano-puma/puma.rb.erb at v3.1.1 · seuros/capistrano-puma · GitHubつまり、このテンプレートを差し替えることができれば、設定だけじゃなくてプログラムも自由にかける。
上記のテンプレートを参考に、config/deploy/templates
またはlib/capistrano/templates
以下にカスタムテンプレートをおけばいい。これらのディレクトリがどこで決まるのかは以下でわかる。
capistrano-puma/puma.rb at v3.1.1 · seuros/capistrano-puma · GitHub
config/deploy/templates/puma.rb
に以下のようなテンプレートを用意して(このテンプレートはbundle exec cap
が実行されるローカルになければいけない点に注意)#!/usr/bin/env puma puts "Hello!! This is custom puma template!!" directory '<%= current_path %>' rackup "<%=fetch(:puma_rackup)%>" environment '<%= fetch(:puma_env) %>' pidfile "<%=fetch(:puma_pid)%>" state_path "<%=fetch(:puma_state)%>"
puma:config
を実行% be cap staging puma:config 00:00 puma:config Uploading /path/to/app/current/config/puma/staging.rb 100.0%
shared/puma.rb
にリリースされてる。#!/usr/bin/env puma puts "Hello!! This is custom puma template!!" directory '/path/to/app/current' rackup "/path/to/app/current/config.ru" environment 'staging' pidfile "/path/to/app/shared/tmp/pids/puma.pid" state_path "/path/to/app/shared/tmp/pids/puma.state"
pumaのふるまいにはぱっと見変更が反映されたのかされてないのかわからない類のものも多いので、これではまる人は多い気がする。そして設定ファイルのパスに環境差分がおきてしまうのも歯がゆい! ↩
もしサーバごとに異なるpuma設定で運用したい場合、
git push
するのはよくない。 ↩
deploy:check
時に自動生成する機能は、たぶんCapfile
にinstall_plugin Capistrano::Puma
と書いていれば有効になっている。 ↩よくおこなわれるカスタム設定はすでにデフォルトのテンプレートに組み込まれてるので、ここを参照。capistrano-puma/puma.rb.erb at v3.1.1 · seuros/capistrano-puma · GitHub ↩
- 投稿日:2019-07-08T02:02:21+09:00
capistrano3でpumaの運用ー設定ファイルの管理どうする?
railsユーザの多くがつかってるのではないかと思われるcapistranoとpuma。この組み合わせで、どうやってpuma起動時に任意の設定を指定するか、意外とわけわからんのでまとめた。
pumaのデフォルト仕様
なにも指定せずにpuma起動すると、
config/puma/{env}.rb
,config/puma.rb
, の優先順位で設定ファイルが読まれる。
puma/configuration.rb at 0cd28f373a245eca4e717bff1415b97bbc2356d2 · puma/puma · GitHubちなみに、ローカル環境などで
rails server
したときはどうも、puma
,thin
,webrick
の順にrackハンドラを探して見つかったやつで起動するっぽい。最近のrailsではrails new
するとpumaが入るので、特にいじらないかぎりrails server
したときにはpumaが起動すると思っていい。
rack/handler.rb at 2.0.7 · rack/rack · GitHub% rails server => Booting Puma => Rails 5.2.3 application starting in development => Run `rails server -h` for more startup options Puma starting in single mode...つまり、ローカルで起動するpumaの挙動を変更したい場合は、
config/puma/development.rb
をつくるか、config/puma.rb
をつくってrails server
起動すればいい。capistranoをつかう場合
しかし、capistrano3-pumaをデフォルト設定でつかう場合、
config/puma/{env}.rb
,config/puma.rb
は完全無視される。1
代わりに、capistrano3-pumaが持つ設定ファイル生成機能(後述)をもとに新規設定ファイルを作成し、デプロイサーバにアップロードし、それをpuma起動時に読むようになる。ここでcapistrano & pumaの設定ファイルの運用方針が、大きく2つの道にわかれる。
pumaオリジナル
config/puma/{env}.rb
,config/puma.rb
で運用するcapistrano3-pumaが提供する設定ファイル機能をつかわずに、従来のpumaの運用方法でやる方法。設定を緻密に詳細に指定したいときはこっちのほうがたぶんやりやすい。
必要な設定を逐一記述して、リポジトリにpushしておく。2
あとは、deploy.rb
またはdeploy/{env}.rb
に、設定ファイルのパスpuma_conf
を設定すればいい。
Why it builds a new puma.config instead of using config/puma/production.rb? (question) · Issue #244 · seuros/capistrano-puma · GitHub具体的にはこういう設定。
set :deploy_to, '/path/to/app' set :puma_conf, "#{current_path}/config/puma/staging.rb" # `current_path`の値は`deploy_to`によって決まるので、 # `puma_conf`は`deploy_to`よりあとに指定する点に注意!こうすると、capistrano経由でpumaを起動するときに、
-C /path/to/app/current/config/puma/staging.rb
が指定されてくれる。% bundle exec cap staging puma:start 00:00 puma:start using conf file /path/to/app/current/config/puma/staging.rb 01 bundle exec puma -C /path/to/app/current/config/puma/staging.rb --daemon ...capistrano3-pumaの設定ファイル生成機能をつかって運用する
capistrano3-pumaには設定ファイル生成機能
puma:config
がある。pumaの設定をがっちりかけなくてもそれなりの設定で動いてくれる。
cap {env} deploy:check
が実行されるときに、存在しなければ/path/to/app/shared/puma.rb
(またはpuma_conf
に指定されたパス)を自動生成して各サーバにアップロードする。3具体的にはこのとき、cap {env} puma:config
というタスクが実行されている。
cap {env} puma:config
を単体で実行すると、既存ファイルが存在するしないに関係なくcapistrano管理上の設定で上書きされる。ふだんcapistrano運用者がつかうコマンドは
cap {env} deploy
(内部的にdeploy:check
を実行する)のはずなので、設定ファイルははじめの一回だけ自動生成され、その後都度更新されることはない。
だから、サーバ上の設定を手動で直接かきかえる運用も可能。ただし、そういう運用をおこなう場合は、cap {env} puma:config
の実行に注意。手動で加えた設定は消されてしまうので。% bundle exec cap staging puma:config 00:00 puma:config Uploading /path/to/app/shared/puma.rb 100.0% # /path/to/app/shared/puma.rb # を確認すると、設定ファイルが追加されてたり、上書きされてるのがわかる # /path/to/app/shared/puma.rb # ちなみに、このパスを変えたい場合は、前述した要領で`puma_conf`を指定すればいいcapistrano管理上のpuma設定を上書きする
cap {env} puma:config
が実行されたときに生成されるファイルは、デフォルトでは以下に定義された値がコピーされる。capistrano-puma/puma.rb at v3.1.1 · seuros/capistrano-puma · GitHub
これらの設定値はもちろんcapistrano設定で全部上書きできる。
config/deploy.rb
やconfig/deploy/{env}.rb
に以下のように記述する。(すでに紹介したpuma_conf
の上書きと同じ要領)set :puma_env, 'staging' set :puma_threads, [4, 4] set :puma_workers, 0 set :puma_preload_app, false設定可能な設定値はこちらのリストも参考
GitHub - seuros/capistrano-puma: Puma integration for Capistrano念のためもう一度言うと、ここで設定した設定値はpuma起動時に直接読まれるわけではない。
puma:config
が実行されて、shared/puma.rb
(またはpuma_conf
で指定されたパス)ファイルにその値がハードコードされ、それが起動時(cap {env} puma:start
)に読み込まれることによってpumaに渡る。
つまり、puma_threads
,puma_workers
などのcapistrano管理上のpuma設定は、cap {env} puma:config
が実行されてはじめてリリースされる。capistrano管理上のpuma設定をプログラムする
ここまで見ると、設定ファイル生成機能はライトにつかえる。しかしpumaのチューニングに余念がない人々にとっては、これだけでは足りない点がある。
puma設定には、設定の「値」だけではなく、「プログラム」がかけるのである。たとえば、pumaプロセスがフォークしたときのコールバックなどをカスタム設定できる仕組みになっている。
GitHub - puma/puma: A Ruby/Rack web server built for concurrencyこういうかんじで。
on_worker_boot do # configuration here end before_fork do # configuration here endこういうことをやりたくなったら4、はじめのpumaオリジナル設定ファイルの運用に切り替えるのをおすすめするが、一応設定ファイル生成機能をつかってもできる。
cap {env} puma:config
が実行されるときに、生成される設定ファイルのテンプレートがこれ。
capistrano-puma/puma.rb.erb at v3.1.1 · seuros/capistrano-puma · GitHubつまり、このテンプレートを差し替えることができれば、設定だけじゃなくてプログラムも自由にかける。
上記のテンプレートを参考に、config/deploy/templates
またはlib/capistrano/templates
以下にカスタムテンプレートをおけばいい。これらのディレクトリがどこで決まるのかは以下でわかる。
capistrano-puma/puma.rb at v3.1.1 · seuros/capistrano-puma · GitHub
config/deploy/templates/puma.rb
に以下のようなテンプレートを用意して(このテンプレートはbundle exec cap
が実行されるローカルになければいけない点に注意)#!/usr/bin/env puma puts "Hello!! This is custom puma template!!" directory '<%= current_path %>' rackup "<%=fetch(:puma_rackup)%>" environment '<%= fetch(:puma_env) %>' pidfile "<%=fetch(:puma_pid)%>" state_path "<%=fetch(:puma_state)%>"
puma:config
を実行% be cap staging puma:config 00:00 puma:config Uploading /path/to/app/current/config/puma/staging.rb 100.0%
shared/puma.rb
にリリースされてる。#!/usr/bin/env puma puts "Hello!! This is custom puma template!!" directory '/path/to/app/current' rackup "/path/to/app/current/config.ru" environment 'staging' pidfile "/path/to/app/shared/tmp/pids/puma.pid" state_path "/path/to/app/shared/tmp/pids/puma.state"
pumaのふるまいにはぱっと見変更が反映されたのかされてないのかわからない類のものも多いので、これではまる人は多い気がする。そして設定ファイルのパスに環境差分がおきてしまうのも歯がゆい! ↩
もしサーバごとに異なるpuma設定で運用したい場合、
git push
するのはよくない。 ↩
deploy:check
時に自動生成する機能は、たぶんCapfile
にinstall_plugin Capistrano::Puma
と書いていれば有効になっている。 ↩よくおこなわれるカスタム設定はすでにデフォルトのテンプレートに組み込まれてるので、ここを参照。capistrano-puma/puma.rb.erb at v3.1.1 · seuros/capistrano-puma · GitHub ↩
- 投稿日:2019-07-08T01:06:42+09:00
今日学んだ事
7/7(日) 21:30〜0:20
●タグ
html.erb<span>〜</apan>・span囲った部分をインライン要素としてグループ化することができるタグ。
グループ化する事で、指定した範囲にスタイルシートを適用できたりする。html.erb<i>〜</i>・フォントを「イタリック体」にする
●averageメソッド
・ActiveRecordクラスのメソッド
使用例
カラム=score
テーブル=students
クラス=Student
scoreカラムのaverageを求めるstudents = students.all students.average.(:score) #scoreの平均を小数点ありの状態で返す小数点を四捨五入する場合
students = students.all students.average.(:score) round #roundメソッドを使用すると小数点を四捨五入する●present?メソッド
・配列の中身がなければfalseを返す
・レビューの評価の平均など出すときに、レビューがない作品にfalseを返したい時などに使う
例index.html.erb<% if product.reviews.present? %>このコードでレビューがない場合でもエラーが回避できる
●レシーバ
・インスタンスメソッドを利用するインスタンス自身の事
例products = Product.all #以下の式のレシーバはproducts products.limit(5)1.Productクラスからproductsインスタンスを生成
2.productsインスタンス自身がlimitメソッドを利用している
3.この時productsがレシーバとなる
ち・な・み・に
メソッドの呼び出しを「オブジェクト(インスタンス)にメッセージ(メソッド)を送る」と表現する。
今回の場合
「products(オブジェクト)にlimit(メッセージ)を送る」となる●selfメソッド
・インスタンスメソッド内にselfを記述すると、そのメソッドを利用したインスタンス(レシーバ)が代入された変数のように扱う事ができる
例def review.average self.review.average(:rate).roundうーん、よく分からない。。。。
●コントローラ内に記述されているlayout 'レイアウトファイル名'について
例
1.class MovieController < ApplicationController 2. layout 'movie' 3. 4. def index 5. @movies = Movie.all 6. end 7.end・MovieControllerのindexアクションが呼ばれたときに表示されるレイアウトはmovie.html.erbとなる。
・なにも指定しないとレイアウトファイルはapplication.html.erbとなる以上
- 投稿日:2019-07-08T01:06:30+09:00
メモ: Ubuntu18.04-desktop 仮想マシンに ruby v2.4.5環境を作る時に実行する一連のコマンド等
自分が再度同じ作業をする時に再利用するためのメモ。
以下ではデスクトップPC上で使用する仮想化ソフトで作る仮想マシンを前提にしてるけど、クラウドPlatform系でもだいたい同様。※なお、この記事では apt で管理されている rbenv は使わずに github から clone してセットアップする方法を扱う。
1. Ubuntu 18.04-desktop の iso イメージを入手
下記URL等から任意に欲しいisoイメージを取得する
http://cdimage-u-toyama.ubuntulinux.jp/releases/2. VMware や VirtualBox 等の仮想化ソフトを使ってVMを起動する
新規仮想マシンの作成とかそんなメニューから、インストーラディスクイメージファイルとして上記で取得したisoイメージを指定してインストールを進める。
仮想化ソフト側で「簡易インストール」みたいな機能を持ってれば、それを使うと楽ちん。3. 作業に必要なパッケージをセットアップする
- 仮想マシンのコンソールでターミナルソフトを起動して以下を実行
ip addr show # 母艦からsshでログインする時に指定するIPアドレスを確認 sudo apt install tasksel sudo tasksel install openssh-server service ssh status # sshサーバの起動状態を確認 ss -ln -t4 # sshサーバの使用ポート番号を確認 exit
- 母艦から ssh でログインして以下を実行
cd # HOMEディレクトリで作業 sudo apt install mysql-client libmysqlclient-dev # mysqlを使うrubyプロジェクトだから入れておく sudo apt install git sudo apt install curl sudo apt install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm5 libgdbm-dev git clone https://github.com/rbenv/rbenv.git ~/.rbenv echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc . .bashrc ~/.rbenv/bin/rbenv init echo 'eval "$(rbenv init -)"' >> ~/.bashrc . .bashrc curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash # rbenvの状態確認 git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build rbenv install 2.4.5 curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash # rbenvの状態確認 rbenv rehash rbenv shell 2.4.5 gem install bundlerあとはプロジェクト次第
ソースをGitリポジトリから取得したり、
母艦とディレクトリを共有する設定をしたり、
RubiMine等のリモートインタープリタ設定などなど、
プロジェクトに応じてあれこれやる。今回の記事は以上です。