tina4ruby 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +80 -0
- data/LICENSE.txt +21 -0
- data/README.md +768 -0
- data/exe/tina4 +4 -0
- data/lib/tina4/api.rb +152 -0
- data/lib/tina4/auth.rb +139 -0
- data/lib/tina4/cli.rb +349 -0
- data/lib/tina4/crud.rb +124 -0
- data/lib/tina4/database.rb +135 -0
- data/lib/tina4/database_result.rb +89 -0
- data/lib/tina4/debug.rb +83 -0
- data/lib/tina4/dev.rb +15 -0
- data/lib/tina4/dev_reload.rb +68 -0
- data/lib/tina4/drivers/firebird_driver.rb +94 -0
- data/lib/tina4/drivers/mssql_driver.rb +112 -0
- data/lib/tina4/drivers/mysql_driver.rb +90 -0
- data/lib/tina4/drivers/postgres_driver.rb +99 -0
- data/lib/tina4/drivers/sqlite_driver.rb +85 -0
- data/lib/tina4/env.rb +55 -0
- data/lib/tina4/field_types.rb +84 -0
- data/lib/tina4/graphql.rb +837 -0
- data/lib/tina4/localization.rb +100 -0
- data/lib/tina4/middleware.rb +59 -0
- data/lib/tina4/migration.rb +124 -0
- data/lib/tina4/orm.rb +168 -0
- data/lib/tina4/public/css/tina4.css +2286 -0
- data/lib/tina4/public/css/tina4.min.css +2 -0
- data/lib/tina4/public/js/tina4.js +134 -0
- data/lib/tina4/public/js/tina4helper.js +387 -0
- data/lib/tina4/queue.rb +117 -0
- data/lib/tina4/queue_backends/kafka_backend.rb +80 -0
- data/lib/tina4/queue_backends/lite_backend.rb +79 -0
- data/lib/tina4/queue_backends/rabbitmq_backend.rb +73 -0
- data/lib/tina4/rack_app.rb +150 -0
- data/lib/tina4/request.rb +158 -0
- data/lib/tina4/response.rb +172 -0
- data/lib/tina4/router.rb +148 -0
- data/lib/tina4/scss/tina4css/_alerts.scss +34 -0
- data/lib/tina4/scss/tina4css/_badges.scss +22 -0
- data/lib/tina4/scss/tina4css/_buttons.scss +69 -0
- data/lib/tina4/scss/tina4css/_cards.scss +49 -0
- data/lib/tina4/scss/tina4css/_forms.scss +156 -0
- data/lib/tina4/scss/tina4css/_grid.scss +81 -0
- data/lib/tina4/scss/tina4css/_modals.scss +84 -0
- data/lib/tina4/scss/tina4css/_nav.scss +149 -0
- data/lib/tina4/scss/tina4css/_reset.scss +94 -0
- data/lib/tina4/scss/tina4css/_tables.scss +54 -0
- data/lib/tina4/scss/tina4css/_typography.scss +55 -0
- data/lib/tina4/scss/tina4css/_utilities.scss +197 -0
- data/lib/tina4/scss/tina4css/_variables.scss +117 -0
- data/lib/tina4/scss/tina4css/base.scss +1 -0
- data/lib/tina4/scss/tina4css/colors.scss +48 -0
- data/lib/tina4/scss/tina4css/tina4.scss +17 -0
- data/lib/tina4/scss_compiler.rb +131 -0
- data/lib/tina4/seeder.rb +529 -0
- data/lib/tina4/session.rb +145 -0
- data/lib/tina4/session_handlers/file_handler.rb +55 -0
- data/lib/tina4/session_handlers/mongo_handler.rb +49 -0
- data/lib/tina4/session_handlers/redis_handler.rb +43 -0
- data/lib/tina4/swagger.rb +123 -0
- data/lib/tina4/template.rb +478 -0
- data/lib/tina4/templates/base.twig +26 -0
- data/lib/tina4/templates/errors/403.twig +22 -0
- data/lib/tina4/templates/errors/404.twig +22 -0
- data/lib/tina4/templates/errors/500.twig +22 -0
- data/lib/tina4/testing.rb +213 -0
- data/lib/tina4/version.rb +5 -0
- data/lib/tina4/webserver.rb +101 -0
- data/lib/tina4/websocket.rb +167 -0
- data/lib/tina4/wsdl.rb +164 -0
- data/lib/tina4.rb +259 -0
- data/lib/tina4ruby.rb +4 -0
- metadata +324 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tina4
|
|
4
|
+
module Drivers
|
|
5
|
+
class MssqlDriver
|
|
6
|
+
attr_reader :connection
|
|
7
|
+
|
|
8
|
+
def connect(connection_string)
|
|
9
|
+
require "tiny_tds"
|
|
10
|
+
uri = parse_connection(connection_string)
|
|
11
|
+
@connection = TinyTds::Client.new(
|
|
12
|
+
host: uri[:host],
|
|
13
|
+
port: uri[:port] || 1433,
|
|
14
|
+
username: uri[:username],
|
|
15
|
+
password: uri[:password],
|
|
16
|
+
database: uri[:database]
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def close
|
|
21
|
+
@connection&.close
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def execute_query(sql, params = [])
|
|
25
|
+
effective_sql = interpolate_params(sql, params)
|
|
26
|
+
result = @connection.execute(effective_sql)
|
|
27
|
+
rows = result.each(symbolize_keys: true).to_a
|
|
28
|
+
result.cancel if result.respond_to?(:cancel)
|
|
29
|
+
rows
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def execute(sql, params = [])
|
|
33
|
+
effective_sql = interpolate_params(sql, params)
|
|
34
|
+
result = @connection.execute(effective_sql)
|
|
35
|
+
result.do
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def last_insert_id
|
|
39
|
+
result = @connection.execute("SELECT SCOPE_IDENTITY() AS id")
|
|
40
|
+
row = result.first
|
|
41
|
+
result.cancel if result.respond_to?(:cancel)
|
|
42
|
+
row[:id]&.to_i
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def placeholder
|
|
46
|
+
"?"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def placeholders(count)
|
|
50
|
+
(["?"] * count).join(", ")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def apply_limit(sql, limit, offset = 0)
|
|
54
|
+
"#{sql} OFFSET #{offset} ROWS FETCH NEXT #{limit} ROWS ONLY"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def begin_transaction
|
|
58
|
+
@connection.execute("BEGIN TRANSACTION").do
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def commit
|
|
62
|
+
@connection.execute("COMMIT").do
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def rollback
|
|
66
|
+
@connection.execute("ROLLBACK").do
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def tables
|
|
70
|
+
rows = execute_query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'")
|
|
71
|
+
rows.map { |r| r[:TABLE_NAME] || r[:table_name] }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def columns(table_name)
|
|
75
|
+
sql = "SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ?"
|
|
76
|
+
rows = execute_query(sql, [table_name])
|
|
77
|
+
rows.map do |r|
|
|
78
|
+
{
|
|
79
|
+
name: r[:COLUMN_NAME] || r[:column_name],
|
|
80
|
+
type: r[:DATA_TYPE] || r[:data_type],
|
|
81
|
+
nullable: (r[:IS_NULLABLE] || r[:is_nullable]) == "YES",
|
|
82
|
+
default: r[:COLUMN_DEFAULT] || r[:column_default],
|
|
83
|
+
primary_key: false
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def parse_connection(str)
|
|
91
|
+
# Format: mssql://user:pass@host:port/database
|
|
92
|
+
match = str.match(%r{(?:mssql|sqlserver)://(?:(\w+):([^@]+)@)?([^:/]+)(?::(\d+))?/(.+)})
|
|
93
|
+
if match
|
|
94
|
+
{ username: match[1], password: match[2], host: match[3],
|
|
95
|
+
port: match[4]&.to_i, database: match[5] }
|
|
96
|
+
else
|
|
97
|
+
{ host: "localhost", database: str }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def interpolate_params(sql, params)
|
|
102
|
+
return sql if params.empty?
|
|
103
|
+
result = sql.dup
|
|
104
|
+
params.each do |param|
|
|
105
|
+
escaped = param.is_a?(String) ? "'#{param.gsub("'", "''")}'" : param.to_s
|
|
106
|
+
result = result.sub("?", escaped)
|
|
107
|
+
end
|
|
108
|
+
result
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tina4
|
|
4
|
+
module Drivers
|
|
5
|
+
class MysqlDriver
|
|
6
|
+
attr_reader :connection
|
|
7
|
+
|
|
8
|
+
def connect(connection_string)
|
|
9
|
+
require "mysql2"
|
|
10
|
+
uri = URI.parse(connection_string.sub(/^mysql:\/\//, "mysql2://"))
|
|
11
|
+
@connection = Mysql2::Client.new(
|
|
12
|
+
host: uri.host || "localhost",
|
|
13
|
+
port: uri.port || 3306,
|
|
14
|
+
username: uri.user,
|
|
15
|
+
password: uri.password,
|
|
16
|
+
database: uri.path&.sub("/", "")
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def close
|
|
21
|
+
@connection&.close
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def execute_query(sql, params = [])
|
|
25
|
+
if params.empty?
|
|
26
|
+
results = @connection.query(sql, symbolize_keys: true)
|
|
27
|
+
else
|
|
28
|
+
stmt = @connection.prepare(sql)
|
|
29
|
+
results = stmt.execute(*params, symbolize_keys: true)
|
|
30
|
+
end
|
|
31
|
+
results.to_a
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def execute(sql, params = [])
|
|
35
|
+
if params.empty?
|
|
36
|
+
@connection.query(sql)
|
|
37
|
+
else
|
|
38
|
+
stmt = @connection.prepare(sql)
|
|
39
|
+
stmt.execute(*params)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def last_insert_id
|
|
44
|
+
@connection.last_id
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def placeholder
|
|
48
|
+
"?"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def placeholders(count)
|
|
52
|
+
(["?"] * count).join(", ")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def apply_limit(sql, limit, offset = 0)
|
|
56
|
+
"#{sql} LIMIT #{limit} OFFSET #{offset}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def begin_transaction
|
|
60
|
+
@connection.query("START TRANSACTION")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def commit
|
|
64
|
+
@connection.query("COMMIT")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def rollback
|
|
68
|
+
@connection.query("ROLLBACK")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def tables
|
|
72
|
+
rows = execute_query("SHOW TABLES")
|
|
73
|
+
rows.map { |r| r.values.first }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def columns(table_name)
|
|
77
|
+
rows = execute_query("DESCRIBE #{table_name}")
|
|
78
|
+
rows.map do |r|
|
|
79
|
+
{
|
|
80
|
+
name: r[:Field],
|
|
81
|
+
type: r[:Type],
|
|
82
|
+
nullable: r[:Null] == "YES",
|
|
83
|
+
default: r[:Default],
|
|
84
|
+
primary_key: r[:Key] == "PRI"
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tina4
|
|
4
|
+
module Drivers
|
|
5
|
+
class PostgresDriver
|
|
6
|
+
attr_reader :connection
|
|
7
|
+
|
|
8
|
+
def connect(connection_string)
|
|
9
|
+
require "pg"
|
|
10
|
+
@connection = PG.connect(connection_string)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def close
|
|
14
|
+
@connection&.close
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def execute_query(sql, params = [])
|
|
18
|
+
converted_sql = convert_placeholders(sql)
|
|
19
|
+
result = if params.empty?
|
|
20
|
+
@connection.exec(converted_sql)
|
|
21
|
+
else
|
|
22
|
+
@connection.exec_params(converted_sql, params)
|
|
23
|
+
end
|
|
24
|
+
result.map { |row| symbolize_keys(row) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def execute(sql, params = [])
|
|
28
|
+
converted_sql = convert_placeholders(sql)
|
|
29
|
+
if params.empty?
|
|
30
|
+
@connection.exec(converted_sql)
|
|
31
|
+
else
|
|
32
|
+
@connection.exec_params(converted_sql, params)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def last_insert_id
|
|
37
|
+
result = @connection.exec("SELECT lastval()")
|
|
38
|
+
result.first["lastval"].to_i
|
|
39
|
+
rescue PG::Error
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def placeholder
|
|
44
|
+
"?"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def placeholders(count)
|
|
48
|
+
(1..count).map { |i| "$#{i}" }.join(", ")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def apply_limit(sql, limit, offset = 0)
|
|
52
|
+
"#{sql} LIMIT #{limit} OFFSET #{offset}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def begin_transaction
|
|
56
|
+
@connection.exec("BEGIN")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def commit
|
|
60
|
+
@connection.exec("COMMIT")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def rollback
|
|
64
|
+
@connection.exec("ROLLBACK")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def tables
|
|
68
|
+
sql = "SELECT tablename FROM pg_tables WHERE schemaname = 'public'"
|
|
69
|
+
rows = execute_query(sql)
|
|
70
|
+
rows.map { |r| r[:tablename] }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def columns(table_name)
|
|
74
|
+
sql = "SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = $1"
|
|
75
|
+
rows = execute_query(sql, [table_name])
|
|
76
|
+
rows.map do |r|
|
|
77
|
+
{
|
|
78
|
+
name: r[:column_name],
|
|
79
|
+
type: r[:data_type],
|
|
80
|
+
nullable: r[:is_nullable] == "YES",
|
|
81
|
+
default: r[:column_default],
|
|
82
|
+
primary_key: false
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def convert_placeholders(sql)
|
|
90
|
+
counter = 0
|
|
91
|
+
sql.gsub("?") { counter += 1; "$#{counter}" }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def symbolize_keys(hash)
|
|
95
|
+
hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tina4
|
|
4
|
+
module Drivers
|
|
5
|
+
class SqliteDriver
|
|
6
|
+
attr_reader :connection
|
|
7
|
+
|
|
8
|
+
def connect(connection_string)
|
|
9
|
+
require "sqlite3"
|
|
10
|
+
db_path = connection_string.sub(/^sqlite:\/\//, "").sub(/^sqlite:/, "")
|
|
11
|
+
@connection = SQLite3::Database.new(db_path)
|
|
12
|
+
@connection.results_as_hash = true
|
|
13
|
+
@connection.execute("PRAGMA journal_mode=WAL")
|
|
14
|
+
@connection.execute("PRAGMA foreign_keys=ON")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def close
|
|
18
|
+
@connection&.close
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def execute_query(sql, params = [])
|
|
22
|
+
results = @connection.execute(sql, params)
|
|
23
|
+
results.map { |row| symbolize_keys(row) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def execute(sql, params = [])
|
|
27
|
+
@connection.execute(sql, params)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def last_insert_id
|
|
31
|
+
@connection.last_insert_row_id
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def placeholder
|
|
35
|
+
"?"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def placeholders(count)
|
|
39
|
+
(["?"] * count).join(", ")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def apply_limit(sql, limit, offset = 0)
|
|
43
|
+
"#{sql} LIMIT #{limit} OFFSET #{offset}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def begin_transaction
|
|
47
|
+
@connection.execute("BEGIN TRANSACTION")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def commit
|
|
51
|
+
@connection.execute("COMMIT")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def rollback
|
|
55
|
+
@connection.execute("ROLLBACK")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def tables
|
|
59
|
+
rows = execute_query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
|
|
60
|
+
rows.map { |r| r[:name] }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def columns(table_name)
|
|
64
|
+
rows = execute_query("PRAGMA table_info(#{table_name})")
|
|
65
|
+
rows.map do |r|
|
|
66
|
+
{
|
|
67
|
+
name: r[:name],
|
|
68
|
+
type: r[:type],
|
|
69
|
+
nullable: r[:notnull] == 0,
|
|
70
|
+
default: r[:dflt_value],
|
|
71
|
+
primary_key: r[:pk] == 1
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def symbolize_keys(hash)
|
|
79
|
+
hash.each_with_object({}) do |(k, v), h|
|
|
80
|
+
h[k.to_s.to_sym] = v if k.is_a?(String) || k.is_a?(Symbol)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/lib/tina4/env.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "digest"
|
|
3
|
+
|
|
4
|
+
module Tina4
|
|
5
|
+
module Env
|
|
6
|
+
DEFAULT_ENV = {
|
|
7
|
+
"PROJECT_NAME" => "Tina4 Ruby Project",
|
|
8
|
+
"VERSION" => "1.0.0",
|
|
9
|
+
"TINA4_LANGUAGE" => "en",
|
|
10
|
+
"TINA4_DEBUG_LEVEL" => "[TINA4_LOG_ALL]",
|
|
11
|
+
"SECRET" => "tina4-secret-change-me"
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
def load(root_dir = Dir.pwd)
|
|
16
|
+
env_file = resolve_env_file(root_dir)
|
|
17
|
+
unless File.exist?(env_file)
|
|
18
|
+
create_default_env(env_file)
|
|
19
|
+
end
|
|
20
|
+
parse_env_file(env_file)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def resolve_env_file(root_dir)
|
|
26
|
+
environment = ENV["ENVIRONMENT"]
|
|
27
|
+
if environment && !environment.empty?
|
|
28
|
+
candidate = File.join(root_dir, ".env.#{environment}")
|
|
29
|
+
return candidate if File.exist?(candidate)
|
|
30
|
+
end
|
|
31
|
+
File.join(root_dir, ".env")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def create_default_env(path)
|
|
35
|
+
api_key = Digest::MD5.hexdigest(Time.now.to_s)
|
|
36
|
+
content = DEFAULT_ENV.map { |k, v| "#{k}=\"#{v}\"" }.join("\n")
|
|
37
|
+
content += "\nAPI_KEY=\"#{api_key}\"\n"
|
|
38
|
+
File.write(path, content)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def parse_env_file(path)
|
|
42
|
+
return unless File.exist?(path)
|
|
43
|
+
File.readlines(path).each do |line|
|
|
44
|
+
line = line.strip
|
|
45
|
+
next if line.empty? || line.start_with?("#")
|
|
46
|
+
if (match = line.match(/\A([A-Za-z_][A-Za-z0-9_]*)=["']?(.*)["']?\z/))
|
|
47
|
+
key = match[1]
|
|
48
|
+
value = match[2].gsub(/["']\z/, "")
|
|
49
|
+
ENV[key] ||= value
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tina4
|
|
4
|
+
module FieldTypes
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.extend(ClassMethods)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def field_definitions
|
|
11
|
+
@field_definitions ||= {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def primary_key_field
|
|
15
|
+
@primary_key_field
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def table_name(name = nil)
|
|
19
|
+
if name
|
|
20
|
+
@table_name = name
|
|
21
|
+
else
|
|
22
|
+
@table_name || self.name.split("::").last.downcase + "s"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def integer_field(name, primary_key: false, auto_increment: false, nullable: true, default: nil)
|
|
27
|
+
register_field(name, :integer, primary_key: primary_key, auto_increment: auto_increment,
|
|
28
|
+
nullable: nullable, default: default)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def string_field(name, length: 255, primary_key: false, nullable: true, default: nil)
|
|
32
|
+
register_field(name, :string, length: length, primary_key: primary_key,
|
|
33
|
+
nullable: nullable, default: default)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def text_field(name, nullable: true, default: nil)
|
|
37
|
+
register_field(name, :text, nullable: nullable, default: default)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def float_field(name, nullable: true, default: nil)
|
|
41
|
+
register_field(name, :float, nullable: nullable, default: default)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def decimal_field(name, precision: 10, scale: 2, nullable: true, default: nil)
|
|
45
|
+
register_field(name, :decimal, precision: precision, scale: scale,
|
|
46
|
+
nullable: nullable, default: default)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def boolean_field(name, nullable: true, default: nil)
|
|
50
|
+
register_field(name, :boolean, nullable: nullable, default: default)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def date_field(name, nullable: true, default: nil)
|
|
54
|
+
register_field(name, :date, nullable: nullable, default: default)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def datetime_field(name, nullable: true, default: nil)
|
|
58
|
+
register_field(name, :datetime, nullable: nullable, default: default)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def timestamp_field(name, nullable: true, default: nil)
|
|
62
|
+
register_field(name, :timestamp, nullable: nullable, default: default)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def blob_field(name, nullable: true, default: nil)
|
|
66
|
+
register_field(name, :blob, nullable: nullable, default: default)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def json_field(name, nullable: true, default: nil)
|
|
70
|
+
register_field(name, :json, nullable: nullable, default: default)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def register_field(name, type, **options)
|
|
76
|
+
field_definitions[name] = { type: type }.merge(options)
|
|
77
|
+
@primary_key_field = name if options[:primary_key]
|
|
78
|
+
|
|
79
|
+
# Define getter/setter
|
|
80
|
+
attr_accessor name
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|