Ruby on Rails (3.x >= 3.2.9) のためのブログです (どっちかというと社内ブログ的な感じで、基礎から書いてきます) 。
Comments

articles ------ articles_tags ------ tags

というように、記事 (Article) と タグ (Tag) を中間テーブルを使って結び付ける場合, その中間テーブルの名前は, それぞれのテーブル名を辞書順にアンダースコアでくっつけたものを使用します。

なので、articles テーブルと tags テーブルの中間テーブルは、articles_tagsです。
(逆に tags_articles としちゃうとダメです!!!)

気をつけること!
[...]

Categories: , ,
Comments


Railsで、「ファイルをアップロードし、そのファイルのパスをデータベースに保存する」という処理を行いたい場合、CarrierWave を使えばできます。

① CarrierWave をインストール


まず、Gemfileに以下を追加
gem 'carrierwave' # これを追加
次に、以下のコマンドを実行

$ bundle install



② アップロード用のクラスを作成する

$rails generate uploader image

このコマンドを実行すると、app/uploaders/image_uploader.rb (ImageUploaderクラス) が作成されます


③ 画像アップロード用に、モデルを定義


images というテーブル (モデルはImage) に、imagename というカラム名で、ファイル名を管理するとします。


つまり、imagename は string型 としてmigrationしてあげればOKです。


このテーブルに対するモデルを以下のように定義します。
class Image < ActiveRecord::Base
  attr_accessible :imagename
  mount_uploade :imagename, ImageUploader
end
これで、アップロード成功時に同時にDBへの保存も行われるようになります。


④ 実際にアップロードしてみる


ほとんどの解説ブログなどでは、モデルと結びついたフォーム (form_for) を利用しているので、ここでは、そうでない場合のやり方で書いてます (form_tag利用)
コントローラーは以下
class UploadTestController < ApplicationController
  def upload
    unless params[:photoname].nil? then
      image = Image.new
      image.imagename = params[:imagename]
      image.save # ここでアップロード処理とDB保存処理が走る
    end
  end
end
ビューは以下 (upload.html.erb)
<%= form_tag  '/upload_test/upload/', :multipart => true do %>
<%= file_field_tag :photoname %>
<%= submit_tag 'upload' %>
<% end %>
これでOKです。
[...]

Categories: , , ,
Comments


ポリモーフィック関連について、今回は解説します。
まず、ポリモーフィック関連とは、以下の図に示すように、type によってリレーション先の親モデルが変わるようなものと子の関係です。



Rails では、***_id (int)と、***_type (string(255)) によって、これを実現しています。

このようなデザインのDB設計をしてる場合の、MigrationファイルやModelの作成方法は以下のようになります。

① ポリモーフィック関連のテーブルのMigrationファイル


class CreateItems < ActiveRecord::Migration
  def change
    create_table :items do |t|
      t.references :itemable, :polymorphic => true # この部分
    end
  end
end

② ポリモーフィック関連のModel


Itemモデル
# Item Model
class Item < ActiveRecord::Base
  belongs_to :itemable, :polymorphic => true # この部分
end
Restaurantモデル
# Restaurant Model
class Restaurant < ActiveRecord::Base
  has_many :items, :as => :itemable # この部分
end
Storeモデル
# Store Model
class Store < ActiveRecord::Base
  has_many :items, :as => :itemable # この部分
end

このように、itemable というポリモーフィック関連用のモデルのようなものを定義し、そいつに対して子と親からリレーションを張る感じです。
その際に、子には :polymorphic => true, 親には :as => :itemable を記述します。


③ ポリモーフィック関連のFixture


次に、テストデータの作成のために。Fixtureファイルの記述法を書きます。(以下)
item1:
  itemable: store1 (Store)
item2:
  itemable: restaurant1 (Restaurant)
※ store1 と restaurant1 のラベルが定義されている仮定です。


これでOKです。
[...]

Categories: , ,
Comments


Model, View, Controller のフレームワークに、サービス層を付け加えたほうがいいのでは〜?という記事をこの前書きました。

RAILSでのアプリケーション作成 - MVC

そこで、

  • app/services 以下に指定した名前のサービスクラスを作成
  • test/unit 以下に作成したサービスに対するTest用クラスを作成
