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 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"