Skip to content
This repository was archived by the owner on May 16, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions lib/viewr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,25 @@ def self.setup_runner(connection, view_files_path, function_files_path)
adapter = DatabaseAdapter.new(connection)
runner = SchemaObjectRunner.new

self.load_views(view_files_path, runner, adapter)
self.load_functions(function_files_path, runner, adapter)
self.load_view_specs(view_files_path, runner, adapter)
self.load_function_specs(function_files_path, runner, adapter)

runner
end

def self.load_views(path, runner, database_adapter)
def self.load_view_specs(path, runner, database_adapter)
view_files = File.join(path, '*.yml')
Dir.glob(view_files).each do |view_file|
runner << View.new_from_yaml(IO.read(view_file), database_adapter)
view_specification = IO.read(view_file)
runner << View.new_from_yaml(view_specification, database_adapter)
end
end

def self.load_functions(path, runner, database_adapter)
def self.load_function_specs(path, runner, database_adapter)
function_files = File.join(path, '*.yml')
Dir.glob(function_files).each do |function_file|
runner << Function.new_from_yaml(IO.read(function_file), database_adapter)
function_specification = IO.read(function_file)
runner << Function.new_from_yaml(function_specification, database_adapter)
end
end
end
44 changes: 30 additions & 14 deletions lib/viewr/database_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,58 @@ def run(statement)
connection.run(statement)
end

def drop_view(view_name)
return unless view_exists?(view_name)
def create_view(view)
return if view_exists?(view.name)

run(drop_view_sql(view_name))
run(create_view_sql(view))
end

def drop_function(function_name)
existing_functions_with_argument_types(function_name).each do |function_with_argument_types|
def drop_view(view)
return unless view_exists?(view.name)

run(drop_view_sql(view))
end

def create_function(function)
run(function.sql)
end

def drop_function(function)
existing_functions_with_argument_types(function.name).each do |function_with_argument_types|
run(drop_function_sql(function_with_argument_types))
end
end

def drop_view_sql(view_name)
case view_type(view_name)
when :view then "DROP VIEW IF EXISTS #{view_name} CASCADE"
when :materialized_view then "DROP MATERIALIZED VIEW #{view_name} CASCADE"
def create_view_sql(view)
case view.type
when :view then "CREATE OR REPLACE VIEW #{view.name} AS #{view.sql}"
when :materialized_view then "CREATE OR REPLACE MATERIALIZED VIEW #{view.name} AS #{view.sql}"
end
end

def drop_view_sql(view)
case view.type
when :view then "DROP VIEW IF EXISTS #{view.name} CASCADE"
when :materialized_view then "DROP MATERIALIZED VIEW #{view.name} CASCADE"
end
end

def drop_function_sql(function_with_argument_types)
"DROP FUNCTION IF EXISTS #{function_with_argument_types} CASCADE"
end

def view_exists?(view_name)
!view_type(view_name).nil?
def view_exists?(view)
!view_exists_in_database?(view).nil?
end

private

def view_type(view_name)
def view_exists_in_database?(view)
result = @connection.fetch(%Q(
SELECT relkind
FROM pg_catalog.pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relname = '#{view_name}'
WHERE n.nspname = 'public' AND c.relname = '#{view.name}'
)).first

case
Expand All @@ -70,4 +87,3 @@ def existing_functions_with_argument_types(function_name)
end
end
end

19 changes: 12 additions & 7 deletions lib/viewr/database_object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ module Viewr
class DatabaseObject
attr_reader :name, :dependencies, :sql, :adapter

def initialize(function_doc, adapter)
@name = function_doc['name']
@dependencies = function_doc['dependencies']
@sql = function_doc['sql']
def initialize(specification, adapter)
@name = specification.fetch('name')
@sql = specification.fetch('sql')

@dependencies = specification['dependencies']

@adapter = adapter
end

Expand All @@ -23,12 +25,16 @@ def dependencies
@dependencies || []
end

def type
@type.to_sym if @type
end

def create
raise '#create is not implemented'
fail '#create is not implemented'
end

def drop
raise '#drop is not implemented'
fail '#drop is not implemented'
end

def eql?(another_function)
Expand All @@ -40,4 +46,3 @@ def hash
end
end
end

10 changes: 8 additions & 2 deletions lib/viewr/function.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

module Viewr
class Function < DatabaseObject

def initialize(specification, adapter)
@type = :function
super
end

def create
@adapter.run(@sql)
@adapter.create_function(self)
end

def drop
@adapter.drop_function(@name)
@adapter.drop_function(self)
end
end
end
11 changes: 8 additions & 3 deletions lib/viewr/view.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

module Viewr
class View < DatabaseObject

def initialize(specification, adapter)
@type = specification.fetch('type', :view)
super
end

def create
@adapter.run(@sql)
@adapter.create_view(self)
end

def drop
@adapter.drop_view(@name)
@adapter.drop_view(self)
end
end
end

7 changes: 3 additions & 4 deletions spec/support/shared_examples_for_database_objects.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@

shared_examples 'a database object' do

let (:yaml) { "---\nname: foo\ndependencies:\n - 'bar'\nsql: 'SQL'" }
let (:yaml) { "---\nname: foo\ndependencies:\n - 'bar'\ntype: 'type'\nsql: 'SQL'" }
let (:view_doc_with_dependencies) do
{
'name' => 'foo',
'dependencies' => ['bar', 'baz'],
'sql' => 'SOME SQL STATEMENT FOR FOO HERE'
'sql' => 'SOME SQL STATEMENT FOR FOO HERE',
}
end
let (:view_doc_without_dependencies) do
{
'name' => 'bar',
'sql' => 'SOME SQL STATEMENT FOR BAR HERE'
'sql' => 'SOME SQL STATEMENT FOR BAR HERE',
}
end

