schema_plus_core 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.
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 -->