Ensure Capitalization for Double-Barrel Names - ruby-on-rails

In a Rails model, I am trying to override the setter to ensure that the first_name and last_name fields are always properly capitalized -- the use-case is for a CRM, so you'd expect to have humanized names.
Is this the best practise to ensure capitalization on the first and last names? Currently, it is failing the tests.
# app/models/concerns/personable.rb
def first_name=(value)
write_attribute(:first_name, value.humanize.gsub(/\b('?[a-z])/) { $1.capitalize })
end
def last_name=(value)
write_attribute(:last_name, value.humanize.gsub(/\b('?[a-z])/) { $1.capitalize })
end
# test/models/customer_test.rb
test "should capitalize first and last names" do
customer = create_customer(first_name: "MARRIE-DAY", last_name: "kennedy")
assert_equal customer.name, "Marrie-Day Kennedy"
end
test "#name returns a affixed surname" do
customer = create_customer(first_name: "Test", last_name: "McKay")
assert_equal "Test McKay", customer.name
end
Failure:
CustomerTest#test_#name_returns_a_affixed_surname:
Expected: "Test McKay"
Actual: "Test Mckay"

humanize will turn McKay into Mckay so the last replacing logic will not applied in this case.
you could try add a mark for those capital characters before humanize, such as McKay -> Mc|K|ay, so that we still know where need to turn capital, as below code:
class Helper
def self.format_name(name)
name.gsub(/([^A-Z])\B([A-Z])/) {"#{$1}{#{$2&.capitalize}}"}
.humanize
.gsub(/\{([a-z])\}?/){$1.capitalize}
.gsub(/\b([a-z])/) {$1.capitalize}
end
end
Helper.format_name("Kennedy") # Kennedy
Helper.format_name("McKay") # McKay
Helper.format_name("MARRIE-DAY") # Marrie-Day

Related

How to delete an entire array in Ruby and test with RSpec

I'm fairly new to Ruby and am currently taking a full stack course. For one of my projects we are building an addressbook. I have set up how to add an entry to the addressbook, however, I can't seem to figure out how to delete an entry (I make an attempt with the remove_entry method in the AddressBook class below but am not having any luck). We are also supposed to test first with RSpec, have the test fail and then write some code to get it to pass. If I didn't include all the info needed for this question let me know (rookie here). Anyway, here is what I have so far:
RSpec
context ".remove_entry" do
it "removes only one entry from the address book" do
book = AddressBook.new
entry = book.add_entry('Ada Lovelace', '010.012.1815', 'augusta.king#lovelace.com')
book.remove_entry(entry)
expect(entry).to eq nil
end
end
AddressBook class
require_relative "entry.rb"
class AddressBook
attr_accessor :entries
def initialize
#entries = []
end
def add_entry(name, phone, email)
index = 0
#entries.each do |entry|
if name < entry.name
break
end
index += 1
end
#entries.insert(index, Entry.new(name, phone, email))
end
def remove_entry(entry)
#entries.delete(entry)
end
end
Entry class
class Entry
attr_accessor :name, :phone_number, :email
def initialize(name, phone_number, email)
#name = name
#phone_number = phone_number
#email = email
end
def to_s
"Name: #{#name}\nPhone Number: #{#phone_number}\nEmail: #{#email}"
end
end
When testing my code with RSpec I receive the following error message:
.....F
Failures:
1) AddressBook.remove_entry removes only one entry from the address book
Failure/Error: expect(entry).to eq nil
expected: nil
got: [#<Entry:0x00000101bc82f0 #name="Ada Lovelace", #phone_number="010.012.1815", #email="augusta.king#lovelace.com">]
(compared using ==)
# ./spec/address_book_spec.rb:49:in `block (3 levels) in <top (required)>'
Finished in 0.02075 seconds (files took 0.14221 seconds to load)
6 examples, 1 failure
Failed examples:
rspec ./spec/address_book_spec.rb:44 # AddressBook.remove_entry removes only one entry from the address book
Just test that the book.entries association is empty:
expect(book.entries).to be_empty
As book is a local variable in your test, you will not get a false negative result if you keep your test atomic. Some best practices on rspec.
Edit:
You can also check the entry was not in the set:
expect(book.entries.index(entry)).to be_nil
or test the change of the array length with:
expect { book.remove_entry(entry) }.to change{book.entries.count}.by(-1)
If you wonder for the be_xxx syntax sugar, if the object respond to xxx?, then you can use be_xxx in your tests (predicate matchers)
I think your expect has an issue. The entry variable is not set to nil, but the entry inside book would be nil.
I think something like this would work better:
expect(book.entries.find { |e| e.name == "Ada Lovelace" }).to eq nil
Better still, your AddressBook could have its own find method, which would make the expect param much nicer, like book.find(:name => "Ada Lovelace").
Finally, I would also put an expect call before the remove_entry call, to make sure its result equals entry.

Ruby: Default values for define?

I have a question about define my main issue is I am a bit confused on how the parameters work for it.
This is my Methods
def repeat(repeated_word)
#repeated_word = repeated_word
"##repeated_word ##repeated_word"
end
This is my rspec test to make sure my method works.
describe "repeat" do
it "should repeat" do
repeat("hello").should == "hello hello"
end
# Wait a second! How can you make the "repeat" method
# take one *or* two arguments?
#
# Hint: *default values*
it "should repeat a number of times" do
repeat("hello", 3).should == "hello hello hello"
end
end
it passes the first test but fails the second. My confusion is if i add a second parameter meaning def repeat(repeat_word, times_repeated)
the first test then fails because it has the wrong number of arguments. Not sure how to set up default values?
def repeat(repeated_word, repeats=2)
repeats.times.map { repeated_word }.join(' ')
end

Rails rspec after_save reference self

I want to add a profanity check on my website.
I'm taking a TD approach and I'm trying the following:
check if profanity exists in specific profile fields
create a flag
create a flag if one does not exist
create a flag if one exists, but has been dismissed
Here is my spec so far:
describe Painter do
before do
#painter = FactoryGirl.create(:painter_flag)
end
context "blacklist flag" do
it "check if profanity exists" do
#painter.experience = "test"
#painter.save
expect {#painter.blacklist_flags?}.to be_true
end
it "create flag if profanity exists" do
#painter.experience = "test"
#painter.save
BlacklistFlag.count.should be > 0
end
end
end
Painter related code:
after_save :create_flag, if: :blacklist_flags?
def blacklist_flags?
list = ""
list << skills
#list << experience
#list << first_name
#list << last_name
#list.downcase.scan(/(badword|badword)/).size > 0
end
def create_flag
end
If I comment out the following code above the two test pass:
list << skills
When I leave the code in I receive the following error:
2) Painter blacklist flag create flag if profanity exists
Failure/Error: #painter = FactoryGirl.create(:painter_flag)
TypeError:
can't convert nil into String
It seems there's a problem with referencing self because skills, experience, etc are part of the model. I'm not sure how to fix this. Please advise.
Update:
FactoryGirl.define do
factory :painter do
first_name "Brian"
last_name "Rosedale"
state "OH"
zip_code "43081"
sequence(:email) {|n| "nobody#{n}#painterprofessions.com" }
phone "12345566"
pdca_member false
password "123456"
factory :painter_flag do
skills = "badword"
end
end
end
Just use this line in your factory for :painter_flag, without the = sign.
skills "badword"
I think what's causing the error is because the callback is been executed on the line #painter = FactoryGirl.create(:painter_flag).
You might want to use FactoryGirl.build method if you want to test the callback.

Passing a simple test

I'm using Rails 3.2's rake tests function. I'm trying to pass a test but it's giving me errors. Btw, when see you how I write, I'm a noob. It's a hacked way of testing, but at least I want to try passing it first.
test "product title must have at least 10 characters" do
ok = %w{ aaaaaaaaaa aaaaaaaaaaa }
bad = %w{ a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa}
ok.each do |name|
assert new_product_title(name).valid?, "#{name} shouldn't be invalid"
end
bad.each do |name|
assert new_product_title(name).invalid?, "#{name} shouldn't be valid"
end
end
with the function
def new_product_title(title)
Product.new(title: title,
description: "yyy",
price: 1,
image_url: "fred.gif")
end
somehow it's not passing.
What's the reason here? And is there a better way to write it?
I'm more concerned about the method. I'm assuming this method is in a product model? It seems what you are trying to do should definitely be controlled by the model, but I don't think you can call a class's method inside the class's definition. I also don't see much utility in a method that creates a new product with specified title, but static description, price, and image_url. If you need default values for specific attributes, you can set those in an initialize method and overwrite them later if needed. Some people frown on setting defaults in initialize so instead you can set them in an after_initialize callback like this:
class Product < ActiveRecord::Base
after_initialize :init
def init
self.description ||= 'yyy'
self.price ||= 1
self.image_url ||= "fred.gif"
end
end
Then whenever you needed to create a new product with a title and the default attributes you can just use
Product.new(:title => "some title")
And if you don't want all the defaults you can just pass the values into new like usual
Product.new(:title => "some other title", :price => 400) # desc & url are still default
About your tests. I always test in RSpec. Since you are using Test Unit (or Mini Test or whatever it is now), my advice my not be correct. But first I would make the variable names more descriptive. Secondly, there are some commas at the end of your assertions that shouldn't be there.
test "product title must have at least 10 characters" do
valid_name = "a" * 10
short_name = "a" * 9
valid_product = Product.new(:name => valid_name)
assert valid_product.valid?
invalid_product = Product.new(:name => short_name)
assert invalid_product.invalid?
end
If you get that working you may want to verify that the product is invalid for the correct reason using an assert equals method on invalid_product.errors.full_messages and the expected string from the error.

Moching rails association methods

Here is my helper method which I want to test.
def posts_correlation(name)
if name.present?
author = User.find_by_name(name)
author.posts.count * 100 / Post.count if author
end
end
A factory for user.
factory :user do
email 'user#example.com'
password 'secret'
password_confirmation { password }
name 'Brian'
end
And finally a test which permanently fails.
test "should calculate posts count correlation" do
#author = FactoryGirl.create(:user, name: 'Jason')
#author.posts.expects(:count).returns(40)
Post.expects(:count).returns(100)
assert_equal 40, posts_correlation('Jason')
end
Like this.
UsersHelperTest:
FAIL should calculate posts count correlation (0.42s)
<40> expected but was <0>.
test/unit/helpers/users_helper_test.rb:11:in `block in <class:UsersHelperTest>'
And the whole problem is that mocha doesn't really mock the count value of author's posts, and it returns 0 instead of 40.
Are there any better ways of doing this: #author.posts.expects(:count).returns(40) ?
When your helper method runs, it's retrieving its own object reference to your author, not the #author defined in the test. If you were to puts #author.object_id and puts author.object_id in the helper method, you would see this problem.
A better way is to pass the setup data for the author in to your mocked record as opposed to setting up expectations on the test object.
It's been a while since I used FactoryGirl, but I think something like this should work:
#author = FactoryGirl.create(:user, name: 'Jason')
(1..40).each { |i| FactoryGirl.create(:post, user_id: #author.id ) }
Not terribly efficient, but should at least get the desired result in that the data will actually be attached to the record.

Resources