From 39637c5e3d3fe717deec4264e1b1646dd76f99bd Mon Sep 17 00:00:00 2001 From: Tatsiujin Chin Date: Wed, 11 Apr 2018 19:48:10 +0800 Subject: [PATCH 1/2] add field! --- src/granite_orm/fields.cr | 37 ++++++++++++++++++++++++------------- src/granite_orm/querying.cr | 3 ++- src/granite_orm/table.cr | 7 ------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/granite_orm/fields.cr b/src/granite_orm/fields.cr index e7e778dc..06d47802 100644 --- a/src/granite_orm/fields.cr +++ b/src/granite_orm/fields.cr @@ -12,8 +12,14 @@ module Granite::ORM::Fields end # specify the fields you want to define and types - macro field(decl) - {% CONTENT_FIELDS[decl.var] = decl.type %} + macro field(decl, **options) + {% CONTENT_FIELDS[decl.var] = options || {} of Nil => Nil %} + {% CONTENT_FIELDS[decl.var][:type] = decl.type %} + end + + # specify the raise-on-nil fields you want to define and types + macro field!(decl, **options) + field {{decl}}, {{options.double_splat(", ")}}raise_on_nil: true end # include created_at and updated_at that will automatically be updated @@ -24,15 +30,17 @@ module Granite::ORM::Fields macro __process_fields # merge PK and CONTENT_FIELDS into FIELDS - {% FIELDS[PRIMARY[:name]] = PRIMARY[:type] %} - {% for name, type in CONTENT_FIELDS %} - {% FIELDS[name] = type %} + {% FIELDS[PRIMARY[:name]] = PRIMARY %} + {% for name, options in CONTENT_FIELDS %} + {% FIELDS[name] = options %} {% end %} # Create the properties - {% for name, type in FIELDS %} - property {{name.id}} : Union({{type.id}} | Nil) - def {{name.id}}! + {% for name, options in FIELDS %} + {% type = options[:type] %} + {% suffixes = options[:raise_on_nil] ? ["?", ""] : ["", "!"] %} + property{{suffixes[0].id}} {{name.id}} : Union({{type.id}} | Nil) + def {{name.id}}{{suffixes[1].id}} raise {{@type.name.stringify}} + "#" + {{name.stringify}} + " cannot be nil" if @{{name.id}}.nil? @{{name.id}}.not_nil! end @@ -50,8 +58,8 @@ module Granite::ORM::Fields # keep a hash of the params that will be passed to the adapter. def content_values parsed_params = [] of DB::Any - {% for name, type in CONTENT_FIELDS %} - {% if type.id == Time.id %} + {% for name, options in CONTENT_FIELDS %} + {% if options[:type].id == Time.id %} parsed_params << {{name.id}}.try(&.to_s("%F %X")) {% else %} parsed_params << {{name.id}} @@ -63,7 +71,8 @@ module Granite::ORM::Fields def to_h fields = {} of String => DB::Any - {% for name, type in FIELDS %} + {% for name, options in FIELDS %} + {% type = options[:type] %} {% if type.id == Time.id %} fields["{{name}}"] = {{name.id}}.try(&.to_s("%F %X")) {% elsif type.id == Slice.id %} @@ -78,7 +87,8 @@ module Granite::ORM::Fields def to_json(json : JSON::Builder) json.object do - {% for name, type in FIELDS %} + {% for name, options in FIELDS %} + {% type = options[:type] %} %field, %value = "{{name.id}}", {{name.id}} {% if type.id == Time.id %} json.field %field, %value.try(&.to_s("%F %X")) @@ -105,7 +115,8 @@ module Granite::ORM::Fields private def cast_to_field(name, value : Type) {% unless FIELDS.empty? %} case name.to_s - {% for _name, type in FIELDS %} + {% for _name, options in FIELDS %} + {% type = options[:type] %} when "{{_name.id}}" if "{{_name.id}}" == "{{PRIMARY[:name]}}" {% if !PRIMARY[:auto] %} diff --git a/src/granite_orm/querying.cr b/src/granite_orm/querying.cr index 4e5a9cff..15e76294 100644 --- a/src/granite_orm/querying.cr +++ b/src/granite_orm/querying.cr @@ -17,7 +17,8 @@ module Granite::ORM::Querying def set_attributes(result : DB::ResultSet) # Loading from DB means existing records. @new_record = false - \{% for name, type in FIELDS %} + \{% for name, options in FIELDS %} + \{% type = options[:type] %} \{% if type.id.stringify == "Time" %} if @@adapter.class.name == "Granite::Adapter::Sqlite" # sqlite3 does not have timestamp type - timestamps are stored as str diff --git a/src/granite_orm/table.cr b/src/granite_orm/table.cr index 66ac3c66..f71d0ebb 100644 --- a/src/granite_orm/table.cr +++ b/src/granite_orm/table.cr @@ -45,13 +45,6 @@ module Granite::ORM::Table @@primary_name = "{{primary_name}}" @@primary_auto = "{{primary_auto}}" - property {{primary_name}} : Union({{primary_type.id}} | Nil) - - def {{primary_name}}! - raise {{@type.name.stringify}} + "#" + {{primary_name.stringify}} + " cannot be nil" if @{{primary_name}}.nil? - @{{primary_name}}.not_nil! - end - def self.table_name @@table_name end From 0ed1e513a460de72ddf7b8fd6f0d0086ef55a76d Mon Sep 17 00:00:00 2001 From: Tatsiujin Chin Date: Sun, 22 Apr 2018 14:22:26 +0800 Subject: [PATCH 2/2] add field spec --- spec/granite_orm/fields/field_spec.cr | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 spec/granite_orm/fields/field_spec.cr diff --git a/spec/granite_orm/fields/field_spec.cr b/spec/granite_orm/fields/field_spec.cr new file mode 100644 index 00000000..41603dd5 --- /dev/null +++ b/spec/granite_orm/fields/field_spec.cr @@ -0,0 +1,34 @@ +require "../../spec_helper" + +class Field < Granite::ORM::Base + adapter pg + + field normal : Int32 + field! raise_on_nil : Int32 +end + +describe Granite::ORM::Fields do + describe "field" do + it "generates a nilable field getter and a raise-on-nil field getter suffixed with '!'" do + field = Field.new(normal: 1) + nil_field = Field.new + + field.normal.should eq(1) + field.normal!.should eq(1) + nil_field.normal.should be_nil + expect_raises(Exception, "Field#normal cannot be nil") { nil_field.normal! } + end + end + + describe "field!" do + it "generates a raise-on-nil field getter and a nilable field getter suffixed with '?'" do + field = Field.new(raise_on_nil: 1) + nil_field = Field.new + + field.raise_on_nil.should eq(1) + field.raise_on_nil?.should eq(1) + expect_raises(Exception, "Field#raise_on_nil cannot be nil") { nil_field.raise_on_nil } + nil_field.raise_on_nil?.should be_nil + end + end +end