Skip to content

Commit

Permalink
Use DB::Serializable instead of DB.mapping. (#996)
Browse files Browse the repository at this point in the history
* Use DB::Serializable instead of DB.mapping. Fixes #364

* bump to latest PG and DB shards to ensure everything works. Fixing compilation errors
  • Loading branch information
jwoertink authored Dec 29, 2023
1 parent 95bf07f commit 641937e
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 34 deletions.
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dependencies:
version: ~> 0.3.0
pg:
github: will/crystal-pg
version: ~> 0.27.0
version: ~> 0.28.0
habitat:
github: luckyframework/habitat
version: ~> 0.4.7
Expand Down
2 changes: 2 additions & 0 deletions src/avram/associations.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ module Avram::Associations
end

# Returns `true` if the association has been preloaded
@[DB::Field(ignore: true)]
getter? {{ assoc_name }}_preloaded : Bool = false
@[DB::Field(ignore: true)]
private getter _preloaded_{{ assoc_name }} : {{ model }}?
end

Expand Down
2 changes: 2 additions & 0 deletions src/avram/associations/has_many.cr
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ module Avram::Associations::HasMany
end

private macro define_has_many_lazy_loading(assoc_name, model, foreign_key, through)
@[DB::Field(ignore: true)]
@_preloaded_{{ assoc_name }} : Array({{ model }})?
@[DB::Field(ignore: true)]
getter? {{ assoc_name }}_preloaded : Bool = false

def _preloaded_{{ assoc_name }}=(vals : Array({{ model }})) : Array({{ model }})
Expand Down
49 changes: 16 additions & 33 deletions src/avram/model.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,11 @@ abstract class Avram::Model
include Avram::Polymorphic
include Avram::SchemaEnforcer

module JSONConverter(T)
def self.from_rs(rs : DB::ResultSet)
value = rs.read(JSON::PullParser?)
T.new(value) if value
end
end

macro inherited
COLUMNS = [] of Nil # types are not checked in macros
ASSOCIATIONS = [] of Nil # types are not checked in macros
include LuckyCache::Cachable
include DB::Serializable
end

def self.primary_key_name : Symbol?
Expand Down Expand Up @@ -67,7 +61,6 @@ abstract class Avram::Model
class_getter table_name : String = Avram::TableFor.table_for({{ @type.id }})
{% end %}
setup(Avram::Model.setup_initialize)
setup(Avram::Model.setup_db_mapping)
setup(Avram::Model.setup_getters)
setup(Avram::Model.setup_column_info_methods)
setup(Avram::Model.setup_association_queries)
Expand All @@ -88,7 +81,6 @@ abstract class Avram::Model
class_getter table_name : String = Avram::TableFor.table_for({{ @type.id }})
{% end %}
setup(Avram::Model.setup_initialize)
setup(Avram::Model.setup_db_mapping)
setup(Avram::Model.setup_getters)
setup(Avram::Model.setup_column_info_methods)
setup(Avram::Model.setup_association_queries)
Expand Down Expand Up @@ -182,30 +174,6 @@ abstract class Avram::Model
end
end

# Setup [database mapping](http://crystal-lang.github.io/crystal-db/api/latest/DB.html) for the model's columns.
#
# NOTE: Avram::Migrator saves `Float` columns as numeric which are converted
# in the avram/charms/float64_extensions.cr file
macro setup_db_mapping(columns, *args, **named_args)
DB.mapping({
{% for column in columns %}
{{column[:name]}}: {
{% if column[:type].id == Array(Float64).id %}
type: Array(PG::Numeric),
{% elsif column[:type].is_a?(Generic) || column[:serialized] %}
type: {{column[:type]}},
{% else %}
type: {{column[:type]}}::Lucky::ColumnType,
{% end %}
nilable: {{column[:nilable]}},
{% if column[:serialized] %}
converter: JSONConverter({{column[:type]}}),
{% end %}
},
{% end %}
})
end

macro setup_association_queries(associations, *args, **named_args)
{% for assoc in associations %}
def {{ assoc[:assoc_name] }}_query
Expand Down Expand Up @@ -257,6 +225,21 @@ abstract class Avram::Model
{% value = nil %}
{% end %}

@[DB::Field(
ignore: false,
key: {{ type_declaration.var }},
{% if is_serialized %}
converter: JSONConverter({{ data_type }}),
{% elsif data_type.id == Array(Float64).id %}
converter: PG::NumericArrayFloatConverter,
{% end %}
)]
{% if data_type.is_a?(Generic) || is_serialized %}
@{{ type_declaration.var }} : {{ data_type }}{% if nilable %}?{% end %}
{% else %}
@{{ type_declaration.var }} : {{ data_type }}::Lucky::ColumnType{% if nilable %}?{% end %}
{% end %}

{% COLUMNS << {name: type_declaration.var, type: data_type, nilable: nilable.id, autogenerated: autogenerated, value: value, serialized: is_serialized, allow_blank: allow_blank} %}
end

Expand Down
8 changes: 8 additions & 0 deletions src/ext/pg/json_converter.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This is used for serialized JSON objects.
# e.g. `column theme : Theme, serialize: true`
module JSONConverter(T)
def self.from_rs(rs : DB::ResultSet)
value = rs.read(JSON::PullParser?)
T.new(value) if value
end
end
12 changes: 12 additions & 0 deletions src/ext/pg/numeric_array_float_converter.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Extends the PG shard and adds a converter for
# converting `Array(PG::Numeric)` columns to `Array(Float64)`. This
# can be used with raw SQL queries.
# ```
# @[DB::Field(converter: PG::NumericArrayFloatConverter)]
# property average_amount : Array(Float64)
# ```
module PG::NumericArrayFloatConverter
def self.from_rs(rs : DB::ResultSet)
rs.read(Array(PG::Numeric)).map(&.to_f)
end
end

0 comments on commit 641937e

Please sign in to comment.