Skip to content

Commit

Permalink
Adds in Array enums. (#1009)
Browse files Browse the repository at this point in the history
* Adds in Array enums. Fixes #940

* Removing this commented code since the actual case is covered in the parent Type
  • Loading branch information
jwoertink authored Mar 23, 2024
1 parent 9f727d2 commit 2249315
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 3 deletions.
13 changes: 13 additions & 0 deletions db/migrations/20240309222910_add_enums_to_bucket.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class AddEnumsToBucket::V20240309222910 < Avram::Migrator::Migration::V1
def migrate
alter table_for(Bucket) do
add enums : Array(Int32), default: [] of Int32
end
end

def rollback
alter table_for(Bucket) do
remove :enums
end
end
end
8 changes: 8 additions & 0 deletions spec/avram/array_column_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,12 @@ describe "Array Columns" do
bucket = SaveBucket.update!(bucket, numbers: nil)
bucket.numbers.should be_nil
end

it "handles Array(Enum)" do
BucketFactory.create &.enums([Bucket::Size::Large, Bucket::Size::Tub])
bucket = BucketQuery.new.last
bucket.enums.should eq([Bucket::Size::Large, Bucket::Size::Tub])
bucket = SaveBucket.update!(bucket, enums: [Bucket::Size::Small])
bucket.enums.should eq([Bucket::Size::Small])
end
end
4 changes: 3 additions & 1 deletion spec/avram/queryable_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1172,12 +1172,14 @@ describe Avram::Queryable do
context "when querying arrays" do
describe "simple where query" do
it "returns 1 result" do
bucket = BucketFactory.new.names(["pumpkin", "zucchini"]).create
bucket = BucketFactory.new.names(["pumpkin", "zucchini"]).enums([Bucket::Size::Medium]).create

query = BucketQuery.new.names(["pumpkin", "zucchini"])
query.to_sql.should eq ["SELECT #{Bucket::COLUMN_SQL} FROM buckets WHERE buckets.names = $1", "{\"pumpkin\",\"zucchini\"}"]
result = query.first
result.should eq bucket

BucketQuery.new.enums.includes(Bucket::Size::Medium).select_count.should eq(1)
end
end

Expand Down
1 change: 1 addition & 0 deletions spec/support/factories/bucket_factory.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ class BucketFactory < BaseFactory
names ["Mario", "Luigi"]
floaty_numbers [0.0]
oody_things [UUID.random]
enums [Bucket::Size::ExtraSmall, Bucket::Size::Medium]
end
end
13 changes: 12 additions & 1 deletion spec/support/models/bucket.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
class Bucket < BaseModel
COLUMN_SQL = "buckets.id, buckets.created_at, buckets.updated_at, buckets.bools, buckets.small_numbers, buckets.numbers, buckets.big_numbers, buckets.names, buckets.floaty_numbers, buckets.oody_things"
COLUMN_SQL = column_names.join(", ") { |col| "buckets.#{col}" }

enum Size
ExtraSmall
Small
Medium
Large
ExtraLarge
Tub
end

table do
column bools : Array(Bool) = [] of Bool
column small_numbers : Array(Int16) = [] of Int16
Expand All @@ -8,5 +18,6 @@ class Bucket < BaseModel
column names : Array(String) = [] of String
column floaty_numbers : Array(Float64) = [] of Float64
column oody_things : Array(UUID) = [] of UUID
column enums : Array(Bucket::Size) = [] of Bucket::Size, converter: PG::EnumArrayConverter(Bucket::Size)
end
end
18 changes: 18 additions & 0 deletions src/avram/charms/enum_extensions.cr
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,28 @@ abstract struct Enum
end
end

def parse(value : Array(T))
SuccessfulCast(Array(T)).new value
end

def parse(values : Array(Int))
results = values.map { |i| parse(i) }
if results.all?(SuccessfulCast)
parse(results.map(&.value.as(T)))
else
FailedCast.new
end
end

def parse(value : T)
SuccessfulCast.new(value)
end

def to_db(values : Array(T))
encoded = values.map { |value| to_db(value) }.as(Array(String))
PQ::Param.encode_array(encoded)
end

def to_db(value : T) : String
value.value.to_s
end
Expand Down
4 changes: 3 additions & 1 deletion src/avram/model.cr
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ abstract class Avram::Model
{% end %}
end

macro column(type_declaration, autogenerated = false, serialize is_serialized = false, allow_blank = false)
macro column(type_declaration, autogenerated = false, serialize is_serialized = false, allow_blank = false, converter = nil)
{% if type_declaration.type.is_a?(Union) %}
{% data_type = type_declaration.type.types.first %}
{% nilable = true %}
Expand All @@ -266,6 +266,8 @@ abstract class Avram::Model
converter: JSONConverter({{ data_type }}),
{% elsif data_type.id == Array(Float64).id %}
converter: PG::NumericArrayFloatConverter,
{% elsif converter %}
converter: {{ converter }},
{% end %}
)]
{% if data_type.is_a?(Generic) || is_serialized %}
Expand Down
15 changes: 15 additions & 0 deletions src/ext/pg/enum_array_converter.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Extends the PG shard and adds a converter for
# converting `Array(Int)` columns to `Array(Enum)`. This
# can be used with raw SQL queries.
# ```
# enum Colors
# Red
# end
# @[DB::Field(converter: PG::EnumArrayConverter(Colors))]
# property colors : Array(Colors)
# ```
module PG::EnumArrayConverter(T)
def self.from_rs(rs : DB::ResultSet)
rs.read(Array(typeof(T.values.first.value))).map { |i| T.from_value(i) }
end
end

0 comments on commit 2249315

Please sign in to comment.