skiima 0.1.000 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +11 -3
  4. data/Gemfile +12 -6
  5. data/Guardfile +13 -11
  6. data/LICENSE +20 -0
  7. data/Procfile.example +2 -0
  8. data/README.md +170 -23
  9. data/Rakefile +26 -22
  10. data/lib/skiima.rb +61 -240
  11. data/lib/skiima/config.rb +60 -0
  12. data/lib/skiima/config/struct.rb +87 -0
  13. data/lib/skiima/db/connector.rb +195 -0
  14. data/lib/skiima/db/connector/active_record.rb +11 -0
  15. data/lib/skiima/db/connector/active_record/base_connector.rb +34 -0
  16. data/lib/skiima/db/connector/active_record/mysql2_connector.rb +147 -0
  17. data/lib/skiima/db/connector/active_record/mysql_connector.rb +177 -0
  18. data/lib/skiima/db/connector/active_record/postgresql_connector.rb +39 -0
  19. data/lib/skiima/db/helpers/mysql.rb +230 -0
  20. data/lib/skiima/db/helpers/postgresql.rb +221 -0
  21. data/lib/skiima/db/resolver.rb +62 -0
  22. data/lib/skiima/dependency/reader.rb +55 -0
  23. data/lib/skiima/dependency/script.rb +63 -0
  24. data/lib/skiima/i18n.rb +24 -0
  25. data/lib/skiima/loader.rb +108 -0
  26. data/lib/skiima/locales/en.yml +2 -2
  27. data/lib/skiima/logger.rb +54 -0
  28. data/lib/skiima/railtie.rb +10 -0
  29. data/lib/skiima/railties/skiima.rake +31 -0
  30. data/lib/skiima/version.rb +2 -2
  31. data/skiima.gemspec +5 -5
  32. data/spec/config/{database.yml → database.yml.example} +16 -0
  33. data/spec/config/database.yml.travis +69 -0
  34. data/spec/db/skiima/{depends.yml → dependencies.yml} +7 -2
  35. data/spec/db/skiima/{empty_depends.yml → empty_dependencies.yml} +0 -0
  36. data/spec/db/skiima/init_test_db/database.skiima_test.mysql.current.sql +7 -0
  37. data/spec/db/skiima/init_test_db/database.skiima_test.postgresql.current.sql +7 -0
  38. data/spec/mysql2_spec.rb +61 -12
  39. data/spec/mysql_spec.rb +66 -27
  40. data/spec/postgresql_spec.rb +55 -34
  41. data/spec/shared_examples/config_shared_example.rb +40 -0
  42. data/spec/skiima/config/struct_spec.rb +78 -0
  43. data/spec/skiima/config_spec.rb +6 -0
  44. data/spec/skiima/db/connector/active_record/base_connector_spec.rb +0 -0
  45. data/spec/skiima/db/connector/active_record/mysql2_connector_spec.rb +3 -0
  46. data/spec/skiima/db/connector/active_record/mysql_connector_spec.rb +3 -0
  47. data/spec/skiima/db/connector/active_record/postgresql_connector_spec.rb +7 -0
  48. data/spec/skiima/db/connector_spec.rb +6 -0
  49. data/spec/skiima/db/resolver_spec.rb +54 -0
  50. data/spec/skiima/dependency/reader_spec.rb +52 -0
  51. data/spec/skiima/{dependency_spec.rb → dependency/script_spec.rb} +3 -41
  52. data/spec/skiima/i18n_spec.rb +29 -0
  53. data/spec/skiima/loader_spec.rb +102 -0
  54. data/spec/skiima/logger_spec.rb +0 -0
  55. data/spec/skiima_spec.rb +43 -64
  56. data/spec/spec_helper.rb +38 -4
  57. metadata +144 -100
  58. data/lib/skiima/db_adapters.rb +0 -187
  59. data/lib/skiima/db_adapters/base_mysql_adapter.rb +0 -308
  60. data/lib/skiima/db_adapters/mysql2_adapter.rb +0 -114
  61. data/lib/skiima/db_adapters/mysql_adapter.rb +0 -287
  62. data/lib/skiima/db_adapters/postgresql_adapter.rb +0 -509
  63. data/lib/skiima/dependency.rb +0 -84
  64. data/lib/skiima_helpers.rb +0 -49
  65. data/spec/skiima/db_adapters/mysql_adapter_spec.rb +0 -38
  66. data/spec/skiima/db_adapters/postgresql_adapter_spec.rb +0 -20
  67. data/spec/skiima/db_adapters_spec.rb +0 -31
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ require 'skiima/db/helpers/postgresql' unless defined? Skiima::Db::Helpers::Postgresql
3
+ require 'skiima/db/connector/active_record/base_connector' unless defined? Skiima::Db::Connector::ActiveRecord::BaseConnector
4
+ require 'active_record/connection_adapters/postgresql_adapter' unless defined? ActiveRecord::ConnectionAdapters::PostreSQLAdapter
5
+
6
+ module Skiima
7
+ module Db
8
+ module Connector
9
+ module ActiveRecord
10
+
11
+ class PostgresqlConnector < Skiima::Db::Connector::ActiveRecord::BaseConnector
12
+ delegate [:local_tz, :column_names, :check_psql_version,
13
+ :table_exists?, :index_exists?, :rule_exists?,
14
+ :view_exists?, :schema_exists?] => :adapter
15
+
16
+ def initialize(adapter, logger, config = {})
17
+ super
18
+ check_psql_version
19
+ end
20
+
21
+ class << self
22
+ delegate postgresql_connection: :active_record_resolver_klass
23
+
24
+ def create_adapter(config, logger, pool)
25
+ case ::ActiveRecord::VERSION::MAJOR
26
+ when 3,4 then send('postgresql_connection', config)
27
+ end
28
+ end
29
+
30
+ def helpers_module
31
+ Skiima::Db::Helpers::Postgresql
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,230 @@
1
+ module Skiima
2
+ module Db
3
+ module Helpers
4
+ module Mysql
5
+ #attr_accessor :version
6
+
7
+ def supported_objects
8
+ [:database, :table, :view, :index, :proc]
9
+ end
10
+
11
+ def execute(sql, name = nil)
12
+ # relying on formatting inside the file is precisely what i wanted to avoid...
13
+ results = sql.split(/^--={4,}/).map do |line|
14
+ super(line)
15
+ end
16
+
17
+ results.first
18
+ end
19
+
20
+ def exec_query(sql, name = 'SQL')
21
+ log(sql, name) do
22
+ exec_stmt(sql, name) do |cols, stmt|
23
+ stmt.to_a
24
+ end
25
+ end
26
+ end
27
+
28
+ #override?
29
+ def tables(name = nil, database = nil, like = nil)
30
+ sql = "SHOW FULL TABLES "
31
+ sql << "IN #{database} " if database
32
+ sql << "WHERE table_type = 'BASE TABLE' "
33
+ sql << "LIKE '#{like}' " if like
34
+
35
+ execute_and_free(sql, 'SCHEMA') do |result|
36
+ result.collect { |field| field.first }
37
+ end
38
+ end
39
+
40
+ def views(name = nil, database = nil, like = nil)
41
+ sql = "SHOW FULL TABLES "
42
+ sql << "IN #{database} " if database
43
+ sql << "WHERE table_type = 'VIEW' "
44
+ sql << "LIKE '#{like}' " if like
45
+
46
+ execute_and_free(sql, 'SCHEMA') do |result|
47
+ result.collect { |field| field.first }
48
+ end
49
+ end
50
+
51
+ def indexes(name = nil, database = nil, table = nil)
52
+ sql = "SHOW INDEX "
53
+ sql << "IN #{table} "
54
+ sql << "IN #{database} " if database
55
+ sql << "WHERE key_name = '#{name}'" if name
56
+
57
+ execute_and_free(sql, 'SCHEMA') do |result|
58
+ result.collect { |field| field[2] }
59
+ end
60
+ end
61
+
62
+ def procs(name = nil, database = nil, like = nil)
63
+ sql = "SELECT r.routine_name "
64
+ sql << "FROM information_schema.routines r "
65
+ sql << "WHERE r.routine_type = 'PROCEDURE' "
66
+ sql << "AND r.routine_name LIKE '#{like}' " if like
67
+ sql << "AND r.routine_schema = #{database} " if database
68
+
69
+ execute_and_free(sql, 'SCHEMA') do |result|
70
+ result.collect { |field| field.first }
71
+ end
72
+ end
73
+
74
+ # needed?
75
+ #def column_definitions(table_name)
76
+ # # "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
77
+ #end
78
+
79
+ # keep?
80
+ #def column_names(table_name)
81
+ # sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
82
+ # execute_and_free(sql, 'SCHEMA') do |result|
83
+ # result.collect { |field| field.first }
84
+ # end
85
+ #end
86
+
87
+ def column_names(table_name)
88
+ columns(table_name).map(&:name)
89
+ end
90
+
91
+ # schema matchers
92
+ def object_exists?(type, name, opts = {})
93
+ send("#{type}_exists?", name, opts) if supported_objects.include? type.to_sym
94
+ end
95
+
96
+ def database_exists?(name)
97
+ #stub
98
+ end
99
+
100
+ def table_exists?(name)
101
+ return false unless name
102
+ return true if tables(nil, nil, name).any?
103
+
104
+ name = name.to_s
105
+ schema, table = name.split('.', 2)
106
+
107
+ unless table # A table was provided without a schema
108
+ table = schema
109
+ schema = nil
110
+ end
111
+
112
+ tables(nil, schema, table).any?
113
+ end
114
+
115
+ def view_exists?(name)
116
+ return false unless name
117
+ return true if views(nil, nil, name).any?
118
+
119
+ name = name.to_s
120
+ schema, view = name.split('.', 2)
121
+
122
+ unless view # A table was provided without a schema
123
+ view = schema
124
+ schema = nil
125
+ end
126
+
127
+ views(nil, schema, view).any?
128
+ end
129
+
130
+ def index_exists?(name, opts = {})
131
+ target = opts[:attr] ? opts[:attr][0] : nil
132
+ raise "requires target object" unless target
133
+
134
+ return false unless table_exists?(target) #mysql blows up when table doesn't exist
135
+ return false unless name
136
+ return true if indexes(name, nil, target).any?
137
+
138
+ name = name.to_s
139
+ schema, target = name.split('.', 2)
140
+
141
+ unless target # A table was provided without a schema
142
+ target = schema
143
+ schema = nil
144
+ end
145
+
146
+ indexes(name, schema, target).any?
147
+ end
148
+
149
+ def proc_exists?(name, opts = {})
150
+ return false unless name
151
+ return true if procs(nil, nil, name).any?
152
+
153
+ name = name.to_s
154
+ schema, proc = name.split('.', 2)
155
+
156
+ unless proc # A table was provided without a schema
157
+ proc = schema
158
+ schema = nil
159
+ end
160
+
161
+ procs(name, schema, proc).any?
162
+ end
163
+
164
+ # queries
165
+ def drop(type, name, opts = {})
166
+ send("drop_#{type}", name, opts) if supported_objects.include? type.to_sym
167
+ end
168
+
169
+ def drop_database(name, opts = {})
170
+ "DROP DATABASE IF EXISTS #{name}"
171
+ end
172
+
173
+ def drop_table(name, opts = {})
174
+ "DROP TABLE IF EXISTS #{name}"
175
+ end
176
+
177
+ def drop_view(name, opts = {})
178
+ "DROP VIEW IF EXISTS #{name}"
179
+ end
180
+
181
+ def drop_proc(name, opts = {})
182
+ "DROP PROCEDURE IF EXISTS #{name}"
183
+ end
184
+
185
+ def drop_index(name, opts = {})
186
+ target = opts[:attr].first if opts[:attr]
187
+ raise "requires target object" unless target
188
+
189
+ "DROP INDEX #{name} ON #{target}"
190
+ end
191
+
192
+ protected
193
+
194
+ #def translate_exception(exception, message)
195
+ # exception
196
+ # # case error_number(exception)
197
+ # # when 1062
198
+ # # RecordNotUnique.new(message, exception)
199
+ # # when 1452
200
+ # # InvalidForeignKey.new(message, exception)
201
+ # # else
202
+ # # super
203
+ # # end
204
+ #end
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ ## encoding: utf-8
211
+ #module Skiima
212
+ # module DbAdapters
213
+ # class BaseMysqlAdapter < Base
214
+ # attr_accessor :version
215
+ #
216
+ # LOST_CONNECTION_ERROR_MESSAGES = [
217
+ # "Server shutdown in progress",
218
+ # "Broken pipe",
219
+ # "Lost connection to MySQL server during query",
220
+ # "MySQL server has gone away" ]
221
+ #
222
+ # # FIXME: Make the first parameter more similar for the two adapters
223
+ # def initialize(connection, logger, connection_options, config)
224
+ # super(connection, logger)
225
+ # @connection_options, @config = connection_options, config
226
+ # @quoted_column_names, @quoted_table_names = {}, {}
227
+ # end
228
+ # end
229
+ # end
230
+ #end
@@ -0,0 +1,221 @@
1
+ module Skiima
2
+ module Db
3
+ module Helpers
4
+ module Postgresql
5
+ attr_accessor :local_tz
6
+ attr_accessor :version
7
+
8
+ def execute(sql, name = nil)
9
+ # relying on formatting inside the file is precisely what i wanted to avoid...
10
+ results = sql.split(/^--={4,}/).map do |line|
11
+ super(line)
12
+ end
13
+
14
+ results.first
15
+ end
16
+
17
+ # skiima
18
+ def supported_objects
19
+ [:database, :schema, :table, :view, :rule, :index]
20
+ end
21
+
22
+ def check_psql_version
23
+ @version = postgresql_version
24
+ if @version < 80200
25
+ raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
26
+ end
27
+ end
28
+
29
+ # schema matchers
30
+ def object_exists?(type, name, opts = {})
31
+ send("#{type}_exists?", name, opts) if supported_objects.include? type.to_sym
32
+ end
33
+
34
+ def database_exists?(name, opts = {})
35
+ query(Skiima.interpolate_sql('&', <<-SQL, { :database => name }))[0][0].to_i > 0
36
+ SELECT COUNT(*)
37
+ FROM pg_databases pdb
38
+ WHERE pdb.datname = '&database'
39
+ SQL
40
+ end
41
+
42
+ def schema_exists?(name, opts = {})
43
+ query(Skiima.interpolate_sql('&', <<-SQL, { :schema => name }))[0][0].to_i > 0
44
+ SELECT COUNT(*)
45
+ FROM pg_namespace
46
+ WHERE nspname = '&schema'
47
+ SQL
48
+ end
49
+
50
+ def table_exists?(name, opts = {})
51
+ schema, table = ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::Utils.extract_schema_and_table(name.to_s)
52
+ vars = { :table => table,
53
+ :schema => ((schema && !schema.empty?) ? "'#{schema}'" : "ANY (current_schemas(false))") }
54
+
55
+ vars.inspect
56
+
57
+ query(Skiima.interpolate_sql('&', <<-SQL, vars))[0][0].to_i > 0
58
+ SELECT COUNT(*)
59
+ FROM pg_class c
60
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
61
+ WHERE c.relkind in ('r')
62
+ AND c.relname = '&table'
63
+ AND n.nspname = &schema
64
+ SQL
65
+ end
66
+
67
+ def view_exists?(name, opts = {})
68
+ schema, view = ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::Utils.extract_schema_and_table(name.to_s)
69
+ vars = { :view => view,
70
+ :schema => ((schema && !schema.empty?) ? "'#{schema}'" : "ANY (current_schemas(false))") }
71
+
72
+ query(Skiima.interpolate_sql('&', <<-SQL, vars))[0][0].to_i > 0
73
+ SELECT COUNT(*)
74
+ FROM pg_class c
75
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
76
+ WHERE c.relkind in ('v')
77
+ AND c.relname = '&view'
78
+ AND n.nspname = &schema
79
+ SQL
80
+ end
81
+
82
+ def rule_exists?(name, opts = {})
83
+ target = opts[:attr] ? opts[:attr][0] : nil
84
+ raise "requires target object" unless target
85
+ schema, rule = ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::Utils.extract_schema_and_table(name.to_s)
86
+ vars = { :rule => rule,
87
+ :target => target,
88
+ :schema => ((schema && !schema.empty?) ? "'#{schema}'" : "ANY (current_schemas(false))") }
89
+
90
+ query(Skiima.interpolate_sql('&', <<-SQL, vars))[0][0].to_i > 0
91
+ SELECT COUNT(*)
92
+ FROM pg_rules pgr
93
+ WHERE pgr.rulename = '&rule'
94
+ AND pgr.tablename = '&target'
95
+ AND pgr.schemaname = &schema
96
+ SQL
97
+ end
98
+
99
+ def index_exists?(name, opts = {})
100
+ target = opts[:attr] ? opts[:attr][0] : nil
101
+ raise "requires target object" unless target
102
+ schema, index = ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::Utils.extract_schema_and_table(name.to_s)
103
+ vars = { :index => index,
104
+ :target => target,
105
+ :schema => ((schema && !schema.empty?) ? "'#{schema}'" : "ANY (current_schemas(false))") }
106
+
107
+ query(Skiima.interpolate_sql('&', <<-SQL, vars))[0][0].to_i > 0
108
+ SELECT COUNT(*)
109
+ FROM pg_indexes pgr
110
+ WHERE pgr.indexname = '&index'
111
+ AND pgr.tablename = '&target'
112
+ AND pgr.schemaname = &schema
113
+ SQL
114
+ end
115
+
116
+ # queries
117
+ def drop(type, name, opts = {})
118
+ send("drop_#{type}", name, opts) if supported_objects.include? type.to_sym
119
+ end
120
+
121
+ def drop_database(name, opts = {})
122
+ "DROP DATABASE IF EXISTS #{name}"
123
+ end
124
+
125
+ def drop_schema(name, opts = {})
126
+ "DROP SCHEMA IF EXISTS #{name}"
127
+ end
128
+
129
+ def drop_table(name, opts = {})
130
+ "DROP TABLE IF EXISTS #{name}"
131
+ end
132
+
133
+ def drop_view(name, opts = {})
134
+ "DROP VIEW IF EXISTS #{name}"
135
+ end
136
+
137
+ def drop_rule(name, opts = {})
138
+ target = opts[:attr].first if opts[:attr]
139
+ raise "requires target object" unless target
140
+
141
+ "DROP RULE IF EXISTS #{name} ON #{target}"
142
+ end
143
+
144
+ def drop_index(name, opts = {})
145
+ "DROP INDEX IF EXISTS #{name}"
146
+ end
147
+
148
+ # in original? why?
149
+ #necessary 2 override?
150
+ #def translate_exception(e, message)
151
+ # e
152
+ # # case exception.message
153
+ # # when /duplicate key value violates unique constraint/
154
+ # # RecordNotUnique.new(message, exception)
155
+ # # when /violates foreign key constraint/
156
+ # # InvalidForeignKey.new(message, exception)
157
+ # # else
158
+ # # super
159
+ # # end
160
+ #end
161
+
162
+ # necessary 2 override?
163
+ #class PgColumn
164
+ # attr_accessor :name, :deafult, :type, :null
165
+ # def initialize(name, default = nil, type = nil, null = true)
166
+ # @name, @default, @type, @null = name, default, type, null
167
+ # end
168
+ #
169
+ # def to_s
170
+ # # to be implemented
171
+ # end
172
+ #end
173
+ def column_names(table_name)
174
+ columns(table_name).map(&:name)
175
+ end
176
+
177
+ # necessary 2 override?
178
+ ## Close then reopen the connection.
179
+ #def reconnect!
180
+ # clear_cache!
181
+ # @connection.reset
182
+ # configure_connection
183
+ #end
184
+
185
+ # necessary 2 override?
186
+ ## Is this connection alive and ready for queries?
187
+ #def active?
188
+ # @connection.status == PGconn::CONNECTION_OK
189
+ #rescue PGError
190
+ # false
191
+ #end
192
+
193
+ protected
194
+
195
+ def get_timezone
196
+ execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
197
+ end
198
+
199
+ private
200
+
201
+ #necessary 2 override?
202
+ # Configures the encoding, verbosity, schema search path, and time zone of the connection.
203
+ # This is called by #connect and should not be called manually.
204
+ #def configure_connection
205
+ # if @config[:encoding]
206
+ # @connection.set_client_encoding(@config[:encoding])
207
+ # end
208
+ # self.client_min_messages = @config[:min_messages] if @config[:min_messages]
209
+ # self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
210
+ #
211
+ # # Use standard-conforming strings if available so we don't have to do the E'...' dance.
212
+ # set_standard_conforming_strings
213
+ #
214
+ # #configure the connection to return TIMESTAMP WITH ZONE types in UTC.
215
+ # execute("SET time zone '#{@local_tz}'", 'SCHEMA') if @local_tz
216
+ #end
217
+
218
+ end
219
+ end
220
+ end
221
+ end