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 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
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ /spec_database_config.rb*
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in objectid_columns.gemspec
4
+ gemspec
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 &mdash; _i.e._, `MyModel.where(:some_oid => ...)` &mdash;
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 &mdash; 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 &mdash; 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
+ &mdash; 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 &mdash; 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 &mdash;
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,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -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"