schema_monkey 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +24 -14
- data/lib/schema_monkey/active_record/connection_adapters/abstract_adapter.rb +3 -4
- data/lib/schema_monkey/active_record/connection_adapters/{mysql_adapter.rb → mysql2_adapter.rb} +1 -1
- data/lib/schema_monkey/client.rb +61 -0
- data/lib/schema_monkey/module.rb +33 -0
- data/lib/schema_monkey/version.rb +1 -1
- data/lib/schema_monkey.rb +12 -50
- metadata +5 -4
- data/lib/schema_monkey/module_support.rb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d6e7678f91c469b42eeac242d5095283edcaf4a
|
4
|
+
data.tar.gz: 11e38e5ba367ced242293615096012c752150cd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1022cc53e764a97faca78bec36a612a5a1b69a13369ba74af3b19a93ece5cbe1dd80002bb50430360e2027b717b3082aefa6c66e2f6422d8936978884ae0ae15
|
7
|
+
data.tar.gz: ff13029ef2dd6a8ecf5084e8c16526ae1439ce14df10710914c21afb69abb4927bc6d5275c141e94434c0f631471d3432a820d872e1086bb6ad4fd7b0faf3f8f
|
data/README.md
CHANGED
@@ -3,38 +3,46 @@
|
|
3
3
|
[![Coverage Status](https://img.shields.io/coveralls/SchemaPlus/schema_monkey.svg)](https://coveralls.io/r/SchemaPlus/schema_monkey)
|
4
4
|
[![Dependency Status](https://gemnasium.com/lomba/schema_monkey.svg)](https://gemnasium.com/SchemaPlus/schema_monkey)
|
5
5
|
|
6
|
-
#
|
6
|
+
# SchemaMonkey
|
7
7
|
|
8
|
-
|
8
|
+
SchemaMonkey is a behind-the-scenes gem to facilitate writing other gems that extend ActiveRecord. It provides:
|
9
|
+
|
10
|
+
* A "middleware"-style interface to key ActiveRecord internal functions. For example, there's a middleware hook to let you insert a handler for migration column definition options, and there are several hooks to insert handlers for the various parts of a schema dump.
|
11
|
+
|
12
|
+
* A convention-based protocol for `include`'ing custom modules into ActiveRecord. You just define your modules and SchemaMonkey will automatically include them in the right places.
|
13
|
+
|
14
|
+
* It takes care of setting up the Railtie appropriately to invoke the gems.
|
15
|
+
|
16
|
+
The middleware interface has two benefits: it provides a clean API so that the client gems don't need to monkey-patch ActiveRecord (SchemaMonkey does all the monkey-patching for you), and it lets the client gems operate in parallel without concern about conflicting monkey-patches.
|
9
17
|
|
10
|
-
schema_monkey is part of the [SchemaPlus](https://github.com/SchemaPlus/) family of Ruby on Rails extension gems.
|
11
18
|
|
12
19
|
## Installation
|
13
20
|
|
14
|
-
In your
|
21
|
+
In your gem's `gemspec`, specify a dependency:
|
15
22
|
|
16
23
|
```ruby
|
17
|
-
|
24
|
+
spec.add_dependency "schema_monkey", "~> <MAJOR>.<MINOR>", ">= <MAJOR>.<MINOR>.<PATCH>"
|
18
25
|
```
|
26
|
+
|
27
|
+
SchemaMonkey follows semantic versioning; it's a good idea to explicitly use the `~>` and `>=` dependencies to make sure your gem's clients don't accidentally pull in a version of SchemaMonkey that your gem isn't compatible with.
|
28
|
+
|
19
29
|
## Compatibility
|
20
30
|
|
21
|
-
|
31
|
+
SchemaMonkey is tested on:
|
22
32
|
|
23
|
-
|
24
|
-
|
33
|
+
<!-- SCHEMA_DEV: MATRIX - begin -->
|
34
|
+
<!-- These lines are auto-generated by schema_dev based on schema_dev.yml -->
|
25
35
|
* ruby **1.9.3** with rails **4.2**, using **mysql2**, **sqlite3** or **postgresql**
|
26
36
|
* ruby **2.1.5** with rails **4.2**, using **mysql2**, **sqlite3** or **postgresql**
|
27
37
|
|
28
|
-
|
38
|
+
<!-- SCHEMA_DEV: MATRIX - end -->
|
29
39
|
|
30
40
|
## Usage
|
31
41
|
|
32
|
-
TODO: Write usage instructions here
|
33
42
|
|
43
|
+
**Sorry -- no real documentation yet. Will get to that as soon as I can.**
|
34
44
|
|
35
|
-
## History
|
36
45
|
|
37
|
-
* See [CHANGELOG](CHANGELOG.md) for per-version release notes.
|
38
46
|
|
39
47
|
## Development & Testing
|
40
48
|
|
@@ -43,7 +51,9 @@ the standard protocol: fork, feature branch, develop, push, and issue pull reque
|
|
43
51
|
|
44
52
|
Some things to know about to help you develop and test:
|
45
53
|
|
46
|
-
* **
|
54
|
+
* **Ugh -- no specs yet. SchemaMonkey is currently tested indirectly by testing the client gems that use it. That's another thing to get to as soon as I can.**
|
55
|
+
|
56
|
+
* **schema_dev**: SchemaMonkey uses [schema_dev](https://github.com/SchemaPlus/schema_dev) to
|
47
57
|
facilitate running rspec tests on the matrix of ruby, rails, and database
|
48
58
|
versions that the gem supports, both locally and on
|
49
59
|
[travis-ci](http://travis-ci.org/SchemaPlus/schema_monkey)
|
@@ -57,4 +67,4 @@ Some things to know about to help you develop and test:
|
|
57
67
|
[schema_dev](https://github.com/SchemaPlus/schema_dev) README.
|
58
68
|
|
59
69
|
The matrix of configurations is specified in `schema_dev.yml` in
|
60
|
-
the project root.
|
70
|
+
the project root.
|
@@ -11,12 +11,11 @@ module SchemaMonkey
|
|
11
11
|
|
12
12
|
dbm = case adapter_name
|
13
13
|
when /^MySQL/i then :Mysql
|
14
|
-
when 'PostgreSQL', 'PostGIS' then :
|
15
|
-
when 'SQLite' then :
|
14
|
+
when 'PostgreSQL', 'PostGIS' then :PostgreSQL
|
15
|
+
when 'SQLite' then :SQLite3
|
16
16
|
end
|
17
17
|
|
18
|
-
SchemaMonkey.
|
19
|
-
SchemaMonkey.insert_middleware(dbm)
|
18
|
+
SchemaMonkey.insert(dbm: dbm)
|
20
19
|
end
|
21
20
|
|
22
21
|
module SchemaCreation
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module SchemaMonkey
|
2
|
+
class Client
|
3
|
+
def initialize(mod)
|
4
|
+
@root = mod
|
5
|
+
@inserted = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def insert(opts={})
|
9
|
+
opts = opts.keyword_args(:dbm)
|
10
|
+
include_modules(dbm: opts.dbm)
|
11
|
+
insert_middleware(dbm: opts.dbm)
|
12
|
+
@root.insert() if @root.respond_to?(:insert) and @root != ::SchemaMonkey
|
13
|
+
end
|
14
|
+
|
15
|
+
def include_modules(opts={})
|
16
|
+
opts = opts.keyword_args(:dbm)
|
17
|
+
find_modules(:ActiveRecord, dbm: opts.dbm).each do |mod|
|
18
|
+
next if mod.is_a? Class
|
19
|
+
component = mod.to_s.sub(/^#{@root}::ActiveRecord::/, '')
|
20
|
+
component = component.gsub(/#{opts.dbm}/i, opts.dbm.to_s) if opts.dbm # canonicalize case
|
21
|
+
next unless base = Module.get_const(::ActiveRecord, component)
|
22
|
+
# Kernel.warn "including #{mod}"
|
23
|
+
Module.include_once base, mod
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def insert_middleware(opts={})
|
28
|
+
opts = opts.keyword_args(:dbm)
|
29
|
+
find_modules(:Middleware, dbm: opts.dbm, and_self: true).each do |mod|
|
30
|
+
next if @inserted[mod]
|
31
|
+
next unless mod.respond_to? :insert
|
32
|
+
# Kernel.warn "inserting #{mod}"
|
33
|
+
mod.insert
|
34
|
+
@inserted[mod] = true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def find_modules(container, opts={})
|
41
|
+
opts = opts.keyword_args(dbm: nil, and_self: nil)
|
42
|
+
return [] unless (container = Module.get_const(@root, container))
|
43
|
+
|
44
|
+
if opts.dbm
|
45
|
+
accept = /\b#{opts.dbm}/i
|
46
|
+
reject = nil
|
47
|
+
else
|
48
|
+
accept = nil
|
49
|
+
reject = /\b#{SchemaMonkey::DBMS.join('|')}/i
|
50
|
+
end
|
51
|
+
|
52
|
+
modules = []
|
53
|
+
modules << container if opts.and_self
|
54
|
+
modules += Module.descendants(container, can_load: accept)
|
55
|
+
modules.select!(&it.to_s =~ accept) if accept
|
56
|
+
modules.reject!(&it.to_s =~ reject) if reject
|
57
|
+
modules
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SchemaMonkey
|
2
|
+
module Module
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def include_once(base, mod)
|
6
|
+
base.send(:include, mod) unless base.include? mod
|
7
|
+
end
|
8
|
+
|
9
|
+
# ruby 2.* supports mod.const_get("Component::Path") but ruby 1.9.3
|
10
|
+
# doesn't. And neither has an option to return nil rather than raising
|
11
|
+
# a NameError
|
12
|
+
def get_const(mod, name)
|
13
|
+
name.to_s.split('::').map(&:to_sym).each do |component|
|
14
|
+
begin
|
15
|
+
mod = mod.const_get(component, false)
|
16
|
+
rescue NameError
|
17
|
+
return nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
mod
|
21
|
+
end
|
22
|
+
|
23
|
+
def descendants(mod, opts={})
|
24
|
+
opts = opts.keyword_args(can_load: nil)
|
25
|
+
consts, auto = mod.constants.group_by{|c| !!mod.autoload?(c)}.values_at(false, true)
|
26
|
+
consts ||= []
|
27
|
+
consts += auto.select &it.to_s =~ opts.can_load if opts.can_load and auto
|
28
|
+
children = consts.map{|c| mod.const_get(c) }.select &it.is_a?(::Module)
|
29
|
+
children + children.flat_map {|c| descendants(c, can_load: opts.can_load) }
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/schema_monkey.rb
CHANGED
@@ -2,9 +2,11 @@ require 'hash_keyword_args'
|
|
2
2
|
require 'its-it'
|
3
3
|
require 'key_struct'
|
4
4
|
require 'middleware'
|
5
|
+
require 'active_support/core_ext/string'
|
5
6
|
|
7
|
+
require_relative "schema_monkey/client"
|
6
8
|
require_relative "schema_monkey/middleware"
|
7
|
-
require_relative "schema_monkey/
|
9
|
+
require_relative "schema_monkey/module"
|
8
10
|
require_relative "schema_monkey/active_record/base"
|
9
11
|
require_relative "schema_monkey/active_record/connection_adapters/abstract_adapter"
|
10
12
|
require_relative "schema_monkey/active_record/connection_adapters/table_definition"
|
@@ -14,69 +16,29 @@ require_relative 'schema_monkey/active_record/schema_dumper'
|
|
14
16
|
require_relative 'schema_monkey/railtie' if defined?(Rails::Railtie)
|
15
17
|
|
16
18
|
module SchemaMonkey
|
19
|
+
extend Module
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
DBMS = [:Postgresql, :Mysql, :Sqlite3]
|
21
|
+
DBMS = [:PostgreSQL, :Mysql, :SQLite3]
|
21
22
|
|
22
23
|
module ActiveRecord
|
23
24
|
module ConnectionAdapters
|
24
25
|
autoload :PostgresqlAdapter, 'schema_monkey/active_record/connection_adapters/postgresql_adapter'
|
25
|
-
autoload :
|
26
|
+
autoload :Mysql2Adapter, 'schema_monkey/active_record/connection_adapters/mysql2_adapter'
|
26
27
|
autoload :Sqlite3Adapter, 'schema_monkey/active_record/connection_adapters/sqlite3_adapter'
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
30
|
-
def self.insert
|
31
|
-
insert_modules
|
32
|
-
include_adapters(::ActiveRecord::ConnectionAdapters::AbstractAdapter, :Abstract)
|
33
|
-
insert_middleware
|
34
|
-
end
|
35
|
-
|
36
31
|
def self.register(mod)
|
37
|
-
|
32
|
+
clients << Client.new(mod)
|
38
33
|
end
|
39
34
|
|
40
|
-
def self.
|
41
|
-
@
|
35
|
+
def self.clients
|
36
|
+
@clients ||= [Client.new(self)]
|
42
37
|
end
|
43
38
|
|
44
|
-
def self.
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.insert_modules
|
51
|
-
registered_modules.each do |mod|
|
52
|
-
get_modules(mod, prefix: 'ActiveRecord', match: /\bActiveRecord\b/, recursive: true).each do |candidate|
|
53
|
-
next if candidate.is_a?(Class)
|
54
|
-
if (base = get_const(::ActiveRecord, candidate.to_s.sub(/^#{mod}::ActiveRecord::/, '')))
|
55
|
-
patch base, mod
|
56
|
-
end
|
57
|
-
end
|
58
|
-
mod.insert if mod.respond_to?(:insert) and mod != self
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.insert_middleware(dbm=nil)
|
63
|
-
@inserted ||= {}
|
64
|
-
|
65
|
-
if dbm
|
66
|
-
match = /\b#{dbm}\b/
|
67
|
-
reject = nil
|
68
|
-
else
|
69
|
-
match = nil
|
70
|
-
reject = /\b(#{DBMS.join('|')})\b/
|
71
|
-
end
|
72
|
-
|
73
|
-
registered_modules.each do |mod|
|
74
|
-
get_modules(mod, prefix: 'Middleware', and_self: true, match: match, reject: reject, recursive: true, respond_to: :insert).each do |middleware|
|
75
|
-
next if @inserted[middleware]
|
76
|
-
middleware.insert
|
77
|
-
@inserted[middleware] = true
|
78
|
-
end
|
79
|
-
end
|
39
|
+
def self.insert(opts={})
|
40
|
+
opts = opts.keyword_args(:dbm)
|
41
|
+
clients.each &it.insert(dbm: opts.dbm)
|
80
42
|
end
|
81
43
|
|
82
44
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: schema_monkey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ronen barzel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -186,15 +186,16 @@ files:
|
|
186
186
|
- lib/schema_monkey.rb
|
187
187
|
- lib/schema_monkey/active_record/base.rb
|
188
188
|
- lib/schema_monkey/active_record/connection_adapters/abstract_adapter.rb
|
189
|
-
- lib/schema_monkey/active_record/connection_adapters/
|
189
|
+
- lib/schema_monkey/active_record/connection_adapters/mysql2_adapter.rb
|
190
190
|
- lib/schema_monkey/active_record/connection_adapters/postgresql_adapter.rb
|
191
191
|
- lib/schema_monkey/active_record/connection_adapters/schema_statements.rb
|
192
192
|
- lib/schema_monkey/active_record/connection_adapters/sqlite3_adapter.rb
|
193
193
|
- lib/schema_monkey/active_record/connection_adapters/table_definition.rb
|
194
194
|
- lib/schema_monkey/active_record/migration/command_recorder.rb
|
195
195
|
- lib/schema_monkey/active_record/schema_dumper.rb
|
196
|
+
- lib/schema_monkey/client.rb
|
196
197
|
- lib/schema_monkey/middleware.rb
|
197
|
-
- lib/schema_monkey/
|
198
|
+
- lib/schema_monkey/module.rb
|
198
199
|
- lib/schema_monkey/railtie.rb
|
199
200
|
- lib/schema_monkey/version.rb
|
200
201
|
- schema_dev.yml
|
@@ -1,48 +0,0 @@
|
|
1
|
-
module SchemaMonkey
|
2
|
-
module ModuleSupport
|
3
|
-
|
4
|
-
def include_once(base, mod)
|
5
|
-
base.send(:include, mod) unless base.include? mod
|
6
|
-
end
|
7
|
-
|
8
|
-
def include_if_defined(base, parent, subname)
|
9
|
-
if submodule = get_const(parent, subname)
|
10
|
-
include_once(base, submodule)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def patch(base, parent = SchemaMonkey)
|
15
|
-
patch = get_const(parent, base)
|
16
|
-
raise "#{parent} does not contain a definition of #{base}" unless patch
|
17
|
-
include_once(base, patch)
|
18
|
-
end
|
19
|
-
|
20
|
-
# ruby 2.* supports mod.const_get("Component::Path") but ruby 1.9.3
|
21
|
-
# doesn't. And neither has a version that can return nil rather than
|
22
|
-
# raising a NameError
|
23
|
-
def get_const(mod, name)
|
24
|
-
name.to_s.split('::').map(&:to_sym).each do |component|
|
25
|
-
begin
|
26
|
-
mod = mod.const_get(component, false)
|
27
|
-
rescue NameError
|
28
|
-
return nil
|
29
|
-
end
|
30
|
-
end
|
31
|
-
mod
|
32
|
-
end
|
33
|
-
|
34
|
-
def get_modules(parent, opts={})
|
35
|
-
opts = opts.keyword_args(:prefix, :match, :reject, :recursive, :respond_to, :and_self)
|
36
|
-
parent = get_const(parent, opts.prefix) if opts.prefix
|
37
|
-
return [] unless parent && parent.is_a?(Module)
|
38
|
-
modules = []
|
39
|
-
modules << parent if opts.and_self
|
40
|
-
modules += parent.constants.reject{|c| parent.autoload? c}.map{|c| parent.const_get(c)}.select(&it.is_a?(Module))
|
41
|
-
modules.reject! &it.to_s =~ opts.reject if opts.reject
|
42
|
-
modules += modules.flat_map { |mod| get_modules(mod, reject: opts.reject, recursive: true) } if opts.recursive
|
43
|
-
modules.select! &it.to_s =~ opts.match if opts.match
|
44
|
-
modules.select! &it.respond_to?(opts.respond_to) if opts.respond_to
|
45
|
-
modules
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|