schema_monkey 0.3.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d7581fe8672d2cf1ef4115bbfc10955a0a2e4c1
4
- data.tar.gz: 5d05d31605887dfcd26fb82f546b94adc73a8d9e
3
+ metadata.gz: 2d6e7678f91c469b42eeac242d5095283edcaf4a
4
+ data.tar.gz: 11e38e5ba367ced242293615096012c752150cd3
5
5
  SHA512:
6
- metadata.gz: 210fc83e5898dec122f9a20441e04a4386eaf5fd68dca7b0ff20d3c69641bee79363f945dd6bc247101468bc71a3be0df5dfcb6da89a4045383e2ab093014b6a
7
- data.tar.gz: f3ca1407f7c2f19cc329eb382ccab6279e5403b1016855f323cb8007a55058b73aa584957d23e8245c0196be4f49d22ebf39456effbc207c708b498d01ade76a
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
- # schema_monkey
6
+ # SchemaMonkey
7
7
 
8
- TODO: Write a gem description
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 application's Gemfile
21
+ In your gem's `gemspec`, specify a dependency:
15
22
 
16
23
  ```ruby
17
- gem "schema_monkey"
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
- schema_monkey is tested on
31
+ SchemaMonkey is tested on:
22
32
 
23
- [//]: # SCHEMA_DEV: MATRIX - begin
24
- [//]: # These lines are auto-generated by schema_dev based on schema_dev.yml
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
- [//]: # SCHEMA_DEV: MATRIX - end
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
- * **schema_dev**: schema_monkey uses [schema_dev](https://github.com/SchemaPlus/schema_dev) to
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 :Postgresql
15
- when 'SQLite' then :Sqlite3
14
+ when 'PostgreSQL', 'PostGIS' then :PostgreSQL
15
+ when 'SQLite' then :SQLite3
16
16
  end
17
17
 
18
- SchemaMonkey.include_adapters(self.class, dbm)
19
- SchemaMonkey.insert_middleware(dbm)
18
+ SchemaMonkey.insert(dbm: dbm)
20
19
  end
21
20
 
22
21
  module SchemaCreation
@@ -1,7 +1,7 @@
1
1
  module SchemaMonkey
2
2
  module ActiveRecord
3
3
  module ConnectionAdapters
4
- module MysqlAdapter
4
+ module Mysql2Adapter
5
5
 
6
6
  def self.included(base)
7
7
  base.class_eval do
@@ -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
@@ -1,3 +1,3 @@
1
1
  module SchemaMonkey
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.0"
3
3
  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/module_support"
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
- extend SchemaMonkey::ModuleSupport
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 :MysqlAdapter, 'schema_monkey/active_record/connection_adapters/mysql_adapter'
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
- registered_modules << mod
32
+ clients << Client.new(mod)
38
33
  end
39
34
 
40
- def self.registered_modules
41
- @registered_modules ||= [self]
35
+ def self.clients
36
+ @clients ||= [Client.new(self)]
42
37
  end
43
38
 
44
- def self.include_adapters(base, dbm)
45
- registered_modules.each do |mod|
46
- include_if_defined(base, mod, "ActiveRecord::ConnectionAdapters::#{dbm}Adapter")
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.3.2
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-26 00:00:00.000000000 Z
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/mysql_adapter.rb
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/module_support.rb
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