Skip to content

Commit

Permalink
add Granite::ORM.migrator to production codes
Browse files Browse the repository at this point in the history
  • Loading branch information
maiha committed Apr 1, 2018
1 parent de92e5d commit a152eba
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 199 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,24 @@ You can register callbacks for the following events:
- **destroy**
- after_destroy

### Migration

- `migrator` provides `drop`, `create` and `drop_and_create` methods

```crystal
class User < Granite::ORM::Base
adapter mysql
field name : String
end
User.migrator.drop_and_create
# => "DROP TABLE IF EXISTS `users`;"
# => "CREATE TABLE `users` (id BIGSERIAL PRIMARY KEY, name VARCHAR(255));"

User.migrator(table_options: "ENGINE=InnoDB DEFAULT CHARSET=utf8").create
# => "CREATE TABLE ... ENGINE=InnoDB DEFAULT CHARSET=utf8;"
```

## Contributing

1. Fork it ( https://github.com/amberframework/granite-orm/fork )
Expand Down
215 changes: 16 additions & 199 deletions spec/spec_models.cr
Original file line number Diff line number Diff line change
@@ -1,39 +1,5 @@
class Granite::ORM::Base
def self.drop_and_create
end
end

{% for adapter in GraniteExample::ADAPTERS %}
{%
adapter_literal = adapter.id

if adapter == "pg"
primary_key_sql = "BIGSERIAL PRIMARY KEY".id
foreign_key_sql = "BIGINT".id
custom_primary_key_sql = "SERIAL PRIMARY KEY".id
custom_foreign_key_sql = "INT".id
created_at_sql = "created_at TIMESTAMP".id
updated_at_sql = "updated_at TIMESTAMP".id
timestamp_fields = "timestamps".id
elsif adapter == "mysql"
primary_key_sql = "BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY".id
foreign_key_sql = "BIGINT".id
custom_primary_key_sql = "INT NOT NULL AUTO_INCREMENT PRIMARY KEY".id
custom_foreign_key_sql = "INT".id
created_at_sql = "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP".id
updated_at_sql = "updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP".id
timestamp_fields = "timestamps".id
elsif adapter == "sqlite"
primary_key_sql = "INTEGER NOT NULL PRIMARY KEY".id
foreign_key_sql = "INTEGER".id
custom_primary_key_sql = "INTEGER NOT NULL PRIMARY KEY".id
custom_foreign_key_sql = "INTEGER".id
created_at_sql = "created_at VARCHAR".id
updated_at_sql = "updated_at VARCHAR".id
timestamp_fields = "timestamps".id
end
%}

{% adapter_literal = adapter.id %}
require "../src/adapter/{{ adapter_literal }}"

module {{adapter.capitalize.id}}
Expand All @@ -43,24 +9,13 @@ end
table_name parents

field name : String
{{ timestamp_fields }}
timestamps

has_many :students

validate :name, "Name cannot be blank" do |parent|
!parent.name.to_s.blank?
end

def self.drop_and_create
exec("DROP TABLE IF EXISTS #{ quoted_table_name };")
exec("CREATE TABLE #{ quoted_table_name } (
id {{ primary_key_sql }},
name VARCHAR(100),
{{ created_at_sql }},
{{ updated_at_sql }}
);
")
end
end

class Teacher < Granite::ORM::Base
Expand All @@ -71,15 +26,6 @@ end
field name : String

has_many :klasss

def self.drop_and_create
exec("DROP TABLE IF EXISTS #{ quoted_table_name };")
exec("CREATE TABLE #{ quoted_table_name } (
id {{ primary_key_sql }},
name VARCHAR(100)
);
")
end
end

class Student < Granite::ORM::Base
Expand All @@ -91,16 +37,6 @@ end

has_many :enrollments
has_many :klasss, through: :enrollments

def self.drop_and_create
exec("DROP TABLE IF EXISTS #{ quoted_table_name };")
exec("CREATE TABLE #{ quoted_table_name } (
id {{ primary_key_sql }},
name VARCHAR(100),
parent_id {{ foreign_key_sql }}
);
")
end
end

class Klass < Granite::ORM::Base
Expand All @@ -113,17 +49,6 @@ end

has_many :enrollments
has_many :students, through: :enrollments

def self.drop_and_create
exec "DROP TABLE IF EXISTS #{ quoted_table_name }"
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
id {{ primary_key_sql }},
name VARCHAR(255),
teacher_id {{ foreign_key_sql }}
)
SQL
end
end

class Enrollment < Granite::ORM::Base
Expand All @@ -133,17 +58,6 @@ end

belongs_to :student
belongs_to :klass

def self.drop_and_create
exec "DROP TABLE IF EXISTS #{ quoted_table_name }"
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
id {{ primary_key_sql }},
student_id {{ foreign_key_sql }},
klass_id {{ foreign_key_sql }}
)
SQL
end
end

class School < Granite::ORM::Base
Expand All @@ -152,16 +66,6 @@ end
field name : String

table_name schools

def self.drop_and_create
exec "DROP TABLE IF EXISTS #{ quoted_table_name }"
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
custom_id {{ primary_key_sql }},
name VARCHAR(255)
)
SQL
end
end

class Nation::County < Granite::ORM::Base
Expand All @@ -170,16 +74,6 @@ end
table_name nation_countys

field name : String

def self.drop_and_create
exec "DROP TABLE IF EXISTS #{ quoted_table_name }"
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
id {{ primary_key_sql }},
name VARCHAR(255)
)
SQL
end
end

class Review < Granite::ORM::Base
Expand All @@ -192,54 +86,18 @@ end
field interest : Float64
field published : Bool
field created_at : Time

def self.drop_and_create
exec "DROP TABLE IF EXISTS #{ quoted_table_name }"
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
id {{ primary_key_sql }},
name VARCHAR(255),
downvotes INT,
upvotes BIGINT,
sentiment FLOAT,
interest REAL,
published BOOL,
{{ created_at_sql }},
{{ updated_at_sql }}
)
SQL
end
end

class Empty < Granite::ORM::Base
adapter {{ adapter_literal }}
table_name emptys
primary id : Int64

def self.drop_and_create
exec "DROP TABLE IF EXISTS #{ quoted_table_name }"
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
id {{ primary_key_sql }}
)
SQL
end
end

class ReservedWord < Granite::ORM::Base
adapter {{ adapter_literal }}
table_name "select"
field all : String

def self.drop_and_create
exec "DROP TABLE IF EXISTS #{ quoted_table_name }"
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
id {{ primary_key_sql }},
#{quote("all")} VARCHAR(255)
)
SQL
end
end

class Callback < Granite::ORM::Base
Expand All @@ -256,33 +114,13 @@ end
history << "{{name.id}}\n"
end
{% end %}

def self.drop_and_create
exec "DROP TABLE IF EXISTS #{ quoted_table_name }"
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
id {{ primary_key_sql }},
name VARCHAR(100) NOT NULL
)
SQL
end
end

class Kvs < Granite::ORM::Base
adapter {{ adapter_literal }}
table_name kvss
primary k : String, auto: false
field v : String

def self.drop_and_create
exec "DROP TABLE IF EXISTS #{ quoted_table_name }"
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
k VARCHAR(255),
v VARCHAR(255)
)
SQL
end
end

class Book < Granite::ORM::Base
Expand All @@ -292,16 +130,6 @@ end

primary id : Int32
field name : String

def self.drop_and_create
exec("DROP TABLE IF EXISTS #{ quoted_table_name };")
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
id {{ custom_primary_key_sql }},
name VARCHAR(255)
)
SQL
end
end

class BookReview < Granite::ORM::Base
Expand All @@ -311,32 +139,21 @@ end

primary id : Int32
field body : String

def self.drop_and_create
exec("DROP TABLE IF EXISTS #{ quoted_table_name };")
exec <<-SQL
CREATE TABLE #{ quoted_table_name } (
id {{ custom_primary_key_sql }},
book_id {{ custom_foreign_key_sql }},
body VARCHAR(255)
)
SQL
end
end

Parent.drop_and_create
Teacher.drop_and_create
Student.drop_and_create
Klass.drop_and_create
Enrollment.drop_and_create
School.drop_and_create
Nation::County.drop_and_create
Review.drop_and_create
Empty.drop_and_create
ReservedWord.drop_and_create
Callback.drop_and_create
Kvs.drop_and_create
Book.drop_and_create
BookReview.drop_and_create
Parent.migrator.drop_and_create
Teacher.migrator.drop_and_create
Student.migrator.drop_and_create
Klass.migrator.drop_and_create
Enrollment.migrator.drop_and_create
School.migrator.drop_and_create
Nation::County.migrator.drop_and_create
Review.migrator.drop_and_create
Empty.migrator.drop_and_create
ReservedWord.migrator.drop_and_create
Callback.migrator.drop_and_create
Kvs.migrator.drop_and_create
Book.migrator.drop_and_create
BookReview.migrator.drop_and_create
end
{% end %}
17 changes: 17 additions & 0 deletions src/adapter/base.cr
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,30 @@ abstract class Granite::Adapter::Base
Granite::Adapter::Base.env(url)
end

module Schema
TYPES = {
"Bool" => "BOOL",
"Float32" => "FLOAT",
"Float64" => "REAL",
"Int32" => "INT",
"Int64" => "BIGINT",
"String" => "VARCHAR(255)",
"Time" => "TIMESTAMP",
}
end

# Use macro in order to read a constant defined in each subclasses.
macro inherited
# quotes table and column names
def quote(name : String) : String
char = QUOTING_CHAR
char + name.gsub(char, "#{char}#{char}") + char
end

# converts the crystal class to database type of this adapter
def self.schema_type?(key : String)
Schema::TYPES[key]? || Granite::Adapter::Base::Schema::TYPES[key]?
end
end

# class level method so we can test it
Expand Down
9 changes: 9 additions & 0 deletions src/adapter/mysql.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ require "mysql"
class Granite::Adapter::Mysql < Granite::Adapter::Base
QUOTING_CHAR = '`'

module Schema
TYPES = {
"AUTO_Int32" => "INT NOT NULL AUTO_INCREMENT PRIMARY KEY",
"AUTO_Int64" => "BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY",
"created_at" => "TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
"updated_at" => "TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
}
end

# Using TRUNCATE instead of DELETE so the id column resets to 0
def clear(table_name)
statement = "TRUNCATE #{quote(table_name)}"
Expand Down
Loading

0 comments on commit a152eba

Please sign in to comment.