Ruby Rails idiom för att testa om en variabel ändrats

18 views
Skip to first unread message

Hans

unread,
Sep 20, 2015, 4:33:53 AM9/20/15
to Svenska Rails-listan
Hej, jag har ett programmeringsproblem som jag inte hittar någon lösning på. 
Jag använder ruby 1.9 och rails 3.2 och har lärt mig att ett ofta använd ruby idiom när man vill effektivisera koden är 

def get_var
  @var ||= SomeClass.new()
end

som endast uppdaterar @var om den ändrats. I rails kan den användas för att endast uppdatera @var om den ändras inom en request.
När jag refactorer min kod för att använda detta idiom har jag för enkelhets skull använt följande kodning, tex

  def self.directors_of_studies_in_county_council((county_council) )
   @directors_of_studies ||= self.directors_of_studies_in_county_council?(county_council)  
  end
  
  def self.directors_of_studies_in_county_council?(county_council)    
    directors_of_studies=[]
   User.where(:county_council_id=>county_council.nil_or.id).order("surname COLLATE utf8_swedish_ci ASC").includes(:user_roles).each do |user| directors_of_studies<<user if user.is?('director_of_studies')  end 
    return directors_of_studies 
  end

Detta kanske fungerar men jag vet att nedanstående användning inte fungerar. Variabeln verkar anta värden som andra samtidiga användare har satt

def self.find_filter(params)
  @targetGroup_filter ||= TargetGroupFilter.find_filter?(params)

def self.find_filter?(params)
  TargetGroupFilter.where(:controller_name=>params[:controller_name] || params[:controller],:user_id=>User.current.id).first 
end

Har jag missförstått idiomet? Fungerar det inte för ruby 1.9? Använder jag det fel i  bägge fallen ? Är endast det sistnämnda uttrycket felaktigt och i så fall varför ? Hur skall det användas ?

joel.j...@gmail.com

unread,
Sep 20, 2015, 5:32:31 AM9/20/15
to rail...@googlegroups.com

Eftersom du memoizar värdet i en klassmetod så kommer det att sparas tills klassen laddas om (dvs din app startas om). Vill du ha det ”per request” eller dvs per instans av klassen så bör du sätta det på instansnivå.


I regel så undviker jag memozation om jag inte ser att det finns ett reelt behov (väldigt långsamma återkommande queries etc som dyker upp fler gånger per request). Det kan lätt leda till svårhittade buggar.


Om du behöver spara undan något på klass nivå så måste du se till att rensa värdet efter varje request. 

Jag antar att ”User.current” är det vanligaste exemplet. Det är helt klart något som bör undvikas om man kan men i det fallet du måste så är det vanligaste sättet att i en controller använda around_filter.


t.ex.


class SomeAuthenticatedController < ApplicationController

  around_filter :set_current_user


  private


  def set_current_user

    User.current = current_user if logged_in?

      yield

    ensure

      User.current = nil

  end

end

class User
  def self.current
    Thread.current[:current_user]
  end

  def self.current= user
    Thread.current[:current_user] = user
  end
end

Vi använder Thread.current istället för vanlig memozation då vanlig memozation inte kommer vara säkert i miljöer som använder trådar (t.ex. om du kör med puma som server).




--
--
Du har erhållit detta e-postmeddelande eftersom du prenumererar på Svenska Rails-listan (Google Groups).
För att posta till listan, skicka e-post till rail...@googlegroups.com
För att gå ur listan, skicka e-post till rails-se+u...@googlegroups.com
För fler alternativ, besök listan på http://groups.google.com/group/rails-se?hl=sv

---
Det här meddelandet skickas till dig eftersom du prenumererar på gruppen "Svenska Rails-listan" i Google Grupper.
Om du vill sluta prenumerera på den här gruppen och inte längre få någon e-post från den skickar du ett e-postmeddelande till rails-se+u...@googlegroups.com.
Fler alternativ finns på https://groups.google.com/d/optout.

Henrik Nyh

unread,
Sep 20, 2015, 5:34:00 AM9/20/15
to rail...@googlegroups.com
Vid en snabb titt tror jag det finns iallafall två problem med

    def self.find_filter(params)
      @targetGroup_filter ||= TargetGroupFilter.find_filter?(params)
    end

Det ena är att eftersom du sätter en instansvariabel inuti en klassmetod så lagras värdet i själva klassen (en controller?). Klasser är globala och delas. Två besökare som båda använder UserController kommer använda olika instanser av controllern, men de kan dela på samma controller-klass.

Lösningen på detta är att göra find_filter till en instansmetod istället.

Det andra är att du använder samma cache-variabel, @targetGroup_filter, oavsett vilka params som skickar in. Så om jag skickar in params {foo: 1} så kommer den hitta ett filter och sen cacha filtret. Om jag sen skickar in params {foo: 2} så kommer den använda det redan cachade värdet.

Enklaste lösningen är nog att hitta färdig kod för det här, t.ex. https://github.com/jnicklas/memoit, som hanterar olika argument/params. Eller koda nåt eget om du verkligen vill – du kan googla "memoize ruby hash" så hittar du lite tips.




Henrik Nyh

unread,
Sep 20, 2015, 5:35:50 AM9/20/15
to rail...@googlegroups.com
2015-09-20 11:33 GMT+02:00 Henrik Nyh <hen...@nyh.se>:
Enklaste lösningen är nog att hitta färdig kod för det här, t.ex. https://github.com/jnicklas/memoit, som hanterar olika argument/params.

Memoit funkar inte med Ruby 1.9, förresten. Du kan testa den här: https://github.com/dkubb/memoizable 

Hans Marmolin

unread,
Sep 20, 2015, 9:32:38 AM9/20/15
to rail...@googlegroups.com
Hej alla som svarat och tack för all snabba svar
Min slutsats är att jag gjort minst 3 fel; använt idiomet på klassparametrar, använt idiomet med variabler i anropet, använt idiomet på metod som kan returnera nil.
Efter förnyad googling (memoize ruby hash) efter era anvisningar, ser jag att det är möjligt att också använda det i dessa fall men då krävs mer komplicerad kodning, eller att man använder memoit gemet (ser inget om at det inte funkar för ruby 1.9 i gem beskrivningen). Många avråder dock för att använda detta idiom då det oft kostar mer än vad man tjänar på det.

Min slutsats är därför att inte använda denna teknik. Om behov finns av att öka system prestanda är andra tekniker som tex rails caching att föredra (se tex http://cmme.org/tdumitrescu/blog/2014/01/careful-what-you-memoize/)
Reply all
Reply to author
Forward
0 new messages