アーカイブ

‘Rails’ カテゴリーのアーカイブ

callbackで止める。filterで止める。 このエントリーを含むはてなブックマーク はてなブックマーク - callbackで止める。filterで止める。

2017 年 10 月 18 日 コメントはありません

ActiveRecord::Callbacks

If the before_validation callback throws :abort, the process will be aborted and ActiveRecord::Base#save will return false.

ActiveRecordのコールバックでは:abortを投げるように変わっているけれど、ActionControllerのフィルターがどうなっていたのか分からなかったので調べたメモ。

Action Controller Overview — Ruby on Rails Guides

If a “before” filter renders or redirects, the action will not run.

APIリファレンスで見つからなかったので、ガイドの方から見つけてきた。

カテゴリー: Rails タグ:

integerのidをbigintにするやつ。 このエントリーを含むはてなブックマーク はてなブックマーク - integerのidをbigintにするやつ。

2017 年 10 月 17 日 コメントはありません


使わなかったのですが、せっかく書いたので貼っておきますね。

カテゴリー: Rails タグ:

nginxとRackとX-Accel-Mappingと。その2 このエントリーを含むはてなブックマーク はてなブックマーク - nginxとRackとX-Accel-Mappingと。その2

2017 年 8 月 15 日 Comments off

GitHubからの通知メールがいくつかまとめて来てたため見逃してしまっていたのですが、この前Rackに投げたPRがマージされていました。

手元でパッチを当てていたので、嬉しい。詳細については↓に書いています。

nginxとRackとX-Accel-Mappingと。 » サイキョウライン
まず、課題となっている場所はここで、いまの実装はこんな感じ。

カテゴリー: Rails, Ruby タグ:

nginxとRackとX-Accel-Mappingと。 このエントリーを含むはてなブックマーク はてなブックマーク - nginxとRackとX-Accel-Mappingと。

2017 年 7 月 3 日 Comments off

Rack

https://github.com/rack/rack/blob/master/lib/rack/sendfile.rb#L149
まず、課題となっている場所はここで、いまの実装はこんな感じ。

