【LT】Railsのyaml、巨大になる前になんとかしようという内容で発表してきました!
反応
heroku使ってると環境変数だから問題は起きないかな。そこまでアプリが膨大になってないってのもあるか #startup_rails
— Kiminari Homma (@kimihom) March 6, 2017
settings.yml って要はグローバル変数と同じなので、可能な限り Model とかに定数で置いておいて、どうしてもアプリ全体で必要なものがあったら yml にするぐらいがちょうど良いと思う #startup_rails
— 神速@リリカルエンジニア (@sinsoku_listy) March 6, 2017
EC2 Systems Managerで環境変数管理している人いるのだろうか。興味ある #startup_rails
— alumys (@alumys) March 6, 2017
Heroku と ElasticBeanstalk 使ってるのでわりと変数問題は起きなかったかな〜!でも使ってないと思うと地獄になるのか。つらい。 #startup_rails
— まさたん😉マッスルファースト (@ma3tk) March 6, 2017
会話など
- 啓蒙や教育を怠らない
- 機能を絞る(コアドメインで勝負する)
Qiitaに初投稿!「hamlでタグを改行しない方法の整理」という記事を書きました
はじめに
タイトルの通り、Qiitaデビューした。前々から投稿したいと思っていたのだが、「ネタがない」「時間がない」などの言い訳をしつつ渋っていましたが、この度初投稿しました。
なんでこの記事を書いたのか
以前働いていたところでは、デザインはデザイナーがhtml, cssまで書き、エンジニアはhamlで実装しているような形でした。 そこではしばしば、デザイナーの書いたhtmlと、hamlで書いて出力されるhtmlが微妙に違うということがしばしばありました。 その原因の1つが、hamlの強制改行によるものでした。
Railsエンジニア1年目の自分は、そもそもhtmlを直接書かないこと、hamlというものがあること、インデントがちょっとずれるだけでエラーでhtmlをrenderできないことなど四苦八苦。改行を操作する方法があるということにたどり着いたのはだいぶ後でした。
そこで、「haml 改行しない」でぐぐると、シンプルな記事がでてくるようになるといいなという思いからこの記事を書きました。
「ワニが改行を食べる」イメージ
答えはいたってシンプルで、> or <の「口が開いている方の改行」を消すという覚え方が良さそうです。 出典は公式ドキュメントです
まとめ
Qiita初投稿を終えて、だいぶハードルが低くなったので、今後も投稿していく
bootstrapとsimple_formのチュートリアル
目的
rails newしてから、bootstrapで管理画面を作るのに一番早い方法が何かを探っていた。
rails_adminやactive_admin等もあるが、今回はある程度自前カスタマイズを想定しているので、全部ビルトインみたいな感じのものは使わない。
bootstrap系のgemとsimple_formを使って、scaffoldでどこまでがんばれるかを見てみる。
対象読者
railsチュートリアル2章までを終えて、generate
コマンド等が実行できる人。
バージョン
$ ruby -v ruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-darwin14] $ rails -v Rails 5.0.1
サンプルなのでrubyバージョンは前後していても動くかと思います!
1. rails new〜bundle install
まずアプリケーションを作りましょう。今回は、テスト書かない、bundle installは後ほどまとめてということで-B
と-T
というオプションを渡します。
(詳しいオプションたちはrails new -h
していろいろ見てみましょう。)
$ rails new test_simple_form -B -T create create README.md create Rakefile create config.ru ... create vendor/assets/stylesheets/.keep remove config/initializers/cors.rb $ cd test_simple_form
次に、Bundlerで使用するGemfileをテキストエディタで編集します。以下の内容に書き換えてください。
source 'https://rubygems.org' git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") "https://github.com/#{repo_name}.git" end gem 'rails', '5.0.1' gem 'sqlite3', '1.3.13' gem 'puma', '3.7.0' gem 'sass-rails', '5.0.6' gem 'uglifier', '3.0.4' gem 'coffee-rails', '4.2.1' gem 'jquery-rails', '4.2.2' gem 'turbolinks', '5.0.1' gem 'jbuilder', '2.6.1' gem 'record_tag_helper', '1.0.0' gem 'bootstrap-sass', '3.3.7' gem 'simple_form', '3.4.0' group :development, :test do gem 'byebug', '9.0.6', platform: :mri end group :development do gem 'bootstrap-generators', '3.3.4' gem 'web-console', '3.4.0' gem 'listen', '3.0.8' gem 'spring', '2.0.1' gem 'spring-watcher-listen', '2.0.1' end
gemをインストールします。
$ bundle install
今回のチュートリアルではbootstrap-sass
、simple_form
、bootstrap-generators
の機能がみそになります。bootstrap-generators
は今日時点でrails5対応が終わっていないようなので、generateだけやってgemfileからけしてもらってもOK
2. generate xx
各種初期化用のgenerateコマンドを実行していきます。
まずはbootstrapのスタイルシートとlayoutファイル、scaffold用のテンプレートを作成するbootstrap:install
です。
途中application.html.erb
を上書くかどうか聞かれますが、OKなのでYを入力してenterを押しましょう。
$ rails g bootstrap:install ... Overwrite /path/to/app/test_simple_form/app/views/layouts/application.html.erb? (enter "h" for help) [Ynaqdh] Y ...
次にsimple_form
の設定ファイルをテンプレート作るコマンドです。
今回はbootstrap用の物を作るので、--bootstrap
というオプションを渡します。
途中lib/templates/erb/scaffold/_form.html.erb
を上書いていいか聞かれるので、Yを押してenter。bootstrap-generatorsがつくった_form.html.erbもありますが、今回はsimple_formのものを使います。
$ rails g simple_form:install --bootstrap ... Overwrite /path/to/app/test_simple_form/lib/templates/erb/scaffold/_form.html.erb? (enter "h" for help) [Ynaqdh] Y ...
リソース作成
scaffoldで何かリソースを作ってみましょう
$ rails g scaffold post title body:text published:boolean $ rails db:create $ rails db:migrate
そしてサーバを立ち上げていくつかリソースを追加してみましょう!
その他
今回のチュートリアルだけで行くと、simple_formは必要ありません。しかし、今後いろいろなformを作るにあたり、アプリケーション全体のformをsimple_formで統一しておくと色々ご利益があるということで、追加しています。
まとめ
railsで開発する場合は、最低限のものを提供するのにものすごく早いスピートで作ることが可能です。
要件が複雑な場合はそのままは使えないのですが、うまく仕事にとりいれていきたいですね。
これだけ抑えればOK!権限管理のDB設計デザインパターン
目的
最近仕事で権限管理の設計をやっていたのだが、設計でかなりはまってしまった。
今後ははまらないように、DB設計や判断基準をまとめておく。
ベースとなるパターン
ユーザとロールは多対1で、ロールとアビリティは多対多に関連している。
権限管理やりましょうという場合にはこれにしておけば大抵の複雑な要求には対応できる。
もしユーザが増えてロールとアビリティの管理が複雑になってきても、DB管理なのである程度自由にやりことができる。
ただし、ちとファーストステップとしてはやりすぎか。
ユーザ-ロールが多対多パターン
前の例に加え、ユーザもロールを多数持つパターン。各権限が互いに素に近い状態、例えばカスタマーサポートとマーケティング、そのどちらも担当する人みたいなケースが有る場合に使える。
アプリケーションが大きくなっていて、権限の数も数十くらいになってきた場合はこれか。
直接アビリティパターン
ユーザ一人ひとりに応じて、できることが完全に異なるパターン。業務が多岐に渡っていて、かつ分業が進んでいる場合に使える。
ただし、ベースパターンでも同様のことはできるため、このパターンが活躍する機会は少ない。
ロールだけパターン
この場合はロールを表すテーブルもしくはカラムを用いてロールの情報のみ保存し、何ができるかはプログラム側で判断する。
アビリティの変化、増減や見直しが少ない場合(社内用管理画面等)に向いている
ユーザ-ロールが多対多、多対1、ユーザにカラム追加など、いろいろあり
このパターンがcancancanと組み合わせるのに良さそう。
admin:booleanパターン
割合的に、管理機能が少なく、管理画面みたいなものが存在しない場合に用いる。
railsチュートリアルとかで出てくる。
no権限パターン
アカウント持っている人は全員がすべてのことをできるパターン。
そもそも使用する人数が少ない場合。
内部向け管理画面はこっちのケースも多い。そもそも必要ない人にはアカウントを発行しないパターン。
できればこれで乗り切りたいので「権限管理したい」という話がきたら、「その権限、本当に必要ですか?」と聞いてあげよう。
まとめ
ベースパターン、ロールだけパターン、admin:booleanパターンだけ覚えておけば、Rails小〜中規模アプリケーションならほぼ対応できそう!
【Railsチュートリアル】慣習的に正しいコードの書き方
目的
Railsチュートリアルで「このコードは慣習的に正しくない」という記述があるが、なぜなのかを説明する。
背景
Railsを仕事で書いていく上で、モデル同士の関連の定義とその使い方をわりと意識しているのだが、いつどこで勉強したのか怪しかった。
出典を明らかにしたかった。
該当の内容
https://railstutorial.jp/chapters/user_microposts?version=5.0#code-micropost_validity_test
前提として、以下のような、関連を持つモデルUser
とMicropost
があるとする。(DBのカラム等は自然に定義されているものとする)
# app/models/user.rb class User has_many :microposts end # app/models/micropost.rb class Micropost < ActiveRecord::Base belongs_to :user end
このときuserに紐づくmicropostを作りたいときは、まず思いつく方法としては以下のようなもの。
user = User.first Micropost.new(content: "test", user_id: user.id)
しかしこのやりかたは、このコードは慣習的に正しくないとRailsチュートリアルで言及されている。
実際は以下のようにする。
user = User.first user.microposts.build(content: "test")
習慣的に良いコードは何がいいのか
関連が明確になる
まずuser.microposts
というコードはUser
からMicropost
への関連を定義していないと動かない。user.microposts
というコード
を見るだけで関連が定義されていることが明確になる。
引数を減らせる
user_idを渡さなくて良い
主語をuserにすることができる
最初のレシーバがuser
になるため、「ユーザがマイクロポストを作る」という見方ができる
依存が少なくなる
Micropost.new
する場合はMicropost
というクラス、user_idという属性に依存しているが、user.microposts
の場合はUser
のmicroposts
という関連(インタフェース)に依存させられる。
まとめ
いろいろ理由をつけてみましたが、やはり一番は「慣習的に」なのでしょう。
業務でRailsを書いていると、先人の書いたコードに引っ張られついついこのあたりの「自然な書き方」を忘れがち。
誰に見せても恥ずかしくないようなRailsのコードを書けるよう、日々心がけたいものです。
その他
この辺はRubyMine使うと指摘してくれるのかな?
sendgrid-rubyを使ったSendGridでのメール送信
はじめに
この記事は「Sansan Advent Calendar 2016」7日目の記事です。
昨日はerikoobeさんによる「エンジニア未経験者が Ruby を学んでみた件」でした。
特に「2. 未経験者から見た、エンジニアの世界」は共感できたのと同時に、やる気のある人へのサポートはやっぱり大切だなと感じました。
この記事の概要
SendGridが公開しているgemを使って、webapi経由でメール送信する方法をざっくり紹介する。
背景
SendGridとは?
SendGridはクラウドベースのメール配信サービスです。smtpサーバを自前で運用することなくメール送信を行うことが出来ます。
この点ではAmazon Simple Email ServiceやMailChimpも同等の機能を持っています。
また、配信だけでなくバウンス後の処理や開封、クリック数取得等のさまざまことをwebapi経由で行えることも特徴の一つです。
なぜこの記事を書いたか
最近までたくさんメールを送る仕事をしていたのですが、smtp→webapiにかえることで配信時間をかなり圧縮出来たので、ぜひ布教していきたいと思いまとめてみました。
前提
SendGridのアカウント取得、apikeyの発行ができている。
サンプル
以下サンプルコードです。(gemのインストールは適宜おこなってください。)
require 'sendgrid-ruby' require 'dotenv' Dotenv.load API_KEY = ENV['API_KEY'] USERS = [ { email: 'to1@example.com', name: 'to1', fullname: 'to_user1' }, { email: 'to2@example.com', name: 'to2', fullname: 'to_user2' } ] mail = SendGrid::Mail.new mail.from = SendGrid::Email.new(email: 'from@example.com') mail.contents = SendGrid::Content.new(type: 'text/plain', value: "%name%さん\nあなたのフルネームは%fullname%です") USERS.each do |u| email, name, fullname = u.values_at(:email, :name, :fullname) sp = SendGrid::Personalization.new sp.to = SendGrid::Email.new(email: email) sp.subject = "#{name}さんこんにちは" sp.substitutions = SendGrid::Substitution.new(key: '%name%', value: name) sp.substitutions = SendGrid::Substitution.new(key: '%fullname%', value: fullname) mail.personalizations = sp end sg = SendGrid::API.new(api_key: API_KEY) response = sg.client.mail._('send').post(request_body: mail.to_json)
登場するclass
SendGrid::Mail
SendGrid::Mail#to_json
を呼ぶことで、現在持っているインスタンス変数からHash形式でrequest_bodyを作ります。
SendGrid::Mail#personalizations=
は、内部ではArray#push
を使っている。インスタンス変数@personalizations
を書き換えるわけではない。
SendGrid::Email
送信先、受信先のメールアドレスや表示名を扱うのに用いる。
SendGrid::Content
メールの本文、type(text, html)を指定するためのクラス。
SendGrid::Personalization
送信先、件名、substitutions等を扱うクラス。どんな項目が指定できるできるかは以下を参考に。
personalizations
気になる点
インタフェースがいけてない
SendGrid::Mail#personalizations=
とかSendGrid::Mail#to_json
とか、パット見と違う挙動なものが多いのでは??
ソースが読みにくい
gemのプロダクトコードはsendgrid/helpers/mail/mail.rbという1つのファイルに集約されている。
その他
本家のreadmeにはsendgrid/helpers/mail/mail.rbを使わない方法も記載されています。
without-mail-helper-class
参考までに上に書いたサンプルコードのhelper使わない版も書いてみました。
mail_info = { from: { email: 'from@example.com' }, content: [ { type: 'text/plain', value: "%name%さん\nあなたのフルネームは%fullname%です" } ] } mail_info[:personalizations] = USERS.map do |u| email, name, fullname = u.values_at(:email, :name, :fullname) { to: [{ email: email }], subject: "#{name}さんこんにちは", substitutions: { '%name%': name, '%fullname%': fullname } } end sg = SendGrid::API.new(api_key: API_KEY) response = sg.client.mail._('send').post(request_body: mail_info)
SendGrid::API
もだいたい40行くらいのコードなので、sendgrid-rubyを使わない方法もありかなとは思いまいた。
その際はsendgrid-ruby
が内部的に使っているruby_http_client
を使うもよし、メールを送信するだけなら全て自分で書いてしまってもいいかなと思います。(webapiにリクエスト送るだけだし。)
まとめ
以上、SendGridが公開しているgemを使って、webapi経由でメール送信する方法でした。 apiクライアントの設計に興味が湧いてきたので、他のgemのソースも読んでみたいと思います!
さいごに
明日はTakeruTakahashiさんの記事です!引き続き「Sansan Advent Calendar 2016」をお楽しみに!
【Ruby】【Rails】RAILS ANTIPATTERNS、chapter3 viewのまとめ
目的
RAILS ANTIPATTERNSをのchapter3(view)の箇所を読んだのでまとめました。
- 作者: Chad Pytel,Tammer Saleh
- 出版社/メーカー: Addison-Wesley Professional
- 発売日: 2010/11/09
- メディア: Kindle版
- この商品を含むブログを見る
PHPitis(PHPっぽくなりがち)
ドメインロジックとか複雑な表示ロジックとか、全部ビュー(app/views
の下にあるファイル)に書かれがちだよねという話です。
解決策:View Helper勉強しよ!
詳しくは書かないけど、form_for
, render
, content_for
, yield(:symbol) || 'default'
, content_for?
について書かれていました。
form_for
は本当にいろいろ使えてすごいのだけど、まだまだ使い切れていない感がある。
content_for
の並びで言うと、provide
もぜひ使いたいですね。
http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-provide
解決策:いい感じにモデルにメソッド追加しよ!
以下のようなコードがあるとする。
<% if current_user && (current_user == @post.user || @post.editors.include?(current_user)) && @post.editable? && @post.user.active? %> <%= link_to 'Edit this post', edit_post_url(@post) %> <% end %>
これはモデルにメソッドを定義してあげて、view側は以下のような形にする。
<% if @post.editable_by?(current_user) %> <%= link_to 'Edit this post', edit_post_url(@post) %> <% end %>
すっきり!
メソッドを定義する場所としては、まあまあ当たり前だけどコントローラで使う場合はモデル、そうじゃなければhelperに置くと書いてあった。
そのif文、helperに移動しよ!
app/views/alerts/index.html.erb
が以下のようになっているとする。
<div class="feed"> <% if @project %> <%= link_to "Subscribe to #{@project.name} alerts.", project_alerts_url(@project, :format => :rss), :class => "feed_link" %> <% else %> <%= link_to "Subscribe to these alerts.", alerts_url(format => :rss), :class => "feed_link" %> <% end %> </div>
この場合はapp/helpers/alerts_helper.rb
を作って以下のようにメソッドを追加する。(若干仕様変わってる?)
module AlertsHelper def rss_link(project = nil) link_to("Subscribe to these #{project.name if project} alerts.", alerts_rss_url(project), class: 'feed_link') end def alerts_rss_url(project = nil) if project project_alerts_url(project, format: :rss) else alerts_url(:rss) end end end
view側のapp/views/alerts/index.html.erb
は以下。
<div class="feed"> <%= rss_link(@project) %> </div>
いつも<div class="feed">
が必要な場合は、content_tagを使ってhelperにうつしてしまってもいいとありました。
その場合はlink_to
じゃなくてcontent_tag :a
をあえて使ってもいいと書いてました。
ちょっとした感想
最後の、「helperに移動しよ!」的な話はいつも悩むところなのと、helperメソッドでcontent_tagをもりもり書くのなら、パーシャルでいいのでは?と思ったりします。
でも今回の= rss_link(@project)
が、= render rss_link, project: @project
になるのはイマイチ感がありますね…なんでだろ。
viewにかかわらず、Railsはやりたいことを色んな方法で実装することができるので、一人で作っているときでさえ統一感がなくなることが有ります。rubocopで潰せるところはいいけど、自分が統一感を持って実装出来ているかどうかたまに振り返りながらすすめていきたいですね。