メタプログラミングRuby 5章 クラス定義
特異メソッド
カレントクラス
Rubyのプログラムは常にカレントオブジェクトselfを持っているが、同時にカレントクラスも持っています。
class C def m1 p self def m2; end end end class D < C; end obj = D.new obj.m1 # => <D:0x00007fc1e2865938> p C.instance_methods(false) # =>[:m1, :m2] p D.instance_methods(false) # => []
メタプログラミングRubyのサンプルに少し書き足してみました。 カレントオブジェクトはDクラスのインスタンスですが、カレントクラスはCクラスであることがわかりますね。よってm2はCクラスのインスタンスメソッドになっています。
Classでクラスを開くことでカレントクラスを変えることができるのですが、この方法はカレントクラスにしたいクラスの名前がわからないと使えません。クラス名がわからない時に使うのが class_evalメソッドです。
def add_method_to(a_class) a_class.class_eval do def m 'Hello!' end end end add_method_to(String) p "string".m # => 'Hello!'
このadd_method_toメソッドではクラスを引数にとってclass_evalを使うことで引数にとったクラスにカレントクラスを変更してからメソッドを定義しています。 class_evalは第四章のinstance_evalとは別物です。 instance_evalはselfを変更してブロック内を評価してくれるものでしたが、class_evalはselfに加えてカレントクラスも変更します。 スコープゲートを開いているわけではないのでスコープは変わりません。
クラスインスタンス変数
クラスのインスタンス変数とクラスのオブジェクトのインスタンス変数は別物です。
class Myclass @my_var = 1 def self.read @my_var end def write @my_var = 2 end def read @my_var end end obj = Myclass.new p obj.read # => nil obj.write p obj.read # => 2 p Myclass.read # => 1
ここでは2つの異なるオブジェクトである@my_varを定義しています。 クラスもオブジェクトの一つであるということを念頭において考えて行くのが大事になってきます。 最初にでてくる@my_varがクラスインスタンス変数。2つ目@my_varが今回だとobjオブジェクトのインスタンス変数ですね。 (ここメタプログラミングRubyでは1つ目の@my_varがobjのインスタンス変数で2つ目の@my_varがMyclassのクラスインスタンス変数だ、と書かれているのですが、コードの順番で行くと逆なので少しわかりにくかったです。説明の順番としては仕方ないのかもしれないですが。) クラスインスタンス変数はClassクラスに属しているオブジェクトのインスタンス変数というのが正しいです。MyclassはClassクラスのインスタンスなのでその中で定義されたものはクラスインスタンス変数となります。 クラスインスタンス変数はクラスしかアクセスできず、そのクラスのインスタンスからアクセスすることはできません。
特異メソッド
Stringに属したオブジェクトが全て大文字化かどうかをbooleanで返すメソッドをつくりたいが、特定の文字列にしかこのメソッドをもたせたくない、という場合。Refinementsを使って、特定のコードの前にusingを挿入するという方法もありますが、別の方法もあります。それが特異メソッドです。 同じクラスのオブジェクトでも、ある特定のオブジェクトにだけメソッドを追加したい、という場合に特異メソッドを使うことができます。
str = "string" def str.title? self.upcase == self end p str.title? # => false p str.methods.grep(/title?/) # => [:title?] p str.singleton_methods # => [:title?]
今までクラスに定義していたクラスメソッド
Class Hoge def self.a_method end end
これはクラスもオブジェクトであるという点からみれば
Hoge = Class.new def Hoge.a_method end
という形とやっていることは同じです。つまりクラスメソッドはクラスの特異メソッドであるという事になります。
クラスマクロ
attt_*のようなメソッドをクラスマクロと呼ぶ。selfがクラスでも使えるのでクラスメソッドということですね。
特異クラス
特異メソッドがどこに定義されているのか?という問の答えが特異クラスです。
obj = Object.new p obj # => #<Object:0x00007f9fa893a068> p obj.singleton_class # => #<Class:#<Object0x00007f9fa893a068>> singleton_class = class << obj p self # => #<Class:#<Object:0x00007f9fa893a068>> end
この例では<Class:#<Object:0x00007f9fa893a068>>がobjオブジェクトの特異クラスです。 class << objでも発見することができますが、singleton_classメソッドを使ったほうが楽でしょう。 オブジェクトのメソッド探索を行う際にはオブジェクトのクラスをみてそして更に親のクラスへとたどって行きますが、実は、オブジェクトのクラスのメソッドを見る前に、そのオブジェクトの特異クラスのメソッドを見にいっているのです。 クラスにもクラスはあるので、当然特異クラスがつくれます。
class C class << self def a_class_method 'C.a_class_method()' end end end
これは要は
class C def self.a_class_method 'C.a_class_method()' end end
と同じで、クラスメソッドはそもそもそのクラスの特異クラスに定義されています。
p C.singleton_class.superclass # => #<Class:Object> p C.superclass # => Object
このようにクラスの特異クラスのスーパークラスはクラスのスーパークラスの特異クラスになります。
大統一理論
Rubyのオブジェクトのルールとして7つのルールが示されています。
- オブジェクトは一種類しかない。それが通常のオブジェクトかモジュールになる。
- モジュールは一種類しかない。それが通常のモジュール、クラス、特異クラスのいずれかになる。
メソッドは一種類しかない。メソッドはモジュール(大半はクラス)に住んでいる。 一種類しかないということは複数から同時に継承しないということです。下記の5にありますが、必ず親から子への一直線以外に継承はしていません。
全てのオブジェクトは(クラスも含めて)「本物のクラス」を持っている。それが通常のクラスか特異クラスである。 メタプログラミングRubyでのいわゆる「右へ」の移動で、本物のクラス、つまり特異クラスをもっているということですね。ほとんどのオブジェクトは特異クラスをもっています。true, false, nilは持っていません。(NilClass等はもっていますが。)
すべてのクラスは(BasicObjectを除いて)一つの祖先(スーパークラスかモジュール)を持っている。つまりあらゆるクラスがBasicObjectに向かって1本の継承チェーンをもっている。 これと1,2,3のルールのおかげでメソッド探索はわかりやすいものになっていると思います。継承チェーンは二股に別れたりはしていません。
オブジェクトの特異クラスのスーパークラスは、オブジェクトのクラスである。クラスの特異クラスのスーパークラスはクラスのスーパークラスの特異クラスである
- メソッドを呼び出すときはRubyはレシーバーの本物のクラスに向かって右へ進み、継承チェーンを「上へ」進む。 この2つからなぜインスタンスがクラスメソッドにアクセス出来ないかを説明することができます。 クラスメソッドはそのクラスの特異クラスに定義されています。 Rubyのメソッド探索では一度だけしか右に行かないので、インスタンスからメソッド探索をおこなった場合、メソッドの特異クラスへと一度右に行った後はその特異クラスのスーパークラスであるobj.classが戻すクラスから上に探索していくので、そのスーパークラスの特異クラスまでは探索しません。よってスーパークラスの特異クラスにあるメソッドはインスタンスからはアクセスできないということになります。
Object#extend
モジュールをincudeするとモジュールのインスタンスメソッドのみがincludeしたクラスでインスタンスメソッドとして使えるようになりますが、 クラスの特異メソッドを開いて、その中でモジュールをincludeするとモジュールのインスタンスメソッドをクラスメソッドとして定義できます。 オブジェクトの特異クラスにも同様にでき、よく使われるので、Object#extendとしてメソッドが提供されています。
module MyModule def my_method; p 'hello'; end end class MyClass extend MyModule end MyClass.my_method # => 'hello'
メソッドラッパー
アラウンドエイリアス
class String alias_method :origin_reverse!, :reverse! def reverse! upcase! origin_reverse! end end p "ruby".reverse! # => "YBUR"
エイリアスを定義することで、メソッドの再定義を行う時に、元のメソッドをエイリアスメソッドで呼び出して、ラップすることができます。
ここでは古いreverse!
を新しいreverse!
にラップしています。一種のモンキーパッチにも近い方法です。
新しいreverse!
は古いreverse!
の周囲(アラウンド)をラップしているのでこのトリックをアラウンドエイリアスをと呼びます。
Refinements
Refinementsを使ってsuperを呼び出すと元のメソッドを呼び出せます。
module StringRefinements refine String do def reverse! upcase! super end end end using StringRefinements p "python".reverse! # => "NOHTYP"
Prependラッパー
module ExpilcitString def reverse! upcase! super end end String.class_eval do prepend ExpilcitString end p "go".reverse! # => "OG"
Prependは継承チェーンの下にモジュールが挿入されるのでメソッド探索の際にはprependされたモジュールにあるメソッドが先に使われる。
5章のクイズ
最初はsuperで解決しようとしたんですが、古い+に依存しているのでなかなか書けませんでした… メタプログラミングRubyのようにアラウンドエイリアスで解決するのが良さそうです。
まとめ
クラスメソッドがどこに定義されているのか?という問いに答えてくれる章でしたね。 ソースコードを読んでextendってなんだ?となることが多かったので、extendで特異クラスにメソッドを定義できるのは知っておきたいところ。 また特異クラスには特異クラスがあるので特異クラスの特異クラスの特異クラスとずっとつなげて行けるのでは…
メタプログラミングRuby 四章 ブロックの読書録
はじめに
前職の出勤が16日に終わり、しばしニートしております。 最近discordbというボイスチャットアプリのbot作成のラッパーGemが面白くていじっているんですが、このGemを読んでいるとメタプログラミングRubyの4章にこんなのあったなぁという気持ちが強まり、読書録として少し理解を文章に起こしてみました。5章と6章も個人的に何度でも反芻したいので書いていきたい。
ブロックは強力なツール
この章ではブロックがスコープを操る強力なツールだということを解説してくれています。 あと途中で上司が逃げます
ブロックは束縛を包んでくれる
Rubyではdef、class、module (これらはスコープを開く門)でスコープが切り替わってローカル変数なんかは共有できなくなります。
class Hogeclass class_value = 1 def hoge_method p class_value # => Error! end end
裏を返すとこの門を開かなければ新しいスコープは開かれません。 class やmoduleであればClass.new {}やModule.new {}としたり、 defをdefine_method { }にしてブロックを渡すことで新しいスコープが開かれなくなります。
class Hogeclass class_value = 1 define_method :hoge_method do p class_value # => 1 end end
このようにブロックは現在の束縛を包み込んでnewやdefine_methodといったメソッドに渡すことができます。
instance_eval
instance_evalはBasicObjectのインスタンスメソッドでブロックを渡すとレシーバーをselfにして評価してくれます。
class Hogeclass def initialize @foo = "value" end private def private_method p "秘密" end end hoge = Hogeclass.new hoge.instance_eval do @foo # => "value" end hoge.instance_eval do private_method # => "秘密" end
instance_evalを使えば例えばテストを書く際にクラスのインスタンス変数をいじったり、クリーンなオブジェクト(BasicObjectのインスタンス等)を作ってブロックを評価することができます。
呼び出し可能オブジェクト
ブロックの「コードを保管して、あとから実行する」という方式はなにもブロックだけのものではありません。
Procオブジェクト
ブロックはオブジェクトではないので後で呼び出したりしたい時に不便です。 ブロックをオブジェクトにするものとしてrubyにはProcクラスがあります。
double = Proc.new { |x| x * 2 } double.call(4) # => 8
わかりやすい変数にインスタンスをいれておけるので呼び出しも楽ですね。
ブロックをProcに変換するのに便利なメソッドもあります。lambdaとprocです。
double = lambda { |x| x * 2 } # => <Proc:0x00007f9e5~~~ triple = proc { |x| x * 3 } # => <Proc:0x00007f9e5~~
どちらもProcのインスタンスがつくられてるのがわかりますね。 Proc , proc とlamdaの違いとしてreturnの意味合いが違うことと、引数のチェックが違うことが挙げられます。 returnの意味合いが違うというのは以下のコードで確認できます。 (メタプログラミングRubyではProc.newとlamdaの違いしかなかったので今回はprocで試してみました)
def lambda_ten_double lambda_proc = lambda { return 10 } result = lambda_proc.call return result * 2 end def proc_ten_double proc_proc = proc { return 10 } result = proc_proc.call #ここでProcが定義されたスコープから戻ってしまう return result * 2 #ここまでたどり着かない end lambda_ten_double # => 20 proc_ten_double # => 10
引数のチェック方法にも違いがあり、
p = Proc.new { |a,b| [a,b] } l = lambda { |a,b| [a,b] } p.call(1, 2, 3) # => [1, 2] l.call(1, 2, 3) #=> ArgumentError (wrong number of arguments~
項数にも厳しくreturnの挙動も単なる終了なので特別な理由がなければlambdaを用いたほうが良さそうです。
&修飾でもブロックをProcに変換することができます。 また&修飾をつかえばProcオブジェクトをブロックに戻すこともできます。 どういうことかというと、
def block_method yield end def do_block_method(&block) #ここの&でブロックをProcオブジェクトに p block.class # => Proc &つけないとProcのまま! block_method(&block) # ここの&でProcオブジェクトからブロックに変換、 ないとオブジェクト渡してしまうのでArgumentエラー end do_block_method { p "hoge" } # => "hoge"
{ p "hoge" } が一度Procオブジェクトになり、またブロックにもどって最終的にblock_methodに渡されていることなります。
Methodオブジェクト
メソッドも呼び出し可能なオブジェクトです。 具体的には
class Hogeclass def initialize @x = "hoge" end def hogemethod @x end end hogeobject = Hogeclass.new method = hogeobject.method :hogemethod method.call # => "hoge"
メソッドオブジェクトは呼び出した時にオブジェクトのスコープで評価される。(今回はHogeclassクラス)
この束縛は解除して別のオブジェクトに束縛したりもできる。(ただし同じクラスかそのサブクラスのオブジェクトに限る)
class Fugaclass < Hogeclass def initialize @x = "fuga" end end unboundmethod = method.unbind fugaobject = Fugaclass.new rebindmethod = unboundmethod.bind(fugaobject) rebindmethod.call # => "fuga"
四章のクイズ
自分は4章最後のクイズでは、サンプルを見る前に以下のようなコードを書いていました。
def setup(&block) @setups << block end def event(description, &block) @setups.map(&:call) puts "ALERT: #{description}" if block.call end @setups = [] load 'events.rb'
eventごとに実行するならevent内でsetupに渡されたProcよびだしても良いのでは?と思って書きました。少し一つのメソッドでやりすぎかも。
この処理だけ行いたいならeventメソッドの 引数&blockは削除して、後置ifもif yieldにしても動作すると思いますが、ブロックを渡す必要があるメソッドでそんなことをしたらわかりにくすぎますね。
改めて見ると逃げた上司ののグローバル変数をどうして削除しない?が刺さるコードです。
グローバル変数を削除して、
lambda { setups = [] Kernel.send :define_method, :setup do |&block| setups << block end Kernel.send :define_method, :event do |description, &block| setups.map(&:call) puts "ALERT: #{description}" if block.call }.call load 'events.rb'
という形にしてみました。
おわりに
一度読んだ本をいざ文章にまとめてみるといろいろ考えることが多くていいですね。ガシガシ続けて行きたひ。 色んな人からツッコミもらいたいのでQiitaに書くか迷いましたが、このブログの主目的が「本の理解を文におこして再確認する」と考えていますし、先人の知見をQiitaに乗っけるのは違くない?と思ってブログに起こしました。
erbからslimへ html2slimの変換で少し気になったこと
erbからslimへ
年末の空いた時間に、個人でたたき台として使っているrailsアプリのviewをerbからslimに変えて見ました。
GitHub - QWYNG/GuideGG: チャンピオンに対するガイド記事のブックマークサイト
for i in app/views/**/*.erb; do erb2slim $i ${i%erb}slim && rm $i; done
変換して削除を繰り返すでコマンド一つで変換できたので楽でしたね
erb2slim app/views -d
でもできます。 HTML2Slim のREADME でも紹介ありますがデェレクトリ指定できるのでforで回す必要ないですね。-dはdelete オプションです。 (コメントありがとうございます!)
slim、読みやすい
slimのREADMEを読んでいてまず覚えておくべきことは
- - が制御コードを意味している。(forとかif) endもいらないようです。
- = が出力するrubyコードの呼び出し(erbでの<%= %>)
- | を使うとパイプよりも深くインデントされた全ての行をコピーする。
程度だと感じました。erbとくらべてすっきりして読みやすいです。
気になったこと
いくつか気になったことをメモしておきます。
cssは.続けて記述することでマージが可能。
<div class="container body-content">
というcssの指定があった場合
.container.body-content
とピリオドをつなげることでcssのマージができます。
自動変換でうまく対応してくれない所
これは自動変換だとどうなんだろうと思った所なのですが
<div id="<%= "user_icon#{user.id}" %>"
このerbのコードが自動変換だと
| <div id=" = "user_icon#{user.id}" | " >
とずいぶんと汚く変換されるのですが slimは= の後に直接 Ruby コードを書けますので
id="user_icon#{user.id}"
と書けます。
また、iframeなんかも自動変換だとうまく変換してくれないので自分でなおしました。
<iframe class="hatenablogcard"
がそのままの表記でslimファイルとして残されてしまうので、手動で
iframe class="hatenablogcard"
とする必要がありました。
今年のまとめ
今年は転職でなかなか精神MP使って大変でした。勉強会、スクール、日々のゲーム友達等々、関わってくださった方には本当に感謝しています。 来年はエンジニアとして働き始める予定です。このブログで技術書の読書録を継続して行きたいですね。
正規化理論 (第三正規形まで)
正規化理論とは
データベース技術 [実践] 入門を読んで第二弾
データベース設計で行うべき手順
第1正規形から第5正規形まであります。
基本的には第三正規形までやれば十分といわれているらしいです。
第一正規形
テーブル構成において重複や繰り返し、複合値(一つのカラムに複数の情報をもたせること)を含んだ構造にしないこと。
重複があると一部書き間違えてしてしまっても検知できず、同じ部署に所属している人同士で部署の連絡先が違うといった事態になってしまいます。
この正規化を行うにはテーブルを複数つくって関連付けを行い重複を避けるという方法が本書には書かれています。
第二正規化
主キーが複数のカラムから構成されていて、そのうちの一部のカラムの値によってのみきまる列があるテーブルを第二正規化が行われていないテーブルと呼ぶ
order_id | user_id | user_name | created |
---|---|---|---|
1 | 201 | 花村陽介 | 2018-12-23 |
2 | 202 | 天城雪子 | 2018-12-24 |
order_idとuser_idでcreatedカラムは決まるがuser_nameカラムはuser_idだけで決まるので、このテーブルは第二正規化が行われていないテーブルと言えます。なぜこういったテーブルが問題なのかというと、同じuser_idを持ちながら違うuser_nameを登録することもできてしまうからでです。
第三正規形
テーブルのすべてのカラムは列は主キーによってただ一つに決まるべきである、というもの。
例えば
user_id | user_name | school | school_place |
---|---|---|---|
1 | 花村陽介 | 八十神高等学校 | 八十稲羽市 |
2 | 伊織 順平 | 月光館学園 | 巌戸台 |
このテーブルではuser_idでuser_nameは決まるが、school_placeはuser_idと直接関係がありません。この構造の問題点は同じschoolであるにもかかわらず、異なるschool_placeが登録できてしまう点です。
まとめ
第一正規形 ー> 重複がないようにしましょう。
第二正規形 ー> 複数の主キーがある場合には一部のキーだけで値が決まるようなカラムは持たせないようにしましょう。
第三正規形 ー> 主キーで直接値が決まらないカラムはそのテーブルが持つべきではありません。
B+Treeインデックス (webエンジニアのためのデータベース技術[実践]入門)
はじめに
webエンジニアのためのデータベース技術[実践]入門を読み進めているのですが、なかなか腹に落ちる感じがないので少量ずつ文字にして理解を勧めていきたい
B+Treeインデックス
ルートブロック、ブランチブロック、リーフブロックという木のような構造をもっているデータベースのインデックス構造の一つ。リーフブロックに実際のデータの格納位置の情報があり、ルート、ブランチにはそれぞれ下のブロックの検索のキーに対して対象のブロックはどこにあるのかという情報のみある。
ハッシュインデックスの進化版?
この図はルートとブランチが兼ねているが、d1を検索するにはまずルートブロックの3以下を選びリーフブロックから1をみつけだして(scanして)d1の場所を見つけるという流れになる。
分岐を多く作れるので枝分かれを2つしか作れない二本木構造より階層を浅くでき、アクセス回数を減らすことができる。
B+TreeとB-Tree
B-Treeというインデックス構造もあり、これは途中のブランチにも値を持つことがあるデータ構造です。リーフまでアクセスしなくても値を見つけることが利点ですが、このB-Treeと比較してB+Treeは範囲検索する時に大きなメリットを持ちます。
上の図を見ればわかりますがB+Treeでは目的の値がそのリーフだけではすべて見つからない時には隣のリーフへジャンプして値を探しに行っています。リーフにしか値はないので途中でブランチブロックに戻ったりする必要はありません。B-Treeはブランチにも値があるかもしれないのでブランチやリーフを行ったり来たりして値の存在確認をしなくてはならない
B+Treeインデックスのメリット
二本木(2又にのみ別れていく木の構造)よりも階層を浅くでき、さらにブランチにデータの格納場所を持たないため一度リーフにたどり着けば範囲検索でも隣のリーフのみ探せば良いので高速。
インデックスの”デファクト”とのこと。デファクトスタンダードってことかな?
RESTとはなにか webを支える技術を読んで
はじめに
エンジニアとして働くための予習としておすすめされた技術書を業務のあとに読んでいるのですが、なにかしらアウトプットしないと身につかないなぁという思いがありブログをしたためました。本のまとめ的な内容なのでQiitaにあげるのも違うよなぁと思った次第です。
今回は山本陽平氏著のWebを支える技術の中で解説されているRESTについてです。
こちらも参考にしました
Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST)
RESTとは
REST (REpresentational State Transfer) とはアーキテクチャスタイル(システムの構造での共通した思想、構造)の一つ
RESTは次の6つのアーキテクチャスタイルを組み合わせた複合アーキテクチャスタイル
クライアント/サーバー
クライアント/サーバーとはクライアントからサーバーにリクエストを送り、サーバーはそれに対してレスポンスを返すというアーキテクチャスタイル。サーバーとクライアントがそれぞれ独立しているので、クライアントをマルチプラットフォームにできたり、逆にサーバーを増やして冗長化し、一つサーバーに問題があっても別のサーバーを使えるようすることができる。
ステートレスサーバー
サーバーがクライアントの要求をいちいち覚えていないようにするアーキテクチャスタイル。
いちいち覚えるのはクライアントが少なければ便利だが、実際は数多くのクライアント相手にしなければならないので、サーバーへの負担は大きくなってしまう。サーバーを増設してもクライアント情報の同期がという問題が発生するので、規模を大きくしにくいという問題もある。
そこでステートレスサーバーとしてクライアント側がそれぞれ要求を自分で保持していれば、サーバーはクライアントの情報を共有する必要がなくなるので、クライアントが多くなってもサーバーを増設することで対応できるようになり、規模を大きくしやすくなる。
Webを支える技術内でのハンバーガー屋さんの例がとってもわかりやすいので、気になる人はWebを支える技術買いましょう。
キャッシュ
サーバーから取得したリソースをクライアント側で保持して使い回すアーキテクチャスタイル。使い回せると通信量が減ってパフォーマンスアップ。
HTTPの機能としてのキャッシュでは有効期限を決めたり(相対的な決め方もできる、現在から何時間とか)、条件付きでクライアントに使わせたりできます。
統一インターフェース
リソースへの操作を制限してみんな同じインターフェースで扱うようにしましょうというアーキテクチャスタイル。
例えばファミコンはいろいろゲームがあるけど操作するのは基本的に同じ形状のコントローラー。ゲームはコントローラーからの入力にのみ対応できるようにすれば良い。これが人によってコントローラーの形状やボタンが全くちがう状態になってしまうと開発者側はゲームをあらゆる入力に答えるよう作らなくてはいけなくなってしまう。
この状態だとゲームは複雑になってしまうばかりか、コントローラーのことを常に考えたものになってしまい、独立性も失ってしまう。
HTTP1.1ではGETやPOSTなどの8個のメソッドという共通のコントローラーに限定することで、リソースへの操作は統一されシンプルかつ、サーバーとクライアントの独立性を高めている。
階層化システム
システムをいくつかの階層に分離するアーキテクチャスタイル。これはシステムを大きくすることへの制約であり、古いシステムをカプセル化してシステム全体への影響を最小限にするといった、システムそれぞれの独立性を高めるような構造をつくることができる。
コードオンデマンド
プログラムをクライアントにダウンロードして実行するアーキテクチャスタイル。JavaScriptやFlash等がこれに当てはまる。クライアントの簡素化、拡張性の向上がみこめるアーキテクチャスタイル。欠点として通信で何をしているのかがわかりにくくなってしまうのであくまでオプションとしての制約。
WebのアーキテクチャスタイルをRESTにするメリット
インターフェースが統一されているので互換性が保たれ、ブラウザをを統一したりしないですむ。クライアントとサーバーを分けていて、かつステートレスなので、規模を大きくすることもやりやすい。
まとめ
RESTというとリソースにCRUDで正しいURIになるようにする、という程度の認識しかなかったので勉強になりました
自分がつくるサービスもRESTという理想になるべく近づけたものを作っていきたいなぁとおもいました(感想)