説明すべき事が多くて少し混乱させてしまうと思います。
疑問点をコメントでもらえればと思います。
rubyのクラスとインスタンス変数へのアクセスについて
こういうクラス定義があったすると、
MyClass.new.nameとは出来ないのは理解できますでしょうか?
@nameはインスタンス変数で、外部に対しては非公開だからですね。
ruby
1class MyClass
2
3 # コンストラクタ
4 # MyClass.newとして使う
5 # メソッド名が異なる点に注意
6 # 違和感あるけどそういうものです
7 def initialize
8 @name = "あいうえお"
9 end
10
11end
@nameを外部から取得できるようにするために、railsのモデル風にnameというメソッドを定義します。
いわゆるゲッターメソッドですね。
本当は名前は、なんでもよくてget_nameとかでもいいです。
後から出て来る、attr_xxx系の命令と動作を同じにするため=説明の都合上でこういう名前にしてます。
ruby
1class MyClass
2
3 def initialize
4 @name = "あいうえお"
5 end
6
7 def name
8 @name
9 end
10
11 # ゲッターの名前は何でもいい
12 def get_name
13 @name
14 end
15
16end
これとまったく同じことをするのが、attr_reader :nameです。
セッターも欲しいなという場合には、attr_accessor :nameとします。
本題はここからで、手で書いてもいいし、attr_xxxを使ってもいいので、MyClass.new.nameと出来るようにすれば、コントローラでも同じように使う事が出来ます。
独自クラスのインスタンス変数を他でも扱う事が出来るわけです。
置き場所について
rails5であれば、app/以下にあるファイルは自動でrequireしてくれます。
従って、app/lib/mylibrary.rbに置けば大丈夫です。
確認方法は、rails consoleでその処理が実行できるかどうかで確認できます。
ただし、クラスまたはモジュールにする必要があります。
app/helpers/にxxxx_helper.rbを定義して、コントローラでincludeする方法もあるんですけど、
カオスになりがちなので、独自クラスを作るほうを先に覚えたほうがいいと思って、そこだけ説明しますね。
たとえば、このようにただメソッドを定義しただけでは、
app/lib/mylibrary.rb
ruby
1def sample
2 puts "sample"
3end
このように認識されません。
$ rails console
> sample
NameError: undefined local variable or method `sample' for main:Object
from (irb):1
そこで、mylibrary.rbの内容をクラスにします。
ファイル名とクラス名は一致させる必要がある点に注意。
ruby
1class Mylibrary
2 def sample
3 puts "sample"
4 end
5end
クラスなので、当然newしてからメソッドを呼ぶ必要がありますが、
きちんと実行できました。
コンストラクタ(initialize)は独自の処理が必要なければ、書かなくてもいいです。
$ rails console
> Mylibrary.new.sample
sample
rails consoleで実行できることは、コントローラでも実行できます。
従って、いきなりコントローラに書かずにconsoleでいろいろ試すのが一番効率がいいです。
実行と結果の確認がすぐできるため。
ブラウザで開くより早いですよね。
コントローラでは、このよう同じように書けます。
rails consoleで実験してうまくいったら、コントローラでもそのまま書けば良いというのがポイントです。
ruby
1class ScoreController < ApplicationController
2
3 def sample
4 Mylibrary.new.sample
5 あるいは、こうも書ける(文法上当たり前)
6 mylib = Mylibrary.new
7 mylib.sample
8 end
9
10end
コントローラの変数をビューに渡す方法=画面に表示する方法について
基本ルールについて理解があやふやっぽいので、ここについても説明します。
- コントローラのインスタンス変数を定義する(インスタンス変数=@付きの変数の事)
- ビューではコントローラのインスタンス変数が使える
- <%= @変数 %>で表示できる
これだけです。
要は、コントローラのメソッド=アクションの中で@変数を作れば、対応するビューでも使えるよ、
というだけのシンプルなルールです。
ruby
1class ScoreController < ApplicationController
2
3 def sample
4 # @mylibを定義したから、app/views/score/sample.html.erbでも@mylibが使える
5 @mylib = Mylibrary.new
6
7 # @customerを定義したから、ビューでも@cusotmerが使えるし
8 # @customer.scoreも使える
9 @customer = Customer.find(1)
10
11 # いきなり@scoreをビューで使いたければ、ここで定義する
12 @score = 13435
13 end
14
15end
個人的におすすめの方法
個人的な方法なのでとりあえず紹介だけしておきます。
内容は分からなくても問題ないですが、余計に混乱させたらすみません・・・。
私はこの手のクラスをコマンドと呼称する派ですが、世間一般ではサービスと呼ぶ事が多いです。
ある1つの操作(ポイントを更新するなど)を単体でクラスにします。
私の場合は、コンストラクタ(new=initaialize)と、実行(run!)を必ず用意します。
コマンドはnewしてrunするという使い方で統一するというルールを自分の中で作っているため。
app/commands/update_score_command.rb
ruby
1# スコアを特定のルールに従って更新するコマンド
2#
3# 使い方:
4# customer = Customer.find(1)
5# command = UpdateScoreCommand.new(customer: customer)
6# command.run!
7# score = command.score
8#
9class UpdateScoreCommand
10 # command.scoreとしたいので、getterだけ用意する
11 # command.score =1234とされてしまう事故は防ぎたいので
12 # attr_accessorは使わない
13 attr_reader :score
14
15 def initialize(customer:)
16 @customer = customer
17 @score = @customer.score
18 end
19
20 def run!
21 @score.sc1 = @customer.point + 100
22 @score.save!
23 end
24end
ruby
1class ScoreController < ApplicationController
2
3 def sample
4 @customer = Customer.find(params[:id])
5 command = UpdateScoreCommand.new(customer: @customer)
6 command.run!
7
8 @score = command.score
9 end
10
11end
追記
追記:mingosさんのおすすめコードについて
attr_reader :scoreのscoreとdef initialize(customer:)の(customer:)についてです。
attr_reader :scoreのscoreですが、mingosさんの例でいくとcommandテーブルのscoreカラムにアクセスするという意味になるのでしょうか?
attr_reader :scoreは、以下のコードと同じ意味です。
ruby
1class UpdateScoreCommand
2 def score
3 @score
4 end
5end
コマンドクラスはrailsのモデルではありませんので、データベースのカラムとは関係がありません。
ここでは、UpdateScoreCommandという普通のクラスのインスタンス変数として@scoreを使っているだけです。
データベースと関係するのは、ActiveRecordあるいはApplicationRecordを継承したクラスのみです。
railsでは、app/models/以下にあるクラスになります。
def initialize(customer:)の(customer:)ですが(customer)と「:」の有無によって何が違ってくるのでしょうか?
もしよかったら教えていただけないでしょうか?宜しくお願いします。
rubyの文法で、名前付き引数とかキーワード引数などと呼ばれるものです。
このようにメソッドを定義した場合、必ず、メソッド名(customer: 値) という風に使わないとエラーとなります。
引数が2つ、3つとかになって来るとありがたみが出てきます。
普通のメソッドは引数の順番を覚えておかなければいけませんが、キーワード引数であれば、引数に名前がついているので順序は適当で良いです。
あまりいい例が思いつきませんが、
ruby
1def sum(a:, b:)
2 a + b
3end
4
5# キーワード引数なら、引数の順序はばらばらでもいい。
6puts sum(b: 10, a: 5)
7puts sum(a: 30, b: 30)
これを使う理由は、コード自体が説明書になるからですね。
今回の例では引数が1つなので、メリットはありませんが、常にこう書いたほうが楽なので。
実際いろいろやってると、メソッドの引数が3つ、4つ、5つって事が出て来ると思います。
そうなると、順番を確認するために毎回メソッド定義を見なくてはいけないくてしんどいです。
例えば、コマンド引数を増やしてみます。
基本的には+100ですが、任意に追加するポイントを変更できるようにしたい・・と無理やり条件を考えてみます。
ruby
1class UpdateScoreCommand
2
3 def initialize(customer:, add: 100)
4 @customer = customer
5 @score = customer.score
6 @add = add.to_i # 文字列とか来るかもしれないので、数値にする
7 end
8
9 def run!
10 @score.sc1 = @customer.point + @add
11 @score.save!
12 end
13end
14
15customer = Customer.find(1)
16c = UpdateScoreCommand.new(customer: customer, add: 500)
17c.run!
もし、キーワード引数を使わないとすると、こうですね。
ruby
1class UpdateScoreCommand
2
3 def initialize(customer, add)
4 @customer = customer
5 @score = customer.score
6 @add = add.to_i
7 end
8end
9
10c = UpdateScoreCommand.new(customer, 500)
仕事でプログラミングをやっていると数年前に書いたものを修正したりすることがよくあるのですが、
その頃にはもう自分で書いたものでさえ覚えていません。
まずは、コードの全体を把握しなければいけませんが、各メソッドの詳細を見なくても、
たぶんこういう事をしているんだろうな、という想像が出来るコードであって欲しいのです。
ruby
1c = UpdateScoreCommand.new(customer: customer, add: 500)
と
c = UpdateScoreCommand.new(customer, 500)
では、どっちが良いかという話なんですね。
時が経っていると、自分で書いたコードでありながら、500ってなんだよ?みたいな気持ちになります。
前者もベストとは思いませんが、addって書いてあるので、まぁ500を足すんだろうくらいの予想ができます。
クラス名もUpdateScore。。。って書いてあるし。
この単純な例では、どっちでも良いという思うかもしれませんが、実際のコードはもっと複雑です。
うーん、うまい説明が出来なくて申し訳ないんですが、将来自分が困らないために面倒でもキーワード引数を使っているわけです。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/10/04 03:33
2017/10/04 03:38
2017/10/04 04:57
2017/10/04 05:29
2017/10/04 05:38