środa, 28 grudnia 2011

Spis statusów i żądań HTTP

Przytaczam za Wikipedią:

1xx Informational

Request received, continuing process.[2]
This class of status code indicates a provisional response, consisting only of the Status-Line and optional headers, and is terminated by an empty line. Since HTTP/1.0 did not define any 1xx status codes, servers must not send a 1xx response to an HTTP/1.0 client except under experimental conditions.
100 Continue
This means that the server has received the request headers, and that the client should proceed to send the request body (in the case of a request for which a body needs to be sent; for example, a POST request). If the request body is large, sending it to a server when a request has already been rejected based upon inappropriate headers is inefficient. To have a server check if the request could be accepted based on the request's headers alone, a client must send Expect: 100-continue as a header in its initial request[2] and check if a 100 Continue status code is received in response before continuing (or receive 417 Expectation Failed and not continue).[2]
101 Switching Protocols
This means the requester has asked the server to switch protocols and the server is acknowledging that it will do so.[2]
102 Processing (WebDAV) (RFC 2518)
As a WebDAV request may contain many sub-requests involving file operations, it may take a long time to complete the request. This code indicates that the server has received and is processing the request, but no response is available yet.[3] This prevents the client from timing out and assuming the request was lost.
103 Checkpoint
This code is used in the Resumable HTTP Requests Proposal to resume aborted PUT or POST requests.[4]
122 Request-URI too long
This is a non-standard IE7-only code which means the URI is longer than a maximum of 2083 characters.[5][6] (See code 414.)

[edit] 2xx Success

This class of status codes indicates the action requested by the client was received, understood, accepted and processed successfully.
200 OK
Standard response for successful HTTP requests. The actual response will depend on the request method used. In a GET request, the response will contain an entity corresponding to the requested resource. In a POST request the response will contain an entity describing or containing the result of the action.[2]
201 Created
The request has been fulfilled and resulted in a new resource being created.[2]
202 Accepted
The request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon, as it might be disallowed when processing actually takes place.[2]
203 Non-Authoritative Information (since HTTP/1.1)
The server successfully processed the request, but is returning information that may be from another source.[2]
204 No Content
The server successfully processed the request, but is not returning any content.[2]
205 Reset Content
The server successfully processed the request, but is not returning any content. Unlike a 204 response, this response requires that the requester reset the document view.[2]
206 Partial Content
The server is delivering only part of the resource due to a range header sent by the client. The range header is used by tools like wget to enable resuming of interrupted downloads, or split a download into multiple simultaneous streams.[2]
207 Multi-Status (WebDAV) (RFC 4918)
The message body that follows is an XML message and can contain a number of separate response codes, depending on how many sub-requests were made.[7]
208 Already Reported (WebDAV) (RFC 5842)
The members of a DAV binding have already been enumerated in a previous reply to this request, and are not being included again.
226 IM Used (RFC 3229)
The server has fulfilled a GET request for the resource, and the response is a representation of the result of one or more instance-manipulations applied to the current instance. [8]

[edit] 3xx Redirection

The client must take additional action to complete the request.[2]
This class of status code indicates that further action needs to be taken by the user agent in order to fulfil the request. The action required may be carried out by the user agent without interaction with the user if and only if the method used in the second request is GET or HEAD. A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop.
300 Multiple Choices
Indicates multiple options for the resource that the client may follow. It, for instance, could be used to present different format options for video, list files with different extensions, or word sense disambiguation.[2]
301 Moved Permanently
This and all future requests should be directed to the given URI.[2]
302 Found
This is an example of industrial practice contradicting the standard.[2] HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect (the original describing phrase was "Moved Temporarily"),[9] but popular browsers implemented 302 with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 to distinguish between the two behaviours.[10] However, some Web applications and frameworks use the 302 status code as if it were the 303.[citation needed]
303 See Other (since HTTP/1.1)
The response to the request can be found under another URI using a GET method. When received in response to a POST (or PUT/DELETE), it should be assumed that the server has received the data and the redirect should be issued with a separate GET message.[2]
304 Not Modified
Indicates the resource has not been modified since last requested.[2] Typically, the HTTP client provides a header like the If-Modified-Since header to provide a time against which to compare. Using this saves bandwidth and reprocessing on both the server and client, as only the header data must be sent and received in comparison to the entirety of the page being re-processed by the server, then sent again using more bandwidth of the server and client.
305 Use Proxy (since HTTP/1.1)
Many HTTP clients (such as Mozilla[11] and Internet Explorer) do not correctly handle responses with this status code, primarily for security reasons.[2]
306 Switch Proxy
No longer used.[2] Originally meant "Subsequent requests should use the specified proxy."[12]
307 Temporary Redirect (since HTTP/1.1)
In this occasion, the request should be repeated with another URI, but future requests can still use the original URI.[2] In contrast to 303, the request method should not be changed when reissuing the original request. For instance, a POST request must be repeated using another POST request.
308 Resume Incomplete
This code is used in the Resumable HTTP Requests Proposal to resume aborted PUT or POST requests.[4]

[edit] 4xx Client Error

The 4xx class of status code is intended for cases in which the client seems to have erred. Except when responding to a HEAD request, the server should include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. These status codes are applicable to any request method. User agents should display any included entity to the user.
400 Bad Request
The request cannot be fulfilled due to bad syntax.[2]
401 Unauthorized
Similar to 403 Forbidden, but specifically for use when authentication is possible but has failed or not yet been provided.[2] The response must include a WWW-Authenticate header field containing a challenge applicable to the requested resource. See Basic access authentication and Digest access authentication.
402 Payment Required
Reserved for future use.[2] The original intention was that this code might be used as part of some form of digital cash or micropayment scheme, but that has not happened, and this code is not usually used. As an example of its use, however, Apple's MobileMe service generates a 402 error ("httpStatusCode:402" in the Mac OS X Console log) if the MobileMe account is delinquent.
403 Forbidden
The request was a legal request, but the server is refusing to respond to it.[2] Unlike a 401 Unauthorized response, authenticating will make no difference.[2]
404 Not Found
The requested resource could not be found but may be available again in the future.[2] Subsequent requests by the client are permissible.
405 Method Not Allowed
A request was made of a resource using a request method not supported by that resource;[2] for example, using GET on a form which requires data to be presented via POST, or using PUT on a read-only resource.
406 Not Acceptable
The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.[2]
407 Proxy Authentication Required
The client must first authenticate itself with the proxy.[2]
408 Request Timeout
The server timed out waiting for the request.[2] According to W3 HTTP specifications: "The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time."
409 Conflict
Indicates that the request could not be processed because of conflict in the request, such as an edit conflict.[2]
410 Gone
Indicates that the resource requested is no longer available and will not be available again.[2] This should be used when a resource has been intentionally removed and the resource should be purged. Upon receiving a 410 status code, the client should not request the resource again in the future. Clients such as search engines should remove the resource from their indices. Most use cases do not require clients and search engines to purge the resource, and a "404 Not Found" may be used instead.
411 Length Required
The request did not specify the length of its content, which is required by the requested resource.[2]
412 Precondition Failed
The server does not meet one of the preconditions that the requester put on the request.[2]
413 Request Entity Too Large
The request is larger than the server is willing or able to process.[2]
414 Request-URI Too Long
The URI provided was too long for the server to process.[2]
415 Unsupported Media Type
The request entity has a media type which the server or resource does not support.[2] For example, the client uploads an image as image/svg+xml, but the server requires that images use a different format.
416 Requested Range Not Satisfiable
The client has asked for a portion of the file, but the server cannot supply that portion.[2] For example, if the client asked for a part of the file that lies beyond the end of the file.
417 Expectation Failed
The server cannot meet the requirements of the Expect request-header field.[2]
418 I'm a teapot (RFC 2324)
This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol, and is not expected to be implemented by actual HTTP servers. However, known implementations do exist.[13]
422 Unprocessable Entity (WebDAV) (RFC 4918)
The request was well-formed but was unable to be followed due to semantic errors.[7]
423 Locked (WebDAV) (RFC 4918)
The resource that is being accessed is locked.[7]
424 Failed Dependency (WebDAV) (RFC 4918)
The request failed due to failure of a previous request (e.g. a PROPPATCH).[7]
425 Unordered Collection (RFC 3648)
Defined in drafts of "WebDAV Advanced Collections Protocol",[14] but not present in "Web Distributed Authoring and Versioning (WebDAV) Ordered Collections Protocol".[15]
426 Upgrade Required (RFC 2817)
The client should switch to a different protocol such as TLS/1.0.[16]
428 Precondition Required
The origin server requires the request to be conditional. Intended to prevent "the 'lost update' problem, where a client GETs a resource's state, modifies it, and PUTs it back to the server, when meanwhile a third party has modified the state on the server, leading to a conflict."[17] Proposed in an Internet-Draft.
429 Too Many Requests
The user has sent too many requests in a given amount of time. Intended for use with rate limiting schemes. Proposed in an Internet-Draft.[17]
431 Request Header Fields Too Large
The server is unwilling to process the request because either an individual header field, or all the header fields collectively, are too large. Proposed in an Internet-Draft.[17]
444 No Response
An nginx HTTP server extension. The server returns no information to the client and closes the connection (useful as a deterrent for malware).
449 Retry With
A Microsoft extension. The request should be retried after performing the appropriate action.[18]
450 Blocked by Windows Parental Controls
A Microsoft extension. This error is given when Windows Parental Controls are turned on and are blocking access to the given webpage.[19]
499 Client Closed Request
An Nginx HTTP server extension. This code is introduced to log the case when the connection is closed by client while HTTP server is processing its request, making server unable to send the HTTP header back.[20]