Expand Down Expand Up @@ -78,4 +78,3 @@
end
end
end

50 changes: 33 additions & 17 deletions spec/viewr/database_adapter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@

let(:connection) { double(:connection) }
let(:database_adapter) { Viewr::DatabaseAdapter.new(connection) }
let(:function) {
function_params = {
'name' => 'function',
'sql' => 'Select * FROM something;'
}
Viewr::Function.new(function_params, database_adapter) }
let(:view) {
view_params = {
'name' => 'view',
'sql' => 'Select * FROM something;'
}
Viewr::View.new(view_params, database_adapter) }
let(:materialized_view) {
materialized_view_params = {
'name' => 'some_materialized_view',
'type' => 'materialized_view',
'sql' => 'Select * FROM something;'
}
Viewr::View.new(materialized_view_params, database_adapter)
}

describe '#initialize' do
it 'saves the given connection to an attribute' do
Expand All @@ -21,43 +41,39 @@

describe '#drop_view' do
it 'does not run SQL statement if view does not exist' do
allow(database_adapter).to receive(:view_exists?).with(:view_name).and_return(false)
expect(database_adapter).to receive(:view_exists?).with(view.name).and_return(false)
expect(database_adapter).not_to receive(:run)

database_adapter.drop_view(:view_name)
database_adapter.drop_view(view)
end

it 'runs the SQL statement returned by #drop_view_sql' do
allow(database_adapter).to receive(:view_exists?).with(:view_name).and_return(true)
expect(database_adapter).to receive(:drop_view_sql).with(:view_name).and_return(:sql_statement)
expect(database_adapter).to receive(:view_exists?).with(view.name).and_return(true)
expect(database_adapter).to receive(:drop_view_sql).with(view).and_return(:sql_statement)
expect(database_adapter).to receive(:run).with(:sql_statement)

database_adapter.drop_view(:view_name)
database_adapter.drop_view(view)
end
end

describe '#drop_function' do
it 'runs the SQL statement returned by #drop_function_sql' do
expect(database_adapter).to receive(:existing_functions_with_argument_types).with(:function_name).and_return([:foo])
expect(database_adapter).to receive(:existing_functions_with_argument_types).with(function.name).and_return([:foo])
expect(database_adapter).to receive(:drop_function_sql).with(:foo).and_return(:sql_statement)
expect(database_adapter).to receive(:run).with(:sql_statement)

database_adapter.drop_function(:function_name)
database_adapter.drop_function(function)
end

end

describe '#drop_view_sql' do
it 'returns an SQL statement to drop the given view if type is :view' do
allow(database_adapter).to receive(:view_type).with('view_name').and_return(:view)

expect(database_adapter.drop_view_sql('view_name')).to eql('DROP VIEW IF EXISTS view_name CASCADE')
expect(database_adapter.drop_view_sql(view)).to eql("DROP VIEW IF EXISTS #{view.name} CASCADE")
end

it 'returns an SQL statement to drop the given materialized view if type is :materialized_view' do
allow(database_adapter).to receive(:view_type).with('view_name').and_return(:materialized_view)

expect(database_adapter.drop_view_sql('view_name')).to eql('DROP MATERIALIZED VIEW view_name CASCADE')
expect(database_adapter.drop_view_sql(materialized_view)).to eql("DROP MATERIALIZED VIEW #{materialized_view.name} CASCADE")
end
end

Expand All @@ -69,17 +85,17 @@

describe '#view_exists?' do
it 'returns false if view type is nil' do
allow(database_adapter).to receive(:view_type).with(:view_name).and_return(nil)
expect(database_adapter).to receive(:view_exists_in_database?).with(view).and_return(nil)

result = database_adapter.view_exists?(:view_name)
result = database_adapter.view_exists?(view)

expect(result).to be false
end

it 'returns true if view type is not nil' do
allow(database_adapter).to receive(:view_type).with(:view_name).and_return(:view)
expect(database_adapter).to receive(:view_exists_in_database?).with(view).and_return(:view)

result = database_adapter.view_exists?(:view_name)
result = database_adapter.view_exists?(view)

expect(result).to be true
end
Expand Down
12 changes: 9 additions & 3 deletions spec/viewr/function_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,26 @@

it_behaves_like 'a database object'

describe '#initialize' do
it 'defaults type to :function' do
function = Viewr::Function.new(function_doc, adapter)
expect(function.type).to eql(:function)
end
end

describe '#create' do
it 'should run the SQL on the adapter' do
expect(adapter).to receive(:run).with('SQL STATEMENT')
expect(adapter).to receive(:create_function).with(function)

function.create
end
end

describe '#drop' do
it 'should drop the function using the adapter' do
expect(adapter).to receive(:drop_function).with('bar')
expect(adapter).to receive(:drop_function).with(function)

function.drop
end
end
end

16 changes: 14 additions & 2 deletions spec/viewr/view_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,29 @@

it_behaves_like 'a database object'

describe '#initialize' do
it 'defaults type to :view' do
view = Viewr::View.new(view_doc, adapter)
expect(view.type).to eql(:view)
end

it 'allows to set type' do
view = Viewr::View.new(view_doc.merge('type' => 'materialized_views'), adapter)
expect(view.type).to eql(:materialized_views)
end
end

describe '#create' do
it 'should run the SQL on the adapter' do
expect(adapter).to receive(:run).with('SQL STATEMENT')
expect(adapter).to receive(:create_view).with(view)

view.create
end
end

describe '#drop' do
it 'should drop the view using the adapter' do
expect(adapter).to receive(:drop_view).with('bar')
expect(adapter).to receive(:drop_view).with(view)

view.drop
end
Expand Down
Loading