08 March 2011

Migrate Attachment_fu to CarrierWave : UploaderFu

Carrierwave is my favorite gem for handling uploads and image thumbnail generation (attachment_fu died of old age and paperclip suffers from hashitis, not a pretty picture)

Anyway our imagebank was still on attachment_fu even though I upgraded to rails3 months ago. We couldn't add images and going through some dirty hacks to resuscitate attachment_fu was too much black magic.

So CarrierWave it was.

But how to stick to attachment_fu's naming convention. CarrierWave has a nice and clean way to migrate from paperclip but none from attachment_fu, because J. Nicklas claims, they're too far a part.

Well here's a little module you can include in your uploader and adapt to your needs.

Any Questions ?

15 February 2011

Some (old) thoughts on Mongoid

I am pasting a post I wrote a year ago after discovering mongoid on hashrockets liveshow and beeing very excited yet disappointed by the idea mongoid is for hierarchy only.... The reason is I planning to write another one soon and want to expand a little on this one.
(disclaimer end)

Lets take the canonical Book app.
My Book app is very book centric, and for many good/bad reasons I do not want my books to be embedded in the Author class.

2 solutions :

  1. the SQLlike solution, where book has the reference author_id and Author is a seperate collection. The problem is you can't do joins, so finding conditions with association (eg books where author.age <>
  2. the Document Oriented solution, where the Author is embedded in Books. After all why not, you can live with that level of duplication if its just a name. But wait you want to add the Author's bio, date of birth, and plenty more attributes... Yup Author really is a collection by itself, it can't just be embedded or it becomes a consistency nightmare.

So from there one possible move is keep the SQL set of mind and do some caching : on top of author_id you add the field author_name and Book pulls out the Author#name on a before_save callback so its available as a condition on Book.find and for direct display in your index files.

But that is stupid. We're still thinking like SQL junkies here, carefully denormalizing a little our data for performance... without the goodness of SQL! DocumentObject is half way through the SQL and the View, its like a show.erb file without the html tags plus all the power of Business Logic. It is not some poor Database intermediate pledging for a little content through complex ORMs asking you to
fullfill more queries before they indulge in providing it.

No ! The hole point of DocumentObject is to mirror the content of the ObjectModel as closely as possible. Which means if a Model is composed of another Model, it should be reflected in its data structure, and that is precisely what an embedded object is in mongoDB/mongoid.

In practice that means having Author stored both in an independent collection "authors" !!!!and!!!! embedded in Book. The embedded author would have a reference (e.g :master_id) to its couterpart in authors collection to keep in sync. You could then decide if the embedded is a light copy of its master or a full blown nested mirror... The main idea is you are not trying to shape your programm to the way your data is stored, you are bending the data to the way you domain model is
shaped, for the same reasons you chose Ruby over Java. Happiness :-)

Call it glorified caching if you wish, I think this could be a small
yet true paradigm shift. Why ?

  • first of all your domain model rules the data not the contrary !
  • you are not constrained anymore to hierarchy vs reference, they both complete themselves
  • you keep the consistency of the dot notation (no ugly author_name here)
  • caching is not an after thought, it is part of your model right from the beginning.
  • duplication may be a problem for some but consider it also like a new possiblity : Author can be tied for some attributes to its master (name, date_of_birth) but then the bio could change/or not depending on the parent's scope (book).

Of course one should not ignore the drawbacks, it would be poorly suited for a heavy writing model app. But I think that suddenly widens the horizon, you don't have to torture yourself figuring out which from mongomapper or mongoid is best suited for your app, or should i stick to SQL.... etc.

Here's some more thoughts :

You cannot avoid the performance hit on the writing part, so you should be able to decide which attributes are going to live on the embedded version to minor that. Those would typically be the ones with small footprints (booleans, integers, small strings, etc) unlikely updated, & usefull for aggregation or find conditions. To keep data in sync nothing extraordinnary : a timestamp on each embedded version to compare with the "master" timestamp versions. The DSL could look something like that :