[edit] 5xx Server Error

The server failed to fulfill an apparently valid request.[2]
Response status codes beginning with the digit "5" indicate cases in which the server is aware that it has encountered an error or is otherwise incapable of performing the request. Except when responding to a HEAD request, the server should include an entity containing an explanation of the error situation, and indicate whether it is a temporary or permanent condition. Likewise, user agents should display any included entity to the user. These response codes are applicable to any request method.
500 Internal Server Error
A generic error message, given when no more specific message is suitable.[2]
501 Not Implemented
The server either does not recognise the request method, or it lacks the ability to fulfill the request.[2]
502 Bad Gateway
The server was acting as a gateway or proxy and received an invalid response from the upstream server.[2]
503 Service Unavailable
The server is currently unavailable (because it is overloaded or down for maintenance).[2] Generally, this is a temporary state.
504 Gateway Timeout
The server was acting as a gateway or proxy and did not receive a timely response from the upstream server.[2]
505 HTTP Version Not Supported
The server does not support the HTTP protocol version used in the request.[2]
506 Variant Also Negotiates (RFC 2295)
Transparent content negotiation for the request results in a circular reference.[21]
507 Insufficient Storage (WebDAV) (RFC 4918)
The server is unable to store the representation needed to complete the request.[7]
508 Loop Detected (WebDAV) (RFC 5842)
The server detected an infinite loop while processing the request (sent in lieu of 208).
509 Bandwidth Limit Exceeded (Apache bw/limited extension)
This status code, while used by many servers, is not specified in any RFCs.
510 Not Extended (RFC 2774)
Further extensions to the request are required for the server to fulfill it.[22]
511 Network Authentication Required
The client needs to authenticate to gain network access. Intended for use by intercepting proxies used to control access to the network (e.g. "captive portals" used to require agreement to Terms of Service before granting full Internet access via a Wi-Fi hotspot). Proposed in an Internet-Draft.[17]
598 Network read timeout error
This status code is not specified in any RFCs, but is used by some[which?] HTTP proxies to signal a network read timeout behind the proxy to a client in front of the proxy.
599 Network connect timeout error[23]
This status code is not specified in any RFCs, but is used by some[which?] HTTP proxies to signal a network connect timeout behind the proxy to a client in front of the proxy.


Mogłem oczywiście podać sam link:
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
ale wolałem mieć wszystko w jednym miejscu kiedy czegoś szukam.

poniedziałek, 19 grudnia 2011

Jak zamienić string na nazwę klasy

Napotkałem na taki problem: nazwy klas (modeli) mam złożone w postaci zwykłych stringów czyli klasycznych ciągów znaków. Chciałem używać tych stringów jako normalnych klas.

zamiast:
  @employees=Employee.all

użyłem czegoś takiego:
  class_name="Employee"
  @employees=Kernel.const_get(class_name).all

Na pierwszy rzut oka wydaje się to niepotrzebne. Do czasu...

czwartek, 1 grudnia 2011

Dynamiczne dodawanie opcji do kontrolki "select"

Wydawałoby się, że jest to prosta i jakże częsta akcja twórców stron i aplikacji internetowych. Jednak nie udało mi się znaleźć żadnego(sic!) rozwiązania, które byłoby zgodne z duchem Rails. Jak nie ma, to trzeba napisać. Siadłem i popełniłem kod jak poniżej.

Założenia są proste (podam w składni Rails) jest jakaś lista select, na przykład:
<%= select_tag :event_container %>
i chciałbym do tej listy dorzucać pracowników z listy
<%= select_tag(:employee_id,
                options_for_select(Employee.all.map { |employee| [employee.full_name, employee.id] }) %>
Chcę sobie kliknąć na listę z pracownikami, a po kliknięciu zaznaczony pracownik ma "przeskoczyć" z jednej listy na drugą. "Przeskoczyć" to znaczy zniknąć z jednej listy, a pojawić się na drugiej i vice versa.
Do widoku wrzucam więc obserwatory:
<%= observe_field "employee_id",
                   :url => {:action => :move_employee_name},
                   :with => "employee_id" %>
<%= observe_field "event_container",
                   :url => {:action => :remove_employee_from_event},
                   :with => "event_container" %>
i tworzę dla nich pliki RJS odpowiednio: move_employee_name.rjs i remove_employee_from_event.rjs. Muszą one być skorelowane z metodami o takich samych nazwach w kontrolerze! Nie zapomnieć!
Wpisy w move_employee_name.rjs dotyczą oczywiście kontrolek select:
page[:event_container].addNewOption @employee.full_name, @employee.id
page[:employee_id].removeSelectedOption
Wpisy w remove_employee_from_event.rjs są analogiczne
page[:employee_id].addNewOption @employee.full_name, @employee.id
page[:event_container].removeSelectedOption

I tu pytanie za 12 punktów: skąd RJS zna te dziwne metody? Mowa oczywiście o ".addNewOption" i ".removeSelectedOption". I od razu odpowiedź: wpisałem je w /public/javascripts/application.js w postaci:
/* Dodawanie metod do szablonów RJS. */
Element.addMethods({
 /* Dodawanie nowej opcji do kontrolki select. W tym przypadku jest to "element". */
 addNewOption: function(element, option, id) {
  element.options.add(new Option(option, id));
  return element;
 },
 /* Usuwanie zaznaczonej opcji w kontrolce select. Bez parametrów, gdyż funkcja leci po wszystkich. */
 removeSelectedOption: function(element) {
  var i;
  for (i = element.length - 1; i >= 0; i--) {
   if (element.options[i].selected) {
    element.remove(i);
    return;
   }
  }
 }
});
Tam jest właśnie cała magia :-)

Sposób opisany tu przeze mnie jest jak najbardziej zgodny z ideą Rails. Daje się łatwo utrzymać w kodzie aplikacji i jest czytelny. Nic tylko brać!

poniedziałek, 8 sierpnia 2011

Polski słownik do Intellij Idea

Nareszcie udało mi się podłączyć polski słownik do mojego ulubionego IDE. Niech będzie, że to reklama i ujawnię (wszakże jest to w temacie), że moim ulubionym IDE jest Intellij Idea. Po jaką cholerę oni tak komplikują tę nazwę? Nie wiem.
Ale ad rem! W środowisku tym możemy podłączyć plik słownika w formacie .dic. Proszę mnie nie pytać jak wygląda ten format. Nie wiem. Wiem natomiast jak go wygenerować.
O tak (w jednej linijce):

aspell --lang pl dump master | aspell --lang pl expand | tr ' ' '\n' > polish.dic


Czyli wchodzę sobie do mojego Linuxa i w terminalu wpisuje powyższą linijkę, czekam chwiluśkę i mam słownik! Nie obywa się oczywiście bez schodów (w Linuxie nic nie obywa się bez schodów) bo system mi pisze, że w katalogu \usr\lib\cos_tam\aspell nie ma żadnego pliku o nazwie pl. W Ubuntu wchodzę sobie w instalator pakietów i doinstalowuję pakiet o wdzięcznie brzmiącej nazwie aspell-pl. Od tej chwili czary już działają. Aby dokończyć dzieła wchodzę do Idei i w Settings wybieram opcję spelling, a tam dodaję katalog, w którym znajduje się wygenerowany plik polish.dic. Idea sama już wszystko dorozumie :-)

wtorek, 28 czerwca 2011

Zmiana położenia profilu i dokumentów w Windows 7

Pierwszą rzeczą, którą robię po instalacji Windows 7 to zmiana położenia profilu dla użytkowników. Dlaczego wszystkie moje dane mają leżeć na partycji, która może być szybko przeinstalowywana? Dziwne, że Microshit nie wpadł na taki pomysł! Można by to zrealizować podczas instalacji Windows, w przypadku gdy jest więcej niż jedna partycja... Ale może nie bardzo to takie dziwne w porównaniu z ich innymi wybrykami.
Co by nie gadał przenieść profile trzeba. Używam takich sztuczek:
  1. Zabezpieczam rejestr. I żeby mi nikt nie pisał, że nie ostrzegałem...
  2. Kopiuję cały folder C:\Users na inny dysk. Dla skupienia uwagi przyjmijmy, że to będzie dysk D:. Jako ciekawostkę podaję, że ten folder widać w systemie jako C:\Użytkownicy i z taką nazwą przekopiuje się na D:. Uwaga! W folderze tym są ukryte pliki i foldery! Należałoby więc wejść do Eksploratora Windows, z menu wybrać Narzędzia -> Opcje Folderów -> Widok -> Pokaż ukryte pliki, foldery i dyski. Dopiero wtedy przekopiuje się OK.
  3. Otwieram edytor rejestru (regedit) i wchodzę w drzewko HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\WINDOWS NT\CurrentVersion\ProfileList
  4. Zmieniam tam 3 (słownie trzy) wartości. Czyli Default, Public, Profile Directory
  5. No i przelogowuję się. Można już zakładać nowego użytkownika, który pokaże się w nowej lokalizacji na innej partycji.
Tak to mogłoby wyglądać przed zmianą:

A tak po zmianie:

Rzecz całą wyczytałem na: http://www.windows7hacker.com/index.php/2009/05/how-to-change-user-profile-default-location-in-windows-7/

poniedziałek, 30 maja 2011

Program do prostych screencastów

Nie potrzeba nic instalować. Usługa już z hostingiem. Dla prostych, szybkich screencastów dla klienta wymarzony:
http://www.screenr.com/

wtorek, 10 maja 2011

System do próbowania html/css/javascript

Na dodatek wspierający frameworki Mootools, jQuery, Prototype, YUI, Dojo, ExtJS i kilka innych.
Wskocz na http://jsfiddle.net/

Piszesz po prostu kod html okraszasz go css'em i uaktywniasz Javascriptem w dowolnej konfiguracji. Efekt po naciśnięciu klawisza "Run".


Sposób na ukrycie swojego emaila na stronie

Wejdź na stronkę:
http://www.vincentcheung.ca/jsencryption/

W żółtym polu "Key" wpisz dowolny klucz. Na przykład taki jak adres tej strony:  "http://pomoceprogramisty.blogspot.com/"
W polu zielonym zapisz swój email i naciśnij przycisk "Encrypt". W polu czerwonym "Clipher text" zobaczysz wygenerowany klucz.
W niebieskim polu "HTML Code" otrzymasz kod do wklejenia na swoją stronę.
Każdy, kto będzie chciał zobaczyć twój email będzie musiał podać klucz, który w naszym przypadku jest adresem tej strony.

Roboty zbierające emaile się nabiedzą :-)

poniedziałek, 9 maja 2011

Pozycja myszy w JavaScript i dynamiczne ustawianie pojemnika

Czasami zachodzi potrzeba aby pojemnik z wygenerowaną zawartością (na przykład Ajaxową) pojawił się w okolicach kursora myszy. Można to wykonać na różny sposób, ale że dużo piszę Ruby on Rails, które używa biblioteki Prototype, to opiszę na tej właśnie drodze.
Aby zapoznać się z działaniem mechanizmów można wypróbować ten kod:
<script type="text/javascript" >
  function getcords(e){
    mouseX = Event.pointerX(e);
    mouseY = Event.pointerY(e);
    //dla celów testowych
    $('debug').innerHTML = 'mouseX:' + mouseX + '-- mouseY:' + mouseY;
  }
  Event.observe(document, 'mousemove', getcords);
</script>
<div id="debug">
  tu za chwilę pojawią się koordynaty kursora myszy
</div> 

Kluczowym jest tu obserwator (albo donosiciel) zdarzeń Event.observe, który w tym przypadku obserwuje cały dokument i śledząc położenie myszy "donosi" do kontenera <div id="debug">.
Jak wcześniej wspomniałem, opisuję to jedynie aby pokazać mechanizm. Nam chodzi o coś innego.
Potrzeba była taka: po prawej stronie ekranu jest lista pytań skróconych $('question_list'). Zajmuje ona całą wysokość ekranu. Klikniesz sobie na pytanie skrócone, a Rails przeszuka bazę i dostarczy pełną treść pytania. Treść tę umieści w kontenerze $('question_long'). Kontener ten wisi sobie spokojnie w jednym miejscu strony, a nam chodziło o to, aby pokazywał się na wysokości klikniętego pytanka na liście, i aby wszystko było pod ręką i przed oczami.
No to trzask:
<script type="text/javascript">
  function getcords(e) {
    mouseY = Event.pointerY(e);
    $('question_long').setStyle('top: ' + mouseY + 'px;');
  }
  Event.observe($('question_list'), 'click', getcords);
</script>
czary mary i działa!

Krótki opis:

Linijka 4:
Element.setStyle nadaje styl CSS wybranemu elementowi na stronie. W naszym przypadku kontenerowi z wygenerowaną pełną treścią pytania. Ustawiamy mu pozycję patrząc od góry strony. Uwaga nie zapomnij mu nadać odpowiedniego pozycjonowania:
#question_long {
  position: fixed;
  right: 200px;
  width: 300px;
  /* tra ta ta, kolory, zaokrąglenia rogów itp. */
}

Linijka 6:
Obserwujemy teraz nie cały dokument, a jedynie listę z pytaniami skróconymi. Nie interesują nas też oczywiście wszystkie ruchy myszy w zakresie tej listy, ale konkretne kliknięcia.

Oczywiście można to wszystko zrobić bez Prototype, ale skoro już jest... to dlaczego nie użyć?

czwartek, 28 kwietnia 2011

Prosta konfiguracja autocomplete w Ruby on Rails

Znowu walczyłem z autocomplete w Rails. Walczyłem, bo inaczej tego nie można określić.

Twórcą tego pluginu jest osobnik o swojsko brzmiącym nicku dhh. Na stronie poświęconej temu pluginowi można nawet (sic!) natknąć się na przykład jego wykorzystania. Przeglądając tę stronkę trudno coś o niej powiedzieć innego niż "łomatulu!". Wyjaśniać to ona niczego nie wyjaśnia, a tylko denerwuje niedziałającymi linkami scriptacoulosa.

Aby zainstalować z konsoli zawołaj:
ruby script/plugin install auto_complete
oczywiście zazwyczaj nie działa :-(
Można więc ręcznie. Ściągnij zatem plugin i rozpakuj do
/vendor/plugins/auto_complete
zazwyczaj pomaga.

Aby użyć tej kontrolki w widoku wpisz:
<%= text_field_with_auto_complete :model_name, 
                                  :field_in_model_to_search, 
                                  {:value => ""}, {:min_chars => 3, :after_update_element => 'approveForm'} %>
gdzie approveForm jest nazwą funkcji JavaScript, która zostanie wywołana po wybraniu elementu z listy.

Aby rozwikłać pozostałe zawiłości złapałem się po raz kolejny za google i gugłam. Czytam, czytam... i nadziwić się nie mogę. Zerknij tylko (albo raczej nie zerkaj) na railsforum, albo nawet na railscasts. To jakaś masakra jest!

Chodziło mi o prostą rzecz. Chciałem sam zapanować nad zapytaniem do bazy i nad tym co ona zwróci do użytkownika.
Najbardziej zdziwił mnie fakt, że to poniżej działa!!!
class CustomersController < ApplicationController
  auto_complete_for :customer, :name, :conditions => ["gender = ?", "female"]
  .
  .
  .
Zadziałało! Uradowany, nigdzie tego nie wyczytałem, tylko wykombinowałem "na macanta".  Fajno jest. I tak zostało na jakiś czas.
Gdy w następnym miejscu aplikacji znów musiałem czegoś podobnego użyć, miałem prawie gotowe rozwiązanie:
class ProductsController < ApplicationController
  # Nie stosuj tego, to nie działa!
  auto_complete_for :product, :name, :conditions => ["group_id = ?", @group_id]
  .
  .
  .
ale to nie działa. Zmienna egzemplarza pojawia się za wcześnie. Trick z tą zmienną @group_id odpowiadał mi, więc nie chciałem stosować trzymania się prawą ręką za lewe ucho i zacząłem drążyć temat. Po jakimś czasie okazało się, że notacja:
auto_complete_for :product, :name, :conditions => ["group_id = ?", @group_id]
tworzy dynamicznie metodę:
def auto_complete_for_product_name
  .
  .
  .
def
Co wiec stało na przeszkodzie aby taka metodę samemu napisać? Nic, więc napisałem:
def auto_complete_for_product_name
  product_name=params[:product][:name]
  conditions=["name = ? AND group_id = ?", product_name, @group_id]
  @products=Product.all(:conditions => conditions)
end
Sprawdzam... prawie działa.
Prawie bo Rails krzyczy, że brak mu pliku widoku auto_complete_for_product_name.erb
Znów "na macanta" poleciałem. Utworzyłem plik widoku i... zadziałało:
<ul> 
  <% @products.each do |product| %>
    <li><%= h product.name %></li> 
  <% end %> 
</ul> 
Rozwiązanie proste, łatwe i przyjemne.
W kontrolerze panuje się nad każdym przejawem tworzonej listy. Można zrobić dosłownie wszystko czego się tylko zapragnie. Nie trzeba z niczym kombinować jak to pokazywali w sreencaście i proponowali na liście. Moje rozwiązanie jest całkowicie naturalne i w duchu Rails.

Pozostała jeszcze sprawa pozostałych opcji.
Opcje są, a jakże. Żywcem je tu przytaczam:
# Required +options+ are:
# :url:: URL to call for autocompletion results
# in url_for format.
#
# Addtional +options+ are:
# :update:: Specifies the DOM ID of the element whose
# innerHTML should be updated with the autocomplete
# entries returned by the AJAX request.
# Defaults to field_id + '_auto_complete'
# :with:: A JavaScript expression specifying the
# parameters for the XMLHttpRequest. This defaults
# to 'fieldname=value'.
# :frequency:: Determines the time to wait after the last keystroke
# for the AJAX request to be initiated.
# :indicator:: Specifies the DOM ID of an element which will be
# displayed while autocomplete is running.
# :tokens:: A string or an array of strings containing
# separator tokens for tokenized incremental
# autocompletion. Example: :tokens => ',' would
# allow multiple autocompletion entries, separated
# by commas.
# :min_chars:: The minimum number of characters that should be
# in the input field before an Ajax call is made
# to the server.
# :on_hide:: A Javascript expression that is called when the
# autocompletion div is hidden. The expression
# should take two variables: element and update.
# Element is a DOM element for the field, update
# is a DOM element for the div from which the
# innerHTML is replaced.
# :on_show:: Like on_hide, only now the expression is called
# then the div is shown.
# :after_update_element:: A Javascript expression that is called when the
# user has selected one of the proposed values.
# The expression should take two variables: element and value.
# Element is a DOM element for the field, value
# is the value selected by the user.
# :select:: Pick the class of the element from which the value for
# insertion should be extracted. If this is not specified,
# the entire element is used.
# :method:: Specifies the HTTP verb to use when the autocompletion
# request is made. Defaults to POST.

Ogólna notacja kontrolki w widoku jest taka:
.
<%= text_field_with_auto_complete(object, method, tag_options = {}, completion_options = {})  %>
.

Aby na przykład zacząć wyszukiwanie od wpisanych trzech liter kontrolkę skomponuj tak:
.
<%= text_field_with_auto_complete :employee, :name, {}, {:min_chars => 3} %>
.

wtorek, 26 kwietnia 2011

Screencasty o Vim'ie

Wybaczcie, że nie będę oryginalny ale dzięki blipowi niezauważenie zacząłem oglądać arcyciekawe (dla mnie oczywiście) screencasty o vim'ie.
Dostępne to to jest pod adresem: http://vimcasts.org i cieszy, że jest taka rzesza entuzjastów.
Nie będę ukrywał, że piszący te słowa z chęcią do tych entuzjastów się przyznaje :-).

piątek, 22 kwietnia 2011

count vs length vs size w Rails

W Ruby zarówno #length jak i #size to synonimy podawania wielkości tablicy bądź tablicy asocjacyjnej.
W Railsowym ActiveRecord wygląda na to, że są trzy drogi podawania wielkości asocjacji:
  1. firm.customers.count - wykonuje standardowe zapytanie SQL COUNT. Co ciekawe można podawać warunki podzbioru. Na przykład :conditions => {:customer_name = "Nowak"}. Jeśli nastąpiło wcześniejsze zapytanie SQL COUNT, wtedy #count pokaże zawartość cache, jeśli nie, wykona nowe zapytanie.
  2. firm.customers.length - zawsze ładuje całą asocjację do pamięci, potem ją zlicza i podaje wielkość. Ważne jest, że ta metoda nie wymusza zapytania, jeśli zawartość asocjacji była już pobierana.
  3. firm.customers.size - ta metoda działa w przybliżeniu jako kombinacja dwóch powyższych. Jeśli acocjacja była już pobierana podaje po prostu jej wielkość i tyle. Jeśli nie była pobierana, to wykonuje #count z punktu 1.
Piszę o tym, bo czasami można się naciąć jeśli oczekuje się bardzo szybkiej operacji, a trwa ona zdecydowanie dłużej niż spodziewana. No i to cachowanie...
Więc właśnie. Dwa słowa odnośnie cachowania. Rozważmy następujące przypadki:
Przypadek 1:
@firm = Firm.find(params[:id]) 
@amount_customers_in_firm = @firm.customers.size 
#tu nastąpi zapytanie do bazy

Przypadek 2:
@firm = Firm.find(params[:id])
@customers = @firm.customers 
@amount_customers_in_firm = @firm.customers.size 
#tu zwrócona zostanie ilość rekordów pobrana z cache

Przypadek 3:
@firm = Firm.find(params[:id])
@customers = @firm.customers 
. # tu
. # jakieś
. # gęste
. # operacje
. # na rekordach
@amount_customers_in_firm = @firm.customers(true).size 
#tu wymuszam ponowne zapytanie do bazy z pominięciem cache

Więcej na temat cachowania asocjacji w Rails guides.
Zaś ogólnie o cachowaniu zawodowy artukuł z thewebfellas.


środa, 13 kwietnia 2011

Zainicjowanie danymi zdalnego repozytorium w Git

Czasami, choć rzadko, musimy zainicjować danymi zdalne repozytorium. Jak zdążyłem się zorientować czytając artykuły o Git'cie, są one pisane dla raczej dla programisty, którego marzeniem jest podłączenie się do istniejącego projektu, ściągnięcie kodu i włożenie do niego własnych trzech groszy. To napisałem dla tych, którzy zakładają projekty.

niedziela, 10 kwietnia 2011

Krótkie screencasty o systemie kontroli wersji - Git

Okazało się, że coś takiego jest:
http://gitcasts.com/

Nawet miło i zrozumiale zrobione. Fajnym akcentem jest to, że na koniec każdego z filmików pokazują się inne w tym temacie. Przydałoby się to na portalu o railscats.


środa, 6 kwietnia 2011

Odcinanie ogonków, czyli zamiana polskich liter w Ruby on Rails

     Napotkałem na problem polegający na tym aby tekst zawierający polskie znaki zamienić na tekst bez polskich znaków. Czyli napis "Łabędź i żółw" ma stać się napisem "Labedz i zolw".
Troszkę poszperałem w sieci, troszkę pokombinowałem i powstał taki kod:
require 'iconv'

class String
  def to_ascii
    ascii = "acelnoszzACELNOSZZ"
    cep = "\271\346\352\263\361\363\234\277\237\245\306\312\243\321\323\214\257\217"
    s = Iconv.new("cp1250", "UTF-8").iconv(self)
    s.tr!(cep, ascii)
    return (s.length==0) ? self : s 
  end
end
     Lubię Ruby za to całkowicie bezkonfliktowe i niezwykle proste wzbogacanie standardowych klas.
