Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Unify pk and content and timestamps into FIELDS #169

Merged
merged 1 commit into from
Mar 31, 2018
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
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