def map_accel_path(env, path)
  if mapping = @mappings.find { |internal,_| internal =~ path }
    path.sub(*mapping)
  elsif mapping = env['HTTP_X_ACCEL_MAPPING']
    internal, external = mapping.split('=', 2).map(&:strip)
    path.sub(/^#{internal}/i, external)
  end
end

nginx

設定はこう。「X-Accel-Mapping」を複数定義している。

server {
  location ~ /foo/(.+) {
    internal;
    alias /path/to/real/foo/$1;
  }

  location ~ /bar/(.+) {
    internal;
    alias /path/to/real/bar/$1;
  }

  location / {
    proxy_set_header X-Sendfile-Type X-Accel-Redirect;
    proxy_set_header X-Accel-Mapping '/path/to/releases/\d{14}/abc/=/foo/';
    proxy_set_header X-Accel-Mapping '/path/to/releases/\d{14}/def/=/bar/';
  }
}

説明

複数の「X-Accel-Mapping」が定義されていると、Rackの「env[‘HTTP_X_ACCEL_MAPPING’]」に渡ってくる時は「, 」(カンマとスペース)で区切られた状態になる。

/path/to/releases/\d{14}/abc/=/foo/, /path/to/releases/\d{14}/def/=/bar/

こうなると、いまのRackの実装ではいきなり「=」でsplitしている(ひとつしか定義されていない前提)ため、結果として「internal」と「external 」はこんな感じになって、正しいパスを返せなくなる。

internal #=> '/path/to/releases/\d{14}/abc/'
external #=> '/foo/, /path/to/releases/\d{14}/def/=/bar/'

対策

そこで、複数の「X-Accel-Mapping」が定義されることも考慮して、この様にしてみた。

def map_accel_path(env, path)
  if mapping = @mappings.find { |internal,_| internal =~ path }
    path.sub(*mapping)
  elsif mapping = env['HTTP_X_ACCEL_MAPPING']
    mapping.split(',').map(&:strip).each do |m|
      internal, external = m.split('=', 2).map(&:strip)
      new_path = path.sub(/^#{internal}/i, external)
      return new_path unless path == new_path
    end
    path
  end
end

これで「X-Accel-Mapping」の数に関わらず、定義したパスを返せるようになった。

ので、PR出してみた。どうかな。
https://github.com/rack/rack/pull/1187

カテゴリー: Rails タグ:

libの場所について。 このエントリーを含むはてなブックマーク はてなブックマーク - libの場所について。

2017 年 3 月 6 日 Comments off

autoload – Rails 5: Load lib files in production – Stack Overflow

Putting lib in app/lib is recommended by rails members

こんなやりとりを見て「おっ、そうなの?」ってことでまるっと移動してみたら、自分で作ったrakeタスクが見えなくなってしまった。
えー、ってことでソースを読んだからこんな感じになってた。

rails/engine.rb at v5.0.2 · rails/rails

paths[“lib/tasks”].existent.sort.each { |ext| load(ext) }

Rails.root直下に置くならconfig.eager_load_pathsに追加しなきゃならないし、app下に置くならrakeタスクは元の場所に残さなければならないから「lib」というディレクトリが2つ存在することになるしで、どっちがいいかちょっと悩ましい。のでした。後者かなぁ。

カテゴリー: Rails タグ:

JSONリクエストの時のCSRF対策について。 このエントリーを含むはてなブックマーク はてなブックマーク - JSONリクエストの時のCSRF対策について。

2017 年 1 月 14 日 Comments off

ちゃんとドキュメントがあったのでメモ。

ActionController::RequestForgeryProtection

It’s important to remember that XML or JSON requests are also affected and if you’re building an API you should change forgery protection method in ApplicationController (by default: :exception):

カテゴリー: Rails タグ:

RailsでPGroongaを使う。その3 このエントリーを含むはてなブックマーク はてなブックマーク - RailsでPGroongaを使う。その3

2016 年 11 月 3 日 Comments off

ここ数日追っていたこの件ですが、groonga/jaのチャットルームで教えていただきました。

コミュニティー | PGroonga

PGroonga用のチャットルームは次の通りです。

groonga/ja – Gitter

実際に追加したコードはこんな感じです。

Extend relation to switch “enable_seqscan” around query execution · yoshuki/article_search_with_pgroonga@049c8d4

Extend relation to switch “enable_seqscan” around query execution

extendingを知らなかったのですが、ここの前後に割り込ませているのですね。スッキリ追加できていてすばらしい。

rails/relation.rb at v5.0.0.1 · rails/rails

def exec_queries

ただここすでにmasterでは変わっているので、注意が必要ですね。(ちゃんとテスト書こう。)

rails/relation.rb at master · rails/rails

def exec_queries(&block)

@kouさんありがとうございました!

カテゴリー: PostgreSQL, Rails タグ:

RailsでPGroongaを使う。その2 このエントリーを含むはてなブックマーク はてなブックマーク - RailsでPGroongaを使う。その2

2016 年 11 月 1 日 Comments off

RailsでPGroongaを使う。 » サイキョウライン

全文検索そのものと、キーワードのハイライトについてはよかったのですが、pgroonga.score関数でとれるスコアを使ったソートで問題が・・・(つづく)

前回の続き、この問題について。端的に言うと、せっかく作ったインデックスを使ってくれずスコアが取れないため、結果として正しくソートが出来ていません。

pgroonga.score関数の仕様はこの様になっています。

pgroonga.score関数 | PGroonga

pgroonga.score関数はインデックスを使わずに全文検索した場合は常に0.0を返します。言い換えると、pgroonga.score関数はシーケンシャルスキャンで全文検索を実行した場合は常に0.0を返します。

そして、チュートリアルでは作ったインデックスを使わせる対策として、enable_seqscanをoffにしていました。

チュートリアル | PGroonga

確実にpgroongaインデックスを使うためにシーケンシャルスキャンを無効にします。

直にクエリを発行するなら、SELECTの前後でenable_seqscanをoff→DEFAULTと一時的に切り替えてあげればよいと思うのですが、ActiveRecordを使っているとクエリが発行されるのは実際に結果が必要になったタイミングなので、この様に書いても意味がありません。

> Article.connection.execute 'SET enable_seqscan = off'
> articles = Article.full_text_search('桃太郎')
> Article.connection.execute 'SET enable_seqscan = DEFAULT'

SELECTの前後にコールバックがあればいいのですが、探してみても用意されてはいないみたい。
after_initialize and after_findは期待している動きとはちょっと違う。)

かと言って、postgresql.confに書いてしまうのはやり過ぎ、というか全体でシーケンシャルスキャンをoffにしてしまうのは問題だと思います。
このような場合、どう実装するのがいいのでしょうか。

[15:30 追記]
ActiveRecordが生成するSQLをpsqlで流すとインデックスが使われることは確認できているので、enable_seqscanをどこで切り替えるか、に悩んでいます。

> puts Article.full_text_search('桃太郎').to_sql
SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles"  WHERE (title %% '桃太郎' OR body %% '桃太郎')  ORDER BY pgroonga_score DESC
=> EXPLAIN SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles"  WHERE (title %% '桃太郎' OR body %% '桃 太郎')  ORDER BY pgroonga_score DESC;
                               QUERY PLAN
-------------------------------------------------------------------------
 Sort  (cost=1.14..1.15 rows=4 width=242)
   Sort Key: (score(articles.*)) DESC
   ->  Seq Scan on articles  (cost=0.00..1.10 rows=4 width=242)
         Filter: ((title %% '桃太郎'::text) OR (body %% '桃太郎'::text))
(4 rows)

=> SET enable_seqscan = off;
SET
=> EXPLAIN SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles"  WHERE (title %% '桃太郎' OR body %% '桃太郎')  ORDER BY pgroonga_score DESC;
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Sort  (cost=4.08..4.09 rows=4 width=242)
   Sort Key: (score(articles.*)) DESC
   ->  Bitmap Heap Scan on articles  (cost=0.00..4.04 rows=4 width=242)
         Recheck Cond: ((title %% '桃太郎'::text) OR (body %% '桃太郎'::text))
         ->  BitmapOr  (cost=0.00..0.00 rows=2 width=0)
               ->  Bitmap Index Scan on index_articles_on_id_and_title_and_body  (cost=0.00..0.00 rows=2 width=0)
                     Index Cond: (title %% '桃太郎'::text)
               ->  Bitmap Index Scan on index_articles_on_id_and_title_and_body  (cost=0.00..0.00 rows=1 width=0)
                     Index Cond: (body %% '桃太郎'::text)
(9 rows)

=> SET enable_seqscan = DEFAULT;
SET
=> EXPLAIN SELECT "articles".*, pgroonga.score("articles") AS pgroonga_score FROM "articles"  WHERE (title %% '桃太郎' OR body %% '桃太郎')  ORDER BY pgroonga_score DESC;
                               QUERY PLAN
-------------------------------------------------------------------------
 Sort  (cost=1.14..1.15 rows=4 width=242)
   Sort Key: (score(articles.*)) DESC
   ->  Seq Scan on articles  (cost=0.00..1.10 rows=4 width=242)
         Filter: ((title %% '桃太郎'::text) OR (body %% '桃太郎'::text))
(4 rows)
カテゴリー: PostgreSQL, Rails タグ:

RailsでPGroongaを使う。 このエントリーを含むはてなブックマーク はてなブックマーク - RailsでPGroongaを使う。

2016 年 10 月 27 日 Comments off

とあるRailsアプリで全文検索が必要になったため、PGroongaを試してみました。

環境はそれぞれこんな感じ。(表示は一部省略)

% cat /etc/lsb-release
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"

% sudo aptitude show postgresql
パッケージ: postgresql
バージョン: 9.5+173

% sudo aptitude show postgresql-9.5-pgroonga
パッケージ: postgresql-9.5-pgroonga
バージョン: 1.1.5-2~xenial1

まず、参考にしたのはこのページです。

Ruby on RailsでPostgreSQLとPGroongaを使って日本語全文検索を実現する方法 – ククログ(2015-11-09)

この記事ではRuby on Railsで作ったアプリケーションからPGroongaを使って日本語全文検索
機能を実現する方法を説明します。

そして、使ったそれぞれの関数のリファレンスはこちら。

実際のコードはこんな感じです。
PostgreSQLにエクステンションを追加するマイグレーション

PGroongaを使わせるためのインデックスを追加するマイグレーション
カラムがこうなっている理由はこちらです。

pgroonga.score関数 | PGroonga

pgroonga.score関数を使うには、pgroongaインデックスにプライマリーキーに指定したカラムを追加する必要があります。もし、プライマリーキーに指定したカラムをpgroongaインデ>ックスに追加していない場合は、pgroonga.score関数は常に0.0を返します。

全文検索をしているクラス
titleとbodyを検索対象にしています。

実際に使用するとこうなります。

> article = Article.full_text_search('桃太郎', true).first
> article.highlighted_body
=> むかしむかしあるところで<span class="keyword">桃太郎</span>という少年が鬼ヶ島に行って鬼を退治しました。

全文検索そのものと、キーワードのハイライトについてはよかったのですが、pgroonga.score関数でとれるスコアを使ったソートで問題が・・・(つづく)

ヒント: インデックス

カテゴリー: PostgreSQL, Rails タグ:

Rails4→5で目にしたdeprecationたち。 このエントリーを含むはてなブックマーク はてなブックマーク - Rails4→5で目にしたdeprecationたち。

2016 年 8 月 24 日 Comments off

なにかの役に立つかも知れないので、淡淡と書いておきます。

DEPRECATION WARNING: Method map is deprecated and will be removed in Rails 5.1, as `ActionController::Parameters` no longer inherits from hash. Using this deprecated behavior exposes potential security problems. If you continue to use this method you may be creating a security vulnerability in your app that can be exploited. Instead, consider using one of these documented methods which are not deprecated: http://api.rubyonrails.org/v5.0.0.1/classes/ActionController/Parameters.html

DEPRECATION WARNING: uniq is deprecated and will be removed from Rails 5.1 (use distinct instead)

DEPRECATION WARNING: `redirect_to :back` is deprecated and will be removed from Rails 5.1. Please use `redirect_back(fallback_location: fallback_location)` where `fallback_location` represents the location to use if the request has no HTTP referer information.

DEPRECATION WARNING: `render :text` is deprecated because it does not actually render a `text/plain` response. Switch to `render plain: 'plain text'` to render as `text/plain`, `render html: '<strong>HTML</strong>'` to render as `text/html`, or `render body: 'raw'` to match the deprecated behavior and render with the default Content-Type, which is `text/plain`.

DEPRECATION WARNING: xhr and xml_http_request methods are deprecated in favor of `get :index, xhr: true` and `post :create, xhr: true`

DEPRECATION WARNING: ActionController::TestCase HTTP request methods will accept only keyword arguments in future Rails versions.
Examples:
get :show, params: { id: 1 }, session: { user_id: 1 }
process :update, method: :post, params: { id: 1 }

カテゴリー: Rails タグ: