From f929fb458012a91474f92cd948739cea98bcb419 Mon Sep 17 00:00:00 2001 From: Konstantin <18617428+dks17@users.noreply.github.com> Date: Sat, 23 Mar 2019 23:22:34 +0300 Subject: [PATCH] Tokens serialization (#1250) --- .../concerns/active_record_support.rb | 24 +------ .../concerns/tokens_serialization.rb | 19 +++++ .../concerns/tokens_serialization_test.rb | 70 +++++++++++++++++++ 3 files changed, 92 insertions(+), 21 deletions(-) create mode 100644 app/models/devise_token_auth/concerns/tokens_serialization.rb create mode 100644 test/models/concerns/tokens_serialization_test.rb diff --git a/app/models/devise_token_auth/concerns/active_record_support.rb b/app/models/devise_token_auth/concerns/active_record_support.rb index d7a2b5581..54d2e71b2 100644 --- a/app/models/devise_token_auth/concerns/active_record_support.rb +++ b/app/models/devise_token_auth/concerns/active_record_support.rb @@ -1,12 +1,10 @@ +require_relative 'tokens_serialization' + module DeviseTokenAuth::Concerns::ActiveRecordSupport extend ActiveSupport::Concern included do - serialize :tokens, JSON unless tokens_has_json_column_type? - - # can't set default on text fields in mysql, simulate here instead. - after_save :set_empty_token_hash - after_initialize :set_empty_token_hash + serialize :tokens, DeviseTokenAuth::TokensSerialization end class_methods do @@ -14,21 +12,5 @@ module DeviseTokenAuth::Concerns::ActiveRecordSupport def dta_find_by(attrs = {}) find_by(attrs) end - - protected - - def tokens_has_json_column_type? - database_exists? && table_exists? && columns_hash['tokens'] && columns_hash['tokens'].type.in?([:json, :jsonb]) - end - - def database_exists? - ActiveRecord::Base.connection_pool.with_connection { |con| con.active? } rescue false - end - end - - protected - - def set_empty_token_hash - self.tokens ||= {} if has_attribute?(:tokens) end end diff --git a/app/models/devise_token_auth/concerns/tokens_serialization.rb b/app/models/devise_token_auth/concerns/tokens_serialization.rb new file mode 100644 index 000000000..0f8dfd7d4 --- /dev/null +++ b/app/models/devise_token_auth/concerns/tokens_serialization.rb @@ -0,0 +1,19 @@ +module DeviseTokenAuth::TokensSerialization + # Serialization hash to json + def self.dump(object) + object.each_value(&:compact!) unless object.nil? + JSON.generate(object) + end + + # Deserialization json to hash + def self.load(json) + case json + when String + JSON.parse(json) + when NilClass + {} + else + json + end + end +end diff --git a/test/models/concerns/tokens_serialization_test.rb b/test/models/concerns/tokens_serialization_test.rb new file mode 100644 index 000000000..e99a6690a --- /dev/null +++ b/test/models/concerns/tokens_serialization_test.rb @@ -0,0 +1,70 @@ +require 'test_helper' + +if DEVISE_TOKEN_AUTH_ORM == :active_record + describe 'DeviseTokenAuth::TokensSerialization' do + let(:ts) { DeviseTokenAuth::TokensSerialization } + let(:user) { FactoryBot.create(:user) } + let(:tokens) do + # Сreate all possible token's attributes combinations + user.create_token + 2.times { user.create_new_auth_token(user.tokens.first[0]) } + user.create_new_auth_token + user.create_token + + user.tokens + end + let(:json) { JSON.generate(tokens) } + + it 'is defined' do + assert_equal(ts.present?, true) + assert_kind_of(Module, ts) + end + + describe '.load(json)' do + let(:default) { {} } + + it 'is defined' do + assert_respond_to(ts, :load) + end + + it 'handles nil' do + assert_equal(ts.load(nil), default) + end + + it 'handles string' do + assert_equal(ts.load(json), JSON.parse(json)) + end + + it 'returns object of undesirable class' do + assert_equal(ts.load([]), []) + end + end + + describe '.dump(object)' do + let(:default) { 'null' } + + it 'is defined' do + assert_respond_to(ts, :dump) + end + + it 'handles nil' do + assert_equal(ts.dump(nil), default) + end + + it 'handles empty hash' do + assert_equal(ts.dump({}), '{}') + end + + it 'deserialize tokens' do + assert_equal(ts.dump(tokens), json) + end + + it 'removes nil values' do + new_tokens = tokens.dup + new_tokens[new_tokens.first[0]][:kos] = nil + + assert_equal(ts.dump(tokens), ts.dump(new_tokens)) + end + end + end +end