質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

1461閲覧

【Rails】モデル内で関数を再帰的に呼び出し、ユニークな文字列を作成する

Romay

総合スコア40

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2019/07/14 10:03

前提・実現したいこと

Ruby 2.4.5
Rails 4.2.8
Gem: devise, friendly_url

https://サイト名/users/:id
の:idをユニーク制限(被らない)ランダム文字列にしようとしています。

また、friendly_urlを使っており、ユーザーが編集画面で自分の好きなURLを設定できるようにしています。
ただ、その前にユーザー登録時にランダム文字列を自動的に付与しデフォルトのURLにしたいと考えています。
(users/:idだと1から連番になり推測されやすいため)

Userモデル内で、新規ユーザー登録時に、カラムfrienly_urlにランダムな文字列を挿入しています。
しかし、friendly_urlは、ユーザー登録後、ユーザー自身で変更できるので、重複しないようチェックをかけ、重複していた場合は再帰的に呼び出し再作成するようにしたいです。

該当のソースコード

User.rb

ruby

1class User < ActiveRecord::Base 2 before_create :generate_url 3 4(省略) 5#friendly_url 6 include FriendlyId 7 friendly_id :friendly_url, :use => [:finders] 8 9 validates :friendly_url, length: { in: 3..20 }, 10 uniqueness: true, #一意性 11 format: { with: /\A[\w@-]*[A-Za-z][\w@-]*\z/ }, 12 # on: :friendly_url_user #登録時に入力必要なし 13 allow_nil: true #無入力許可 14 15 #ユーザー登録時のfriendly_id自動生成 16 def generate_url 17 if friendly_url.nil? 18 self.friendly_url = SecureRandom.hex(10) 19 self.save 20 #ここで、ランダムに作成したfriendly_urlが既に存在する場合、再帰的に呼び出してもう一度関数実行したい 21 end 22 end 23end

試したこと

再帰的な関数を色々調べたのですが分からず詰まってしまっています。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

再帰にする必要は無いでしょう。

self.friendly_url = SecureRandom.hex(10) untile self.save self.friendly_url = SecureRandom.hex(10) end

厳密には save 失敗の原因が uniqueness のみで有ることも調べる必要はあるでしょう。

ところで、saveで調べなくても

friendly_url = SecureRandom.hex(10) while User.find_by(friendly_url: friendly_url) friendly_url = SecureRandom.hex(10) end

でも ほぼ 同じことができますし、saveでやるのとちがって、uniqunessであることが確実ですが、saveでやる必要が有るほどクリティカルですか?

投稿2019/07/14 11:51

winterboum

総合スコア23329

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Romay

2019/07/15 05:45

コメントありがとうございます。 1つめは、until文でsaveができるまで繰り返すということですね。 2つめは、作成したランダム文字列が存在するかどうかfind_byして、falseになるまで繰り返す、という認識で間違い無いでしょうか? 2つめの場合、その後にfriendly_url.saveをしてあげる必要もありますでしょうか? 2つめのほうが分かりやすくていいなと個人的には思いました!
winterboum

2019/07/15 06:16

2つめはそれでユニークな friendly_url を得て、そしてだいにゅうしてsaveが必要です。 SecureRandom 使っているから大丈夫だろうと思いますが、この方法は注意すべきところが有ります。 ユニークであることを確認してSAVEする間に他の人がユニークであることを確認してsaveしてしまうかもしれない。ニックナームとか名前とかユーザに自由に入れさせるものでのユニーク性を確保する場合は、そこに注意が必要です
Romay

2019/07/15 07:11

なるほど、おっしゃるとおりその可能性も考えられますね。 それを考慮する場合、どのような設計をすべきだとお考えでしょうか?
Romay

2019/07/15 07:53

def generate_url if friendly_url.nil? friendly_url = SecureRandom.hex(10) while User.find_by(friendly_url: friendly_url) friendly_url = SecureRandom.hex(10) end self.friendly_url = friendly_url end end というかたちで、得られたユニークなfriendly_urlをselfで代入する形にしたところ、保存もされておりうまくいきました!しかし、self.saveを追加したところエラーが出てしまいました。この場合、saveは必要ないのでしょうか?self.friendly_url = friendly_urlのみで保存されていたため、疑問に思いました。
winterboum

2019/07/15 07:58

ああ、うっかりしてました。 has_*, belngs_to が定義してあり、かつ この例では self.friendly_url の self がDBにsave済であった場合は、 self.friendly_url = friendly_url でsaveされます
winterboum

2019/07/15 07:59

>それを考慮する場合、どのような設計をすべきだとお考えでしょうか? case by case です。一般的に説明できるほどは私もベテランではないので、、、、
Romay

2019/07/15 08:05

なるほど!アソシエーション定義しているかつsave済みだからだったのですね! 今回の場合は、saveする間に他のユーザーがsaveするという状況は考えにくいので無視することとします。 ちなみに、上記で成功したのですが、ランダム文字列が20文字になっております。SecureRandom.hex(10)で、10文字と認識していたのですが、2回生成されて繋がってしまっているのでしょうか?
winterboum

2019/07/15 08:11

大抵の説明には 16進10文字 と読める説明なのですが、どうも 10Byteの16進表現の様です。 ソース読んでいないので結果からの判断です
Romay

2019/07/15 08:39

10Byteだとしても、半角文字だと10文字になるとの認識なのですが、違いますでしょうか。自分は知識が乏しく、2回生成されているのでは?と思ってしまい。。。
winterboum

2019/07/15 08:42

16進文字10Byteではないです。 10Byteの整数の16進表現です。 1Byte の整数を16進で表現すると2文字です。
Romay

2019/07/15 09:13

なるほど!そういうことですか。大変勉強になりました!!ご回答いただきありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問