Na pierwszy rzut oka pada pytanie: po co sprawdzać długość łańcucha s? Więc to zagadka. Dla wszystkich, którzy zgadną i nadeślą rozwiązanie - nagroda :-)

     Jeśli potrzebujesz takiego rozwiązania to wrzuć powyższy listing do pliku na przykład o nazwie str_utl.rb i umieść w Railsach w katalogu lib, a do pliku environment.rb wstaw require 'str_utl'. U mnie działa to tak:
>> "Łabędź i żółw".to_ascii
>> "Labedz i zolw"


niedziela, 3 kwietnia 2011

Jak w JavaScript przenieść się na inną stronę

Ciągle zapominam tę komendę i tę składnię, postanowiłem więc to sobie zapisać:
window.location.href = 'http://piszemyprogramy.pl';
JavaScript napotykając taką frazę przeniesie nas na naszą ulubioną stronę http://piszemyprogramy.pl

Fragmentu adresu

Często się zdarza, że potrzebuję z adresu wymontować jego fragment. Zamiast się męczyć z wyrażeniami regularnymi można to zrobić tak:
window.location.host : dostaniesz sub.domain.com:8080 lub sub.domain.com:80
window.location.hostname : dostaniesz sub.domain.com
window.location.protocol : dostaniesz http:
window.location.port : dostaniesz 8080 lub 80
window.location.origin : dostaniesz http://sub.domain.com
window.location.pathname : dostaniesz (na przykład) orders/index


Jak zamieścić ikonkę facebooka "Lubię.to" na swojej stronie

Wejdź po prostu na:
http://developers.facebook.com/docs/reference/plugins/like/
W pole "URL to Like (?)" wpisz adres swojej strony i kliknij przycisk "Get Code".
Wklej pobrany kod na swojej stronce. Działa.


Rails autocomplete - uruchamianie akcji po kliknięciu

     Pomiędzy tysiącami dodatków do Ruby on Rails znajduje się kilka, z których korzystamy praktycznie zawsze. Jednym z nich jest "autocomplete". Umożliwia on dynamicznie uzupełnianie zawartości pola tekstowego treścią z bazy danych. Użyteczne, proste w obsłudze i łatwo implementowalne w programie.
Przy czym stwierdzenie "łatwo implementowalne" dałem tak troszkę na wyrost. Pierwsze schody już na samym początku: dokumentacja. Na stronie pluginu otrzymujemy przykład prostej konfiguracji i linki do szczegółowej dokumentacji. Radość? Przedwczesna! Ta prosta konfiguracja opisywana na stronie jest o tyle szybko implementowalna, o ile nigdy nie wystarczająca do Twojego projektu, który akurat piszesz. Natychmiast więc klikasz w linki aby uzyskać szczegóły API, przykłady kodu i inne tego typu, wydawało by się, duperele. Co się okazuje? Linki prowadzą na strony scriptaculous, które nie tylko, że niczego nie rozwiązują, to jeszcze wstydliwie się rozjeżdżają. Jeśli nie wiesz o czym piszę, to kliknij sobie tam na te linki. Wstyd? Mi też się tak wydaje. Nie podaję więc ich tutaj. Podaję zaś link do działającej dokumentacji scriptaculous. Nie pytaj mnie dlaczego tak jest? Nie wiem. Tysiące programistów pytających się o to samo w sieci też nie wie. Cisną się na usta epitety :-) ale nie z takimi rzeczami dawaliśmy radę :-)

czwartek, 31 marca 2011

Stronicowanie w Ruby on Rails - najpopularniejsze opcje

Mowa oczywiście o pluginie do Ruby on Rails o nazwie will_paginate.

Aby go zainstalować z konsoli zawołaj:
gem install will_paginate
potem do
/config/environment.rb
wpisz
config.gem 'will_paginate', :version => '~> 2.3.15'
przy czym uwaga! :version => '~> 2.3.15' nie tyczy się wersji rails, tylko wersji tego plugina!

Opcje wyświetlania:
  • :previous_label — domyślnie: "« Previous", nazwa linka do poprzedniej strony
  • :next_label — domyślnie: "Next »" , nazwa linka do następnej strony
  • :page_links — kiedy jest to false, tylko poprzedni/następny link jest renderowany (domyślnie: true)
  • :inner_window — mówi ile linków widnieje przed i za aktualną stroną (domyślnie: 4)
  • :outer_window — mówi ile linków widnieje dookoła pierwszej i ostatniej strony (domyślnie: 1)
  • :separator — string reprezentujący separator oddzielający numerki stron (domyślnie: pojedyncza spacja) 
Na przykład:
<%= will_paginate @customers, :previous_label => "wstecz", :next_label => "dalej" %>


Opcje HTML:
  • :class — nazwa klasy CSS dla wygenerowanego DIV'a (domyślnie: "pagination")
  • :container — przełącza renderowanie dla podanego wcześniej DIV'a. Ustaw na false tylko w przypadku stosowanie własnych rozwiązań (domyślnie: true)
  • :id — HTML ID dla kontenera z linkami  (domyślnie: nil). Ustaw true dla automatycznego rozpoznawania ID z nazwy klasy obiektów kolekcji. Na przykład jeśli stronicujesz model ArticleComment to automatycznie zostanie nadany ID "article_comments_pagination". 
Opcje zaawansowane:
  • :param_name — nazwa parametru zawartego w URL  (domyślnie:page)
  • :params — dodatkowe parametry przy generowaniu stronicowania (na przykład :controller => "foo", :action => nil)
  • :renderer — nazwa klasy, klasa lub instancja klasy dla renderowanych linków (domyślnie: WillPaginate::LinkRenderer)
Wszystkie opcje rozpoznawane przez will_paginate stają się automatycznie opcjami kontenera DIV. Na przykład:
<%= will_paginate @posts, :style => 'font-size: small' %>
… staje się po renderowaniu:
<div class="pagination" style="font-size: small"> ... </div>



Pełna dokumentacja na naszej stronie poświęconej dokumentacji API will_paginate.


Stronicowanie w Ruby on Rails - dokumentacja do API

Skądinąd dobry plugin dla Ruby on Rails do stronicowania o tajemniczej nazwie will_paginate ma kilka ciekawych możliwości. Niestety o możliwościach tych nie dowiesz się ze strony macierzystej. Większość linków tam nie działa. Nie pytaj dlaczego. Nie wiem. Nie wiedzą również o tym inni internauci. Powstają na grupach nawet wątki z pytaniami do autora o ten fenomen.

Kogo denerwuje brak dokumentacji do API tego pluginu mam dla niego rozwiązanie. Jako, że nas to też wkurzyło przygotowaliśmy dla wszystkich dokumentację API dla will_paginate v. 2.3.15. Korzystaj :-)


niedziela, 27 marca 2011

Jak w C++ zainicjalizować otwarcie pliku klasą std::string

#include <iostream>
#include <fstream>
#include <string>

int main()
{
  std::string filename = "testfile";      
  std::ifstream fin;

  fin.open(filename.c_str()); // Works just fine.
  fin.close();

  //fin.open(filename); // Error: no such method.
  //fin.close();
}

Pełny tekst tutaj:
http://stackoverflow.com/questions/32332/why-dont-the-stdfstream-classes-take-a-stdstring

Polskie literki w czystym Qt

w nagłówku:
#include <QTextCodec>

na początku programu należy wywołać:

// w tłumaczeniach np. tr("ąśżźćółń");
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
// w pozostałych tekstach klasy QString
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));

sobota, 26 marca 2011

Ikonki, paski, klepsydry - wszystko o czekaniu (waiting icons)

W każdej aplikacji webowej potrzeba animowanych elementów przedstawiających proces ładowania. Albo potrzeba ikon, które nie mówią nic o procesie ładowania poza enigmatycznym zapewnieniem, że jakoweś tajne informacje przetwarzane są po stronie serwera.
Na tej stronce mamy wybór: http://mentalized.net/activity-indicators/

piątek, 25 marca 2011

Kilka sztuczek w modelach Ruby on Rails

Przedstawiam kilka ciekawych "chwytów" dla modeli w Ruby on Rails. Dla jednych bardzo odkrywcze, a dla innych może już nie.

Polskie nazwy dla atrybutów

