piątek, 29 października 2010

Array#to_proc - a little experiment


class Array
def to_proc
Proc.new{|obj| self.map{ |e| obj.send(e) } }
end
end

["foo", "bar"].map(&[:upcase, :capitalize])
=> [["FOO", "Foo"], ["BAR", "Bar"]]


How do you like it ?

We could also go this way:


class Array
def to_proc
Proc.new{|obj| self.map{ |e| e.to_proc.call(obj) } }
end
end


Maybe not the fastest thing in the world but definitely elegant.

piątek, 10 września 2010

Growing Object-Oriented Software Guided by Tests

Growing Object-Oriented Software Guided by Tests jest chyba jedną z najlepszych książek jakie przeczytałem w życiu i moim zdaniem powinna być obowiązkowa na programistycznych studiach. Jeśli masz jeszcze jakieś obiekcje i wymówki to po tej książce już nie będziesz ich mieć. Wspaniale napisana książka, która krok po kroku pokazuje jak stworzyć dobrze działający program z odesparowaną logiką i widokiem, dobrze przetestowany na różnych poziomach. To wszystko w duchu Agile czyli ikrementacyjnie, tak by po każde iteracji było coś nowego działającego, przetestowanego, by jakość kodu wzrastała wraz z naszym rozumieniem domeny biznesowej. Pomimo odstraszającej ceny jak na polskie warunki i tak warto kupić.

Growing Object-Oriented Software Guided by Tests (strona książki)

poniedziałek, 30 sierpnia 2010

Dynamically removing some website content in Rails 3 Way (Prototype, UJS)


<div id="search">
  <div class="results">
    Search results
  </div>
  <%= link_to 'Reset results', '#', :'data-remove' => '#search .results' %>
</div>



document.observe("dom:loaded", function() {
$(document.body).observe("click", function(event) {
element = event.findElement();
resetWhat = element.readAttribute('data-remove')
if(resetWhat) {
resetable = $$(resetWhat);
for(var i=0; i< resetable.size(); i++) {
resetable[i].remove();
}
event.stop();
return false;
}
return true;
});
});



You could implement data-show, data-hide, data-toggle, data-appear etc. in the same easy way :-) I can certainly imagine hundreds of applications for such data-* custom attributes. Hopefully, we will have some libraries which will make good use of this pattern.



You can check it live here: Live heroku application with demos

Visit rails3-demos github repository to see the source code and colaborate on other demos. Be creative!

środa, 18 sierpnia 2010

Module#attr_accessor_with_default by Rails3 (and its speed measured by benchmark)

Did you know that every time you change the value it will recreate reader method?

class Module
# Declare an attribute accessor with an initial default return value.
#
# To give attribute :age the initial value 25:
#
# class Person
# attr_accessor_with_default :age, 25
# end
#
# some_person.age
# => 25
# some_person.age = 26
# some_person.age
# => 26
#
# To give attribute :element_name a dynamic default value, evaluated
# in scope of self:
#
# attr_accessor_with_default(:element_name) { name.underscore }
#
def attr_accessor_with_default(sym, default = nil, &block)
raise 'Default value or block required' unless !default.nil? || block
define_method(sym, block_given? ? block : Proc.new { default })
module_eval(<<-EVAL, __FILE__, __LINE__ + 1)
def #{sym}=(value) # def age=(value)
class << self; attr_reader :#{sym} end # class << self; attr_reader :age end
@#{sym} = value # @age = value
end # end
EVAL
end
end



The effect is like that:


BigNumber = 1_000_000

def test
BigNumber.times do
yield
end
end

Benchmark.bmbm do |x|

x.report("normal") do
klass = Class.new
klass.class_eval {attr_accessor :age}
k = klass.new
test do
k.age = 12
end
end

x.report("default") do
klass = Class.new
klass.class_eval { attr_accessor_with_default :age, 25 }
k = klass.new
test do
k.age = 12
end
end
end


Rehearsal -------------------------------------------
normal 0.230000 0.000000 0.230000 ( 0.230735)
default 5.500000 0.000000 5.500000 ( 5.503365)
---------------------------------- total: 5.730000sec

user system total real
normal 0.220000 0.000000 0.220000 ( 0.220314)
default 5.440000 0.000000 5.440000 ( 5.439583)

String#constantize . Can it be faster ?


BigNumber = 100_000
Constants = ["Rails", "ActiveSupport::Autoload", "ActiveSupport::Dependencies::LoadingModule"]

def test
BigNumber.times do
Constants.each do |c|
yield c
end
end
end

Benchmark.bmbm do |x|

x.report("custom ok") do
test do |const|
const = '::' << const unless const.starts_with?("::")
eval(const)
end
end

x.report("custom wrong") do
test do |const|
eval(const)
end
end

x.report("active support implementation") do
test do |const|
names = const.split('::')
names.shift if names.empty? || names.first.empty?

constant = Object
names.each do |name|
constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name)
end
constant
end
end
end




And the results for Ruby 1.9.2 are:


Rehearsal --------------------------------------------------
custom ok 5.220000 0.020000 5.240000 ( 5.412122)

custom wrong 4.550000 0.020000 4.570000 ( 4.716120)

active support 1.560000 0.000000 1.560000 ( 1.597956)
implementation
---------------------------------------- total: 11.370000sec

user system total real
custom ok 5.180000 0.000000 5.180000 ( 5.273098)

custom wrong 4.450000 0.030000 4.480000 ( 4.547156)

