mystic 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mystic.rb +92 -99
- data/lib/mystic/extensions.rb +11 -5
- data/lib/mystic/migration.rb +36 -46
- data/lib/mystic/model.rb +104 -98
- data/lib/mystic/postgres.rb +79 -0
- data/lib/mystic/sql.rb +5 -239
- data/lib/mystic/sql/column.rb +37 -0
- data/lib/mystic/sql/index.rb +78 -0
- data/lib/mystic/sql/table.rb +122 -0
- metadata +11 -12
- data/lib/mystic/adapter.rb +0 -145
- data/lib/mystic/adapters/abstract.rb +0 -90
- data/lib/mystic/adapters/mysql.rb +0 -51
- data/lib/mystic/adapters/postgres.rb +0 -95
- data/lib/mystic/constants.rb +0 -11
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mystic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathaniel Symer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: densify
|
@@ -38,9 +38,9 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
description:
|
42
|
-
|
43
|
-
email: nate@
|
41
|
+
description: Pooled PG singleton instance. Focus more on writing SQL than dealing
|
42
|
+
with a finnicky ORM. It powers the Pawky backend.
|
43
|
+
email: nate@natesymer.com
|
44
44
|
executables:
|
45
45
|
- mystic
|
46
46
|
extensions: []
|
@@ -49,15 +49,14 @@ files:
|
|
49
49
|
- LICENSE
|
50
50
|
- bin/mystic
|
51
51
|
- lib/mystic.rb
|
52
|
-
- lib/mystic/adapter.rb
|
53
|
-
- lib/mystic/adapters/abstract.rb
|
54
|
-
- lib/mystic/adapters/mysql.rb
|
55
|
-
- lib/mystic/adapters/postgres.rb
|
56
|
-
- lib/mystic/constants.rb
|
57
52
|
- lib/mystic/extensions.rb
|
58
53
|
- lib/mystic/migration.rb
|
59
54
|
- lib/mystic/model.rb
|
55
|
+
- lib/mystic/postgres.rb
|
60
56
|
- lib/mystic/sql.rb
|
57
|
+
- lib/mystic/sql/column.rb
|
58
|
+
- lib/mystic/sql/index.rb
|
59
|
+
- lib/mystic/sql/table.rb
|
61
60
|
homepage: https://github.com/ivytap/mystic
|
62
61
|
licenses:
|
63
62
|
- MIT
|
@@ -81,6 +80,6 @@ rubyforge_project:
|
|
81
80
|
rubygems_version: 2.2.2
|
82
81
|
signing_key:
|
83
82
|
specification_version: 4
|
84
|
-
summary:
|
85
|
-
|
83
|
+
summary: Pooled PG singleton instance. Focus more on writing SQL than dealing with
|
84
|
+
a finnicky ORM.
|
86
85
|
test_files: []
|
data/lib/mystic/adapter.rb
DELETED
@@ -1,145 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "densify"
|
4
|
-
require "access_stack"
|
5
|
-
|
6
|
-
module Mystic
|
7
|
-
class Adapter
|
8
|
-
attr_accessor :pool_size,
|
9
|
-
:pool_timeout,
|
10
|
-
:pool_expires
|
11
|
-
|
12
|
-
@@blocks = {}
|
13
|
-
|
14
|
-
def self.create(name="",opts={})
|
15
|
-
name = name.to_s.downcase.strip
|
16
|
-
name = "postgres" if name =~ /^postg.*$/i # Includes PostGIS
|
17
|
-
name = "mysql" if name =~ /^mysql.*$/i
|
18
|
-
|
19
|
-
require "mystic/adapters/" + name
|
20
|
-
|
21
|
-
Object.const_get("Mystic::#{name.capitalize}Adapter").new opts
|
22
|
-
end
|
23
|
-
|
24
|
-
def initialize(opts={})
|
25
|
-
opts.symbolize.each do |k,v|
|
26
|
-
k = ('@' + k.to_s).to_sym
|
27
|
-
instance_variable_set k,v if instance_variables.include? k
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Gets the adapter name (examples: postgres, mysql)
|
32
|
-
def self.adapter
|
33
|
-
a = name.split("::").last.sub("Adapter","").downcase
|
34
|
-
a = "abstract" if a.empty?
|
35
|
-
a
|
36
|
-
end
|
37
|
-
|
38
|
-
def adapter
|
39
|
-
self.class.adapter
|
40
|
-
end
|
41
|
-
|
42
|
-
# Map a block to an adapter's block hash
|
43
|
-
# This avoids cases where a subclass' class var
|
44
|
-
# changes that class var on its superclass
|
45
|
-
def self.map_block(key, block)
|
46
|
-
@@blocks[adapter] ||= {}
|
47
|
-
@@blocks[adapter][key] = block
|
48
|
-
end
|
49
|
-
|
50
|
-
# Fetch a block for the current adapter
|
51
|
-
def block_for(key)
|
52
|
-
b = @@blocks[adapter][key] rescue nil
|
53
|
-
b ||= @@blocks["abstract"][key] rescue nil
|
54
|
-
b ||= lambda { "" }
|
55
|
-
b
|
56
|
-
end
|
57
|
-
|
58
|
-
#
|
59
|
-
# Adapter DSL
|
60
|
-
# Implemented using method_missing
|
61
|
-
#
|
62
|
-
|
63
|
-
# example
|
64
|
-
# execute do |inst,sql|
|
65
|
-
# inst.exec sql
|
66
|
-
# end
|
67
|
-
|
68
|
-
# execute(inst,sql)
|
69
|
-
# Executes SQL and returns Ruby types
|
70
|
-
|
71
|
-
# TODO: different kinds of escaping: ident & quote
|
72
|
-
|
73
|
-
# sanitize(inst,sql)
|
74
|
-
# Escapes a literal
|
75
|
-
|
76
|
-
# connect(opts)
|
77
|
-
# Creates an instance of the DB connection
|
78
|
-
|
79
|
-
# disconnect(inst)
|
80
|
-
# Disconnects and destroys inst (a database connection)
|
81
|
-
|
82
|
-
# validate(inst)
|
83
|
-
# Checks if inst is a valid connection
|
84
|
-
|
85
|
-
# Map missing methods to blocks
|
86
|
-
# DB ops or SQL generation
|
87
|
-
def self.method_missing(meth, *args, &block)
|
88
|
-
map_block meth, block
|
89
|
-
end
|
90
|
-
|
91
|
-
#
|
92
|
-
# Adapter methods
|
93
|
-
# These are called internally
|
94
|
-
# They are called by class methods
|
95
|
-
# in ../mystic.rb
|
96
|
-
#
|
97
|
-
def connect(opts)
|
98
|
-
disconnect unless @pool.nil?
|
99
|
-
@pool = AccessStack.new(
|
100
|
-
:size => @pool_size,
|
101
|
-
:timeout => @pool_timeout,
|
102
|
-
:expires => @pool_expires,
|
103
|
-
:create => lambda { block_for(:connect).call opts }
|
104
|
-
)
|
105
|
-
end
|
106
|
-
|
107
|
-
def disconnect
|
108
|
-
@pool.destroy ||= block_for :disconnect
|
109
|
-
@pool.empty!
|
110
|
-
end
|
111
|
-
|
112
|
-
def reap
|
113
|
-
@pool.validate ||= block_for :validate
|
114
|
-
@pool.reap
|
115
|
-
end
|
116
|
-
|
117
|
-
def execute(sql)
|
118
|
-
raise AdapterError, "Adapter's connection pool doesn't exist and so Mystic has not connected to the database." if @pool.nil?
|
119
|
-
@pool.with { |inst| block_for(:execute).call inst, sql }
|
120
|
-
end
|
121
|
-
|
122
|
-
def sanitize(str)
|
123
|
-
@pool.with { |inst| block_for(:sanitize).call inst, str }
|
124
|
-
end
|
125
|
-
|
126
|
-
def json_supported?
|
127
|
-
block_for(:json_supported).call
|
128
|
-
end
|
129
|
-
|
130
|
-
def serialize_sql(obj)
|
131
|
-
return case obj
|
132
|
-
when SQL::Table
|
133
|
-
block_for(:table).call obj
|
134
|
-
when SQL::Index
|
135
|
-
block_for(:index).call obj
|
136
|
-
when SQL::Column
|
137
|
-
block_for(:column).call obj
|
138
|
-
when SQL::Operation
|
139
|
-
res = block_for(obj.kind).call obj
|
140
|
-
obj.callback.call unless obj.callback.nil?
|
141
|
-
res
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "mystic"
|
4
|
-
require "mystic/adapter"
|
5
|
-
require "mystic/sql"
|
6
|
-
|
7
|
-
#
|
8
|
-
# This adapter is designed to hold
|
9
|
-
# basic, spec-adherent SQL generation
|
10
|
-
# so adapters can be more DRY
|
11
|
-
#
|
12
|
-
|
13
|
-
module Mystic
|
14
|
-
class Adapter
|
15
|
-
|
16
|
-
json_supported { false }
|
17
|
-
|
18
|
-
table do |obj|
|
19
|
-
sql = []
|
20
|
-
sql.push "CREATE TABLE #{obj.name} (#{obj.columns.map(&:to_sql)*","})" if obj.create?
|
21
|
-
sql.push "ALTER TABLE #{obj.name} #{obj.columns.map{|c| "ADD COLUMN #{c.to_sql}" }*', '}" unless obj.create?
|
22
|
-
sql.push *(obj.indeces.map(&:to_sql)) unless obj.indeces.empty?
|
23
|
-
sql.push *(obj.operations.map(&:to_sql)) unless obj.operations.empty?
|
24
|
-
sql*"; "
|
25
|
-
end
|
26
|
-
|
27
|
-
column do |obj|
|
28
|
-
sql = []
|
29
|
-
sql << obj.name
|
30
|
-
sql << obj.kind.downcase
|
31
|
-
sql << "(#{obj.size})" if obj.size && !obj.size.empty? && !obj.geospatial?
|
32
|
-
sql << "(#{obj.geom_kind}, #{obj.geom_srid})" if obj.geospatial?
|
33
|
-
sql << (obj.constraints[:null] ? "NULL" : "NOT NULL") if obj.constraints.member?(:null)
|
34
|
-
sql << "UNIQUE" if obj.constraints[:unique]
|
35
|
-
sql << "PRIMARY KEY" if obj.constraints[:primary_key]
|
36
|
-
sql << "REFERENCES " + obj.constraints[:references] if obj.constraints.member?(:references)
|
37
|
-
sql << "DEFAULT " + obj.constraints[:default] if obj.constraints.member?(:default)
|
38
|
-
sql << "CHECK(#{obj.constraints[:check]})" if obj.constraints.member?(:check)
|
39
|
-
sql*" "
|
40
|
-
end
|
41
|
-
|
42
|
-
index do |index|
|
43
|
-
sql = []
|
44
|
-
sql << "CREATE"
|
45
|
-
sql << "UNIQUE" if index.unique
|
46
|
-
sql << "INDEX"
|
47
|
-
sql << index.name if index.name
|
48
|
-
sql << "ON"
|
49
|
-
sql << index.table_name
|
50
|
-
sql << "(#{index.columns.map(&:to_s).join(',')})"
|
51
|
-
sql*" "
|
52
|
-
end
|
53
|
-
|
54
|
-
#
|
55
|
-
## Operations
|
56
|
-
#
|
57
|
-
|
58
|
-
drop_columns do |obj|
|
59
|
-
"ALTER TABLE #{obj.table_name} #{obj.column_names.map{|c| "DROP COLUMN #{c.to_s}" }*', '}"
|
60
|
-
end
|
61
|
-
|
62
|
-
rename_column do |obj|
|
63
|
-
"ALTER TABLE #{obj.table_name} RENAME COLUMN #{obj.old_name} TO #{obj.new_name}"
|
64
|
-
end
|
65
|
-
|
66
|
-
create_view do |obj|
|
67
|
-
"CREATE VIEW #{obj.name} AS #{obj.sql}"
|
68
|
-
end
|
69
|
-
|
70
|
-
drop_view do |obj|
|
71
|
-
"DROP VIEW #{obj.name}"
|
72
|
-
end
|
73
|
-
|
74
|
-
drop_table do |obj|
|
75
|
-
"DROP TABLE #{obj.table_name} #{obj.cascade? ? "CASCADE" : "RESTRICT" }"
|
76
|
-
end
|
77
|
-
|
78
|
-
rename_table do |obj|
|
79
|
-
"ALTER TABLE #{obj.old_name} RENAME TO #{obj.new_name}"
|
80
|
-
end
|
81
|
-
|
82
|
-
#
|
83
|
-
## Transaction Operations
|
84
|
-
#
|
85
|
-
|
86
|
-
start_transaction { "BEGIN" }
|
87
|
-
commit_transaction { "COMMIT" }
|
88
|
-
rollback_transaction { "ROLLBACK" }
|
89
|
-
end
|
90
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "mysql2"
|
4
|
-
|
5
|
-
# TODO:
|
6
|
-
# 1. Implement geometry
|
7
|
-
|
8
|
-
module Mystic
|
9
|
-
class MysqlAdapter < Mystic::Adapter
|
10
|
-
ALGORITHMS = [:default, :inplace, :copy]
|
11
|
-
LOCKS = [:default, :none, :shared, :exclusive]
|
12
|
-
|
13
|
-
connect { |opts| Mysql2::client.new opts }
|
14
|
-
disconnect { |mysql| mysql.close }
|
15
|
-
validate { |mysql| mysql.ping }
|
16
|
-
execute { |mysql, sql| mysql.query(sql).to_a }
|
17
|
-
sanitize { |mysql, str| mysql.escape str }
|
18
|
-
|
19
|
-
drop_index do |index|
|
20
|
-
"DROP INDEX #{index.name} ON #{index.table_name}"
|
21
|
-
end
|
22
|
-
|
23
|
-
index do |index|
|
24
|
-
sql = []
|
25
|
-
sql << "CREATE"
|
26
|
-
sql << "UNIQUE" if index.unique
|
27
|
-
sql << "INDEX"
|
28
|
-
sql << index.name if index.name
|
29
|
-
sql << "ON"
|
30
|
-
sql << index.table_name
|
31
|
-
sql << "USING #{obj.type.to_s.capitalize}" if index.type
|
32
|
-
sql << "(#{index.columns.map(&:to_s).join ',' })"
|
33
|
-
sql << "COMMENT #{index.comment.truncate 1024}" if index.comment
|
34
|
-
sql << "ALGORITHM #{index.algorithm.to_s.upcase}" if !index.lock && ALGORITHMS.include? index.algorithm
|
35
|
-
sql << "LOCK #{index.lock.to_s.upcase}" if !index.algorithm && LOCKS.include? index.lock
|
36
|
-
sql*" "
|
37
|
-
end
|
38
|
-
|
39
|
-
# Leverage MySQL savepoints to make up for transactions' flaws
|
40
|
-
start_transaction { <<-sql
|
41
|
-
BEGIN;
|
42
|
-
SAVEPOINT mystic_migration8889;
|
43
|
-
DECLARE EXIT HANDLER FOR SQLWARNING ROLLBACK TO mystic_migration8889;
|
44
|
-
DECLARE EXIT HANDLER FOR NOT FOUND ROLLBACK TO mystic_migration8889;
|
45
|
-
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK TO mystic_migration8889;
|
46
|
-
sql
|
47
|
-
}
|
48
|
-
commit_transaction { "RELEASE SAVEPOINT mystic_migration8889;COMMIT" }
|
49
|
-
rollback_transaction { "ROLLBACK TO mystic_migration8889; RELEASE SAVEPOINT mystic_migration8889; ROLLBACK" }
|
50
|
-
end
|
51
|
-
end
|
@@ -1,95 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "mystic/adapters/abstract"
|
4
|
-
require "pg"
|
5
|
-
|
6
|
-
# Mystic adapter for Postgres, includes PostGIS
|
7
|
-
|
8
|
-
module Mystic
|
9
|
-
class PostgresAdapter < Mystic::Adapter
|
10
|
-
INDEX_TYPES = [
|
11
|
-
:btree,
|
12
|
-
:hash,
|
13
|
-
:gist,
|
14
|
-
:spgist,
|
15
|
-
:gin
|
16
|
-
].freeze
|
17
|
-
|
18
|
-
CONNECT_FIELDS = [
|
19
|
-
:host,
|
20
|
-
:hostaddr,
|
21
|
-
:port,
|
22
|
-
:dbname,
|
23
|
-
:user,
|
24
|
-
:password,
|
25
|
-
:connect_timeout,
|
26
|
-
:options,
|
27
|
-
:tty,
|
28
|
-
:sslmode,
|
29
|
-
:krbsrvname,
|
30
|
-
:gsslib
|
31
|
-
].freeze
|
32
|
-
|
33
|
-
connect { |opts| PG.connect opts.subhash(*CONNECT_FIELDS) }
|
34
|
-
disconnect { |pg| pg.close }
|
35
|
-
validate { |pg| pg.status == CONNECTION_OK }
|
36
|
-
sanitize { |pg, str| pg.escape_string str }
|
37
|
-
json_supported { true }
|
38
|
-
|
39
|
-
execute do |pg, sql|
|
40
|
-
res = pg.exec sql
|
41
|
-
v = res[0][Mystic::JSON_COL] if res.ntuples == 1 && res.nfields == 1
|
42
|
-
v ||= res.ntuples.times.map { |i| res[i] } unless res.nil?
|
43
|
-
v ||= []
|
44
|
-
v
|
45
|
-
end
|
46
|
-
|
47
|
-
drop_index do |index|
|
48
|
-
"DROP INDEX #{index.index_name}"
|
49
|
-
end
|
50
|
-
|
51
|
-
create_ext do |ext|
|
52
|
-
"CREATE EXTENSION \"#{ext.name}\""
|
53
|
-
end
|
54
|
-
|
55
|
-
drop_ext do |ext|
|
56
|
-
"DROP EXTENSION \"#{ext.name}\""
|
57
|
-
end
|
58
|
-
|
59
|
-
index do |index|
|
60
|
-
storage_params = index.opts.subhash :fillfactor, :buffering, :fastupdate
|
61
|
-
|
62
|
-
sql = []
|
63
|
-
sql << "CREATE"
|
64
|
-
sql << "UNIQUE" if index.unique
|
65
|
-
sql << "INDEX"
|
66
|
-
sql << "CONCURENTLY" if index.concurrently
|
67
|
-
sql << index.name unless index.name.nil?
|
68
|
-
sql << "ON #{index.table_name}"
|
69
|
-
sql << "USING #{index.type}" if INDEX_TYPES.include? index.type
|
70
|
-
sql << "(#{index.columns.map(&:to_s).join ',' })"
|
71
|
-
sql << "WITH (#{storage_params.sqlize})" unless storage_params.empty?
|
72
|
-
sql << "TABLESPACE #{index.tablespace}" unless index.tablespace.nil?
|
73
|
-
sql << "WHERE #{index.where}" unless index.where.nil?
|
74
|
-
sql*' '
|
75
|
-
end
|
76
|
-
|
77
|
-
table do |table|
|
78
|
-
sql = []
|
79
|
-
|
80
|
-
if table.create?
|
81
|
-
tbl = []
|
82
|
-
tbl << "CREATE TABLE #{table.name} (#{table.columns.map(&:to_sql)*","})"
|
83
|
-
tbl << "INHERITS #{table.inherits}" if table.inherits
|
84
|
-
tbl << "TABLESPACE #{table.tablespace}" if table.tablespace
|
85
|
-
sql << tbl*' '
|
86
|
-
else
|
87
|
-
sql << "ALTER TABLE #{table.name} #{table.columns.map{ |c| "ADD COLUMN #{c.to_sql}" }*', ' }"
|
88
|
-
end
|
89
|
-
|
90
|
-
sql.push(*table.indeces.map(&:to_sql)) unless table.indeces.empty?
|
91
|
-
sql.push(*table.operations.map(&:to_sql)) unless table.operations.empty?
|
92
|
-
sql*'; '
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
data/lib/mystic/constants.rb
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
module Mystic
|
4
|
-
MysticError = Class.new StandardError
|
5
|
-
RootError = Class.new StandardError
|
6
|
-
EnvironmentError = Class.new StandardError
|
7
|
-
AdapterError = Class.new StandardError
|
8
|
-
CLIError = Class.new StandardError
|
9
|
-
MIG_REGEX = /(?<num>\d+)_(?<name>[a-z]+)\.rb$/i # matches migration files (ex '1_MigrationClassName.rb')
|
10
|
-
JSON_COL = "mystic_return_json89788"
|
11
|
-
end
|