agaskar.com

Feb 27 2010

authlogic doesn’t seem to like html multipart in tests

      xhr :post, :create, { :item_id => @item.id.to_param, :Filedata => fixture_file_upload('images/sample1.jpg', 'image/jpeg') }, :html => { :multipart => true }
fails
      xhr :post, :create, { :item_id => @item.id.to_param, :Filedata => fixture_file_upload('images/sample1.jpg', 'image/jpeg') }
succeeds.

not going to track this one down, because my test was green without it. Maybe this is blowing away some important headers? …

Comments (View)
+

Rails Routing Namespaces with Nested Routes can cause Model failures.

Working on my side project, I refactored some controllers into an admin namespace, resulting in a directory tree that looked something like this:
-controllers/
  -admin/
    -item/
      -photo_controller.rb
    -item_controller.rb
All the tests passed, yet whenever I hit my (previously working) admin/items controller, I got:
NoMethodError (undefined method `find' for Admin::Item:Module):
  app/controllers/admin/items_controller.rb:7:in `index'

It looked like the route was somehow forcing the namespacing of the model. I went nuts googling for it and found zero explanations. Finally, on a hunch, I renamed the item directory to items and the photo_controller to Admin::Items::PhotoController instead of Admin::Item::PhotoController and everything worked.

Another option is to use ::Item in your controller instead of Item. I think I might go with this one — would rather be more specific than resort to naming tricks.

I’m still bewildered as to how or why this is happening — I can’t quite see how having an Admin::Item:: namespace means that all of the sudden, Item will now refer to Admin::Item, although I do have a pet theory. Perhaps when Item is not found in the current namespace, it goes UP a namespace and eventually hits the model. Since I now have an Admin::Item, it doesn’t bother looking in the main namespace.

I can’t believe this was so tough to google for. I imagine this would nail anyone who has a nested route in a namespace. I’m a bit surprised it’s not a more common error.

I’m assuming tests didn’t fail because the Admin::Item::PhotosController was not loaded during the Admin::ItemsController test.

Comments (View)
Feb 20 2010

Active Record DB Types: A Reference

Native types:

add_column :native_type, :name, :options
:primary_key 
:string 
:text
:integer 
:float
:decimal
:datetime
:timestamp 
:time 
:date 
:binary 
:boolean

Native Type Options:

:limit - Requests a maximum column length. This is number of characters for :string and :text columns and number of bytes for :binary and :integer columns.
:default - The column‘s default value. Use nil for NULL.
:null - Allows or disallows NULL values in the column. This option could have been named :null_allowed.
:precision - Specifies the precision for a :decimal column.
:scale - Specifies the scale for a :decimal column.

Table definitions:

  create_table :products do |t|
    t.integer :shop_id, :creator_id
    t.string  :name, :value, :default =%gt; "Untitled"
    t.timestamps
  end
You can use any native type, or the following helpers.

timestamps

adds created_at and updated_at timestamp columns to your table.
...
t.timestamps
...

references

Adds the integer field “#{name}_id”. Also adds the string field “#{name}_type” if polymorphic is true. You can pass a hash to polymorphic and specify a default if desired.
create_table :taggings do |t|
    t.references :tag
    t.references :tagger, :polymorphic => true
    t.references :taggable, :polymorphic => { :default => 'Photo' }
  end

Auto-generate an add_column migration

There is a special syntactic shortcut to generate migrations that add fields to a table.
 script/generate migration add_fieldname_to_tablename fieldname:string

Comments (View)
Feb 17 2010

has_many :through, accepts_nested_attributes_for, and validations

I had a need to write some code that would permit the following:

it "accepts attributes for item_photos" do
    photo = Factory(:photo)
    item = Item.create!(Factory.attributes_for(:item, :item_photos_attributes => [{:photo_id => photo.id}]))
    item.item_photos.should have(1).item_photo
    item.photos.first.should == photo
end
Basically, you can use nested attributes to when creating items to create lookup records for existing photos. Of course, the create kept failing due to the item_photo in question missing an item (well, actually … I started with a create, which of course happily continued on its way until I tried to look at item photos and it was blank. The lesson is that you should ALWAYS test that your create or saves have occured, either with a should be_true or a bang method so they explode). This blew my mind — how does accept_nested_attributes_for even work if you need to have an id before saving child associations? Well, it was my fault, of course. My item_photos model looked like this:
class ItemPhoto < ActiveRecord::Base
  belongs_to :item
  belongs_to :photo

  validates_presence_of :item
  validates_presence_of :photo
end
I added the validations because I believed it would be silly to have an item_photo without an associated item or photo. What would you do with such a thing? Of course, my overzealous validating meant my accept_nested_attributes could never hope to succeed. Removing these validations made the test pass happily. Basically, this seems to indicate that Rails can’t yet handle validations on parent belongs_to associations in children yet, so you’re probably going to have to skip them. It sounds like this is possibly going to make it into a future version of rails, but if not, there you have it: a simple solution for a foolish problem that is *really* hard to google for.

Comments (View)
Jan 26 2010

Fun (and evil) tricks with rspec

      original_glob = Dir.method(:glob)
      Dir.stub!(:glob).and_return do |glob_string|
        if glob_string =~ /public/
          glob_string
        else
          original_glob.call(glob_string)
        end
      end
Of course, you shouldn’t do this because return blocks on stubs (calculated return blocks) are evil. Still fun, though.

Comments (View)
Jan 23 2010

JSON.stringify bug with Native FF Implementation

Firefox past 3.5.4 natively implements JSON.stringify with replacers, similarly to the way JSON2 works. However, it’s doing something wrong (optimization related?). Supposedly, you can pass stringify a replacer function as a second argument and it will use whatever is returned from that argument as the value in the serialized copy. HOWEVER, if you pass a value back that contains the same values (but is not === to) the original value, the JSON.stringify uses the original value instead. This sucks for me, because I want to pass back an array with the toJSON method wiped, without destroying the (incorrectly implemented by Prototype) toJSON method on the original. Here’s a jasmine spec:

function json2PrototypeFix(key, value) {
  if (typeof this[key] == 'object' && Object.prototype.toString.apply(this[key]) === '[object Array]') {
    var copy = this[key].slice(0);
    this[key] = [1];
     return copy;
  } else {
    return value
  }

}
describe('JSON with protoype', function () {

 it('should properly stringify an object with child arrays', function() {
    var array = [1,2];
    var obj = {"foo": array};
    var result = JSON.stringify(obj, json2PrototypeFix);
    expect(result).toEqual("{\"foo\":[1,2]}");
  });
});
The failure I get here is
Expected '{"foo":"[1]"}' to equal '{"foo":[1,2]}'.
which implies I’m using the original value, even though I passed back the copy I made of it. If I allow Crockford’s original JSON2 to overwrite the stringify function, then everything works as expected. This is super frustrating — both Prototype AND Firefox are broken in this case, so I can’t reliably fix the parts of Jasmine that need JSON.stringify to work correctly when Prototype is present. I think I’m just going to check for the presence of prototype and then just use their toJSON, instead of fixing the issue using JSON2’s replacer support (which isn’t going to be present everywhere anyways — sounds like it’ll break in browsers that implement JSON.stringify without the replacer arg; ie FF

Comments (View)
Dec 30 2009

Tracking a remote fork locally with Git

git add remote SomeRemote git://github.com/SomeRemote/some-project.git
git fetch SomeRemote
git branch --track SomeRemote SomeRemote/master
git checkout SomeRemote
Now you’ve got a local branch (named after the user who created it) that is tracking changes to that repo. Meaning if you
git pull
in there, you’ll get their latest changes. If you’ve got someone you frequently collaborate with who makes changes to their own fork, this is super handy.

Comments (View)
Dec 15 2009
First, you need to include the library and create a new instance using the new2 method (or new with a lot of parameters or new3 with a hash of every parameter needed; it’s sort of up to you).

according to this Ruby tutorial slideshow transcription, these are the basic differences between new, new2, and new3 constructors for xmlrpc. Can I take a moment to salute the careful consideration that was given to these names? [1]

[1] Yes, I am using my middle finger here.

Comments (View)
Dec 10 2009

if ENV[‘RAILS_ENV’] == ‘production’
ENV[‘GEM_PATH’] = ‘/home/USERNAME/.gems’
require ‘/home/USERNAME/.gems/gems/rack-1.0.0/lib/rack.rb’
end

is apparently the crap you have to add to get the correct version of rack on dreamhost. Really? That seems like a pain in the ass.

Comments (View)
Oct 31 2009

rspec, errors, and idiocy

Just spent ten minutes writing a test that should’ve taken thirty seconds. The final test looks like this:

it "should throw an Already Running error if there is already a server running" do
    some_thing.should_receive(:some_method).and_raise(RuntimeError.new('no error'))
    lambda {
      some_thing.that_triggers_the_error()
      }.should raise_error(RuntimeError, "some error")
end
The first mistake was mine. I wrote:
some_thing.should_receive(:some_method).and_throw(RuntimeError.new('some error'))
Too much javascript, I guess. But the crazy thing is, yes, it throws. You end up getting a NameError: uncaught throw for ‘some error”. Super confusing, right? Easy mistake to make, though. The second issue is more of an rspec message shortcoming. I originally had:
 .should raise_error(RuntimeError.new('some error'))
which resulted in the matcher error:
expected some error but nothing was raised 
But something *was* raised — it just wasn’t being matched correctly because I used the wrong matcher arguments. This is, yes, technically also my fault, but the unhelpful matcher error makes it difficult to catch. Anyways, hopefully this gotcha description finds its way into the hands of people who need it, since google wasn’t super helpful when I dropped in some key words.

Comments (View)
Page 1 of 8