Skip to content
This repository has been archived by the owner on Dec 3, 2020. It is now read-only.

Контроллер

4urbanoff edited this page Nov 27, 2014 · 1 revision

При генерации новой модели автоматически создаются файлы клиентского и серверного контроллера

Клиентский контроллер

Имеет два основных метода:

  • stop() останавливает обработку запроса

  • redirect( url ) производит переадресацию на другой адрес, вызывает метод stop(), url это строка типа /user/person или user/person (ведущий слеш не обязателен)

Рассмотрим файл app/client/javascripts/controllers/users.js клиентского контроллера модели User на примере:

Nali.Controller.extend Users:

  actions: 
    # объект - список экшенов
    # в каждом экшене, включая фильтры before и after 
    # доступна обрабатываемая коллекция в свойстве @collection,
    # фильтры, по которым она отфильтрована в свойстве @filters
    # и переданные параметры в свойстве @params
    # имя экшена указывается в следующем виде:
    # action/filter/filter/:param/:param, где action это само название
    # экшена, filter - некий параметр для выборки коллекции
    # params - параметр который нужно передать в контроллер
    # именование фильтров и параметров различается лишь тем,
    # что имена последних начинаются с двоеточия
    default: 'index'
      # экшен по умолчанию - если не найдено 
      # совпадения с другими экшенами	
      # запрос попадет в экшен index, также позволяет сокращать адреса 
      # т.е. /user или /users, автоматически расцениваются как
      # /user/index и /users/index соответственно
    before: 
      # объект - список предварительных фильтров
      'person': ->
        # метод выполнится перед экшеном person
        # @stop() - остановит обработку запроса
        # предотвратит запуск экшена и фильтров after
      'person, index, account': ->
        # метод выполнится перед экшенами person, index и account
      '! auth': ->
        # метод выполнится перед каждым экшеном кроме auth
      '! auth, index': ->
        # метод выполнится перед каждым экшеном кроме auth и index
    after:
      # объект - список последующих фильтров
      'person': ->
        # метод выполнится после экшена person
        # @stop() - остановит обработку запроса
      '! index': ->
        # метод выполнится после каждого экшена кроме index

    index: ->
      # тело экшена, срабатывает на запросы начинающиеся с
      # /users/index
      # /user/index
      # /user и /users - при условии, что указана опция default: 'index'
      # имя экшена в ссылках можно указывать, как в единственном, 
      # так и во множественном числе, остальная часть запроса, 
      # неподходящая под требования экшена,  если она есть, будет 
      # проигнорирована, например при запросе /user/man/18,
      # /man/18 - будет проигнорировано т.к. экшен не принимает 
      # фильтров и параметров
      # @collection будет содержать обрабатываемую коллекцию 
      # согласно заданных фильтров, здесь она будет пуста
      # @params объект принятых параметров, здесь он будет пуст
      # @filter объект фильтров коллекции, здесь он будет пуст
      @collection.add @Application.user
      # в коллекцию будет добавлен пользователь
      # ранее сохраненный в свойстве user объекта Nali.Application

    'person/gender/age': ->
      # сработает на запросы
      # /users/person
      # @collection будет пуста
      # @filters = {}
      # @params = {}
      # -----------------
      # /users/person/man
      # @collection - список юзеров, у которых: gender is man
      # @filters = { gender: 'man' }
      # @params = {}
      # -----------------
      # /users/person/man/18
      # @collection - список юзеров, у которых: gender is man, age is 18
      # @filters = { gender: 'man', age: '18' }
      # @params = {}
      # -----------------
      # /users/person/man/18/moscow/russia
      # @collection - список юзеров, у которых: gender is man, age is 18
      # @filters = { gender: 'man', age: '18' }
      # @params = {}
      # /moscow/russia - проигнорировано
      # -----------------
      @redirect 'home' unless @collection.length
      # перенаправит роутер на адрес /home если коллекция пуста
      # запрос попадет в default экшен контроллера Nali.Controller.Homes
      # остановит выполнение текущего запроса, вызвав метод @stop()

    'account/gender/age/:sortBy/:desc': ->
      # сработает на запросы
      # /users/account
      # @filters = {}
      # @params = { sort_by: null, desc: null }
      # -----------------
      # /users/account/man
      # @params = { sort_by: null, desc: null }
      # -----------------
      # /users/account/man/18
      # @filters = { gender: 'man', age: '18' }
      # @params = { sort_by: null, desc: null }
      # -----------------
      # /users/account/man/18/age
      # @filters = { gender: 'man', age: '18' }
      # @params = { sort_by: 'age', desc: null }
      # -----------------
      # /users/account/man/18/age/desc
      # @filters = { gender: 'man', age: '18' }
      # @params = { sort_by: 'age', desc: 'desc' }
      @collection.order by: @params.sortBy, desc: !!@params.desc
      # список юзеров будет отсортирован по возрасту по убыванию

    'auth/:token': ->
      # сработает на запросы
      # /users/auth
      # @filters = {}
      # @params = { token: null }
      # @collection будет пуста
      # -----------------
      # /users/auth/324hb3298723f98nf23f09
      # @filters = {}
      # @params = { token: '324hb3298723f98nf23f09' }
      # @collection будет пуста
      # -----------------
      unless @params.token?
        @stop()
        # остановит выполнение текущего запроса,
        # предотвратит вызов after фильтров
      else
        @autentication @params.token
        # вызов метода контроллера

  autentication: ( token ) ->
  # какие то действия

Порядок запуска экшена и его фильтров

