From 3b802e2487d74e82df5f2da30e316ca75621d12a Mon Sep 17 00:00:00 2001 From: Alessandro Desantis Date: Fri, 29 Dec 2017 14:18:05 +0100 Subject: [PATCH 1/6] Implement Rack integration --- lib/pragma/migration.rb | 3 + lib/pragma/migration/base.rb | 4 ++ lib/pragma/migration/repository.rb | 6 ++ lib/pragma/migration/runner.rb | 12 +++- pragma-migration.gemspec | 6 +- spec/pragma/migration/base_spec.rb | 23 +++++-- spec/pragma/migration/repository_spec.rb | 36 +++++++++- spec/pragma/migration/runner_spec.rb | 87 +++++++++++++++++++----- 8 files changed, 145 insertions(+), 32 deletions(-) diff --git a/lib/pragma/migration.rb b/lib/pragma/migration.rb index 7952c9c..9a3716b 100644 --- a/lib/pragma/migration.rb +++ b/lib/pragma/migration.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require 'rack' +require 'mustermann' + require 'pragma/migration/base' require 'pragma/migration/repository' require 'pragma/migration/version' diff --git a/lib/pragma/migration/base.rb b/lib/pragma/migration/base.rb index ed758a0..9f28162 100644 --- a/lib/pragma/migration/base.rb +++ b/lib/pragma/migration/base.rb @@ -13,6 +13,10 @@ def apply_to(pattern) def describe(description) @description = description end + + def applies_to?(request) + request.path =~ Mustermann.new(pattern) + end end def up(request) diff --git a/lib/pragma/migration/repository.rb b/lib/pragma/migration/repository.rb index 6ad86c7..57a8a5d 100644 --- a/lib/pragma/migration/repository.rb +++ b/lib/pragma/migration/repository.rb @@ -23,6 +23,12 @@ def migration_active?(user_version, migration) end end + def migrations_applying_to(request, user_version:) + migrations_since(user_version).select do |migration| + migration.applies_to?(request) + end + end + private def versions diff --git a/lib/pragma/migration/runner.rb b/lib/pragma/migration/runner.rb index 19a5f3a..bd2ea2d 100644 --- a/lib/pragma/migration/runner.rb +++ b/lib/pragma/migration/runner.rb @@ -11,20 +11,26 @@ def initialize(repository:, user_version:) end def run_upwards(request) - repository.migrations_since(user_version).each do |migration| + migrations_for(request).each do |migration| request = migration.new.up(request) end request end - def run_downwards(response) - repository.migrations_since(user_version).reverse.each do |migration| + def run_downwards(request, response) + migrations_for(request).reverse.each do |migration| response = migration.new.down(response) end response end + + private + + def migrations_for(request) + repository.migrations_applying_to(request, user_version: user_version) + end end end end diff --git a/pragma-migration.gemspec b/pragma-migration.gemspec index 8e2802a..1f1d3e8 100644 --- a/pragma-migration.gemspec +++ b/pragma-migration.gemspec @@ -22,10 +22,12 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_development_dependency 'bundler' + spec.add_dependency 'mustermann', '~> 1.0' + spec.add_dependency 'rack', '~> 2.0' + + spec.add_development_dependency 'coveralls' spec.add_development_dependency 'rake' spec.add_development_dependency 'rspec' spec.add_development_dependency 'rubocop' spec.add_development_dependency 'rubocop-rspec' - spec.add_development_dependency 'coveralls' end diff --git a/spec/pragma/migration/base_spec.rb b/spec/pragma/migration/base_spec.rb index 07dd0b5..f81e20e 100644 --- a/spec/pragma/migration/base_spec.rb +++ b/spec/pragma/migration/base_spec.rb @@ -2,22 +2,31 @@ RSpec.describe Pragma::Migration::Base do subject(:migration_klass) do - Class.new(described_class) + Class.new(described_class) do + apply_to '/api/v1/articles/*' + describe 'Test migration' + end end describe '.apply_to' do - before { subject.apply_to('test_pattern') } - it 'sets the pattern' do - expect(migration_klass.pattern).to eq('test_pattern') + expect(migration_klass.pattern).to eq('/api/v1/articles/*') end end describe '.describe' do - before { subject.describe('test_description') } - it 'sets the description' do - expect(migration_klass.description).to eq('test_description') + expect(migration_klass.description).to eq('Test migration') + end + end + + describe '.applies_to?' do + it 'returns true when the pattern applies to a path' do + expect(migration_klass).to be_applies_to(Rack::Request.new('PATH_INFO' => '/api/v1/articles/1')) + end + + it 'returns false when the pattern does not apply to a path' do + expect(migration_klass).not_to be_applies_to(Rack::Request.new('PATH_INFO' => '/api/v1/posts/1')) end end end diff --git a/spec/pragma/migration/repository_spec.rb b/spec/pragma/migration/repository_spec.rb index 39729b2..e53f3ba 100644 --- a/spec/pragma/migration/repository_spec.rb +++ b/spec/pragma/migration/repository_spec.rb @@ -42,13 +42,11 @@ describe '.migration_active?' do let(:migration1) { Class.new(Pragma::Migration::Base) } let(:migration2) { Class.new(Pragma::Migration::Base) } - let(:migration3) { Class.new(Pragma::Migration::Base) } before do repository.version '2017-12-24' repository.version '2017-12-25', [migration1] repository.version '2017-12-26', [migration2] - repository.version '2017-12-27', [migration3] end it 'returns false when the migration is not active' do @@ -59,4 +57,38 @@ expect(repository.migration_active?('2017-12-25', migration2)).to eq(true) end end + + describe '.migrations_applying_to' do + let(:migration1) do + Class.new(Pragma::Migration::Base) do + apply_to '/api/v1/articles/*' + end + end + + let(:migration2) do + Class.new(Pragma::Migration::Base) do + apply_to '/api/v1/articles/*' + end + end + + let(:migration3) do + Class.new(Pragma::Migration::Base) do + apply_to '/api/v1/posts/*' + end + end + + before do + repository.version '2017-12-24' + repository.version '2017-12-25', [migration1] + repository.version '2017-12-26', [migration2] + repository.version '2017-12-27', [migration3] + end + + it 'returns the migrations applying to the current request' do + expect(repository.migrations_applying_to( + Rack::Request.new('PATH_INFO' => '/api/v1/articles/1'), + user_version: '2017-12-25' + )).to eq([migration2]) + end + end end diff --git a/spec/pragma/migration/runner_spec.rb b/spec/pragma/migration/runner_spec.rb index 4714f7d..3fe7134 100644 --- a/spec/pragma/migration/runner_spec.rb +++ b/spec/pragma/migration/runner_spec.rb @@ -6,32 +6,62 @@ let(:repository) do Class.new(Pragma::Migration::Repository) do remove_author_name = Class.new(Pragma::Migration::Base) do + apply_to '/api/v1/articles/:id' + def up(request) - request.delete(:author_name) + request.tap { |r| r.params.delete('author_name') } end def down(response) - response.merge(author_name: 'John') + parsed_body = JSON.parse(response.body) + + Rack::Response.new( + JSON.dump(parsed_body.merge('author_name' => 'John')), + response.status, + response.headers + ) end end rename_author_to_author_id = Class.new(Pragma::Migration::Base) do + apply_to '/api/v1/articles/:id' + def up(request) - request.merge(author: request.delete(:author_id)) + request.tap { |r| r.params['author'] = r.params.delete('author_id') } end def down(response) - response.merge(author_id: response.delete(:author)) + parsed_body = JSON.parse(response.body.first) + + Rack::Response.new( + JSON.dump(parsed_body.merge( + 'author_id' => parsed_body.delete('author') + )), + response.status, + response.headers + ) end end convert_published_at_into_unix_epoch = Class.new(Pragma::Migration::Base) do + apply_to '/api/v1/articles/:id' + def up(request) - request.merge(published_at: Time.new(request[:published_at]).to_i) + request.tap do |r| + r.params['published_at'] = Time.new(request.params['published_at']).to_i + end end def down(response) - response.merge(published_at: Time.at(response[:published_at]).to_s) + parsed_body = JSON.parse(response.body.first) + + Rack::Response.new( + JSON.dump(parsed_body.merge( + 'published_at' => Time.at(parsed_body['published_at']).to_s + )), + response.status, + response.headers + ) end end @@ -58,16 +88,23 @@ def down(response) let(:time) { Time.new('2014-11-06T10:40:54+11:00') } let(:request) do - { - author_id: 'test_id', - published_at: time.to_s - } + Rack::Request.new( + 'PATH_INFO' => '/api/v1/articles/1', + 'rack.input' => '' + ).tap do |r| + { + 'author_id' => 'test_id', + 'published_at' => time.to_s + }.each_pair do |key, value| + r.update_param key, value + end + end end it 'applies the migrations to the request' do - expect(subject.run_upwards(request)).to eq( - author: 'test_id', - published_at: time.to_i + expect(subject.run_upwards(request).params).to eq( + 'author' => 'test_id', + 'published_at' => time.to_i ) end end @@ -75,17 +112,31 @@ def down(response) describe '#run_downwards' do let(:time) { Time.new('2014-11-06T10:40:54+11:00') } + let(:request) do + Rack::Request.new( + 'PATH_INFO' => '/api/v1/articles/1', + 'rack.input' => '' + ).tap do |r| + { + 'author_id' => 'test_id', + 'published_at' => time.to_s + }.each_pair do |key, value| + r.update_param key, value + end + end + end + let(:response) do - { + Rack::Response.new(JSON.dump( author: 'test_id', published_at: time.to_i - } + )) end it 'applies the migrations to the response' do - expect(subject.run_downwards(response)).to eq( - published_at: time.to_s, - author_id: 'test_id' + expect(JSON.parse(subject.run_downwards(request, response).body.first)).to eq( + 'published_at' => time.to_s, + 'author_id' => 'test_id' ) end end From bcd30738e55310a54d9a0ffb3269381661e1ae85 Mon Sep 17 00:00:00 2001 From: Alessandro Desantis Date: Fri, 29 Dec 2017 18:36:23 +0100 Subject: [PATCH 2/6] Put version number in its own file --- lib/pragma/migration.rb | 2 +- lib/pragma/migration/version_number.rb | 5 +++++ pragma-migration.gemspec | 3 +-- 3 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 lib/pragma/migration/version_number.rb diff --git a/lib/pragma/migration.rb b/lib/pragma/migration.rb index 9a3716b..74d74c3 100644 --- a/lib/pragma/migration.rb +++ b/lib/pragma/migration.rb @@ -7,9 +7,9 @@ require 'pragma/migration/repository' require 'pragma/migration/version' require 'pragma/migration/runner' +require 'pragma/migration/version_number' module Pragma module Migration - VERSION = '0.1.0' end end diff --git a/lib/pragma/migration/version_number.rb b/lib/pragma/migration/version_number.rb new file mode 100644 index 0000000..4ffe9ef --- /dev/null +++ b/lib/pragma/migration/version_number.rb @@ -0,0 +1,5 @@ +module Pragma + module Migration + VERSION = '0.1.0' + end +end diff --git a/pragma-migration.gemspec b/pragma-migration.gemspec index 1f1d3e8..76799ee 100644 --- a/pragma-migration.gemspec +++ b/pragma-migration.gemspec @@ -1,9 +1,8 @@ - # frozen_string_literal: true lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'pragma/migration' +require 'pragma/migration/version_number' Gem::Specification.new do |spec| spec.name = 'pragma-migration' From bfe6f238855bd7ff72496bdf0d148c85f1a2ab89 Mon Sep 17 00:00:00 2001 From: Alessandro Desantis Date: Fri, 29 Dec 2017 18:37:24 +0100 Subject: [PATCH 3/6] Fix coding style issues --- lib/pragma/migration/version_number.rb | 2 ++ spec/pragma/migration/base_spec.rb | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/pragma/migration/version_number.rb b/lib/pragma/migration/version_number.rb index 4ffe9ef..04f5b11 100644 --- a/lib/pragma/migration/version_number.rb +++ b/lib/pragma/migration/version_number.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Pragma module Migration VERSION = '0.1.0' diff --git a/spec/pragma/migration/base_spec.rb b/spec/pragma/migration/base_spec.rb index f81e20e..0fd0fe5 100644 --- a/spec/pragma/migration/base_spec.rb +++ b/spec/pragma/migration/base_spec.rb @@ -22,11 +22,15 @@ describe '.applies_to?' do it 'returns true when the pattern applies to a path' do - expect(migration_klass).to be_applies_to(Rack::Request.new('PATH_INFO' => '/api/v1/articles/1')) + expect(migration_klass).to be_applies_to( + Rack::Request.new('PATH_INFO' => '/api/v1/articles/1') + ) end it 'returns false when the pattern does not apply to a path' do - expect(migration_klass).not_to be_applies_to(Rack::Request.new('PATH_INFO' => '/api/v1/posts/1')) + expect(migration_klass).not_to be_applies_to( + Rack::Request.new('PATH_INFO' => '/api/v1/posts/1') + ) end end end From 6ad7761720833496e911f00eb7efacfc70a0eeaf Mon Sep 17 00:00:00 2001 From: Alessandro Desantis Date: Fri, 29 Dec 2017 18:46:31 +0100 Subject: [PATCH 4/6] Make DSL methods protected --- lib/pragma/migration/base.rb | 10 ++++--- lib/pragma/migration/repository.rb | 12 ++++---- spec/pragma/migration/repository_spec.rb | 36 ++++++++++++++---------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/lib/pragma/migration/base.rb b/lib/pragma/migration/base.rb index 9f28162..4c204e7 100644 --- a/lib/pragma/migration/base.rb +++ b/lib/pragma/migration/base.rb @@ -6,6 +6,12 @@ class Base class << self attr_reader :pattern, :description + def applies_to?(request) + request.path =~ Mustermann.new(pattern) + end + + protected + def apply_to(pattern) @pattern = pattern end @@ -13,10 +19,6 @@ def apply_to(pattern) def describe(description) @description = description end - - def applies_to?(request) - request.path =~ Mustermann.new(pattern) - end end def up(request) diff --git a/lib/pragma/migration/repository.rb b/lib/pragma/migration/repository.rb index 57a8a5d..143540f 100644 --- a/lib/pragma/migration/repository.rb +++ b/lib/pragma/migration/repository.rb @@ -4,11 +4,6 @@ module Pragma module Migration class Repository class << self - def version(number, migrations = []) - versions << Version.new(number, migrations) - sort_versions - end - def sorted_versions versions.dup end @@ -29,6 +24,13 @@ def migrations_applying_to(request, user_version:) end end + protected + + def version(number, migrations = []) + versions << Version.new(number, migrations) + sort_versions + end + private def versions diff --git a/spec/pragma/migration/repository_spec.rb b/spec/pragma/migration/repository_spec.rb index e53f3ba..3199a8a 100644 --- a/spec/pragma/migration/repository_spec.rb +++ b/spec/pragma/migration/repository_spec.rb @@ -5,19 +5,25 @@ describe '.version' do it 'adds a version' do - repository.version '2017-12-27' + repository.instance_eval do + version '2017-12-27' + end + expect(repository.sorted_versions).to match([instance_of(Pragma::Migration::Version)]) end it 'keeps versions sorted' do - repository.version '2017-12-28' - repository.version '2017-12-27' + repository.instance_eval do + version '2017-12-28' + version '2017-12-27' + end + expect(repository.sorted_versions.map(&:number)).to eq(['2017-12-27', '2017-12-28']) end it 'initializes migrations' do migration = Class.new(Pragma::Migration::Base) - repository.version '2017-12-28', [migration] + repository.send :version, '2017-12-28', [migration] expect(repository.sorted_versions.first.migrations).to eq([migration]) end end @@ -28,10 +34,10 @@ let(:migration3) { Class.new(Pragma::Migration::Base) } before do - repository.version '2017-12-24' - repository.version '2017-12-25', [migration1] - repository.version '2017-12-26', [migration2] - repository.version '2017-12-27', [migration3] + repository.send :version, '2017-12-24' + repository.send :version, '2017-12-25', [migration1] + repository.send :version, '2017-12-26', [migration2] + repository.send :version, '2017-12-27', [migration3] end it 'collects all the changes since the specified version' do @@ -44,9 +50,9 @@ let(:migration2) { Class.new(Pragma::Migration::Base) } before do - repository.version '2017-12-24' - repository.version '2017-12-25', [migration1] - repository.version '2017-12-26', [migration2] + repository.send :version, '2017-12-24' + repository.send :version, '2017-12-25', [migration1] + repository.send :version, '2017-12-26', [migration2] end it 'returns false when the migration is not active' do @@ -78,10 +84,10 @@ end before do - repository.version '2017-12-24' - repository.version '2017-12-25', [migration1] - repository.version '2017-12-26', [migration2] - repository.version '2017-12-27', [migration3] + repository.send :version, '2017-12-24' + repository.send :version, '2017-12-25', [migration1] + repository.send :version, '2017-12-26', [migration2] + repository.send :version, '2017-12-27', [migration3] end it 'returns the migrations applying to the current request' do From 4b253483cd83ebfe0612575fb62dd51f11906402 Mon Sep 17 00:00:00 2001 From: Alessandro Desantis Date: Fri, 29 Dec 2017 19:02:59 +0100 Subject: [PATCH 5/6] Move user version calculation to Repository --- lib/pragma/migration/repository.rb | 41 ++++++++++++++++++++---- lib/pragma/migration/runner.rb | 15 +++------ spec/pragma/migration/repository_spec.rb | 14 ++++---- spec/pragma/migration/runner_spec.rb | 8 +++-- 4 files changed, 50 insertions(+), 28 deletions(-) diff --git a/lib/pragma/migration/repository.rb b/lib/pragma/migration/repository.rb index 143540f..dec425e 100644 --- a/lib/pragma/migration/repository.rb +++ b/lib/pragma/migration/repository.rb @@ -8,29 +8,56 @@ def sorted_versions versions.dup end - def migrations_since(user_version) - @versions.select { |version| version > user_version }.flat_map(&:migrations) + def user_version_proc + @user_version_proc ||= (lambda do |request| + request.get_header('X-Api-Version') + end) end - def migration_active?(user_version, migration) - @versions.any? do |version| - version > user_version && version.migration?(migration) + def migrations_since(request_or_version) + user_version = if request_or_version.is_a?(Rack::Request) + user_version_from(request_or_version) + else + request_or_version end + + @versions.select { |version| version > user_version }.flat_map(&:migrations) end - def migrations_applying_to(request, user_version:) - migrations_since(user_version).select do |migration| + def migrations_for(request) + migrations_since(request).select do |migration| migration.applies_to?(request) end end + def migration_active?(migration, request: nil, user_version: nil) + unless request || user_version + fail ArgumentError, 'You must pass one of :request or :user_version' + end + + user_version ||= user_version_from(request) + + @versions.any? do |version| + version > user_version && version.migration?(migration) + end + end + protected + def user_version_from(request) + user_version = user_version_proc.call(request) + sorted_versions.include?(user_version) ? user_version : sorted_versions.last + end + def version(number, migrations = []) versions << Version.new(number, migrations) sort_versions end + def user_version(&block) + @user_version_proc = block + end + private def versions diff --git a/lib/pragma/migration/runner.rb b/lib/pragma/migration/runner.rb index bd2ea2d..d84a4fa 100644 --- a/lib/pragma/migration/runner.rb +++ b/lib/pragma/migration/runner.rb @@ -3,15 +3,14 @@ module Pragma module Migration class Runner - attr_reader :repository, :user_version + attr_reader :repository - def initialize(repository:, user_version:) + def initialize(repository) @repository = repository - @user_version = user_version end def run_upwards(request) - migrations_for(request).each do |migration| + repository.migrations_for(request).each do |migration| request = migration.new.up(request) end @@ -19,18 +18,12 @@ def run_upwards(request) end def run_downwards(request, response) - migrations_for(request).reverse.each do |migration| + repository.migrations_for(request).reverse.each do |migration| response = migration.new.down(response) end response end - - private - - def migrations_for(request) - repository.migrations_applying_to(request, user_version: user_version) - end end end end diff --git a/spec/pragma/migration/repository_spec.rb b/spec/pragma/migration/repository_spec.rb index 3199a8a..25817b2 100644 --- a/spec/pragma/migration/repository_spec.rb +++ b/spec/pragma/migration/repository_spec.rb @@ -56,15 +56,15 @@ end it 'returns false when the migration is not active' do - expect(repository.migration_active?('2017-12-25', migration1)).to eq(false) + expect(repository.migration_active?(migration1, user_version: '2017-12-25')).to eq(false) end it 'returns true when the migration is active' do - expect(repository.migration_active?('2017-12-25', migration2)).to eq(true) + expect(repository.migration_active?(migration2, user_version: '2017-12-25')).to eq(true) end end - describe '.migrations_applying_to' do + describe '.migrations_for' do let(:migration1) do Class.new(Pragma::Migration::Base) do apply_to '/api/v1/articles/*' @@ -91,10 +91,10 @@ end it 'returns the migrations applying to the current request' do - expect(repository.migrations_applying_to( - Rack::Request.new('PATH_INFO' => '/api/v1/articles/1'), - user_version: '2017-12-25' - )).to eq([migration2]) + expect(repository.migrations_for(Rack::Request.new( + 'PATH_INFO' => '/api/v1/articles/1', + 'X-Api-Version' => '2017-12-25' + ))).to eq([migration2]) end end end diff --git a/spec/pragma/migration/runner_spec.rb b/spec/pragma/migration/runner_spec.rb index 3fe7134..d36fd05 100644 --- a/spec/pragma/migration/runner_spec.rb +++ b/spec/pragma/migration/runner_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Pragma::Migration::Runner do - subject { described_class.new(repository: repository, user_version: user_version) } + subject { described_class.new(repository) } let(:repository) do Class.new(Pragma::Migration::Repository) do @@ -90,7 +90,8 @@ def down(response) let(:request) do Rack::Request.new( 'PATH_INFO' => '/api/v1/articles/1', - 'rack.input' => '' + 'rack.input' => '', + 'X-Api-Version' => '2017-12-25', ).tap do |r| { 'author_id' => 'test_id', @@ -115,7 +116,8 @@ def down(response) let(:request) do Rack::Request.new( 'PATH_INFO' => '/api/v1/articles/1', - 'rack.input' => '' + 'rack.input' => '', + 'X-Api-Version' => '2017-12-25', ).tap do |r| { 'author_id' => 'test_id', From 1e4cd63a2326328c28926208884d67d604750e1f Mon Sep 17 00:00:00 2001 From: Alessandro Desantis Date: Fri, 29 Dec 2017 19:03:49 +0100 Subject: [PATCH 6/6] Fix coding style issues --- lib/pragma/migration/repository.rb | 8 +++++--- spec/pragma/migration/runner_spec.rb | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/pragma/migration/repository.rb b/lib/pragma/migration/repository.rb index dec425e..0e6e16f 100644 --- a/lib/pragma/migration/repository.rb +++ b/lib/pragma/migration/repository.rb @@ -9,9 +9,11 @@ def sorted_versions end def user_version_proc - @user_version_proc ||= (lambda do |request| - request.get_header('X-Api-Version') - end) + @user_version_proc ||= begin + lambda do |request| + request.get_header('X-Api-Version') + end + end end def migrations_since(request_or_version) diff --git a/spec/pragma/migration/runner_spec.rb b/spec/pragma/migration/runner_spec.rb index d36fd05..f44583b 100644 --- a/spec/pragma/migration/runner_spec.rb +++ b/spec/pragma/migration/runner_spec.rb @@ -91,7 +91,7 @@ def down(response) Rack::Request.new( 'PATH_INFO' => '/api/v1/articles/1', 'rack.input' => '', - 'X-Api-Version' => '2017-12-25', + 'X-Api-Version' => '2017-12-25' ).tap do |r| { 'author_id' => 'test_id', @@ -117,7 +117,7 @@ def down(response) Rack::Request.new( 'PATH_INFO' => '/api/v1/articles/1', 'rack.input' => '', - 'X-Api-Version' => '2017-12-25', + 'X-Api-Version' => '2017-12-25' ).tap do |r| { 'author_id' => 'test_id',