object_id_gem 1.0.6
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 +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: 
|
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"
|