RINQ – Ruby Integrated Query Language
2008-04-12 Dodaj komentarz
Cele
Już od dłuższego czasu odchodzi się od pisania zapytań do baz danych poprzez pisanie zapytań SQL. Ma to wiele przyczyn, wśród nich można wymienić:
- niezgodność pomiędzy modelem relacyjnym a obiektowym
- brak przenośności – niestety SQL nie jest standardem, różne bazy danych mają własną składnię, często mocno się różniącą
- kolejny język do nauki
- zagrożenie SQL Injection
Wszystkie te problemy rozwiązuje Mapowanie Obiektowo-Relacyjne (ORM – Object-Relational Mapping). Lecz istnieje jeszcze jeden zagadnienie, którym warto by się zająć:
- obsługa wyłącznie baz danych
Istnieje wiele różnych źródeł danych, nie tylko relacyjne bazy danych. Odwołujemy się do nich bardzo często w ten sam sam sposób. Wybieramy tylko te elementy kolekcji które są nam potrzebne, wybieramy potrzebne pola elementów, sortujemy wedle zadanych kryteriów. Czy można korzystać z różnych danych w ten sam sposób?
LINQ – Language Integrated Query
LINQ to technika dostępna w .NET (np. z poziomu języka C#). Umożliwia ona tworzenie zapytań zintegrowanych z językiem z którego korzystamy.
Jej zadaniem jest ujednolicenie sposobu dostępu do danych z różnych źródeł. Przykładowe zapytania:
- Obiekty w aplikacji
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; var q = from n in numbers where n < 5 select n; [/sourcecode] </li> <li> <b>Dane XML</b> XElement doc = XElement.Load(@"numbers.xml"); var q = new XElement("numbers", from n in doc.Elements("number") where (int) n < 5 select new XElement("number", n) ) ); [/sourcecode] </li> <li> <b>Bazy danych</b> Database db = Database(...) var q = from n in db.numbers where n.number < 5 select new {n.number}; [/sourcecode] </li> </ul> <h1>Przegląd możliwości języka Ruby</h1> <p> Odpowiedniki LINQ da się stworzyć w wielu językach. Ale w Ruby będzie to implementacja nie tylko działająca ale też dająca możliwość tworzenia czytelnego kodu. </p> <h2>DSL - Domain-specific programming language</h2> <p> <b>Języki dziedzinowe</b> w odróżnieniu od <b>języków ogólnych</b> (takich jak C, Java czy Python) są wysokowyspecjalizowane i tworzone to rozwiązywania konkretnych problemów. Dzięki temu są dobrze dopasowane do określonych zadań, a przez to wygodne w stosowaniu. Pozwala to na przyśpieszenie pracy z nimi. </p> <p> Języki dziedzinowe wcale nie są czymś nowym. Stykamy się z nimi codzienne i wydają się nam tak oczywiste że nie dostrzegamy czym są. Warto więc wymienić kilka przykładów: </p> <ul> <li>znaki drogowe</li> <li>notacja muzyczna</li> <li>wyrażenia regularne</li> <li>pliki Makefile</li> </ul> <p> DSL jest bardzo często stosowany w języku Ruby. Przykładem tego może być ActiveRecord dostępny w Ruby On Rails. Przykładowy model: </p> class Post < ActiveRecord::Base has_many :comments has_and_belongs_to_many :tags end [/sourcecode] <h2>Metaprogramowanie</h2> <p> Metaprogramowanie dało by się streścić jako <i>programy piszące programy</i>. W Ruby, jako że jest on językiem dynamicznym, program może zmieniać nawet swój własny kod podczas działania. Najczęściej służy temu funkcja <i>eval</i>: </p> name = 'plus' op = '+' a = "def #{name}(a, b) a #{op} b end" eval(a) plus(1,2)
Symbole
'very_long_name'
:very_long_name
Symbole bywają (błędnie) nazywane lekkimi stringami. Bardzo często stosuje się je jako klucze w słownikach.
Atrybuty – settery, gettery
class Cos def a @a end def a=(value) @a = value end end
class Cos attr_accesor :a end
Pomijanie nawiasów w wywołaniu funkcji
c = Cos.new() c.a=(7) puts c.a() funkcja({'a'=>'Ala', b=>'Beata'}) inna_funkcja(3.14, {'a'=>'Ala'})
c = Cos.new c.a = 7 puts c.a funkcja 'a'=>'Ala', b=>'Beata' inna_funkcja 3.14, 'a'=>'Ala'
Jeśli nie spowoduje to niejednoznaczności to można usunąć nawiasy z wywołania funkcji. Dzięki temu oszczędza się kilku znaków oraz sprawia, że kod wygląda czytelniej.
Ruby nie posiada argumentów w postaci słów kluczowych tak jak Python. Można je jednak jest bardzo łatwo zastąpić używając słowników z pominiętymi nawiasami klamrowymi. W takich konstrukcjach bardzo często używa się symboli.
Otwarte moduły
module Kernel def query() puts 'this is query' end end
Moduły można łatwo modyfikować. Jeśli coś dodamy do modułu Kernel
Otwarte klasy
class Fixnum def hours self * 3600 end alias hour hours end Time.mktime(2006, 01, 01) + 14.hours
Metody można nie tylko dodawać ale też zamieniać (korzystając z aliasów).
Otwarte obiekty
a = 'hello' class <<a def to_s "<#{self}>" end end
Brakujące metody
class Cos def method_missing(method_name, *args) method_name = method_name.to_s if method_name[0..3] == 'say_' puts method_name[4..-1] else raise NoMethodError, "`#{method_name}' in #{self}" end end end
Domknięcia
def two_times() if block_given? yield yield end end two_times { puts 'Hello'} [1,2,3,4].delete_if {|a| a % 2 == 0}
RINQ
Pozwolę sobie przedstawić tu dwie propozycje propozycje RINQ. Są one autorstwa Stena Friedricha z Technische Fachhochschule Berlin.
Operatory zapytań
Pierwsza metoda przypomina trochę korzystanie z cout w C++, lub Django ORM. Wywołujemy po koleji ciąg funkcji z których każda zwraca ten sam obiekt, lecz zmodyfikowany przez to wywołanie.
customernames = customers. qwhere {|c| c.address.city == "Torun"}. qselect {|c| {:firstname => c.firstname, :lastname => c.lastname}}. qorderby {|c| c.lastname}
Wyrażenia zapytań
Tu wkracza DSL. Przykład takiej implementacji autorstwa Petera Vanbroekhovena jest How to create a Domain Specific Language? – Metaprogramming in Ruby.
customerfirstnames = query do qfrom :c => customers qwhere c.lastname == "Kowalski" qselect c.firstname end
Slajdy