object_id_gem 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +44 -0
- data/CHANGES.md +23 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +225 -0
- data/Rakefile +6 -0
- data/lib/objectid_columns.rb +127 -0
- data/lib/objectid_columns/active_record/base.rb +33 -0
- data/lib/objectid_columns/active_record/relation.rb +40 -0
- data/lib/objectid_columns/arel/visitors/to_sql.rb +88 -0
- data/lib/objectid_columns/dynamic_methods_module.rb +127 -0
- data/lib/objectid_columns/extensions.rb +41 -0
- data/lib/objectid_columns/has_objectid_columns.rb +47 -0
- data/lib/objectid_columns/objectid_columns_manager.rb +451 -0
- data/lib/objectid_columns/version.rb +4 -0
- data/object_id_gem.gemspec +71 -0
- data/spec/objectid_columns/helpers/database_helper.rb +178 -0
- data/spec/objectid_columns/helpers/system_helpers.rb +92 -0
- data/spec/objectid_columns/system/basic_system_spec.rb +600 -0
- data/spec/objectid_columns/system/extensions_spec.rb +69 -0
- metadata +194 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 934ad550ee4c80cdbcd2328e27d88b80d9586f7faf3e780c7fe27ace3e0cb90c
|
4
|
+
data.tar.gz: 90254785189ba6af205e0712397047bb19817ee5d8f5cdb42eb59c8967e1e612
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4492abbc45cf0ec58722c0cdb261dd48ba60c7481f9e61e0a3ff06d263b275201c8386f7a4af5a19f576e9e661f23e6536eae1eac696a426cdb09126d033c0a2
|
7
|
+
data.tar.gz: a3f4425ec8ca9eb2bdab29eed7284cfbef46fac0c8274466a0dedc9f124e7ae69c212f875a8ab697ffedbd44a4c7e1e393db115d6454ecf6d2f0bb24787a3d21
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
rvm:
|
2
|
+
- "1.8.7"
|
3
|
+
- "1.9.3"
|
4
|
+
- "2.0.0"
|
5
|
+
- "2.1.3"
|
6
|
+
- "jruby-1.7.16"
|
7
|
+
env:
|
8
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=3.0.20 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=mysql
|
9
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=3.0.20 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=postgres
|
10
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=3.0.20 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=sqlite
|
11
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=3.1.12 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=mysql
|
12
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=3.1.12 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=postgres
|
13
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=3.1.12 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=sqlite
|
14
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=3.2.19 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=mysql
|
15
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=3.2.19 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=postgres
|
16
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=3.2.19 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=sqlite
|
17
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=4.0.10 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=mysql
|
18
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=4.0.10 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=postgres
|
19
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=4.0.10 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=sqlite
|
20
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=4.1.6 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=mysql
|
21
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=4.1.6 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=postgres
|
22
|
+
- OBJECTID_COLUMNS_AR_TEST_VERSION=4.1.6 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=sqlite
|
23
|
+
before_script:
|
24
|
+
- mysql -e 'create database myapp_test;'
|
25
|
+
- psql -c 'create database myapp_test;' -U postgres
|
26
|
+
matrix:
|
27
|
+
exclude:
|
28
|
+
# ActiveRecord 4.x doesn't support Ruby 1.8.7
|
29
|
+
- rvm: 1.8.7
|
30
|
+
env: OBJECTID_COLUMNS_AR_TEST_VERSION=4.0.10 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=mysql
|
31
|
+
- rvm: 1.8.7
|
32
|
+
env: OBJECTID_COLUMNS_AR_TEST_VERSION=4.0.10 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=postgres
|
33
|
+
- rvm: 1.8.7
|
34
|
+
env: OBJECTID_COLUMNS_AR_TEST_VERSION=4.0.10 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=sqlite
|
35
|
+
- rvm: 1.8.7
|
36
|
+
env: OBJECTID_COLUMNS_AR_TEST_VERSION=4.1.6 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=mysql
|
37
|
+
- rvm: 1.8.7
|
38
|
+
env: OBJECTID_COLUMNS_AR_TEST_VERSION=4.1.6 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=postgres
|
39
|
+
- rvm: 1.8.7
|
40
|
+
env: OBJECTID_COLUMNS_AR_TEST_VERSION=4.1.6 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=sqlite
|
41
|
+
allow_failures:
|
42
|
+
- env: OBJECTID_COLUMNS_AR_TEST_VERSION=3.1.12 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=sqlite
|
43
|
+
- rvm: 1.8.7
|
44
|
+
env: OBJECTID_COLUMNS_AR_TEST_VERSION=3.2.19 OBJECTID_COLUMNS_TRAVIS_CI_DATABASE_TYPE=sqlite
|
data/CHANGES.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Change History for ObjectidColumns
|
2
|
+
|
3
|
+
### Version 1.0.5: October 10, 2014
|
4
|
+
|
5
|
+
* Fixed an issue where using `objectid_columns` on a parent model class that was then inherited from (via STI) could cause an internal exception.
|
6
|
+
* Updated Travis testing specifications to the latest versions of Ruby/JRuby and ActiveRecord.
|
7
|
+
|
8
|
+
### Version 1.0.3: June 11, 2014
|
9
|
+
|
10
|
+
* Fixed an issue where, if you tried to declare `has_objectid_primary_key` on a table that didn't exist (usually meaning it just hadn't been migrated into existence yet), you'd get an error. Now, we ignore this declaration.
|
11
|
+
|
12
|
+
### Version 1.0.2: April 14, 2014
|
13
|
+
|
14
|
+
* Fixed an issue where, if you tried to pass an ObjectID instance in a `where` clause for a column that didn't exist or wasn't on a table that declared any ObjectID columns, you could get an error from deep down in `ObjectidColumns`. (Now, you'll still get an error, but it will be the ActiveRecord error you expect, instead.)
|
15
|
+
* Rails 4.1 support.
|
16
|
+
* Bumped Travis version matrix to the latest point-releases of Rails 3.2 and 4.0.
|
17
|
+
|
18
|
+
### Version 1.0.1: March 7, 2014
|
19
|
+
|
20
|
+
* Compatibility with the [`composite_primary_keys`](https://github.com/composite-primary-keys/composite_primary_keys)
|
21
|
+
gem, so that you can use object-ID columns as part of a composite primary key.
|
22
|
+
* Fixed an issue where you could not save an ActiveRecord model that had an ObjectId column as its primary key.
|
23
|
+
Implemented this by teaching Arel how to deal with BSON ObjectIds, which should have broader benefits, too.
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Swiftype, Inc.
|
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,225 @@
|
|
1
|
+
# ObjectidColumns
|
2
|
+
|
3
|
+
Transparely store MongoDB [ObjectId](http://docs.mongodb.org/manual/reference/object-id/) values (the primary key of
|
4
|
+
all MongoDB tables) in ActiveRecord objects in a relational database. You can store values as:
|
5
|
+
|
6
|
+
* A binary column (`BINARY`, `VARBINARY`, `BLOB`, _etc._) of length at least 12; the ObjectId value will be stored as
|
7
|
+
pure binary data — the most efficient format. (Most databases, including MySQL and PostgreSQL, can index this
|
8
|
+
column and work with it just like a String; it just takes half as much space (!).)
|
9
|
+
* A String column (`CHAR`, `VARCHAR`, _etc._) of length at least 24; the ObjectId value will be stored as a hexadecimal
|
10
|
+
string.
|
11
|
+
|
12
|
+
(Note that it is not possible to store binary data transparently in a String column, because not all byte sequences
|
13
|
+
are valid binary data in all possible character sets.)
|
14
|
+
|
15
|
+
Once declared, an ObjectId column will return instances of either `BSON::ObjectId` or `Moped::BSON::ObjectId`
|
16
|
+
(depending on which one you have loaded) when you access an attribute of a model that you've declared as an ObjectId
|
17
|
+
column. It will accept a String (in either hex or binary formats) or an instance of either of those classes when
|
18
|
+
assigning to the column.
|
19
|
+
|
20
|
+
`ObjectidColumns` also allows you to use ObjectId values as primary keys; it will assign them to new records by
|
21
|
+
default, and make sure `find(id)` accepts them.
|
22
|
+
|
23
|
+
This gem requires either the `moped` gem (which defines `Moped::BSON::ObjectId`) or the `bson` gem (which defines
|
24
|
+
`BSON::ObjectId`) for the actual ObjectId classes it uses. It declares an official dependency on neither, because we
|
25
|
+
want to allow you to use either one. It will accept either one when assigning ObjectIds; it will return ObjectIds as
|
26
|
+
whichever one you have loaded, (currently) preferring `BSON::ObjectId` if you have both.
|
27
|
+
|
28
|
+
ObjectidColumns supports Ruby 1.8.7, 1.9.3, 2.0.0, and 2.1.0, plus JRuby 1.7.9; it supports ActiveRecord 3.0.20,
|
29
|
+
3.1.12, 3.2.17, 4.0.4, and 4.1.0. It supports SQLite 3.x, MySQL 5.x, and PostgreSQL 8.x. (These are just the
|
30
|
+
versions it's tested against; while it will not work with ActiveRecord 2.x, it is otherwise highly unlikely to
|
31
|
+
be sensitive to exact ActiveRecord or Ruby versions, or type of RDBMS, and generally should work with most
|
32
|
+
combinations.)
|
33
|
+
|
34
|
+
*Note*: If you use SQLite3 with ActiveRecord 3.1.x on MRI (_i.e._, not JRuby), or SQLite3 with ActiveRecord 3.2.x on
|
35
|
+
MRI 1.8.7, there is a bug in the SQLite3-ActiveRecord integration that causes ObjectId primary keys to not work. (They
|
36
|
+
are inserted properly, but searching by primary key fails.) Needless to say, these are very rare combinations of
|
37
|
+
software to be running in production, but it's worth noting. (ActiveRecord 3.0.x or 3.2.x work fine with SQLite3 on
|
38
|
+
MRI; SQLite3 with ActiveRecord 3.2.x works fine on MRI >= 1.9.3; and MySQL and PostgreSQL work perfectly everywhere.)
|
39
|
+
|
40
|
+
Current build status: ![Current Build Status](https://api.travis-ci.org/swiftype/objectid_columns.png?branch=master)
|
41
|
+
|
42
|
+
Brought to you by the folks at [Swiftype](https://www.swiftype.com). First version written by [Andrew Geweke](https://www.github.com/ageweke).
|
43
|
+
|
44
|
+
## Installation
|
45
|
+
|
46
|
+
First, make sure you have either `BSON::ObjectId` or `Moped::BSON::ObjectId` defined in your Rails environment;
|
47
|
+
these are the two ObjectId classes that `ObjectidColumns` can work with. If you don't have either defined, add one
|
48
|
+
of these lines to your `Gemfile` (loading both is fine, but unnecessary):
|
49
|
+
|
50
|
+
gem 'bson'
|
51
|
+
OR gem 'moped'
|
52
|
+
|
53
|
+
Now, add this line to your application's Gemfile:
|
54
|
+
|
55
|
+
gem 'objectid_columns'
|
56
|
+
|
57
|
+
And then execute:
|
58
|
+
|
59
|
+
$ bundle
|
60
|
+
|
61
|
+
Or install it yourself as:
|
62
|
+
|
63
|
+
$ gem install objectid_columns
|
64
|
+
|
65
|
+
## Usage
|
66
|
+
|
67
|
+
If you name your object-ID columns with `_oid` at the end, simply do this:
|
68
|
+
|
69
|
+
class MyModel < ActiveRecord::Base
|
70
|
+
has_objectid_columns
|
71
|
+
end
|
72
|
+
|
73
|
+
This will automatically find any columns that end in `_oid` and make them ObjectId columns. When reading them, you will
|
74
|
+
get back an instance of an ObjectId class (from `moped` or `bson`, depending on which one you have loaded; see the
|
75
|
+
introduction for more). When writing them, you can assign a String in hex or binary formats, or an instance of either
|
76
|
+
of the supported ObjectId classes.
|
77
|
+
|
78
|
+
If you didn't name your columns this way, or _don't_ want to pick up columns ending in `_oid`, just name them
|
79
|
+
explicitly:
|
80
|
+
|
81
|
+
class MyModel < ActiveRecord::Base
|
82
|
+
has_objectid_columns :some_oid, :foo
|
83
|
+
end
|
84
|
+
|
85
|
+
This will not only define `some_oid` and `foo` as being ObjectId columns, but it will also skip the automatic detection
|
86
|
+
of columns ending in `_oid`.
|
87
|
+
|
88
|
+
ObjectidColumns will never automatically make a primary-key column an ObjectId, even if it ends with `_oid`; if you
|
89
|
+
want a primary key to be an ObjectId, you must do that explicitly, using `has_objectid_primary_key`, below.
|
90
|
+
|
91
|
+
Note that trying to declare a column as an ObjectId column if it isn't of a supported type (a type that ActiveRecord
|
92
|
+
considers to be `:string` or `:binary`), or if it isn't long enough to support an ObjectId (twelve characters for
|
93
|
+
binary columns, 24 for string columns); this will happen from the `has_objectid_columns` call (so at load time for the
|
94
|
+
model class).
|
95
|
+
|
96
|
+
Once you have declared such a column:
|
97
|
+
|
98
|
+
my_model = MyModel.find(...)
|
99
|
+
|
100
|
+
my_model.my_oid # => BSON::ObjectId('52eab2cf78161f1314000001')
|
101
|
+
my_model.my_oid.to_s # => "52eab2cf78161f1314000001" (built-in behavior from BSON::ObjectId)
|
102
|
+
my_model.my_oid.to_binary # => "R\xEA\xB2\xCFx\x16\x1F\x13\x14\x00\x00\x01"
|
103
|
+
my_model.my_oid.to_binary.encoding # => #<Encoding:ASCII-8BIT>
|
104
|
+
|
105
|
+
my_model.my_oid = BSON::ObjectId.new # OK
|
106
|
+
my_model.my_oid = "52eab32878161f1314000002" # OK
|
107
|
+
my_model.my_oid = "R\xEA\xB2\xCFx\x16\x1F\x13\x14\x00\x00\x01" # OK
|
108
|
+
|
109
|
+
MyModel.where(:my_oid => some_oid).first # => my_model -- i.e., where(...) works with a hash.
|
110
|
+
|
111
|
+
Note that to assign a binary-format string, it must have an encoding of `Encoding::BINARY` (which is an alias for
|
112
|
+
`Encoding::ASCII-8BIT`). (If your string has a different encoding, it may be coming from a source that does not
|
113
|
+
actually support full binary data transparently, which _will_ cause big problems.)
|
114
|
+
|
115
|
+
### Gotchas
|
116
|
+
|
117
|
+
If you query on an ObjectId column and use the hash syntax — _i.e._, `MyModel.where(:some_oid => ...)` —
|
118
|
+
then everything will work perfectly; `ObjectidColumns` looks for this syntax and properly translates the value from
|
119
|
+
an ObjectId object, binary String, or hex String to the proper database value. On the other hand, if you specify a
|
120
|
+
SQL statement or fragment of SQL yourself, you must do the translation, or you'll either get a database error or just
|
121
|
+
no rows found:
|
122
|
+
|
123
|
+
MyModel.where("some_oid = ?", my_oid.to_bson_id.to_binary) # if some_oid is binary
|
124
|
+
MyModel.where("some_oid = ?", my_oid.to_bson_id.to_s) # if some_oid is a String
|
125
|
+
|
126
|
+
(Without actively parsing SQL, which is kind of an insane thing to do, there is no easy way around this. Even if we
|
127
|
+
detected ObjectId objects in the set of values passed in, we'd have no way of figuring out which ObjectId column they
|
128
|
+
were constraining on, and thus whether to turn them into binary or hexadecimal ObjectId values.)
|
129
|
+
|
130
|
+
### Using an ObjectId for a Primary Key
|
131
|
+
|
132
|
+
You can use ObjectId values as primary keys:
|
133
|
+
|
134
|
+
class MyModel < ActiveRecord::Base
|
135
|
+
has_objectid_primary_key
|
136
|
+
end
|
137
|
+
|
138
|
+
Perhaps obviously, your primary-key column must either be `:binary` of length >= 12 or `:string` of length >= 24.
|
139
|
+
|
140
|
+
If your primary-key column is not called `:id`, you must do one of two things:
|
141
|
+
|
142
|
+
* Set the primary key on the class _before_ you call `has_objectid_primary_key`, by just using the normal ActiveRecord
|
143
|
+
`self.primary_key = :foo` syntax.
|
144
|
+
* Pass the primary key as an argument to `has_objectid_primary_key`: `has_objectid_primary_key :foo`.
|
145
|
+
|
146
|
+
(The two are exactly equivalent.)
|
147
|
+
|
148
|
+
When you do this, `ObjectidColumns` adds a `before_create` hook to your model, assigning a new ObjectId immediately
|
149
|
+
before the first time a row is saved in the database. This will not replace an existing ID, so, if you always (or
|
150
|
+
sometimes) assign a new ID yourself, `ObjectidColumns` will not overwrite your ID.
|
151
|
+
|
152
|
+
Perhaps obviously, this means that new IDs are generated and stored client-side; you almost certainly should declare
|
153
|
+
your primary-key column as `NOT NULL`, but you don't need to give it a default (and probably shouldn't, unless you're
|
154
|
+
using a database function that is capable of generating correctly-formatted ObjectId values using the correct
|
155
|
+
algorithm).
|
156
|
+
|
157
|
+
|
158
|
+
### Setting the Preferred Class
|
159
|
+
|
160
|
+
If you have both the `bson` and `moped` gems defined, then, by default, ObjectId columns will be returned as instances
|
161
|
+
of `bson`'s `BSON::ObjectId` class. If you want to use `moped`'s instead, do this:
|
162
|
+
|
163
|
+
ObjectidColumns.preferred_bson_class = Moped::BSON::ObjectId
|
164
|
+
|
165
|
+
### Extensions
|
166
|
+
|
167
|
+
This gem extends String with a single method, `#to_bson_id`; it simply returns an instance of the preferred BSON class
|
168
|
+
from that String if it's in either the valid hex or the valid binary format, or raises `ArgumentError` otherwise.
|
169
|
+
|
170
|
+
This gem also extends whatever BSON ObjectId classes are loaded with methods `to_bson_id` (which just returns `self`),
|
171
|
+
and the method `to_binary`, which returns a binary String of length 12 for that object ID.
|
172
|
+
|
173
|
+
### Running Specs
|
174
|
+
|
175
|
+
`objectid_columns` has thorough system-level (_i.e._, integration) tests, written in RSpec. Because nearly all of its
|
176
|
+
functionality is centered around interfacing with ActiveRecord (as opposed to having significant, complex code within
|
177
|
+
its codebase directly), there are no unit tests — they would simply be setting complex expectations around calls
|
178
|
+
to ActiveRecord, making the tests fragile and not particularly useful.
|
179
|
+
|
180
|
+
In order to run these specs, you must have access to a database you can use. It's best if the database is dedicated
|
181
|
+
to running these specs. The tests create and destroy their own tables, and make every effort to clean up anything they
|
182
|
+
created at the end — so it should be possible to piggyback on top of an existing database you also use for other
|
183
|
+
things. However, it's _always_ much safer to use a dedicated database. (Note that this is intentional use of the word
|
184
|
+
"database", as opposed to "database server"; you don't need, for example, an entirely separate instance of `mysqld`
|
185
|
+
— just a separate database that you can switch to using `USE ....`.)
|
186
|
+
|
187
|
+
Once you have this set up, simply create a file at the root level of the Gem (_i.e._, inside the root
|
188
|
+
`objectid_columns` directory) called `spec_database_config.rb`, and define a constant
|
189
|
+
`OBJECTID_COLUMNS_SPEC_DATABASE_CONFIG` as so:
|
190
|
+
|
191
|
+
OBJECTID_COLUMNS_SPEC_DATABASE_CONFIG = {
|
192
|
+
:database_gem_name => 'mysql2',
|
193
|
+
:require => 'mysql2',
|
194
|
+
:config => {
|
195
|
+
:adapter => 'mysql2',
|
196
|
+
:database => 'objectid_columns_specs_db',
|
197
|
+
:username => 'root'
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
The keys are as follows:
|
202
|
+
|
203
|
+
* `:database_gem_name` is the name of the RubyGem that provides access to the database — exactly as you'd put
|
204
|
+
it in a `Gemfile`;
|
205
|
+
* `:require` is whatever should be passed to Ruby's built-in `require` statement to require the Gem —
|
206
|
+
typically this is the same as `:database_gem_name`, but not always (this is the same as a Gemfile's `:require => ..
|
207
|
+
` syntax);
|
208
|
+
* `:config` is exactly what gets passed to `ActiveRecord::Base.establish_connection`, and so you can pass any
|
209
|
+
options that it accepts (which are the same as what goes in Rails' `database.yml`).
|
210
|
+
|
211
|
+
Once you've done this, you can run the system specs using `bundle exec rspec spec/objectid_columns/system`. (Or run
|
212
|
+
them along with the unit specs with a simple `bundle exec rspec spec`.)
|
213
|
+
|
214
|
+
Note that there's also support deep in the code (in
|
215
|
+
`objectid_columns/spec/objectid_columns/helpers/database_helper.rb`)
|
216
|
+
for defining connections to [Travis CI](https://travis-ci.org/)'s database options, so that Travis can run the tests
|
217
|
+
automatically. Generally, you don't need to worry about this, but it's worth noting.
|
218
|
+
|
219
|
+
## Contributing
|
220
|
+
|
221
|
+
1. Fork it ( http://github.com/swiftype/objectid_columns/fork )
|
222
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
223
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
224
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
225
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require "objectid_columns/version"
|
2
|
+
require "objectid_columns/active_record/base"
|
3
|
+
require "objectid_columns/active_record/relation"
|
4
|
+
require "objectid_columns/arel/visitors/to_sql"
|
5
|
+
require "active_record"
|
6
|
+
|
7
|
+
# This is the root module for ObjectidColumns. It contains largely just configuration and integration information;
|
8
|
+
# all the real work is done in ObjectidColumns::ObjectidColumnsManager. ObjectidColumns gets its start through the
|
9
|
+
# module ObjectidColumns::ActiveRecord::Base, which gets mixed into ::ActiveRecord::Base (below) and is where methods
|
10
|
+
# like +has_objectid_columns+ are declared.
|
11
|
+
module ObjectidColumns
|
12
|
+
class << self
|
13
|
+
# This is the set of classes we support for representing ObjectIds, as objects, themselves. BSON::ObjectId comes
|
14
|
+
# from the +bson+ gem, and Moped::BSON::ObjectId comes from the +moped+ gem.
|
15
|
+
#
|
16
|
+
# Note that this gem does not declare a dependency on either one of these gems, because we want you to be able to
|
17
|
+
# use either, and there's currently no way of expressing that explicitly. Instead,
|
18
|
+
# .available_objectid_columns_bson_classes, below, takes care of figuring out which ones are availble and loading
|
19
|
+
# them.
|
20
|
+
#
|
21
|
+
# Order is important here: when creating new ObjectId objects (such as when reading from an ObjectId column), we
|
22
|
+
# will prefer the earliest one of these classes that is actually defined. You can change this using
|
23
|
+
# .preferred_bson_class=, below.
|
24
|
+
#
|
25
|
+
#
|
26
|
+
# Any class added here has to obey the following constraints:
|
27
|
+
#
|
28
|
+
# * You can create a new instance from a hex string using .from_string(hex_string)
|
29
|
+
# * Calling #to_s on it returns a hexadecimal String of exactly 24 characters
|
30
|
+
#
|
31
|
+
# Both these objects currently do. If they change, or you want to introduce a new ObjectId representation class,
|
32
|
+
# a small amount of refactoring will be necessary.
|
33
|
+
SUPPORTED_OBJECTID_BSON_CLASS_NAMES = %w{BSON::ObjectId Moped::BSON::ObjectId}
|
34
|
+
|
35
|
+
# When we create a new ObjectId object (such as when reading from a column), what class should it be? If you have
|
36
|
+
# multiple classes loaded and you don't like this one, you can call .preferred_bson_class=, below.
|
37
|
+
def preferred_bson_class
|
38
|
+
@preferred_bson_class ||= available_objectid_columns_bson_classes.first
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sets the preferred BSON class to the given class.
|
42
|
+
def preferred_bson_class=(bson_class)
|
43
|
+
unless SUPPORTED_OBJECTID_BSON_CLASS_NAMES.include?(bson_class.name)
|
44
|
+
raise ArgumentError, "ObjectidColumns does not support BSON class #{bson_class.name}; it supports: #{SUPPORTED_OBJECTID_BSON_CLASS_NAMES.inspect}"
|
45
|
+
end
|
46
|
+
|
47
|
+
@preferred_bson_class = bson_class
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns an array of Class objects -- of length at least 1, but potentially more than 1 -- of the various
|
51
|
+
# ObjectId classes we have available to use. Again, because we don't explicitly depend on the BSON gems
|
52
|
+
# (see above), this needs to take care of trying to load and require the gems in question, and fail gracefully
|
53
|
+
# if they're not present.
|
54
|
+
def available_objectid_columns_bson_classes
|
55
|
+
@available_objectid_columns_bson_classes ||= begin
|
56
|
+
# Try to load both gems, but don't fail if there are errors
|
57
|
+
%w{moped bson}.each do |require_name|
|
58
|
+
begin
|
59
|
+
gem require_name
|
60
|
+
rescue Gem::LoadError => le
|
61
|
+
end
|
62
|
+
|
63
|
+
begin
|
64
|
+
require require_name
|
65
|
+
rescue LoadError => le
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# See which classes we have managed to load
|
70
|
+
defined_classes = SUPPORTED_OBJECTID_BSON_CLASS_NAMES.map do |name|
|
71
|
+
eval("if defined?(#{name}) then #{name} end")
|
72
|
+
end.compact
|
73
|
+
|
74
|
+
# Raise an error if we haven't loaded either
|
75
|
+
if defined_classes.length == 0
|
76
|
+
raise %{ObjectidColumns requires a library that implements an ObjectId class to be loaded; we support
|
77
|
+
the following ObjectId classes: #{SUPPORTED_OBJECTID_BSON_CLASS_NAMES.join(", ")}.
|
78
|
+
(These are from the 'bson' or 'moped' gems.) You seem to have neither one installed.
|
79
|
+
|
80
|
+
Please add one of these gems to your project and try again. Usually, this just means
|
81
|
+
adding this to your Gemfile:
|
82
|
+
|
83
|
+
gem 'bson'
|
84
|
+
|
85
|
+
(ObjectidColumns does not explicitly depend on either of these, because we want you
|
86
|
+
to be able to choose whichever one you prefer.)}
|
87
|
+
end
|
88
|
+
|
89
|
+
defined_classes
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Is the given object a valid BSON ObjectId? This doesn't count Strings in any format -- only objects.
|
94
|
+
def is_valid_bson_object?(x)
|
95
|
+
available_objectid_columns_bson_classes.detect { |k| x.kind_of?(k) }
|
96
|
+
end
|
97
|
+
|
98
|
+
# Creates a new BSON ObjectId from the given String, which must be in the hexadecimal format.
|
99
|
+
def construct_objectid(hex_string)
|
100
|
+
preferred_bson_class.send(:from_string, hex_string)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Creates a new BSON ObjectId, from scratch; this must return an ObjectId with a value, suitable for assigning
|
104
|
+
# to a newly-created row. We use this only if you've declared that your primary key column is an ObjectId, and
|
105
|
+
# then only if you're about to save a new row and it has no ID yet.
|
106
|
+
def new_objectid
|
107
|
+
preferred_bson_class.new
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Include the modules that add the initial methods to ActiveRecord::Base, like +has_objectid_columns+.
|
113
|
+
::ActiveRecord::Base.class_eval do
|
114
|
+
include ::ObjectidColumns::ActiveRecord::Base
|
115
|
+
end
|
116
|
+
|
117
|
+
# This adds our patch to +#where+, so that queries will work properly (assuming you use Hash-style syntax).
|
118
|
+
::ActiveRecord::Relation.class_eval do
|
119
|
+
include ::ObjectidColumns::ActiveRecord::Relation
|
120
|
+
end
|
121
|
+
|
122
|
+
# require 'arel/visitors/to_sql'
|
123
|
+
::Arel::Visitors::ToSql.class_eval do
|
124
|
+
include ::ObjectidColumns::Arel::Visitors::ToSql
|
125
|
+
end
|
126
|
+
|
127
|
+
require "objectid_columns/extensions"
|