schema_plus_core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +18 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +434 -0
  7. data/Rakefile +9 -0
  8. data/gemfiles/Gemfile.base +4 -0
  9. data/gemfiles/activerecord-4.2/Gemfile.base +3 -0
  10. data/gemfiles/activerecord-4.2/Gemfile.mysql2 +10 -0
  11. data/gemfiles/activerecord-4.2/Gemfile.postgresql +10 -0
  12. data/gemfiles/activerecord-4.2/Gemfile.sqlite3 +10 -0
  13. data/lib/schema_plus/core.rb +27 -0
  14. data/lib/schema_plus/core/active_record/base.rb +22 -0
  15. data/lib/schema_plus/core/active_record/connection_adapters/abstract_adapter.rb +43 -0
  16. data/lib/schema_plus/core/active_record/connection_adapters/abstract_mysql_adapter.rb +17 -0
  17. data/lib/schema_plus/core/active_record/connection_adapters/mysql2_adapter.rb +64 -0
  18. data/lib/schema_plus/core/active_record/connection_adapters/postgresql_adapter.rb +46 -0
  19. data/lib/schema_plus/core/active_record/connection_adapters/sqlite3_adapter.rb +43 -0
  20. data/lib/schema_plus/core/active_record/connection_adapters/table_definition.rb +36 -0
  21. data/lib/schema_plus/core/active_record/migration/command_recorder.rb +16 -0
  22. data/lib/schema_plus/core/active_record/schema_dumper.rb +102 -0
  23. data/lib/schema_plus/core/middleware.rb +71 -0
  24. data/lib/schema_plus/core/schema_dump.rb +123 -0
  25. data/lib/schema_plus/core/sql_struct.rb +30 -0
  26. data/lib/schema_plus/core/version.rb +5 -0
  27. data/schema_dev.yml +8 -0
  28. data/schema_plus_core.gemspec +31 -0
  29. data/spec/dumper_spec.rb +53 -0
  30. data/spec/middleware_spec.rb +175 -0
  31. data/spec/spec_helper.rb +35 -0
  32. data/spec/sql_struct_spec.rb +29 -0
  33. data/spec/support/enableable.rb +30 -0
  34. data/spec/support/test_dumper.rb +42 -0
  35. data/spec/support/test_reporter.rb +57 -0
  36. metadata +212 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 14d4c29d30666d3262db03f9eb185ad19183aa0f
