News: Order Avandia Purchase Lasix Order Leukeran Menosan Order Brite Buy Ansaid Order Sarafem Purchase Adalat Cheap Celexa Purchase Bontril Purchase Phentermine Purchase Ophthacare Cheap Methocarbam Buy Geriforte Cheap Cephalexin Buy Ashwagandha Order Retin-A Lexapro Buy Lukol Purchase Nexium Cheap Fioricet CLA Order Cymbalta Buy Myambutol Order Nimotop Buy Rocaltrol Cheap Himcospaz Buying Didrex Order Zimulti Cheap Plendil Purchase Koflet Cheap Noroxin Cheap Deltasone Inderal Order Motrin Nicotinell Cheap Nizoral Buy Mentax Vasodilan Buy Parlodel Cheap Loprox Norpace CR Shoot Paxil Purchase Avandamet Order Requip Buy Rogaine Female Viagra Cheap Imitrex Cheap Hyzaar Buying Phentermine Lotrisone Purchase Fosamax Purchase Xeloda Zithromax Cheap Requip Buy Norvasc Purchase Quibron-T Buy Topamax Eurax Cheap Nicotinell Purchase Sorbitrate Buy Coreg Cheap Leukeran Viagra Purchase Methocarbam Purchase Zithromax Cheap Flovent Cheap Calan Purchase Cordarone Buy Pamelor Purchase Endep Purchase Menosan Buy Vasodilan Order Zerit Cheap Lotrisone Buy Seroquel Order Serophene Augmentin Buy Effects Cheap Tricor Cheap Prandin Purchase Loprox Purchase Augmentin Cheap Dilantin Lopressor Purchase High Buy Motrin Buy Zithromax Flovent Purchase CLA Cheap Superman Purchase Lisinopril Cheap Viramune Buy Abana Cephalexin Buy Protonix Buy Fioricet Order Crestor Buy Hydrocodone Order Tenormin Order Carisoprodol Atrovent Cheap Prozac Purchase Reosto Purchase Atrovent Purchase Flovent Cheap Tenuates Purchase Cardizem Buy Flexeril Cheap Styplon Order Methocarbam Purchase Pamelor Cheap Coreg Purchase Buspar ActoPlus Met Deltasone Purchase Retin-A Mentax Purchase Cytotec Buy Sustiva Motrin Purchase Ashwagandha Lariam Cheap Zerit Order Mobic Cheap Bonnisan Cheap Isoptin Order Seroquel Geriforte Cheap Accutane Purchase Lexapro Cheap Lariam Purchase Prevacid Cheap Xenacore Order Lotensin Buy Requip Buy Mevacor Purchase Norvasc Purchase Tricor Prometrium Isoptin Adalat Purchase Himcocid Purchase Flexeril Order Eurax Cheap Norvasc Pamelor Cheap Effexor Hoodia Cheap Accupril Cheap Dostinex Meridia Purchase Shoot Purchase Zebeta Order Capoten Cystone Starlix Purchase Brite Order Nolvadex Cheap V-Gel Aricept Purchase Nolvadex Cheap Flonase Buy Diakof Buy Inderal Order Kytril Arava Buy Celebrex Order Aceon Buy Clonazepam Buy Leukeran Purchase Fioricet Buy Effexor Buy Speman Purchase Diflucan Rumalaya Purchase Kytril Cheap Isordil Buy Lopid Order Diflucan Cheap Lasix Cheap Phentermine Cheap Zyloprim Fastin Cheap Nimotop Buy Famvir Buy Lotrisone Watson Cheap Lincocin Oxytrol Adderall Cheap Maxaquin Zimulti Purchase Capoten Avapro Cheap Parlodel Order Atarax Order Tulasi Buy Ismo Order Avandamet Buy Didrex Order Danazol Purchase Penisole Female Sexual Buy Darvocet Buy Lasix Buy Purinethol Purchase Ativan Penis Growth Buy Proventil Vicodin Order Pletal Oxycontin Plendil Calan Zyrtec Purchase Didrex Leukeran Cheap Pravachol Cheap Myambutol Purchase Rumalaya Order AyurSlim Buy Exelon Herbolax Buy Quibron-T Buy CLA Buy Online Renalka Cheap Aldactone Buy Noroxin Order Lopid Order Zyprexa Cheap Purinethol Buy Clarina Buy Avodart Arimidex Orgasm Enhancer Purchase Lukol Purchase Inderal Atacand Order Overnight Codeine Cheap Clarinex Buy Monoket Depakote Buying Viagra Order Amoxil Purchase Zanaflex Purchase V-Gel Purchase Prinivil Order Ephedrine Order Plavix Purchase Mexitil Order Synthroid Order Antabuse Buy Lortab Purchase Herbolax Buy Lopressor Mexitil Cheap Antabuse Buy Zanaflex Buy Pletal Cheap Motrin Buy Plan Buy Rimonabant Cheap Prinivil Order Himcospaz Purchase Mentax Order Fioricet Order Nicotinell Buy Cozaar Buy Septilin Fosamax Butalbital Purchase Adipex Purchase Septilin Order Karela Cheap Ventolin Lincocin Purchase Aristocort Coumadin Purchase Desyrel Cheap Elavil Plan B Cheap AyurSlim Order Elimite Order Viramune Buy Purim Purchase Keftab Cheap Azulfidine Cheap Atacand Buy Lincocin Order Myambutol Order Cardura Cheap Brahmi Purchase Topamax Buy Cordarone Buy Trandate Buy Triphala Purchase Clomid Elimite Order Renalka AyurSlim Buy Superman Buy Lynoral Zebeta Cheap Rumalaya Cheap Ophthacare Order Mevacor Purchase Danazol Brahmi Liv.52 Cheap Diflucan Cheap Mobic Purchase Codeine Buy Nonoxinol Viagra Jelly Purchase Detrol Order Xenacore Purchase Sarafem Order Aciphex Purchase Bactroban Chitosan Order Imdur Cheap Prometrium Buy Actos Nizoral Buy Cialis Rimonabant Buy Nimotop Buy Zyvox Order V-Gel Touch-Up Kit Cardura Buy Tulasi Purchase Diazepam Feldene Purchase Requip Buy Emsam Purchase Actos Male Sexual Prandin Septilin Buy Plendil Order Proventil Cheap Lorazepam Purchase Avapro Cheapest Ultram Buy Norco Prevacid Coreg Cheap Stromectol Buy Naprosyn Clonazepam Purchase Famvir Order Menosan Buy Xanax Buy Omnicef Cheap Zanaflex Buy Geodon Purchase Soma Buy Dospan Order Hoodia Buy Prevacid Buy Retin-A Cheap Prilosec Purchase Cozaar Order StretchNil Order Hydrocodone Purchase Evista Aciphex Acne-n-Pimple Cream Order Accupril V-Gel Purchase Zyloprim Cyklokapron Cheap Tulasi Buy Desyrel Order Isordil Purchase Lariam Order Serevent Claritin Purchase Tenuate Ventolin Buy Sarafem Order Diabecon Order Koflet Purchase Zyban Superloss Multi Purchase Xanax Cheap Gasex Cordarone Buy Penisole Ashwagandha Cheap Proventil Order Atacand Buy Proscar Order Trimox Cheap Exelon Purchase Ambien Buy Lioresal Buy Oxycontin Buy Lamictal Order Sinequan Purchase Renalka Buy Differin Purchase Didronel Order Aldactone Mentat Trimox Buy Cipro Purchase Pletal Purchase Tenormin Buy Tricor Purchase Celebrex Cheap Sarafem Viagra Soft Cheap Shallaki Lisinopril Parlodel Order Lamisil Order Watson Himcocid Cheap Bactroban Order Imitrex Proventil Phentermine Order Sumycin Order Brahmi Buy Diethylpropion Cheap Zetia Purchase Diabecon Purchase Hytrin Buy Zocor Purchase Prednisone Order Combivent Buy Fastin Order Lotrisone Order Ventolin Professional Plasma Cheap Menosan Purchase Trandate Cheap Differin Purchase Oxytrol Buy Plavix Lopid Cheap Trazodone Brafix Buy Prometrium Cheap Lopid Order Didrex Cheap Himplasia Cheap Zelnorm Cheap Famvir Cheap Clarina Purchase Noroxin Order Evista Purchase Zyrtec Order Maxaquin Kytril Order Levlen Lukol Cheap Oxycontin Cheap Mysoline Purchase Prometrium Buy Carisoprodol Cheap Zithromax Purchase Ultram Order Keftab Reosto Order Parlodel Epivir-HBV Order Adalat Altace Order Mysoline Purchase Nicotinell Cheap Loxitane Order Biaxin Cheap Nonoxinol Cheap Coumadin Buy Atarax Order Nonoxinol Buy Hoodia Buy Vantin Hoodia Weight Cheap Flexeril Purchase Rocaltrol Buy Risperdal Buy Bonnisan Order Vasodilan Purchase Combivent Order Prozac Buy Revia Cheap Avandia Buy AyurSlim Buy Flonase Buy Kytril Cheap Diazepam Purchase Cymbalta Order Gasex Order Lopressor Purchase Cipro Cheap Lexapro Buy Vicodin Order Ionamin Cheap Sorbitrate Purchase Tramadol Buy Cephalexin Purchase Isordil Grifulvin V Order Clonazepam Cheap Biaxin Purchase Zimulti Order Geriforte Cheap Diakof Purchase Snoroff Cheap Diovan Order Diovan Order Trandate Cheap Diarex Purchase Antabuse Cheap Relafen Order Sustiva Purchase Mycelex-G Buy Zimulti Buy Buspar Cheap Rogaine Purchase Confido Order Zithromax Purchase Purinethol Cheap Phentrimine Buy Avapro Nonoxinol Order Claritin Purchase Lopressor Hair Loss Order Micardis Cheap Arimidex Order Sorbitrate Cheap Omnicef Cheap Levlen Buy Lamisil Avandia Order Bactroban Buy Kamagra Buy Ventolin Order Nirdosh Accupril Purchase Crestor Order Paxil Cheap Endep Cheap Serevent Order Cephalexin Buy Brite Purchase Zocor Order Aricept Order Lasix Buy Atacand Buy Xeloda Purchase Cardura Purchase Zestril Buy Azulfidine Buy Isordil Buy Zovirax Lotensin Buy Valium InnoPran XL Buy Levothroid Purchase Percocet Zantac Cheap Zyrtec Loprox Order Monoket Cheap StretchNil Buy Glucophage Buy Vasotec Mental Booster Buy Adipex Purchase Levaquin Order Levitra Pilex Cheap Snoroff Buy Accupril Lasix Hytrin Order Zestril Cheap Zovirax Aleve Diabecon Xanax Order Chitosan Cheap Proscar Tulasi Purchase Prilosec Purchase Feldene Cheap Celebrex Cheap Prograf Soma Buy Singulair Order Zantac Cheap Ephedrine Buy Starlix Order Levaquin Purchase Cyklokapron Buy Prandin Trazodone Ismo Purchase Darvocet Flonase Purchase Micardis Buy Menosan Purchase Himplasia Cheap Brite Purchase Allegra Order Ashwagandha Cheap Pilex Order Adipex Order Exelon Purchase Bonnisan Pravachol Order Premarin Clarinex Cheap CLA Aristocort Buy Lotensin Cheap Elimite Purchase Speman Cheap Watson Norco Zero Nicotine Cheap Atrovent Cheap Acomplia Order Reosto Purchase Pilex Purchase Acticin Adipex Order Fosamax Purchase Vasotec Cheap Feldene Capoten Order Lincocin Ephedrine Order Pravachol Exelon Cheap Sinequan Order Casodex Men Attracting Himcospaz Celexa Order Buspar Cheap Tenuate Order Lariam Purchase Lorazepam Cheap Copegus Order Trazodone Order Pamelor Flexeril Cheap Aristocort Diovan Cheap Monoket Purchase Premarin Cheap Glucophage Order Prevacid Order Septilin Buy Cardizem Didrex Order Lasuna Purchase Lamisil Buy Dilantin Purchase Miacalcin Buy Avandamet Plavix Buy Elavil Virility Gum Buy Calan Darvocet Buy Vytorin Purchase Aciphex Order Naprosyn Purchase Celexa Purchase Himcolin Cheap Zebeta Buy Augmentin Cheap Depakote Cheap Aceon Buy Snoroff Purchase Protonix Order Calan Buy Shallaki Order Copegus Buy Atrovent Cheap Femcare Diflucan Cheap Levitra Buy Dosages Nexium Purchase Mevacor Avandamet Cheap Cialis Triphala Combivent Himplasia Buy Amoxil Buy Evecare Cheap Avodart Purchase Singulair Green Tea Zerit Cheap Casodex Purchase Glucophage Purchase Relafen Pletal Buy Zestril Buy Femcare Purchase Stromectol Detrol Cheap Geriforte Buy Himplasia Buy Cyklokapron Buy Amaryl Women Attracting Buy Coumadin Buy Keftab Cheap Butalbital Order Vytorin Purchase Biaxin Purchase Nonoxinol Cheap Inderal Cheap Ativan Buy Karela Buy Loprox Order Amaryl Buy Levaquin Cheap Vicodin Lasuna Hydrocodone Buy Mentat Cheap Ansaid Buy Codeine Buy Biaxin Buy Oxytrol Cheap Evista Buy Cystone Vitamin A Buy Imdur Order Diazepam Cheap Cymbalta Cheap Serophene Order Protonix Purchase Prandin Cheap Lopressor Male Enhancement Cheap Nexium Purinethol Order Prometrium Purchase Aldactone Gyne-Lotrimin Protonix Order Bonnisan Order Flovent Cheap Adalat Buy Nolvadex Order Zyvox Cheap Mycelex-G Purchase Effexor Danazol Purchase Imdur Levlen Order Plan Order Tenuate Purchase StretchNil Viramune Purchase Triphala Order Zyrtec Cheap Hydrocodone Rogaine Buy Avandia Order Norco Cheap Urispas Buy Zebeta Methocarbam Buy Methocarbam Buy Zyrtec Antabuse Cheap Ambien Buy Aricept Buy Procardia Purchase Lipitor Order Codeine Order Cordarone Purchase Zyvox Myambutol Cheap Lioresal Cipro Lioresal Cheap Crestor Order Cipro Cheap Nirdosh Purchase Sumycin Order Acyclovir Cheap Brafix Avodart Order Cardizem Aldactone Desyrel Buy Tramadol Buy Urispas Purchase Lortab Diet Maxx Order Confido Purchase Zovirax Cheap Atarax Order Zanaflex Order Emsam Purchase Zelnorm Cheap Buspar Lozol Buying Tenuate Cheap Levaquin Zyprexa

Archive for the 'Rails' Category

RSpec, meet Flow.

Flow is the demon that appears after about an hour of using RSpec. He sits on my shoulder and watches me code.

After a few minutes he stands up. I know to turn down the music. He whispers into my ear. I nod in agreement and mouth back a response.

Flow understands my frustration with testing individual methods outside any greater context or usage scenario.

RSpec is a beautiful tool. Leaps and bounds ahead of the standard testing framework built into Rails. It does this, I believe, by allowing you to use more natural language. But it can be be misused. Which is actually closer to abuse, because unless you’re paying respect to flow, you’re fucking yourself.

Don’t test methods outside of the context in which they should be used. It doesn’t mean shit to the person reading your code — even if that person is you.

Your program is a play. Your objects are actors. You are the writer and director. You say action! You say cut! You know all the lines, but it’s not your job to voice them. You assign those roles to your actors, and they carry it out much better than your sorry ass ever could.

Context. Acknowledge it. If your tests look like this:

describe 'An InterestList' do
 
  before(:each) do
    @interest_list = InterestList.create
  end
 
  describe 'upon creation' do
 
    it 'should not be a new record'
    it 'should be empty'
    it 'should report having zero Interest objects'
 
  end
 
  describe 'when adding a ProductInventoryPrototype' do
 
    before(:each) do
      @cheese = inventory_prototypes(:cheese)
    end
 
    it 'should answer true'
 
  end
 
  describe 'with one ProductInventoryPrototype in it' do
 
    before(:each) do
      @cheese = inventory_prototypes(:cheese)
      @interest_list.add(@cheese)
    end
 
    it 'should not be empty'
    it 'should report having one interest'
 
  end
 
  describe 'removing a ProductInventoryPrototype' do
 
    before(:each) do
      @cheese = inventory_prototypes(:cheese)
      @interest_list.add(@cheese)
    end
 
    it 'should answer with the item when that item is removed'
 
  end
 
  describe 'after removing a ProductInventoryPrototype' do
 
    before(:each) do
      @cheese = inventory_prototypes(:cheese)
      @interest_list.add(@cheese)
      @interest_list.remove(@cheese)
    end
 
    it 'should be empty when that item is removed'
    it 'should report having zero items'
 
  end
 
end

You’re fucking with the laws of nature, and you will be smitten.

What’s wrong with it? The

#before

code is reinitializing all the steps you’ve just accomplished.

Make it look like this.

describe 'When working with an InterestList,' do
 
  before(:each) do
    @interest_list = InterestList.create
  end
 
  describe 'it must first be created.' do
 
    it 'It should not be a new record.'
    it 'It should be empty.'
    it 'It should report having zero Interest objects.'
 
    describe ' After which you can add a product.' do
 
      before(:each) do
        @cheese = inventory_prototypes(:cheese)
      end
 
      it 'To which it should answer true.'
 
      describe 'It now has one product in it.' do
 
        before(:each) do
          @interest_list.add(@cheese)
        end
 
        it 'It should not be empty.'
        it 'It should report having one interest.'
 
        describe 'Now we remove a ProductInventoryPrototype.' do
 
          it 'It should answer with the item when that item is removed.'
 
          describe 'After removing a ProductInventoryPrototype,' do
 
            before(:each) do
              @interest_list.remove(@cheese)
            end
 
            it 'it should be empty when that item is removed.'
            it 'it should report having zero items'
 
          end
 
        end
 
      end
 
    end
 
  end
 
end

Of primary importance is a subtle point. I didn’t illustrate it in the two examples above, but I will now:

Before:

describe 'When working with an InterestList,' do
 
  before(:each) do
    @interest_list = InterestList.create
  end
 
  describe 'it must first be created.' do
 
    it 'It should not be a new record.'
    it 'It should be empty.'
    it 'It should report having zero Interest objects.'
 
    describe ' After which you can add a product.' do
 
      before(:each) do
        @cheese = inventory_prototypes(:cheese)
      end
 
      it 'To which it should answer true.'
      it 'It should not be empty.'
      it 'It should report having one interest.'
...

After:

describe 'When working with an InterestList,' do
 
  before(:each) do
    @interest_list = InterestList.create
  end
 
  describe 'it must first be created.' do
 
    it 'It should not be a new record.'
    it 'It should be empty.'
    it 'It should report having zero Interest objects.'
 
    describe ' After which you can add a product.' do
 
      before(:each) do
        @cheese = inventory_prototypes(:cheese)
      end
 
      it 'To which it should answer true.'
 
      describe ' At which point' do
 
        it 'it should not be empty.'
        it 'it should report having one interest.'
...

On the surface it’s just another level of nesting. After meditation, you realize you are first testing the object’s answer to your message. Second, you are testing the state of the universe after that action is made. The difference is subtle, but important. If you don’t understand the importance, recall the indifference you felt when you first saw RSpec: it’s Rails’ standard test framework with different words thrown in.