Rails z "automatu" fajnie wyświetla błędy dla modelu. Fajnie tylko pod warunkiem, że się spolonizowało aplikację. Pozostało oczywiście to, że atrybuty wyświetlają się nadal w swojej oryginalnej formie.
Jest na to rada. Umieszczamy to w modelu:
HUMANIZED_ATTRIBUTES = {
  :last_name => "Nazwisko",
  :first_name => "Imię",
  .
  .
  .
  :gender => "Płeć"
}
def self.human_attribute_name(attr)
  HUMANIZED_ATTRIBUTES[attr.to_sym] || super
end
No i już wszędzie pojawi się zamiast "last_name" napis "Nazwisko".

Walidacje dla grupy atrybutów (pól)

Załóżmy, że mamy kilka pól dla których należy nanieść ograniczenie. Warto zbudować dla nich stałą, aby później (po latach) zmieniając kod aplikacji rozumieć co artysta (ja) miał na myśli :-)
Mogłoby to wyglądać mniej więcej tak:
class DayPayment < ActiveRecord::Base
  SHORT_FIELD_LENGHT = 4 
  SHORT_FIELDS = %w(morning afternoon midnight)
  .
  .
  .
  validates_lenght_of SHORT_FIELDS, :maximum => SHORT_FIELD_LENGHT
  .
  .
  .
end 


Proste typy wyliczeniowe wymagane w polach

Załóżmy, że chcielibyśmy aby w bazie zapisywana była płeć użytkownika w formacie tekstowym jako "mężczyzna" albo "kobieta". Możemy to zrobić tak:
class User < ActiveRecord::Base
  VALID_GENDERS = ["mężczyzna", "kobieta"]
  .
  .
  .
  validates_inclusion_of :gender,
                         :in => VALID_GENDERS,
                         :allow_nil => true,
                         :message => 'tylko "mężczyzna" albo "kobieta"'
  .
  .
  .
end
opis:
VALID_GENDERS -  jak zwykle wykorzystywanie stałych, aby później rozumieć co się pisało;
:allow_nil => true - bo użytkownik nie musi wypełniać pola "płeć";

Wykorzystanie zakresu jako ograniczenia

Ruby udostępnia przejrzysty w swej składni i bardzo prosty w użyciu system zakresów. Można go użyć w modelu na przykład do ograniczenia wprowadzanych dat. Wejdą wtedy tylko te akceptowane. Można to zrobić na przykład tak:

class User < ActiveRecord::Base
  YEAR_FROM = 1939
  VALID_DATES = DateTime.new(YEAR_FROM)..DateTime.now
  .
  .
  .
  validates_inclusion_of :birth_date,
                         :in => VALID_DATES,
                         :allow_nil => true,
                         :message => "jest nieprawidłowa"
  .
  .
  .
end
opis:
VALID_DATES - zakres dopuszczalnych dat
:allow_nil => true - bo użytkownik nie musi wypełniać daty

Więcej informacji w książce Hartla i Prohazki "RailsSpace".

Ciekawy artykuł - stronicowanie, sortowanie tabel w Ruby on Rails

Stronicowanie i sortowanie tabel to bardzo popularne zagadnienie w każdej aplikacji webowej, czyli również w Rails. Dodatkowo autor porusza problem wyszukiwania w tabelach.
Całość artykułu znajduje się pod adresem:
http://dev.nozav.org/rails_ajax_table.html

środa, 23 marca 2011

Jak w jQuery wywołać funkcję z parametrami

JQuery w bardzo wielu miejscach używa funkcji bezimiennej jako parametru.
Na przykład jeśli chcemy zareagować na zmianę w polu "#name":
jQuery("#name").change(function(){
  //tu jakieś operacje
});
Wszystko fajnie do momentu, gdy te wewnętrzne funkcje nie zaczynają się rozrastać i ciągnąć za sobą nowe funkcje. Kod staje się całkowicie nieczytelny i bałagan z każdą chwilą się pogłębia.
Częściowym rozwiązaniem jest osadzanie funkcji nazwanych jako parametru.
W poprzednim przykładzie funkcję bezimienną moglibyśmy zastąpić nazwaną, jak choćby:
function makeChanges() {
  //tu kod z funkcji nienazwanej
}
którą osadzamy w poprzednim przykładzie:
jQuery("#name").change(makeChanges);
Zwróć uwagę na brak nawiasów!

Taaa... wygląda nieźle.  Wygląda nieźle, ale tylko do momentu, w którym musimy użyć funkcji makeChanges() w jakimś innym miejscu kodu. Po to przecież ją wyekstrahowaliśmy (pamiętasz zasadę DRY?). Potrzebny wtedy jakiś parametr, a najczęściej dwa... albo i trzy.
Niestety coś takiego w sposób prosty nie zadziała:
jQuery("#name").change(makeChanges(paramOne, paramTwo));
Aby zadziałało funkcja makeChanges() musi zwrócić funkcję gdyż tego wymaga jQuery. Zróbmy więc tak:
function makeChanges(paramOne, paramTwo) {
  return function() { 
    // tu dopiero kod z funkcji nienazwanej
    // z wykorzystaniem parametru paramOne
    // i parametru paramTwo
  } 
}
Teraz działa!

Miłej zabawy :-)

czwartek, 17 marca 2011

Zawartość generowana w plikach layoutu (Layout in layout) w Ruby on Rails

Zagadnienie, które poruszyłem w temacie wzbudza wiele emocji (albo raczej postów na listach). Jak zwykle burza pomysłów u kreatywnych programistów. Oczywiście rozwiązanie jest już zaimplementowane w Railsach. Rozwiązaniem tym jest
content_for
Drobny przykład.
Załóżmy, że na panelu bocznym jest standardowe menu aplikacji. Zasadniczo spełnia ono wszystkie pokładane w nim nadzieje oprócz menu dla usera, które jako jedyne wymaga zmiany hasła. Dobrze by było więc aby podczas operacji dotyczącej użytkownika w panu pojawiała się dodatkowo opcja "zmiana hasła".
W panelu bocznym standardowo menu wygląda tak:
<ul>
  <li>klienci</li>
  <li>płatności</li>
  .
  .
  ...a tu dodatkowe opcje menu w zależności od kontekstu...
</ul>
Zamiast łapać się prawą ręką za lewe ucho użyjmy standardowych mechanizmów Rails.
W rzeczonym szablonie umieszczamy zajawkę:
<ul>
  <li>klienci</li>
  <li>płatności</li>
  .
  .
  <%= yield :new_options %> 
</ul>

zaś w dowolnym pliku *.html.erb używamy content_for:
<% content_for :new_options do %>
  <li>zmień hasło</li>
  <li>sprawdź email</li>
<% end %>

Dzięki temu zapisowi dwie nowe pozycje znajdą się w menu panela bocznego.

Oczywiście w głównym szablonie aplikacji może być więcej nazwanych bloków yield, które będą mogły być wypełniane w zależności od okoliczności. Bardzo wygodne, proste i łatwe.

Po więcej informacji sięgnij do tutoriala Layouty i Renderowanie w Ruby on Rails


poniedziałek, 14 marca 2011

Przykłady użytecznych elementów strony z użyciem jQuery

Ciekawie i prosto zrobione:
http://www.noupe.com/jquery/50-amazing-jquery-examples-part1.html
aczkolwiek szata graficzna pozostawia wiele do życzenia.

Ba Bach - dwa strzały aby spolonizować aplikację Ruby on Rails

Jak spolonizować aplikację pisaną w Ruby on Rails? Wystarczy zerknąć do netu aby złapać się za głowę ile trzeba przekopać materiału aby zdobyć taką wiedzę. Pomysły, pomysły, pomysły... Ludzie ich mają setki. Kreatywni programiści. Na dodatek są w stanie prawicę dać sobie odciąć za słuszność ich pomysłu. Przeglądając je zastanawiałem się jak bardzo można wykręcić sobie prawą nogę aby złapać nią za lewe ucho? Przecież twórcy Rails to przewidzieli. O co kopie kruszyć?

Wystarczą dwa strzały!

Strzał pierwszy, czyli tytułowy Ba:
1. Napisz sobie plik pl.yml. Nie chce się? Pobierz od nas PiszemyProgramy.pl/download/pl.yml i wrzuć do katalogu /config/locales
Strzał drugi, czyli Bach:
2. Dodaj wpis config.i18n.default_locale = :pl do pliku /config/environment.rb

Wygrałeś. Trafiłeś dwie dziesiątki. Koniec pieśni.

