motolog

Everything I love in my life.

Railsでサブドメイン間でログインを保持するためのセッション設定

Railsでサブドメインにアクセスすると、デフォルトの設定では、ログイン(セッション)が保持されません。

解決策は以下の通りです。
ググると間違った(or 古い)情報も散乱していたので、惑わされないようにそちらも一応のせておきます。

session_store.rbの設定

session_store.rb で以下のように設定します。




# session_store.rb
Appname::Application.config.session_store :cookie_store, key: '_user_session', domain: 'example.com'

少し解説すると、

domain: 'example.com'

のように、domainを指定するのがポイント。
production, staging, development で以下のように分岐してあげればいいかなと。

if Rails.env.production?
Appname::Application.config.session_store :cookie_store, key: '_user_session', domain: 'example.com'
elsif Rails.env.staging?
Appname::Application.config.session_store :cookie_store, key: '_user_session', domain: 'staging-example.com'
else
Appname::Application.config.session_store :cookie_store, key: '_user_session', domain: 'lvh.me'
end

ちなみに、stagingで用いている 'lvh.me' は、127.0.0.0 に割り当てらているドメインです。localhost だとサブドメインのチェックはできないので、lvh.me はすごく便利。

domainオプションを追記する際には、keyも変えるべし

ここはハマったところなのですが、domain: 'example.com' を追加すると、セッションの情報も変わります。しかし、keyの名前は同じ。

すると、既にログインしていたユーザーがログアウトできなくなるなどの不都合が生じてしまう可能性があります。(実装にも依ると思いますが)

なので、domainオプションを追加するタイミングで、session_key の名前も変更しておいたほうが無難だなーと思います。

関連記事



Railsでカスタムサブドメイン-本サイトとサブドメイン間のURL設定

Railsでサブドメインを実装した際に、本サイトとサブドメインサイト間の受け渡しで少しハマりました。

サブドメインサイトにいる状態で、root_path や root_url でリンク先を指定しても、本サイトではなく、そのサブドメインのトップに飛ばされます。

この記事では、url_for メソッドを利用して、サブドメインサイトと本サイト間を行き来する方法をご紹介します。



url_forでメソッド作成

subdomain_url_helper を以下のように作成

module SubdomainUrlHelper
  def with_subdomain(subdomain)
    subdomain ||= 'www'
    subdomain += "."
    [subdomain, request.domain].join
  end

  # - remove subdomain and go to the original site
  #    url_for(:subdomain => false)
  # - go to the specific subdomain site
  #    url_for(:requested_subdomain => 'subdomain')
  def url_for(options = nil)
    if options.kind_of?(Hash) && options.has_key?(:subdomain)
      options[:host] = with_subdomain(options.delete(:subdomain))
    elsif options.kind_of?(Hash) && options.has_key?(:requested_subdomain)
      options[:host] = with_subdomain(options.delete(:requested_subdomain))
      options[:controller] = 'users'
      options[:action] = 'show'
    end
    super
  end
end

メインは、2つめのurl_forメソッド。
with_subdomainメソッドは、引数の subdomain と request.domain を合わせるためのもの。なお、subdomain が存在しなければ、www が代わりに入る。
そのため、

url_for(:subdomain => false)

とすると、

# if request.domain is "example.com"
www.example.com

のようになる。

url_for については、渡された hashのkey で条件分岐してあるのは、見ての通りです。

コードがなかなか汚いのでだれかリファクタお願いします。

関連記事



Railsでカスタムサブドメイン機能を実装する時のルーティング設定

