diff --git a/README.md b/README.md index e6d5448..47877fc 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,32 @@ s2 = Sequenced.create s2.id #=> 2 # and so on ``` +Subclasses can share their superclass sequenced field(s) and/or have their own sequenced field(s): +```ruby +class Sequenced + include Mongoid::Document + include Mongoid::Sequence + + field :my_sequence, :type => Integer + sequence :my_sequence +end + +class SubSequenced + use_superclass_sequence + + field :my_sub_sequence, :type => Integer + sequence :my_sub_sequence +end + +s1 = Sequenced.create +s1.my_sequence #=> 1 + +s2 = SubSequenced.create +s2.my_sequence #=> 2 +s2.my_sub_sequence #=> 1 + +``` + ## Consistency Mongoid::Sequence uses the atomic [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) command, so you shouldn't have to worry about the sequence's consistency. diff --git a/lib/mongoid-sequence.rb b/lib/mongoid-sequence.rb index f29f69a..c09ef90 100644 --- a/lib/mongoid-sequence.rb +++ b/lib/mongoid-sequence.rb @@ -1,5 +1,6 @@ require "mongoid-sequence/version" require "active_support/concern" +require "set" module Mongoid module Sequence @@ -11,23 +12,42 @@ module Sequence module ClassMethods attr_accessor :sequence_fields + attr_accessor :klazzes def sequence(field) + self.klazzes ||= Set.new + self.klazzes << self self.sequence_fields ||= [] self.sequence_fields << field end + + def use_superclass_sequence + self.klazzes ||= self.superclass.klazzes = Set.new + self.klazzes << self.superclass + end + end def set_sequence - sequences = self.db.collection("__sequences") - self.class.sequence_fields.each do |field| - next_sequence = sequences.find_and_modify(:query => {"_id" => "#{self.class.name.underscore}_#{field}"}, - :update => {"$inc" => {"seq" => 1}}, - :new => true, - :upsert => true) - - self[field] = next_sequence["seq"] - end if self.class.sequence_fields + sequences = get_sequence_collection + self.class.klazzes.each do |klazz| + klazz.sequence_fields.each do |field| + next_sequence = sequences.find_and_modify(:query => {"_id" => "#{klazz.name.underscore}_#{field}"}, + :update => {"$inc" => {"seq" => 1}}, + :new => true, + :upsert => true) + + self[field] = next_sequence["seq"] + end if klazz.sequence_fields + end + end + + def get_sequence_collection + if self.embedded? + sequences = self._parent.db.collection("__sequences") + else + sequences = self.db.collection("__sequences") + end end end end diff --git a/test/embedded_test.rb b/test/embedded_test.rb new file mode 100644 index 0000000..e82cca0 --- /dev/null +++ b/test/embedded_test.rb @@ -0,0 +1,11 @@ +require "test_helper" + +class EmbeddedTest < BaseTest + def test_embedded_sequence + parent = ParentSequencedModel.create + child = parent.child_sequenced_models.create + + assert_equal parent.auto_increment, 1 + assert_equal child.child_auto_increment, 1 + end +end diff --git a/test/models/child_sequenced_model.rb b/test/models/child_sequenced_model.rb new file mode 100644 index 0000000..14facbf --- /dev/null +++ b/test/models/child_sequenced_model.rb @@ -0,0 +1,9 @@ +class ChildSequencedModel + include Mongoid::Document + include Mongoid::Sequence + + field :child_auto_increment, :type => Integer + sequence :child_auto_increment + + embedded_in :child_sequenced_models, :inverse_of => :child_sequenced_models +end diff --git a/test/models/parent_sequenced_model.rb b/test/models/parent_sequenced_model.rb new file mode 100644 index 0000000..2e6bef4 --- /dev/null +++ b/test/models/parent_sequenced_model.rb @@ -0,0 +1,9 @@ +class ParentSequencedModel + include Mongoid::Document + include Mongoid::Sequence + + field :auto_increment, :type => Integer + sequence :auto_increment + + embeds_many :child_sequenced_models +end diff --git a/test/models/sub_class_multi_sequenced_model.rb b/test/models/sub_class_multi_sequenced_model.rb new file mode 100644 index 0000000..0a586c7 --- /dev/null +++ b/test/models/sub_class_multi_sequenced_model.rb @@ -0,0 +1,8 @@ +class SubClassMultiSequencedModel < FirstSequencedModel + + field :sequence, :type => Integer + sequence :sequence + + use_superclass_sequence + +end diff --git a/test/models/sub_class_self_sequenced_model.rb b/test/models/sub_class_self_sequenced_model.rb new file mode 100644 index 0000000..3c428bd --- /dev/null +++ b/test/models/sub_class_self_sequenced_model.rb @@ -0,0 +1,6 @@ +class SubClassSelfSequencedModel < FirstSequencedModel + + field :sequence, :type => Integer + sequence :sequence + +end diff --git a/test/models/sub_class_super_sequenced_model.rb b/test/models/sub_class_super_sequenced_model.rb new file mode 100644 index 0000000..743b73d --- /dev/null +++ b/test/models/sub_class_super_sequenced_model.rb @@ -0,0 +1,5 @@ +class SubClassSuperSequencedModel < FirstSequencedModel + + use_superclass_sequence + +end diff --git a/test/sub_class_test.rb b/test/sub_class_test.rb new file mode 100644 index 0000000..1d01648 --- /dev/null +++ b/test/sub_class_test.rb @@ -0,0 +1,47 @@ +require "test_helper" + +class SubClassTest < BaseTest + def test_subclass_parent_sequence + parent = FirstSequencedModel.create + child = SubClassSuperSequencedModel.create + + assert_equal parent.auto_increment + 1, child.auto_increment + + parent2 = FirstSequencedModel.create + child2 = SubClassSuperSequencedModel.create + + assert_equal parent2.auto_increment + 1, child2.auto_increment + end + + def test_subclass_self_sequence + parent = FirstSequencedModel.create + child = SubClassSelfSequencedModel.create + + assert_equal parent.auto_increment, 1 + assert_equal child.auto_increment, nil + assert_equal child.sequence, 1 + + parent2 = FirstSequencedModel.create + child2 = SubClassSelfSequencedModel.create + + assert_equal parent2.auto_increment, 2 + assert_equal child2.auto_increment, nil + assert_equal child2.sequence, 2 + end + + def test_subclass_multi_sequence + parent = FirstSequencedModel.create + child = SubClassMultiSequencedModel.create + + assert_equal parent.auto_increment, 1 + assert_equal child.auto_increment, 2 + assert_equal child.sequence, 1 + + parent2 = FirstSequencedModel.create + child2 = SubClassMultiSequencedModel.create + + assert_equal parent2.auto_increment, 3 + assert_equal child2.auto_increment, 4 + assert_equal child2.sequence, 2 + end +end \ No newline at end of file