Jeśli potrzeba Ci szczegółów przeczytaj to: http://apohllo.pl/guides/i18n.html delektując się pięknem polskiego języka.

Jak masz zapędy masochistyczne i chcesz poznać kreatywność programistów zerknij też tu: http://rubyonrails.pl/forum/p3572-2007-10-22-00:50:44

sobota, 12 marca 2011

GIT - kontrowersje i problemy

Jak się okazało nie tylko ja wieszam psy na panu Linusie za wyprodukowanie gigantycznego problemu w postaci GIT'a. Ale do rzeczy.
Wspomniana przeze mnie we wcześniejszych postach książka Pro Git ukazuje w sekcji o cofaniu zmian komendę:
> git commit --amend //nie używaj tego!
jako receptę na zapomniane poprawki. Autor z lekkością motylka zawiadamia czytelnika o pięknych możliwościach git'a. Na litość boską! Nie używaj tego! Mogą Cię za to zaszlachtować inni uczestnicy projektu. Narobisz takiego bigosu w kodzie i tylu problemów dla prowadzącego projekt, że raczej nie rozważy poważnie Twojej kandydatury podczas rozdzielania premii, albo po prostu Cię wyleje i będzie miał spokój.
To co się dzieje w publicznym repozytorium po użyciu tego cholerstwa... Omg!
Najlepiej jest to opisane tutaj:
http://stackoverflow.com/questions/253055/how-do-i-push-amended-commit-to-the-remote-git-repo
ale życzę abyś nie miał z tym nic do czynienia.

piątek, 11 marca 2011

GIT - usuwanie kilku commitów jednym poleceniem

Jak wiadomo usunięcie konkretnego commita w GIT'cie odbywa się komendą:
git revert odcisk_SHA
Całe szczęście, że można wpisywać tylko 7 pierwszych znaków odcisku SHA commita.
Aby sprawdzić ostatnie np. dwa odciski commitów zawołaj:
git log -2
Czasami się tak zdarza, że można zabrnąć w kodzie w ślepą uliczkę nie tworząc do tego gałęzi (branch). Może zaistnieć wtedy potrzeba usunięcia pod rząd kilku commitów. Wpisywanie "z palca" raczej nie wchodzi w rachubę bo można oszaleć. Trzeba więc było coś wymyślić.
Z pomocą przyszła komenda
git rev-list
Ma ona mnóstwo opcji, ale w podstawie wyświetla ona między innymi odciski SHA commitów. Gdy się użyje w takiej składni:
git rev-list f182667 --max-count=5
to wyświetlone zostanie 5 odcisków commitów  począwszy od commita oznaczonego jako f182667. No to już bliziutko do finału, który brzmi:

for i in `git rev-list f182667 --max-count=5`; do git revert --no-edit $i; done
Po co została użyta opcja --no-edit? Ano gdyby jej nie było, to pięć razy git by prosił o opisanie kolejnie wykonywanej operacji revert. Upierdliwe, że hej.

UWAGA! Pułapka: te górne apostrofki to nie są te normalne... To są te po lewej stronie klawisza "1" pod tyldą "~".

Rake spis poleceń

Udało mi się znaleźć prawie kompletny spis poleceń Rake dla Ruby on Rails.

  • rake gems:install - instaluje gemy zawarte w pliku config/environment.rb
  • rake db:fixtures:load - Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y
  • rake db:migrate - Migrate the database through scripts in db/migrate. Target specific version with VERSION=x
  • rake db:schema:dump - Create a db/schema.rb file that can be portably used against any DB supported by AR.
  • rake db:schema:load - Load a schema.rb file into the database.
  • rake db:sessions:clear - Clear the sessions table.
  • rake db:sessions:create - Creates a sessions table for use with CGI::Session::ActiveRecordStore.
  • rake db:structure:dump - Dump the database structure to a SQL file.
  • rake db:test:clone - Recreate the test database from the current environment's database schema.
  • rake db:test:clone_structure - Recreate the test databases from the development structure.
  • rake db:test:prepare - Prepare the test database and load the schema.
  • rake db:test:purge - Empty the test database.
  • rake doc:appBuild the app HTML Files.
  • rake doc:clobber_app - Remove rdoc products.
  • rake doc:clobber_plugins - Remove plugin documentation.
  • rake doc:clobber_rails Remove rdoc products.
  • rake doc:plugins - Generate documation for all installed plugins.
  • rake doc:rails - Build the rails HTML Files.
  • rake doc:reapp - Force a rebuild of the RDOC files
  • rake doc:rerails - Force a rebuild of the RDOC files
  • rake log:clear - Truncates all *.log files in log/ to zero bytes
  • rake rails:freeze:edge - Lock this application to latest Edge Rails. Lock a specific revision with REVISION=X.
  • rake rails:freeze:gems - Lock this application to the current gems (by unpacking them into vendor/rails)
  • rake rails:unfreeze - Unlock this application from freeze of gems or edge and return to a fluid use of system gems
  • rake rails:update - Update both scripts and public/javascripts from Rails.
  • rake rails:update:javascripts - Update your javascripts from your current rails install.
  • rake rails:update:scripts - Add new scripts to the application script/ directory.
  • rake stats - Report code statistics (KLOCs, etc) from the application.
  • rake test - Test all units and functionals
  • rake test:functionals - Run tests for functionalsdb:test:prepare
  • rake test:integration - Run tests for integrationdb:test:prepare
  • rake test:plugins - Run tests for pluginsenvironment
  • rake test:recent - Run tests for recentdb:test:prepare
  • rake test:uncommitted - Run tests for uncommitteddb:test:prepare
  • rake test:units - Run tests for unitsdb:test:prepare
  • rake tmp:cache:clear - Clears all files and directories in tmp/cache
  • rake tmp:clear - Clear session, cache, and socket files from tmp/
  • rake tmp:create - Creates tmp directories for sessions, cache, and sockets
  • rake tmp:sessions:clear - Clears all files in tmp/sessions
  • rake tmp:sockets:clear - Clears all ruby_sess.* files in tmp/sessions

Źródło na:
http://www.tutorialspoint.com/ruby-on-rails/rails-and-rake.htm
Wszystko w jednym miejscu, krótko i treściwie.

GIT - używanie, czyli codzienne starcia z urojeniami Linusa

Mimo, że używam GIT'a na co dzień, to gdyby mnie ktoś zapytał dlaczego, udzieliłbym nieco wymijającej odpowiedzi. Osobiście nie mam o nim dobrego zdania. System wygląda na projekt, który miał być mały, a stał się za duży. Co ciekawe tak chyba było w istocie. Linus tworząc Linuxa napisał system kontroli wersji dla tego projektu. Zaczęto go używać powszechnie, bo na wolnej licencji , bo tworzył to wielki Linus... Diabli wiedzą dlaczego...
Logiki tam z lupą szukać. Weźmy pierwszy z brzegu przykład. Utworzenie nowej gałęzi wygląda normalnie:
git branch test
ale przełączenie się na nią to już prawą nogą za lewe ucho:
git checkout test
O kurde! COŚ jest nie tak. Sprawdzamy wobec tego co to jest checkout:
http://www.diki.pl/slownik-angielskiego/?q=checkout&x=0&y=0
I dowiadujemy się, że to kasa, bądź wymeldowanie. Na litość boską! Towarzyszu Linusie, skąd ci to wpadło do głowy? Słówko "branch" (gałąź) cię boli? Czyli gałąź niedokładnie opisuje gałąź? Lepiej do przełączenia na gałąź użyć słowa kasa-wymeldowanie? Dlaczego nie "cegła" albo "sznurówka"? Tak samo przypadkowe i tak samo dobre.
Myślisz drogi czytaczu, że to odosobniony przypadek? Można książkę napisać o głupotach GIT'a. Zresztą niech przemówią fakty. Wrzuć w google frazę "git commit", a zaraz potem "bazaar commit". Sprawdź za każdym razem ilość wyników. I co? Tobie też wyszło, że stron wyjaśniających działanie GIT'a jest (uwaga!) stopiętnaścierazywięcej? Albo 115 razy więcej?! Każdy, kto choć chwilę się nad tym zastanowi natychmiast odrzuci kwestię popularności GIT'a versus Bazaar'a. Bazaar jest po prostu prosty :-) Fajnie wyszło: "po prostu prosty". Ale tak jest w istocie. Bazaar jest prosty. W Bazaar aby cofnąć ostatni commit piszesz:
bazaar uncommit
O zgrozo! Jak prosto i domyślnie!
Sprawdźmy więc jak to się robi w GIT:
git revert 1ed35d3
To nie ściema! Tak jest naprawdę. I to jest skrócona wersja! Użyłem klucza tylko w wersji 7-dmio znakowej. A co by było gdybym musiał użyć całego klucza SHA? Drogi Linusie! Brawa za kreatywność.
Pomijam tu kwestię, że na przykład, w skądinąd dobrej książce o Git'cie:
http://progit.org/book/pl/
na próżno będziesz szukał informacji na temat jak cofnąć ostatni commit. Ciekawe no nie? Ciekawym też jest fakt, że w ogóle taka książka jest potrzebna. Wiem, bo ją przeczytałem. Nie dlatego abym był masochistą. Nie zwariowałem. Przeczytałem, bo trzeba się było nauczyć. Ale, jak widać, jej przeczytanie nie daje nawet tak podstawowej wiedzy jak cofnąć commit. A teraz uwaga... aby korzystać bezproblemowo z Bazaara wystarczy poświęcić kwadrans na przeczytanie jednego tutoriala. Więcej tutoriali ani książek nie ma. Nie ma... zgadnij dlaczego? Ano dlatego, że nie potrzeba. Tam wszystko jest prosto i kawa na ławę. Jeden tutorial i po sprawie. Po co się rozpisywać skoro wszystko jest proste?
Już słyszę te pytania: "dlaczego, baranie, nie używasz więc Bazaara?". Ano spuszczę głowę i odpowiem, że dla tego samego co durnych windowsów. Wszędzie do tego coś jest. A to wtyczka do NetBeansów i Idei. A to u naszego dostawcy usług hostingowych pełne wsparcie i tutoriale. Masakra! Uległem wielkości githuba. Stuliłem po sobie uszy, przymknąłem paszczę i używam tej durnoty. Mam nadzieję, że Ty, jak już dotarłeś do końca tych słów, to chociaż obejrzysz Bazaara i zapłaczesz dlaczego takie wspaniałe narzędzie pozostaje w cieniu fantasmagorii wielkiego Linusa.

