Running with Ruby

Tag: template

Controller stylesheet tag – style dla danego kontrolera

Tworząc arkusze stylów dla projektów często zdarza się, że chcemy dodać styl specyficzny dla danego kontrolera. Przykładowo w kontrolerze wyświetlającym pewien specyficzny rodzaj danych, chcemy mieć inne formatowanie. W przypadku Railsów składać się to będzie na dwie części – najpierw w ApplicationControlerze dodamy sobie metodę która ustawiać nam będzie trzy zmienne dostępne zarówno w kontrolerach jak i widokach:

  • @action_name – nazwa akcji
  • @controller_name – nazwa kontrolera
  • @module_name – nazwa modułu

Przy tym ostatnim (@module_name) należy pamiętać, że możemy mieć kilka “poziomów”, np: Admin::Core::Logs, tak więc będziemy to trzymać w tablicy ([Admin, Core, Logs]). Przyjmiemy też konwencję, że nazwy będą przechowywane z małej litery.

  def get_module_and_controller_name
    my_class_name = self.class.name
    @action_name = self.action_name
    if my_class_name.index("::").nil? then
      @module_name = nil
      @controller_name = self.class.to_s.split('Controller').first.underlinize
    else
      @controller_name = self.class.to_s.split("::").last.split('Controller').first.underlinize
      if (my_class_name.split("::").length > 2)
        @module_name = my_class_name.split("::")
        @module_name = @module_name[0, @module_name.length-1].collect{|m| m.underlinize}
      else
        @module_name = Array.new(1, my_class_name.split("::").first.underlinize)
      end
    end
  end

Taką metodę ustawiamy jako before_filter.

Teraz jeszcze tylko helper do widoków:

  def controller_stylesheet_link_tag(module_name, controller)

    if module_name.class.to_s.downcase == 'array'
      module_name = module_name.join('/')
    end
    ex = FileTest.exist?(File.join(Rails.root, 'public',
        'stylesheets', module_name, "#{controller}.css"))

    stylesheet_link_tag "#{module_name}/#{controller}" if ex
  end

oraz wywołanie:

controller_stylesheet_link_tag(@module_name, @controller_name)

Warto wspomnieć, że powyższe rozwiązanie działa na 3.0.7. Dla 3.1.0 będzie troszkę inaczej, ale to pozostawiam wam :)

Wersja dla Padrino jest troszkę mniej “fajna” (aczk. wymaga before_filtre’a):

  def controller_stylesheet_link_tag
    path = request.path.split('/')
    styles = ''
    while path.count > 0
      file = File.join(Padrino.root, 'app', 'stylesheets', 'controllers', path)+'.less'
      if File.exists?(file)
        styles += stylesheet_link_tag 'controllers'+path.join('/')
      end
      path.delete(path.last)
    end
    styles
  end

Rails3 + link_to + update z pominięciem edit

Ostatnio przeglądając kod Redmine’a natknąłem się na fajny kawałek kodu. Nigdy się nad tym nie zastanawiałem i w zasadzie, nigdy zbytnio nie potrzebowałem.

Jak za pomocą linka, zaktualizować wybrany atrybut instancji modelu?

Zazwyczaj do aktualizacji danej instancji korzystam z edit’a oraz forma odpowiedniego do tego. Dzisiaj jednak nadarzyła się  ku temu okazja.

Mam w systemie model o nazwie Cron który odpowiada za cykliczne odpalanie komend/metod które są w nim yieldowane. Np:

    Cron.backup{
      # Wykonaj nową kopię zapasową
      Backup.create
      # Usuń stare kopie zapasowe
      Backup.sweep(Setting::Cron.backup_lifetime)
    }

Powyższy “cron” o nazwie backup odpala się co 7 dni wykonując kopię zapasową systemu. Usuwa przy okazji kopie starsze niż backup_lifetime. Z ciekawostek warto wspomnieć, że dzięki “fajności” Rubiego, mogłem napisać sobie w bardzo prosty sposób własne mapowanie, nie po kolumnach a po wierszach. Właśnie dzięki temu metody modelu Cron są generowane dynamicznie. Ale to jest temat na oddzielną notkę.

Wracając do sedna sprawy. Model Cron korzysta z pola updated_at aby określić kiedy Cron był ostatnio odpalany. Jeśli było to dawniej niż zadana w instancji częstotliwość – uruchamia kod yieldowany.

Problem pojawił się kiedy chciałem dorobić “wymuszone” odpalenie instancji Crona. Najprostszą metodą do zrobienia tego, było wyedytowanie updated_at i nadanie mu wartości np. 1.year.ago. Rozwiązać można to było tworząc metodę w stylu force. Nie byłoby to jednak zgodne z RESTem. Bardzo fajnie rozwiązano to w Redminie. Mianowicie tworzony jest link_to który zawiera wszelkie potrzebne informacje. Jak taki link wygenerować? Bardzo prosto:

link_to "Wymuś", cron_path(task, 'cron[updated_at]' => 1.year.ago), :method => :put

Metoda link_to stworzy nam link który zostanie zrealizowany przez metodę update na instancji modelu Cron. Dzięki dodaniu parametru:

'cron[updated_at]' => 1.year.ago

informujemy Railsy, że ma naszemu cronowi przypisać datę ostatniego wykonania na rok wstecz. Dzięki temu, przy najbliższej możliwej okazji, kod z Crona zostanie zrealizowany.

Copyright © 2018 Running with Ruby

Theme by Anders NorenUp ↑