Skip to content

Commit

Permalink
Implement Pragma::Operation hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
aldesantis committed Jul 17, 2018
1 parent 1257eb1 commit e1b46f4
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 21 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Expand Up @@ -26,6 +26,9 @@ Style/BlockDelimiters:
Exclude:
- 'spec/**/*'

Style/Documentation:
Enabled: false

Layout/AlignParameters:
EnforcedStyle: with_fixed_indentation

Expand Down
35 changes: 35 additions & 0 deletions lib/pragma/migration.rb
Expand Up @@ -9,10 +9,45 @@
require 'pragma/migration/runner'
require 'pragma/migration/bond'
require 'pragma/migration/middleware'
require 'pragma/migration/hooks/operation'
require 'pragma/migration/gem_version'

module Pragma
# Provides API payload migrations to support clients on older versions of your API.
module Migration
# The default +user_version_proc+.
DEFAULT_USER_VERSION_PROC = lambda do |request|
request.get_header('X-Api-Version')
end

class << self
# @!attribute [rw] repository
# @return [Pragma::Migration::Repository] your migrations repository
#
# @!attribute [rw] user_version_proc
# @return [Object] a callable taking a +Rack::Request+ as argument and returning an API
# version identifier
attr_accessor :repository
attr_writer :user_version_proc

def user_version_proc
@user_version_proc ||= DEFAULT_USER_VERSION_PROC
end

# Returns the user's API version for the given request.
#
# @param request [Rack::Request] the request
#
# @return [String] the API version identifier
def user_version_from(request)
version = user_version_proc.call(request)

if version && repository.sorted_versions.include?(version)
version
else
repository.sorted_versions.last.number
end
end
end
end
end
37 changes: 37 additions & 0 deletions lib/pragma/migration/hooks/operation.rb
@@ -0,0 +1,37 @@
# frozen_string_literal: true

module Pragma
module Migration
module Hooks
module Operation
def rolled_migrations(options)
build_migration_bond(options).rolled_migrations
end

def applying_migrations(options)
build_migration_bond(options).applying_migrations
end

def migration_rolled?(options, migration)
build_migration_bond(options).migration_rolled?(migration)
end

def migration_applies?(options, migration)
build_migration_bond(options).migration_applies?(migration)
end

private

def build_migration_bond(options)
Bond.new(
repository: Pragma::Migration.repository,
request: options['rack.request'],
user_version: Pragma::Migration.user_version_from(options['rack.request'])
)
end

::Pragma::Operation::Base.prepend(self) if defined?(::Pragma::Operation::Base)
end
end
end
end
20 changes: 5 additions & 15 deletions lib/pragma/migration/middleware.rb
Expand Up @@ -15,20 +15,11 @@ module Migration
# request.get_header 'X-Api-Version'
# end)
class Middleware
# The default for +user_version_proc+.
DEFAULT_VERSION_PROC = lambda do |request|
request.get_header('X-Api-Version')
end

# Initializes the middleware.
#
# @param app [Object] your app
# @param repository [Repository] your migration repository
# @param user_version_proc [Proc] a proc that takes a request and returns a version number
def initialize(app, repository:, user_version_proc: DEFAULT_VERSION_PROC)
def initialize(app)
@app = app
@repository = repository
@user_version_proc = user_version_proc
end

# Executes the middleware.
Expand All @@ -51,9 +42,9 @@ def call(env)
original_request = Rack::Request.new(env)

runner = Runner.new(Bond.new(
repository: @repository,
repository: repository,
request: original_request,
user_version: user_version_from(original_request)
user_version: Pragma::Migration.user_version_from(original_request)
))

migrated_request = runner.run_upwards
Expand All @@ -67,9 +58,8 @@ def call(env)

private

def user_version_from(request)
version = @user_version_proc.call(request)
@repository.sorted_versions.include?(version) ? version : @repository.sorted_versions.last
def repository
Pragma::Migration.repository
end
end
end
Expand Down
1 change: 1 addition & 0 deletions pragma-migration.gemspec
Expand Up @@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
spec.add_dependency 'rack', '~> 2.0'

spec.add_development_dependency 'coveralls'
spec.add_development_dependency 'pragma-operation'
spec.add_development_dependency 'rake'
spec.add_development_dependency 'rspec'
spec.add_development_dependency 'rubocop'
Expand Down
97 changes: 97 additions & 0 deletions spec/pragma/migration/hooks/operation_spec.rb
@@ -0,0 +1,97 @@
# frozen_string_literal: true

RSpec.describe Pragma::Migration::Hooks::Operation do
subject(:result) do
operation_klass.call(
{},
{
'rack.request' => Rack::Request.new('X-Test-Api-Version' => api_version)
}
)
end

before do
module API
module Migration
class SendNewsletterAutomatically < Pragma::Migration::Base
end
end

class MigrationRepository < Pragma::Migration::Repository
version '2018-07-15'

version '2018-07-16', [
API::Migration::SendNewsletterAutomatically
]
end
end

Pragma::Migration.repository = API::MigrationRepository

Pragma::Migration.user_version_proc = lambda do |request|
request.get_header 'X-Test-Api-Version'
end
end

let(:operation_klass) do
Class.new(Pragma::Operation::Base) do
success :check_migrations!

def check_migrations!(options, **)
options['result.migration_rolled'] = migration_rolled?(
options,
API::Migration::SendNewsletterAutomatically
)

options['result.migration_rolled'] = migration_applies?(
options,
API::Migration::SendNewsletterAutomatically
)

options['result.rolled_migrations'] = rolled_migrations(options)

options['result.applying_migrations'] = applying_migrations(options)
end
end
end

context 'when the migration is not rolled' do
let(:api_version) { '2018-07-15' }

it 'computes whether a migration was rolled' do
expect(result['result.migration_rolled']).to eq(false)
end

it 'computes whether a migration applies' do
expect(result['result.migration_applies']).to eq(true)
end

it 'returns the correct rolled migrations' do
expect(result['result.rolled_migrations']).to eq([])
end

it 'returns the correct applying migrations' do
expect(result['result.applying_migrations']).to eq([API::Migration::SendNewsletterAutomatically])
end
end

context 'when the migration is rolled' do
let(:api_version) { '2018-07-16' }

it 'marks the newsletter as sent' do
expect(result['result.migration_rolled']).to eq(true)
end

it 'computes whether a migration applies' do
expect(result['result.migration_applies']).to eq(false)
end

it 'returns the correct rolled migrations' do
expect(result['result.rolled_migrations']).to eq([API::Migration::SendNewsletterAutomatically])
end

it 'returns the correct applying migrations' do
expect(result['result.applying_migrations']).to eq([])
end
end
end
14 changes: 8 additions & 6 deletions spec/pragma/migration/middleware_spec.rb
Expand Up @@ -2,12 +2,14 @@

RSpec.describe Pragma::Migration::Middleware do
subject do
described_class.new(app,
repository: repository,
user_version_proc: (lambda do |request|
request.get_header 'X-Test-Api-Version'
end)
)
described_class.new(app)
end

before do
Pragma::Migration.repository = repository
Pragma::Migration.user_version_proc = lambda do |request|
request.get_header 'X-Test-Api-Version'
end
end

let(:app) do
Expand Down
11 changes: 11 additions & 0 deletions spec/pragma/migration/migration_spec.rb
@@ -0,0 +1,11 @@
# frozen_string_literal: true

RSpec.describe Pragma::Migration do
describe '#repository=' do
it 'changes the repository' do
expect {
subject.repository = 'test'
}.to change(subject, :repository).to('test')
end
end
end
1 change: 1 addition & 0 deletions spec/spec_helper.rb
@@ -1,4 +1,5 @@
require "bundler/setup"
require 'pragma/operation'
require "pragma/migration"

require 'coveralls'
Expand Down

0 comments on commit e1b46f4

Please sign in to comment.