Ach... byłbym zapomniał... kolejny z milionów tutoriali o GIT'cie:
http://marioosh.5dots.pl/2009/06/27/gitowy-system-kontroli-wersji.html
tam chociaż jest coś o cofnięciu commita...

devPytania.pl - serwis dla programistów

Ciekawy jestem jak to się będzie rozwijać...

Póki co jest to dobrze zrobiony serwis pod względem programistycznym. Wyglądem nieco przypomina StackOverflow.
http://devpytania.pl
W skrócie: pytania i odpowiedzi programistów.  Blogi programistów.

czwartek, 10 marca 2011

Vim - kompletny plik _vimrc, lub _gvimrc

Okazało się, że publikacja kilku wpisów wywołała żywe zainteresowanie Vim'em jako hiper-szybkim edytorem tekstu. Zdaję sobie sprawę, że na początku ciężko jest przebrnąć przez ogrom możliwości konfiguracyjnych. Zamieszczam więc tutaj jedną z wersji naszego pliku _gvimrc jako przykład:
set nocompatible
source $VIMRUNTIME/vimrc_example.vim

"Nie chcę aby gvim był podobny do windowsów
"source $VIMRUNTIME/mswin.vim
"behave mswin

"nie zawijaj wierszy
set nowrap

"wcięcia
set tabstop=4
set shiftwidth=4

"poprawne odkodowywanie plików wraz zautomatycznym ich rozpoznawaniem
set fileencodings=utf-8,latin2
set enc=utf-8

"chcę zachować pliki przed zmianą, tyle że w tym katalogu
set backupdir=d:/Archiwum/vim_backups

"schemat kolorów
"colorscheme desert
colorscheme murphy

"wielkość okna
set columns=120
set lines=60
winp 0 0

"ignorowanie wielkości liter podczas wyszukiwania
set ignorecase

"ukrywanie wskaźnika myszy gdy się pisze
set mousehide
set linebreak

"autocomplete po naciśnięciu Ctrl-P lub Ctrl-N
set completeopt=longest,menuone
inoremap <expr> <CR> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>"
inoremap <expr> <C-n> pumvisible() ? '<C-n>' :
  \ '<C-n><C-r>=pumvisible() ? "\<lt>Down>" : ""<CR>'
inoremap <expr> <M-,> pumvisible() ? '<C-n>' :
  \ '<C-x><C-o><C-n><C-p><C-r>=pumvisible() ? "\<lt>Down>" : ""<CR>'

"Włączam pokazanie numerów wierszy
set number
set numberwidth=4 "ale nie więcej niż 9999

"na dole i na górze zostawiam 5 linijek żeby coś widzieć
set scrolloff=5

"set cursorcolumn " highlight the current column
set cursorline " highlight current line
set incsearch " BUT do highlight as you type you search phrase

"Obsługa foldingu czyli zwijania i rozwijania wierszy
set foldmethod=manual
let Tlist_Auto_Open=0 " let the tag list open automagically
let Tlist_Compact_Format = 1 " show small menu
let Tlist_Ctags_Cmd = 'ctags' " location of ctags
let Tlist_Enable_Fold_Column = 0 " do show folding tree
let Tlist_Exist_OnlyWindow = 1 " if you are the last, kill yourself
let Tlist_File_Fold_Auto_Close = 0 " fold closed other trees
let Tlist_Sort_Type = "name" " order by
let Tlist_Use_Right_Window = 1 " split to the right side of the screen
let Tlist_WinWidth = 40 " 40 cols wide, so i can (almost always) read my functions

"Ideą, która przyświecała mi przy tworzeniu rozwinięć były dwie literki i "#".
"Wszystko po to aby się nie myliło z normalnym pisaniem. Ten # całkowicie to
"separuje.
ab <ph# <?php echo; ?><left><left><left><left>
ab fo# for($i=0; $i<10; ++$i) {
}<up>A
ab if# if() {
}<Esc><up><End>3<Left>
"W poniższym rozwinięciu ciekawostka jest spacja na końcu wyrażenia.
"Denerewującą cechą rozwinięć jest to, że na końcu każdego wstawiają ten biały znak,
"który je rozwija (np. CR albo spacja). Dzięki temu, że na końcu jest spacja
"poszukiwanie w linii (f) szuka właśnie spacji i dostawia na końcu biały znak
"znajdując się dokładnie na nazwie funkcji.
ab fu# function nazwa_funkcji() {
}<Esc><Up><Home>f
ab $th# $this-><Esc>F-

"Rozwinięcia dla html
ab <ht# <html>
<head>
<title></title>
</head>
<body>

</body>
</html><Esc>3<up>
ab <di# <div class=""></div><Esc><Home>f>
ab <p# <p></p><Esc><Home>f>
ab <fo# <form>
</form><Esc>kFm
ab <ta# <table>
<tr>
<td></td>
</tr>
</table><Esc>2<Up><Home>f>
ab <ul# <ul>
<li></li>
</ul><Esc><Up><Home>f>
ab <li# <li></li><Esc><Home>f>
ab <h1# <h1></h1><Esc><Home>f>
ab <h2# <h2></h2><Esc><Home>f>
ab <h3# <h3></h3><Esc><Home>f>
ab <in# <input id="" name="" type="text" class="" /><Esc>F<3f"
ab <a# <a href="" title=""></a><Esc>4F"
ab cl# class=""<Esc>2F"

"wstawianie nawiasików
map \( <Esc>a('')<Esc><Left>i
map \[ <Esc>a['']<Esc><Left>i
map \{ <Esc>a{
}<Esc><Up>A


"otaczanie
map \h1 <Esc>bi<h1><Esc>ea</h1><Esc>F<
map \h2 <Esc>bi<h2><Esc>ea</h2><Esc>F<
map \h3 <Esc>bi<h3><Esc>ea</h3><Esc>F<
map \sp <Esc>bi<span><Esc>ea</span><Esc>F<
map \a <Esc>bi<a href="" title=""><Esc>ea</a><Esc>3F"

"zakomentowanie linii znaczkiem # (dla Ruby)
map <C-K> mkI#<Esc>`k

"zakomentowanie linii znaczkiem //
map <C-L> mkI//<Esc>`k

"usunięcie wyrazu, na którym stoi kursor
map <C-D> bdw
W następnych wpisach wyjaśnię co ciekawsze fragmenty.