From c0aa376dfa3fe59121e33efc10b282f165b8f5de Mon Sep 17 00:00:00 2001 From: Matthew McGarvey Date: Fri, 4 Dec 2020 15:52:02 -0600 Subject: [PATCH] Add support for database views --- .../20201202225520_create_admin_users_view.cr | 14 +++++++++++ ...201202231134_create_nickname_infos_view.cr | 14 +++++++++++ spec/support/models/admin_user.cr | 16 +++++++++++++ spec/support/models/nickname_info.cr | 6 +++++ spec/view_spec.cr | 23 +++++++++++++++++++ src/avram/model.cr | 18 +++++++++++++++ 6 files changed, 91 insertions(+) create mode 100644 db/migrations/20201202225520_create_admin_users_view.cr create mode 100644 db/migrations/20201202231134_create_nickname_infos_view.cr create mode 100644 spec/support/models/admin_user.cr create mode 100644 spec/support/models/nickname_info.cr create mode 100644 spec/view_spec.cr diff --git a/db/migrations/20201202225520_create_admin_users_view.cr b/db/migrations/20201202225520_create_admin_users_view.cr new file mode 100644 index 000000000..a3d35a979 --- /dev/null +++ b/db/migrations/20201202225520_create_admin_users_view.cr @@ -0,0 +1,14 @@ +class CreateAdminUsersView::V20201202225520 < Avram::Migrator::Migration::V1 + def migrate + execute <<-SQL + CREATE VIEW admin_users AS + SELECT users.* + FROM users + JOIN admins on admins.name = users.name; + SQL + end + + def rollback + execute "DROP VIEW admin_users;" + end +end diff --git a/db/migrations/20201202231134_create_nickname_infos_view.cr b/db/migrations/20201202231134_create_nickname_infos_view.cr new file mode 100644 index 000000000..5cb2af120 --- /dev/null +++ b/db/migrations/20201202231134_create_nickname_infos_view.cr @@ -0,0 +1,14 @@ +class CreateNicknameInfosView::V20201202231134 < Avram::Migrator::Migration::V1 + def migrate + execute <<-SQL + CREATE VIEW nickname_infos AS + SELECT users.nickname, COUNT(nickname) + FROM users + GROUP BY nickname; + SQL + end + + def rollback + execute "DROP VIEW nickname_infos;" + end +end diff --git a/spec/support/models/admin_user.cr b/spec/support/models/admin_user.cr new file mode 100644 index 000000000..1a70852de --- /dev/null +++ b/spec/support/models/admin_user.cr @@ -0,0 +1,16 @@ +# an AdminUser is a User who's name is also found in the Admin table +class AdminUser < BaseModel + view do + primary_key id : Int64 + timestamps + column name : String + column age : Int32 + column year_born : Int16? + column nickname : String? + column joined_at : Time + column total_score : Int64? + column average_score : Float64? + column available_for_hire : Bool? + has_one sign_in_credential : SignInCredential? + end +end diff --git a/spec/support/models/nickname_info.cr b/spec/support/models/nickname_info.cr new file mode 100644 index 000000000..1918b8411 --- /dev/null +++ b/spec/support/models/nickname_info.cr @@ -0,0 +1,6 @@ +class NicknameInfo < BaseModel + view do + column nickname : String + column count : Int64 + end +end diff --git a/spec/view_spec.cr b/spec/view_spec.cr new file mode 100644 index 000000000..3ece6842e --- /dev/null +++ b/spec/view_spec.cr @@ -0,0 +1,23 @@ +require "./spec_helper" + +include LazyLoadHelpers + +describe "views" do + it "works with a primary key" do + user = UserBox.create + admin = AdminBox.new.name(user.name).create + admin_user = AdminUser::BaseQuery.find(user.id) + + admin_user.name.should eq user.name + end + + it "works without a primary key" do + UserBox.new.nickname("Johnny").create + UserBox.new.nickname("Johnny").create + UserBox.new.nickname("Johnny").create + nickname_info = NicknameInfo::BaseQuery.first + + nickname_info.nickname.should eq "Johnny" + nickname_info.count.should eq 3 + end +end diff --git a/src/avram/model.cr b/src/avram/model.cr index 1e79d53e0..2f89a7c4e 100644 --- a/src/avram/model.cr +++ b/src/avram/model.cr @@ -54,6 +54,24 @@ abstract class Avram::Model setup(Avram::SchemaEnforcer.setup) end + macro view(view_name = nil) + {% unless view_name %} + {% view_name = run("../run_macros/infer_table_name.cr", @type.id) %} + {% end %} + + {{ yield }} + + class_getter table_name = {{ view_name.id.symbolize }} + TABLE_NAME = {{ view_name.id.symbolize }} + 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) + setup(Avram::BaseQueryTemplate.setup) + setup(Avram::SchemaEnforcer.setup) + end + macro primary_key(type_declaration) PRIMARY_KEY_TYPE = {{ type_declaration.type }} PRIMARY_KEY_NAME = {{ type_declaration.var.symbolize }}