Skip to content
Merged
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
3 changes: 2 additions & 1 deletion lib/fx/adapters/postgres/functions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ class Functions
FUNCTIONS_WITH_DEFINITIONS_QUERY = <<~SQL.freeze
SELECT
pp.proname AS name,
pg_get_functiondef(pp.oid) AS definition
pg_get_functiondef(pp.oid) AS definition,
pg_get_function_identity_arguments(pp.oid) AS arguments
FROM pg_proc pp
JOIN pg_namespace pn
ON pn.oid = pp.pronamespace
Expand Down
16 changes: 14 additions & 2 deletions lib/fx/function.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@ class Function
include Comparable

attr_reader :name, :definition
delegate :<=>, to: :name

def initialize(row)
@name = row.fetch("name")
@definition = row.fetch("definition")
@arguments = row.fetch("arguments", nil)
end

def <=>(other)
signature <=> other.signature
end

def ==(other)
name == other.name && definition == other.definition
signature == other.signature && definition == other.definition
end

def signature
if @arguments.nil?
name
else
"#{name}(#{@arguments})"
end
end

def to_schema
Expand Down
96 changes: 87 additions & 9 deletions spec/fx/function_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,121 @@

RSpec.describe Fx::Function do
describe "#<=>" do
it "delegates to `name`" do
it "delegates to `signature`" do
function_a = Fx::Function.new(
"name" => "name_a",
"definition" => "some definition"
"definition" => "some definition",
"arguments" => ""
)
function_b = Fx::Function.new(
"name" => "name_b",
"definition" => "some definition"
"definition" => "some definition",
"arguments" => ""
)
function_c = Fx::Function.new(
"name" => "name_c",
"definition" => "some definition"
"definition" => "some definition",
"arguments" => ""
)

expect(function_b).to be_between(function_a, function_c)
end

it "orders overloads by signature" do
add_int = Fx::Function.new(
"name" => "add",
"definition" => "some definition",
"arguments" => "integer, integer"
)
add_text = Fx::Function.new(
"name" => "add",
"definition" => "some definition",
"arguments" => "text, text"
)

expect(add_int <=> add_text).to be < 0
end
end

describe "#==" do
it "compares `name` and `definition`" do
it "compares `signature` and `definition`" do
function_a = Fx::Function.new(
"name" => "name_a",
"definition" => "some definition"
"definition" => "some definition",
"arguments" => ""
)
function_b = Fx::Function.new(
"name" => "name_b",
"definition" => "some other definition"
"definition" => "some other definition",
"arguments" => ""
)

expect(function_a).not_to eq(function_b)
end

it "distinguishes overloads with the same name" do
add_int = Fx::Function.new(
"name" => "add",
"definition" => "CREATE FUNCTION add(integer)",
"arguments" => "integer"
)
add_text = Fx::Function.new(
"name" => "add",
"definition" => "CREATE FUNCTION add(text)",
"arguments" => "text"
)

expect(add_int).not_to eq(add_text)
end
end

describe "#signature" do
it "returns name with arguments when arguments are present" do
function = Fx::Function.new(
"name" => "inc",
"definition" => "some definition",
"arguments" => "integer"
)

expect(function.signature).to eq("inc(integer)")
end

it "returns name with multiple arguments" do
function = Fx::Function.new(
"name" => "add",
"definition" => "some definition",
"arguments" => "integer, integer"
)

expect(function.signature).to eq("add(integer, integer)")
end

it "returns name with empty parens for no-arg functions" do
function = Fx::Function.new(
"name" => "now_utc",
"definition" => "some definition",
"arguments" => ""
)

expect(function.signature).to eq("now_utc()")
end

it "returns just the name when arguments key is missing" do
function = Fx::Function.new(
"name" => "now_utc",
"definition" => "some definition"
)

expect(function.signature).to eq("now_utc")
end
end

describe "#to_schema" do
it "returns a schema compatible version of the function" do
function = Fx::Function.new(
"name" => "uppercase_users_name",
"definition" => "CREATE OR REPLACE TRIGGER uppercase_users_name ..."
"definition" => "CREATE OR REPLACE TRIGGER uppercase_users_name ...",
"arguments" => ""
)

expect(function.to_schema).to eq(<<-EOS)
Expand All @@ -52,7 +129,8 @@
it "maintains backslashes" do
function = Fx::Function.new(
"name" => "regex",
"definition" => "CREATE OR REPLACE FUNCTION regex \\1"
"definition" => "CREATE OR REPLACE FUNCTION regex \\1",
"arguments" => ""
)

expect(function.to_schema).to eq(<<-'EOS')
Expand Down