class Books
has_one :author, :exclude =>["bio", "ratings"]

To go a little further I was imagining what a user's library app would look like.
In a SQL environment you'd have a library table with user_id, and book_id, and :

class User
has_many :books, :through => :library

whereas in mongoid :

class Library
has_one :user
has_many :books, :only => ["title", "author.name"] do
field :rating, integer
field :read, boolean
include Book::LibraryMethods #could be a convention

Each row of the library collection would contain all the users book. But those books wouldn't be a simple proxy of Book, but a striped out/ enhanced version, specially suited for the Library. The Developper should be encouraged to use the power of modules so that each set of methods are consistent with each scope.

class Author
has_many :books, :only =>["title", "publication_date"] do
field :writing_context
include Book::AuthorMethods

Another approach would be to simply dump the proxy design and go with modules. It strikes me that an embedded object in Mongo is very similar to a Ruby module : it only lives in the context of the class/docObject it is included in. That drifts us away from the current dsl of mongoid and I haven't put much thouth in yet. However I have a few more ideas. I'm sure we haven't yet scratched the surface of the doc object possibilities....

Meanwhile here's a some experimental code for accessing embedded association without a proxy : http://gist.github.com/285877


01 September 2009

Paypal Express & Active Merchant diagram

Paypal Express can be slightly confusing, specially when you want to stub the calls to the server for offline testing. So I made a quick and dirty "pense bete" to help myself, and thought I could share it. Tell me if you see anything wrong or unclear.

15 August 2009

Refactoring to REST : why context matters

The wrong path

The app I refactored licenses pictures. The licensee creates an order, selects pictures, finally submits the order, which is then reviewed by Mr licensor. The cart relied on on a current_order the same way current_user works (with a session id), to add or remove a picture from it.

A few months later we decided to extend the app to have automated invoices as well as payments online. Naturally I created an "Invoice" and a "Payment" model which both belonged to an "Order" with a "one to one relationship".

So, to access payments and invoices, I also relied on current_order.

That made sense at the begining : as soon as I accessed an Order#id it would check if the current_order was the same as the one found with params[:id] and if not, I updated it along with the cart.

def set_current_order
self.current_order==@order || self.current_cart= (self.current_order= @order).assets )

But what if i wanted to access a payment directly, without first going through the orders controller (e.g. in some email inviting a customer to pay) ? I would have to make sure the payment I'm accessing belongs to the user and then update current_order with @payment.order so I don't endup having any mismatch with a trailing current_order.

Not unsolvable but : ...

Bad guess !!! I should only access a payment through the order it belongs to. What I'm doing wrong here is setting the context (an order) after finding the object it lives in (it's payment).

The Importance of Context

Rule of thumb : get the context before the object it lives in !!!

Why you ask ? Because I could easily end up having a Payment view linking to an order it doesn't belong to.

That meant : instead of having links like this
link_to "payment", current_order.payment

...they should all be like this :
link_to "payment", [@order, :payment]

That way you have a consistent before_filter for every singleton (payment, invoice, items etc) belonging to an order.

Although the idea of having a single point of access through current_order seemed a good starting point, not translating it with the conventions REST resources suggest, had me tangled up in inconsistencies.

It might seem obvious to many of you, but with the :shallow=>true option you get in your routes, which enables access to a resource without its parent, one might forget why, in many cases, the context of a resource is more than relevant : necessary.

Next I will talk of form_for which doesn't play very well with a singleton resource.


04 August 2009

Tabbed Navigation for Rails

When I start a new rails app and write a tabbed menu, I always postpone the moment I'll determine how it's going to set the current tab. And when it comes to it, I usually do quick and dirty stuff with the controller name, the @category.name or whatever. But last time, facing a step by step form you could navigate through with tabs, I thought "enough of it I need a solution once and for all".

