んじゃあ、フルネームを作ろう。
いや、前の生地でやろうと思ったんだけど、testで詰まって、記事が長くなったんで、新たに立てた。
フルネーム = 姓 + ミドル・ネーム(存在するならば) + 名、な感じで。
で、ミドル・ネームは、「"」で括って、出力する方面で。
で、コード。
app/models/user.rb
+ #Generate Fullname == family_name + middle_name(unless empty) with double-quote + given_name + def full_name + name = family_name + " " + name += "\"#{middle_name}\" " unless middle_name.empty? + name += given_name + name + end
イヤ、つうか、ね?
絶対に、名前のフォーマットを決めるメソッドとか、どっかにあるよ?
i18n使ったようなの。
絶対あるね(キッパリ)。
無い筈ないね(シッカリ)。
んで、テスト書く。
test/unit/user_test.html
+ def test_should_create_users_fullname_with_middle_name + user = users(:quentin) + assert user.full_name == "Family \"Middle\" Given" + end + + def test_should_create_users_fullname_without_middle_name + user = users(:aaron) + assert user.full_name == "Hogehoge Fugafuga" + end
んで、test。
バッチリ、だね。おk。
とりあえず、メールアドレスで認証するようにする。
今どき、ハンドル・ネームでのログインってのも、ねえ?
なんてことを、bloggerにも書いてたな...
まあ、ヨシとするwww
で、Userモデルから、loginとnameを消して、nicknameを追加することにする。
で、マイグレーションファイルの作成。
まず、loginとnameの削除。
$ script/generate migration RemoveColumnLoginAndNameFromUser login:string name:string
で、編集。
*数字*_remove_column_login_and_name_from_user.rb
class RemoveColumnLoginAndNameFromUser < ActiveRecord::Migration def self.up remove_column :users, :login remove_column :users, :name end def self.down add_column :users, :name, :string, :limit => 40 add_column :users, :login, :string, :limit => 100, :default => '', :null => true end end
んで、nicknameの追加。
$ script/generate migration AddColumnNicknameToUser nickname:string
で、編集。
*数字*_add_column_nickname_to_user.rb
class AddColumnNicknameToUser < ActiveRecord::Migration def self.up add_column :users, :nickname, :string, :limit => 100, :default => '', :null => true end def self.down remove_column :users, :nickname end end
はい。これで、DBのスキーマ変更はイイでしょう。
後は、
- user.loginへの呼び出しを、user.emailへ変更する(一部user.nickname)
- なぜか('/')なんて形でルートへパスを指定しているところがあるんで、(root_path)-に書き換える
- ルートパスを:controller => "sessions", :action => "new"に設定する。
- public/index.htmlをindex.html.bakへリネームする。
って云う感じですか。
まあ、面倒だから、diff。
app/helpers/users_helper.rb
- options.reverse_merge! :content_method => :login, :title_method => :login, :class => :nickname + options.reverse_merge! :content_method => :email, :title_method => :email, :class => :nickname
app/models/user.rb
- validates_presence_of :login - validates_length_of :login, :within => 3..40 - validates_uniqueness_of :login - validates_format_of :login, :with => Authentication.login_regex, :message => Authentication.bad_login_message + validates_presence_of :nickname + validates_format_of :nickname, :with => Authentication.name_regex, :message => Authentication.bad_name_message, :allow_nil => true + validates_length_of :nickname, :maximum => 100 - validates_format_of :name, :with => Authentication.name_regex, :message => Authentication.bad_name_message, :allow_nil => true - validates_length_of :name, :maximum => 100 (略) - attr_accessible :login, :email, :name, :password, :password_confirmation + attr_accessible :email, :nickname, :password, :password_confirmation (略) def self.authenticate(login, password) return nil if login.blank? || password.blank? - u = find_in_state :first, :active, :conditions => {:login => login} # need to get the salt + u = find_in_state :first, :active, :conditions => {:email => login} # need to get the salt u && u.authenticated?(password) ? u : nil end (略) - def login=(value) - write_attribute :login, (value ? value.downcase : nil) - end -
app/controllers/users_controller.rb
if success && @user.errors.empty? - redirect_back_or_default('/') + redirect_back_or_default(root_path) flash[:notice] = "Thanks for signing up! We're sending you an email with your activation code." (略) - redirect_to '/login' + redirect_to(login_path) (略) - redirect_back_or_default('/') + redirect_back_or_default(root_path) (略) - redirect_back_or_default('/') + redirect_back_or_default(root_path)
app/controllers/sessions_controller.rb
- user = User.authenticate(params[:login], params[:password]) + user = User.authenticate(params[:email], params[:password]) (略) - redirect_back_or_default('/') + redirect_back_or_default(root_path) (略) - @login = params[:login] + @login = params[:email] (略) - redirect_back_or_default('/') + redirect_back_or_default(root_path)
app/views/users/new.html.erb
-<p><%= label_tag 'login' %><br/> -<%= f.text_field :login %></p> +<p><%= label_tag 'nickname' %><br/> +<%= f.text_field :nickname %></p>
app/views/users/_user_bar.html.erb
- <div id="user-bar-greeting">Logged in as <%= link_to_current_user :content_method => :login %></div> + <div id="user-bar-greeting">Logged in as <%= link_to_current_user :content_method => :nickname %></div>
app/views/sessions/new.html.erb
-<p><%= label_tag 'login' %><br /> -<%= text_field_tag 'login', @login %></p> +<p><%= label_tag 'email' %><br /> +<%= text_field_tag 'email', @login %></p>
app/views/user_mailer/signup_notification.erb
- Username: <%=h @user.login %> + Email: <%=h @user.email %>
app/views/user_mailer/activation.erb
-<%=h @user.login %>, your account has been activated. Welcome aboard! +<%=h @user.nickname %>, your account has been activated. Welcome aboard!
config/routes.rb
+ map.root :controller => 'sessions', :action => 'new'
んで、public/index.htmlのリネーム
$ svn move ./public/index.html ./public/index.html.bak
で、マイグレートして動作確認。
$ rake db:migrate
おk。
スバラシイ:-)
コミットする前にテストでもしましょうか。
で、
$ rake
してみたら、アンタ!
=> Errors running test:units and test:functionals!
!
!?
で、ターミナルをスクロール・バックしてみると...
=> /Users/hogehoge/Rails/climb-on/config/initializers/mail.rb:4: You have a nil object when you didn't expect it! (NoMethodError)
ん?
あれ?
んんんんんん...じゃあ、とりあえず、config.yml読み込むところコメントアウトして、これまでmail.rbに書いてたのを全部コメントアウトして、以下の内容をベタ書き追記。
ActionMailer::Base.delivery_method = :smtp ActionMailer::Base.smtp_settings = { :address => 'your.smtp.domain', :port => 587, :domain => 'localdomain', :authentication => :login, :user_name => 'username', :password => 'password' }
んで、もう一度、
$ rake => 19 tests, 0 assertions, 0 failures, 19 errors
ぅわおっ!!
全滅じゃんwww
まあ、あれだ、loginカラムとnameカラム削ってるから、その辺でテストが引っかかってるんだろうな、と。
んじゃあ、その辺なんとかしましょうか。
とりあえず、fixture変えないと、ね。
test/fixtures/users.yml
quentin: id: 1 nickname: quentin email: quentin@example.com salt: ee4305f8dfb7d8a338f88fa8953e1eae5b97647a # SHA1('0') crypted_password: d3cb87455619be9535daab550a3f9ab5a98b2d93 # 'monkey' created_at: <%= 5.days.ago.to_s :db %> remember_token_expires_at: <%= 1.days.from_now.to_s %> remember_token: 77de68daecd823babbb58edb1c8e14d7106e83bb activation_code: activated_at: <%= 5.days.ago.to_s :db %> state: active aaron: id: 2 nickname: aaron email: aaron@example.com salt: 4f0811fbeea7c6eb2b9b3608e63a74cf2050edd8 # SHA1('1') crypted_password: 023b11faf750a945ab19acca61fbee391692586d # 'monkey' created_at: <%= 1.days.ago.to_s :db %> remember_token_expires_at: remember_token: activation_code: 1b6453892473a467d07372d45eb05abc2031647a activated_at: state: pending old_password_holder: id: 3 nickname: old_password_holder email: salty_dog@example.com salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test created_at: <%= 1.days.ago.to_s :db %> activation_code: activated_at: <%= 5.days.ago.to_s :db %> state: active admin: id: 4 nickname: admin email: admin@example.com salt: 7e3041ebc2fc05a40c60028e2c4901a81035d3cd crypted_password: 00742970dc9e6319f8019fd54864d3ea740f04b1 # test # activation_code: adminscode # only if you're activating new signups created_at: <%= 1.days.ago.to_s :db %>
...やっぱやめたwww
なんか、テストの為のテストみたいな感じになってきたwww
ちょっと先に進みたい。
んで、強行進行!
出たとこ勝負っ!
とも思ったけど、これからは、新しいメソッド作るのがメインになってくるんで、とりあえず、現存のテストをコメント・アウトして、今後のメソッドは、テストすることにしよう。
おう、しよう。
コードをキレイにしましょうか。
で、これからは、自分のコードを足すのがメインになってくる筈なんで、コードのスタイルを統一していきましょうかね。
で、基本的に、Rubyコーディング規約に準ずることにしたよ。
他人が敷いたレールに則るのって、ラクチンだよねw
Railsの基本だね;-p
だがしかし、外すところでは、外すよ。
例えば、ファイル名は、「-」じゃなくって、「_」繋ぎにするとか。
まあ、これも、railsがそうなってるから、なんだけどね;-p
メソッドの引数に関しても、宣言くさいもの、
include、require
belongs_to、has_many、has_one、has_and_belongs_to_many
validate_hogehoge
attr_hogehoge
なんかは、敢えて()を付けないことにした。
特に、validate_hogehogeは、付いてない方が、可読性が良い気がする。
良い気がすれば、おk。wwww
っていうか、この一節、bloggerからの丸コピーwww
名を名乗れ。
とりあえず、ユーザmodelの中に、本名の項目を作ろう。
family_nameとmiddle_nameとgiven_nameね。
外人も使うかも知れないからねwww
$ script/generate migration AddColumnFamilyNameAndMiddleNameAndGivenNameToUser family_name:string middle_name:string given_name:string
んで、編集。
db/migrate/*数字*_add_column_family_name_and_middle_name_and_given_name_to_user.rb
class AddColumnFamilyNameAndMiddleNameAndGivenNameToUser < ActiveRecord::Migration def self.up add_column :users, :family_name, :string, :limit => 100, :default => '' add_column :users, :middle_name, :string, :limit => 100, :default => '', :null => true add_column :users, :given_name, :string, :limit => 100, :default => '' end def self.down remove_column :users, :given_name remove_column :users, :middle_name remove_column :users, :family_name end end
middle_nameは、無い場合があるけど、まあ、普通の文化圏の方なら、名字と名前は無い事は無いでしょう。
ホントに?www
んで、validationを書こう。
app/models/user.rb
validates_format_of :email, :with => Authentication.email_regex, :message => Authentication.bad_email_message + validates_presence_of :family_name + validates_format_of :family_name, :with => Authentication.name_regex, :message => Authentication.bad_name_message + validates_length_of :family_name, :maximum => 100 + + validates_format_of :middle_name, :with => Authentication.name_regex, :message => Authentication.bad_name_message, :allow_nil => true + validates_length_of :middle_name, :maximum => 100 + + validates_presence_of :given_name + validates_format_of :given_name, :with => Authentication.name_regex, :message => Authentication.bad_name_message + validates_length_of :given_name, :maximum => 100 + # HACK HACK HACK -- how to do attr_accessible from here?
んじゃあ、ちょっとfixture書き換える。
test/fixtures/users.yml
nickname: quentin + family_name: Family + middle_name: Middle + given_name: Given email: quentin@example.com nickname: aaron + family_name: Hogehoge + middle_name: + given_name: Fugafuga email: aaron@example.com nickname: old_password_holder + family_name: 名字 + middle_name: ミドル・ネーム + given_name: 名前 email: salty_dog@example.com nickname: admin + family_name: アドミン + middle_name: ザ + given_name: 管理者 email: admin@example.com
んで、テストコードを足す。
test/unit/user_test.rb
fixtures :users + def test_invalid_with_empty_attributes + user = User.create + assert !user.valid? + assert user.errors.invalid?(:nickname) + assert user.errors.invalid?(:email) + assert user.errors.invalid?(:family_name) + assert user.errors.invalid?(:given_name) + end
んで、テスト。
$ ruby test/unit/user_test.rb => Loaded suite test/unit/user_test => Started => . => Finished in 0.108762 seconds. => => 1 tests, 5 assertions, 0 failures, 0 errors
ん、スバラシイぃ:-)
まあ、何がわかったって、新規User作成時に、nicknameとemailとfamily_nameとgiven_nameのいずれかで、validation_hogehogeがエラーを返してないかどうかを見ているだけなんだけどwww
まあ、でも、今やったのって、validation書いただけだもんねぇ...
んじゃあ、もう少し、コードを足すか。
test/unit/user_test.rb
+ def test_invalid_with_empty_attributes + user = User.create + assert !user.valid? + assert user.errors.invalid?(:nickname) + assert user.errors.invalid?(:email) + assert user.errors.invalid?(:family_name) + assert user.errors.invalid?(:given_name) + end + + def test_should_create_user + assert_difference 'User.count' do + user = create_user + assert !user.new_record?, "#{user.errors.full_messages.to_sentence}" + end + end + + protected + def create_user(options = {}) + record = User.new({ + :nickname => "Nick", + :email => 'quire@example.com', + :password => 'quire69', + :password_confirmation => 'quire69', + :family_name => "Family", + :middle => "Middle", + :given_name => "Given" + }.merge(options)) + record.register! if record.valid? + record + end +
んで、テスト。
$ ruby ./test/unit/user_test.rb Loaded suite test/unit/user_test Started .F Finished in 0.081857 seconds. 1) Failure: => test_should_create_user(UserTest) => [test/unit/user_test.rb:21:in `test_should_create_user' => /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/core_ext/test/unit/assertions.rb:46:in `assert_difference' => test/unit/user_test.rb:19:in `test_should_create_user' => /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `__send__' => /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/testing/setup_and_teardown.rb:60:in `run']: => Nickname can't be blank and Family name can't be blank and Given name can't be blank. => <false> is not true. => => 2 tests, 6 assertions, 1 failures, 0 errors
ん?
んん?
ん〜...
で、はまったはまったwww
結局、どうも、Userモデルで、attr_accessibleにfamily_nameとmiddle_nameとGiven_nameを指定していないから、ということに到達しましたwww
いやあ、これが、結局ここで何時間も止まることになる訳ですがwww
んで、修正。
app/models/user.rb
- attr_accessible :login, :email, :name, :password, :password_confirmation + attr_accessible :email, :password, :password_confirmation, :nickname, :family_name, :middle_name, :given_name
で、これでどうよ?
$ ruby ./test/unit/user_test.rb => Loaded suite test/unit/user_test => Started => .E => Finished in 0.142293 seconds. => => 1) Error: => test_should_create_user(UserTest): => SocketError: getaddrinfo: nodename nor servname provided, or not known (略) => 2 tests, 5 assertions, 0 failures, 1 errors
はいいいいっ!?
で、はまるはまるwwww
結局、どうも、config/initializers/mail.rbで、設定を端折っていたからだと思われ。
で、ちゃんと設定して、
$ ruby test/unit/user_test.rb Loaded suite test/unit/user_test Started .E =>Finished in 2.002131 seconds. => => 1) Error: =>test_should_create_user(UserTest): =>Net::SMTPFatalError: 550 5.1.2 Bad destination system: quire@example.com (略) => 2 tests, 5 assertions, 0 failures, 1 errors
はいいいっ!?
じゃあ、test/unit/user_test.rbのcreate_userメソッドの中の:emailを、まともなメールアドレスに書き換える。
=> Loaded suite test/unit/user_test => Started => .E => Finished in 3.069374 seconds. => => 1) Error: => test_should_create_user(UserTest): => Net::SMTPFatalError: 550 5.7.0 From address is not one of your addresses. (略) => 2 tests, 5 assertions, 0 failures, 1 errors
もう、なんか、ねwww
どうでもイイ気がしてきたwww