Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Простенькая реализация ролей на примере Ruby

Anna | 20.06.2014 | нет комментариев
Всем здравствуй. Я длинное время программировал на PHP и применял Zend 1. Работал над огромным планом платежной системы. Система авторизации подразумевала пользователей, их авторизацию и распределение по ролям. Распределение по ролям было достаточно обширным и ветвистым. Вообще в большинстве планов если уж требуется авторизация, то, наверно, понадобится правда бы минимальное распределение по ролям.
Вовсе незадолго я начал план на Ruby и подыскивал гем для авторизации. Но толком так и не обнаружил прекрасного и Отчетливого гема, реализующего распределения по ролям, а может нехорошо искал. Сейчас хочу рассказать о своем способе решения этой задачи.

Задача.

Реализовать распределение по ролям с вероятностью наследования малыми силами, не создавая кучу таблиц и лишних полей.

Реализация:
1. Для начала побеседуем о БД.

Первым делом нужно сделать таблицу ролей. Раз уж нам нужно применять наследование ролей, то видимо, что придется беречь древовидную конструкцию. Я применял реляционную БД (Postgresql). В реляционных БД беречь деревья не дюже комфортно, но способов для такого хранения предостаточно. Я применял «material path». Данный способ весьма примитивен и заключается в том, что в некотором поле (скажем path) мы бережем путь данной строки таблицы касательно корня дерева из id элементов, которые являются прародителями для данного элемента. Скажем запись 0/12/23/97 обозначает, что нынешний элемент c id 97 вложен в элемент 23, тот, что вложен в 12. А 12 — является корневым. Ну да не буду детально останавливаться на способах хранения древовидных конструкций.
Выходит Ruby on Rails использует миграции для внесения изменений в БД, следственно привожу пример миграции, которую я применял для таблицы ролей:

 class CreateRoles < ActiveRecord::Migration
  def up
    create_table :roles do |t|
      t.string :name, :unique => true
      t.string :path
    end
    execute <<SQL
ALTER TABLE roles ADD CONSTRAINT uniq_role_name UNIQUE(name);
INSERT INTO roles (name,path) VALUES ('registred',':registred');
INSERT INTO roles (name,path) VALUES ('admin',':registred:admin');
SQL
  end

  def down
    drop_table :roles
  end
end

Сразу оговорюсь, что я использую в миграция SQL код для того, Дабы на ярусе БД организовывать Foreign key. Полагаю, что многие сочтут это неправильным. Не могу привести никаких аргументов ни за ни вопреки этого способа, а потому умоляю не останавливаться на этом внимание.
Также приведу SQL для создания таблицы (для тех, кто не на руби либо не использует миграции):

CREATE TABLE "public"."roles" (
"id" int4 DEFAULT nextval('roles_id_seq'::regclass) NOT NULL,
"name" varchar(255),
"path" varchar(255),
CONSTRAINT "roles_pkey" PRIMARY KEY ("id"),
CONSTRAINT "uniq_role_name" UNIQUE ("name")
)
WITH (OIDS=FALSE)
;

ALTER TABLE "public"."roles" OWNER TO "nalogovaya.ru";

Таким образом мы получаем таблицу с тремя полями:

  1. id — первичный ключ
  2. name — строка (наименование роли)
  3. path — путь до роли во мнимом дереве ролей.

И здесь же вставляю 2 роли.
Раз у нас присутствует авторизация — значит есть некая таблица users в которой хранится информация о пользователе. В ней нужно сделать поле-ключ для указания роли пользователя.
Привожу пример миграции:

class AddRoleToUser < ActiveRecord::Migration
  def up
    execute <<SQL
ALTER TABLE users ADD COLUMN role_id integer DEFAULT 1;
ALTER TABLE users ADD CONSTRAINT "role_relation" FOREIGN KEY ("role_id")
REFERENCES "public"."roles" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
SQL
  end

  def down
    execute <<SQL
ALTER TABLE users DROP COLUMN role_id;
SQL
  end
end

Выходит! Мы имеем таблицу ролей в БД и указатель роли в таблице пользователей. Работа с БД на этом закончена.

2. Начальный код

Для начала сотворим модель ролей. Приведу начальный код файла role.rb а после этого поясню:

#!/bin/env ruby
# encoding: utf-8
class Role < ActiveRecord::Base
  extend ActiveModel::Callbacks
  has_many :users

  ROLES = Role.all

  def self.find_by_name(name)
    ROLES.select{|item| item.name==name}.first
  end

  def >= (value)
    value = checkRole(value)
    !self.path.index(value.path).nil?
  end

  def <= (value)
    value = checkRole(value)
    !self.>=(value)
  end

  def == (value)
    value = checkRole(value)
    self.name==value.name
  end

  protected
  def checkRole(value)
    value.instance_of?(String) ? Role.find_by_name(value) : value
    value
  end
end

ROLES = Role.all — подозрительный ход для уменьшения числа запросов к БД. При старте рельс сразу подгружаются все роли, и к БД запросов огромнее не будет.
Способ checkRole — (назовите как вам огромнее понравится) служит экстраординарно для комфорта, Дабы роли дозволено было сопоставлять легко по наименованию, а не только по обьектам класса Role.
Остальные способы — легко функции сопоставления ролей.
Сейчас перейдем к пользователям. Для начала укажем связку:

belongs_to :role

Если вы используете Rails 4 и гем protected_attributes, то не позабудьте еще добавить

attr_accessible :role
3. Применение.

Самая славная часть. Если, безусловно, первых 2 пункта — это то что Вам было необходимо =)
Пример:

        <% if current_user.role>='admin' #если роль пользователя "админ" либо старше, что вряд ли, показываем ему что-то %>
            <%= link_to 'Удалить всю входящую почту!', requests_path, :method => :delete %>
        <% end %>

Спасибо за внимание, верю кому-нибудь потребуется такая реализация ролей.

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB