niedziela, 27 marca 2011

Kick-ass language

Haskell was made by some really smart guys (with PhDs) (...) when a committee of researchers got together to design a kick-ass language.
- Miran Lipovaca "Learn You a Haskell for Great Good!: A Beginner's Guide" (No Starch Press, 2011)

środa, 23 marca 2011

Rails3: respond_with i pusty json/xml/yaml przy update

W Rails3 możemy zamiast (bardzo "nie-DRY") bloku respond_to:

class ArticlesController < ApplicationController
# GET /articles.json
# GET /articles.xml
def index
@articles = Article.all
respond_to do |format|
format.json { render json: @articles }
format.xml { render xml: @articles }
end
end
end

możemy ustawić raz w jaki sposób chcemy odpowiadać na request:

class ArticlesController < ApplicationController
respond_to :json, :xml
# GET /articles.json
# GET /articlel.xml
def index
respond_with @articles = Article.all
end
end
dużo ładniej prawda? :)

Jest jednak jeden problem - przy akcji update (czy każdej innej do której dostajemy się poprzez PUT) Rails zwraca status OK (200) zamiast zmienionego obiektu. Może czasem to dobrze, możliwe, że to nawet bardziej zgodne ze specyfikacją REST, tego nie wiem. Wiem tyle, że potrzebowałem aby update zwracało obiekt. Można zrobić to na 3 sposoby - dobry, okropny, jeszcze gorszy :) Zacznijmy od końca. Możemy w każdej akcji używać:

# GET /articles.json
# GET /articles.xml
def index
@articles = Article.all
render json: @articles
end
view raw index.rb hosted with ❤ by GitHub

Skazujemy się jednak na jeden format zwracanych danych. Możemy wrócić znowu do bloku respond_to (to jest opcja nr.2 - okropna), lub nadpisać responder.

Tworzymy plik application_responder.rb w katalogu lib/

class ApplicationResponder < ActionController::Responder
def api_behavior(error)
raise error unless resourceful?
if get?
display resource
elsif has_errors?
display resource.errors, :status => :unprocessable_entity
elsif post?
display resource, :status => :created, :location => api_location
elsif put?
display resource, status: :ok, location: api_location
elsif has_empty_resource_definition?
display empty_resource, :status => :ok
else
display head(:ok)
end
end
end

Następnie zmieniamy ApplicationController (app/controllers/application_controller.rb):

require 'application_responder'
class ApplicationController < ActionController::Base
protect_from_forgery
self.responder = ApplicationResponder
end

Dzięki temu możemy w akcji update używać respond_with:

# PUT /articles/1.json
# PUT /articles/1.xml
def update
article = Article.find(params[:id])
article.update_attributes(params[:article])
respond_with article
end

niedziela, 20 marca 2011

Za granicą nie wiedzą co to ułamki zwykłe?

Książka Programming in Scala, rozdział Functional Objects, paragraf A specification of class Rational. Czytamy:


A rational number is a number that can be expressed as a ratio n/d , where `n` and `d` are integers, except that `d` cannot be zero. `n` is called the numerator and `d` the denominator. Examples of rational numbers are 1/2 , 2/3 , 112/239 , and 2/1 . Compared to floating-point numbers, rational numbers have the advantage that fractions are represented exactly, without rounding or approximation. 

The class we’ll design in this chapter must model the behavior of rational numbers, including allowing them to be added, subtracted, multiplied, and divided. To add two rationals, you must first obtain a common denominator, then add the two numerators. For example, to add 1/2 + 2/3 , you multiply both parts of the left operand by 3 and both parts of the right operand by 2, which gives you 3/6 + 4/6 . Adding the two numerators yields the result, 7/6 . To multiply two rational numbers, you can simply multiply their numerators and multiply their denominators. Thus, 1/2 ∗ 2/5 gives 2/10 , which can be represented more compactly in its "normalized" form as 1/5 . You divide by swapping the numerator and denominator of the right operand and then multiplying. For instance 1/2 / 3/5 is the same as 1/2 ∗ 5/3 , or 5/6 .

Właśnie, dzięki uprzejmości Panów Ordersky, Spoon, Venners dowiedzieliśmy się czym są ułamki zwykłe, jak je dodawać, mnożyć, dzielić i (uwaga!) skracać. Naprawdę żałuję, że zabrakło miejsca żeby nauczyć nas jak je odejmować.

Zawsze wydawało mi się, że programiści to w miarę inteligentne osoby. Przy okazji raczej uzdolnione matematycznie i w ogóle. Czyżby w Szwajcarii (skąd pochodzi Martin Odersky) ułamki zwykłe były wiedzą tajemną znaną garstce wybrańców (czytaj studentów  École Polytechnique Fédérale de Lausanne, na której M. Ordersky wykłada)?


Mówiąc szczerze bardzo rozbawił mnie ten fragment :)


"Hasło nie zawiera wymaganych znaków"

No cholera jasna! Dajcie mi spokój!


Jak chcę mieć banalne hasło, które złamie pierwszy lepszy piątoklasista to moja sprawa. Chcę mieć 3 znaki? OKEJ. Chcę mieć same cyfry? OKEJ. Chcę mieć hasło w typu #$$%$%&^$#$#%#@@$$%%? OKEJ.

To moja sprawa jakie mam hasło!!!

wtorek, 8 marca 2011

django-piston: Przesyłanie dodatkowych parametrów POST do metody BaseHandler.create()

django-piston daje nam 4 podstawowe metody REST: read(), create(), update(), delete(). Są one bardzo proste i najczęściej musimy je nadpisać, aby uzyskać chciany przez nas efekt.

Metoda create() korzysta domyślnie tylko z parametrów przesłanych w POST (request.POST). Chcąc przesłać do niej dodatkowy parametr lub zmienić istniejący musimy nadpisać całą metodę (i tak kilka razy dla każdego Handlera używajacego create()). Chyba że nadpiszemy tą metodę raz (najlepiej w osobnym pliku, chociażby: libs.my_piston.handler.), aby przyjmowała dodatkowy słownik jako parametr funkcji i (jeżeli zostanie podany) dodała go do słownika pobranego z request.POST.

from piston.handler import BaseHandler
from piston.utils import rc
class DummyBaseHandler(BaseHandler):
'''
`DummyBaseHandler`
'''
model = None
def create(self, request, *args, **kwargs):
'''
Almost copy of `piston.handler.BaseHandler.create()`
It gets one more attribute: `attrs` (dict)
`attrs` contains fields for model. If it's not given simple `request.POST` is used.
'''
if not self.has_model():
return rc.NOT_IMPLEMENTED
attrs = self.flatten_dict(request.POST)
# my changes [start]
if 'attrs' in kwargs:
attrs.update(kwargs['attrs'])
# my changes [end]
try:
inst = self.model.objects.get(**attrs)
return rc.DUPLICATE_ENTRY
except self.model.DoesNotExist:
inst = self.model(**attrs)
inst.save()
return inst
except self.model.MultipleObjectsReturned:
return rc.DUPLICATE_ENTRY
class BaseHandler(DummyBaseHandler): pass

Teraz w naszym handlerze:

from piston.utils import validate
from libs.my_piston.handler import BaseHandler
from apps.task.models import Task
from apps.task.forms import TaskForm
class TaskBaseHandler(BaseHandler):
model = Task
class TaskHandler(TaskBaseHandler):
allowed_methods = ('POST',)
@validate(TaskForm)
def create(self, request, *args, **kwargs):
attrs = {
'user': request.user,
}
return super(TaskHandler, self).create(request, *args, attrs=attrs)
view raw task_handler.py hosted with ❤ by GitHub