🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Ruby on Rails 5

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

Q&A

解決済

3回答

1719閲覧

attr_accessorの変数を増やす時の基準とかってありますか?

tomoharu

総合スコア107

Ruby on Rails 5

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

0グッド

0クリップ

投稿2019/11/12 17:22

題名通りです。

Railsのattr_accessorに、変数をシンボルで定義できますが、例えば一つのメソッド内でしかその変数を使わないのにattr_accessorに定義するというのは、設計的にどうなのかという質問です。

例えば、

Userインスタンスがupdateされたときに、nameカラムだけが更新されたかどうかをコントローラーで判定し、それによってリダイレクト先を変えたいとします。
まずbefore_updateにchanged メソッドを用意しようとしました。

class UsersController def methodA user.update end end

class User   before_update :update_only_name?   def update_only_name?   self.changed == ['name']   end end

です。

しかし、このままでは、update_only_name?の返り値が取れず、controllerで判定できません。
なので、attr_accessorを用意し、

class User   attr_accessor :update_only_name   before_update :update_only_name?   def update_only_name?   if self.changed == ['name'] update_only_name = true end   end end

にして、コントローラーで

class UsersController def methodA user.update if user.update_only_name == true redirect_to :root else redirect_to :other_path end end end

にできます。

ですが、update_only_nameはこの機能のためだけに作られる変数であり、Userモデルの他の部分では使われない可能性が高いです。
この実装はクリーンだと思いますか?
他に方法はありますでしょうか。

何卒宜しくお願いします。

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

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

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

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

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

guest

回答3

0

ベストアンサー

変更されたカラムがname(とupdated_at)だけであることを判別するだけであれば、attr_accessorbefore_updateも不要です。

rb

1class User 2 def update_only_name? 3 self.previous_changes.keys == %w[name, updated_at] 4 end 5end 6 7class UsersController 8 def methodA 9 user.update 10 if user.update_only_name? 11 redirect_to :root 12 else 13 redirect_to :other_path 14 end 15 end 16end

ソースコードから一部抜粋

rb

1 # Change the name: 2 # 3 # person.name = 'Bob' 4 # person.changed? # => true 5 # person.name_changed? # => true 6 # person.name_changed?(from: nil, to: "Bob") # => true 7 # person.name_was # => nil 8 # person.name_change # => [nil, "Bob"] 9 # person.name = 'Bill' 10 # person.name_change # => [nil, "Bill"] 11 # 12 # Save the changes: 13 # 14 # person.save 15 # person.changed? # => false 16 # person.name_changed? # => false 17 # 18 # Reset the changes: 19 # 20 # person.previous_changes # => {"name" => [nil, "Bill"]} 21 # person.name_previously_changed? # => true 22 # person.name_previous_change # => [nil, "Bill"] 23 # person.reload! 24 # person.previous_changes # => {} 25 # 26 # Rollback the changes: 27 # 28 # person.name = "Uncle Bob" 29 # person.rollback! 30 # person.name # => "Bill" 31 # person.name_changed? # => false 32 # 33 # Assigning the same value leaves the attribute unchanged: 34 # 35 # person.name = 'Bill' 36 # person.name_changed? # => false 37 # person.name_change # => nil 38 # 39 # Which attributes have changed? 40 # 41 # person.name = 'Bob' 42 # person.changed # => ["name"] 43 # person.changes # => {"name" => ["Bill", "Bob"]} 44 # 45 # If an attribute is modified in-place then make use of 46 # <tt>[attribute_name]_will_change!</tt> to mark that the attribute is changing. 47 # Otherwise \Active \Model can't track changes to in-place attributes. Note 48 # that Active Record can detect in-place modifications automatically. You do 49 # not need to call <tt>[attribute_name]_will_change!</tt> on Active Record models. 50 # 51 # person.name_will_change! 52 # person.name_change # => ["Bill", "Bill"] 53 # person.name << 'y' 54 # person.name_change # => ["Bill", "Billy"]

投稿2019/11/13 01:07

編集2019/11/13 01:30
Mugheart

総合スコア2349

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

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

tomoharu

2019/11/13 03:41

まさに求めていたやつです。ありがとうございます!
guest

0

今回の質問に関して言うなら、maisumakunさんが書かれている通りです。
「このままでは、update_only_name?の返り値が取れず」という意味が??

一般的には、
インスタンス内でのみアクセスできるインスタンス変数を、インスタンスの外から触れるようにするために attre_* があります。ですからその定義をするか否かは、インスタンス内で使うか否かではなく、インスタンスの外から利用するようにすべきか否かで決めるものかと。(*)
今回の例は、methodが外から使えるのですからインスタンス変数に取り直す必要はない、ということです。

*)リファクタリングをバリバリやるようになると、内部だけの利用でも定義することがありますが

投稿2019/11/13 00:17

winterboum

総合スコア23567

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

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

tomoharu

2019/11/13 01:00 編集

ご回答ありがとうございます。下でも回答させていただいたのですが、changedメソッドはコールバック実行時にのみ機能するので、インスタンスメソッド内では動かず、条件文として機能しないという認識です。。。 https://qiita.com/ysKey2/items/7e429e478069b61b53bd こちらになります
winterboum

2019/11/13 01:00

call back の時に呼ぶように設定した、ということであって、そのmethodはいつ呼んでも良いのです
tomoharu

2019/11/13 01:03

すみません。まだ理解できておらず。。。changedメソッドはrailsでもともと定義されているメソッドで、普通にcontrollerでuser.changed_only_name? としても、changedは機能せず、配列が空になります。。。before_updateでメソッドを呼び出して初めて、['name']という配列が返ってくるという認識なのですが。。。
winterboum

2019/11/13 01:11

ああ、使うmethod間違えてますね。changed は true、falseが返りますから。 changed_attributes とか、使ってください。 attribute名だけ返すのもあると思う
tomoharu

2019/11/13 03:11 編集

ありがとうございます。やってみます。そうなりますと、winterboumさんの考えとしては、特にattr_accessorの数が増えること自体は悪ではないとお考えですか?
winterboum

2019/11/13 03:16

微妙 必要なattr_accessorが増えることは問題無いですが、 「必要」の判定基準が問題。インスタンス内部のデータを外に扱わせると言うのは独立性が減り結合が密になるので、オブジェクト指向に限らず望ましいことでは無いので
tomoharu

2019/11/13 03:41

承知しました。ありがとうございます。
guest

0

ですが、update_only_nameはこの機能のためだけに作られる変数であり、Userモデルの他の部分では使われない可能性が高いです。

それなら変数である必要がないのではないでしょうか。直接user.update_only_name?とすれば、変数は不要かと思います。

投稿2019/11/12 23:34

maisumakun

総合スコア145967

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

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

tomoharu

2019/11/13 01:01 編集

ご回答ありがとうございます。changedメソッドはコールバック実行時にのみ機能するので、インスタンスメソッド内では動かず、条件文として機能しないんですよね。。 https://qiita.com/ysKey2/items/7e429e478069b61b53bd こちらになります。
maisumakun

2019/11/13 00:59

changesやprevious_changesなど、コールバック外で変更列を取得する手段も存在します。
tomoharu

2019/11/13 03:10

ありがとうございます 調べます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問