運営中のサービス「BoxToYou(https://www.box2you.com)」でカスタムサブドメイン機能をリリースしました。

いくつかハマったところがあったので、数回に分けてメモしていきまする。

今回は、routingの設定とリンクの設定方法について。



サブドメインの条件定義

まず、subdomain.rb でサブドメインの条件をかきかき。

#subdomain.rb 

class Subdomain
  def self.matches?(request)
    request.subdomain.present? && request.subdomain != "www"
  end
end

RailsのRequestオブジェクトにsubdomainメソッドが定義されているので、

request.subdomain

という風にリクエストのサブドメイン部分「http://◯◯◯.example.com」の◯◯◯を簡単に呼び出せます。ただし、「www」もサブドメインとして処理されますので、カスタムサブドメインの設定としては不適切として、

request.subdomain != "www"

という風にmatchの条件から排除しています。

ルーティングの設定

次に、routesファイルの設定。

#routes.rb

# routes setting of subdomain
# should be placed above the "root :to" path
require "#{Rails.root}/app/helpers/subdomain"
  constraints(Subdomain) do
    match "/" => 'users#show'
    match "/*a" => 'application#render_error404'
  end

# other routes settings
root :to => "users#index" #

上記のsubdomain.rbを読み込み、
constraints で条件がマッチした際には、別のactionに飛ばすようにしています。

match "/" => 'users#show'
match "/*a" => 'application#render_error404'

については、アプリの仕様で、root_path の後ろには何も来ない場合の処理です。404 error のページへ飛ぶようにcontroller側で設定しています。

コントローラー側の制御

ちなみに、subdomain の controller 側の処理はこんな感じです。

# users_controller.rb

# if no users exist who has the requested.subdomain, 
# redirect the request to the top page.
unless User.where(:subdomain => request.subdomain).present?
  return redirect_to url_for(:subdomain => false)
 end

# specific a user from request.subdomain
@user = User.where(:subdomain => request.subdomain).first

リクエストされたサブドメインを持ったユーザーが存在しなかった場合は、トップページにリダイレクトしています。
url_for(:subdomain => false)に関しては、subdomain_url_helper.rb というヘルパーを作って設定しています。(詳細はこちら

そして、次に、subdomain は各ユーザーがユニークで持てるようにしているので、

@user = User.where(:subdomain => request.subdomain).first

で特定のユーザーを特定して、インスタンス変数に入れています。

こんな感じで、だいたいの設定はできるかと。


関連記事

参考記事



Rails - mockを使って1つの属性のみにvalidationを走らせてみる

ユニークなユーザー名やサブドメインを付与するときに、ajaxで入力されたその場で有効な値かどうかを判断してあげれば、ユーザビリティは向上するだろう。ツイッターなど大規模なウェブサービスでは実装されていることが多いかと思う。

しかし、.valid? メソッドを使ってしまうと、そのオブジェクト全体にvalidationがかかってしまい不都合が生じてしまうので、今回は、1つの属性に対してvalidationをする方法について扱う。

具体的には、以下のように、検証を行いたい1つの属性を持った新しいオブジェクト(ここでは、mock)を作り、mock.valid? でエラーを発生させ、そのエラーに調べたい属性のkeyがあるかどうかを見る。

def valid_attribute?(attr, value, present_username)
  mock = User.new(attr => value)
  unless mock.valid?
    if mock.errors.has_key?(attr) && value != present_username
      return false
    else
      return true
    end
  end
  true
end

attr が調べたい属性の名前、value がその属性に与えられた値。
さらに、ここでは、ユーザーネームの検証を行うと仮定して、present_username という引数を与えてみた。

ポイントは、

mock = User.new(attr => value)

で、ユーザーが入力した値を用いて、新しいユーザー mock を擬似的につくり、

unless mock.valid?

で、エラーを発生させ(全ての属性の検証にクリアすれば true が返る)、

if mock.errors.has_key?(attr) && value != present_username

で、エラーに attr という key があり、かつ、入力された値 value が現在のユーザーネーム present_username と異なる、という条件で value が不正な条件を作り出している点。

個人的に、mock をつくるのと、errors.has_key? を使うあたり、勉強になった。

色んな方法があるのだなーと。

参考にしたのは、stackoverflow の以下の記事。
Is there a way to validate a specific attribute on an ActiveRecord without instantiating an object first?

また、ActiveRecord を使用している場合は、以下の記事で触れられている方法が使えそう。
Rails - 状況によってsave時に実行するバリデーションを切り替える


view側の実装は別記事にする予定。

Rails で jquery.cookie.js を使って閲覧履歴を表示してみた

jquery.cookie.js と Rails の cookies を使うと、簡単に履歴表示機能を実装することができた。
item オブジェクトが id 属性と name 属性を持っている場合の実装例を書いてみた。

jquery.cookie.jsでクッキーを保存

$(function(){
  var item = "<%= @item.id %>";
  var cookie_name = 'recently_viewed_items';
  var viewed_items = [];
  var delete_item = false;
  $.cookie.defaults.path = "/";

  // 既にクッキーが存在している場合は、ストリングを配列にする
  if($.cookie(cookie_name)){
    viewed_items = $.cookie(cookie_name).split(",");
  }

  // 重複していなければ、itemを配列に追加
  if($.inArray(item, viewed_items)<0){
    viewed_items.push(item);
  }

  // 5個以上ならば1つ削除
  if (viewed_items.length >= 5){
    viewed_items.shift();
  }

  // 配列をクッキ―に保存
  $.cookie(cookie_name, viewed_items);

});


Railsのcookiesを利用してview側で表示を制御

<h2>
  最近チェックしたアイテム
</h2>
<% items = [] %>
<% items = cookies[:recently_viewed_items].split(",") unless cookies[:recently_viewed_items].nil? %>
<% items.each do |id| %>
  <% item = Item.find(id) %>
    <%= item.name %>
<% end %>


jquery 側の実装で item が持つ id を1つの string としてクッキーに保存し、そのクッキーの情報を Rails の view側で1つ1つの id に分解しそれぞれの item の name を表示してみた。

jqueryとRailsのクッキーを使えばこんな簡単にできてしまうという。すごいすごい。



参考にさせてもらった記事

Rails cookies の細かな設定などは上記の記事を確認してみてください。
まだまだ修行中の身なので、おかしな点やもっと効率良くできる点などありましたら、教えていただけると幸いです。

@motokiyoshida

Facebookログインボタンを外したらコンバージョン率が3%アップしたという話

Eコマースを運営する上で、コンバージョン率はKPIの1つとなることが多い。それだけ、色々とA/Bテストが繰り返されている。

ノルウェーの化粧品メーカーのA/Bテストの結果が顕著で面白かったので少し触れることにする。
原文:Facebook Login Reduces Ecommerce Sales (Case Study)



実験はシンプルで、8000人のユーザーに対して、半数には①「Emailフォーム+Facebookログインボタン」を表示し、もう半数には②「Emailフォーム」のみを表示するというもの。以下が実際の表示。

①「Emailフォーム+Facebookログインボタン」
f:id:show_motto:20130823210711p:plain

②「Emailフォームのみ」
f:id:show_motto:20130823210719p:plain


Facebookログインボタンなしでコンバージョン率3%増加

結果はかなり顕著。FacebookログインボタンなしでEmailフォームのみの②の場合の方が、コンバージョン率が3%も高かったそうだ。これは、当サイトの1週間あたりの売上に換算すると、100万円にも及ぶという。もちろん、規模が大きくなるにつれて、その額も大きくなる。


ブランディングやセキュリティリスクという視点

で、この記事を読んで思い出したのが、メルマガの管理が簡単にできるサービス「MailChimp」ブログの以下の記事。
Social Login Buttons Aren’t Worth It

f:id:show_motto:20130823213558p:plain

この記事は、タイトルにある通り、ソーシャルログインボタン(ここでは、Facebookログインボタンとツイッターログインボタン)の有用性について問題提起している。

内容を簡単にまとめると、

  • ソーシャルログインボタンがあるとログイン失敗率は少しだけ減少する
  • でも、ブランディングやセキュリティリスクを考えると、その少しの改善は見合うのか?
  • 正解はないけれど、この記事が議論の発端になればいいな

みたいなことを言っている。つまり、MailChimp のデータでは、ソーシャルログインボタンによってコンバージョン率はやや上昇したが、ブランディングやセキュリティという別の点におけるパフォーマンスに疑問を呈しているわけだ。


自サイトでテストしてみることがスタート

1つ目のノルウェーの化粧品メーカーの例を取り上げている記事の最後でも言われているが、このデータはあくまでも1つの例に過ぎないとのこと。他社のデータに踊らされるなと筆者は締めくくっている。

というわけで、ソーシャルログインボタンの効用については、数字で定量的に測りつつ、ブランドなど定性的な面も見ていくといいのかなと思いました。

シリコンバレーで流行りの Growth Hacking 的な記事でした。


綺麗な英語に触れたかったら「The NEW YORKER」を読むといいかも

アメリカ在住も長い尊敬する友人2人が口を揃えておすすめするメディアがある。それが「THE NEW YORKER」。ウィットの利いた「うまい」文章が他と比べ圧倒的に多いとのこと。

f:id:show_motto:20130823193525p:plain

僕もたまに読んでいるけど、「ああうまいなあ」と楽しみながら読めたりする。たまに、何が言いたいんだろうと悩んでしまう時もあるけれど、だんだんと分かるようになれたらなと思って続けている。

良質な英文に触れたいという人は読んでみるといいかも
http://www.newyorker.com/



渋谷・神泉の担々麺が最高にうまいラーメン屋「うさぎ」は1度は食すべき!

一度食べたらクセになる味。うさぎの担々麺はまさにその言葉にぴったりである。
渋谷道玄坂を登り切って少し進み路地を曲がると、ラーメン屋というよりもバーのような風貌の小さなお店が見えてくる。



「知っていなければ入らないだろうな」という印象だが、それでもお昼時はいつも人で賑わっている。きっとうさぎの虜となったリピーター達が多いのだろう。

前置きはこれくらいにして、下の担々麺は僕の1番のお気に入り。
f:id:show_motto:20180224234352p:plain



そして、汁なし担々麺もまたクセになる味。
f:id:show_motto:20180224234411p:plain



さらに、通常のラーメンも中華そばを極めて上品な味付けでうまい。

f:id:show_motto:20180224234440p:plain

食べログでも3.56の評価はさすがだなと。
ぜひ足を運んでみてください!

食べログはこちら



PDFコピペの文字化けで困った時はGoogle ドキュメントを使うと解決するかも

PDFでもらったデータをコピペしようとしたら、完全に文字化けしてしまった使いものにならない。ググってみると同じような問題に直面したことのある人は多くいるようで、様々な対処法が見つかった。テキストとして保存してみたり、PDFからWordに変換してくれるウェブサービスを使ってみたり。

しかし、どれもうまくいかず、うーんと悩んでいると、先輩社員さんがふとあるアイデアを思いついた。

Google Docs で PDF を開いたらもしかしたらいけるんじゃね?

おお。Google さんなら何かうまいこといくんじゃないか。私も直感的にそんな予感がした。結果的にうまくいったので手順を紹介しよう。といってもすごく簡単。

 

1.グーグルドライブのダッシュボードから、PDFをアップロードする

f:id:show_motto:20130822232222j:plain

上記の写真のボタンを押して、「ファイル」を選択。文字情報を抜き出したいPDFファイルをアップロードする。

2.アップロードされたPDFファイルをGoogle ドキュメントとして開く

f:id:show_motto:20130822232848j:plain

アップロードされたデータを右クリックして、「開く」→「Google ドキュメント」を選択。

するとすると、画像データの下に文字が出てきたー!!

となるはずです。

文字化けはそれぞれで問題が異なるため、この方法が全ての場面で役立つかどうかは分かりませんが、試してみる価値はあるかと思います。少なくとも私の場合はできたので。

参考までに、メモでした。

 

Pinterest風レイアウトがPinterest以外のサイトにおいて役立たずな理由

なるほどと思った記事があったのでシェアすることにした。

Why Pinterest-style infinite-scroll layouts are worthless for everyone except Pinterest | Stephen Corwin's Blog

f:id:show_motto:20130224214108p:plain

Pinterest風レイアウトとは、本家Pinterestに見られるように

  1. グリッド型の画像で
  2. 半永久的にスクロールできる

 

レイアウトといえるだろう。このようなレイアウトは、Pinterestのリリース以来、多くの人を魅了し、現在では、Etsy や Ebay といった米国における大規模ECサイトでも採用されるほか、日本のスタートアップ界でも、Whitelist や WishScope などで採用されている。

 

本記事で指摘されている Pinterest風デザインの問題点をまとめると、以下のようになる。

ここ数年のウェブサイトを見てきて、"Less is More"ということは分かっている。Pinterest風のサイトを見ても、すべてを見渡すことはできるが何も記憶には残らない。コンテンツがありすぎるのだ。

 

では、なぜこのようなレイアウトがPinterestでは機能するのか。それは、サイトの目的の違いにある。

Pinterestが機能するのは、ユーザーが何か特定のものを探して訪問するサイトではないし、また、ユーザーがある1枚の写真に着目することは彼らの成功にとって重要ではないからだ。Pinterestはアートサイトなのだ。...
しかし、もしこのレイアウトを製品を打ったり何かをプロモーションするために使うのであれば、そして、それが会社にとってコアなビジネスモデルなのであれば、それは瞬く間にマズい選択となるだろう。

 

つまり、Pinterest風レイアウトは、アートサイトとしては快適により多くのものを見せるという意味で優秀だが、ECなどの見せるだけで終わってはいけないようなサイトにおいてはよろしくないというわけだ。 

 

これにはいろいろと考えさせられた。筆者の言いたいことはよく分かる。ECサイトにおいては、「見せるの先の購買というアクション」に持っていかなくては意味が無い。

しかし、Pinterest風レイアウトが一概に悪いとは思わない。冒頭で挙げたように、米国の有名企業も採用しているこのレイアウトには、それだけの魅力がある。

スクロールしている楽しさは、ショッピングモールでぶらぶらとお店を徘徊する楽しさを想起させる。これは、今までのECにはなかった新たな価値提供の仕方である。

 

そこで、ECサイトに携わる者として私が思うのは、Pinterest風レイアウトの弱点を補ってあげる形を考えるべきということだ。それには、様々な方法があるだろう。強弱をつけてあげるのもその内の一つだ。

工夫の仕様はデザイナーさんの腕の見せどころだろう。ここは、UIというより、UXに近いところかもしれない。

私も、ECサイトに関わるデザイナーの端くれとして、色々とアイデアを巡らせようと思う。

by @show_motto

 

© 2018 Motoki Yoshida