Bedzie sie dzialo... Rails 3.1 i 3.2

Nadchodzące w ActiveRecord zmiany zwiastują niemałą rewolucje. Oczywiście zanim rewolucja nastąpi czeka nas jeszcze wydanie Rails 3.1 gdzie pojawią się wszystkie ostrzeżenia dotyczące metod które wylecą (a wylatuje ich naprawde dużo). Myśle ze w Rails 3.2 przestanie działać 99 % obecnych pluginów. Nowy system relacji mi sie podoba, przypomina to co prezentował Sequel ale wydaje sie być jeszcze bardziej niskopoziomowy (Sequel zawierał już wiele uproszczeń, tutaj tych uproszczeń nie ma).

Po więcej odsyłam tutaj:
http://m.onkey.org/2010/1/22/active-record-query-interface

Tymczasem jeśli ktoś chce spróbować to należy:

  1. Zainstalowac thor’a
  2. Ściągnąć edge rails (git://github.com/rails/rails.git) i zbudować gem’y (rake package) i zainstalowac activesupport, activemodel, activerecord (wszystkie gem’y po zbudowaniu powinny byc w kataogach pkg)
  3. Ściągnąc edge Arel (git://github.com/rails/arel.git) i zbudować gem (thor package) i zainstalować (gem install pkg/arel-2.0.pre.gem)

Przykładowy kod żeby wszystko ze sobą spiąć (dla MySQL):

[code]require ‘rubygems’
require ‘arel’
require ‘active_record’
require ‘logger’

ActiveRecord::Base.logger = Logger.new(‘arel.log’)

ActiveRecord::Base.configurations = {
‘sd’ => {
:adapter => ‘mysql’,
:username => ‘root’,
:encoding => ‘utf8’,
:database => ‘baza’,
}
}

ActiveRecord::Base.establish_connection ‘sd’

Arel::Table.engine = Arel::Sql::Engine.new(ActiveRecord::Base)[/code]
Osobiście bardzo podoba mi się nowy system budowania zapytań. Oto prosty przykład budowania relacji tabeli users z tabelą locations i prostego warunku OR:

users = Table(:users) locations = Table(:locations) query = users.join(locations).on(users[:location_id].eq(locations[:id])).where(users[:name].eq('pkondzior').or(users[:name].eq('rgrabowski')))
Okres przejściowy będzie naprawde trudny dla wielu aplikacji, ale myśle ze nowy system usprawni mocno pisanie złożonych zapytań i warunków. Każde takie zapytanie można bez problemu enumerować, dobudowywać kolejne jego części lub po porstu zamienić na sql metodą #to_sql. Jest też możliwość dopiywania opcji poprzez Hash #apply_finder_options jeśli ktoś już naprawde będzie musiał :wink:

Dodam tylko ze wzoruja sie chyba na API SQLAlchemy z pythona.

Jeszcze w temacie… przykład użycia nowego API w Model’ach wygląda jak narazie troche topornie:

Location.where(Location.arel_table[:name].eq("Washington")).to_sql => SELECT `locations`.* FROM `locations` WHERE (`locations`.`name` = 'Washington')

nie kombinuj za mocno: “przykład użycia nowego API w modelach” wygląda zdecydowanie lepiej ;).

Co do tych wszystkich nowinek, to jest to fajne że starają się schować głębiej SQL. A jak to na prawdę będzie działać to zobaczymy.

[quote=PaK]Jeszcze w temacie… przykład użycia nowego API w Model’ach wygląda jak narazie troche topornie:

Location.where(Location.arel_table[:name].eq("Washington")).to_sql => SELECT `locations`.* FROM `locations` WHERE (`locations`.`name` = 'Washington')
[/quote]
Ja cały czas właśnie czekam na jakąś sensowną propozycję conditions zrobione w ActiveRecord (tzn. bez instalowania pluginów i ze wsparciem rails core team), które pozwolą na OR bez uciekania do stringów.

Czekam na to samo… i zapowiada sie obiecująco. Perspektywa Arel wygląda to przejrzyście:

users = Table(:users) users.project(users[:id]).where(users[:name].eq("pkondzior").or(users[:name].matches("%grabowski%")).and(users[:active].eq(true))).to_sql => "SELECT `users`.`id` FROM `users` WHERE ((`users`.`name` = 'pkondzior' OR `users`.`name` LIKE '%grabowski%') AND `users`.`active` = 1)"
Ale ActiveRecord ma to troche dziwnie obudowane:

User.where(User.arel_table[:name].eq("pkondzior").or(User.arel_table[:name].matches("%grabowski%")).and(User.arel_table[:active].eq(true))).to_sql => "SELECT `users`.* FROM `users` WHERE (((`users`.`name` = 'pkondzior' OR `users`.`name` LIKE '%grabowski%') AND `users`.`active` = 1))"
Natomiast naprawde topornie jest obecnie przepisywany Hash na Arel:

User.where(:name => 'pkondzior')

Przechodzi w:

User.where(Arel::Attribute.new(User.unscoped, :name).eq("pkondzior")).to_sql => "SELECT `users`.* FROM `users` WHERE (`users`.`name` = 'pkondzior')"
Nie mam pojęcia dlaczego obecnie jest to generowane właśnie w taki sposób. Chyba aktualnie nie mają jeszcze pomysłu jak udostępnić nowe API Arel w ActiveRecord

[quote=PaK]User.where(:name => 'pkondzior')
Przechodzi w:

User.where(Arel::Attribute.new(User.unscoped, :name).eq("pkondzior")).to_sql => "SELECT `users`.* FROM `users` WHERE (`users`.`name` = 'pkondzior')"
Nie mam pojęcia dlaczego obecnie jest to generowane właśnie w taki sposób. Chyba aktualnie nie mają jeszcze pomysłu jak udostępnić nowe API Arel w ActiveRecord[/quote]
Ale syf. Przypominają mi się czasy php i propela. Jeśli coś takiego miałoby zostać to… nie wiem co powiedzieć. Zaczynam wierzyć w koniec świata w 2012 roku ;).

