Tagged with "rails - Coderkitty"

Picking up the breadcrumbs

Its always easy to skip going through the details of what your stacked up knowledge and just go for trying out new stuff. This time, I take it one step back and skim through pages of books I have always ignored and pick some sections that are worth looking into and post them here.

I’ve been trying to cut down on paper materials at home. Mostly, trying to read all those books and photocopied materials and just jot down notes from them. I decided to post them instead. So here goes..

From a photocopy of a ruby/rails book (no title gathered):

Some string extensions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string = "Now is the time"
puts string.at(2)  #=> "w"
puts string.from(8)  #=> "he time"
puts string.to(8)  #=> "Now is th"
puts string.first  #=> "N"
puts string.first(3)  #=> "Now"
puts string.last  #=> "e"
puts string.last(4)  #=> "time"
 
puts string.starts_with?("No")  #=> true
puts string.ends_with?("ME")  #=> false
 
count = Hash.new(0)
string.each_char { |ch | count[ch] += 1 }  
#=> {" " => 3, "w" => 1, "N" => 1, "o" => 1, "e" => 2, "h" => 1, "s" => 1, "t" => 2, "i" => 2 }

Customizing inflections:

1
2
3
4
5
Inflector.inflections do |inflect|
  inflect.plural(/-in-law$/, "s-in-law")
  inflect.singular(/s-in-law$/, "-in-law")
  inflect.uncountable("air", "information", "water")  #no singular form, no plural form
end

Extensions to numbers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
puts 3.ordinalize  #=> "3rd"
puts 321.ordinalize  #=> "321st"
 
puts 20.byes  #=> 20
puts 20.kilobytes  #=> 20480
puts 20.megabytes  #=> 20971520
puts 20.gigabytes  #=> 21474836480
puts 20.terabytes  #=> 21990232555520
puts 20.petabytes  #=> 22517998136852480
puts 1.exabyte  #=> 1152921504606846976
 
puts 20.seconds  #=> 20
puts 20.minutes  #=> 1200
puts 20.hours  #=> 72000
puts 20.days  #=> 1728000
puts 20.weeks  #=> 12096000
puts 20.fortnights  #=> 24192000
puts 20.months  #=> 51840000
puts 20.years  #=> 630720000
 
puts Time.now  #=> Tue May 18 22:03:21 +0800 2010
puts 20.minutes.ago  #=> Tue May 18 21:43:53 +0800 2010
puts 20.hours.from_now  #=> Wed May 19 18:04:11 +0800 2010
puts 20.weeks.from_now  #=> Tue Oct 05 22:04:22 +0800 2010
puts 20.months.ago  #=> Thu Sep 18 22:04:34 +0800 2008
puts 20.minutes.until("2010-12-26 12:00:00".to_time)  #=> Sun Dec 26 11:40:00 UTC 2010
puts 20.minutes.since("2006-11-08 01:15:00".to_time)  #=> Wed Nov 08 01:35:00 UTC 2006

Time and Date Extensions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
now = Time.now  
puts now  #=>  Tue May 18 22:09:17 0800 2010
puts now.to_date  #=> 2010-05-18
puts now.to_s  #=> Tue May 18 22:09:17 +0800 2010
 
puts now.to_s(:short)  #=> 18 May 22:09
puts now.to_s(:long)  #=> May 18, 2010 22:09
puts now.to_s(:db)  #=> 2010-05-18 22:09:17
puts now.to_s(:rfc822)  #=> Tue, 18 May 2010 22:09:17 +0900
puts now.ago(3600)  #=> Tue May 18 21:09:17 +0800 2010
puts now.at_beginning_of_day  #=> Tue May 18 00:00:00 +0800 2010
 
puts now.at_beginning_of_month  #=> Sat May 01 00:00:00 +0800 2010
puts now.at_beginning_of_week  #=> Mon May 17 00:00:00 +0800 2010
puts now.beginning_of_quarter  #=> Thu Apr 01 00:00:00 +0800 2010
puts now.at_beginning_of_year  #=> Fri Jan 01 00:00:00 +0800 2010
puts now.at_midnight  #=> Tue May 18 00:00:00 +0800 2010
 
puts now.change(:hour => 13)  #=> Tue May 18 13:00:00 +0800 2010
puts now.last_month  #=> Sun Apr 18 22:09:17 +0800 2010
puts now.last_year  #=> Mon May 18 22:09:17 +0800 2009
puts now.midnight  #=> Tue May 18 00:00:00 +0800 2010
puts now.monday  #=> Mon May 17 00:00:00 +0800 2010
 
puts now.months_ago(2)  #=> Thu Mar 18 22:09:17 +0800 2010
puts now.months_since(2)  #=> Sun Jul 18 22:09:17 +0800 2010
puts now.next_week  #=> Mon May 24 00:00:00 +0800 2010
puts now.next_year  #=> Wed May 18 22:09:17 +0800 2011
puts now.seconds_since_midnight  #=> 79757.033881
 
puts now.since(7200)  #=> Wed May 19 00:09:17 +0800 2010
puts now.tomorrow  #=> Wed May 19 22:09:17 +0800 2010
puts now.years_ago(2)  #=> Sun May 18 22:09:17 +0800 2008
puts now.years_since(2)  #=> Fri May 18 22:09:17 +0800 2012
puts now.yesterday  #=> Mon May 17 22:09:17 +0800 2010
 
puts now.advance(:days => 30)  #=> Thu Jun 17 22:09:17 +0800 2010
puts Time.days_in_month(2)  #=> 28
puts Time.days_in_month(2, 2000)  #=> 29

Extension to Ruby Symbols:

1
2
3
4
5
groups = posts.group_by { |post| post.author_id }
 
or
 
groups = post.group_by(&:author_id)

The power of toggle

I don’t know if we’ve been missing out on deliberate API lovin’ or that we’re becoming dumber every minute because of too much work, or its just the love month? :) …but we’ve been missing the power of “toggle“.

Toggle is defined as:

any instruction that works first one
way and then the other; it turns
something on the first time it is used
and then turns it off the next time

Needless to say, toggle is very efficient for use with boolean values (defined by either true or false). Let’s take a specific scenario:

1
2
3
4
5
6
7
8
9
10
11
12
  create_table "profiles", :force => true do |t|
    t.string   "salutation",            :limit => 8, :default => ""
    t.string   "name",                  :limit => 200, :default => ""
    t.datetime "created_at"
    t.datetime "updated_at"
  end
 
  create_table "themes", :force => true do |t|
    t.integer "profile_id",
    t.string   "name",            :limit => 100, :default => ""
    t.boolean "primary",        :default => false
  end

So, there are multiple themes per profile. You should get at least that part. Your boolean column should always be set to false by default. Either you replace the name of your column to suit that requirement, or write dirtier code. (I.e., instead of have a “public” column all set to true, use “private” column all set to false by default). Now, of course, your associations should be setup properly similar to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Profile < ActiveRecord::Base
 
  # filter_chain methods here...
  # validations here...
 
  # associations start...
  has_many :themes do 
    def primary
      find(:first, :conditions => ["primary = ?", true])
    end
  end
  # associations end...
 
end
 
class Theme < ActiveRecord::Base
 
  # filter_chain methods here...
  # validations here...
 
  # associations start...
  belongs_to :profile
  # associations end...
 
  named_scope :current_theme_of, lambda { |profile|
    { :conditions => [ "profile_id = ? and primary = ?", profile.id, true ] }
  }
end

Of course, there would be several records for the Theme table that will belong to a profile. You will offer a checkbox on every create and update of the themes of a particular profile, but how do you ensure that there will only be one primary theme set for each profile record?

This is dirty: (KIDS, DO NOT TRY THIS AT HOME)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Theme < ActiveRecord::Base
 
  # filter_chain methods here...
  after_save :verify_unique_primary_theme
  # validations here...
 
  # associations start...
  belongs_to :profile
  # associations end...
 
  named_scope :current_theme_of, lambda { |profile|
    { :conditions => [ "profile_id = ? and primary = ?", profile.id, true ] }
  }
 
  def verify_unique_primary_theme
    # get all themes by this profile 
    # set all their primary value to false
    # set this new one's primary value to true
  end
end

Man, I wouldn’t bet on a quick update on a profile whose set of themes would be more than hundreds, or even thousands! This, of course, is always worth a try:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Theme < ActiveRecord::Base
 
  # filter_chain methods here...
  # validations here...
 
  # associations start...
  belongs_to :profile
  # associations end...
 
  named_scope :current_theme_of, lambda { |profile|
    { :conditions => [ "profile_id = ? and primary = ?", profile.id, true ] }
  }
 
  def primary?
    primary
  end
 
  def validate
    #ensure that primary true is for one record only
    if primary? and !profile.themes.primary.nil?
      #toggle the old primary theme of this said profile
      profile.themes.primary.toggle!(:primary)
    end
  end
 
end

Simple, yet it works! Each time another theme gets toggled to be the primary theme, you only need to make two writes to your database:

  1. Save your new item
  2. Flip the old primary to false

PS: I did write messy code on purpose to illustrate what I meant for this post.

I hope this toggle magic works for you too the next time you think about boolean switches!

:)

Forcing urls to use https

I found this extremely useful:

1
2
3
def redirect_to_ssl
    redirect_to url_for params.merge({:protocol => 'https://'}) unless request.ssl?
end

Put this in your application controller and call

1
before_filter :redirect_to_ssl

on every controller that would require ssl. You can pick which actions would only require this by extending your before_filter declaration as such:

1
before_filter :redirect_to_ssl, :only => [:new, :purchase]

or even by exclusion, whichever is more convenient for your case:

1
before_filter :redirect_to_ssl, :except => [:show]

PS: There are other resources too, such as this one (that could be of help): http://fuadcse.blogspot.com/2009/01/redirecting-http-request-to-https-in.html

Migrate to your WordPress blog using Ruby and Atom

Recently, I have encountered a dilemma regarding my personal blog. I have it hosted by a local blog host provider. It was great staying there and blogging there when there was still this very tight-knit community of bloggers. From time to time, we’d all meet up and just express our thoughts in person. The providers were really great with hearing out their users’ thoughts, wishes including rants and raves. They were really good with what they were doing (up to some point in time).

Here are a couple of my raves about their service and the community:

  • Most of the members were real people. Not just an avenue for spammers and anonymous bloggers
  • People of that community were intelligent people. Its nice to have some good conversations with them.
  • There’s always a superhero in the forum who always tries to help (though he’s not connected with the providers themselves)
  • Even their higher management mingle with the people of their community
  • Customer support is quick to reply

And some of my rants too:

  • Sometimes, they forget about threads/issues
  • Their customer support is very poor in their knowledge of how the system works
  • Their customer support cannot provide tech support
  • No integration to offline blogging software (though WordPress supports this)
  • I found out, (while writing this script), that they had a lot of substandard tweaks (especially in the markup)

And yes, it might be too obvious for some, I have experienced all of my rants in the past few weeks that have passed. I was under the intent of making an offline backup of my blog (hosted with them). And with this, I got in touch with their customer support. I have been courteous enough, patient and understanding that my request might come in weird, or exceptional to that case. The I.ph platform is really made of WordPress engine, and that they tweaked this engine to conform to some of their requirements in providing their own bloghost. I knew for a fact that a backup could easily have been given only to special requests like mine. But this was not my case.

It seemed to me that they were reluctant, if not totally disagreeing with the fact that they’d release a backup of my blog.. for what reason, I don’t know why. Maybe because their tweaks have made WordPress engine‘s support for one click backup unusable?? I tried to reason out by giving them clarification of what I needed from them, but they made me wait in vain. Alas, my patience broke out when I received an email from their customer support saying..

Of course I know my own RSS feed, don’t I? But that was not what was asked for! At rage, I decided to take matters into my own fiery hands. I couldn’t say well enough that that email only made me furious.. well, it also made me excited and happy! Happy that I could once again write my own code to reach my own goal! Since I was bent on getting my own backup in an easy way, I looked up some reference online on whether somebody else could have done or wrote something similar to what I had in mind. I found this, but it seems to be dead as of the time of writing.

Now, being the Ruby lover that I am, I decided to write a Ruby script that would scrape my content and push it onto my new WordPress blog. There are a couple of assumptions/caveats for this script:

  • You can scrape off content from only YOUR OWN BLOG. Please don’t use this to steal other people’s content. I am not liable for any online content theft from the use of this script.
  • You must be able to understand the structure of the blog you are scraping. You should know where the “excerpt”, “main body”, “post date”, “post author”, etc. info are located from the markup/source of the blog you are scraping.
  • You must have the credentials of the blog where you want to push the scraped content.
  • If in case you need something else, or some more tweaking to this script, you must know Ruby, or maybe you can drop me a line and I’ll see how I can help you.
  • This script uses Atom tools, although there are other gems available, this is what I chose to use.
  • Turn on Atom publishing in your WordPress blog.

Ready?

  • First, of course, have a working Ruby setup.

  • Next, make sure you have the following gems: (1) Hpricot and (2) Atom

1
2
sudo gem install hpricot
sudo gem install atom-tools
  • Test the following requires in your irb. If it doesn’t raise an error, then you’re good to go. :)
1
2
3
4
5
6
require 'rubygems'
require 'open-uri'
require 'net/http'
require 'hpricot'
require 'atom/entry'
require 'atom/collection'
  • Secure all the variables you’ll need:
1
2
3
4
5
6
7
wp_blog_host = "livinglife.sweetperceptions.com"  #the wordpress blog you want to post to
wp_blog_uri = "http://#{wp_blog_host}"  #making it browseable
wp_base = "http://#{wp_blog_host}/wp-app.php"  #appending the base for publishing posts
wp_blog_username = "myusername"  #username to the wordpress blog you want to post to
wp_blog_password = "mypassword"  #password to the wordpress blog you want to post to
your_blog_source = "http://sweetperceptions.i.ph"  #the page you want to scrape content from
which_pages = 1..19  #put the pages here, if the content source will need this, see how I used it
  • You must have registered categories on your recipient wordpress blog to be able to automatically categorize your posts. If your blog source already have categories, then you can use these exact names for the migration. If you only have tags, I can still help you out (as this was my trouble too). You’ll need to declare your registered categories as such (these are my own categories):
1
registered_categories = ["About me", "Artistry", "Cool Finds", "Dreams", "Events", "Health and Beauty", "Horoscope", "Living Life", "Meme", "Movies", "Music", "Notes", "Pet Love", "Quotes", "Random thoughts", "Stories to share", "Techie", "Travel"]
  • If these new registered categories do not match those that you currently have in your blog source, then explicitly setting “synonym categories” would help. For those who did not categorize their posts, and solely rely on their tags, this could be your best avenue for categorizing your posts automatically. Try to categorize which tags (and old categories) would fall into your new categories.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
synonym_categories = {
  "About me" => ["me"], 
  "Artistry" => ["poem"],
  "Cool Finds" => ["cool"],
  "Dreams" => ["dream","dreams"],
  "Events" => ["event", "bday", "birthday", "Christmas", "New year", "new-year", "celebration"],
  "Health and Beauty" => ["health", "sickness", "headache", "fever", "cancer"],
  "Horoscope" => ["cookie", "fortune", "horoscope", "astrology", "psych"],
  "Living Life" => ["life", "kalokohan"],
  "Meme" => ["meme"],
  "Movies" => ["hollywood", "movie", "movies", "movie-lines", "happy-feet"],
  "Music" => ["song", "songs", "singer", "music", "ost"],
  "Notes" => ["notes"],
  "Pet Love" => ["pet", "cat", "dog", "animal", "animals", "pets"],
  "Quotes" => ["quote", "quotes"],
  "Random thoughts" => ["thought", "thoughts", "think", "logic"],
  "Stories to share" => ["story", "stories", "adventure"],
  "Techie" => ["tech", "techie", "work", "web2.0", "development", "software", "online", "skype", "pc"],
  "Travel" => ["philippines", "travel", "province"],
}
  • When everything here is set, you’ll need to understand that if you don’t have your own sitemap of posts’ links, you’ll need to extract them manually by visiting all pages of your blog_source to get their urls. This might take some time. Its better if you have a list of your own posts’ urls via a sitemap. If not, then you’ll have to set your blog source pages like this (mine had the format http://sweetperceptions.i.ph/page/N):
1
2
3
4
which_pages.each do |page|
  from = Hpricot(open(your_blog_source + "/page/#{page.to_s}/"))
  urls << (from/"h3[@class='entrytitle']/a").collect{|x| x['href']}
end
  • Or, use your links from a sitemap. I only used text format for my own use. To do this,
1
urls = File.readlines(urls_to_import).map { |line| line.chomp }

Now, let’s take a deep breath. The next steps would be more about Hpricot, and parsing your own document to extract the necessary information. I’ll walk you through how I got mine, but then you’ll have to construct your own for each section necessary. wink I’ll show you the snippet of the section involved, followed by the Hpricot code to get it. I used Firebug to help me with this, by the way.

This is an example of one post in my blog source:

  • Get the title of your post. Each blog entry in my blog source is enclosed in a div which contains an h3 tag with class ‘entrytitle’. The actual title is enclosed in a link. You always call the inner_html of the element node you’re looking at to get the text.

1
  title = (CGI::unescapeHTML((doc/"div/h3[@class='entrytitle']/a").inner_html.strip)).gsub(/\r\n/, '')
  • I explicitly set the author to my name. Next, we get the time stamp. Some would have the timestamp displayed, while the others won’t. If you want to capture the exact timestamp of the post, better to have this in the script. Mine was displayed at the bottom of the blog entry inside a div with class ‘meta-post’. Since this entire thing contains other information too, I used regular expression to match the format of my timestamp. NOTE: Others would have a different timestamp format. Please adjust as necessary.

  • The date is still out there. The date stamp of my post is found inside a div with a span whose class is called ‘date’.

1
  datestr = ((doc/"div/span[@class='date']").inner_html.strip).gsub(/\r\n/, '')
  • After we have the time and the date stamp, we concatenate them to form a string that would be accepted as a valid date via Atom publishing. NOTE: You may need to adjust your timezone calculations for this. Mine resolves to be -0500.
1
2
  datestr = datestr + " " + timestr
  datestr = DateTime.parse(datestr).strftime('%a, %-d %b %Y %T -0500')
  • Next comes the tricky part: Categorizing your post! If you were able to correctly assemble your registered categories and synonym categories, this would be a breeze. Well, at any rate, you can keep on emptying your blog for the posts and redoing them again as frequent as you have/want to. Find where your tags are enclosed. Mine is found inside a div with class ‘tag-list’. If your tags match the exact name of a category, then its automatically added to your filtered tags. Next, your synonym categories would be parsed to see if your tags can be used to classify to your new registered category.

1
2
3
4
5
6
7
8
9
10
11
12
  filtered_tags = []
  tags = (doc/"div[@class='tag-list']/a").collect{|x| x.inner_html}
 
  #rule 1 -> exact match
  filtered_tags << tags.collect{|x| x if registered_categories.include?(x)}.compact
 
  #rule 2 -> synonyms
  synonym_categories.keys.each do |syn|
    filtered_tags << tags.collect{|x| syn if (synonym_categories[syn]).include?(x)}.compact
  end
 
  tags = filtered_tags.flatten.compact.uniq.join(',')
  • Now to get your entire post. Yipee! You need to find a distinguishable id attached to every post. There really should be one unique id for each post. They usually look like ‘post-id’ or any flavors of that. Mine was enclosed in a div with id called ‘postentry-{id}’. You’ll need to get the this id!

  # Get your contents by finding all paras in the entry post
  entry_id = "postentry-#{doc.at("div[@class='blog']")['id'].split('-').last}"
 
  # Get the main body content
  contents = (doc/"##{entry_id}")
  • We are almost done. With this, there could be some parts of the body that you’d want to remove. You can do this by getting to the element via Hpricot and remove each of them.
  # Remove unneeded elements  
  (doc/"##{entry_id}/h3").remove  
  (doc/"##{entry_id}/span[@class='date']").remove  
  (doc/"##{entry_id}/div[@class='tag-list']").remove
  (doc/"##{entry_id}/div[@class='meta-post']").remove
  • Lastly, assemble your element and post away!
  # Atom Author element
  author = Atom::Author.new
  author.name = author
  author.uri = wp_blog_uri
 
  # Atom Entry element  
  entry = Atom::Entry.new
  entry.title = title
  entry.summary = hExcerpt
  entry.content = content
  entry.content.type = "html"
  entry.published = datestr
  entry.updated = datestr
  entry.tag_with(tags, ',')
  entry.authors << author
 
  req = Atom::HTTP.new
  req.user = wp_blog_username
  req.pass = wp_blog_password
  req.always_auth = :basic
 
  # Atom Collection
  c = Atom::Collection.new(wp_base + "/posts", req)
 
  res = c.post! entry

Did you enjoy this? I knew I did the first time! It feels great not having to manually copy and paste my content from the old blog source to the new one. Best of it all, I was able to keep my categories. I’m still looking at how to transfer all of my tags into the new one along with the comments, but I’m not yet successful with doing it as of the moment. If you’d like a full copy of this script, you can find it here.

Goodluck to your migration! I hope this helped.

Dec 9, 2008 - Coderkitty, Testing    No Comments

Testing is a Remarkable thing!

I never knew testing could really change the way I code. I know I’m a good programmer because I love implementing standards and doing the “right things” the “first time”.. and yet testing has made me realize something..

I never got to know how to properly test my own code. I’ve only used a lot of selenium, trying to dig into making it work for simple end user testing and usability of the application, but framework testing that involves the unit tests, integration tests and even performance tests are now putting themselves in the limelight for my programming career.

Looking at a huge challenge of writing tests for an old yet immature application (that I know of), I felt that this would be a very beautiful yet painstaking challenge. It can help me grow as a programmer and still save the project and the sanity of my colleagues. I have closely inspected the use of certain frameworks.

The following have been my choices: RSpec, Shoulda, Cucumber, Rails Unit Test. I have asked around and most of my network responded with their love of RSpec. I know that RSpec is already gained widespread use and popularity amongst Rails developers, but as I was also looking into using Shoulda, I found that there truly were things that I did not like from RSpec that I very much found in Shoulda. Some of these are:

  1. I am having difficulty of testing nested resources in RSpec.
  2. RSpec’s it-something-blank syntax doesn’t really amuse me (on a personal note, thought it fascinated me way back at the time I discovered it).
  3. I don’t like the way messages are displayed by RSpec when a test case fails.

Shoulda on the other hand looked cleaner for me. I also like the straightforwardness of the should-have-this methods for testing (especially in ActiveRecords).

Now, what I was missing in between was using the strength of RSpec in all other areas, and the readability and macros of Shoulda. Here is when I stumbled across Remarkable; and truly I must agree it is remarkable!

I have immediately jumped into writing a simple test for one of my models as follows:

class Feedback &gt; ActiveRecord::Base
  acts_as_audited
  validates_presence_of :name, :message =&gt; "..hey you can't be anonymous!", :on =&gt; :create
  validates_presence_of :email, :message =&gt; "..I need your contact details.", :on =&gt; :create
  validates_presence_of :topic, :message =&gt; "..what are you writing to me about?", :on =&gt; :create
  validates_presence_of :message, :message =&gt; "..so you were saying?", :on =&gt; :create
 
  validates_format_of :email, :with =&gt; /^([^@\s]{1}+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message =&gt; "..it should look something like yourname@something.com", :on =&gt; :create
 
  validates_length_of :message, :minimum =&gt; 5, :message =&gt; "..uhm, can you be more specific?"
  validates_length_of :name, :minimum =&gt; 3, :message =&gt; "..uhm, can you be more specific?"
  validates_length_of :topic, :minimum =&gt; 5, :message =&gt; "..uhm, can you be more specific?"
end

I know there are only few resources in the net that show you the exact counterpart of code being tested and test codes, but here’s mine. Maybe this can help.

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
 
describe Feedback, 'A Feedback instance' do
 
    VALID = {
      :name =&gt; "firstname lastname",
      :email =&gt; "email.email@email.com",
      :topic =&gt; "some topic",
      :message =&gt; "some message"
    }
 
  it 'should accept VALID attributes' do
    assert f = Feedback.new(VALID)
    assert f.valid?
  end
 
  should_require_attributes(:name, :message =&gt; "..hey you can't be anonymous!")
  should_require_attributes(:email, :message =&gt; "..I need your contact details.")
  should_require_attributes(:topic, :message =&gt; "..what are you writing to me about?")
  should_require_attributes(:message, :message =&gt; "..so you were saying?")
 
  should_ensure_length_at_least(:name, 3, :short_message =&gt; "..uhm, can you be more specific?")
  should_ensure_length_at_least(:message, 5, :short_message =&gt; "..uhm, can you be more specific?")
  should_ensure_length_at_least(:topic, 5, :short_message =&gt; "..uhm, can you be more specific?")
 
  should_allow_values_for(:name, "maricris", "ace", "greg", "john paul", "mary jesus joseph")
  should_not_allow_values_for(:name, "aa", "at", "jp", "1", "s", :message =&gt; "..uhm, can you be more specific?")
 
  should_allow_values_for(:email, "email.email@email.com", "email_email@yahoo.com", "123@yahoo.com")
  should_not_allow_values_for(:email, "123@.com", "testing!@!yahoocom", "3@11234.#8com", :message =&gt; "..it should look something like yourname@something.com")
end

There. I know its not the best and the prettiest, but its a work in progress. Writing these wee lines of code to test a small, almost bare model was truly a worthwhile experience. What I really liked about doing this was the fact that at some point, while I was working backward (not the usual TDD), when I arbitrarily took that my model was initially written meekly but completely, I was wrong.

In the process of enumerating the cases where the validation should fail, I really had to go back to my model and change something. Writing tests can make you more precise, and even make you feel that you can bring out your code to production and feel confident.

I can now see why Gregg said he learned to love testing too. Its not like I am forcing you to, but you should have a taste of this cake and be your own judge. Will you like it or not? ..its totally up to you. This will bring me not just sanity, but also confidence and a lot more freedom to rewrite and refactor in the future. Just great! smile

Trust nothing but tests

Where have all the tests gone? What tests???

I hate it that I have to manually exert so much effort into checking everything one by one to see that nothing is broken. In the end, what you have painstakingly put into order always gets blamed, and you don’t have any proof whatsoever that its not broken.

I “shoulda”, I coulda.. but can’t

I have often exercised optimism and tried to work on my own tests for my own modules. Yeah, but no yeah. I end up using up all of my time for coding because the time allotted for the whole thing isn’t even enough for just coding the feature!

Now, when time comes that you want to put some improvements, you become doubtful.. very unconfident. When a bug ticket comes up, you just don’t know where to look. It could’ve been elegant, hackish to just run rcov or your tests and find out where and what went wrong.. but no.

My energy gets wasted

What more can I say.. I just can’t seem to trust anything anymore without tests. And yes, my energy gets wasted.

  • Facebook
  • LinkedIn
  • FriendFeed
  • Twitter
  • Tumblr
  • Flickr
  • YouTube
  • Vimeo