Friday, March 19, 2010

When Regular Assignment Fails, update_attributes!...?

I've run into my second gotcha with Rails now, solved by the same function: update_attributes!. Both times it took me entirely too long to figure out.



I have a many-to-many assocation, whose proper model implementation is excellently described in the official guide. As expected, my join model :belongs_to each of the models in the many-to-many relationship. We have 3 models, all related to each other; 2 of them are related through the third:



Or, in code:


## in the Receipt class...
:belongs_to :store
:belongs_to :customer

## in the Store class..
:has_many :receipts
:has_many :customers, :through => :receipts

## in the Customer class...
:has_many :receipts
:has_many :stores, :through => :receipts



This ends up being really handy, because we can easily do things with collections owned by either a store or a customer:

@store.receipts.total_price.each do |r|...

Adapting this blog-building example from the Rails guides, led me to create a receipt while building on a store:

@receipt = @store.receipts.build(params[:receipt])

Being new at the time I wrote this code, I figured an easy-enough way to also determine customer ownership of receipts was to extend the assignments:

@receipt = @store.receipts.build(params[:receipt]) = @customer.receipts.build(params[:receipt])

Seems like it should work, but this throws undefined method `build=' errors. I promptly erased that last error-causing bit and headed back to the drawing board -- this time checking the server logs while executing a successful new/create operation. All of my attributes but customer_id were being passed.

Solution: change controller's create method to update the attributes (new portion in bold):


@receipt = @store.receipts.build(params[:receipt])
@receipt.update_attributes!(:customer_id => current_user.id)



Just like that, we're passing all attributes and everything in the @customer.receipts collection is working!



  • Note this does assume the excellent RESTful authentication plugin is being used and the user is logged in. Not a problem, since I don't expose my create method to users who aren't logged in, but worth noting nonetheless.

Monday, March 15, 2010

Rails Webapp: Closed Alpha Build

Ever want to use your webapp while it's still a closed alpha? Me too.


My solution is to only let people who have a passphrase create an account. Thanks to a suggestion by Rob125 in freenode's #RubyOnRails channel, I found the easiest way to do this was to modify 3 files.




  1. Create a constant in your Rails::Initializer.run do |config| method (config/environment.rb):

    Note you'll need to restart your app server for this to take effect.

    MY_SECRET_PHRASE = "econometrics is fun!"


  2. Add a textbox to your signup form (app/views/users/new.html.erb if using restful_authentication):

    <p>What's the passphrase?<br />
    <%= text_field_tag 'passphrase' %></p>


  3. Compare the constant to your parameter in the create method of the Users controller (app/controllers/users_controller.rb):

    @secret = params[:passphrase]
    if @secret == MY_SECRET_PHRASE then
      #all that successful stuff...
    else
      #all that flash[:error] stuff...



And there you have it. Easy to only allow access to a few a people while testing and easy to get rid of when it's time to truly go live. There is a drawback in the form of overhead that comes with an initializer, but certainly it's a fair tradeoff for a quick solution to a temporary problem.

Saturday, March 13, 2010

PostgreSQL 'permissions error'

Too many times now I've searched high and low to find the cause of my PostgreSQL 'permissions' error. Certainly it didn't make sense...I had created a superuser and a database with that superuser; PostgreSQL was started;

dropdb dbname
would drop the database and
createdb dbname
would create the database. What was wrong?



In each case I had forgotten something so basic most guides seem to leave it out:

initdb
If you're running Ubuntu and installed PostgreSQL through apt-get, you'll need to initialize your database with the commands below as the user you want to own the data. Generally I make a login with the same name as a database superuser to simplify things.



  • Note the folder must also be owned by the user who will own the data.

  • Note multiple databases can be stored in this datafolder.


/usr/lib/postgresql/8.4/bin/initdb -D /path/to/datafolder

Friday, March 12, 2010

Ubuntu Karmic 9.10 and ruby-pg

PostgreSQL, Ubuntu, and Rails seem exactly like a winning combination for the type of work I do.

As of this writing (March 12, 2010), the best driver to connect Rails and PostgreSQL is easily ruby-pg. My development environment is Snow Leopard, and my troubles installing ruby-pg on OS X were alleviated by this very helpful article at icoretech.

My server environment of choice is Ubuntu. Getting things up and running had me stumbling a bit again with the ruby-pg gem driver. Turns out the solution is simple -- just install the postgresql-server-dev-8.4 package and the gem installs fine.

The following steps will have PostgreSQL 8.4 and the ruby-pg driver up and running on your Ubuntu Karmic system (assuming you've already installed Ruby)

sudo apt-get install postgresql
sudo apt-get install postgresql-server-dev-8.4
sudo gem install pg