Плагины в Ruby on Rails — миф или реальность?

d5dcfca348bd447477b4f0550da98d40.png

Разработчики приложений на Ruby on Rails однозначно знают и используют гемы —  библиотеки, которые являются частью фреймворка RubyGems, механизма управления библиотеками или пакетами в Ruby. Однако мало кто из них прибегает к использованию плагинов. 

В этой статье мы хотим рассказать вам:

  • Что такое плагины

  • Для чего их используют

  • Как их создавать

Программисты всегда готовы применять существующие решения для возникающих проблем, не тратя слишком много времени. Особенно, когда речь идёт о Ruby on Rails, где гибкость является нормой, и использование существующих решений для различных задач считается само собой разумеющимся. Благодаря повторному использованию кода программист может сэкономить время и усилия, не изобретая при этом велосипед. 

Ни один язык программирования не идеален, и команда Ruby не может добавить всё, что пожелает каждый из разработчиков. Поэтому в Rails 1.0 появилась система плагинов, чтобы сделать Rails действительно гибким, позволяющим программистам делиться готовыми решениями с другими разработчиками. Плагин — отличный способ упаковать часто применяемые решения общих проблем и повторно использовать код. 

Команда Rails рекомендует публиковать все популярные плагины в RubyGems. Первый шаг перед тем, как вы сможете создать плагин, — понимание, является ли ваш плагин специфичным только для приложения, над которым вы работаете, или его можно использовать в различных приложениях. Если подключаемый модуль, который вы создаёте, зависит от приложения, он будет рассматриваться как vendor specific plugin. Если ваш плагин можно применять в различных приложениях, он считается гемифицированным плагином (gem plugin).

В наших проектах мы неоднократно использовали регистрацию с помощью учётных записей социальных сетей, поэтому для дальнейших проектов мы решили разработать гем, где регистрация для каждой социальной сети (Вконтакте, Одноклассники, Google, Apple) является отдельным плагином.

38cff989b1fe09bee53de3786d5874bb.png

Для начала мы описали модуль Plugins:

развернуть код

require 'oauth_client/errors'

module OauthClient
  # Module in which plugins should be stored. Also contains logic
  # for registering and loading plugins.
  module Plugins
	@plugins = {}

	# If the registered plugin already exists, use it. Otherwise, require it
	# and return it. This raises a LoadError if such a plugin doesn't exist

	def self.load_plugin(name)
  	unless @plugins[name]
    	require "oauth_client/plugins/#{name}"
    	raise OuathException, "plugin #{name} did not register itself correctly" unless @plugins[name]
  	end

  	@plugins[name]
	end

	# Delegate call to the plugin in a way that works across Ruby versions.
	def self.configure(plugin, uploader, **opts)
  	return unless plugin.respond_to?(:configure)

  	plugin.configure(uploader, **opts)
	end

	# Register the given plugin, so that it can be loaded
	def self.register_plugin(name, mod)
  	@plugins[name] = mod
	end
  end
end

Данный модуль позволил загружать и конфигурировать плагины. 

Далее рассмотрим реализацию самих плагинов на примере Вконтакте и Одноклассники. 

Для настройки регистрации через Вконтакте мы описали метод register, который выполняет все необходимые операции, а затем зарегистрировали данный плагин для того, чтобы в дальнейшем мы смогли его загрузить и использовать в нашем приложении:

развернуть код

require 'faraday'
require 'oj'
require_relative '../errors'

module OauthClient
  module Plugins
	module Vkontakte
  	BASE_URL = 'https://api.vk.com/method/account.getProfileInfo'
  	VERSION = '5.131'

  	def self.register(access_token: nil)
    	request = Faraday.new(
      	url: BASE_URL,
      	params: {
        	access_token: access_token,
        	v: VERSION
      	},
      	headers: { 'Content-Type' => 'application/json' }
    	)
    	data = Oj.load(request.get.body, symbol_keys: true)
    	raise OuathException, data[:error][:error_msg] if data[:error].present?

    	data[:response]
  	end
	end

	register_plugin(:vkontakte, Vkontakte)
  end
end

Для запроса на API Одноклассники нужны дополнительные опции, поэтому в данном модуле мы определили метод configure:

развернуть код

require 'faraday'
require 'oj'
require_relative '../errors'

module OauthClient
  module Plugins
	module Odnoklassniki
  	BASE_URL = 'https://api.ok.ru/fb.do'

  	def self.configure(uploader, **opts)
    	uploader.opts[:odnoklassniki] ||= {}
    	uploader.opts[:odnoklassniki].merge!(opts)
  	end

  	def self.register(access_token: nil)
    	request = Faraday.new(
      	url: BASE_URL,
      	params: {
        	application_key: OauthClient.opts[:odnoklassniki][:key],
        	method: 'users.getCurrentUser',
        	access_token: access_token
      	},
      	headers: { 'Content-Type' => 'application/json' }
    	)
    	data = Oj.load(request.get.body, symbol_keys: true)
    	raise OuathException, data[:error_msg] if data[:error_msg].present?

    	data
  	end
	end

	register_plugin(:odnoklassniki, Odnoklassniki)
  end
end

И наконец, базовый модуль OauthClient выглядит следующим образом:

развернуть код

require 'oauth_client/plugins'
require 'oauth_client/version'

module OauthClient
  @opts = {}

  def self.plugin(plugin, **opts)
	plugin = Plugins.load_plugin(plugin) if plugin.is_a?(Symbol)
	Plugins.configure(plugin, self, **opts)
	plugin
  end

  def self.opts
	@opts
  end
end

С помощью метода plugin после установки гема в конфигурации config/initializers/oauth_client.rb появляется возможность загрузить плагины, необходимые тому или иному приложению.

Пример конфигурации для гема в приложении:

require 'oauth_client'

OauthClient.plugin :vkontakte
OauthClient.plugin :odnoklassniki, { key: ENV['OK_KEY' ] }

Данная настройка позволила нам подгружать только необходимые плагины. Например, если в приложении требуется регистрация через Вконтакте и Одноклассники, то использование плагинов для регистрации через Google или Apple недоступно. Так как они не были загружены ранее, то при их вызове появляется следующая ошибка:

64a04d5fbcf10eded31c367fa76cf82a.png

Плагины — это хорошее решение для кода, которое вам можно будет использовать в других пакетах или приложениях. Он расширяет функциональность уже существующего приложения и добавляет функциональность для создания нового приложения с нуля.

Ссылка на гит с исходным кодом.

© Habrahabr.ru