-
-
Save ShogunPanda/1086265 to your computer and use it in GitHub Desktop.
| # encoding: utf-8 | |
| module Mongoid | |
| # Include this module to add automatic sequence feature (also works for _id field, so SQL-Like autoincrement primary key can easily be simulated) | |
| # usage: | |
| # class KlassName | |
| # include Mongoid::Document | |
| # include Mongoid::Sequence | |
| # ... | |
| # field :number, :type=>Integer | |
| # sequence :number | |
| # ... | |
| module Sequence | |
| extend ActiveSupport::Concern | |
| module ClassMethods | |
| def sequence(_field) | |
| # REPLACE FIELD DEFAULT VALUE | |
| _field = _field.to_s | |
| field(_field, fields[_field].options.merge(:default => lambda{ self.class.set_from_sequence(_field)})) | |
| end | |
| def set_from_sequence(_field) | |
| sequences = self.db.collection("__sequences") | |
| counter_id = "#{self.class.name.underscore}_#{_field}" | |
| # Increase the sequence value and also avoids conflicts | |
| catch(:value) do | |
| value = nil | |
| begin | |
| value = sequences.find_and_modify( | |
| :query => {"_id" => counter_id}, | |
| :update=> {"$inc" => {"value" => 1}}, | |
| :new => true, | |
| :upsert => true | |
| ).send("[]", "value") | |
| end while self.first({:conditions => {_field => value}}) | |
| throw :value, value | |
| end | |
| end | |
| def reset_sequence(_field) | |
| sequences = self.db.collection("__sequences") | |
| counter_id = "#{self.class.name.underscore}_#{_field}" | |
| sequences.find_and_modify(:query => {"_id" => counter_id}, :update=> {"$set" => {"value" => 0}}, :new => true, :upsert => true) | |
| end | |
| end | |
| end | |
| end |
Just packaged something similar to this into a gem: https://github.com/goncalossilva/mongoid-sequence
@paulsfds: I believe you won't have ID issue with this gem.
Well done! Thanks!
@goncalossilva - Thanks for your reply. I installed your gem, but I am getting errors. I always get the error, "ActionController::RoutingError (uninitialized constant Mongoid::Sequence)." I also tried one of your test classes (http://github.com/goncalossilva/mongoid-sequence/blob/master/test/models/id_sequenced_model.rb), and I also get the same error.
In my Gemfile I have gem "mongoid-sequence", and I did do a bundle install, so I am not sure what is wrong.
@paulsfds That's odd. Are you using "bundle exec rails server"? If yes, is your app open source (for me to take a look)?
@goncalossilva Yes, I did use bundle exec rails server. I uploaded a sample project on my github account here: http://github.com/paulsfds/MongoidSequenceTest. When I start up the server and try to access the /samples route, I get the following error:
Started GET "/samples" for localhost at 2012-03-19 15:53:32 -0700
ActionController::RoutingError (uninitialized constant Mongoid::Sequence):
app/models/sample.rb:3:in <class:Sample>' app/models/sample.rb:1:in<top (required)>'
app/controllers/samples_controller.rb:1:in `<top (required)>'
@paulsfds I just clone your project, ran bundle install, ran bundle exec rails s, hit /samples and everything worked fine. Could you update your bundler and see if the problem disappears?
@goncalossilva I don't know what the problem is, but I'm still getting the same error. I am using Bundler version 1.0.21. I tried uninstalling the gem (gem uninstall mongoid-sequence -a) and deleting the Gemfile.lock. I also tried bundle update, but the gem is up to date already. I wonder if anyone else is having this issue or is it just me?
@paulsfds I'm using ruby 1.9.3p125 and bundler 1.1.1 here.
@paulsfds I'm 99% confident that you won't have that issue anymore with mongoid-sequence 0.1 (released a few minutes ago). Try it! :)
@goncalossilva I'm playing around with this gem again (I had forgotten about it for a month), and it seems to be working but I noticed one issue. The model's find() method only takes an integer, and it errors out if you try and pass it a string.
1.9.2-p290 :022 > Sample.find(1)
=> #<Sample _id: 1, _type: nil, name: nil>
1.9.2-p290 :024 > Sample.find("1")
BSON::InvalidObjectId: illegal ObjectId format: 1
The default Mongoid implementation of find takes only a string. A workaround is to just force the param to be an integer like so:
@sample = Sample.find(params[:id].to_i)
I noticed that in your gem, the id actually becomes an integer whereas in Mongoid, the id is a BSON::ObjectId object. This may be the problem, is there a way to store the sequential numbers (for ids) as BSON::ObjectId instead of as in integer?
1.9.2-p290 :027 > Sample.find(1)._id
=> 1
1.9.2-p290 :028 > Sample.find(1)._id.is_a?(Integer)
=> true
1.9.2-p290 :009 > Event.first._id
=> BSON::ObjectId('4f98e4271d41c827cb000007')
1.9.2-p290 :010 > Event.first._id.is_a?(Integer)
=> false
@paulsfds I believe that's the default behavior when you field :my_id, :type => Integer... the sequencing code doesn't play with any of that. Please open an issue with this if you think it's something else :)
Yes, I tried that as well. The IDs still get generated in a non-MySQL fashion and I have problems with the Event.find method. When I try to view the object by accessing it through /events/, I get the following error: