Tagged with "ror - 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

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

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