4
+ data.tar.gz: 84a5af526cb51da858aee93e203ec6cb4c788fdb
5
+ SHA512:
6
+ metadata.gz: a333726087546218a3bad3b0b85d4396fb04aad22080636c1bce21a009a1c1e022729092a7044e82571788f1fe4bf9375c72b60d2a08a32fef9a8c39afc354ee
7
+ data.tar.gz: 644d807294fd2d3b863fb4ef6ff0588cad8ab15b82503be681b3ff4078e4bfa9edf37f167de7dab123811bfa56f90a95e2cd542d138d65a7888e9d8599314fd4
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /coverage
2
+ /tmp
3
+ /pkg
4
+ /Gemfile.local
5
+
6
+ *.lock
7
+ *.log
8
+ *.sqlite3
9
+ !gemfiles/**/*.sqlite3
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ # This file was auto-generated by the schema_dev tool, based on the data in
2
+ # ./schema_dev.yml
3
+ # Please do not edit this file; any changes will be overwritten next time
4
+ # schema_dev gets run.
5
+ ---
6
+ sudo: false
7
+ rvm:
8
+ - 2.1.5
9
+ gemfile:
10
+ - gemfiles/activerecord-4.2/Gemfile.mysql2
11
+ - gemfiles/activerecord-4.2/Gemfile.postgresql
12
+ - gemfiles/activerecord-4.2/Gemfile.sqlite3
13
+ env: POSTGRESQL_DB_USER=postgres MYSQL_DB_USER=travis
14
+ addons:
15
+ postgresql: '9.3'
16
+ before_script: bundle exec rake create_databases
17
+ after_script: bundle exec rake drop_databases
18
+ script: bundle exec rake travis
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ File.exist?(gemfile_local = File.expand_path('../Gemfile.local', __FILE__)) and eval File.read(gemfile_local), binding, gemfile_local
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 ronen barzel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,434 @@
1
+ [![Gem Version](https://badge.fury.io/rb/schema_plus_core.svg)](http://badge.fury.io/rb/schema_plus_core)
2
+ [![Build Status](https://secure.travis-ci.org/SchemaPlus/schema_plus_core.svg)](http://travis-ci.org/SchemaPlus/schema_plus_core)
3
+ [![Coverage Status](https://img.shields.io/coveralls/SchemaPlus/schema_plus_core.svg)](https://coveralls.io/r/SchemaPlus/schema_plus_core)
4
+ [![Dependency Status](https://gemnasium.com/lomba/schema_plus_core.svg)](https://gemnasium.com/SchemaPlus/schema_plus_core)
5
+
6
+ # SchemaPlus::Core
7
+
8
+ SchemaPlus::Core creates an internal extension API to ActiveRecord. The idea is that:
9
+
10
+ * ShemaPlus::Core does the monkey-patching so clients don't have to know too much about the internal of ActiveRecord.
11
+
12
+ * SchemaPlus::Core's extension API is consistent across the various connection adapters, so clients don't have to figure out how to extend each connection adapter independently.
13
+
14
+ * SchemPlus::Core's extension API intends to remain reasonably stable even as ActiveRecord changes.
15
+
16
+ By itself, SchemaPlus::Core does not change any behavior or add any external features to ActiveRecord. It just makes the API available to clients.
17
+
18
+ SchemaPlus::Core is a client of [schema_monkey](https://github.com/SchemaPlus/schema_monkey), using [modware](https://github.com/ronen/modware) to define middleware callback stacks.
19
+
20
+
21
+ ## Compatibility
22
+
23
+ SchemaPlus::Core is tested on:
24
+
25
+ <!-- SCHEMA_DEV: MATRIX - begin -->
26
+ <!-- These lines are auto-generated by schema_dev based on schema_dev.yml -->
27
+ * ruby **2.1.5** with activerecord **4.2**, using **mysql2**, **sqlite3** or **postgresql**
28
+
29
+ <!-- SCHEMA_DEV: MATRIX - end -->
30
+
31
+
32
+ ## Installation
33
+
34
+ As usual:
35
+
36
+ <!-- SCHEMA_DEV: TEMPLATE INSTALLATION - begin -->
37
+ <!-- These lines are auto-inserted from a schema_dev template -->
38
+ As usual:
39
+
40
+ ```ruby
41
+ gem "schema_plus_core" # in a Gemfile
42
+ gem.add_dependency "schema_plus_core" # in a .gemspec
43
+ ```
44
+
45
+ To use with a rails app, also include
46
+
47
+ ```ruby
48
+ gem "schema_monkey_rails"
49
+ ```
50
+
51
+ which creates a Railtie to that will insert SchemaPlus::Core appropriately into the rails stack. To use with Padrino, see [schema_monkey_padrino](https://github.com/SchemaPlus/schema_monkey_padrino).
52
+
53
+ <!-- SCHEMA_DEV: TEMPLATE INSTALLATION - end -->
54
+
55
+
56
+ ## Usage
57
+
58
+ The API is in the form of a collection of [modware](https://github.com/ronen/modware) middleware callback stacks. A client of the API uses [schema_monkey](https://github.com/SchemaPlus/schema_monkey) to insert middleware modules into the stacks. As per [schema_monkey](https://github.com/SchemaPlus/schema_monkey), the typical module structure looks like:
59
+
60
+ ```ruby
61
+ require "schema_plus/core"
62
+
63
+ module MyClient
64
+ module Middleware
65
+ #
66
+ # Middleware modules to insert in SchemaPlus::Core API stacks
67
+ #
68
+ end
69
+ module ActiveRecord
70
+ #
71
+ # direct ActiveRecord enhancements, should your client need any.
72
+ #
73
+ end
74
+ end
75
+
76
+ SchemaMonkey.register MyClient
77
+ ```
78
+
79
+ For example, a client could use the `Migration::Index` stack to automatically make an index unique if any column starts with 'u':
80
+
81
+ ```ruby
82
+ require "schema_plus/core"
83
+
84
+ module AutoUniquify
85
+ module Middleware
86
+
87
+ module Migration
88
+ module Index
89
+ def before(env)
90
+ env.options[:unique] = true if env.column_names.grep(/^u/).any?
91
+ end
92
+ end
93
+ end
94
+
95
+ end
96
+ end
97
+
98
+ SchemaMonkey.register AutoUniquify
99
+ ```
100
+
101
+ Ideally most clients will not need to define direct ActiveRecord enhancements, other than perhaps to create new methods on public classes. If you have a client that needs more complex monkey-patching, that could be a sign that SchemaPlus::Core's API is missing some useful functionality -- consider submitting a PR to SchemaPlus::Core add it!
102
+
103
+ ## API details
104
+
105
+ For organizational clarity, the SchemaPlus::Core stacks are grouped into modules based on broad categories. In the Env field tables below, Initialized
106
+
107
+ ### Schema
108
+
109
+ Stacks for general queries pertaining to the entire database schema:
110
+
111
+ * `Schema::Indexes`
112
+
113
+ Wrapper around the `connection.indexes(table_name)` method. Env contains:
114
+
115
+ Env Field | Description | Initial value
116
+ --- | --- | ---
117
+ `:index_definitions` | The result of the lookup | `[]`
118
+ `:connection` | The current ActiveRecord connection | *context*
119
+ `:table_name` | The name of the table to query | *arg*
120
+ `:query_name` | Label sometimes used by ActiveRecord logging | *arg*
121
+
122
+ The base implementation appends its results to `env.index_definitions`
123
+
124
+ * `Schema::Tables`
125
+
126
+ Wrapper around the `connection.tables()` method. Env contains:
127
+
128
+ Env Field | Description | Initialized
129
+ --- | --- | ---
130
+ `:tables` | The result of the lookup | `[]`
131
+ `:connection` | The current ActiveRecord connection | *context*
132
+ `:table_name` | (SQlite3 only) | *arg*
133
+ `:database` | (Mysql only) | *arg*
134
+ `:like` | (Mysql only) | *arg*
135
+ `:query_name` | Label sometimes used by ActiveRecord logging | *arg*
136
+
137
+ The base implementation appends its results to `env.tables`
138
+
139
+ ### Model
140
+
141
+ Stacks for class methods on ActiveRecord models.
142
+
143
+ * `Model::Columns`
144
+
145
+ Wrapper around the `Model.columns` query
146
+
147
+ Env Field | Description | Initialized
148
+ --- | --- | ---
149
+ `:columns` | The resulting Column objects | `[]`
150
+ `:model` | The model Class being queried | *context*
151
+
152
+ The base implementation appends its results to `env.columns`
153
+
154
+ * `Model::ResetColumnInformation`
155
+
156
+ Wrapper around the `Model.reset_column_information` method
157
+
158
+ Env Field | Description | Initialized
159
+ --- | --- | ---
160
+ `:model` | The model Class being reset | *context*
161
+
162
+ The base implementation performs the reset.
163
+
164
+ ### Migration
165
+
166
+ Stacks for operations that define database objects in migrations and schema loading.
167
+
168
+ * `Migration::Column`
169
+
170
+ Callback stack for various ways to define or modify a column.
171
+
172
+ Env Field | Description | Initialized
173
+ --- | --- | ---
174
+ `:caller` | The ActiveRecord instance responsible for performing the action | *context*
175
+ `:operation` | One of `:add`, `:change`, `:define`, `:record` | *context*
176
+ `:table_name` | The name of the table | *arg*
177
+ `:column_name` | The name of the column | *arg*
178
+ `:type` | The ActiveRecord column type (`:integer`, `:datetime`, etc.) | *arg*
179
+ `:options` | The column options | *arg*, default `{}`
180
+
181
+ The base implementation performs the column operation. No value is returned.
182
+
183
+ Notes:
184
+
185
+ 1. The `:operation` field has the following meanings:
186
+
187
+ * `:add` - The column will be added immediately (`Migration#add_column`)
188
+ * `:change` - The column will be changed immediately (`Migration#change_column`)
189
+ * `:define` - The column will be added to table definition, which will be emitted later
190
+ * `:record` - The column info will be added to a migration command recorder, for later playback in reverse by `Migration#down`
191
+
192
+ 2. In the case of a table definition using `t.references` or `t.belongs_to`, the `:type` field will be set to `:reference` and the `:column_name` will include the `"_id"` suffix
193
+
194
+ 3. ActiveRecord's base implementation may make nested calls to column creation. For example: References result in a nested call to create an integer column; Polymorphic references nest calls to create two columns; Sqlite3 implements `:change` by a nested call to a new table definition. SchemaPlus::Core doesn't attempt to normalize or suppress these; each such nested call will result in its own `Migration::Column` stack execution.
195
+
196
+ * `Migration::Index`
197
+
198
+ Callback stack for various ways to define an index column.
199
+
200
+ Env Field | Description | Initialized
201
+ --- | --- | ---
202
+ `:caller` | The ActiveRecord instance responsible for performing the action | *context*
203
+ `:operation` | `:add` or `:define` | *context*
204
+ `:table_name` | The name of the table | *arg*
205
+ `:column_names` | The names of the columns | *arg*
206
+ `:options` | The index options | *arg*, default `{}`
207
+
208
+ The base implementation performs the index creation operation. No value is returned.
209
+
210
+ Notes:
211
+
212
+ 1. The `:operation` field has the following meanings:
213
+
214
+ * `:add` - The index will be added immediately (`Migration#add_index`)
215
+ * `:define` - The index will be added to a table definition, which will be emitted later.
216
+
217
+ ### Sql
218
+
219
+ Stacks for internal operations that generate SQL.
220
+
221
+ * `Sql::ColumnOptions`
222
+
223
+ Callback stack around generation of the SQL options for a column definition.
224
+
225
+ Env Field | Description | Initialized
226
+ --- | --- | ---
227
+ `:sql` | The resulting SQL | `""`
228
+ `:caller` | The ActiveRecord::SchemaCreation instance | *context*
229
+ `:connection` | The current ActiveRecord connection | *context* |
230
+ `:column` | The column definition object | *context* |
231
+ `:options` | The column definition options | *context* |
232
+
233
+ The base implementation appends the options SQL to `env.sql`
234
+
235
+ * `Sql::IndexComponents`
236
+
237
+ Callback stack around generation of the SQL for an index definition.
238
+
239
+ Env Field | Description | Initialized
240
+ --- | --- | ---
241
+ `:sql` | The resulting SQL components, in a struct with fields `:name`, `:type`, `:columns`, `:options`, `:algorithm`, `:using` | *empty struct*
242
+ `:connection` | The current ActiveRecord connection | *context*
243
+ `:table_name` | The name of the table | *context*
244
+ `:column_names` | The names of the columns | *context*
245
+ `:options` | The index options | *context*
246
+
247
+ The base implementation *overwrites* the contents of `env.sql`
248
+
249
+ Notes:
250
+
251
+ 1. SQLite3 ignores the `:type`, `:algoritm`, and `:using` fields of `env.sql`
252
+
253
+ * `Sql::Table`
254
+
255
+ Callback stack around generation of the SQL for a table
256
+
257
+ Env Field | Description | Initialized
258
+ --- | --- | ---
259
+ `:sql` | The resulting SQL components in a struct with fields `:command`, `:name`, `:body`, `:options`, `:quotechar` | *empty struct*
260
+ `:caller` | The ActiveRecord::SchemaCreation instance | *context*
261
+ `:connection` | The current ActiveRecord connection | *context*
262
+ `:table_definition` | The TableDefinition object for the table | *context*
263
+
264
+ The base implementation *overwrites* the contents of `env.sql`
265
+
266
+ Notes:
267
+
268
+ 1. `env.sql.command` contains the index creation command such as `CREATE TABLE` or `CREATE TEMPORARY TABLE`
269
+ 2. `env.sql.quotechar` contains the quote character ', ", or \` to wrap `env.sql.name` in.
270
+
271
+
272
+ ### Query
273
+
274
+ Stacks around low-level query execution
275
+
276
+ * `Query::Exec`
277
+
278
+ Callback stack wraps the emission of sql to the underlying dbms gem.
279
+
280
+ Env Field | Description | Initialized
281
+ --- | --- | ---
282
+ `:result` | The result of the database query | *unset*
283
+ `:caller` | The ActiveRecord::SchemaCreation instance | *context*
284
+ `:sql` | The SQL string | *context*
285
+ `:binds` | Values to substitute into the SQL string
286
+ `:query_name` | Label sometimes used by ActiveRecord logging | *arg*
287
+
288
+
289
+ ### Dumper
290
+
291
+ SchemaPlus::Core provides a state object and of callbacks to various phases of the schema dumping process. The dumping process fleshes out the state object-- nothing is actually written to the dump file until after the state is fleshed out.
292
+
293
+ #### Schema Dump state
294
+
295
+ * `Class SchemaPlus::Core::SchemaDump`
296
+
297
+ An instance of `SchemaPlus::Core::SchemaDump` gets passed to each of the callback stacks; the dump gets built up by fleshing out its contents. `SchemaDump` has the following fields and methods:
298
+
299
+ * `dump.initial = []` - an array of strings containing statements to start the schema with, such as `enable_extension 'hstore'`
300
+ * `dump.data = OpenStruct.new` - a place for clients to store arbitrary data between phases
301
+ * `dump.tables = {}` - a hash mapping table names to SchemaDump::Table objects
302
+ * `dump.final = []` - an array of strings containing statements to end the schema with.
303
+ * `dump.depends(table_name, [prerequisite_table_names])` - call this method to ensure that the definition of `table_name` won't be output before its prerequisites.
304
+
305
+ * `Class SchemaPlus::Core::SchemaDump::Table`
306
+
307
+ Each table in the dump has its contents in a SchemaDump::Table object, with these fields:
308
+
309
+ * `table.name` - the table name
310
+ * `table.pname` - table as actually used in SQL (without any prefixes or suffixes)
311
+ * `table.options` - a string containing the options to `Migration.create_table`
312
+ * `table.columns = []` - an array of SchemaDump::Table::Column objects
313
+ * `table.indexes = []` - an array of SchemaDump::Table::Index objects
314
+ * `table.statements` - a collection of statements to include in the table definition; each is a string that should start with `"t."`
315
+ * `table.trailer` - a collection of migration statements to include immediately outside the table definition. Each is a string
316
+
317
+ * `Class SchemaPlus::Core::SchemaDump::Table::Column`
318
+
319
+ Each column in a table has its contents in a SchemaDump::Table::Column object, with these fields and methods:
320
+
321
+ * `column.name` - the column name
322
+ * `column.type` - the column type (i.e., what comes after `"t."`)
323
+ * `column.options` - an optional string containing the options for the column
324
+ * `column.comments` - an optional string containing a comment to put in the schema dump
325
+ * `column.add_option(option)` - adds an option to the current string, separating with a "," if the current set isn't blank
326
+ * `column.add_comment(comment)` - adds an option to the current string, separating with a ";" if the current string isn't blank
327
+
328
+ * `Class SchemaPlus::Core::SchemaDump::Table::Index`
329
+
330
+ Each index in a table has its contents in a SchemaDump::Table::Index object, with these fields and methods:
331
+
332
+ * `index.name` - the index name
333
+ * `index.columns` - the columns that are in the index
334
+ * `index.options` - an optional string containing the options for the index
335
+ * `index.add_option(option)` - adds an option to the current string, separating with a "," if the current set isn't blank
336
+
337
+ #### Schema Dump Middleware stacks
338
+
339
+ * `Dumper::Initial`
340
+
341
+ Callback stack wraps the creation of initial statements for the dump.
342
+
343
+ Env Field | Description | Initialized
344
+ --- | --- | ---
345
+ `:initial` | The initial statements | []
346
+ `:dump` | The SchemaDump object | *context*
347
+ `:dumper` | The current ActiveRecord::SchemaDumper instance| *context*
348
+ `:connection` | The current ActiveRecord connection | *context*
349
+
350
+ The base method appends initial statements to `env.initial`.
351
+
352
+ * `Dumper::Tables`
353
+
354
+ Callback stack wraps the dumping of all tables.
355
+
356
+ Env Field | Description | Initialized
357
+ --- | --- | ---
358
+ `:dump` | The SchemaDump object | *context*
359
+ `:dumper` | The current ActiveRecord::SchemaDumper instance| *context*
360
+ `:connection` | The current ActiveRecord connection | *context*
361
+
362
+ The base method iterates through all tables, dumping each.
363
+
364
+
365
+ * `Dumper::Table`
366
+
367
+ Callback stack wraps the dumping of each table
368
+
369
+ Env Field | Description | Initialized
370
+ --- | --- | ---
371
+ `:table` | A SchemaDump::Table object| `table.name` *only*
372
+ `:dump` | The SchemaDump object | *context*
373
+ `:dumper` | The current ActiveRecord::SchemaDumper instance| *context*
374
+ `:connection` | The current ActiveRecord connection | *context*
375
+
376
+ The base method iterates through all columns and indexes of the table, and *overwrites* the contents of `table`,
377
+
378
+ Notes:
379
+
380
+ 1. When the stack is called, `env.dump.tables[env.table.name]` contains the `env.table` object.
381
+
382
+ * `Dumper::Indexes`
383
+
384
+ Callback stack wraps the dumping of the indexes of a table
385
+
386
+ Env Field | Description | Initialized
387
+ --- | --- | ---
388
+ `:table` | A SchemaDump::Table object| *context*
389
+ `:dump` | The SchemaDump object | *context*
390
+ `:dumper` | The current ActiveRecord::SchemaDumper instance| *context*
391
+ `:connection` | The current ActiveRecord connection | *context*
392
+
393
+ The base method appends the collection of SchemaDump::Table::Index objects to `env.table.indexes`
394
+
395
+
396
+ ## History
397
+
398
+ * 0.1.0 Initial release
399
+
400
+ ## Development & Testing
401
+
402
+ Are you interested in contributing to SchemaPlus::Core? Thanks! Please follow the standard protocol: fork, feature branch, develop, push, and issue pull request.
403
+
404
+ Some things to know about to help you develop and test:
405
+
406
+ <!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_DEV - begin -->
407
+ <!-- These lines are auto-inserted from a schema_dev template -->
408
+ * **schema_dev**: SchemaPlus::Core uses [schema_dev](https://github.com/SchemaPlus/schema_dev) to
409
+ facilitate running rspec tests on the matrix of ruby, activerecord, and database
410
+ versions that the gem supports, both locally and on
411
+ [travis-ci](http://travis-ci.org/SchemaPlus/schema_plus_core)
412
+
413
+ To to run rspec locally on the full matrix, do:
414
+
415
+ $ schema_dev bundle install
416
+ $ schema_dev rspec
417
+
418
+ You can also run on just one configuration at a time; For info, see `schema_dev --help` or the [schema_dev](https://github.com/SchemaPlus/schema_dev) README.
419
+
420
+ The matrix of configurations is specified in `schema_dev.yml` in
421
+ the project root.
422
+
423
+
424
+ <!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_DEV - end -->
425
+
426
+ <!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_MONKEY - begin -->
427
+ <!-- These lines are auto-inserted from a schema_dev template -->
428
+ * **schema_monkey**: SchemaPlus::Core is implemented as a
429
+ [schema_monkey](https://github.com/SchemaPlus/schema_monkey) client,
430
+ using [schema_monkey](https://github.com/SchemaPlus/schema_monkey)'s
431
+ convention-based protocols for extending ActiveRecord and using middleware stacks.
432
+ For more information see [schema_monkey](https://github.com/SchemaPlus/schema_monkey)'s README.
433
+
434
+ <!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_MONKEY - end -->