labria’s ruby blog

random ruby/rails stuff

Freckle API.

one comment

UPD: Freckle updated the API and docs. The new ones are here.

I'm a Freckle user, and I love the service. The only thing I really miss is a simple client to store my time entries without visiting the site.

So, I sat down to write that simple client app, knowing Freckle has an API.

Alas, things aren't that simple. The only thing you can in fact do using the API description is to get the list of your projects. But after a bit of digging and trial and error i figured out some of the real API, and wanted to share it with anyone who might want to use it.

In all the examples yourfreckle is your Freckle domain name, and refet7vjpj01elltewf2fbqd9znkbh9 is your API key. All examples use JSON, but should work with XML too, just replace .json with .xml

Getting the list of projects. This is done by a simple GET request to http://yourfreckle.letsfreckle.com/api/projects.json?token=refet7vjpj01elltewf2fbqd9znkbh9, which will return a JSON array of projects in the form of

[
{"project": {"name": "Bibla", "updated_at": "2009-06-30T14:52:51Z", "account_id": 3279, "id": 5120,
"enabled": true, "user_id": null, "stepping": 15, "budget": null, "created_at": "2009-06-30T14:52:51Z"}},
{"project": {"name": "Inventure", "updated_at": "2009-06-30T14:02:15Z", "account_id": 3279, "id": 5114,
"enabled": true, "user_id": null, "stepping": 15, "budget": null, "created_at": "2009-06-30T14:02:15Z"}},
{"project": {"name": "OMD", "updated_at": "2009-06-30T14:27:20Z", "account_id": 3279, "id": 5118,
"enabled": true, "user_id": null, "stepping": 15, "budget": null, "created_at": "2009-06-30T14:27:20Z"}}
]
 

The significant bits here are the "name" and "id" fields. You will surely need both of them later on.

Getting the list of entries. This one is not quite what we want it to be. You can get only the last 100 entries, and no filters. To obtain this you need to do a GET request to http://yourfreckle.letsfreckle.com/api/entries.json?token=refet7vjpj01elltewf2fbqd9znkbh9, but you will only get the latest entries for all users on all projects. I found no way to filter them yet, the RESTful approach didn't work, so you have to filter them yourself in your app, if needed. The entries come back in the following form:

 
[{"entry": {"updated_at": "2009-07-10T18:34:16Z", "project_id": 5151, "billable": true, "minutes": 300, "date": "2009-07-10", "url": null, "id": 51177,
"time_to": null, "time_from": null, "description": "A sample entry description", "formatted_description": "A sample entry description", "user_id": 3635, "created_at": "2009-07-10T18:26:09Z"}},
{"entry": {"updated_at": "2009-07-11T02:03:37Z", "project_id": 5120, "billable": true, "minutes": 30, "date": "2009-07-10", "url": null, "id": 51253,
"time_to": null, "time_from": null, "description": "A second sample entry description", "formatted_description": "", "user_id": 3574, "created_at": "2009-07-11T02:03:37Z"}},
{"entry": {"updated_at": "2009-07-10T18:25:29Z", "project_id": 5151, "billable": true, "minutes": 120, "date": "2009-07-10", "url": null, "id": 51176,
"time_to": null, "time_from": null, "description": "A third sample entry description", "formatted_description": "A third sample entry description", "user_id": 3635, "created_at": "2009-07-10T18:25:29Z"}},
{"entry": {"updated_at": "2009-07-10T18:24:14Z", "project_id": 5151, "billable": true, "minutes": 120, "date": "2009-07-10", "url": null, "id": 51175,
"time_to": null, "time_from": null, "description": "And one more sample entry description", "formatted_description": "And one more sample entry description", "user_id": 3635, "created_at": "2009-07-10T18:24:14Z"}},
{"entry": {"updated_at": "2009-07-10T19:27:29Z", "project_id": 5112, "billable": true, "minutes": 60, "date": "2009-07-10", "url": null, "id": 51195,
"time_to": null, "time_from": null, "description": "haha haha", "formatted_description": "", "user_id": 3572, "created_at": "2009-07-10T19:27:29Z"}}...]
 

As you can see, entries don't mention project or user names, only reference them by id, so if you really wanna know who did what you need to do the user and project mappings yourself.