Sooo, looking at plugins first tabnav seemed to be the reference. But I wanted something i could easily tweek and understand, and this was just overkill for me needs.

Then I looked at the free for all tab helper which has some very interesting solutions, the most elegant being the one relying only on css and the body tag (no logic involved). But that wasn't flexible enough, they were all making the assumption the tabs would switch controllers.

So having to get my hands dirty I first refreshed my memory with my favourite source of hints and came up with what I believe is a very elegant and flexible solution.

This is minimalistic but you can see that it is very easily extendable. The main idea is that you are sendind to the tab class a matcher that is going to set the current tab.

My matcher was a bit complex as you can see below(it also sets the partial to render), but keep in mind that you could do exactly the same like this :

tab_for :controller => controller_name do |t|

and it works with no further logic.

What I appreciate most is that the logic for matching the tab is kept seperate ( it only relies on the params inside the links ) and the "tab_for do" syntax is very railsish & familiar.

30 April 2009

Couch Str0m and the Great Chicken

The hole Couchdb act as a Str0m sheds an interesting light on a notion I’m pretty fond of, because it partially reveals the mechanisms of community formation : it’s called intersubjectivity.

Before I start throwing Lacan at you, let me illustrate it with a (famous) joke : the guy thinks he’s a corn weed and is terrified he’ll be eaten by the giant chicken. He’s locked up in an asylum, follows a successful treatment and the doctors free him after 6 months. But as soon as he’s out the guy comes running back. Puzzled doctors ask : “What’s the matter, you know you’re not a corn weed don’t you ?”. “Yes I know it, but does the giant chicken know it ?”

What stroke me is that most of the posts/comments I stumbled upon while discovering all the rage were saying : “I’m not personnaly offended, don’t get mistaken by the idea i’m some prude, political correct fanatic, it’s just that i am offended for the people whom were offended”. So the question raises : who was really offended ? The giant chicken ?

As soon as you are in the social sphere, presuming people think a certain way almost automatically constrains your thoughts in shaping themselves like theirs. A portion of yourself might still whisper “these aren’t your own thoughts, you are not a corn seed”, and that voice may remain, but you’ll defend them and act upon them as if they were yours. The funny thing is that everybody is doing the same things which is acting upon the assumption the others think and believe a certain way.

Yes I just dicovered social convention, big deal ! More interestingly it is the fact that social convention is based on the implicit. By that i mean what is explicit is meant to be debated, questionned, opened to the games of construction & deconstruction under the knife of language. Implicit is sacred : as soon as someone breaks in and says “THE KING IS NAKED”, or “THE GREAT CHIKEN DOESN’T EXIST”which is throwing the implicit in the explicit, the community ciment cracks... violently.

It’s also very healthy.

18 April 2009

default_scope use case

Ryan Bate in rails 2.3 extras episode throws 2 sugestions on when to use default_scope (which is a sort of global named_scope, that can be overriden).
  1. default_scope :conditions => "deleted_at is not NULL"
  2. default_scope :order => "position DESC"

Ok thats fine but recently i've come up with a more interesting use.

I've built an online payment system for chaplin's image bank and going through all the hastle of getting ssl certificate, a new ip for the subdomain etc etc it certenly not something I wanted to repeat. (BTW thanks again to Ryan bates for the great serie of episodes on paypal). Well since I would be using it for other parts of charliechaplin.com domain I decided to share the payment table, invoice table and user table through differant rails application. That way I could also have an app gathering all the info for budget management.

Yes Rails modularization is definitely one aspect of the framework I was eager to explore at some point specially when it came to membership. However you still need to know if a payment comes from the image bank app, or the live perfomance app, or the funny hat app.

So instead of using STI with the risk of breaking things by renaming Payment with PhotoPayment or something, i brought default_scope in the game along with a before_save (or before_create if you prefer)

And voilĂ , nothing else to change.