Przy okazji tworzenia Traduko stwierdziliśmy, że fajnie by było, jakby linki umieszczone w tekstach, w tym permalinki do oryginałów, były pingowane. Z tego co się zorientowaliśmy w polskiej sieci trudno odnaleźć polskie produkty, które zaimplementowany miałyby pingback. Oczywiście, jest sporo blogów korzystających chociażby z Wordpressa, które właśnie dzięki niemu umożliwiają przesyłanie pingów - poprzez skrypt xmlrpc.php. Ja chciałbym poruszyć sedno sprawy - jak w prosty sposób zaimplementować Pingback w Ruby on Rails. Przede wszystkim przyda się lektura specyfikacji tegoż protokołu. Wyczytać w nim możemy, że komunikacja odbywa się na bazie protokołu XML-RPC, a serwer powinien posiadać metodę pingback.ping odpowiedzialną właśnie za pingi. To w niej należy umieścić wszelkie odpowiedzi na pingi, jak również operacje, które chcemy wykonać przy pingu (Wordpress z tego co zauważyłem dodaje komentarze pod odpowiednimi postami). Zajmę się zatem najpierw serwerem, później zaś przejdę do konstrukcji klienta.
Serwer
Zatem za co jest odpowiedzialny serwer? Przede wszystkim odbiera żądania do WebServices, odpowiada na nie i wykonuje odpowiednie operacje, które ma wykonywać ping, w moim przypadku było to tylko dodanie informacji do bazy, że taki ping zaistniał. Pierwszą rzeczą, którą musimy zrobić jest wygenerowanie nowego WebService’u odpowiedzialnego właśnie za pingowanie, choć możemy w nim również umieścić dowolne inne WS. Ja nazwałem to “przewrotnie” xmlrpc, choć tak naprawdę do metod można mieć dostęp również przez SOAP (generuje się przy tym plik wsdl [tzn. istnieje on tylko wirtualnie] dotyczący naszych serwisów). Zatem wywołujemy w naszym projekcie: ruby script/generate web_service Xmlrpc. Tworzy to nam kilka plików, m. in. xmlrpc_controller - odpowiedzialny za obsługę żądań, oraz xmlrpc_api (w katalogu apis) - zawierający API naszego WS. W ww. plikach utworzone są dwie klasy: XmlrpcController dziedziczący po ApplicationController oraz XmlrpcApi dziedziczący po ActionWebService::API::Base. By ping był dobrze dobrze nazwany (czyli wedle specyfikacji pingback.ping) musimy utworzyć jeszcze przynajmniej dwie klasy: Pingback dziedziczącą po ActionWebService::Base oraz PingbackApi dziedziczącą po ActionWebService::API::Base.
Naszemu kontrolerowi (XmlrpcController) aplikujemy trzy metody:
wsdl_service_name 'Xmlrpc'
web_service_dispatching_mode :layered
web_service :pingback, Pingback.new
Pierwsza nadaje nazwę naszym WS’om, druga przypisuje metodę dysponowania poszczególnymi WSami, ostatnia tworzy WS o nazwie pingback, za którą odpowiedzialna jest obiekt klasy Pingback. Więcej słów się należy metodzie web_service_dispatching_mode, bo to właśnie ona decyduje o tym, że nasza metoda ostatecznie będzie się nazywała pingback.ping. Otóż przyjmuje ona trzy możliwe wartości (symbole):
- direct - klasą odpowiedzialną za żądanie jest dany kontroler, a nazwą jest po prostu nazwa metody, którą zaimplementujemy (i podamy w API)
- delegated - klasą odpowiedzialną za żądanie jest zdefiniowana klasa, nazwą jest po prostu nazwa metody zdefiniowanej, natomiast adres do serwisu jest postaci: /api/serwis (gdyby nasz skrypt używać metody delegated to adresem do serwisu byłoby: /api/pingback)
- layered - klasą odpowiedzialną za żądanie jest zdefiniowana klasa, nazwa jest postaci: nazwa_serwisu.metoda (w naszym przypadku
pingback.ping); adresem dostępowym tak jak w przypadku direct’a jest po prostu /api (oczywiście należy pamiętać, że leży ono w kontrolerze Xmlrpc, zatem pełny adres to /xmlrpc/api)
Jak można łatwo zauważyć ja użyłem ostatniej metody, dzięki której uzyskać wyspecyfikowaną nazwę pingback.ping. Zajmijmy się teraz API naszego WS. Jak można wyczytać w specyfikacji Pingback 1.0 do korzystania z niego wystarczy zaimplementować metodę pingback.ping. Metoda ta “zjada” sourceURI (strona z której puszczamy pinga) i targetURI (strona do której puszczamy pinga), a następnie wypluwa albo błąd, albo informację o sukcesie (z tego co wyczytałem - dowolną). Definiujemy zatem w API PingbackApi metodę ping:
api_method :ping, :expects => [{'sourceURI' => :string}, {'targetURI' => :string}], :returns => [:string]
Jak łatwo się domyślić :ping to nazwa naszej metody (która zlayerowana będzie miała pożądaną postać), :expects powinien zwierać tablicę hashów z nazwami zmiennych i ich typami, a :returns wartość zwracaną przez metodę. Można przy tym używać struktur, a nie tylko typów prostych, jednak przy implementacji Pingbacku nie jest to nam potrzebne.
Nasza klasa XmlrpcApi powinna również wywoływać metodę inflect_names z zaaplikowanym false - można spróbować bez wywoływania, jednak może nie działać (inflection tworzy liczby mnogie w różnych miejscach, gdzie jest to logiczne wg twórców RoR ;)). Dalej wystarczy zaimplementować w klasie Pingback odpowiednią metodę (ping) i już właściwie cały WS będzie gotowy. Metodę ping przedstawię w następnej notce, tak samo zresztą jak WebService’owy scaffolding i klienta XML-RPC.