Getting the list of account users. GET to http://yourfreckle.letsfreckle.com/api/users.json?token=refet7vjpj01elltewf2fbqd9znkbh9, resulting in something like that:

 
[{"user": {"id": 3570, "first_name": "Alexey", "time_format": "hours_minutes", "login": "wolfson", "last_name": "Wolfson", "email": "wolfson@gmail.com"}},
{"user": {"id": 3572, "first_name": "Dmitry", "time_format": "fraction", "login": "labria", "last_name": "Krassovski", "email": "labria@startika.com"}},
{"user": {"id": 3571, "first_name": "Maxim", "time_format": "hours_minutes", "login": "boork", "last_name": "Boork", "email": "boork@gmail.com"}}
]

This one was simple, right? :)

Creating stuff. To create things you do POST requests with parameters in the request body. The parameters, contrary to the API description, are not JSON, but simple string params, in the form of object_name[property]=something&object_name[other_property]=something_else. This is the usual Rails way of sending parameters. Just don't forget to URL encode them before sending =)

Creating a project. POST to http://yourfreckle.letsfreckle.com/api/projects.json?token=refet7vjpj01elltewf2fbqd9znkbh9 with the body set to "project[name]=SomeProjectName". The reply body will be empty, just watch the HTTP code, 201 means you've created the project, anything else is probably an error (but don't expect to get any meaningful error description)

Creating an entry. This is where you need the project ids that you obtained earlier. POST to http://yourfreckle.letsfreckle.com/api/entries.json?token=refet7vjpj01elltewf2fbqd9znkbh9 with the body set to "entry[minutes]=15&entry[project_id]=5151&entry[user]=labria&entry[description]=foobar&entry[date]=2009-07-10". Here, project_id is the id of the project you want to post to, and user is the username of the user you want to post it to, everything else is quite obvious. Funny thing: using your own API key you can post entries for any user on your team.

Well, thats about it. There may be more stuff i don't yet know about, but the things described here should be enough to create a simple client application. You can't do much about reporting, as 100 entries is surely not enough, but I hope this improves in some way over time.

PS: Thanks to the Freckle team for the nice and easy to use service.

PPS: No, I didn't write any client app that does more than getting the list of your projects yet. I'll post a follow up when I do =)

PPPS: This by no means isn't a good technical description of the API, but it should get you going. Maybe I'll write a better one later.

Written by labria

July 11th, 2009 at 11:20 am

Posted in Rails

Weird RSpec practice.

leave a comment

I recently found myself doing a wierd thing. While writing specs i add this spec to the end of the file:

it "should fail" do
  raise "foo"
end

The reason to do it? Simple: every time i have all the specs passing in the current file, autospec begins running all the specs in my project, breaking my red-green cycle for about 30 seconds. Adding a failing spec prevents it from doing it, and speeds up my work.

Am I doing something wrong?

Written by labria

June 25th, 2009 at 3:06 am

Posted in Uncategorized

Tagged with ,

A new version of my First App. *updated*

leave a comment

After quite some time with the docs and google (and help on IRC from some a nice guy named Sidnicious) I finally made my app behave as it should, and now it shortens the URLs you drop on it (as was requested by one of my very few users).

UPD: It now has Growl integration! Hooray!

You can grab the new version here

Seems the last thing to do is to add some configuration (api key for instance).

Written by labria

April 28th, 2009 at 2:27 am

Posted in ObjC

Heroku pricing.

leave a comment

Heroku announced their pricing today. As with all rails-centric hosting solutions, the price is sky high. You can get a LOT more if you just go and get Amazon EC2 and set everything up yourself. Yes, they do provide you with automatic and painless scaling, but for the price difference I would prefer the pain of scaling, once i get to the point of needing it.

Anybody needs Amazon deployment? Feel free to contact me =)

Written by labria

April 24th, 2009 at 3:57 pm

Posted in Rails

My first Cocoa app.

leave a comment

I love Objective-C.

I understood that while writing my first app, a dead simple bit.ly client. The app is a bleeding beta yet, but it works.
The thing is simple, you copy a link, run the app (from the dock, its faster to do it that way), and you get a short link in your clipboard to paste.
One of the current limitations is that it only works with URLs without parameters. It will silently fail if the URL is too complex, leaving you with an empty clipboard =)

