mystic 0.0.9 → 0.1.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 +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
|