# Devise
user = User.new(email: 'test@example.com',
password: 'password',
password_confirmation: 'password')
user.save
środa, 15 stycznia 2020
Utworzenie z konsoli nowego użytkownika Devise w Rails
wtorek, 7 stycznia 2020
* - (gwiazdka, asterix) czyli operator splat w Ruby
def my_method(*args)
args.each {|arg| puts arg}
end
a wywołanie:
my_method(:a, "ala ma kota", 1, 2, 3.14)
A tak przy okazji... splat ma sporo więcej możliwości.
Opisane fajnie tutaj: https://www.honeybadger.io/blog/ruby-splat-array-manipulation-destructuring/
środa, 1 lipca 2015
OR zamiast AND w zapytaniach Rails
where - wynikowy SQL jest generowany zazwyczaj z AND. Na przykład w tym zapytaniu wszędzie w SQL'u jest AND:
VariantDetail.joins(product_detail: {product: [:channel, :type]},
variant: {starpacks: :version})
.all_active
.where(code: cover[:id], value: value,
pkb_cats_discr: {percent: percent.to_i},
pkb: {channel_id: channel.id, type_id: @type.id},
version: {code: ver.code})
Mi zaś, chodziło o to, aby kolumna
percent była rozważana dla dwóch wartości: jako 0 (zero) albo NULL.Okazało się, że można to zrobić w dość prosty sposób. Wystarczy użyć w klauzuli
where, tablicy.Na przykład tak:
{percent: [0, nil]}
co skutkuje takim kompletnym zapisem:
VariantDetail.joins(product_detail: {product: [:channel, :type]},
variant: {starpacks: :version})
.all_active
.where(code: cover[:id], value: value,
pkb_cats_discr: {percent: [0, nil]},
pkb: {channel_id: channel.id, type_id: @type.id},
version: {code: ver.code})
czwartek, 25 września 2014
Sumowanie wartości w rekordach wybranego już zestawu z bazy danych
A co jeśli chcemy zsumować ilość wszystkich towarów zakupionych przez klientów w tym zestawie?
Zawsze robiłem to tak:
@customers.sum(&:amount)No i fajnie. Działało. Działało do momentu, dopóki nie sumowałem ilości przepracowanych godzin, które to godziny było obliczane z dat. Z niewiadomych przyczyn generowana kolumna była w formacie string i sumowanie tworzyło wielki string.
Można to zrobić tak:
@customers.sum{|customer| customer.amount.to_i}
Problem znika.
piątek, 19 września 2014
Wstawienie własnego parametru do ścieżki Paperclip'a
path" Paperclip'a nie występuje nic podobnego do "firm_id".
Innymi słowy chciałem zapisać coś takiego:
has_attached_file :scan_picture, :styles => {:thumb => '50x50>'},
:default_url => '/images/:style/missing.png',
:url => "/system/scan_pictures/:firm_id/:id/:style/:basename.:extension",
:path => ":rails_root/public/system/scan_pictures/:firm_id/:id/:style/:basename.:extension"
Jest to jednak możliwe :-)
Zrobiłem tak:
w katalogu
/config/initializers stworzyłem plik paperclip_interpolations.rbw tym pliku umieściłem zapis:
Paperclip.interpolates('firm_id') do |attachment, style|
attachment.instance.move_out.customer.firm_id.to_s.parameterize
end
Tłumaczyć to można tak:Weź egzemplarz klasy, która wywołuje dołączanie pliku (
attachment.instance), pobierz z niego obiekt wyjazd (move_out), a z niego klienta i firmę, do której klient należy. Przekształć firm_id na string i użyj metody prametrize.
No i aby powyższe lepiej zrozumieć można przytoczyć jeszcze specyfikację
MoveOut (wyjazd):
class MoveOut < ActiveRecord::Base belongs_to :customer end
czwartek, 30 stycznia 2014
Dynamiczne dodanie pola do obiektu w Ruby
Więc robię to tak:
@customer = Customer.find(1)
@customer.class.module_eval { attr_accessor :age}
@customer.age = 69 #już niemłody
sobota, 23 lutego 2013
Króciutko o wysyłaniu emaili w Rails
Tworzę sobie model. Roboczo nazwijmy go UserMailer. Dziedziczy on po ActionMailer::Base. W modelu tym ustawiam metody, z których każda jest wzorem wysyłanego emaila.
Na przykład dla newslettera tworzę:
def newsletter(newsletter, email)
recipients email
from "#{Options::Program_name} - newsletter :: <#{Options::Email}>"
subject newsletter.subject
sent_on Time.now
body({
:body => newsletter.body
})
end
Przekazane parametry to:
newsletter- obiekt zawierający dane dla newslettera takie jak choćby temat;emailemaile (emaile) do wysyłki
newsletter, a nazywająca się :body wystąpi w widoku.Więcej na temat konstrukcji metod dla ActionMailer można znaleźć na stronie rails http://apohllo.pl/guides/action_mailer_basics.html
Jeśli już mam metodę, to przygotowuję dla niej widok. Dla takiej konfiguracji, jaką opisuję widoki winny być umieszczone w folderze /views/user_mailer czyli w nazwie zgodnej z nazwą modelu.
Widok powinien być standardowym plikiem html.erb z dopiskiem .text. W naszym przypadku dla metody newsletter widok powinien się zwać nomen omen newsletter.text.html.erb albo newsletter.text.erb. W widokach należy unikać stosowania zewnętrznych styli i w ogóle ograniczyć zabawę ze stylami, gdyż w klientach mailowych źle się to wyświetla. Należy raczej pozostać przy dawnym formatowaniu przy pomocy tabel.
|
Tu jest jakiś nagłówek ...................................
|
| <%= @body %> |
|
A tu jest jakaś stopka ...................................
|
@body, którą definiowaliśmy w metodzie jako :body.Pozostało już tylko jakoś wywołać tego newslettera. Robię to tak:
UserMailer.deliver_newsletter(newsletter_data, "jan.testowy@test.pl")W metodzie
deliver_newsletter() po podkreślniku występuje nazwa metody, którą klasa UserMailer ma wywołać. Obiekt newsletter_data zawiera w swych polach dane do wysyłki newslettera. Na przykład newsletter_data.subject zawiera temat newslettera.
czwartek, 8 listopada 2012
Różnica pomiędzy delete a destroy w Rails
Jeśli natomiast chcemy aby Rails panowało nad wszystkim zastosujmy destroy. Wolniej ale w zgodzie z wszelkimi regułami pisanej przez nas aplikacji.
Używanie w Rails tekstu typu String zamiast nazwy pola, metody, alboasocjacji
Czyli zamiast pisać tak:
customer=Customer.find(params[:id]) customer.orders.each do |order| # ... tu jakieś operacje z użyciem order .... end
możemy napisać tak:
customer=Customer.find(params[:id])
customer.send("orders").each do |order|
# ... tu jakieś operacje z użyciem order ....
end
piątek, 21 września 2012
Jak przesłać więcej niż jedną zawartość pola Ajaxem
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= observe_field "employee_last_name",
:url => {:action => :nazwa_metody_w_kontrolerze},
:update => "div_to_update",
:frequency => 1,
:with => "Form.serializeElements($('employee_last_name', 'employee_first_name'))" %>
No i jest. Teraz w metodzie nazwa_metody_w_kontrolerze zmienna parameters dostarcza nam imię i nazwisko pracownika w postaci:
params[:employee][:first_name] params[:employee][:last_name]
czwartek, 15 marca 2012
Mapy tras zgodne z architekturą REST W Rails
Tu szczegóły dla modelu Product:
w pliku routes.rb powstał zapis
map.resources :productsdzięki temu jednemu zapisowi powstała mapa tras:
| Metoda | Ścieżka | Akcja | Metoda pomocnicza |
|---|---|---|---|
| GET | /products | index | products_url |
| GET | /products/new | new | new_product_url |
| POST | /products | create | products_url |
| GET | /products/1/edit | edit | edit_product_url(:id => 1) |
| PUT | /products/1 | update | products_url(:id => 1) |
| GET | /products/1 | show | product_url(:id => 1) |
| DELETE | /products/1 | destroy | product_url(:id => 1) |
piątek, 9 marca 2012
Warunkowy operator trójargumentowy lub po prostu '?'
Zapis
label = length == 1 ? " argument" : " argumenty"Powyższe wyrażenie przypisuje do zmiennej label łańcuch tekstowy w liczbie pojedynczej lub mnogiej. Oczywiście w zależności od wartości logicznej warunku
length == 1Jeśli warunek jest prawdziwy to liczba pojedyncza, jeśli warunek jest fałszywy, to liczba mnoga.
piątek, 20 stycznia 2012
Jak wyeleminować duplikaty w tabelach złączeń
has_and_belongs_to_many :categories, :uniq => trueale niestety nie zawsze.
Aby zabezpieczyć się przed umieszczaniem duplikatów najpierw robimy migrację, która przygotuje unikalny indeks zabezpieczający bazę danych przed zduplikowaniem wpisu:
. add_index :categories_firms, [ :category_id, :firm_id ], :unique => true, :name => 'unique_by_category_and_firm' .Teraz co prawda nie da się zrobić duplikowanego wpisu, ale baza danych wywala błędy.
Przjdźmy więc do modelu firmy i tam dopiszmy do has_and_belongs_to_many:
. has_and_belongs_to_many :categories, :uniq => true, :before_add => :validates_category .czyli zanim dokona się wpisu należałoby wykonać jakąś walidację:
def validates_category(category) raise ActiveRecord::Rollback if self.categories.include? category end
Proste, łatwe i przyjemne.
Inspirację zaczerpnąłem z
http://stackoverflow.com/questions/4988630/habtm-uniqueness-constraint
poniedziałek, 19 grudnia 2011
Jak zamienić string na nazwę klasy
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"
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].removeSelectedOptionWpisy 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ć!
czwartek, 28 kwietnia 2011
Prosta konfiguracja autocomplete w Ruby on Rails
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_completeoczywiście zazwyczaj nie działa :-(
Można więc ręcznie. Ściągnij zatem plugin i rozpakuj do
/vendor/plugins/auto_completezazwyczaj 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 . . . defCo 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) endSprawdzam... 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} %>
.
piątek, 22 kwietnia 2011
count vs length vs size w Rails
W Railsowym ActiveRecord wygląda na to, że są trzy drogi podawania wielkości asocjacji:
- 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.
- 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.
- 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.
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, 6 kwietnia 2011
Odcinanie ogonków, czyli zamiana polskich liter w Ruby on Rails
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"
poniedziałek, 4 kwietnia 2011
Fajna książka o Ruby on Rails v3
http://ruby.railstutorial.org/ruby-on-rails-tutorial-book#sec:development_tools
niedziela, 3 kwietnia 2011
Rails autocomplete - uruchamianie akcji po kliknięciu
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ę :-)