» » » Сокрытие в Ruby. А ещё скрываем классы из Top-Level

 

Сокрытие в Ruby. А ещё скрываем классы из Top-Level

Автор: admin от 12-08-2018, 21:05, посмотрело: 18

Что бы далеко не ходить, сразу определимся с терминами.




  • Инкапсуляция — упаковка данных и функций в единый компонент.

  • Сокрытие — представляет собой принцип проектирования, заключающийся в разграничении доступа различных частей программы к внутренним компонентам друг друга.



Взято с вики. В языке программирования Ruby с инкапсуляцией вроде как всё хорошо. С сокрытием на первый взгляд тоже, нам доступны локальные переменные, переменные инстансов, разные уровни доступа к методам (public, protected, private). Но иногда этого может не хватать.



Рассмотрим следующий пример.



class User
    class Address < String
        def ==(other_object)
            # хитрое сравнение
        end
    end

    def initialize(name:, address: nil)
        @name = name 
        @address = Address.new(address) 
    end
end


Класс Address мы объявляем внутри User, предполагаем, что это не просто некоторый абстрактный адрес, а адрес со специфичной логикой которая нужна только в контексте объектов User. И более того, мы не хотим, что бы этот самый Address был доступен из любого места программы, т.е. не только инкапсулируем его внутри User, но и хотим его скрыть для всех других объектов. Как это сделать?



Можно попробовать через private.



class User
   private
    class Address < String
        def ==(other_object)
            # хитрое сравнение
        end
    end
end


Загружаем и выполняем например внутри pry и получаем:



User::Address
=> User::Address
User::Address.new
=> ""


Тем самым убеждаемся, что модификатор private в таком контексте не работает. Зато есть просто волшебный метод private_constant который сработает как надо. Ведь классы в руби это тоже константы. Теперь мы можем написать private_constant :Address и при попытке доступа к User::Address словить ошибку:



NameError: private constant User::Address referenced



Теперь ставим задачку посложнее. Добавляем класс кэширования который будет использовать redis.



#shared_cache.rb
require 'redis'
class SharedCache
end


И вроде бы ничего не предвещает беды, до тех пор пока где то посреди View, внутри erb шаблона, кто-нибудь не захочет написать напрямую redis.get redis.set в обход даже SharedCache. Лечим следующим образом:



require 'redis'
SharedCache.send :const_set, :Redis, Redis
Object.send :remove_const, :Redis

Redis
NameError: uninitialized constant Redis
from (pry):7:in `__pry__'


Что произошло? Через вызов remove_const мы убираем Redis фактически из Top-Level видимости объектов. Но перед эти мы помещаем Redis внутрь SharedCache. Далее мы можем через private_constant ограничить доступ к SharedCache::Redis. Однако в таком случае мы уже не сможем достучаться до класса Redis никоим образом, даже если захотим использовать его где-то ещё. Облагораживаем и позволяем сделать require внутрь нескольких классов:



class SharedCache
    require_to 'redis', :Redis
    private_constant :Redis

    def storage
        Redis
    end
end

class SharedCache2
    require_to 'redis', :Redis
    private_constant :Redis
end


Попытки вызова Redis:



[1] pry(main)> SharedCache::Redis
NameError: private constant SharedCache::Redis referenced
from (pry):1:in `<main>'
[2] pry(main)> require 'redis'
=> false
[3] pry(main)> Redis
NameError: uninitialized constant Redis
from (pry):6:in `<main>'
[4] pry(main)> SharedCache.new.storage
=> Redis
[5] pry(main)> SharedCache2::Redis
NameError: private constant SharedCache2::Redis referenced
from (pry):1:in `<main>'


Для чего это можно использовать:




  • Для сокрытия внутренних служебных классов внутри другого класса или модуля.

  • Инкапсуляция с сокрытием логики внутри сервисных классов — можно запретить обращение к некоторым классов в обход сервисных объектов.

  • Убрать "опасные" классы из Top-Level видимости, например для запрета к обращению к БД из View или сериализаторов. В Rails можно "скрыть" все ActiveRecord классы и давать к ним доступ выборочно в конкретных местах.



И пример реализации require_to который перемещает константы из Top-Level на нужный уровень видимости.





Источник: Хабр / Интересные публикации

Категория: Веб-разработка

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *