startmac
生まれてはじめて、プログラミングなるものしてみんとて。
いやー、Webディレクターをしていると、ちょっとでいいから自分でプログラミングができるといいなと思いはじめるんですよねー。
でもあまりにも敷居が高くて、なかなか手を出せず、そしてどんどん月日は流れていくばかり。
で、このたびStart Macに当選してMacBookをもらったとき、これを機に、
「今度こそ、絶対に、何が何でもプログラミングを学ぼう」
と思ったんですよね。ほら、MacってベースがUNIXだから、なんとなくプログラミングとかもやりやすそうな気もするし。
なので、今回はちょっと気合を入れて、先生を見つけて、時間をとって、とうとうやってしまいました。
プログラミング童貞を捧げる相手は、「Ruby on Rails」。とても簡単にプログラミングができると話題のフレームワークです。
Rubyというプログラミング言語をベースに、色々とプログラミングがしやすい環境を整えているとのこと。詳しくはITProの記事なんかをお読みください。
先生は増井雄一郎(masuidrive)さん。「10分で作るRailsアプリ」ムービーで有名な、Ruby on Rails伝道師の1人です。PukiWiki Developers Teamの立ち上げや、僕も使っている「アバウトミー」の開発などでも有名。
アゼンブラをガリガリ書くところから最先端フレームワークまでこなす、流しのプログラマーで、かつ、しゃべりや原稿書きも得意で、人に教えるのも好きという、まさに先生にうってつけの逸材です。
その増井さんを丸1日拘束して、みっちりとレッスンしていただきました。
内容は、
生まれてこのかた全くプログラムをしたことのない僕が、Ruby on Railsを使って、「サイトの『いい』『悪い』の投票をする」というWebサービスを1日で作る
です。
で、結果はですね……
本当に1日でできてしまったのです……
感動しました! いや素晴らしい体験でしたよ!
Ruby on Rails万歳! なに? 通はRoRとか略すの? とにかく万歳!
世界がグッと広がった気がします。「自分でもできるんじゃないか」という気がガンガンしてきました。
今まで学校でも予習復習など1度もしたことがないのですが、今回の経験は、どうしても忘れたくないので、みっちり復習してまとめを作りました。
そのまとめを、以下に公開します。超長いので読むには適さないですが、書いてある通りにすると、あなたも僕と同じWebサービスが作れます。
また、既にプログラミングが得意な人は、「へー、初心者はここでひっかかるんだ」とか「なるほど、こういう理解の仕方なんだ」という楽しみができるかもしれません。
---------------------------
【注意事項】
●1行辺りの文字数を稼ぐために、入れ子を色で表現しています。それぞれの色の意味は以下の通りです。
第1階層(例:1)
第2階層(例:1-1)
第3階層(例:1-1-1)
第4階層(例:1-1-1-1)
●「<」「>」は、全て全角の「<」「>」にしてますが、実際には半角の「<」「>」が正しいです。
●まだ解決していない疑問点があり、それは「※」をつけて書いています。解決され次第書き直していくつもりです。
1
まずは「MySQL」というデータベースソフトをダウンロードしてスタートさせる。Ruby on Railsから登録するデータは、全てこのMySQL上のデータベースに格納することになる。
1-1
MySQLは公式サイトから、バージョンは5.0.45をダウンロード。
1-1-1
公式サイトのトップページで、メニューバーにある「Community」をクリックし、その後右サイドバーの「Downloads and Documentation」エリアの一番上の「MySQL 5.0 Database Server - Community Edition」をクリックして(バージョン番号の5.0は今後変わるかも)、OSを選んで、ミラーサイトを選択するとダウンロードがはじまる。
1-2
「MySQL GUI Tools(データベースの管理ツールMySQL Administrator.appとビューワーのMySQL Query Browser.appが入ってる)」というアプリも同時にインストール
1-2-1
公式サイトのトップページで「Community」をクリックし、その後右サイドバーの「MySQL GUI Tools」をクリックして、OSとミラーサイトを選択するとダウンロードがはじまる。
1-3
インストール方法は普通のアプリと一緒。ダブルクリックすると解凍されてフォルダが開くので、その中の拡張子.pkgのファイルをダブルクリックしてインストール。
1-4
MySQLは「システム環境設定」の中に入るので、そこから選んでスタートさせる。
2
「MySQL Administrator.app」を起動して、今回作るアプリの元となるデータベースを作成する。
2-1
「Accounts」で左下の「+」を押して、新しいアカウントを作る。決める項目は名前とパスワードだけでOK。
注意!:アカウント名とデータベース名は、あんま長いとこのAdminツール上で全部表示されないので注意。
2-2
「Catalogs」メニューを選んで、右下の「+」を押して、新しくデータベースを作る。今回作ったデータベースの名前は「lsndb070908」。
※データベースと「Shemata」は同じ意味? 違うもの? 確認中。
2-2-1
右のリストから作ったデータベースを選択して、中央下部の「Table Actions」を選んで、今作ったデータベースの中に新しいテーブルを作る(エクセルファイルの中に新しいシートを作るようなイメージ)。今回作ったテーブルの名前は「articles」。
注意!:テーブル名は必ず複数形じゃないといけない(そして、後述するモデル名が、その名刺の単数形になる)。
※テーブル名は必ず複数形じゃないといけない? 確認中。基本はそうなはず。3-5-2にも同様の疑問あり。
2-2-2
テーブル「articles」の中にカラム(項目)を作成する。表下部の「+」を押して、以下のものを作った(設定は「Column Details」というエリアの中で行った)。
・「Name」は「id」、「Data Type」は「INTEGER」、「NOT NULL」と「AUTO INC」と「Primary key」にチェック
・「Name」は「url」、「Data Type」は「TEXT」、「NOT NULL」にチェック
・「Name」は「good」、「Data Type」は「INTGER」、「NOT NULL」にチェック、「Dafault Value」に「0」
・「Name」は「bad」、「Data Type」は「INTGER」、「NOT NULL」にチェック、「Dafault Value」に「0」
※urlはデフォルトが空なのに「NOT NULL」にチェックを入れた。何故? 確認中。
2-2-2-1
原則的には、必ず最初のカラムは「id」で、数字が順番に入り、Primary keyになってないといけない
※本当にそうかどうか確認中。基本はそうなはず。
2-2-2-2
ちなみに、よく使うデータタイプは以下の通り
・INT
・FLOAT ー 少数も含む実数
・DATETIME
・VARCHAR ー 文字数制限があるテキスト
・TEXT
※INTとINTEGERの違いは確認中。
2-2-3
「Indices」を選んで、でインデックスを作成。作成したインデックスは以下の通り。
・「Name」は「good_index」。「Column」は「good」
・「Name」は「bad_index」。「Column」は「bad」
2-2-3-1
インデックスがあると検索が早くなる(けど、インデックスが多すぎるとデータベースが遅くなる)。
2-2-4
「Apply」を押して、出てくるダイアログで「Execute」を押す。これでSQLが発行され、めでたくテーブルが作成完了となる。
2-3
2-1で作ったアカウントに、データベース「lsndb070908」をいじれる権限を与える。
「Accounts」でlsnusr070908の中の「%」を選択。「Schema Privileges」タブの右側「Schemas」エリアで「lsndb070908」を選び、左側の「Available Privileges」内の項目全選択して、「<」を押して、全て「Assigned Privileges」に移す。これでデータベース「lsndb070908」を操作する権限が与えられた。「Save Changes」を押して保存。
なお、このときエラーが出たが、MySQL Administrator.appを立ち上げ直したらうまくいった。なんだったんだろう?
3
ここまででデータベースの操作は完了。いよいよRuby on Rails本番となる。Macでは「Locomotive」というのをインストールするとRuby on Railsに必要な環境が一通りインストールされるので、それを使う。
3-1
左下の「+」を押して、「Create New」するして、新しいプロジェクトを作る。今回の名前は「lsn070908」。
作ったら「Run」ボタンを押して、プロジェクトを起動する。
3-2
ファインダーから見て、「lsn070908」フォルダ内の「config」フォルダ内の「datbase.yml」の「development:」の項目を以下のように書き換え、作ったプロジェクトがデータベースにアクセスできるようにした
development:
adapter: mysql
database: lsndb070908
username: lesson070908
password: 070908
socket: /tmp/mysql.sock
注意!:「password:」の後に半角スペースを入れるのを忘れずに
3-3
「Applications」メニューから「Open Terminal」を選び、ターミナルを起動
3-4
以下の手続きを行って、「model」と「controller」と「view」を作る。
3-4-1
今回は、「model」「controller」「view」の3つを使う「MVCモデル」という方法でプログラミングを行う。映画に例えると、modelは役者で、2-2で作ったテーブルの各カラムが棚のように並んでいる。controllerはシナリオで、modelの棚に何を入れるかとか、viewに何を表示するかといったことを決める。viewは舞台のようなもので、実際のWebページを指す。
3-4-1-1
なお、今回はmodelが1つだが、複数のmodelが絡んでいた場合、それぞれのmodelがどのような関係にあるかを頭に描けると、プログラミングがしやすい。プログラミングする前に関係図を書いた方がいい。
3-4-1-2
なお、Webのコミュニケーション系アプリは、基本的にはデータベースに対して「CRUD(Create、Read、Update、Delete)」の処理をするという作業の繰り返しでできている。こう聞くととたんに簡単に聞こえるから不思議…
3-4-2
まずmodelを作る「./script/generate model article」と打って、modelを作成。これの意味は、「generateというコマンド 作るものの種類 それの名前」。以下のようなものができあがる。
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/article.rb
create test/unit/article_test.rb
create test/fixtures/articles.yml
create db/migrate
create db/migrate/001_create_articles.rb
※恐らく、「model名とtable名は基本的に一致していて、table名はmodel名の複数形である必要がある。今回はテーブル名が「articles」なので、モデル名が「article」」。で正しいと思うが、確認中。2-2-1にも同様の疑問あり。
※もし「table名はmodel名の複数形の必要がある」のであれば、例えばtable名で「010203」といったものは作れない(複数形にできないから)? 確認中
3-4-3
ここでちゃんとデータベースに書き込みができるか実験。
3-4-3-1
まず「./script/console」と打って、コンソールを起動する
3-4-3-2
「Article.create(:url => 'http://coolsummer.typepad.com/kotori/', :good =>1)」と入力。意味は「モデル名.やること(:項目名 => 'この中のものを追加')」。「MySQL Query Browser.app」を起動して、実際にデータが登録されているか確認。
注意!:モデル名は最初を大文字にしないとエラーになる。
※modelを指定するときは、最初の1文字目を大文字にするのは、決まりごと? 何か理由が?
※そもそも、「Article」はモデル名? それとも別のもの? 確認中(以下はArticleをモデル名だと前提にして書く)。
3-4-3-3
どのような項目が取得できるのか実験。「hensu = Article.find(1)」を実行。これは「Articleというモデルの中のidが1の行の内容をhensuに入れる」という意味。2-2-2と3-5-4-2を見ると判るが、テーブル「articles」の1行目には、以下のようなデータが入ってるはず。
・「id」に1
・「url」にhttp://coolsummer.typepad.com/kotori/
・「good」に1
・「bad」に0
Rubyは、複数のデータを変数に入れると、それを自動的に配列として処理してくれる(9-2を参照)。この命令の結果は以下の通り。
#<Article:0x332ea08 @attributes={"good"=>"1", "url"=>"http://coolsummer.typepad.com/kotori/", "bad"=>"0", "id"=>"1"}>
3-4-3-4
この後、コンソールでそれぞれのカラム名(アトリビュートと呼ぶ?)を以下のような形式で指定すると、以下のような返事が返ってくる。
>> hensu.good
=> 1
>> hensu.url
=> "http://coolsummer.typepad.com/kotori/"
>> hensu.bad
=> 0
>> hensu.id
=> 1
3-4-3-5
ここで「hensu.bad = 1」とし、その後「hensu.bad」とすると、「1」と返ってくる。しかしMySQL Query Browserで見ても、badの値は0のまま。これはbadに1を代入したということを保存していないから。「hensu.save」としてからもう一度MySQL Query Browserで見ると、ちゃんとbadが1になっている。
※「hensu.save」の「.」以下のことをアトリビュートと呼ぶ? 確認中。
※そもそも、たとえば、「Article.new」の場合、「Article」と「new」と「Article.new」はそれぞれ何と呼ぶか? 確認中(「クラス」「メソッド」「モデル」「テーブル」「アクション」「アーティクル」などなどが頭のなかでごっちゃになっている…)
※saveの他にはどんな種類のアトリビュートがあるのか、確認中(もしかして、http://railsapi.masuidrive.jp/class/ActiveRecord%3A%3ABase の中の「Public Class Methods」「Public Instance Methods」「Protected Class Methods」「Protected Instance Methods」が書ける命令一覧? よく使うのは?)。
3-4-3-6
id番号じゃなくて、urlのカラムを検索対象にして行を引っ張ってくることも可能。やり方は「Article.find_by_url('http://coolsummer.typepad.com/kotori/')」。
3-4-3-7
コンソールから抜けるときは「exit」と入力する。
3-4-4
次はcontrollerとviewを作る。
3-4-4-1
「scaffold」というのを使うと、プログラムを作るためのcontrollerとviewの「足場」を自動で組んでくれる。プログラムをゼロから書くんじゃなくて、最初にちゃんとしたテンプレートがある感じ。
3-4-4-2
やり方は「generate scaffold article vote」。意味は「generate ジェネレートするものの種類 model名 controller名」。今回のcontroller名は「vote」にしている。以下のようなものができあがる。
exists app/controllers/
exists app/helpers/
create app/views/vote
exists app/views/layouts/
exists test/functional/
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
identical app/models/article.rb
identical test/unit/article_test.rb
identical test/fixtures/articles.yml
create app/views/vote/_form.rhtml
create app/views/vote/list.rhtml
create app/views/vote/show.rhtml
create app/views/vote/new.rhtml
create app/views/vote/edit.rhtml
create app/controllers/vote_controller.rb
create test/functional/vote_controller_test.rb
create app/helpers/vote_helper.rb
create app/views/layouts/vote.rhtml
create public/stylesheets/scaffold.css
3-4-5
この時点で、一応ブラウザで表示可能。URLは「http://localhost:ポート番号/コントローラー名」。今回の場合は「http://localhost:3006/vote/」。
Ruby on Railsでのプログラミングは、基本的には、このデフォルトで生成されるページを書き直していくという作業になる。
なお、ここでブラウザから表示しようとしたら、500エラーが出た。いろいろチェックしたけど判らず、結局イチから全部作り直したらうまくいった。
4
controllerを書き換えて、自分が望む動作をさせる。
4-1
ファインダーから、今作ったプロジェクトの「app」フォルダの中の「controllers」フォルダを見ると、「vote_controller.rb」というのができてる。これがcontroller。
4-2
「defなんとか~end」までが1つのアクション。例えば、
def index
list
render :action => 'list'
end
ブラウザで「http://localhost:ポート番号/コントローラー名/アクション名」を開くと、そのアクションを実行する(例:http://localhost:3006/vote/list)。アクション名を指定しないと、vote_controller.rbの中の「index」のアクションを実行する。
4-2-1
def indexの中について説明。1行目の「list」は、アクション「def list」を呼び出している(本来「def list()と書くべきだが、空の括弧は省略可能だとのこと)。2行目の「render :action => 'list'」は、使用するテンプレート名を指定している。
4-2-2
indexから呼び出されているアクション「list」の詳細は、以下の通り。
def list
@article_pages, @articles = paginate :articles, :per_page => 10
end
「pagenate」という関数は、ページ分けをするときに用いる。「:articles」はモデルの複数形、「:per_page」は1ページあたりの件数を指定している。
「@article_pages」の中には、全体が何ページになり、今何ページ目かという情報が入っている。「@articles」は、実際のデータが入っている。今回の場合は、テーブル「articles」の中の10行分のデータが入っている。
※「:articles」はモデルの複数形ではなく、テーブル名を指している? それとも全然違う何かを指している? 確認中。もしテーブル名自体を指しているのであれば、さらなる疑問は、なぜ他の場所では「Article.うんたら」といった形で、モデルを通してデータベースのデータを引っ張ってきているのに、ここだけ直接データベースのテーブルをしているような書き方になっているのか。いずれにしろ確認中。(http://railsapi.masuidrive.jp/module/ActionController%3A%3APagination/paginate には「class_name」と書かれている。クラスって何を指す?)
※「@article_pages」の中に入っている情報が「全体が何ページになり、今何ページ目か」かは、ちょっと怪しい。確認中
※.「@article_pages」の中に入っている「今何ページ目か」という情報は、どこから取って来ている?(urlの「?page=2」と書かれた部分を、pagenateという命令が@article_pagesに代入している?)
※括弧を省略しないで書くと、「(@article_pages, @articles) = paginate(:articles, :per_page => 10)」? それとも「@article_pages, @articles = paginate(:articles, :per_page => 10)」? 確認中
※「@article_pages」の中のデータを見る方法、確認中
※「@articles」の中に入っている情報が「テーブル「articles」の中の10行分のデータ」かはちょっと怪しい。確認中。
※「@article」の中のデータを見る方法、確認中
4-2-2-1
なお、controllerからviewに渡す変数は「@」を付ける決まりになっている。
5
viewを書き換える。
5-1
viewはテンプレートのようなもの。プロジェクトフォルダの中の「app」→「views」フォルダに入っている。
5-1-1
viewは、基本はhtmlだが、拡張子が「.rhtml」といって、中にRubyのコードを直接書くことができる。「.rhtml」の前の名前は、controllerの中の「def うんたら」で書いたアクション名(「うんたら」の部分)と一緒。個々のアクション名とviewのhtmlファイルの名前は、1対1で対応している。だからcontrollerの中に「list」というアクションがあったら、何も指定しなければ、自動的にviewsのlist.rthmlを使う。
※「個々のアクション名とviewのhtmlファイルの名前は、1対1で対応している。だからcontrollerの中に「list」というアクションがあったら、何も指定しなければ、自動的にviewsのlist.rthmlを使う」はちょっと怪しい。確認中。
5-2
「list.rhtml」というのを開く。開いた直後のテンプレート内容の説明は以下の通り。
5-2-1
<% うんたら >で囲んだタグには、コードを書ける。<%= うんたら >で囲んだコードは実際にhtmlを生成する。<%=h うんたら >で囲むと、クロスサイトスクリプティング対策用に、記号をエンコードしてくれる(「<」を「&lt;」にしてくれる、など)。
※「<%h= うんたら >」の説明はちょっと怪しい、確認中。
5-2-2
<% for column in Article.content_columns %>
<th><%= column.human_name %></th>
<% end %>
ここで、各項目のタイトル一覧を作っている。意味は以下の通り(9-9でも実験しているので、参照のこと)。
5-2-2-1
「Article.content_columns」はモデル「article」を通してテーブル「articles」の中のカラム名のデータを1つ取ってきて、それを「column」という変数に代入している。
5-2-2-2
「column.human_name」はArticle.content_columnsで取ってくるデータのカラム名を、頭を大文字にして出力している。
5-2-2-3
全体は「for ~ end」で囲まれていて、「Article.content_columns」の中が空になるまで処理が繰り返される。「for column in Article.content_columns」の最初の1回転目は、「column」の中に「url」という言葉が入る。2回転目は「good」という言葉が、3回転目は「bad」という言葉が入る。4つ以上のカラムはないので、自動的に終了する。
※この説明だと、最初のカラムは「id」のはずなのに、実際「http://localhost:3006/vote/list/」で見ると、最初のカラムが「url」になっている。なんで? 確認中。
5-2-3
<% for article in @articles %>
@articlesの中には、この例の場合、テーブルarticlesの中の10行分のデータが入っている(4-2-2を参照のこと)。そのデータを1行ずつ「article」という変数の中に入れている。
5-2-4
<% for column in Article.content_columns %>
<td><%=h article.send(column.name) %></td>
<% end %>
※「article.send(column.name)」ってのが判らない。特に「send」が。確認中。僕の推測だと…「send(column.name)」は、この直前で変数「column」に代入している情報の「name」というアトリビュートを表している。たぶん、「Article.content_columns」という命令の中の「name」という項目は、実際のデータベース「articles」のカラムのタイトル名。つまりforの1回転目は「send(column.name)」の中身は「url(いや、1個目は「id」?)」で、2回転目は「good」、3回転目は「bad」? 確認中。一応consoleでは、以下のような実験結果が出た。
>>Article.content_column[0].name※「send」ってのがよく判らない。何用のコマンド? 例えばここで「article.(column.name)」とか「article.column.name」とか「article.[column.name]」と言う書き方はできない? 確認中。
=> "url"
5-3
「list.rhtml」を以下のように書き換えた。
元:
<tr>
<% for column in Article.content_columns %>
<th><%= column.human_name %></th>
<% end %>
</tr>
<tr>
<% for column in Article.content_columns %>
<td><%=h article.send(column.name) %></td>
<% end %>
<td><%= link_to 'Show', :action => 'show', :id => article %></td>
<td><%= link_to 'Edit', :action => 'edit', :id => article %></td>
<td><%= link_to 'Destroy', { :action => 'destroy', :id => article }, :confirm => 'Are you sure?', :method => :post %></td>
</tr>
<% end %>
今:
<% for article in @articles %>
<tr>
<td><%= article.url %></td>
<td><%= link_to 'いいね', :action => 'good', :id => article.id %>(<%= article.good %>)</td>
<td><%= link_to 'うーん', :action => 'bad', :id => article.id %>(<%= article.bad %>)</td>
</tr>
<% end %>
説明は以下の通り。
5-3-1
タイトル行と「Show」「Edit」「Destroy」は消した
5-3-2
good(いいね)とbad(うーん)をクリック可能にした。「link_to」と書くと「<a href >」になって、それがクリックされたときに実行するアクションを「:action」で指定できる。以下の「6」でvote_controller.rbを書き換えて、クリックすると数字が増えるようにする。「@articles」は4-2-2で指定した「@articles」と一緒(@をつけた名前はcontrollerとviewで渡しあえる)。
6
「vote_controller.rb」に以下のアクションを書き加えた
def good
@article = Article.find(params[:id])
@article.good += 1
@article.save
redirect_to(:action => 'index')
enddef bad
@article = Article.find(params[:id])
@article.bad += 1
@article.save
redirect_to(:action => 'index')
end
・「@article = Article.find(params[:id])」は、list.rhtmlに書き加えた「<%= link_to 'いいね', :action => 'good', :id => article.id %>」で「:id」にid番号が代入されているので、その番号をキーにmodelである「Article」から1行を引っ張って来て、それを「@article」に代入している。
・「@article.good += 1」「@article.save」は、goodの数字に1を足して、保存している。
・「redirect_to(:action => 'index')は、処理が終わった後、アクション名「index」にリダイレクトしている。
※事前に「:id」にはid番号が代入されているが、「Article.find(:id)」ではなく「Article.find(params[:id])」と書かなくちゃいけないのは何故? 変数名をそのまま指定できる場合と、「params[変数名]」と書かなくてはいけない場合の2種類ある?
※「:id」は、その前にlist.rhtmlで指定された番号が入っているのだと思うが、controllerとviewsの両方で受け渡しする変数は、頭に「@」がつくはずでは? これはviewsとcontrollerの両方で渡しあってる変数に見えるが、「@」がついていないのは何故? 他にもこういう特殊用例はある?
7
最後の作業。トップページに、新規URL登録メニューを作る
7-1
「new.rhtml」の内容を、「list.rthml」に転記する。転記したタグは以下の通り。
<% form_tag :action => 'create' do %>
<%= render :partial => 'form' %>
<%= submit_tag "Create" %>
<% end %>
※「do」ってなに? 確認中
※「:partial」ってなに? 「_form.rhtml」の内容を埋め込むってこと? 確認中。
※なんで「Create」だけ「'」じゃなくて「"」で囲まれている?
7-2
「_form.rhtml」を以下のように下記変える。
元:
<!--[form:article]-->
<p><label for="article_url">Url</label><br/>
<%= text_area 'article', 'url' %></p><p><label for="article_good">Good</label><br/>
<%= text_field 'article', 'good' %></p><p><label for="article_bad">Bad</label><br/>
<%= text_field 'article', 'bad' %></p>
<!--[eoform:article]-->
今:
<!--[form:article]-->
<label for="article_url">url</label> : <%= text_field 'article', 'url' %>
<!--[eoform:article]-->
7-2-1
「<%= text_field 'article', 'url' %>」は、実際には以下のようにブラウザに表示される。
<input id="article_url" name="article[url]" size="30" type="text" />
7-3
「vote_controller.rb」は今回は書き換えの必要なし。「create」の中身は以下の通り。
def create
@article = Article.new(params[:article])
if @article.save
flash[:notice] = 'Article was successfully created.'
redirect_to :action => 'list'
else
render :action => 'new'
end
end
※どういう理屈で「params[:article]」の中にurlが入ってるのかがよくわからない。たぶんformの中の「name="article[url]"」と関係あるんだと思うんだけど…確認中。また、formの中の「article_url」が、アクションcreateの中のどこにも出てこないけど、どこで使ってるの? 確認中(7-2-1を見る限り、formに入れた値は、「article」という変数の中ではなくて、「article_url」または「article[url]」という変数に代入されたように見える)。
※@article.saveにならない場合というのはありえるの? 確認中。
※flash[:notice]で何か表示した後で、listにリダイレクトしたら、flash[:notice]の内容は瞬時に消えるのでは? 確認中
7-4
できあがり! こんなのができた!
数字やURLはちゃんとデータベースから引っ張ってきていています! Webのプログラムって、基本的にはデータベースから値を取ってきたり、データベースに値を書き込む「CRUD(3-4-1-2参照)」だから、これも結構立派なWebのプログラムではないでしょうか!?
8
urlにパラメーターとしてサイトのURLを指定すると、「いいね」/「うーん」数が表示されるようなパーツも作る
8-1
「parts.rhtml」を作る。中身は以下の通り。
<%= link_to 'いいね', :action => 'good_parts', :id => @article.id %>(<%= @article.good %>)
|
<%= link_to 'うーん', :action => 'bad_parts', :id => @article.id %>(<%= @article.bad %>)
8-1-1
アクション名は「good_parts」「bad_parts」に変えている(リダイレクト先がindex.rhtmlではないので)
8-1-2
「article」は「@article」に変えている。理由は7-2参照のこと(いずれにしろvote_controller.rbで@articleを指定し直さなくてはならず、それをもう一度articleに入れ直す意味があまりないため)。
8-2
vote_controller.rbに以下を書き加える
def parts
@article = Article.find_or_create_by_url(params[:q])
enddef good_parts
@article = Article.find(params[:id])
@article.good += 1
@article.save
redirect_to(:action => 'parts', :q => @article.url)
enddef bad_parts
@article = Article.find(params[:id])
@article.bad += 1
@article.save
redirect_to(:action => 'parts', :q => @article.url)
end
8-2-1
「Article.find_or_create_by_url」は、modelであるArticleを通してデータベース「articles」の中を探し指定されたurlを含む行があればその行を取り出し、なければ新しく、そのurlをカラムurlに入れた行を作る、という意味。
8-2-2
「params[:q]」は、今回パラメーターとしてURLの中に直接「http://localhost:3001/vote/parts?q=http%3A%2F%2Fkkkkk」みたいに書いてもらうので、qという変数の中に入ってるはずのurlを代入している。
8-2-3
「good_parts」「bad_parts」は、リダイレクト先が「index.rhtml」じゃなくて「parts.rhtml」。アクション「parts」を呼び出し、引数として「q」にurlを渡している
※「redirect_to(:action => 'parts', :q => @article.url」と書くと、自動的にurlの中に「?q=http://うんたら」と入るというのは、redirect_toの機能? それとももっと別のところで取り決めている動きのルール? なんだか不思議。どうしてredirect_toの後でブラウザに表示されるurlに自動的に「?q=うんたら」と入るのか。確認中
8-3
こんなのができた!
これで、URLを<$MTEntryPermalink$>jで指定して、インラインフレームとかで表示するようにすれば、アッという間に自分のブログに「記事ごとの『いい』『悪い』投票機能」をつけることできます!
9
おまけ。変数とか配列とか。変数というのは値を入れる箱。配列というのは複数の値を順番に並べて入れておくことができる箱。
以下の画面例は全部、consoleを起動して、「>>」というところに1行ずつ命令を打ち込んで、結果を確認している。
9-1
「=」の左に変数名、右に「'」でくくって代入する値を入れると、その値が変数に入る。consoleでその変数名を入力すると、変数に入っている値を教えてくれる。「puts 変数名」と入力すると、変数に入っている値が表示される。
>> a = 'url'
=> "url"
>> a
=> "url"
>> puts a
url
9-2
「[」「]」で全体をくくり、1つ1つの要素を「'」でくくり、要素は「,」で区切ると、配列として変数に格納される。配列は0番から始まり、配列名の後で「[」「]」でくくって番号を入れると、その番号に格納されている値が表示される
>> a = ['url','good','bad']
=> ["url", "good", "bad"]
>> a[0]
=> "url"
>> a[1]
=> "good"
>> a[2]
=> "bad"
>> a[3]
9-3
forで、以下の例に書くと、配列aの中の値を1つずつcに代入していく。
配列の中の値が全部代入されたら、自動的にforが終わる
>> for c in a
>> puts c
>> end
url
good
bad
=> ["url", "good", "bad"]
9-4
配列の中の値が数値だった場合は、それは数字として取り扱われる(足し算引き算ができる)。
>> a = [1,2,3]
=> [1, 2, 3]
>> sum = 0
=> 0
> for i in a
>> sum = sum + i
>> end
=> [1, 2, 3]
>> sum
=> 6
>> puts sum
6
9-5
「Article.new」とやると、新しい行が作られる。あらかじめテーブル「articles」で指定された初期値が入っている。デフォルトの値は、今回の例の場合は以下の通り。
>> a = Article.new
=> #<Article:0x333e5c0 @new_record=true, @attributes={"good"=>0, "url"=>"", "bad"=>0}>
9-6
変数aの中にArticle.newで作った値を入れた場合、それを取り出すときは、「a.カラム名」と入力する
>> a.url
=> ""
>> a.good
=> 0
>> a.bad
=> 0
9-7
Article.newをやるとき、デフォルトで値を入れておきたいときは入れたい値を「({」「})」でくくって、「=>」を使って代入する
。複数の値を入れたいときは「,」で区切る
>> a = Article.new({'url' => 'http://def.com','good' => '1'})
=> #<Article:0x3328dec @new_record=true, @attributes={"good"=>"1", "url"=>"http://def.com", "bad"=>0}>
>> a.good
=> 1
>> a.url
=> "http://def.com"
9-8
「Article.find(1)」と書くと、テーブル「articles」の1行目のデータをごっそりと持ってくる。
>> Article.find(1)
=> #<Article:0x36a5498 @attributes={"good"=>"19", "url"=>"http://coolsummer.typepad.com/kotori/", "bad"=>"6", "id"=>"1"}>
「Article.find(1).url」と書くと、テーブル「articles」の中の1行目のカラム(アトリビュート)「url」の情報を持って来れる
>> Article.find(1).url
=> "http://coolsummer.typepad.com/kotori/"
9-9
「Article.content_columns」と書くと、この命令で取得できるデータの一覧が出てくる(たぶんカラム名とその設定一覧)。
>> Article.content_columns
=> [#<ActiveRecord::ConnectionAdapters::MysqlColumn:0x334a514 @type=:text, @limit=nil, @default="", @null=false, @name="url", @primary=false, @scale=nil, @original_default="", @sql_type="text", @precision=nil>, #<ActiveRecord::ConnectionAdapters::MysqlColumn:0x334a3d4 @type=:integer, @limit=11, @default=0, @null=false, @name="good", @primary=false, @scale=nil, @original_default="0", @sql_type="int(11)", @precision=nil>, #<ActiveRecord::ConnectionAdapters::MysqlColumn:0x334a26c @type=:integer, @limit=11, @default=0, @null=false, @name="bad", @primary=false, @scale=nil, @original_default="0", @sql_type="int(11)", @precision=nil>]
代入すると配列として代入されて、配列として指定すると取り出せる。
>> a = Article.content_columns
=> [#<ActiveRecord::ConnectionAdapters::MysqlColumn:0x334a514 @type=:text, @limit=nil, @default="", @null=false, @name="url", @primary=false, @scale=nil, @original_default="", @sql_type="text", @precision=nil>, #<ActiveRecord::ConnectionAdapters::MysqlColumn:0x334a3d4 @type=:integer, @limit=11, @default=0, @null=false, @name="good", @primary=false, @scale=nil, @original_default="0", @sql_type="int(11)", @precision=nil>, #<ActiveRecord::ConnectionAdapters::MysqlColumn:0x334a26c @type=:integer, @limit=11, @default=0, @null=false, @name="bad", @primary=false, @scale=nil, @original_default="0", @sql_type="int(11)", @precision=nil>]
>> a[0].human_name
=> "Url"
>> a[0].name
=> "url"
>> a[0].primary
=> false
>> a[0].sql_type
=> "text"
>> a[2].name
=> "bad"
9-10
以下のようにして、新しい行を作って、いろいろ変更して、データベースに保存することができる。
>> a = Article.new
=> #<Article:0x3652374 @new_record=true, @attributes={"good"=>0, "url"=>"", "bad"=>0}>
>> a.url = 'http://savetest'
=> "http://savetest"
>> a.save
=> true
10
その他疑問と、次の目標。
10-1
疑問1。書き方について。以下の4つはどれも正常に受け付けられるみたいですが、それぞれの書き方の違いは何?
例1:
Article.create(:url => 'http://test1')
Article.create('url' => 'http://test2')
Article.create({:url => 'http://test3'})
Article.create({'url' => 'http://test4'})
例2:
a = Article.new(:url => 'http://1')
a = Article.new('url' => 'http://2')
a = Article.new({:url => 'http://3'})
a = Article.new({'url' => 'http://4'})
10-2
次は以下にチャレンジ。
・ページネートするときに、先にページがないときはリンクがなくて「前のページ」「次のページ」というテキストだけになり、先にページがあるときはリンクになるように改造
・リストを逆順(つまり新規登録順)に改造
---------------------------
はぁはぁ……以上です。もしうっかり間違って全部読んじゃった方がいらっしゃったら……お疲れ様でした……
今は、ニヤニヤしながら、繰り返しコンソールから配列にデータを入れたり、データベースからデータを取り出したりしています。ハタから見たらかなり気持ち悪いですが、新鮮で楽しいので仕方ありません。
あと、本を読んだり、ちょっとずつ色々作ろうとしたりして、少しずつ前進しようとしています。いつか実を結んで、みなさまに公開できるものが作れるといいのですが……まだ道のりは遠そうです。
最後に、増井さん、本当にありがとうございました! 間違いなく今年ベスト10に入る経験でした。
まだまだ判らないことがたくさんあると思うのですが、今後ともどうぞよろしくお願いいたします。
Ruby on Rails入門―優しいRailsの育て方
西 和則 (著)