Skip to content

Commit

Permalink
refactor: Unify pk and content and timestamps into FIELDS (amberfra…
Browse files Browse the repository at this point in the history
  • Loading branch information
maiha authored and drujensen committed Mar 31, 2018
1 parent 5808e1c commit 481f7eb
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 67 deletions.
70 changes: 25 additions & 45 deletions src/granite_orm/fields.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,29 @@ module Granite::ORM::Fields

macro included
macro inherited
CONTENT_FIELDS = {} of Nil => Nil
FIELDS = {} of Nil => Nil
end
end

# specify the fields you want to define and types
macro field(decl)
{% FIELDS[decl.var] = decl.type %}
{% CONTENT_FIELDS[decl.var] = decl.type %}
end

# include created_at and updated_at that will automatically be updated
macro timestamps
{% SETTINGS[:timestamps] = true %}
field created_at : Time
field updated_at : Time
end

macro __process_fields
# merge PK and CONTENT_FIELDS into FIELDS
{% FIELDS[PRIMARY[:name]] = PRIMARY[:type] %}
{% for name, type in CONTENT_FIELDS %}
{% FIELDS[name] = type %}
{% end %}

# Create the properties
{% for name, type in FIELDS %}
property? {{name.id}} : Union({{type.id}} | Nil)
Expand All @@ -29,51 +37,32 @@ module Granite::ORM::Fields
@{{name.id}}.not_nil!
end
{% end %}
{% if SETTINGS[:timestamps] %}
property? created_at : Time?
property? updated_at : Time?
{% for name in %w( created_at updated_at ) %}
def {{name.id}}
raise {{@type.name.stringify}} + "#" + {{name.stringify}} + " cannot be nil" if @{{name.id}}.nil?
@{{name.id}}.not_nil!
end
{% end %}
{% end %}

# keep a hash of the fields to be used for mapping
def self.fields(fields = [] of String)
{% for name, type in FIELDS %}
fields << "{{name.id}}"
{% end %}
{% if SETTINGS[:timestamps] %}
fields << "created_at"
fields << "updated_at"
{% end %}
return fields
def self.fields : Array(String)
@@fields ||= {{ FIELDS.empty? ? "[] of String".id : FIELDS.keys.map(&.id.stringify) }}
end

def self.content_fields : Array(String)
@@content_fields ||= {{ CONTENT_FIELDS.empty? ? "[] of String".id : CONTENT_FIELDS.keys.map(&.id.stringify) }}
end

# keep a hash of the params that will be passed to the adapter.
def params
def content_values
parsed_params = [] of DB::Any
{% for name, type in FIELDS %}
{% for name, type in CONTENT_FIELDS %}
{% if type.id == Time.id %}
parsed_params << {{name.id}}?.try(&.to_s("%F %X"))
{% else %}
parsed_params << {{name.id}}?
{% end %}
{% end %}
{% if SETTINGS[:timestamps] %}
parsed_params << created_at.to_s("%F %X")
parsed_params << updated_at.to_s("%F %X")
{% end %}
return parsed_params
end

def to_h
fields = {} of String => DB::Any

fields["{{PRIMARY[:name]}}"] = {{PRIMARY[:name]}}?

{% for name, type in FIELDS %}
{% if type.id == Time.id %}
fields["{{name}}"] = {{name.id}}?.try(&.to_s("%F %X"))
Expand All @@ -83,18 +72,12 @@ module Granite::ORM::Fields
fields["{{name}}"] = {{name.id}}?
{% end %}
{% end %}
{% if SETTINGS[:timestamps] %}
fields["created_at"] = created_at?.try(&.to_s("%F %X"))
fields["updated_at"] = updated_at?.try(&.to_s("%F %X"))
{% end %}

return fields
end

def to_json(json : JSON::Builder)
json.object do
json.field "{{PRIMARY[:name]}}", {{PRIMARY[:name]}}?

{% for name, type in FIELDS %}
%field, %value = "{{name.id}}", {{name.id}}?
{% if type.id == Time.id %}
Expand All @@ -105,11 +88,6 @@ module Granite::ORM::Fields
json.field %field, %value
{% end %}
{% end %}

{% if SETTINGS[:timestamps] %}
json.field "created_at", created_at?.try(&.to_s("%F %X"))
json.field "updated_at", updated_at?.try(&.to_s("%F %X"))
{% end %}
end
end

Expand All @@ -127,13 +105,15 @@ module Granite::ORM::Fields
private def cast_to_field(name, value : Type)
{% unless FIELDS.empty? %}
case name.to_s
when "{{PRIMARY[:name]}}"
{% if !PRIMARY[:auto] %}
@{{PRIMARY[:name]}} = value.as({{PRIMARY[:type]}})
{% end %}

{% for _name, type in FIELDS %}
when "{{_name.id}}"
if "{{_name.id}}" == "{{PRIMARY[:name]}}"
{% if !PRIMARY[:auto] %}
@{{PRIMARY[:name]}} = value.as({{PRIMARY[:type]}})
{% end %}
return
end

return @{{_name.id}} = nil if value.nil?
{% if type.id == Int32.id %}
@{{_name.id}} = value.is_a?(String) ? value.to_i32(strict: false) : value.is_a?(Int64) ? value.to_i32 : value.as(Int32)
Expand Down
30 changes: 13 additions & 17 deletions src/granite_orm/querying.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,18 @@ module Granite::ORM::Querying
def set_attributes(result : DB::ResultSet)
# Loading from DB means existing records.
@new_record = false
self.\{{primary_name}} = result.read(\{{primary_type}})

\{% for name, type in FIELDS %}
self.\{{name.id}} = result.read(Union(\{{type.id}} | Nil))
\{% end %}

\{% if SETTINGS[:timestamps] %}
if @@adapter.class.name == "Granite::Adapter::Sqlite"
# sqlite3 does not have timestamp type - timestamps are stored as strings
# will break for null timestamps
self.created_at = Time.parse(result.read(String), "%F %X" )
self.updated_at = Time.parse(result.read(String), "%F %X" )
else
self.created_at = result.read(Union(Time | Nil))
self.updated_at = result.read(Union(Time | Nil))
end
\{% if type.id.stringify == "Time" %}
if @@adapter.class.name == "Granite::Adapter::Sqlite"
# sqlite3 does not have timestamp type - timestamps are stored as str
# will break for null timestamps
self.\{{name.id}} = Time.parse(result.read(String), "%F %X" )
else
self.\{{name.id}} = result.read(Union(\{{type.id}} | Nil))
end
\{% else %}
self.\{{name.id}} = result.read(Union(\{{type.id}} | Nil))
\{% end %}
\{% end %}
return self
end
Expand All @@ -53,7 +49,7 @@ module Granite::ORM::Querying
# DSL.
def all(clause = "", params = [] of DB::Any)
rows = [] of self
@@adapter.select(@@table_name, fields([@@primary_name]), clause, params) do |results|
@@adapter.select(@@table_name, fields, clause, params) do |results|
results.each do
rows << from_sql(results)
end
Expand Down Expand Up @@ -84,7 +80,7 @@ module Granite::ORM::Querying
# find_by returns the first row found where the field maches the value
def find_by?(field : String | Symbol, value)
row = nil
@@adapter.select_one(@@table_name, fields([@@primary_name]), field.to_s, value) do |result|
@@adapter.select_one(@@table_name, fields, field.to_s, value) do |result|
row = from_sql(result) if result
end
return row
Expand Down
11 changes: 6 additions & 5 deletions src/granite_orm/transactions.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,20 @@ module Granite::ORM::Transactions
if (value = @{{primary_name}}) && !new_record?
__run_before_update
@updated_at = now
params_and_pk = params
params_and_pk << value
fields = self.class.content_fields
params = content_values + [value]

begin
@@adapter.update @@table_name, @@primary_name, self.class.fields, params_and_pk
@@adapter.update @@table_name, @@primary_name, fields, params
rescue err
raise DB::Error.new(err.message)
end
__run_after_update
else
__run_before_create
@created_at = @updated_at = now
params = params()
fields = self.class.fields
fields = self.class.content_fields.dup
params = content_values
if value = @{{primary_name}}
fields << "{{primary_name}}"
params << value
Expand Down

0 comments on commit 481f7eb

Please sign in to comment.