読者です 読者をやめる 読者になる 読者になる

Shiho's engineering blog

誰でもみれるノートのようなもの

rails メール一斉送信 #自分用メモ

/tmp/users.csv にデータを出力

SELECT * FROM users INTO OUTFILE ‘/tmp/users.csv’ FIELDS TERMINATED BY ‘,’ OPTIONALLY ENCLOSED BY ‘“’;

一斉送信

 User.all.each do |user|
        MessageMailer.send_diffusion(@message, user).deliver!
      end

管理画面からメール作成、一斉送信

# app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
  def all_notify(users, title, content)
    mail from: 'mail_sender@address.jp'
    mail to: users.map{|u| u.mail}
    mail subject: title
    @content = content
  end
end

# app/view/user_mailer/all_notify.txt.erb

こんにちは
ここから本文--------------
#{@content}
---------------------ここまで

# app/controllers/user_controller.rb メールを送信するコントローラ

# 前略
  def send_to_all
    title = params[:title]
    content = params[:content]
    users = User.all
    UserMailer.all_notify(users, title, content).deliver
  end
# 後略

参考

qiita.com

teratail.com

railsの画像はどこに置くのが良いのか?

どう選ぶのか

結論、assetのコンパイル対象にしたいならassets以下、ブラウザのキャッシュコントロールのためのバージョン管理化に置かれなくていいならpublic以下に置くと良い。 基本的にはシステム側で作る画像はassets以下、ユーザー側のアップロードする画像はpublic配下に置いておくと良い。

だけど、背景画像にしたい時は

public以下に置くようにしている。 気持ち表示速度が速い気がするし、ファイルの書き換えもあまり起こらないと思われるため。 image_urlを使って表示するより、シンプルな気がするため。

先頭に/ スラッシュをつけるとpublic以下になる

<%= image_tag '/images/hoge.png' %>

なら、指定配置場所がpublic/images/hoge.pngだと判断される。

<img src="/images/hoge.png" />
<div style="background-image:url(/images/hoge.png)" /></div>

scssだと

#hoge.css.scss
.hoge {
  background-image: url(/images/hoge.png);
}

参考

Ruby on Rails - Railsで扱う画像はassetsとpublic、どちらに置くといいのでしょうか?(16345)|teratail

Railsアプリをデプロイするとbackground: url(bg-hero-001.jpg)が表示されなくなる話 - Qiita

かんたん自作シェアボタン | Ruby で動的にツイート内容を変える

f:id:serendipity4u:20170417172047p:plain

Ruby で日本語を含む文字列を扱うために、URLモジュールを入れる

ブラウザは、URLにエスケープされた文字列が含まれていた場合それを展開して表示するが、通信するときはエンコードしている。そのため、a href =“"に入れる日本語はエンコードしておく。

続きを読む

delayed_job

githubの重点箇所を英訳。

delayed_job 3.0.0は Rails 3.0以上しかサポートしていないのに注意。

delayed_job を Active Recordで使いたいなら, delayed_job_active_record をGemfileに加える.

gem 'delayed_job_active_record'

Active Record バックエンドはjobsテーブルを必要とする. 以下のコマンドで作成する。

bundle exec rails generate delayed_job:active_record
bundle exec rake db:migrate

.delay.method(params)をどんなプロジェクトでも呼べば、それはバッググランドプロセスとなる。

# delayed_jobなし
@user.activate!(@device)

# delayed_jobあり
@user.delay.activate!(@device)

どんなときでもバックグラウンドでプロセスを走らせたいなら、handle_asynchronously メソッドでデフォルト設定にできる。

class Device
  def deliver
    # long running method
  end
  handle_asynchronously :deliver
end

device = Device.new
device.deliver

パラメータ

handle_asynchronously とdelay は以下のパラメータを利用する:

  • :priority (number): 小さいナンバーほど優先される; デフォルトは0 (例: handle_asynchronously :send_mailer, :priority => 20)

  • :run_at (Time): 指定した秒後に実行する (実行を未来にできる、例:handle_asynchronously :call_a_class_method, :run_at => Proc.new { 2.hours.from_now })

私の環境

すごいシンプルなログインなしセッションでのユーザー情報保存機能【rails】自分用メモ

シチュエーション

  • クイズアプリで、わざわざログインさせたくないけど10分間くらいユーザー情報を保存して(次の問いに繋げるといったことをして)いたい。

  • ECサイトのカートに入れる機能

  • ECサイトのお気に入り機能

  • 投票機能で、同一セッションで多重投票されたくない時

実装レベル

重要度の低い情報を扱うとする(個人情報を含まない)

require "cgi"
require "cgi/session"

cgi = CGI.new
session = CGI::Session.new(cgi,{"new_session"=>false})

コマンドラインでオフラインモードで利用するには

hoge.rb(main):001:0> require 'cgi'
=> []
hoge.rb(main):002:0> cgi = CGI.new
(offline mode: enter name=value pairs on standard input)

# 入力を求められる

hoge=123&fuga=456

#さらにCtrl-D を押すことで続きのプログラムが実行される。


#<CGI:0x007fef6b652ce8
 @accept_charset="UTF-8",
 @accept_charset_error_block=nil,
 @cookies={},
 @max_multipart_length=134217728,
 @multipart=false,
 @options={:accept_charset=>"UTF-8", :max_multipart_length=>134217728},
 @output_cookies=nil,
 @output_hidden=nil,
 @params={"hoge"=>["123"], "fuga"=>["456"]}>

# デフォルトでは、一時ファイル領域のテキストファイルで記録される


session = CGI::Session.new(cgi)