以上の機能を持ったrakeコマンドを作成しました。
以下のスクリプトを, lib/tasks の中に作成してください (ファイル名は service.rake)



# encoding: utf-8
# サービスクラスを作成する
# app/services以下
#
# rake service:create SERVICE="hoge"
#   このコマンドで、app/services/hoge_service.rbが作成 class名はHogeService
namespace :service  do

  # service ファイルの作成
  task :create do
    service_file_name = ENV['SERVICE'].to_s

    if service_file_name != "" and File.extname(service_file_name).empty? then

      service_file_name = service_file_name + "_service"
      services_dir = File::expand_path('./app/services');

      # services ディレクトリがなかったら作成
      FileUtils.mkdir_p(services_dir) unless FileTest.exist?(services_dir)

      service_file_path = services_dir + "/" + service_file_name + ".rb"
      classname = convert_to_camelcase(service_file_name)

      if make_service_class(classname, service_file_path) then
        print "\e[34m" + "app/services/" + service_file_name + ".rb\e[0m" + "を作成しました。\n"

        # UnitTest用のクラス作成
        test_services_dir = File::expand_path('./test/unit');
        test_service_file_path = test_services_dir + "/" + service_file_name + "_test.rb"

        if make_service_test_class(classname, test_service_file_path) then
          print "\e[34m" + "test/unit/" + service_file_name + "_test.rb" + "\e[0m" + "を作成しました。\n"
        end

      else
        print "\e[41m" + "既に指定したサービスが存在しています。" + "\e[0m\n"
      end
    else
      print "\e[41m" + "正しい SERVICE パラメータを指定して下さい。" + "\e[0m\n"
    end
  end

  def convert_to_camelcase(str)
    return str.split('_').map{|s| s.capitalize}.join('')
  end

  def make_service_class(service_class_name, filepath)

    if !FileTest.exist?(filepath) then

      test_program = <<EOS
# encoding: utf-8

class %%CLASS_NAME%%
end
EOS
      test_program = test_program.sub("%%CLASS_NAME%%", service_class_name)
      f = open(filepath, "w")
      f.write(test_program)
      f.close()

      return true

    else
      return false
    end
  end

  def make_service_test_class(service_class_name, filepath)
    if !FileTest.exist?(filepath) then
      test_program = <<EOS
# encoding: utf-8

class %%CLASS_NAME%%Test < ActiveSupport::TestCase
end
EOS
      test_program = test_program.sub("%%CLASS_NAME%%", service_class_name)
      f = open(filepath, "w")
      f.write(test_program)
      f.close()

      return true

    else
      return false
    end
  end
end



このファイルを作成し、

$ rake service:create SERVICE="hoge_hoge"


とすれば、

  • app/services/hoge_hoge_service.rb が作成されます。 
  • 同時に、test/unit/hoge_hoge_service_test.rb が作成されます。 

ちょー適当に作りましたが、メモ残しておきます。 ノリで、rakeの独自タスクの作成テストとして作っただけなので、めちゃくちゃ汚いソースコードですが勘弁して下さい。。。すいません。。。


[...]

Categories: , , , ,
Comments


独自の rakeタスクを作成するには、以下の手順を。

① lib/tasks の中に、hoge.rake を作成


例えば、以下の様なファイルを作成します
# lib/tasks/hoge.rake
namespace :hoge  do
  task :piyo do
    # ENV['opt'] で、rake hoge:piyo opt="[文字]" で指定した[文字]の部分を取得できます
    # ここに実行したい処理を記述
  end
end


② rakeコマンド実行


あとはコマンドを実行するだけ
$ rake hoge:piyo opt="オプション"
[...]

Categories: , , ,
Comments


Rails3 において、Pagenation 処理を行う場合、Kaminari という gem を使うのが良さそうです。Kaminari については、[ コチラ : Kaminariの使い方 Rails3時代のpaginationの標準候補 ] の記事にとてもわかり易くまとめられていました。

① gem でインストール
$ gem install kaminari

② Gemfile に以下を記述。
gem 'kaminari'
③ コマンド実行
$ bundle install


使い方は先程の参考サイトを。
[...]

Categories: , , ,
Comments


Rails は MVCのフレームワークで、
  • Model : ビジネスロジックを書く場所
  • View : データを、ユーザーにどんな形で見せるかを記述
  • Controller : ユーザーの操作に対して、Modelをコントロールする
部分に分かれています。

ちなみに、そもそもMVCフレームワークの元の概念は....とかのMVC論争には、そこまで触れる気はありません。。

Railsでアプリケーションを作成する際には、この MVC に沿って開発をするのですが、その際に注意することや、自分が開発する上での方針を書いておく。(何がよくて何が悪いというより、ただのメモとして見てくれたら嬉しいです)

  • Controllerはできるだけ薄くする
  • ビジネスロジックなどはModelに記述する
  • ( Helperで、Modelをあつかっても良い。それで逆に複雑化するならやめておく )
  • 複数のModelにまたがるような処理だとかは、Service層を作るのもありかも (app/services や、app/models/services の作成) ちなみにGrailsでは、service層がフレームワークとして組み込まれてるらしいです。ただ、あくまでもビジネスロジックとなる部分はModelに記述すること。Serviceは「手続き」の部分を担当
  • 当たり前だけど、UnitTest はちゃんとしようww


こんなとこです。なんかもっとこうした方がいいよ!みたいな意見があれば欲しいです。





[...]

Categories: , , , , ,
Comments

Unit Testは、以下のコマンドで実行できます。
# test/units 以下ファイルすべてをテスト
$rake test:units
# 指定したファイルのみテスト
$rake test:units TEST=[テストするスクリプトへのパス]


① まず最初にテスト用のデータベースを作成


# 現在のschema.rbの情報をもとにテストDB作成
$ rake db:test:load
# テストDBの削除は以下のコマンド
$ rake db:test:purge
# (他にもいくつか作成用コマンドがあります)

ちなみに、texture で記述したテストデータが展開されます。


② テストの内容を作成


たとえば、userモデルのスクリプトのテストをしてみることにします。
そのために、まずテスト用のスクリプトを編集します。model を作成した際に、test/unit 以下に user_test.rb が作成されているはずです。デフォルトでは以下の様なコードになっています。
# test/unit/user_test.rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
end

これを、編集します (例えば以下)
# test/unit/user_test.rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  
  def setup
    # テストメソッドの実行前に行いたい内容を記述
    # texture の読込のような、リソースの初期化など
    # e.g.) @u = users(:user1)
  end

  def teardown
    # テストメソッドの実行後に行いたい内容を記述
    # リソースの破棄など
    # e.g.) @u = nil
  end

  test "user save" do
    user = User.new({
      :name => 'ユーザー1',
      :email => 'test@example.com'
    })
    assert user.save, "ユーザーの保存に失敗しました"
  end
end
※1 ここで、test の後の "user save" はテストの名前で、一意な名前で指定
※2 setup は、テストメソッドの実行前に呼び出される
※3 teardown は、テストメソッドの実行後に呼び出される

assert文は、処理結果が正しいかチェックするためのメソッドです。
様々な種類のチェック用メソッドがありますが、それは[ユニットテストで使うメソッド | Railsドキュメント]を参考にすると良いと思います。


③ テストを実行


この様に、テストスクリプトを書いたら、冒頭に記述したコマンドを打てばテストを実行できます。
$rake test:units TEST=test/unit/user_test.rb
[...]

Categories: , , , ,
Comments

データベースにテストデータを入れたい場合、fixture というものを使用します。

以下の様に model を作成した場合、test/fixtures 以下に、xxx.yml が作成されます。
$ rails generate model user
      invoke  active_record
      create    db/migrate/20121204111949_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/unit/user_test.rb
      create      test/fixtures/users.yml
このファイル (test/fixtures/users.yml) に、テストデータを記入します。

以下がその例

# test/fixtures/users.yml
<% 1.upto(3) do |n| %>
user<%= n %>:
  name: ユーザー<%= n %>
<% end %>

また、アソシエーションで外部キーを設定したい場合は、結び付けたいデータのラベル名を指定します。
# test/fixtures/articles.yml
<% 1.upto(3) do |n| %>
<% 1.upto(5) do |m| %>
article<%= n %>-<%= m %>:
  user: user<%= n %> #user1ラベルを指定
  name: 記事<%= m %>
<% end %>


