From 6fb6677a1e2d50e1b14dd86e23c51698df3314db Mon Sep 17 00:00:00 2001 From: ahoward Date: Fri, 10 Aug 2012 09:06:48 -0600 Subject: [PATCH] first commit --- README.md | 51 ++++---------- Rakefile | 4 +- lib/mongoid-sequence.rb | 125 ++++++++++++++++++++++++++++++++++ mongoid-sequence.gemspec | 36 ++++++++++ test/helper.rb | 16 +---- test/mongoid-sequence.rb | 84 +++++++++++++++++++++++ test/mongoid-sequence_test.rb | 84 +++++++++++++++++++++++ 7 files changed, 349 insertions(+), 51 deletions(-) create mode 100644 lib/mongoid-sequence.rb create mode 100644 mongoid-sequence.gemspec create mode 100644 test/mongoid-sequence.rb create mode 100644 test/mongoid-sequence_test.rb diff --git a/README.md b/README.md index de7d27f..7cac23a 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,35 @@ NAME ---- - mongoid-grid_fs + mongoid-sequence INSTALL ------- - gem install mongoid-grid_fs + gem install mongoid-sequence SYNOPSIS -------- ````ruby - require 'mongoid-grid_fs' + require 'mongoid' + require 'mongoid-sequence' - g = GridFs.put readable - GridFS.get id + class Page + include Mongoid::Document + include Mongoid::Sequence - GridFS.delete id + sequence :number + end + + p Page.create.number #=> 1 + p Page.create.number #=> 2 + p Page.create.number #=> 3 ```` DESCRIPTION ----------- -mongoid_grid_fs is a pure mongoid 3 / moped implementation of the mongodb -grid_fs specification - -ref: http://www.mongodb.org/display/DOCS/GridFS+Specification - -it has the following features: - -- implementation is on top of mongoid for portability. moped (the driver) is - barely used - -- simple, REST-like api - -- support for custom namespaces (fs.files vs. image.files) - -- pathnames and io-like objects can be written to the grid - -- auto-unique pathnames are generated (by default) to avoid collisions using #put - - 'path/info/a.rb' -> '$object_id/a.rb' - -- [] and []= methods which allow the grid to be used like a giant file - hash in the sky - -- supprt for data_uris - - ````erb - - <%= image_tag :src => file.data_uri %> - - ```` +mongoid_sequence is a pure mongoid sequence generator based on mongodb's +increment operator diff --git a/Rakefile b/Rakefile index 04a52bc..45acc75 100644 --- a/Rakefile +++ b/Rakefile @@ -1,8 +1,8 @@ This.name = - "Mongoid::GridFs" + "Mongoid::Sequence" This.synopsis = - "a mongoid 3/moped compatible implementation of the grid_fs specification" + "a mongoid 3/moped compatible sequence generator for your models" This.rubyforge_project = 'codeforpeople' This.author = "Ara T. Howard" diff --git a/lib/mongoid-sequence.rb b/lib/mongoid-sequence.rb new file mode 100644 index 0000000..4376ccc --- /dev/null +++ b/lib/mongoid-sequence.rb @@ -0,0 +1,125 @@ +require 'mongoid' + +module Mongoid + module Sequence + def Sequence.version + '1.0.0' + end + + ClassMethods = proc do + def sequence(fieldname, *args, &block) + options = args.extract_options!.to_options! + + sequence_name = ( + options.delete(:sequence) || sequence_name_for(fieldname) + ) + + args.push(options) + + before_create do |doc| + doc[fieldname] = SequenceGenerator.for(sequence_name).next + end + + field(fieldname, *args, &block) + + SequenceGenerator.for(sequence_name) + end + + def sequence_for(fieldname) + SequenceGenerator.for(sequence_name_for(fieldname)) + end + + def sequence_name_for(fieldname) + SequenceGenerator.sequence_name_for(self, fieldname) + end + end + + InstanceMethods = proc do + def sequence_for(fieldname) + self.class.sequence_for(fieldname) + end + end + + def Sequence.included(other) + other.send(:instance_eval, &ClassMethods) + other.send(:class_eval, &InstanceMethods) + super + end + end + + class SequenceGenerator + include Mongoid::Document + + field(:name, :type => String) + + field(:value, :default => 0, :type => Integer) + + validates_presence_of(:name) + validates_uniqueness_of(:name) + + validates_presence_of(:value) + + index({:name => 1}, {:unique => true}) + + Cache = Hash.new + + class << self + def for(name) + name = name.to_s + + Cache[name] ||= ( + begin + create!(:name => name) + rescue + where(:name => name).first || create!(:name => name) + end + ) + end + + alias_method('[]', 'for') + + def sequence_name_for(klass, fieldname) + "#{ klass.name.underscore }-#{ fieldname }" + end + end + + after_destroy do |sequence| + Cache.delete(sequence.name) + end + + def next + inc(:value, 1) + end + + def current_value + reload.value + end + + def reset! + update_attributes!(:value => 0) + end + end +end + + + + +if $0 == __FILE__ + + Mongoid.configure do |config| + config.connect_to('mongoid-sequence') + end + + class A + include Mongoid::Document + include Mongoid::Sequence + + p sequence(:number) + end + + A.sequence_for(:number).destroy + + a = A.create! + p a + +end diff --git a/mongoid-sequence.gemspec b/mongoid-sequence.gemspec new file mode 100644 index 0000000..010bcf7 --- /dev/null +++ b/mongoid-sequence.gemspec @@ -0,0 +1,36 @@ +## mongoid-sequence.gemspec +# + +Gem::Specification::new do |spec| + spec.name = "mongoid-sequence" + spec.version = "1.0.0" + spec.platform = Gem::Platform::RUBY + spec.summary = "mongoid-sequence" + spec.description = "a mongoid 3/moped compatible sequence generator for your models" + + spec.files = +["README.md", + "Rakefile", + "lib", + "lib/mongoid-sequence.rb", + "test", + "test/helper.rb", + "test/mongoid-sequence.rb", + "test/mongoid-sequence_test.rb", + "test/testing.rb"] + + spec.executables = [] + + spec.require_path = "lib" + + spec.test_files = "test/mongoid-sequence.rb" + + + + spec.extensions.push(*[]) + + spec.rubyforge_project = "codeforpeople" + spec.author = "Ara T. Howard" + spec.email = "ara.t.howard@gmail.com" + spec.homepage = "https://github.com/ahoward/mongoid-sequence" +end diff --git a/test/helper.rb b/test/helper.rb index 03da677..79f7098 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -1,18 +1,8 @@ # -*- encoding : utf-8 -*- +require 'pp' require_relative 'testing' -require_relative '../lib/mongoid-grid_fs.rb' +require_relative '../lib/mongoid-sequence.rb' Mongoid.configure do |config| - config.connect_to('mongoid-grid_fs_test') -end - -require 'stringio' - -class SIO < StringIO - attr_accessor :filename - - def initialize(filename, *args, &block) - @filename = filename - super(*args, &block) - end + config.connect_to('mongoid-sequence') end diff --git a/test/mongoid-sequence.rb b/test/mongoid-sequence.rb new file mode 100644 index 0000000..b9c8f0d --- /dev/null +++ b/test/mongoid-sequence.rb @@ -0,0 +1,84 @@ +require_relative 'helper' + +Testing Mongoid::Sequence do +## +# + Sequence = + Mongoid::Sequence + + SequenceGenerator = + Mongoid::SequenceGenerator + + prepare do + SequenceGenerator.destroy_all + end + +## +# + test 'can be included' do + assert do + model_class do + include Mongoid::Sequence + end + end + end + + test 'provided a "sequence" class method that builds a field' do + m = + assert do + model_class do + include Mongoid::Sequence + + sequence 'number' + end + end + + assert{ m.fields['number'].name == 'number' } + end + + test 'autoincrements the field on create' do + m = + assert do + model_class do + include Mongoid::Sequence + + sequence 'number' + end + end + + 10.times do |i| + assert{ m.create.number == i.succ } + end + end + + test 'allows the sequence to be reset' do + m = + assert do + model_class do + include Mongoid::Sequence + + sequence 'number' + end + end + + assert{ m.sequence_for(:number).reset! } + assert{ m.sequence_for(:number).next == 1 } + assert{ m.sequence_for(:number).next != 1 } + assert{ m.sequence_for(:number).reset! } + assert{ m.sequence_for(:number).next == 1 } + end + + + +protected + def model_class(&block) + m = Class.new + m.class_eval do + def m.name() 'm' end + include Mongoid::Document + end + m.destroy_all + m.class_eval(&block) if block + m + end +end diff --git a/test/mongoid-sequence_test.rb b/test/mongoid-sequence_test.rb new file mode 100644 index 0000000..b9c8f0d --- /dev/null +++ b/test/mongoid-sequence_test.rb @@ -0,0 +1,84 @@ +require_relative 'helper' + +Testing Mongoid::Sequence do +## +# + Sequence = + Mongoid::Sequence + + SequenceGenerator = + Mongoid::SequenceGenerator + + prepare do + SequenceGenerator.destroy_all + end + +## +# + test 'can be included' do + assert do + model_class do + include Mongoid::Sequence + end + end + end + + test 'provided a "sequence" class method that builds a field' do + m = + assert do + model_class do + include Mongoid::Sequence + + sequence 'number' + end + end + + assert{ m.fields['number'].name == 'number' } + end + + test 'autoincrements the field on create' do + m = + assert do + model_class do + include Mongoid::Sequence + + sequence 'number' + end + end + + 10.times do |i| + assert{ m.create.number == i.succ } + end + end + + test 'allows the sequence to be reset' do + m = + assert do + model_class do + include Mongoid::Sequence + + sequence 'number' + end + end + + assert{ m.sequence_for(:number).reset! } + assert{ m.sequence_for(:number).next == 1 } + assert{ m.sequence_for(:number).next != 1 } + assert{ m.sequence_for(:number).reset! } + assert{ m.sequence_for(:number).next == 1 } + end + + + +protected + def model_class(&block) + m = Class.new + m.class_eval do + def m.name() 'm' end + include Mongoid::Document + end + m.destroy_all + m.class_eval(&block) if block + m + end +end