active support 1.570000 0.000000 1.570000 ( 1.576236)
implementation

wtorek, 6 lipca 2010

Rails 3 bug that occures when using inherited callback without calling super . Nasty error when scopes are called on STI classes.

In case you are using the inherited callback in Rails3, don't forget to call super.

class BaseAttribute < ActiveRecord::Base

def self.inherited(klass)
super

klass.base_attribute_class.class_eval do
extend MyModule
end
end

Otherwise STI might not work properly. In my case for every inherited class I was looking for another one based on their names and extending them with some module which was defining scopes. Unofortunatelly I was getting errors that some methods was called on NilClass when I tried to use those scopes. Calling the super helped. Another thing is that with out using
super
the method
subclasses
also works badly.

niedziela, 27 czerwca 2010

Przepis na koktajl

Stopień trudności: 1/10.

Wymagane urządzenia: blender.

Do zrobienia po jednej porcji dla dwóch osób.

Składniki:

  • 2 banany raczej większe niż mniejsze

  • 2 nektarynki średniego rozmiaru

  • 160g jogurtu greckiego

  • 200g kefiru



Procedura działania:
Banany obrać i wrzucić do odpowiedniego naczynia.
Nektarynki pozbawione pestek także tam wrzucić.
Zalać jogurtem i kefirem.
Składniki zmiksować i przelać do szklanek lub kufli.
Podawać po ochłodzeniu w lodówce.

wtorek, 15 czerwca 2010

Vpn client (vpnc) on ubuntu 10.04 (Lucid Lynx) recipe


sudo apt-get install libgcrypt-dev subversion subversion-tools
svn checkout http://svn.unix-ag.uni-kl.de/vpnc/tags/vpnc-0.5.3/ #visit the site to check for newest version
cd vpnc-0.5.3/
make
./pcf2vpnc cisco_vpn_profile_that_the_company_gave_you_for_windows_client.pcf > example.conf
sudo ./vpnc ./example.conf

niedziela, 13 czerwca 2010

Powtarzanie transakcji w Rails

Czy ktoś z was próbował automatycznie powtarzać transakcje w Rails dla których poszedł deadlock? Nie znalazłem żadnego prostego rozwiązania. Udało mi się to wprwadzie napisać w przedostatni dzień w pracy dla Cubiware, jednak moje rozwiązanie miało pewne ograniczenia. Jedyny prosty pomysł jaki mi przyszedł do głowy tzn. taki, który by wymagał tylko zamknięcia kodu w odpowiednim bloku:


RepeatTransaction.start do

end


to użycie w tym celu systemowego fork(), co jednak wydaje się być bardzo ciężkim (wydajnościowo) rozwiązaniem.

Wrocław.

Jutro pierwszy dzień w nowej pracy we Wrocławiu. Dzisiaj nareszcie skończyłem się wypakowywać. Mam nadzieję, że tym razem dłużej tu pomieszkam, tym bardziej, że całkiem ładnie się nam z Martą trafiło. Naprawdę nie widzi mi się następna przeprowadzka.

czwartek, 7 stycznia 2010

Świat Książki - nie polecam! (To nie Amazon). Plus słów kilka o obsłudze klienta

W telegraficznym skrócie.

Zamówiłem trzeci tom Pana Lodowego ogrodu, ale przesłali mi drugi. W sumie nic by się nie stało gdyby później przysłali mi dobrą książkę, jednak bardzo ubolewam nad tym, że kazali mi wpierw odesłać poprzednią na swój koszt, który oni mi potem zwrócą. Być może moje żądania odnośnie obsługi klienta są dla niektórych wygórowane i śmieszne jednak naprawdę nie uważam, że firma po popełnieniu błędu powinna jeszcze kazać klientowi podejmować jakieś czasochłonne działania, bo inaczej ona tego błędu nie naprawi. Na boga, przecież to, że ja im nie odeślę tej książki kosztuje ich max 30zł straty. Czy naprawdę nie lepiej odżałować tych pieniędzy by zwiększyć satysfakcję konsumentów i po prostu od razu wysłać mi dobry tom? Może jeszcze się doczekamy, że u nas będzie tak jak w Amazonie? Oh, jak wspaniały pochwalny post bym napisał, że doczekaliśmy się, że i u nas jest jak w Ameryce, gdyby Świat Książki odżałował te 30 zeta i wysłał mi trzeci tom. Oh, z jak wielką przyjemnością mówiłbym wszystkim moim przyjaciołom, jak dobrze mnie potraktowali i nawet jeśli nie zawsze jest najtaniej to przynajmniej wiem, że jak popełnią błąd to nie będę musiał w śniegu iść na pocztę odsyłać książki.

Na marginesie: W pewnej amerykańskiej firmie, w której projekcie uczestniczę zdarzało się, że oferowano klientom rekompensatę, za to że niektóre (wcale nie kluczowe) dane nie wyświetlały poprawnie. Wszystko się liczyło dobrze, klienci płacili tyle ile powinni, żaden wielki błąd w oprogramowaniu nie wystąpił ale niektóre rzeczy nie wyświetlały się prawidłowo i zostały naprawione już jedeń dzień po zgłoszeniu uwagi. A jednak klient został potraktowany tak jakby go nieuczciwie policzono, jakby stało się coś wielkiego i dostał darmowy okres korzystania z usługi. Chyba jednak wciąż jest jakaś mentalna różnica.