You were wrong then and you’re wrong now.

Words offer the means to meaning, and for those who will listen, the enunciation of truth. - V

If an object’s answer to the message passed violates expectations, flow stops. You know, not that a method is broken, but that flow is broken.

Of secondary importance is that your

#before

methods are no longer redundant.

The result of running your tests now looks like:

When working with an InterestList, it must first be created. It should not be a new record. (Not Yet Implemented)
When working with an InterestList, it must first be created. It should be empty. (Not Yet Implemented)
When working with an InterestList, it must first be created. It should report having zero Interest objects. (Not Yet Implemented)
When working with an InterestList, it must first be created. After which you can add a product. To which it should answer true. (Not Yet Implemented)
When working with an InterestList, it must first be created. After which you can add a product. It now has one product in it. It should not be empty. (Not Yet Implemented)
When working with an InterestList, it must first be created. After which you can add a product. It now has one product in it. It should report having one interest. (Not Yet Implemented)
When working with an InterestList, it must first be created. After which you can add a product. It now has one product in it. Now we remove a ProductInventoryPrototype. It should answer with the item when that item is removed. (Not Yet Implemented)
When working with an InterestList, it must first be created. After which you can add a product. It now has one product in it. Now we remove a ProductInventoryPrototype. After removing a ProductInventoryPrototype, it should be empty when that item is removed. (Not Yet Implemented)
When working with an InterestList, it must first be created. After which you can add a product. It now has one product in it. Now we remove a ProductInventoryPrototype. After removing a ProductInventoryPrototype, it should report having zero items (Not Yet Implemented)

This is easier to read and understand than a series of isolated tests. You’re made aware of the context in which each test is being run, which is priceless when you’re attempting to understand the system from a high level.

Congratulations. You are no longer a moron coding outside the realm of the context in which your code lives.

Turn an acts_as_tree structure into a select drop-down.

This is probably the easiest way to turn Rails’ acts_as_tree structure into a select field:

HTML:

<select name="category_id">
  <%= expand_tree_into_select_field(Category.top) %>
</select>

Ruby helper:

def expand_tree_into_select_field(categories)
  returning(String.new) do |html|
    categories.each do |category|
      html << %{<option value="#{ category.id }">#{ '&nbsp;&nbsp;&nbsp;' * category.ancestors.size }#{ category.name }</option>}
      html << expand_tree_into_select_field(category.children) if category.has_children?
    end
  end
end

The Category.top method is simply:

def self.top
  find(:all, :conditions => [ 'parent_id IS NULL' ])
end

Finally, your Category class should implement has_children?:

def has_children?
  children.size > 0
end

That last bit is, of course, optional. You could easily write category.children.size > 0, but that’s not very object-oriented now, is it?

Enjoy!

ActiveRecord’s sum or Rails’ Enumerable’s sum?

product.inventory_transactions.sum(&:amount) # ArgumentError: wrong number of arguments...

Why? Because model.relationship returns a modified version of the Array class. The object returned acts very much like (if not identical to) calling Model.find, and accepts most (if not all) of find’s options.

So note that if you are calling model.relationship.sum, you either specify the attribute you’re interested in:

product.inventory_transactions.sum(:amount)

Or, if, for whatever reason, you want to use Enumerable’s sum method, you can convert to a standard array:

product.inventory_transactions.to_a.sum(&:amount)

What’s the difference? Model.relationship.sum is a database operation. model.relationship.to_a.sum is a pure Ruby operation.

Comparing custom objects with uniq.

You author a Client model. You have an array of clients and you want to call Ruby’s uniq method on them. You define “unique” clients as having the same name. This doesn’t work:

>> c1 = Client.find(1)
=> #<Client id: 1, artist_id: 1, name: "Daniel Waite", alias: "Rabbit", birthdate: nil, created_at: "2008-01-13 13:20:14", updated_at: "2008-01-23 01:03:26">
>> c2 = Client.find(4)
=> #<Client id: 4, artist_id: 1, name: "Daniel Waite", alias: nil, birthdate: nil, created_at: "2008-01-14 23:44:58", updated_at: "2008-01-14 23:44:58">
 
[ c1, c2 ].uniq.size # 2

Being the clever Ruby programmer you are, you quickly modify client…

class Client < ActiveRecord::Base
 
  def ==(other)
    name == other.name
  end
 
end

Satisfied, you run the above code again.

[ c1, c2 ].uniq.size # 2

What the fuck? Frustrated, you go medieval…

class Client < ActiveRecord::Base
 
  def ==(other)
    name == other.name
  end
 
  def ===(other)
    name == other.name
  end
 
  def eql?(other)
    name == other.name
  end
 
  def equal?(other)
    name == other.name
  end
 
end

Huffing and puffing, you carefully run your code again…

[ c1, c2 ].uniq.size # 2

ANGER SHARKS ARE SWIMMING!

But fear not my friend, for you have sought, and so the answer shall be revealed to you…

class Client < ActiveRecord::Base
 
  def hash
    name.hash
  end
 
  def eql?(other)
    name == other.name
  end
 
end

Wearily, you try again…

[ c1, c2 ].uniq.size # 1

Success!

Yes, both methods must be overridden.

You may have never seen it, but the hash method is available to all objects. I’m not quite sure how or why this works, but it does. Rejoice.

**UPDATE**

Russ pointed out that for new or unsaved objects, yes, all you have to override is the eql? method. However, to compare for equality for existing objects (i.e. objects retrieved from the database) you must override both methods.

This makes things more interesting, too. If you wanted to define equality between clients as having both the same name and the same birthdate, you can write…

class Client < ActiveRecord::Base
 
  def hash
    %{#{ name }#{ birthdate }}.hash
  end
 
  def eql?(other)
    hash == other.hash
  end
 
end

And now you have a more complex definition of what equality means. Gotta love Ruby.

Rails’ find_by_xxx and find_all_by_xxx methods accept arrays and ranges!

Use an array as the argument to a find_by_xxx method:

Appointment.find_by_date([ Date.today, Date.today + 1])

And the generated SQL:

SELECT * FROM appointments WHERE (appointments.'date' IN ('2007-10-07','2007-10-08')) LIMIT 1

Use a range as the argument to a find_all_by_xxx method:

Appointment.find_by_date((Date.today..Date.today + 1))
SELECT * FROM appointments WHERE (appointments.'date' BETWEEN '2007-10-07' AND '2007-10-08') LIMIT 1

Both methods (find_by and find_all_by) accept both arrays and ranges.

Scientists agree: Using JSON to bring the richness of the Rails controller and view layers to Ajax requests makes life more enjoyable!

How do you handle an Ajax request whereby the request itself is successful (i.e. it returns a status 200 OK), but the requested operation fails. For example, a user attempts to create a new ActiveRecord object, but the object fails validation and thus doesn’t get saved to the database. The HTTP request was successful, but the user’s wishes weren’t fulfilled.

There are many, many ways to handle such a request, and most of them are fucking painful. Especially if you don’t care to muck around with endless JavaScript.

What I’m about to propose is most useful for those using jQuery (or any framework outside of Prototype, actually). I say this because a lot of what this technique does is also accomplished with RJS templates. Why not just use RJS templates? Because Prototype sucks balls in the documentation department. jQuery doesn’t. Therefore, I use jQuery.

Anywho, let’s start with a controller action:

def create
  @object = Object.new(params[:object])
 
  if @stock_color.save
    flash[:positive] = %{Object saved.}
  else
    flash[:negative] = %{Object not saved.}
  end
 
  if request.xhr?
    render(:partial => 'xhr_create', :layout => false)
  else
    redirect_to(:action => 'index')
  end
end

And the Ajax-specific partial for that action:

<%=
 
  {
    :flash => { :positive => flash[:positive], :negative => flash[:negative] },
    :objects => { :object => @object },
    :views => { :some_partial => render(:partial => 'some_partial') }
  }.to_json
 
%>

Note the to_json method we’re appending to our Hash. This converts and injects everything we care about from Rails directly into a format easily read and manipulated by JavaScript. We could easily add all our session and params data, too.

Finally, and most importantly, we’ll see how much easier it is to manipulate our page now that we have all the information we could possibly want from the Rails environment:

$('#quote_form').submit(function() {
    $.getJSON('/objects/create', $(this).serialize(), function(response) {
      Flash.set(response.flash); // An object I created to handle setting the flash.
 
      if(response.objects.quote.errors.length == 0) {
        $('#objects').append('<li>' + response.objects.object.attributes.name + '</li>');
        $('#object_form').resetForm();
      } else {
        // Handle the error here. Remember, you have access to the standard ActiveRecord errors!
      }
    });
 
    // Don't actually submit the form, let the above JavaScript do its thing.
    return false;
  });

The pattern is a simple one.

  1. Do what you would normally do in a controller action.
  2. In addition to what you just did in that action, conditionally render a standard .rhtml partial if the request came from JavaScript.
  3. In the view that gets rendered for Ajax requests, create a standard Ruby hash with all the information you’re interested in. This includes any instance variables, session, params and even other views (this is good for generating lots of HTML that would otherwise be a hassle to generate in JavaScript).
  4. Call to_json on that hash.
  5. Smile with glee at all the kick ass information you have available to you in JavaScript!

The only thing I don’t like about this approach is that the controller code gets a bit longer and slightly more complex — it also introduces new views where there were previously none. On the flip side, it makes handling Ajax requests a freaking breeeze, and that’s worth it!

Rspec problems.

I’m writing this, not because I have an answer, but because I have a question. A question I don’t see anyone else asking. I wonder if I’m unique in having the question, but I doubt it.

I’m writing this, not because I think I’ll get a response, but that hopefully I will find an answer, so that the next person with this same question will find an answer.

Rspec won’t run its tests. Running rake spec or any of its variants failed, saying something like…

Rabbit:~/apps/inkwell rabbit$ rake spec:models
/usr/local/bin/rake:17:Warning: require_gem is obsolete.  Use gem instead.
(in /Users/rabbit/apps/inkwell)
/Users/rabbit/apps/inkwell/vendor/plugins/rspec/lib/spec/runner/option_parser.rb:180:in `readlines': No such file or directory - /Users/rabbit/apps/inkwell/config/../spec/spec.opts (Errno::ENOENT)
        from /Users/rabbit/apps/inkwell/vendor/plugins/rspec/lib/spec/runner/option_parser.rb:180:in `parse_options_file'
        from /Users/rabbit/apps/inkwell/vendor/plugins/rspec/lib/spec/runner/option_parser.rb:136:in `parse'
        from /usr/local/lib/ruby/1.8/optparse.rb:1260:in `call'
        from /usr/local/lib/ruby/1.8/optparse.rb:1260:in `parse_in_order'
        from /usr/local/lib/ruby/1.8/optparse.rb:1247:in `catch'
        from /usr/local/lib/ruby/1.8/optparse.rb:1247:in `parse_in_order'
        from /usr/local/lib/ruby/1.8/optparse.rb:1241:in `order!'
        from /usr/local/lib/ruby/1.8/optparse.rb:1332:in `permute!'
        from /usr/local/lib/ruby/1.8/optparse.rb:1353:in `parse!'
        from /Users/rabbit/apps/inkwell/vendor/plugins/rspec/lib/spec/runner/option_parser.rb:155:in `parse'
        from /Users/rabbit/apps/inkwell/vendor/plugins/rspec/lib/spec/runner/option_parser.rb:88:in `create_behaviour_runner'
        from /Users/rabbit/apps/inkwell/vendor/plugins/rspec/lib/spec/runner/command_line.rb:14:in `run'
        from /Users/rabbit/apps/inkwell/vendor/plugins/rspec/bin/spec:3
rake aborted!
Command ruby -I"/Users/rabbit/apps/inkwell/vendor/plugins/rspec/lib" "/Users/rabbit/apps/inkwell/vendor/plugins/rspec/bin/spec" "spec/models/tattoo_artist_spec.rb" --options "/Users/rabbit/apps/inkwell/config/../spec/spec.opts" failed

I’ve got Rspec and Rspec::Rails installed as plugins in my vendor directory. I’ve tried reinstalling the plugins but no go. This is a brand-new Rails app on 1.2.3. Anyone…?

Update.

If you’re getting the above error, read the section entitled Bootstrap on the Rspec website, or simply type ruby script/generate rspec. Duh.

Discovering self-evaluating objects. (pt. 2 of 2)

Notice: There’s quite a bit of code covered in this post. You can follow along with most of it using IRB. However, to run the final example, you’ll be better off downloading this complete Rails application. Follow the instructions in doc/readme.

Notice: This is part two of a two-part post. Read the first post if you haven’t already.

All right! We’re taking the experience we gained from our last endeavor, and we’re putting it on rails.

First off, let’s think about our goal — what are we trying to accomplish? How does it flow? What does it look like? In this case it’s pretty simple. It should probably look something like this:

1
product.evaluate('cost')

That’s concise, and it looks good. But it’s not the whole story. So let’s think some more.

We know we have products, as shown above. We have rules, presumably shown above as the argument to the evaluate method. What about variables? I don’t see those in the code above.

Before we go any further, let’s hash out a quick drawing to show our understanding of our objects and their relationships. … Done!

diagram

Pretty basic right now, but let’s talk about it.

Rules have many products have many variables. Interesting. When you say it like that, it almost doesn’t make sense. A rule has many products? What would that look like code-wise?

1
2
3
4
5
class Rule
 
  has_many :products
 
end

Hmm… looks perverted, (a rule has products?) but let’s keep going with some pseudo code.

1
2
product = rule.products.create(:name => 'Cheese')
product.variables.create(:name => 'cost', :value => 2)

I dig the second part:

1
product.variables.create(:name => 'cost', :value => 2)

So let’s keep that in mind as we progress.

But what about rule.products? Our first line of code tells us that products should be able to evaluate rules, so there’s gotta be some connection between the two. What do we know least about? I’d probably say the rule objects themselves right now, so let’s take a stab at designing one.

What are rules? Well, let’s look at a real-life example.

1
(x + y) * z

What do you see? … You could say you see a simple math formula. Perhaps, if you recall David West’s explanation of self-evaluating objects, you see an array of operators and variables. Before I tell you what I see, I’d like to say that what I see is based off my previous experience handling self-evaluating objects. That said, I see a string.

That’s it. A simple string.

1
"(x + y) * z"

It’s so simple it’s obvious. And obvious things are often the most difficult thing to see. Now that we know what a rule is, let’s try and define it using object parlance. What can a rule do? What’s it comprised of? What are its responsibilities? Who must it collaborate with to get its job done?

The first thing I see is a definition. Every rule has a definition. I suppose also, that a rule could have a name; it would certainly make distinguishing one rule from another easier.

So now we have named rules with definitions. Let’s see some potential code.

1
2
rule = Rule.new(:name => 'Product cost')
rule.definition = '(cost * tax) + cost'

Looks pretty clean. What next? Oh, that’s right, the obvious: a rule can evaluate itself. Ah, there’s the meat. Now we have a new, non AR-inherited responsibility: evaluation. Let’s take a look..

1
2
3
4
5
6
7
class Rule < ActiveRecord::Base
 
  def evaluate
    eval(definition)
  end
 
end

That’d be nice if that’s all we had to do. Unfortunately it’s not. Executing that as is will raise an error. If we think back to our earlier work, we did simple string replacement to get the proper values into our definitions.

1
2
3
def evaluate
  definition.gsub('uh oh!', "we're missing something")
end

Things just got interesting. Our rule doesn’t have access to the variables needed for substitution in its definition. Hmm… let’s stop and think for a moment.

Rules, products and variables. Products have variables for use in rules. A rule’s definition must have its variables substituted out for actual values. What does that look like?

1
2
rule = Rule.new(:name => 'Product cost', :definition => '(cost * tax) + cost')
rule.evaluate(product)

Hmm… that could work. A rule requires a product be passed to it to gather the contents of its variables.

Let’s try that.

1
2
3
4
5
6
7
8
9
10
11
class Rule < ActiveRecord::Base
 
  def evaluate(product)
    product.variables.each do |variable|
      definition.gsub!(variable.name, variable.value)
    end
 
    eval(definition)
  end
 
end

Not bad. A rule can evaluate itself in the context of a given product’s variables. This will work provide the product passed has all the variables required of the rule, and vice versa.

Of course, this model doesn’t look like our original diagram, and that’s okay. Things rarely go according to plan. Let’s review what we’ve done so far.

Rule objects stand alone. They are named mathematical definitions that evaluate in the context of a given product. (In reality, ‘product’ could be any object that responds to a variables method.)

So our end result is now:

1
rule.evaluate(product)

That’s the opposite of what I originally proposed (product.evaluate(rule)), and I’ll be honest; it feels weird. On the bright side, this solution is much simpler than my original Rails version. However, I still like the feel of:

1
product.evaluate(rule)

But hey, whatever works, right? There’s just one more thing… rules within rules. Ah, interesting. What would that look like?

Well, actually, it shouldn’t look any different than what we have now:

1
rule.evaluate(product)

The above should still give us a single number. So it’s not the end-result (API) code that will be changing, it’s our internal code that will change. But how? Let’s take a look at our definition.

1
(cost * tax) + cost

The part in parentheses represents the tax of our product. So we’re really saying:

1
tax + cost

But now we’ve run into a problem. Is tax a rule or a variable? Currently there’s no way to know the difference. How can we solve that? We need to be able to differentiate between rules and variables. Or at least, I think we should.

We could not, and simply p