また、この様に外部キーを設定する場合、model に リレーションの設定をしなければなりません (めっちゃハマった.........(´;ω;`)ブワッ)

この場合は、
# models/article.rb
class Article < ActiveRecord::Base
  belongs_to :user # この記述が必要!!
end
のように、belongs_toを指定します。

この様にして、fixtureファイルを作成したら、rake db:fixtures:load コマンドを実行
$ rake db:fixtures:load

これでテストデータが挿入されます。
[...]

Categories: , , , ,
Comments


Ruby on Rails 3 ポケットリファレンス [単行本(ソフトカバー)] 山田 祥寛 (著)


13年のロングセラーシリーズ・ポケットリファレンスにWebアプリケーションフレームワークの新定番であるRails3が新登場。逆引き形式で目的の情報をすぐ探せて、サンプルコードを見ながら具体的な実装のイメージがつかめます。Rails3.1以降に対応、標準ライブラリとなったjQueryやCoffeeScriptの話題もフォローしています。定番解説書である『Ruby on Rails 3 アプリケーションプログラミング』の著者・山田祥寛氏による、現場で役立つ信頼の1冊。

とりあえず、この書籍オススメです。

Rails でやりたいことを簡単に検索できるし、かつ解りやすいため、最初に何か本を買って Rails を勉強したい!!ってひとにはこの本で十分じゃないかと思います。
[...]

Categories: , ,
Comments


Railsでトランザクション処理を実装する場合、その機能をActiveRecordが用意してくれています。 transaction do ..... end を使用すればOKなのです。

def hoge
  User.transaction do
    user1 = User.new
    user1.save! # save!メソッドは保存できない場合、例外ActiveRecord::RecordInvalidが発生

    user2 = User.new
    user2.save!

  end
  #例外が発生しなかった場合の処理
  render :text = "成功"

  rescue => e
  #例外が発生した場合の処理
  render :text = "失敗"
end

自動でロールバックしてくれます。

[...]

Categories: , ,
Comments


Rails で CSRF(クロスサイトリクエストフォージェリ) の対策を行う場合、
コントローラーで protect_from_forgery を、テンプレートで csrf_meta_tag を使用します。
そして、formを作成する時に、ビューヘルパーを利用してフォームを作成すればOKです(form_for / form_tag など)

以下、参考サイトです。


[...]

Categories: , ,
Comments

Rails のサーバーは以下のコマンドで起動できます。
$ rails server [name] [options]

[name]
mongrelやthinなど (WEBrick以外を指定したい場合のみ指定)

[options]
-p [portnum], --port=[portnum] ポート番号 デフォルト : 3000
-b [ipAddr], --binding=[ipAddr] バインドするIPアドレス デフォルト : 0.0.0.0
-d, --deamon デーモンとして起動する デーモンとして起動したrailsサーバーを停止する場合は、ps -ef | grep script/rails でプロセスを確認し、kill -9 [processID] を行う
-e [env-name], --enviroment=[env-name] 環境の指定 (production, development, test のどれか) デフォルト : development

これでサーバーを起動できます。
[...]

Categories: , , ,
Comments

model のアクセサメソッドについてメモメモ。

class User < ActiveRecord::Base
  attr_accessor :name
end

このように、Model に アクセサメソッドを定義して、

user = User.new
user.name = "名前"
user.save

のようにして永続化をしようとしたが、name カラムの値がDBに保存されなかった。
ActiveRecord を継承してるので、そのActiveRecord 側がなんかしちゃうのかな。。。

ちょっと詳しく調べる時間ないので、後回しですが、attr_accessor の記述を消して普通に
user = User.new
user.name = "名前"
user.save
をやるだけでいけました。
[...]

Categories: , ,
Comments

Model では、attr_accessible と attr_protected というクラスメソッドがあります。 これらについて。
class User < ActiveRecord::Base
  attr_accessible :name, :email
  attr_protected :id
end

attr_accessible は、代入を許されている (publicのの様なもの) カラムで、attr_protected は、代入を許さない (newや@params['user']['name'], attribute=.. のようなもの) カラムを指定するもの。

attr_accessible に指定しないものは、 attr_protected になる様です。
ちなみに、メソッドを介してのカラムの値の変更は大丈夫です。
[...]

Categories: , , ,