[quote=radarek][quote=PaK]User.where(:name => 'pkondzior')
Przechodzi w:

User.where(Arel::Attribute.new(User.unscoped, :name).eq("pkondzior")).to_sql => "SELECT `users`.* FROM `users` WHERE (`users`.`name` = 'pkondzior')"
Nie mam pojęcia dlaczego obecnie jest to generowane właśnie w taki sposób. Chyba aktualnie nie mają jeszcze pomysłu jak udostępnić nowe API Arel w ActiveRecord[/quote]
Ale syf. Przypominają mi się czasy php i propela. Jeśli coś takiego miałoby zostać to… nie wiem co powiedzieć. Zaczynam wierzyć w koniec świata w 2012 roku ;).[/quote]
Warto chyba nadmienić, że ten kod to “niskopoziomowy” framework Arel z którego ma korzystać ActiveRecord więc użytkownik Rails’ów nieczęsto będzie musiał bezpośrednio z niego korzystać (w domyśle tak często jak teraz korzysta z find_by_sql np.) IMO chodzi o wyeliminowanie budowania zapytań sklejając ze sobą stringi, które jest obecnie w kodzie AR opierając framework AR na frameworku Arel - czy coś pomieszałem ? :slight_smile: Wg. mnie będzie lepiej o ile jakimś dziwnym trafem nie zostaniemy zmuszeni do korzystania z API przypominającego JPA (patrząc na to co się dzieje z Rails 3 i w którą stronę wszystko zmierza jestem przekonany, że nie zostaniemy zmuszeni :wink: )

Dokładnie tak. Wszystko co do tej pory można było robic z find() będzie nadal dostępne pod postacią where() z tą różnicą że będzie to tłumaczone na zapytanie dopiero w bibliotece ARel a nie tak jak do tej pory w ActiveRecord. Już teraz kod AR jest w owiele lepszym staniem IMHO po podłączeniu tego ARel, AR 3.2 będzie już zupełnie innym ActiveRecord.

Te przykłady które podałem to nie jest ZALECANE stosowanie, to jest coś co ja wyciągnołem z kodu żeby pokazać jak to mniejwięcej teraz działa. Ja osobiście mam nadzieje ze nowe API podniesie troche jakość złożonych zapytań i edge cases przestaną wyratać jak grzyby po deszczu :wink: No ale pewnie jestem tutaj zbyt dużym optymistą…

[quote=radarek][quote=PaK]User.where(:name => 'pkondzior')
Przechodzi w:

User.where(Arel::Attribute.new(User.unscoped, :name).eq("pkondzior")).to_sql => "SELECT `users`.* FROM `users` WHERE (`users`.`name` = 'pkondzior')"
Nie mam pojęcia dlaczego obecnie jest to generowane właśnie w taki sposób. Chyba aktualnie nie mają jeszcze pomysłu jak udostępnić nowe API Arel w ActiveRecord[/quote]
Ale syf. Przypominają mi się czasy php i propela. Jeśli coś takiego miałoby zostać to… nie wiem co powiedzieć. Zaczynam wierzyć w koniec świata w 2012 roku ;).[/quote]
Dramatyzujesz :stuck_out_tongue: pokazałem tylko jak obecnie AR składa zapytania :wink: Jeśli tylko znajdą sposób na łatwiejsze przedstawianie tych atrybutów ARel w kodzie AR to nie będzie tak źle. Jeśli byś wzioł teraz Ruby TreeParser to napisanie zajebiste DSL’a na bazie czegoś takiego staje sie 100x prostrze :wink: To samo tyczy sie każdej innej biblioteki typu Squirrel, Searchlogic które zazwyczaj wymyślały swoje mechanizmy łączenia kodu SQL od nowa.

PaK, przecież napisałeś że to przykład użycia nowego API w modelach :>. Zmyliłeś mnie.

Przykład użycia nowego API Arel z poziomu Modelu :wink:
Model#arel_table, Model#arel_engine nie należa jeszcze do oficjalnego API ActiveRecord, to samo tyczy sie chyba Model#unscoped. Natomiast Arel::Attribute.new(Model#unscoped, :column_name) to już API ARel z wykorzystaniem ActiveRecord, faktycznie troche nieprecyzyjny byłem wcześniej :wink: moja wina.

Nick Kallen, jeden z programistów Twitter’a opisuje dlaczego powstał Arel

Mnie zastanawia tylko następująca sprawa:
Załóżmy:

class Author < ActiveRecord::Base
has_many :books
end

class Book < ActiveRecord::Base
belongs_to :author
end

Dawniej mogłem zapytać:

Book.all(:joins => :author, :conditions =>{:author =>{:lastname => “King”}})

Teraz trzeba będzie:

Book.join(authors).on(authors[:id].eq(books[:author_id])).where(authors[:lastname].eq(“King”))

Czyli 1) musze podać warunek złączenia
2) zapis jest dłuższy i wcale nie bardziej czytelny
Chyba, że da się to zapisać inaczej

@wpasternak: doczytaj ostatni post PaK’a i skup się na tym poście: http://m.onkey.org/2010/1/22/active-record-query-interface :slight_smile:

Zaraz będe testował - sama idea AREL wydaje się trafiona w 10 - trzeba bedzie tylko pewnie poczekać aż dograją na tip -top składnie no i troche się pomęczyć przy przestawieniu na nowy zapis

Testowałem: Da się zapisać tak:

Book.joins(:author).where(:authors =>{:lastname =>“King”}).all

Dla mnie git :slight_smile:

Nie wiem czym tu się jarać. Znacznie większe możliwości ma Sequel. M.in. ma ładniejszy i potężniejszy DSL (sumy logiczne to żaden tam problem), ma prepared statement (znacznie przyśpiesza niektóre operacje i automatycznie znika możliwość SQL injection), ma bardzo wygodną metodę .sql którą można dokleić w dowolnym etapie budowania zapytania aby wyświetlić jaki ostateczny SQL zostanie stworzony (i to się wyświetli bez jakiegokolwiek łączenia się z bazą!). Ma też eager preloading, transakcje, sharding i inne zaawansowane mechanizmy bazy relacyjnej itp. itd.

Jarek, ale wszyscy śledzący blog Yehudy i komentarze nań już wiedzą, że jesteś niezbyt pozytywnie nastawiony do Rails 3 od strony nawet samej idei, nie wspominając o poszczególnych rozwiązaniach :wink:

Prawdopodobnie tym, że jedna z najpopularniejszych (żeby nie było flame’u: najpopularniejsza != najlepsza) bibliotek do obsługi bazy danych w Ruby dostanie genialne API i jest gruntownie zrefaktoryzowana. Większość osób tutaj po prostu już używa ActiveRecorda i przesiadka na innego ORMa będzie trudniejsza niż na kolejną wersję AR. W pierwszym przypadku musisz od razu przepisać cały kod, w drugim powoli poprawiać wszystkie deprecation warnings.