При обработке запроса первыми вызываются фильры before (если они есть), затем тело экшена и фильры after (если они есть), фильтры вызываются в том порядке, в каком они описаны. Согласно коду из примера при обращении по адресу /user/account порядок запуска будет следующим:

  1. 'person, index, account' - before фильтр
  2. '! auth' - before фильтр
  3. '! auth, index' - before фильтр
  4. 'account/gender/age/:sortBy/:desc' - сам экшен
  5. '! index' - after фильтр

Общий порядок работы контроллера на примере

При запросе /users/account/man/18/age/desc роутер выявит совпадение с маршрутом, ведущим в экшен account контроллера Users, произведет разбор адреса на фильры { gender: 'man', age: '18' } и параметры { sort_by: 'age', desc: 'desc' }, после чего передаст управление контроллеру Users. Контроллер создаст запрос в модель User вида User.where gender: 'man', age: '18, получит коллекцию отобранных моделей, выполнит before фильтры, затем экшен account, в котором отсортирует коллекцию, и, после выполнения фильтров after, запустит отрисовку видов коллекции командой collection.show 'account', где 'account' - имя выполняемого экшена. В последнюю очередь контроллер даст роутеру команду на смену url в адресной строке браузера. Если во время выполнения экшена и его фильтров был вызван метод @stop(), контроллер завершает свою работу на текущем месте в порядке запуска и уничтожает коллекцию, вызвав collection.destroy().

Серверный контроллер

Предназначен для работы с моделями и запросами пользователя, каждый контроллер наследуется от общего родителя ApplicationController и расширяется модулем Nali::Controller. Данный модуль релизует методы save, destroy и select для работы с моделями, поэтому не стоит создавать свои методы с такими же именами, только если вы намеренно не хотите переопределить их.

Обращение к методам контроллера

Из любого места клиентской части приложения можно обратиться к методам контроллера для получения данных или совершения каких либо манипуляций с помощью метода query('controller.action', params, success, failure), который производит запрос к серверу в экшен action контроллера controller, передает объект параметров params. Если на сервере был вызван trigger_success( params ) - запустит success с параметрами params, если trigger_failure, то выполнится failure. Колбэки success и failure не являются обязательными агрументами. Пример:

class UsersController < ApplicationController
	
	include Nali::Controller
	
	def online
		# метод контроллера, params - хэш входящих параметров
		user = User.find params[ :id ]
		if user.online?
			trigger_success 'yes'
			# вызовем success-callback на стороне клиента 
			# передать можно как строку, так и объект
		else 
			trigger_failure 'no'
			# вызовем failure-callback на стороне клиента 
		end
	end

end
# запрос из любого места клиентской части приложения
@query 'users.online', id: 1, 
	( result ) ->
		console.log result + ', user online'
	( result ) ->
		console.log result + ', user offline'
# если пользователь онлайн
# > yes, user online
# если пользователь оффлайн
# > no, user offline	

Внутри контроллера

  • params - хэш полученных с клиента параметров
  • client - текущий клиент
  • clients - список всех подключенных сейчас клиентов

Фильтры

Это методы которые запускаются до или после экшена контроллера, очень похожи на фильтры контроллеров клиентской части, регистрируются спомощью методов:

  • before регистрирует фильтр выполняющийся перед обращением к любому методу контроллера
  • before_only регистрирует фильтр выполняющийся перед обращением к конкретным указанным методам контроллера
  • before_except регистрирует фильтр выполняющийся перед обращением к любому методу контроллера за исключением указанных
  • after регистрирует фильтр выполняющийся после обращения к любому методу контроллера
  • after_only регистрирует фильтр выполняющийся после обращения к конкретным указанным методам контроллера
  • after_except регистрирует фильтр выполняющийся после обращения к любому методу контроллера за исключением указанных

Для прерывания обработки запроса существует метод stop, который предотвращает переход обработки запроса в последующие фильтры и экшен (если был вызван в before фильтре). Методы-фильтры применимы к уже определенным методам save, destroy и select, а также для удобства их можно выносить в ApplicationController для применения их в других контроллерах.

class ApplicationController
	
	protected

		def check_auth
			stop unless @user = client[ :user ]
			# остановит запрос перед любым экшеном если в хранилище
			# текущего клиента отсутствует модель пользователя
			# прикрепленного, например, после авторизации
		end

end

class UsersController < ApplicationController
	
	include Nali::Controller

	before do check_auth end
	# сработает перед обращением в любой метод

	before_only :online do
		puts 'before_only_online'
		# сработает перед обращением в метод online после фильтра check_auth
	end

	before_except :online, :save do
		puts 'before_except_online_save'
		# сработает перед обращением в любой метод  
		# кроме online и save после фильтра check_auth
		# метод save уже определен в модуле Nali::Controller
	end

	after do after_all_methods end
	# сработает после обращения в любой метод 

	after_only :online, :destroy do after_only_online_destroy end
	# сработает после обращения в метод online или destroy
	# метод destroy уже определен в модуле Nali::Controller

	after_except :online do
		puts 'after_except_online'
		# сработает после обращении в любой метод кроме online
	end
	
	def online
	end

	private
		def after_all_methods
			puts 'after_all_methods'
		end

		def after_only_online_destroy
			puts 'after_only_online_destroy'
		end
end

Если до или после экшена вызывается несколько фильтров, то их вызов происходит в том порядке, в каком они зарегистрированы в коде контроллера.

Селекторы

Предназначены для выборки и отправки моделей по запросу с клиента, регистрируются методом selector, описаны в разделе моделей (см. Загрузка моделей с сервера)