The new way to do Cross Domain Ajax/Comet: Cross-Origin Resource Sharing![]() Wedoist features a realtime system that's powered by comet where the state is synced across all open sessions. Today we have rewritten our client side implementation to use Cross-Origin Resource Sharing, which is a relatively new technology to do cross domain communication. This technology is supported by following browsers:
Implementing comet is a challenge, both on the backend and the client side. In this blog post I will only feature the client side implementation. How is our comet setup?comet0X.wedoist.com points to a nginx server that proxies to JBoss Netty servers. This makes it possible to support SSL and load balance. This solution scales really well, but we need to do cross-domain requests to comet0X.wedoist.com. For this we used ScriptCommunicator, which is one of my more popular open-source projects. It basically uses script tags for communication and it resolves to hacks in order to detect errors. Using Cross-Origin Resource SharingThe main points of Cross-Origin Resource Sharing are following:
Our implementation of this strategy is shared below. Specifying Access-Control-Allow-OriginYou need to specify what origins you are accepting requests from and you do this by adding a special Access-Control-Allow-Origin header. In nginx you can do following to only accept from https://wedoist.com;
location / {
add_header Access-Control-Allow-Origin https://wedoist.com;
proxy_pass http://0.0.0.0:8001/;
}
To accept from everywhere you would do this (it isn't recommended to due CSRF exploits!):
location / {
add_header Access-Control-Allow-Origin *;
proxy_pass http://0.0.0.0:8001/;
}
Implementation of the new script communicator
You can download and fork the code here: Using the new script communicatorTo use the new script communicator simply do following: var url = 'http://some_domain.com/give_me_js_back.php?i=42&callback=foocall'
ScriptCommunicator.sourceJavascript(url, on_success, on_error)
on_success and on_error are functions. on_error is only supported by browser that support Cross-Origin Resource Sharing, i.e.:
Like the old ScriptCommunicator implementation your server should return JavaScript that can be eval'ed. References
Why we picked Amazon AWS over Rackspace and dedicated hosting![]() Last saturday we switched Wedoist from Rackspace's Cloud to Amazon AWS. In this post I will present why we have done this and why we picked Amazon AWS instead of Rackspace's Cloud or dedicated hosting. Why not Rackspace's Cloud?We initially used Rackspace because we thought they had lots of experience and expertise in hosting. Now we are not so sure and we don't have a huge trust in their systems or their network. Our problem was that a lot of users had reported performance issues. Wedoist is used in about 50 countries and each time we debugged these issues they were from users that came from "remote countries" such as Brazil, Hong Kong or Taiwan. Having developed web-applications for a long time we were sure it was a network issue and even I could reproduce this issue in Chile. This was our main reason for moving to Amazon AWS: Rackspace's network does not seem to work that well in "remote areas" of the world. The second reason is that Amazon AWS seems to be a superior product that offers more solutions such as:
Amazon AWS also seems to be in rapid development, for example, Amazon DynamoDB was released last week (and it seems to be an awesome database solution!) Why not dedicated hosting?You will get a lot more hardware by going for dedicated hosting (either buying or leasing servers). For example, if you read pinboard's article on dedicated hosting, A Short Rant About Hosting, you will find prices that are much cheaper than Amazon's. Here are our reasons why we didn't go the dedicated hosting route:
Cost isn't everything:
We don't have resources to use multiple data centers:
We can easily scale:
Our focus isn't on managing servers:
Amazon AWS seems to be a marketleader Model View Controller Rewrite (JavaScript)
I think it's worthwhile rewriting large amounts of code if it adds significant advantage such as cleaner and more maintainable codebase. I have taken some screenshots along the way to document how I do a rewrite.
The contextIn 2006, when I started Todoist, MochiKit was one of the better JavaScript libraries. Today MochiKit is largely forgotten and the state of the art JavaScript libraries are backbone.js, spine.js or ember.js. All of these newer libraries use the Model-View-Controller (MVC) pattern, a pattern I have written about in the past: Model View Controller rewrite in JavaScript/CoffeeScriptI will now show a series of screenshots of what the problem is and how I find a solution that makes the code simpler and more maintainable. Each project in Todoist has a counter that shows how many tasks the project holds. In the old codebase there's one creator and 7 (yes 7!) update functions:
I could rewrite this to use fewer update functions or even just one, but I could also go an extra mile to ensure that such blunders do not happen in the future. The better way to do this is to use the MVC pattern: the basic idea is that we create a counter that autoupdates itself each time the model changes. I looked at how modern libraries handled this (I was largely inspired by backbone.js ) and I began scribbling down how an API could look like:
I did not want to use backbone.js directly as I don't like the way routes work or that it's dependent on underscore (adding backbone.js and underscore would result in 2000 lines of extra code). I also think I can learn more by implementing stuff myself and I can do a customized solution that works well with my existing code. After this step I began to implement a test to see if my API made sense:
I tried to use the new code for the counter (I had two solutions, either binding it to a project object or using ProjectsModel). I tried to use them both throughout some code to see how they would behave in different contexts:
Then I set these solutions against each other - - reflecting on pros and cons of each solution:
The solution I ended up with was following:
Would love to hear about how you attack a rewrite. Happy hacking! Hiring CoffeeScript and Python programmers
We are expanding the team at Todoist and Wedoist with CoffeeScript and Python programmers.
Some of our stats:
Join us either freelance or full-time and work on something that makes the world more productive. Send your resume to amix@amix.dk, be sure to include some code you are proud of (or a link to your GitHub/BitBucket profile). Get 50% off Todoist Premium, for a limited time only
For the next 72 hours only, Todoist Premium is 50% off!
Want to become more productive in 2012? Upgrade for $14/year at todoist.com/premiumPromotion. Todoist is espeically powerful with browser plugins (that integrate nicely wtih Gmail). Check out: vimgrep: Searching through multiple file extensions
Here's a neat trick you can do with vimgrep to search through multiple file extensions (for example, finding a reference in all .js and .coffee files).
To find Users in just .js files recursively you would do: :vimgrep /Users/ **/*.js To find Users in .js and .coffee files recursively you would do: :vimgrep /Users/ **/*.js **/*.coffee This is super useful when you are refactoring code. I am unsure if Ack.vim can do something similar. If you know, please leave it as a comment! You can check out some of my other Vim tips here. Todoist and Wedoist frontpage redesigns
When I started Todoist in 2007 I didn't know much about design. I didn't think much about it and I mostly cared about functional products that solved problems.
Over the years something has changed in me and I am currently obsessed with design. I also think design applies to everything - - from clothing, to code, to food. And design isn't only about how it looks like, but also how it works. I think my change has been fueled by Apple's amazing comeback. One of Apple's biggest differentiating factors is their amazing designs. They built the biggest technology company based on great designs - outperforming companies that solely focused on functionality.
Here is a redesign of Todoist's and Wedoist's fronpages. It's not live yet, but will be live during the next week:
Today's entrepreneurs are spoiled brats
This is a comment I left on Hacker News covering Herval's complaints about Start-Up Chile:
Pythonic JavaScript to CoffeeScript compiler
I am rewriting some of my old JavaScript code into CoffeeScript. I feel more happy in CoffeeScript and I think I produce more readable and more maintainable code. It also offers some great features such as classes. To help me on this transition I use js2coffee which compiles JavaScript to CoffeeScript. I have made an extension of js2coffee that is a bit more Pythonic (produces Python looking CoffeeScript code :-)).
I have before covered CoffeeScript and you should read my post if you don't know what CoffeeScript is: The Pythonic extension of js2coffeejs2coffee is great, but I don't like some of the code it produces as it's more similar to Ruby than Python. I have forked js2coffee and provided a --pythonic option which does following things:
Here's a little showcase of what --pythonic does different: $ ~/> cat test.'sThe JavaScript code we are compiling to CoffeeScript: function helloWorld(some_value) {
if(some_value != 'hello')
return;
aFunctionCall('hello');
return "value";
}
$ js2coffee test.jsCompiling it via the standard js2coffee produces following code: helloWorld = (some_value) ->
return unless some_value is "hello"
aFunctionCall "hello"
"value"
$ js2coffee --pythonic test.jsCompiling it with the --pythonic option produced following code - - which for me is easier to understand given my Python background: helloWorld = (some_value) ->
return if some_value isnt "hello"
aFunctionCall("hello")
return "value"
Google Closure + CoffeeScript?Another project I am currently working on is to improve CoffeeScript's compiler so it can work with Google Closure. What this enables is to write high-level code in CoffeeScript that gets minimized and optimized by Google Closure's compiler (which does non-trivial code transformations and optimizations). There's already a project that does something similar to this:
Code
·
Code improvement
·
Code rewrite
·
Design
·
JavaScript
·
node.js
•
Permanent link
•
28. Dec 2011
Books that I read (new section)
As a new section I will link (and maybe review) books that I read or want to read.
Currently reading: Autobiography of a Yogi by Paramahansa Yogananda
Following Ask HN: Best book you read in 2011 I have added following to my reading list: The Undiscovered Self by Carl G. Jung
The Checklist Manifesto: How to Get Things Right by Atul Gawande
Filtering through vimgrep results using regular expressions
Here's a neat trick you can do with vimgrep / ack.vim to filter through results using regular expressions. This is super useful when doing a refactoring. Let me show an example of how I used this trick recently.
I wanted to rename todoist.users to todoist.apps.sessions, since it was a more appropriate name. Here's how I made sure that all the imports got updated: 1. Search for all occurrences of users in Python files, :vimgrep "users" **/*.py":
2. Show all results in a quick list window by typing :cope in command mode:
3. Copy the results of the quick list window into a new buffer. By for example typing gg => V => G => y => :ene => p 4. Delete all lines that have models in them (as we want to ignore todoist.models.users). You do this by typing :g/models/d. 5. Delete all lines that don't have import in them (we want only to focus on import lines). You do this by typing :g!/import/d After this step I only see results that are relevant for my refactoring. A bonus for you vimrcAdd this to your .vimrc to quickly transform your quicklist into a new buffer that's syntax highlighted: map <leader>cc :botright cope<cr> map <leader>co ggVGy:tabnew<cr>:set syntax=qf<cr>pgg Anytime you press leader + co in a quicklist window it will isolate the quicklist in a new tab and syntax highlight it. py_static_check: Statically check your Python code for errors
I have released py_static_check, an useful tool that can statically check your Python code for common errors. Statically implies that the code isn't evaluated.
py_static_check is based on pyflakes and adds following features:
To install it do following: $ sudo easy_install py_static_check To fork it (and improve it) go to github: Here are some of the things py_static_check can do. Catch undefined names, even for star importsExample code: from os import *
def function_with_error():
print path
print paths
star_imports.py: import os
STAR_IMPORTS = {
'os': os.__all__,
}
Running it with py_static_check (with -s argument): $ py_static_check -s tests/star_import.py tests/undefined_name_star.py tests/undefined_name.py:5: undefined name 'paths Running it with pyflakes: $ pyflakes tests/undefined_name_star.py tests/undefined_name_star.py:6: 'from os import *' used; unable to detect undefined names Ignore not used warningsExample code: from os import path
Running it with py_static_check (with -i argument): $ py_static_check -i tests/ignore_not_used.py $ py_static_check tests/ignore_not_used.py tests/ignore_not_used.py:10: 'path' imported but unused Assigned but never usedLike pyflakes it can catch a lot of errors, such as defining a variable without using it. Example code: def some_function():
def inner_fn():
local_var = ""
Running it with py_static_check: $ py_static_check tests/assigned_but_never_used.py tests/assigned_but_never_used.py:8: local variable 'local_var' is assigned to but never used
Announcements
·
Code
·
Code improvement
·
Code rewrite
·
Python
·
Tips
•
Permanent link
•
19. Dec 2011
Making ugly code more beautiful using Python's with statement
I have before featured Python 2.5's with statement and I am using it more and more. It makes code more readable and beatiful. Yesterday I used it in a beautiful way and I want to highlight it here.
My task was to ensure that the correct language is set when sending out emails. My first iteration used following code: #Remember the current language
current_lang = get_current_lang()
#Set language by uid if possible
user_lang = get_lang_by_uid(uid)
if user_lang:
set_current_lang(user_lang)
#… MAIL CODE …
#Revert back to old language
set_current_lang(current_lang)
Now this is fine and well, but not that clean, especially when you have to use it in multiple places. I knew there must be a better way to do this. After some iterations I came up with this marvel: with set_lang_by_uid(uid):
#… MAIL CODE …
A one liner that I can use everywhere! The implementation of set_lang_by_uidImplementing set_lang_by_uid is trivial - that's the great thing about using the with statement! Here's how it's implemented: from contextlib import contextmanager
@contextmanager
def set_lang_by_uid(uid):
user_lang = get_lang_by_uid(uid)
if user_lang:
current_lang = get_current_lang()
set_current_lang(user_lang)
yield
set_current_lang(current_lang)
else:
yield
More referencesCheck out my older writing on the with statement: |
|