Update: The new version handles all urls, it seems.

The todo list is as follows:

  • Let the user change the api key used.
  • Handle all kinds of urls.
  • maybe: run in background monitoring the clipboard and shorten urls if found =)

Download it: cocobit.zip

Feedback is welcome =)

Written by labria

April 20th, 2009 at 8:04 pm

Posted in ObjC

At last!

leave a comment

They made it!
Phusion has released passenger with nginx support.
No more bloated apache installs, hooray!
I've been waiting for this since the day passenger was first released.
UPD: well, it has some issues as of now, but I'm sure it's all gonna be fixed soon enough!

Written by labria

April 17th, 2009 at 3:08 am

Posted in Uncategorized

Tagged with ,

Status update.

leave a comment

It's been awhile since I last wrote here. Quite a lot happened in the meantime.

I now work at a really nice russian company named Getalime. It's small, very friendly and cozy. Funny thing is that 2 weeks after joining it I moved to live in Israel, so I never met any of my coworkers =)

I've been involved in a lot of Rails (and even one Merb) projects. My first patch has been accepted to the Rails core. I'm one of the two first russian regular ruby podcasters, with 100+ RSS subscribers and around 400 listeners of each episode. I'm not sure if it's a lot or not, but I'm happy anyway =)

Now I've decided that it was about time to revive this blog, and so I will try! See you soon, i hope.

Written by labria

March 22nd, 2009 at 8:52 am

Posted in Editorial

WordPress migration.

leave a comment

Well, yes. I've done it. I betrayed ruby and rails, and migrated the blog to WordPress.

Mephisto is a nice try, but it's not as useable and powerful as the big player in blogging.

My boss said I should write my own blog in SmallTalk, which I'll probably do one day, but it's much longer to do than adding WordPress to my nginx+php-fastcgi setup =)

PS: I'll add feed redirection a bit later, for now no one will notice the migration for now

Written by labria

March 21st, 2009 at 8:27 pm

Posted in Editorial

Ajaxy rails docs.

leave a comment

I don’t know if i’m the only one with the problem, but i just couldn’t get the “doc:rails” rake task to work with the jaxdoc RDoc template. I finished by running rdoc by hand to create the docs, here’s the code Tell me if I forgot to exclude something.

Written by labria

May 10th, 2008 at 7:22 pm

Posted in Rails

Sharing sessions between Rails apps.

leave a comment

Sometimes (in my case 2 times) you may have more than one application running off the same database, partially sharing model code. In the first case I had 4 apps (one main and four satellites) running this way, but the userbase was not shared between them (most users didn’t actually know of the other apps), so common sessions were not needed. In the second case (a distrbuted file sharing network) users floated between the main site and satellites and I wanted to include flash[:notices] while redirecting them. As you know, flash messages are kept in the session, so i needed all the apps to share the session data for the user.

There are 2 things you need to do to share sessions (this applies when using subdomains, i’m not sure if it’s doable with totally different domains).

First: make all the apps use the same session key and secret by editing the environment.rb file:

 
  config.action_controller.session = {
    :session_key => '_your_session',
    :secret      => 'some_long_string_of_letters_and_numbers'
  }
 

This is done so all your apps recognize each others session data. Second, you need to alter the session_domain option of ActionController (in one of your initializers files):

 
  ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.update(:session_domain => ".yourdomain.com")
 

This one is to make the subdomains recognize the main domain’s cookies.

My problem, however, was that this setup worked only one way. I could set session variables in the main app and read from the satellite, but not the other way. As found out later, the problem is that the rails2 default session store is CookieStore. And cookies written by the top level domain can’t be altered by subdomains. To fix this i had to migrate to the ActiveRecord session store.

After a few hours of setting all of this up and testing, I decided that all of this was too much pain and security issues to be used in production, so I’ll just have another way of sending messages between the apps. But I also thought that someone may find this info useful (the CookieStore problem wasn’t evident to me), so I wanted to share it =)

Written by labria

March 27th, 2008 at 7:18 pm

Posted in Rails