=> #<CGI::Session:0x007fef6ce1a4c0
 @dbman=#<CGI::Session::FileStore:0x007fef6ce1a290 @hash={}, @path="/var/folders/g2/4h5d5fms0qg_3chcnw9s3tm00000gq/T/cgi_sid_3b0a42ee87db63bd">,
 @dbprot=[#<CGI::Session::FileStore:0x007fef6ce1a290 @hash={}, @path="/var/folders/g2/4h5d5fms0qg_3chcnw9s3tm00000gq/T/cgi_sid_3b0a42ee87db63bd">],
 @new_session=true,
 @session_id="bea46c8b0b7e8d8cf29f7647251b5f1f">


session.session_id
=> "bea46c8b0b7e8d8cf29f7647251b5f1f"

cookies

Cookieとは、Webサイトの提供者が、Webブラウザを通じて訪問者のコンピュータに一時的にデータを書き込んで保存させる仕組み。

CookieにはWebサイト(Webサーバ)側が指定したデータを保存しておくことができ、利用者の識別や属性に関する情報や、最後にサイトを訪れた日時などを記録しておくことが多い。ネットサービスなどのサイトで利用者のIDなどが保存されると、次にアクセスしたときに自動的に利用者の識別が行われ、前回の続きのようにサービスを受けることができる。 -IT用語辞典

cookies[:hoge] = { :value => "value", :expires => "30.days.from_now", :path => "/store", :domain => "www.example.com", :secure => true }
  • value ブラウザに保存する文字列。文字列のみが格納できる。
  • expiers クッキーの有効期限。設定をしないと、クッキーをメモリ上で管理しブラウザセッション*1が終了すると削除される。
  • path ブラウザがクッキーを送信するパスを設定する
  • domain ブラウザがクッキーを送信するドメインを設定する
  • secure httpsの時だけクッキーを送信するように設定する (追記)

  • httponly trueにすると、IEFireFoxではJavaScriptcookieを取得できなくなる

session

session[:hoge] = "hoge"
# sessionが多重ハッシュになっている場合、
session[:hoge][:fuga]
# => NoMethodError: undefined method `[]' for nil:NilClass

# `try`はこんな感じで書ける
session[:hoge].try(:[], :fuga)

# 削除する
session.delete(:hoge)

session と cookie を整理

ステートレスなHTTPというシステムの下で、URLでuser id を持たせるのは危険である。なぜなら、URLはクライアント側で改変可能であるから。

そこで、サーバーからクライアント側にsession IDだけ渡して、クライアント側に保持してもらう仕組み(クライアント側に保持してもらう仕組みがcookie)が生まれた。クライアント側がサーバー側にsession IDを渡すと、サーバー側は保持していたユーザー情報をsession IDと照らし合わせて提供する。

参考

yachibit.hateblo.jp

blog.willnet.in

d.hatena.ne.jp

Deviseで管理者が承認したらユーザーのアカウントをアクティベートする実装【Rails】

f:id:serendipity4u:20170417172047p:plain

userカラムにapprovedを足す

class AddApprovedToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :approved, :boolean, :default => false, :null => false
    add_index  :users, :approved
  end

  def self.down
    remove_index  :users, :approved
    remove_column :users, :approved
  end
end

続きを読む

私のSanDisk16GBが唐突に壊れて思い出の写真が消えたがMini_Tool_Recoveryのおかげで復旧できた話

f:id:serendipity4u:20170417174925p:plain

意気揚々と、SDカードをmacに入れた私。

だがしかし、「なんか読みとれないよ」的な文章が現れる。 macの「写真」アプリも起動しない。

一眼レフにセットした状態ではプレビューできるのにな〜と思い、 もう一度カメラに入れて見ようとしたところ…

「画像がありません」

の文字。

絶望。

しかし、あるはずだ!解決策!

続きを読む

女子大学生が法人設立(LLC)をしてきたので感想・苦労したことをまとめます

f:id:serendipity4u:20170120201624j:plain

フリーランスとして請けていたweb制作ですが、だんだん規模が大きくなってきたのと、個人で開発しているウェブサービスもより本格的に運用していきたいとの思いから、法人化を行うことにしました。

続きを読む

letsencryptの更新が上手く行かなかった時のメモ。自動化までの道のり【rails, nginx,AWS,capistrano環境】

既存のletsencryptディレクトリを消しやり直す

letsencryptの更新が上手く行かなかった時のメモ。

新しく認証し直す。 まず /etc/letsencrypt/ ディレクトリを削除。

sudo git clone https://github.com/letsencrypt/letsencrypt /usr/local/letsencrypt

sudo /usr/local/letsencrypt/letsencrypt-auto --help --debug

sudo service nginx stop 

nginxを止めないと"Could not bind TCP port 443 because it is already in use by another process on this system (such as a web server). Please stop the program in question and then try again.“というメッセージが出る。

sudo /usr/local/letsencrypt/letsencrypt-auto certonly --standalone -d example.com -d www.example.com --agree-tos

で、

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/example.com/fullchain.pem. Your cert will
   expire on 2017-05-08. To obtain a new or tweaked version of this
   certificate in the future, simply run letsencrypt-auto again. To
   non-interactively renew *all* of your certificates, run
   "letsencrypt-auto renew"

上手くいった。

自動化する

sudo vi /etc/cron.daily/letsencrypt
sudo chmod +x /etc/cron.daily/letsencrypt

/etc/cron.daily/letsencrypt の内容。

#!/bin/sh

LE_HOME=/usr/local/letsencrypt
WEBROOT=/var/www/rails/example/current/public #capistranoを使っているのでcurrentディレクトリを挟む
DOMAIN=example.com
SUBDOMAIN=www.example.com
LOG=/var/log/letsencrypt/renew.log

$LE_HOME/letsencrypt-auto certonly --debug --non-interactive --keep-until-expiring --webroot -w $WEBROOT -d $DOMAIN -d $SUBDOMAIN >> $LOG 2>&1
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
    echo Automated renewal failed:
    exit 1
fi
service nginx reload

nginx設定ファイルで正しく証明書の場所が示されていることを確認。

/etc/nginx/conf.d/example.conf

server {
        listen 443 ssl;
        server_name xx.xx.xx.xx; #アプリのElastic IPに変更してください
        ssl_certificate      /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/example.com/privkey.pem;
        ssl_session_cache builtin:1000 shared:SSL:10m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_session_timeout 1d;
        root /var/www/rails/example/current/public; #自分のアプリケーション
...

参考

qiita.com