From 8b28b6bcc103074f70db3c25fc7382c78c276d6b Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Fri, 28 Jul 2017 13:54:35 +0200 Subject: [PATCH 01/15] Refactor #alias_method_chain implementation with ruby's native #prepend --- lib/mongoid/token.rb | 10 +++++++- lib/mongoid/token/collision_resolver.rb | 31 ++++++++++++++----------- lib/mongoid/token/collisions.rb | 14 +++++------ 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/lib/mongoid/token.rb b/lib/mongoid/token.rb index 8f49cbd..713e6dd 100644 --- a/lib/mongoid/token.rb +++ b/lib/mongoid/token.rb @@ -25,6 +25,10 @@ def token(*args) override_to_param(options) if options.override_to_param? end + def resolvers + @resolvers + end + private def add_token_field_and_index(options) self.field options.field_name, :type => String, :default => default_value(options) @@ -32,10 +36,14 @@ def add_token_field_and_index(options) end def add_token_collision_resolver(options) - resolver = Mongoid::Token::CollisionResolver.new(self, options.field_name, options.retry_count) + @resolvers ||= [] + resolver = Mongoid::Token::CollisionResolver.new( + self, options.field_name, options.retry_count + ) resolver.create_new_token = Proc.new do |document| document.send(:create_token, options.field_name, options.pattern) end + @resolvers.push(resolver) end def define_custom_finders(options) diff --git a/lib/mongoid/token/collision_resolver.rb b/lib/mongoid/token/collision_resolver.rb index d18c8b0..e1b40f0 100644 --- a/lib/mongoid/token/collision_resolver.rb +++ b/lib/mongoid/token/collision_resolver.rb @@ -2,6 +2,21 @@ module Mongoid module Token + module SafeOperationsHandler + def insert(options = {}) + safe_operation { super(options) } + end + + def upsert(options = {}) + safe_operation { super(options) } + end + + def safe_operation(&block) + resolver = self.class.resolvers.first + resolve_token_collisions(resolver) { with(write: { w: 1 }, &block) } + end + end + class CollisionResolver attr_accessor :create_new_token attr_reader :klass @@ -9,29 +24,17 @@ class CollisionResolver attr_reader :retry_count def initialize(klass, field_name, retry_count) - @create_new_token = Proc.new {|doc|} + @create_new_token = proc { |doc| } @klass = klass @field_name = field_name @retry_count = retry_count klass.send(:include, Mongoid::Token::Collisions) - alias_method_with_collision_resolution(:insert) - alias_method_with_collision_resolution(:upsert) + klass.send(:prepend, SafeOperationsHandler) end def create_new_token_for(document) @create_new_token.call(document) end - - private - def alias_method_with_collision_resolution(method) - handler = self - klass.send(:define_method, :"#{method.to_s}_with_#{handler.field_name}_safety") do |method_options = {}| - self.resolve_token_collisions handler do - with(:safe => true).send(:"#{method.to_s}_without_#{handler.field_name}_safety", method_options) - end - end - klass.alias_method_chain method.to_sym, :"#{handler.field_name}_safety" - end end end end diff --git a/lib/mongoid/token/collisions.rb b/lib/mongoid/token/collisions.rb index 58304b5..9da562a 100644 --- a/lib/mongoid/token/collisions.rb +++ b/lib/mongoid/token/collisions.rb @@ -6,15 +6,13 @@ def resolve_token_collisions(resolver) begin yield rescue Mongo::Error::OperationFailure => e - if is_duplicate_token_error?(e, self, resolver.field_name) - if (retries -= 1) >= 0 - resolver.create_new_token_for(self) - retry - end - raise_collision_retries_exceeded_error resolver.field_name, resolver.retry_count - else - raise e + raise e unless is_duplicate_token_error?(e, self, resolver.field_name) + if (retries -= 1) >= 0 + resolver.create_new_token_for(self) + retry end + raise_collision_retries_exceeded_error(resolver.field_name, + resolver.retry_count) end end From e9da5aa041d8f3c72c06a7eaf3ecd2930da7eee4 Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Mon, 31 Jul 2017 09:37:17 +0200 Subject: [PATCH 02/15] remove resolver parameter from resolve_token_collisions lookup resolvers array instead --- lib/mongoid/token/collision_resolver.rb | 3 +-- lib/mongoid/token/collisions.rb | 10 ++++++--- spec/mongoid/token/collisions_spec.rb | 30 ++++++++++++++++++++----- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/lib/mongoid/token/collision_resolver.rb b/lib/mongoid/token/collision_resolver.rb index e1b40f0..de9df4e 100644 --- a/lib/mongoid/token/collision_resolver.rb +++ b/lib/mongoid/token/collision_resolver.rb @@ -12,8 +12,7 @@ def upsert(options = {}) end def safe_operation(&block) - resolver = self.class.resolvers.first - resolve_token_collisions(resolver) { with(write: { w: 1 }, &block) } + resolve_token_collisions { with(write: { w: 1 }, &block) } end end diff --git a/lib/mongoid/token/collisions.rb b/lib/mongoid/token/collisions.rb index 9da562a..d26968d 100644 --- a/lib/mongoid/token/collisions.rb +++ b/lib/mongoid/token/collisions.rb @@ -1,12 +1,16 @@ module Mongoid module Token module Collisions - def resolve_token_collisions(resolver) - retries = resolver.retry_count + def resolve_token_collisions + retries = nil begin yield rescue Mongo::Error::OperationFailure => e - raise e unless is_duplicate_token_error?(e, self, resolver.field_name) + resolver = self.class.resolvers.select do |r| + is_duplicate_token_error?(e, self, r.field_name) + end.first + raise e unless resolver + retries ||= resolver.retry_count if (retries -= 1) >= 0 resolver.create_new_token_for(self) retry diff --git a/spec/mongoid/token/collisions_spec.rb b/spec/mongoid/token/collisions_spec.rb index 404b50d..6256fd3 100644 --- a/spec/mongoid/token/collisions_spec.rb +++ b/spec/mongoid/token/collisions_spec.rb @@ -11,13 +11,19 @@ resolver.stub(:create_new_token_for){|doc|} document.class.send(:include, Mongoid::Token::Collisions) document.stub(:is_duplicate_token_error?).and_return(true) + document.class.stub(:resolvers).and_return([resolver]) end context "and there are zero retries" do it "should raise an error after the first try" do resolver.stub(:retry_count).and_return(0) attempts = 0 - expect{document.resolve_token_collisions(resolver) { attempts += 1; raise Mongo::Error::OperationFailure }}.to raise_error Mongoid::Token::CollisionRetriesExceeded + expect do + document.resolve_token_collisions do + attempts += 1 + raise Mongo::Error::OperationFailure + end + end.to raise_error Mongoid::Token::CollisionRetriesExceeded expect(attempts).to eq 1 end end @@ -26,7 +32,12 @@ it "should raise an error after retrying once" do resolver.stub(:retry_count).and_return(1) attempts = 0 - expect{document.resolve_token_collisions(resolver) { attempts += 1; raise Mongo::Error::OperationFailure }}.to raise_error Mongoid::Token::CollisionRetriesExceeded + expect do + document.resolve_token_collisions do + attempts += 1 + raise Mongo::Error::OperationFailure + end + end.to raise_error Mongoid::Token::CollisionRetriesExceeded expect(attempts).to eq 2 end end @@ -35,7 +46,12 @@ it "should raise an error after retrying" do resolver.stub(:retry_count).and_return(3) attempts = 0 - expect{document.resolve_token_collisions(resolver) { attempts += 1; raise Mongo::Error::OperationFailure }}.to raise_error Mongoid::Token::CollisionRetriesExceeded + expect do + document.resolve_token_collisions do + attempts += 1 + raise Mongo::Error::OperationFailure + end + end.to raise_error Mongoid::Token::CollisionRetriesExceeded expect(attempts).to eq 4 end end @@ -45,7 +61,9 @@ document.stub(:is_duplicate_token_error?).and_return(false) resolver.stub(:retry_count).and_return(3) e = Mongo::Error::OperationFailure.new("nope") - expect{document.resolve_token_collisions(resolver) { raise e }}.to raise_error(e) + expect do + document.resolve_token_collisions { raise e } + end.to raise_error(e) end end end @@ -73,7 +91,9 @@ end it "should raise an error" do - expect{ document.raise_collision_retries_exceeded_error(:token, 3) }.to raise_error(Mongoid::Token::CollisionRetriesExceeded) + expect { document.raise_collision_retries_exceeded_error(:token, 3) }.to( + raise_error(Mongoid::Token::CollisionRetriesExceeded) + ) end end From fbb61df9aec530649530c95709a42800d02428e5 Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Mon, 31 Jul 2017 10:08:36 +0200 Subject: [PATCH 03/15] Upgrade mongoid version # Conflicts: # mongoid_token.gemspec --- mongoid_token.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongoid_token.gemspec b/mongoid_token.gemspec index 1db0778..cc45a49 100644 --- a/mongoid_token.gemspec +++ b/mongoid_token.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.description = %q{Mongoid token is a gem for creating random, unique tokens for mongoid documents. Highly configurable and great for making URLs a little more compact.} s.rubyforge_project = "mongoid_token" - s.add_dependency 'mongoid', '~> 5.0.0' + s.add_dependency 'mongoid', '~> 6' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") From 1796f598bf1781b331f889ed324df096d7645ccc Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Mon, 31 Jul 2017 10:04:40 +0200 Subject: [PATCH 04/15] Ensure non mongoid_token exceptions are cought and tokens regenerated --- spec/mongoid/token_spec.rb | 48 ++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/spec/mongoid/token_spec.rb b/spec/mongoid/token_spec.rb index 6303c04..9f8781c 100644 --- a/spec/mongoid/token_spec.rb +++ b/spec/mongoid/token_spec.rb @@ -138,11 +138,49 @@ class UntaintedDocument end end - it "should allow for multiple tokens of different names" do - document_class.send(:token, :contains => :alpha_upper) - document_class.send(:token, :field_name => :sharing_id, :contains => :alpha_lower) - expect(document.token).to match(/[A-Z]{4}/) - expect(document.sharing_id).to match(/[a-z]{4}/) + context "should allow for multiple tokens of different names" do + before do + document_class.send(:token, contains: :alpha_upper) + document_class.send(:token, field_name: :sharing_id, + contains: :alpha_lower) + end + + it { expect(document.token).to match(/[A-Z]{4}/) } + it { expect(document.sharing_id).to match(/[a-z]{4}/) } + end + + context "should raise exception for duplicated token" do + class Doc + include Mongoid::Document + include Mongoid::Token + + token contains: :alpha_upper + token field_name: :sharing_id, contains: :alpha_lower + index({ foo: 1 }, unique: true, sparse: true) + + field :foo + end + + before do + Doc.create_indexes + doc + dup_doc.token = doc.token + dup_doc.sharing_id = doc.sharing_id + dup_doc.foo = doc.foo + end + + let(:doc) { Doc.create(foo: "hello") } + let(:dup_doc) { Doc.new } + let(:exception) do + "insertDocument :: caused by :: 11000 E11000 duplicate key error "\ + 'index: mongoid_token_test.docs.$foo_1 dup key: { : "hello" } (11000)' + end + + it do + expect { dup_doc.save }.to raise_error(exception) + expect(dup_doc.token).not_to eq(doc.token) + expect(dup_doc.sharing_id).not_to eq(doc.sharing_id) + end end end From 30537c39802d3c9d81bf1c8ae8941e05059bc356 Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Mon, 31 Jul 2017 10:19:23 +0200 Subject: [PATCH 05/15] Remove older ruby versions --- .travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 412d7fe..8e6f95c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,7 @@ language: ruby rvm: - - 1.9.3 - - 2.0.0 - - 2.1.0 - - 2.1.1 + - 2.2.2 gemfile: - Gemfile From b256286b50e7255e5859e62b8ec1b9503371cc5e Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Mon, 31 Jul 2017 10:26:54 +0200 Subject: [PATCH 06/15] Make code climate reporting work --- .travis.yml | 4 +++- spec/spec_helper.rb | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8e6f95c..a1c7a84 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,4 +12,6 @@ services: env: - CODECLIMATE_REPO_TOKEN=b216164ab66da464aa02fe5b862811ba0526c8dc7ea291ebe53056be4b6b5e1f -script: "bundle exec rspec" +script: + - bundle exec rspec + - bundle exec codeclimate-test-reporter diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 570d0c5..40fafbb 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ +require "simplecov" require "codeclimate-test-reporter" -CodeClimate::TestReporter.start +SimpleCov.start $: << File.expand_path("../../lib", __FILE__) From 45203061bc1044b9894a7a652e04f1c305408aaa Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Mon, 31 Jul 2017 11:12:11 +0200 Subject: [PATCH 07/15] Make exception assertion compatible with mongo 2.4 --- spec/mongoid/token_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/mongoid/token_spec.rb b/spec/mongoid/token_spec.rb index 9f8781c..e4c80aa 100644 --- a/spec/mongoid/token_spec.rb +++ b/spec/mongoid/token_spec.rb @@ -172,8 +172,7 @@ class Doc let(:doc) { Doc.create(foo: "hello") } let(:dup_doc) { Doc.new } let(:exception) do - "insertDocument :: caused by :: 11000 E11000 duplicate key error "\ - 'index: mongoid_token_test.docs.$foo_1 dup key: { : "hello" } (11000)' + /E11000 duplicate key error index: mongoid_token_test.docs\.\$foo_1/ end it do From 6f1b7f9f259f2d65653cbaf5cb9180d45b8d7325 Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Mon, 2 Dec 2019 17:24:14 +0100 Subject: [PATCH 08/15] Gem autoload using zeitwerk --- Gemfile | 4 +- lib/mongoid/token.rb | 6 - lib/mongoid/token/collision_resolver.rb | 2 - ...tions.rb => collision_retries_exceeded.rb} | 2 - lib/mongoid/token/error.rb | 5 + lib/mongoid/token/options.rb | 136 +++++++++--------- lib/mongoid_token.rb | 5 +- mongoid_token.gemspec | 1 + 8 files changed, 82 insertions(+), 79 deletions(-) rename lib/mongoid/token/{exceptions.rb => collision_retries_exceeded.rb} (90%) create mode 100644 lib/mongoid/token/error.rb diff --git a/Gemfile b/Gemfile index d0d1ea8..0cb708f 100644 --- a/Gemfile +++ b/Gemfile @@ -3,8 +3,8 @@ source "http://rubygems.org" gemspec group :test do - gem 'rspec', '2.14.1' + gem 'rspec' gem "codeclimate-test-reporter", require: nil gem 'database_cleaner' - gem 'mongoid-rspec', '1.5.1' + gem 'mongoid-rspec' end diff --git a/lib/mongoid/token.rb b/lib/mongoid/token.rb index 713e6dd..ff02d71 100644 --- a/lib/mongoid/token.rb +++ b/lib/mongoid/token.rb @@ -1,9 +1,3 @@ -require 'mongoid/token/exceptions' -require 'mongoid/token/options' -require 'mongoid/token/generator' -require 'mongoid/token/finders' -require 'mongoid/token/collision_resolver' - module Mongoid module Token extend ActiveSupport::Concern diff --git a/lib/mongoid/token/collision_resolver.rb b/lib/mongoid/token/collision_resolver.rb index de9df4e..988c93b 100644 --- a/lib/mongoid/token/collision_resolver.rb +++ b/lib/mongoid/token/collision_resolver.rb @@ -1,5 +1,3 @@ -require 'mongoid/token/collisions' - module Mongoid module Token module SafeOperationsHandler diff --git a/lib/mongoid/token/exceptions.rb b/lib/mongoid/token/collision_retries_exceeded.rb similarity index 90% rename from lib/mongoid/token/exceptions.rb rename to lib/mongoid/token/collision_retries_exceeded.rb index cc74da2..801f84b 100644 --- a/lib/mongoid/token/exceptions.rb +++ b/lib/mongoid/token/collision_retries_exceeded.rb @@ -1,7 +1,5 @@ module Mongoid module Token - class Error < StandardError; end - class CollisionRetriesExceeded < Error def initialize(resource = "unknown resource", attempts = "unspecified") @resource = resource diff --git a/lib/mongoid/token/error.rb b/lib/mongoid/token/error.rb new file mode 100644 index 0000000..e09bb7f --- /dev/null +++ b/lib/mongoid/token/error.rb @@ -0,0 +1,5 @@ +module Mongoid + module Token + class Error < StandardError; end + end +end diff --git a/lib/mongoid/token/options.rb b/lib/mongoid/token/options.rb index afe20e7..d78c835 100644 --- a/lib/mongoid/token/options.rb +++ b/lib/mongoid/token/options.rb @@ -1,78 +1,82 @@ -class Mongoid::Token::Options - def initialize(options = {}) - @options = merge_defaults validate_options(options) - end +module Mongoid + module Token + class Options + def initialize(options = {}) + @options = merge_defaults validate_options(options) + end - def length - @options[:length] - end + def length + @options[:length] + end - def retry_count - @options[:retry_count] - end + def retry_count + @options[:retry_count] + end - def contains - @options[:contains] - end + def contains + @options[:contains] + end - def field_name - !@options[:id] && @options[:field_name] || :_id - end + def field_name + !@options[:id] && @options[:field_name] || :_id + end - def skip_finders? - @options[:skip_finders] - end + def skip_finders? + @options[:skip_finders] + end - def override_to_param? - @options[:override_to_param] - end + def override_to_param? + @options[:override_to_param] + end - def generate_on_init - @options[:id] || @options[:generate_on_init] - end + def generate_on_init + @options[:id] || @options[:generate_on_init] + end - def pattern - @options[:pattern] ||= case @options[:contains].to_sym - when :alphanumeric - "%s#{@options[:length]}" - when :alpha - "%w#{@options[:length]}" - when :alpha_upper - "%C#{@options[:length]}" - when :alpha_lower - "%c#{@options[:length]}" - when :numeric - "%d1,#{@options[:length]}" - when :fixed_numeric - "%d#{@options[:length]}" - when :fixed_numeric_no_leading_zeros - "%D#{@options[:length]}" - when :fixed_hex_numeric - "%h#{@options[:length]}" - when :fixed_hex_numeric_no_leading_zeros - "%H#{@options[:length]}" - end - end + def pattern + @options[:pattern] ||= case @options[:contains].to_sym + when :alphanumeric + "%s#{@options[:length]}" + when :alpha + "%w#{@options[:length]}" + when :alpha_upper + "%C#{@options[:length]}" + when :alpha_lower + "%c#{@options[:length]}" + when :numeric + "%d1,#{@options[:length]}" + when :fixed_numeric + "%d#{@options[:length]}" + when :fixed_numeric_no_leading_zeros + "%D#{@options[:length]}" + when :fixed_hex_numeric + "%h#{@options[:length]}" + when :fixed_hex_numeric_no_leading_zeros + "%H#{@options[:length]}" + end + end - private - def validate_options(options) - if options.has_key?(:retry) - STDERR.puts "Mongoid::Token Deprecation Warning: option `retry` has been renamed to `retry_count`. `:retry` will be removed in v2.1" - options[:retry_count] = options[:retry] - end - options - end + private + def validate_options(options) + if options.has_key?(:retry) + STDERR.puts "Mongoid::Token Deprecation Warning: option `retry` has been renamed to `retry_count`. `:retry` will be removed in v2.1" + options[:retry_count] = options[:retry] + end + options + end - def merge_defaults(options) - { - id: false, - length: 4, - retry_count: 3, - contains: :alphanumeric, - field_name: :token, - skip_finders: false, - override_to_param: true, - generate_on_init: false - }.merge(options) + def merge_defaults(options) + { + id: false, + length: 4, + retry_count: 3, + contains: :alphanumeric, + field_name: :token, + skip_finders: false, + override_to_param: true, + generate_on_init: false + }.merge(options) + end + end end end diff --git a/lib/mongoid_token.rb b/lib/mongoid_token.rb index 72859bd..e106622 100644 --- a/lib/mongoid_token.rb +++ b/lib/mongoid_token.rb @@ -1 +1,4 @@ -require 'mongoid/token' +require 'zeitwerk' +require 'active_support/concern' +loader = Zeitwerk::Loader.for_gem +loader.setup diff --git a/mongoid_token.gemspec b/mongoid_token.gemspec index cc45a49..163bde3 100644 --- a/mongoid_token.gemspec +++ b/mongoid_token.gemspec @@ -20,4 +20,5 @@ Gem::Specification.new do |s| s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] + s.add_dependency 'zeitwerk' end From 46aa33ac3592e99150246a4cb459d0d471f3216c Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Mon, 2 Dec 2019 17:25:43 +0100 Subject: [PATCH 09/15] Update mongo version --- mongoid_token.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongoid_token.gemspec b/mongoid_token.gemspec index 163bde3..16f88c8 100644 --- a/mongoid_token.gemspec +++ b/mongoid_token.gemspec @@ -14,7 +14,7 @@ Gem::Specification.new do |s| s.description = %q{Mongoid token is a gem for creating random, unique tokens for mongoid documents. Highly configurable and great for making URLs a little more compact.} s.rubyforge_project = "mongoid_token" - s.add_dependency 'mongoid', '~> 6' + s.add_dependency 'mongoid', '~> 7' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") From 606c5a8849e968181835c9a29351291b219c3002 Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Tue, 3 Dec 2019 11:18:12 +0100 Subject: [PATCH 10/15] Use new :expect syntax --- lib/mongoid/token/collisions.rb | 3 +- spec/mongoid/token/collisions_spec.rb | 47 ++++++++-------- spec/mongoid/token/finders_spec.rb | 23 +++++--- spec/mongoid/token/generator_spec.rb | 62 ++++++++++++++++----- spec/mongoid/token/options_spec.rb | 74 ++++++++++++++----------- spec/mongoid/token_spec.rb | 80 +++++++++++++++++---------- 6 files changed, 183 insertions(+), 106 deletions(-) diff --git a/lib/mongoid/token/collisions.rb b/lib/mongoid/token/collisions.rb index d26968d..acd5980 100644 --- a/lib/mongoid/token/collisions.rb +++ b/lib/mongoid/token/collisions.rb @@ -28,7 +28,8 @@ def raise_collision_retries_exceeded_error(field_name, retry_count) def is_duplicate_token_error?(err, document, field_name) err.message =~ /(11000|11001)/ && err.message =~ /dup key/ && - err.message =~ /"#{document.send(field_name)}"/ + err.message =~ /"#{document.send(field_name)}"/ && + true end end end diff --git a/spec/mongoid/token/collisions_spec.rb b/spec/mongoid/token/collisions_spec.rb index 6256fd3..5882b90 100644 --- a/spec/mongoid/token/collisions_spec.rb +++ b/spec/mongoid/token/collisions_spec.rb @@ -7,16 +7,16 @@ let(:resolver) { double("Mongoid::Token::CollisionResolver") } before(:each) do - resolver.stub(:field_name).and_return(:token) - resolver.stub(:create_new_token_for){|doc|} + allow(resolver).to receive(:field_name).and_return(:token) + allow(resolver).to receive(:create_new_token_for){|doc|} document.class.send(:include, Mongoid::Token::Collisions) - document.stub(:is_duplicate_token_error?).and_return(true) - document.class.stub(:resolvers).and_return([resolver]) + allow(document).to receive(:is_duplicate_token_error?).and_return(true) + allow(document.class).to receive(:resolvers).and_return([resolver]) end context "and there are zero retries" do it "should raise an error after the first try" do - resolver.stub(:retry_count).and_return(0) + allow(resolver).to receive(:retry_count).and_return(0) attempts = 0 expect do document.resolve_token_collisions do @@ -30,7 +30,7 @@ context "and retries is set to 1" do it "should raise an error after retrying once" do - resolver.stub(:retry_count).and_return(1) + allow(resolver).to receive(:retry_count).and_return(1) attempts = 0 expect do document.resolve_token_collisions do @@ -44,7 +44,7 @@ context "and retries is greater than 1" do it "should raise an error after retrying" do - resolver.stub(:retry_count).and_return(3) + allow(resolver).to receive(:retry_count).and_return(3) attempts = 0 expect do document.resolve_token_collisions do @@ -58,8 +58,9 @@ context "and a different index is violated" do it "should bubble the operation failure" do - document.stub(:is_duplicate_token_error?).and_return(false) - resolver.stub(:retry_count).and_return(3) + allow(document).to(receive(:is_duplicate_token_error?) + .and_return(false)) + allow(resolver).to receive(:retry_count).and_return(3) e = Mongo::Error::OperationFailure.new("nope") expect do document.resolve_token_collisions { raise e } @@ -80,8 +81,8 @@ stub_const("Rails", Class.new) logger = double("logger") - logger.stub("warn"){ |msg| message = msg } - Rails.stub("logger").and_return(logger) + allow(logger).to receive("warn") { |msg| message = msg } + allow(Rails).to receive("logger").and_return(logger) begin document.raise_collision_retries_exceeded_error(:token, 3) @@ -103,18 +104,18 @@ end context "when there is a duplicate key error" do it "should return true" do - document.stub("token").and_return("tokenvalue123") - err = double("Mongo::Error::OperationFailure") - err.stub("details").and_return do - { - "err" => "E11000 duplicate key error index: mongoid_token_test.links.$token_1 dup key: { : \"tokenvalue123\" }", - "code" => 11000, - "n" => 0, - "connectionId" => 130, - "ok" => 1.0 - } - document.is_duplicate_token_error?(err, document, :token) - end + allow(document).to receive("token").and_return("tokenvalue123") + err = double('Mongo::Error::OperationFailure') + allow(err).to(receive("message") + .and_return('insertDocument :: caused by :: 11000 E11000'\ + ' duplicate key error index: mongoid_token_'\ + 'test.documents.$token_1 dup key: '\ + '{ : "tokenvalue123" } (11000) (on localhost'\ + ':27017, legacy retry, attempt 1) (on localh'\ + 'ost:27017, legacy retry, attempt 1)')) + expect(document.is_duplicate_token_error?(err, document, :token)).to( + be(true) + ) end end end diff --git a/spec/mongoid/token/finders_spec.rb b/spec/mongoid/token/finders_spec.rb index 0f49893..d940229 100644 --- a/spec/mongoid/token/finders_spec.rb +++ b/spec/mongoid/token/finders_spec.rb @@ -2,29 +2,36 @@ describe Mongoid::Token::Finders do after do - Object.send(:remove_const, :Document) if Object.constants.include?(:Document) - Object.send(:remove_const, :AnotherDocument) if Object.constants.include?(:AnotherDocument) + if Object.constants.include?(:Document) + Object.send(:remove_const, :Document) + end + + if Object.constants.include?(:AnotherDocument) + Object.send(:remove_const, :AnotherDocument) + end end it "define a finder based on a field_name" do klass = Class.new field = :another_token Mongoid::Token::Finders.define_custom_token_finder_for(klass, field) - klass.singleton_methods.should include(:"find_by_#{field}") + expect(klass.singleton_methods).to include(:"find_by_#{field}") end it "retrieve a document using the dynamic finder" do class Document; include Mongoid::Document; field :token; end - document = Document.create!(:token => "1234") + document = Document.create!(token: "1234") Mongoid::Token::Finders.define_custom_token_finder_for(Document) - Document.find_by_token("1234").should == document + expect(Document.find_by_token("1234")).to eq(document) end it 'retrieves multiple documents using the dynamic finder' do class Document; include Mongoid::Document; field :token; end - document = Document.create!(:token => "1234") - document2 = Document.create!(:token => "5678") + document = Document.create!(token: "1234") + document2 = Document.create!(token: "5678") Mongoid::Token::Finders.define_custom_token_finder_for(Document) - Document.find_by_token(["1234", "5678"]).should == [document, document2] + expect(Document.find_by_token(["1234", "5678"])).to( + eq([document, document2]) + ) end end diff --git a/spec/mongoid/token/generator_spec.rb b/spec/mongoid/token/generator_spec.rb index fdc3a5a..86b111e 100644 --- a/spec/mongoid/token/generator_spec.rb +++ b/spec/mongoid/token/generator_spec.rb @@ -3,55 +3,91 @@ describe Mongoid::Token::Generator do describe "#generate" do it "generates lowercase characters" do - 100.times{ Mongoid::Token::Generator.generate("%c").should =~ /[a-z]/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%c")).to match(/[a-z]/) + end end it "generates uppercase characters" do - 100.times{ Mongoid::Token::Generator.generate("%C").should =~ /[A-Z]/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%C")).to match(/[A-Z]/) + end end it "generates digits" do - 100.times{ Mongoid::Token::Generator.generate("%d").should =~ /[0-9]/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%d")).to match(/[0-9]/) + end end it "generates non-zero digits" do - 100.times{ Mongoid::Token::Generator.generate("%D").should =~ /[1-9]/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%D")).to match(/[1-9]/) + end end it "generates hexdigits" do - 100.times{ Mongoid::Token::Generator.generate("%h").should =~ /[0-9a-f]/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%h")).to match(/[0-9a-f]/) + end end it "generates non-zero hexdigits" do - 100.times{ Mongoid::Token::Generator.generate("%H").should =~ /[1-9a-f]/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%H")).to match(/[1-9a-f]/) + end end it "generates alphanumeric characters" do - 100.times{ Mongoid::Token::Generator.generate("%s").should =~ /[A-Za-z0-9]/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%s")).to match(/[A-Za-z0-9]/) + end end it "generates upper and lowercase characters" do - 100.times{ Mongoid::Token::Generator.generate("%w").should =~ /[A-Za-z]/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%w")).to match(/[A-Za-z]/) + end end it "generates URL-safe punctuation" do - 100.times{ Mongoid::Token::Generator.generate("%p").should =~ /[\.\-\_\=\+\$]/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%p")).to( + match(/[\.\-\_\=\+\$]/) + ) + end end it "generates patterns of a fixed length" do - 100.times{ Mongoid::Token::Generator.generate("%s8").should =~ /[A-Za-z0-9]{8}/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%s8")).to( + match(/[A-Za-z0-9]{8}/) + ) + end end it "generates patterns of a variable length" do - 100.times{ Mongoid::Token::Generator.generate("%s1,5").should =~ /[A-Za-z0-9]{1,5}/ } + 100.times do + expect(Mongoid::Token::Generator.generate("%s1,5")).to( + match(/[A-Za-z0-9]{1,5}/) + ) + end end it "generates patterns with static prefixes/suffixes" do - 100.times { Mongoid::Token::Generator.generate("prefix-%s4-suffix").should =~ /prefix\-[A-Za-z0-9]{4}\-suffix/ } + 100.times do + expect(Mongoid::Token::Generator.generate("prefix-%s4-suffix")).to( + match(/prefix\-[A-Za-z0-9]{4}\-suffix/) + ) + end end it "generates more complex patterns" do - 100.times { Mongoid::Token::Generator.generate("pre-%d4-%C3-%d4").should =~ /pre\-[0-9]{4}\-[A-Z]{3}\-[0-9]{4}/ } + 100.times do + expect(Mongoid::Token::Generator.generate("pre-%d4-%C3-%d4")).to( + match(/pre\-[0-9]{4}\-[A-Z]{3}\-[0-9]{4}/) + ) + end end end end diff --git a/spec/mongoid/token/options_spec.rb b/spec/mongoid/token/options_spec.rb index bc186e7..4a3f677 100644 --- a/spec/mongoid/token/options_spec.rb +++ b/spec/mongoid/token/options_spec.rb @@ -1,56 +1,55 @@ require File.join(File.dirname(__FILE__), %w[.. .. spec_helper]) describe Mongoid::Token::Options do - before do - @options = Mongoid::Token::Options.new( - { - :length => 9999, - :retry_count => 8888, - :contains => :nonsense, - :field_name => :not_a_token - } - ) + + let(:options) do + Mongoid::Token::Options.new(length: 9999, + retry_count: 8888, + contains: :nonsense, + field_name: :not_a_token) end it "should have a length" do - @options.length.should == 9999 + expect(options.length).to eq(9999) end it "should default to a length of 4" do - Mongoid::Token::Options.new.length.should == 4 + expect(Mongoid::Token::Options.new.length).to eq(4) end it "should have a retry count" do - @options.retry_count.should == 8888 + expect(options.retry_count).to eq(8888) end it "should default to a retry count of 3" do - Mongoid::Token::Options.new.retry_count.should == 3 + expect(Mongoid::Token::Options.new.retry_count).to eq(3) end it "should have a list of characters to contain" do - @options.contains.should == :nonsense + expect(options.contains).to eq(:nonsense) end it "should default to an alphanumeric set of characters to contain" do - Mongoid::Token::Options.new.contains.should == :alphanumeric + expect(Mongoid::Token::Options.new.contains).to eq(:alphanumeric) end it "should have a field name" do - @options.field_name.should == :not_a_token + expect(options.field_name).to eq(:not_a_token) end it "should default to a field name of 'token'" do - Mongoid::Token::Options.new.field_name.should == :token + expect(Mongoid::Token::Options.new.field_name).to eq(:token) end it "should create a pattern" do - Mongoid::Token::Options.new.pattern.should == "%s4" + expect(Mongoid::Token::Options.new.pattern).to eq("%s4") end describe "override_to_param" do + let(:options) { Mongoid::Token::Options.new(override_to_param: false) } + it "should be an option" do - expect(Mongoid::Token::Options.new({:override_to_param => false}).override_to_param?).to eq false + expect(options.override_to_param?).to eq false end it "should default to true" do @@ -58,9 +57,10 @@ end end - describe "skip_finder" do + describe "skip_finder" do + let(:options) { Mongoid::Token::Options.new(skip_finders: true) } it "should be an option" do - expect(Mongoid::Token::Options.new({:skip_finders => true}).skip_finders?).to eq true + expect(options.skip_finders?).to eq true end it "should default to false" do @@ -70,33 +70,43 @@ describe "id" do context "when true" do + let(:options) do + Mongoid::Token::Options.new(id: true, field_name: :a_token) + end + it "returns '_id' sa the field name" do - expect(Mongoid::Token::Options.new({id: true, field_name: :a_token}).field_name).to eq :_id + expect(options.field_name).to eq :_id end end context "when false" do + let(:options) do + Mongoid::Token::Options.new(id: false, field_name: :a_token) + end + it "returns the field_name option as the field name" do - expect(Mongoid::Token::Options.new({id: false, field_name: :a_token}).field_name).to eq :a_token + expect(options.field_name).to eq :a_token end end end describe :generate_on_init do - it "defaults to false" do - expect(Mongoid::Token::Options.new({}).generate_on_init).to eq false - end + let(:options) { Mongoid::Token::Options.new(params) } + let(:params) { {} } + subject { options.generate_on_init } + + it { is_expected.to be(false) } context "when id option set" do - it "is true" do - expect(Mongoid::Token::Options.new({id: true}).generate_on_init).to eq true - end + let(:params) { { id: true } } + + it { is_expected.to be(true) } end context "when id option is not set" do - it "is false" do - expect(Mongoid::Token::Options.new({id: false}).generate_on_init).to eq false - end + let(:params) { { id: false } } + + it { is_expected.to be(false) } end end end diff --git a/spec/mongoid/token_spec.rb b/spec/mongoid/token_spec.rb index e4c80aa..fd69087 100644 --- a/spec/mongoid/token_spec.rb +++ b/spec/mongoid/token_spec.rb @@ -2,9 +2,17 @@ describe Mongoid::Token do after do - Object.send(:remove_const, :Document) if Object.constants.include?(:Document) - Object.send(:remove_const, :AnotherDocument) if Object.constants.include?(:AnotherDocument) - Object.send(:remove_const, :UntaintedDocument) if Object.constants.include?(:UntaintedDocument) + if Object.constants.include?(:Document) + Object.send(:remove_const, :Document) + end + + if Object.constants.include?(:AnotherDocument) + Object.send(:remove_const, :AnotherDocument) + end + + if Object.constants.include?(:UntaintedDocument) + Object.send(:remove_const, :UntaintedDocument) + end end let(:document_class) do @@ -36,12 +44,12 @@ class Document describe "options" do it "should accept custom field names" do - document_class.send(:token, :field_name => :smells_as_sweet) + document_class.send(:token, field_name: :smells_as_sweet) expect(document).to have_field(:smells_as_sweet) end it "should accept custom lengths" do - document_class.send(:token, :length => 13) + document_class.send(:token, length: 13) expect(document.token.length).to eq 13 end @@ -52,17 +60,17 @@ class UntaintedDocument end dc = Class.new(UntaintedDocument) - dc.send(:token, :skip_finders => true) + dc.send(:token, skip_finders: true) expect(dc.public_methods).to_not include(:find_with_token) end it "should disable `to_param` overrides" do - document_class.send(:token, :override_to_param => false) + document_class.send(:token, override_to_param: false) expect(document.to_param).to_not eq document.token end it "should return id when token does not exist when calling `to_param`" do - document_class.send(:token, :override_to_param => true) + document_class.send(:token, override_to_param: true) document.unset :token expect(document.to_param).to eq document.id.to_s end @@ -70,43 +78,45 @@ class UntaintedDocument describe "contains" do context "with :alphanumeric" do it "should contain only letters and numbers" do - document_class.send(:token, :contains => :alphanumeric, :length => 64) + document_class.send(:token, contains: :alphanumeric, length: 64) expect(document.token).to match(/[A-Za-z0-9]{64}/) end end context "with :alpha" do it "should contain only letters" do - document_class.send(:token, :contains => :alpha, :length => 64) + document_class.send(:token, contains: :alpha, length: 64) expect(document.token).to match(/[A-Za-z]{64}/) end end context "with :alpha_upper" do it "should contain only uppercase letters" do - document_class.send(:token, :contains => :alpha_upper, :length => 64) + document_class.send(:token, contains: :alpha_upper, length: 64) expect(document.token).to match(/[A-Z]{64}/) end end context "with :alpha_lower" do it "should contain only lowercase letters" do - document_class.send(:token, :contains => :alpha_lower, :length => 64) + document_class.send(:token, contains: :alpha_lower, length: 64) expect(document.token).to match(/[a-z]{64}/) end end context "with :numeric" do it "should only contain numbers" do - document_class.send(:token, :contains => :numeric, :length => 64) + document_class.send(:token, contains: :numeric, length: 64) expect(document.token).to match(/[0-9]{1,64}/) end end context "with :fixed_numeric" do it "should contain only numbers and be a fixed-length" do - document_class.send(:token, :contains => :fixed_numeric, :length => 64) + document_class.send(:token, contains: :fixed_numeric, length: 64) expect(document.token).to match(/[0-9]{64}/) end end context "with :fixed_numeric_no_leading_zeros" do it "should contain only numbers, be a fixed length, and have no leading zeros" do - document_class.send(:token, :contains => :fixed_numeric_no_leading_zeros, :length => 64) + document_class.send(:token, + contains: :fixed_numeric_no_leading_zeros, + length: 64) expect(document.token).to match(/[1-9]{1}[0-9]{63}/) end end @@ -220,13 +230,13 @@ class Doc end context "when the document is cloned" do it "should set the token to nil" do - document.class.send(:token, :length => 64, :contains => :alpha_upper) + document.class.send(:token, length: 64, contains: :alpha_upper) d2 = document.clone expect(d2.token).to be_nil end it "should generate a new token with the same options as the source document" do - document.class.send(:token, :length => 64, :contains => :alpha_upper) + document.class.send(:token, length: 64, contains: :alpha_upper) d2 = document.clone d2.save expect(d2.token).to_not eq document.token @@ -237,7 +247,7 @@ class Doc describe "finders" do it "should create a custom find method" do - document_class.send(:token, :field_name => :other_token) + document_class.send(:token, field_name: :other_token) expect(document.class.public_methods).to include(:find_by_other_token) end end @@ -260,15 +270,21 @@ class Doc document.token = "1234" document.save d2 = document.clone - d2.stub(:generate_token).and_return("1234") - expect{d2.save}.to raise_exception(Mongoid::Token::CollisionRetriesExceeded) + allow(d2).to receive(:generate_token).and_return("1234") + expect { d2.save }.to( + raise_exception(Mongoid::Token::CollisionRetriesExceeded) + ) end it "should raise an exception when collisions can't be resolved on create!" do document.token = "1234" document.save - document_class.any_instance.stub(:generate_token).and_return("1234") - expect{document_class.create!}.to raise_exception(Mongoid::Token::CollisionRetriesExceeded) + allow_any_instance_of(document_class).to( + receive(:generate_token).and_return("1234") + ) + expect { document_class.create! }.to( + raise_exception(Mongoid::Token::CollisionRetriesExceeded) + ) end end @@ -276,23 +292,29 @@ class Doc I18n.enforce_available_locales = false # Supress warnings in this example document_class.send(:field, :name) document_class.send(:validates_presence_of, :name) - document_class.any_instance.stub(:generate_token).and_return("1234") - document_class.stub(:model_name).and_return(ActiveModel::Name.new(document_class, nil, "temp")) - expect{document_class.create!}.to raise_exception(Mongoid::Errors::Validations) + allow_any_instance_of(document_class).to(receive(:generate_token) + .and_return("1234")) + allow(document_class).to(receive(:model_name) + .and_return(ActiveModel::Name.new(document_class, nil, "temp"))) + expect { document_class.create! }.to( + raise_exception(Mongoid::Errors::Validations) + ) end context "with other unique indexes present" do before(:each) do document_class.send(:field, :name) - document_class.send(:index, {:name => 1}, {:unique => true}) + document_class.send(:index, { name: 1 }, unique: true) document_class.create_indexes end context "when violating the other index" do it "should raise an operation failure" do duplicate_name = "Got Duped." - document_class.create!(:name => duplicate_name) - expect{ document_class.create!(:name => duplicate_name) }.to raise_exception(Mongo::Error::OperationFailure) + document_class.create!(name: duplicate_name) + expect { document_class.create!(name: duplicate_name) }.to( + raise_exception(Mongo::Error::OperationFailure) + ) end end end @@ -310,7 +332,7 @@ class Doc $stdout = @orig_stdout end - let(:document){ document_class.new } + let(:document) { document_class.new } it "should replace the _id field with a token" do expect(document).to have_field(:_id) From c27cbf532c764d0e0453d3aa18bb2c506da9e7cb Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Tue, 3 Dec 2019 10:20:27 +0100 Subject: [PATCH 11/15] Add appraisals Add rails 5.1 Add rails 5.1 to travisci --- .gitignore | 1 + .travis.yml | 13 ++++++++----- Appraisals | 12 ++++++++++++ gemfiles/mongoid_6.gemfile | 14 ++++++++++++++ gemfiles/mongoid_6_rails_5.1.gemfile | 15 +++++++++++++++ gemfiles/mongoid_7.gemfile | 14 ++++++++++++++ mongoid_token.gemspec | 26 ++++++++++++++------------ 7 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 Appraisals create mode 100644 gemfiles/mongoid_6.gemfile create mode 100644 gemfiles/mongoid_6_rails_5.1.gemfile create mode 100644 gemfiles/mongoid_7.gemfile diff --git a/.gitignore b/.gitignore index 2f0a9ac..f9baecb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ Gemfile.lock pkg/* .DS_Store +gemfiles/*.gemfile.lock diff --git a/.travis.yml b/.travis.yml index a1c7a84..bbfe74c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,13 @@ language: ruby -rvm: - - 2.2.2 - -gemfile: - - Gemfile +matrix: + include: + - rvm: 2.5.7 + gemfile: gemfiles/mongoid_7.gemfile + - rvm: 2.5.7 + gemfile: gemfiles/mongoid_6.gemfile + - rvm: 2.5.7 + gemfile: gemfiles/mongoid_6_rails_5.1.gemfile services: - mongodb diff --git a/Appraisals b/Appraisals new file mode 100644 index 0000000..5916e51 --- /dev/null +++ b/Appraisals @@ -0,0 +1,12 @@ +appraise 'mongoid-7' do + gem 'mongoid', '~> 7.0' +end + +appraise 'mongoid-6' do + gem 'mongoid', '~> 6.0' +end + +appraise 'mongoid-6-rails-5.1' do + gem 'mongoid', '~> 6.0' + gem 'rails', '~> 5.1' +end diff --git a/gemfiles/mongoid_6.gemfile b/gemfiles/mongoid_6.gemfile new file mode 100644 index 0000000..b05525a --- /dev/null +++ b/gemfiles/mongoid_6.gemfile @@ -0,0 +1,14 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "mongoid", "~> 6.0" + +group :test do + gem "codeclimate-test-reporter", require: nil + gem "database_cleaner" + gem "mongoid-rspec" + gem "rspec" +end + +gemspec path: "../" diff --git a/gemfiles/mongoid_6_rails_5.1.gemfile b/gemfiles/mongoid_6_rails_5.1.gemfile new file mode 100644 index 0000000..d1dcf14 --- /dev/null +++ b/gemfiles/mongoid_6_rails_5.1.gemfile @@ -0,0 +1,15 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "mongoid", "~> 6.0" +gem "rails", "~> 5.1" + +group :test do + gem "codeclimate-test-reporter", require: nil + gem "database_cleaner" + gem "mongoid-rspec" + gem "rspec" +end + +gemspec path: "../" diff --git a/gemfiles/mongoid_7.gemfile b/gemfiles/mongoid_7.gemfile new file mode 100644 index 0000000..c5148a1 --- /dev/null +++ b/gemfiles/mongoid_7.gemfile @@ -0,0 +1,14 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "mongoid", "~> 7.0" + +group :test do + gem "codeclimate-test-reporter", require: nil + gem "database_cleaner" + gem "mongoid-rspec" + gem "rspec" +end + +gemspec path: "../" diff --git a/mongoid_token.gemspec b/mongoid_token.gemspec index 16f88c8..bd42de8 100644 --- a/mongoid_token.gemspec +++ b/mongoid_token.gemspec @@ -1,24 +1,26 @@ -# -*- encoding: utf-8 -*- -$:.push File.expand_path("../lib", __FILE__) -require "version" +$LOAD_PATH.push File.expand_path('lib', __dir__) +require 'version' Gem::Specification.new do |s| - s.name = "mongoid_token" + s.name = 'mongoid_token' s.version = MongoidToken::VERSION s.platform = Gem::Platform::RUBY - s.authors = ["Nicholas Bruning"] - s.email = ["nicholas@bruning.com.au"] - s.homepage = "http://github.com/thetron/mongoid_token" - s.licenses = ['MIT'] + s.authors = ['Nicholas Bruning'] + s.email = %w[nicholas@bruning.com.au] + s.homepage = 'http://github.com/thetron/mongoid_token' + s.licenses = %w[MIT] s.summary = %q{A little random, unique token generator for Mongoid documents.} s.description = %q{Mongoid token is a gem for creating random, unique tokens for mongoid documents. Highly configurable and great for making URLs a little more compact.} - s.rubyforge_project = "mongoid_token" - s.add_dependency 'mongoid', '~> 7' + s.rubyforge_project = 'mongoid_token' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") - s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } - s.require_paths = ["lib"] + s.executables = `git ls-files -- bin/*`.split("\n") + .map { |f| File.basename(f) } + s.require_paths = %w[lib] + s.add_dependency 'mongoid', '>= 6' s.add_dependency 'zeitwerk' + s.add_development_dependency 'appraisal', '~> 2.2' + s.add_development_dependency 'wwtd' end From f86f7c4b7c4a65447c065c4aeca85f67b627156f Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Tue, 3 Dec 2019 11:36:47 +0100 Subject: [PATCH 12/15] Use more ruby naming convention duplicate_token_error? --- lib/mongoid/token/collisions.rb | 4 ++-- spec/mongoid/token/collisions_spec.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/mongoid/token/collisions.rb b/lib/mongoid/token/collisions.rb index acd5980..6e1fefe 100644 --- a/lib/mongoid/token/collisions.rb +++ b/lib/mongoid/token/collisions.rb @@ -7,7 +7,7 @@ def resolve_token_collisions yield rescue Mongo::Error::OperationFailure => e resolver = self.class.resolvers.select do |r| - is_duplicate_token_error?(e, self, r.field_name) + duplicate_token_error?(e, self, r.field_name) end.first raise e unless resolver retries ||= resolver.retry_count @@ -25,7 +25,7 @@ def raise_collision_retries_exceeded_error(field_name, retry_count) raise Mongoid::Token::CollisionRetriesExceeded.new(self, retry_count) end - def is_duplicate_token_error?(err, document, field_name) + def duplicate_token_error?(err, document, field_name) err.message =~ /(11000|11001)/ && err.message =~ /dup key/ && err.message =~ /"#{document.send(field_name)}"/ && diff --git a/spec/mongoid/token/collisions_spec.rb b/spec/mongoid/token/collisions_spec.rb index 5882b90..7cd1c0e 100644 --- a/spec/mongoid/token/collisions_spec.rb +++ b/spec/mongoid/token/collisions_spec.rb @@ -10,7 +10,7 @@ allow(resolver).to receive(:field_name).and_return(:token) allow(resolver).to receive(:create_new_token_for){|doc|} document.class.send(:include, Mongoid::Token::Collisions) - allow(document).to receive(:is_duplicate_token_error?).and_return(true) + allow(document).to receive(:duplicate_token_error?).and_return(true) allow(document.class).to receive(:resolvers).and_return([resolver]) end @@ -58,7 +58,7 @@ context "and a different index is violated" do it "should bubble the operation failure" do - allow(document).to(receive(:is_duplicate_token_error?) + allow(document).to(receive(:duplicate_token_error?) .and_return(false)) allow(resolver).to receive(:retry_count).and_return(3) e = Mongo::Error::OperationFailure.new("nope") @@ -98,7 +98,7 @@ end end - describe "#is_duplicate_token_error?" do + describe "#duplicate_token_error?" do before(:each) do document.class.send(:include, Mongoid::Token::Collisions) end @@ -113,7 +113,7 @@ '{ : "tokenvalue123" } (11000) (on localhost'\ ':27017, legacy retry, attempt 1) (on localh'\ 'ost:27017, legacy retry, attempt 1)')) - expect(document.is_duplicate_token_error?(err, document, :token)).to( + expect(document.duplicate_token_error?(err, document, :token)).to( be(true) ) end From d53a1f8f43fdfecabea24297b5fb0123851cc8f0 Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Tue, 3 Dec 2019 13:18:06 +0100 Subject: [PATCH 13/15] Ensure collisions to work on mongodb 2.6. 3.0 and 4.0 --- lib/mongoid/token/collisions.rb | 2 +- spec/mongoid/token/collisions_spec.rb | 41 +++++++++++++++++++-------- spec/mongoid/token_spec.rb | 2 +- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/lib/mongoid/token/collisions.rb b/lib/mongoid/token/collisions.rb index 6e1fefe..b6ce31b 100644 --- a/lib/mongoid/token/collisions.rb +++ b/lib/mongoid/token/collisions.rb @@ -26,7 +26,7 @@ def raise_collision_retries_exceeded_error(field_name, retry_count) end def duplicate_token_error?(err, document, field_name) - err.message =~ /(11000|11001)/ && + [11000, 11001].include?(err.code) && err.message =~ /dup key/ && err.message =~ /"#{document.send(field_name)}"/ && true diff --git a/spec/mongoid/token/collisions_spec.rb b/spec/mongoid/token/collisions_spec.rb index 7cd1c0e..89a3b3f 100644 --- a/spec/mongoid/token/collisions_spec.rb +++ b/spec/mongoid/token/collisions_spec.rb @@ -103,19 +103,36 @@ document.class.send(:include, Mongoid::Token::Collisions) end context "when there is a duplicate key error" do - it "should return true" do + before do allow(document).to receive("token").and_return("tokenvalue123") - err = double('Mongo::Error::OperationFailure') - allow(err).to(receive("message") - .and_return('insertDocument :: caused by :: 11000 E11000'\ - ' duplicate key error index: mongoid_token_'\ - 'test.documents.$token_1 dup key: '\ - '{ : "tokenvalue123" } (11000) (on localhost'\ - ':27017, legacy retry, attempt 1) (on localh'\ - 'ost:27017, legacy retry, attempt 1)')) - expect(document.duplicate_token_error?(err, document, :token)).to( - be(true) - ) + allow(err).to(receive("message").and_return(message)) + end + let(:err) { double('Mongo::Error::OperationFailure', code: 11000) } + subject { document.duplicate_token_error?(err, document, :token) } + + context "mongodb version 2.6, 3.0" do + let(:message) do + "insertDocument :: caused by :: 11000 "\ + "E11000 duplicate key error index: "\ + "mongoid_token_test.documents.$token_1 "\ + "dup key: { : \"tokenvalue123\" } (11000) "\ + "(on localhost:27017, legacy retry, attempt 1) "\ + "(on localhost:27017, legacy retry, attempt 1)" + end + + it { is_expected.to be(true) } + end + + context "mongodb version 4" do + let(:message) do + "E11000 duplicate key error collection: "\ + "mongoid_token_test.docs index: token_1 "\ + "dup key: { : \"tokenvalue123\" } (11000) "\ + "(on localhost:27017, legacy retry, attempt 1) "\ + "(on localhost:27017, legacy retry, attempt 1)" + end + + it { is_expected.to be(true) } end end end diff --git a/spec/mongoid/token_spec.rb b/spec/mongoid/token_spec.rb index fd69087..48a72d4 100644 --- a/spec/mongoid/token_spec.rb +++ b/spec/mongoid/token_spec.rb @@ -182,7 +182,7 @@ class Doc let(:doc) { Doc.create(foo: "hello") } let(:dup_doc) { Doc.new } let(:exception) do - /E11000 duplicate key error index: mongoid_token_test.docs\.\$foo_1/ + /(E11000 duplicate key error index: mongoid_token_test.docs\.\$foo_1|E11000 duplicate key error collection: mongoid_token_test\.docs index: foo_1 dup key)/ end it do From 9e04f9cef7e44a0f986bad7844ef9d3d268eeed5 Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Tue, 3 Dec 2019 14:20:38 +0100 Subject: [PATCH 14/15] Fix rubocop errors --- Gemfile | 6 +- lib/mongoid/token.rb | 19 ++++-- lib/mongoid/token/collision_resolver.rb | 2 + .../token/collision_retries_exceeded.rb | 5 +- lib/mongoid/token/collisions.rb | 11 ++- lib/mongoid/token/error.rb | 2 + lib/mongoid/token/finders.rb | 4 +- lib/mongoid/token/generator.rb | 68 ++++++++++--------- lib/mongoid/token/options.rb | 43 ++++++------ lib/mongoid_token.rb | 6 +- spec/mongoid/token/collisions_spec.rb | 10 +-- spec/mongoid/token/finders_spec.rb | 18 +++-- spec/mongoid/token/options_spec.rb | 1 - spec/mongoid/token_spec.rb | 56 +++++++++------ spec/spec_helper.rb | 14 ++-- 15 files changed, 155 insertions(+), 110 deletions(-) diff --git a/Gemfile b/Gemfile index 0cb708f..8cce338 100644 --- a/Gemfile +++ b/Gemfile @@ -3,8 +3,8 @@ source "http://rubygems.org" gemspec group :test do - gem 'rspec' gem "codeclimate-test-reporter", require: nil - gem 'database_cleaner' - gem 'mongoid-rspec' + gem "database_cleaner" + gem "mongoid-rspec" + gem "rspec" end diff --git a/lib/mongoid/token.rb b/lib/mongoid/token.rb index ff02d71..30663e0 100644 --- a/lib/mongoid/token.rb +++ b/lib/mongoid/token.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Mongoid module Token extend ActiveSupport::Concern @@ -24,9 +26,10 @@ def resolvers end private + def add_token_field_and_index(options) - self.field options.field_name, :type => String, :default => default_value(options) - self.index({ options.field_name => 1 }, { :unique => true, :sparse => true }) + field options.field_name, type: String, default: default_value(options) + index({ options.field_name => 1 }, unique: true, sparse: true) end def add_token_collision_resolver(options) @@ -55,24 +58,26 @@ def set_token_callbacks(options) end def override_to_param(options) - self.send(:define_method, :to_param) do - self.send(options.field_name) || super() + send(:define_method, :to_param) do + send(options.field_name) || super() end end def default_value(options) - options.generate_on_init && Mongoid::Token::Generator.generate(options.pattern) || nil + options.generate_on_init && + Mongoid::Token::Generator.generate(options.pattern) || nil end end protected + def create_token(field_name, pattern) - self.send :"#{field_name.to_s}=", self.generate_token(pattern) + send :"#{field_name}=", generate_token(pattern) end def create_token_if_nil(field_name, pattern) if self[field_name.to_sym].blank? - self.create_token field_name, pattern + create_token field_name, pattern end end diff --git a/lib/mongoid/token/collision_resolver.rb b/lib/mongoid/token/collision_resolver.rb index 988c93b..d8fbde9 100644 --- a/lib/mongoid/token/collision_resolver.rb +++ b/lib/mongoid/token/collision_resolver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Mongoid module Token module SafeOperationsHandler diff --git a/lib/mongoid/token/collision_retries_exceeded.rb b/lib/mongoid/token/collision_retries_exceeded.rb index 801f84b..28f7c9b 100644 --- a/lib/mongoid/token/collision_retries_exceeded.rb +++ b/lib/mongoid/token/collision_retries_exceeded.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Mongoid module Token class CollisionRetriesExceeded < Error @@ -7,7 +9,8 @@ def initialize(resource = "unknown resource", attempts = "unspecified") end def to_s - "Failed to generate unique token for #{@resource} after #{@attempts} attempts." + "Failed to generate unique token for #{@resource} after #{@attempts} "\ + "attempts." end end end diff --git a/lib/mongoid/token/collisions.rb b/lib/mongoid/token/collisions.rb index b6ce31b..ba0ed79 100644 --- a/lib/mongoid/token/collisions.rb +++ b/lib/mongoid/token/collisions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Mongoid module Token module Collisions @@ -10,6 +12,7 @@ def resolve_token_collisions duplicate_token_error?(e, self, r.field_name) end.first raise e unless resolver + retries ||= resolver.retry_count if (retries -= 1) >= 0 resolver.create_new_token_for(self) @@ -21,12 +24,16 @@ def resolve_token_collisions end def raise_collision_retries_exceeded_error(field_name, retry_count) - Rails.logger.warn "[Mongoid::Token] Warning: Maximum token generation retries (#{retry_count}) exceeded on `#{field_name}'." if defined?(Rails) + if defined?(Rails) + Rails.logger.warn "[Mongoid::Token] Warning: Maximum token "\ + "generation retries (#{retry_count}) exceeded on "\ + "`#{field_name}'." + end raise Mongoid::Token::CollisionRetriesExceeded.new(self, retry_count) end def duplicate_token_error?(err, document, field_name) - [11000, 11001].include?(err.code) && + [11_000, 11_001].include?(err.code) && err.message =~ /dup key/ && err.message =~ /"#{document.send(field_name)}"/ && true diff --git a/lib/mongoid/token/error.rb b/lib/mongoid/token/error.rb index e09bb7f..f952f9e 100644 --- a/lib/mongoid/token/error.rb +++ b/lib/mongoid/token/error.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Mongoid module Token class Error < StandardError; end diff --git a/lib/mongoid/token/finders.rb b/lib/mongoid/token/finders.rb index f85d3ad..0ba25e3 100644 --- a/lib/mongoid/token/finders.rb +++ b/lib/mongoid/token/finders.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Mongoid module Token module Finders @@ -6,7 +8,7 @@ def self.define_custom_token_finder_for(klass, field_name = :token) if token.is_a?(Array) self.in field_name.to_sym => token else - self.find_by field_name.to_sym => token + find_by field_name.to_sym => token end end end diff --git a/lib/mongoid/token/generator.rb b/lib/mongoid/token/generator.rb index d407408..bf91d41 100644 --- a/lib/mongoid/token/generator.rb +++ b/lib/mongoid/token/generator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # proposed pattern options # %c - lowercase character # %C - uppercase character @@ -7,57 +9,50 @@ # %w - upper and lower alpha character # %p - URL-safe punctuation # -# Any pattern can be followed by a number, representing how many of that type to generate +# Any pattern can be followed by a number, representing how many of that type +# to generate module Mongoid module Token module Generator REPLACE_PATTERN = /%((?[cCdDhHpsw]{1})(?\d+(,\d+)?)?)/ + TYPES = { + c: ->(length) { down_character(length) }, + C: ->(length) { up_character(length) }, + d: ->(length) { digits(length) }, + D: ->(length) { integer(length) }, + h: ->(length) { digits(length, 16) }, + H: ->(length) { integer(length, 16) }, + s: ->(length) { alphanumeric(length) }, + w: ->(length) { alpha(length) }, + p: ->(_length) { "-" } + }.freeze def self.generate(pattern) - pattern.gsub REPLACE_PATTERN do |match| - match_data = $~ - type = match_data[:character] - length = [match_data[:length].to_i, 1].max + pattern.gsub REPLACE_PATTERN do + match = $~ + type = match[:character] + length = [match[:length].to_i, 1].max - case type - when 'c' - down_character(length) - when 'C' - up_character(length) - when 'd' - digits(length) - when 'D' - integer(length) - when 'h' - digits(length, 16) - when 'H' - integer(length, 16) - when 's' - alphanumeric(length) - when 'w' - alpha(length) - when 'p' - "-" - end + TYPES[type.to_sym].call(length) end end - private def self.rand_string_from_chars(chars, length = 1) - Array.new(length).map{ chars.sample }.join + Array.new(length).map { chars.sample }.join end def self.down_character(length = 1) - self.rand_string_from_chars ('a'..'z').to_a, length + rand_string_from_chars ("a".."z").to_a, length end def self.up_character(length = 1) - self.rand_string_from_chars ('A'..'Z').to_a, length + rand_string_from_chars ("A".."Z").to_a, length end def self.integer(length = 1, base = 10) - (rand(base**length - base**(length-1)) + base**(length-1)).to_s(base) + (rand(base**length - base**(length - 1)) + base**(length - 1)). + to_s(base) end def self.digits(length = 1, base = 10) @@ -65,15 +60,22 @@ def self.digits(length = 1, base = 10) end def self.alpha(length = 1) - self.rand_string_from_chars (('A'..'Z').to_a + ('a'..'z').to_a), length + rand_string_from_chars (("A".."Z").to_a + ("a".."z").to_a), length end def self.alphanumeric(length = 1) - (1..length).collect { (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr }.join + (1..length).map do + i = Kernel.rand(62) + if i < 10 + i + 48 + else + i + (i < 36 ? 55 : 61) + end.chr + end.join end def self.punctuation(length = 1) - self.rand_string_from_chars ['.','-','_','=','+','$'], length + rand_string_from_chars %w[. - _ = + $], length end end end diff --git a/lib/mongoid/token/options.rb b/lib/mongoid/token/options.rb index d78c835..17805b2 100644 --- a/lib/mongoid/token/options.rb +++ b/lib/mongoid/token/options.rb @@ -1,6 +1,20 @@ +# frozen_string_literal: true + module Mongoid module Token class Options + PATTERNS = { + alphanumeric: ->(length) { "%s#{length}" }, + alpha: ->(length) { "%w#{length}" }, + alpha_upper: ->(length) { "%C#{length}" }, + alpha_lower: ->(length) { "%c#{length}" }, + numeric: ->(length) { "%d1,#{length}" }, + fixed_numeric: ->(length) { "%d#{length}" }, + fixed_numeric_no_leading_zeros: ->(length) { "%D#{length}" }, + fixed_hex_numeric: ->(length) { "%h#{length}" }, + fixed_hex_numeric_no_leading_zeros: ->(length) { "%H#{length}" } + }.freeze + def initialize(options = {}) @options = merge_defaults validate_options(options) end @@ -34,32 +48,17 @@ def generate_on_init end def pattern - @options[:pattern] ||= case @options[:contains].to_sym - when :alphanumeric - "%s#{@options[:length]}" - when :alpha - "%w#{@options[:length]}" - when :alpha_upper - "%C#{@options[:length]}" - when :alpha_lower - "%c#{@options[:length]}" - when :numeric - "%d1,#{@options[:length]}" - when :fixed_numeric - "%d#{@options[:length]}" - when :fixed_numeric_no_leading_zeros - "%D#{@options[:length]}" - when :fixed_hex_numeric - "%h#{@options[:length]}" - when :fixed_hex_numeric_no_leading_zeros - "%H#{@options[:length]}" - end + @options[:pattern] ||= + PATTERNS[@options[:contains]].call(@options[:length]) end private + def validate_options(options) - if options.has_key?(:retry) - STDERR.puts "Mongoid::Token Deprecation Warning: option `retry` has been renamed to `retry_count`. `:retry` will be removed in v2.1" + if options.key?(:retry) + warn "Mongoid::Token Deprecation Warning: option `retry` has "\ + "been renamed to `retry_count`. `:retry` will be "\ + "removed in v2.1" options[:retry_count] = options[:retry] end options diff --git a/lib/mongoid_token.rb b/lib/mongoid_token.rb index e106622..69f41dc 100644 --- a/lib/mongoid_token.rb +++ b/lib/mongoid_token.rb @@ -1,4 +1,6 @@ -require 'zeitwerk' -require 'active_support/concern' +# frozen_string_literal: true + +require "zeitwerk" +require "active_support/concern" loader = Zeitwerk::Loader.for_gem loader.setup diff --git a/spec/mongoid/token/collisions_spec.rb b/spec/mongoid/token/collisions_spec.rb index 89a3b3f..dfb5e91 100644 --- a/spec/mongoid/token/collisions_spec.rb +++ b/spec/mongoid/token/collisions_spec.rb @@ -8,7 +8,7 @@ before(:each) do allow(resolver).to receive(:field_name).and_return(:token) - allow(resolver).to receive(:create_new_token_for){|doc|} + allow(resolver).to receive(:create_new_token_for) { |doc| } document.class.send(:include, Mongoid::Token::Collisions) allow(document).to receive(:duplicate_token_error?).and_return(true) allow(document.class).to receive(:resolvers).and_return([resolver]) @@ -58,8 +58,8 @@ context "and a different index is violated" do it "should bubble the operation failure" do - allow(document).to(receive(:duplicate_token_error?) - .and_return(false)) + allow(document).to(receive(:duplicate_token_error?). + and_return(false)) allow(resolver).to receive(:retry_count).and_return(3) e = Mongo::Error::OperationFailure.new("nope") expect do @@ -93,7 +93,7 @@ it "should raise an error" do expect { document.raise_collision_retries_exceeded_error(:token, 3) }.to( - raise_error(Mongoid::Token::CollisionRetriesExceeded) + raise_error(Mongoid::Token::CollisionRetriesExceeded), ) end end @@ -107,7 +107,7 @@ allow(document).to receive("token").and_return("tokenvalue123") allow(err).to(receive("message").and_return(message)) end - let(:err) { double('Mongo::Error::OperationFailure', code: 11000) } + let(:err) { double("Mongo::Error::OperationFailure", code: 11_000) } subject { document.duplicate_token_error?(err, document, :token) } context "mongodb version 2.6, 3.0" do diff --git a/spec/mongoid/token/finders_spec.rb b/spec/mongoid/token/finders_spec.rb index d940229..8d7ffa2 100644 --- a/spec/mongoid/token/finders_spec.rb +++ b/spec/mongoid/token/finders_spec.rb @@ -19,19 +19,25 @@ end it "retrieve a document using the dynamic finder" do - class Document; include Mongoid::Document; field :token; end + class Document + include Mongoid::Document + field :token + end + document = Document.create!(token: "1234") Mongoid::Token::Finders.define_custom_token_finder_for(Document) expect(Document.find_by_token("1234")).to eq(document) end - it 'retrieves multiple documents using the dynamic finder' do - class Document; include Mongoid::Document; field :token; end + it "retrieves multiple documents using the dynamic finder" do + class Document + include Mongoid::Document + field :token + end + document = Document.create!(token: "1234") document2 = Document.create!(token: "5678") Mongoid::Token::Finders.define_custom_token_finder_for(Document) - expect(Document.find_by_token(["1234", "5678"])).to( - eq([document, document2]) - ) + expect(Document.find_by_token(%w[1234 5678])).to(eq([document, document2])) end end diff --git a/spec/mongoid/token/options_spec.rb b/spec/mongoid/token/options_spec.rb index 4a3f677..efca5aa 100644 --- a/spec/mongoid/token/options_spec.rb +++ b/spec/mongoid/token/options_spec.rb @@ -1,7 +1,6 @@ require File.join(File.dirname(__FILE__), %w[.. .. spec_helper]) describe Mongoid::Token::Options do - let(:options) do Mongoid::Token::Options.new(length: 9999, retry_count: 8888, diff --git a/spec/mongoid/token_spec.rb b/spec/mongoid/token_spec.rb index 48a72d4..1dfb1c4 100644 --- a/spec/mongoid/token_spec.rb +++ b/spec/mongoid/token_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require File.join(File.dirname(__FILE__), %w[.. spec_helper]) describe Mongoid::Token do @@ -113,7 +115,8 @@ class UntaintedDocument end end context "with :fixed_numeric_no_leading_zeros" do - it "should contain only numbers, be a fixed length, and have no leading zeros" do + it "contain only numbers, be a fixed length, "\ + "and have no leading zeros" do document_class.send(:token, contains: :fixed_numeric_no_leading_zeros, length: 64) @@ -124,24 +127,24 @@ class UntaintedDocument describe "pattern" do it "should conform" do - document_class.send(:token, :pattern => "%d%d%d%d%C%C%C%C") + document_class.send(:token, pattern: "%d%d%d%d%C%C%C%C") expect(document.token).to match(/[0-9]{4}[A-Z]{4}/) end context "when there's a static prefix" do it "should start with the prefix" do - document_class.send(:token, :pattern => "PREFIX-%d%d%d%d") + document_class.send(:token, pattern: "PREFIX-%d%d%d%d") expect(document.token).to match(/PREFIX\-[0-9]{4}/) end end context "when there's an infix" do it "should contain the infix" do - document_class.send(:token, :pattern => "%d%d%d%d-INFIX-%d%d%d%d") + document_class.send(:token, pattern: "%d%d%d%d-INFIX-%d%d%d%d") expect(document.token).to match(/[0-9]{4}\-INFIX\-[0-9]{4}/) end end context "when there's a suffix" do it "should end with the suffix" do - document_class.send(:token, :pattern => "%d%d%d%d-SUFFIX") + document_class.send(:token, pattern: "%d%d%d%d-SUFFIX") expect(document.token).to match(/[0-9]{4}\-SUFFIX/) end end @@ -182,7 +185,13 @@ class Doc let(:doc) { Doc.create(foo: "hello") } let(:dup_doc) { Doc.new } let(:exception) do - /(E11000 duplicate key error index: mongoid_token_test.docs\.\$foo_1|E11000 duplicate key error collection: mongoid_token_test\.docs index: foo_1 dup key)/ + lte_mongo3 = + "E11000 duplicate key error index: mongoid_token_test.docs\.\$foo_1" + gte_mongo4 = + "E11000 duplicate key error collection: mongoid_token_test\.docs "\ + "index: foo_1 dup key" + + /(#{lte_mongo3}|#{gte_mongo4})/ end it do @@ -195,7 +204,7 @@ class Doc describe "callbacks" do context "when the document is a new record" do - let(:document){ document_class.new } + let(:document) { document_class.new } it "should create the token after being saved" do document_class.send(:token) expect(document.token).to be_nil @@ -223,7 +232,7 @@ class Doc context "when the document is initialized with a token" do it "should not change the token after being saved" do document_class.send(:token) - token = 'test token' + token = "test token" expect(document_class.create!(token: token).token).to eq token end end @@ -235,7 +244,8 @@ class Doc expect(d2.token).to be_nil end - it "should generate a new token with the same options as the source document" do + it "should generate a new token with the same options "\ + "as the source document" do document.class.send(:token, length: 64, contains: :alpha_upper) d2 = document.clone d2.save @@ -266,38 +276,42 @@ class Doc end context "when creating a new record" do - it "should raise an exception when collisions can't be resolved on save" do + it "should raise an exception when collisions can't "\ + "be resolved on save" do document.token = "1234" document.save d2 = document.clone allow(d2).to receive(:generate_token).and_return("1234") expect { d2.save }.to( - raise_exception(Mongoid::Token::CollisionRetriesExceeded) + raise_exception(Mongoid::Token::CollisionRetriesExceeded), ) end - it "should raise an exception when collisions can't be resolved on create!" do + it "should raise an exception when collisions can't "\ + "be resolved on create!" do document.token = "1234" document.save allow_any_instance_of(document_class).to( - receive(:generate_token).and_return("1234") + receive(:generate_token).and_return("1234"), ) expect { document_class.create! }.to( - raise_exception(Mongoid::Token::CollisionRetriesExceeded) + raise_exception(Mongoid::Token::CollisionRetriesExceeded), ) end end - it "should not raise a custom error if another error is thrown during saving" do + it "should not raise a custom error "\ + "if another error is thrown during saving" do I18n.enforce_available_locales = false # Supress warnings in this example document_class.send(:field, :name) document_class.send(:validates_presence_of, :name) - allow_any_instance_of(document_class).to(receive(:generate_token) - .and_return("1234")) - allow(document_class).to(receive(:model_name) - .and_return(ActiveModel::Name.new(document_class, nil, "temp"))) + allow_any_instance_of(document_class).to( + receive(:generate_token).and_return("1234"), + ) + allow(document_class).to(receive(:model_name). + and_return(ActiveModel::Name.new(document_class, nil, "temp"))) expect { document_class.create! }.to( - raise_exception(Mongoid::Errors::Validations) + raise_exception(Mongoid::Errors::Validations), ) end @@ -313,7 +327,7 @@ class Doc duplicate_name = "Got Duped." document_class.create!(name: duplicate_name) expect { document_class.create!(name: duplicate_name) }.to( - raise_exception(Mongo::Error::OperationFailure) + raise_exception(Mongo::Error::OperationFailure), ) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 40fafbb..5f7de52 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,15 +1,17 @@ +# frozen_string_literal: true + require "simplecov" require "codeclimate-test-reporter" SimpleCov.start -$: << File.expand_path("../../lib", __FILE__) +$: << File.expand_path("../lib", __dir__) -require 'database_cleaner' -require 'mongoid' -require 'mongoid-rspec' -require 'mongoid_token' +require "database_cleaner" +require "mongoid" +require "mongoid-rspec" +require "mongoid_token" -ENV['MONGOID_ENV'] = "test" +ENV["MONGOID_ENV"] = "test" RSpec.configure do |config| Mongo::Logger.logger.level = Logger::ERROR From 8b0b106311b951d04294dc642a0d1fb3988ca741 Mon Sep 17 00:00:00 2001 From: Ricard Forniol Date: Tue, 3 Dec 2019 16:59:01 +0100 Subject: [PATCH 15/15] Update README --- README.md | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 3d961e0..0e70e7e 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ gem 'mongoid_token', '~> 3.0.0' # For mongoid >= 5 gem 'mongoid_token', '~> 4.0.0' + +# For mongoid >= 6 +gem 'mongoid_token', '~> 5.0.0' ``` Then update your bundle @@ -101,8 +104,8 @@ This one is easy, it's just an integer. __Example:__ ```ruby -token :length => 6 # Tokens are now of length 6 -token :length => 12 # Whow, whow, whow. Slow down egghead. +token length: 6 # Tokens are now of length 6 +token length: 12 # Whow, whow, whow. Slow down egghead. ``` You get the idea. @@ -130,8 +133,8 @@ never start with zeros __Examples:__ ```ruby -token :contains => :alpha_upper, :length => 8 -token :contains => :fixed_numeric +token contains: :alpha_upper, length: 8 +token contains: :fixed_numeric ``` #### Patterns (`:pattern`) @@ -160,7 +163,7 @@ generated character, and are as follows: __Example:__ ```ruby -token :pattern => "PRE-%C%C-%d%d%d%d" # Generates something like: 'PRE-ND-3485' +token pattern: "PRE-%C%C-%d%d%d%d" # Generates something like: 'PRE-ND-3485' ``` You can also add a repetition modifier, which can help improve readability on @@ -169,7 +172,7 @@ more complex patterns. You simply add any integer after the letter. __Examples:__ ```ruby -token :pattern => "APP-%d6" # Generates something like; "APP-638924" +token pattern: "APP-%d6" # Generates something like; "APP-638924" ``` ### Field Name (`:field_name`) @@ -180,9 +183,9 @@ use multiple tokens one a single document. __Examples:__ ```ruby -token :length => 6 -token :field_name => :sharing_token, :length => 12 -token :field_name => :yet_another +token length: 6 +token field_name: :sharing_token, length: 12 +token field_name: :yet_another ``` @@ -192,7 +195,7 @@ This will prevent the gem from creating the extra `find_by_*` methods. __Example:__ ```ruby -token :skip_finders => true +token skip_finders: true ``` @@ -204,7 +207,7 @@ this behaviour off: __Example:__ ```ruby -token :override_to_param => false +token override_to_param: false ``` @@ -217,8 +220,8 @@ option is for you. __Examples:__ ```ruby -token :retry_count => 9 -token :retry_count => 0 +token retry_count: 9 +token retry_count: 0 ``` # Notes @@ -238,5 +241,6 @@ In particular: [olliem](https://github.com/olliem), [msolli](https://github.com/msolli), [siong1987](https://github.com/siong1987), [stephan778](https://github.com/stephan778), -[eagleas](https://github.com/eagleas), and -[jamesotron](https://github.com/jamesotron). +[eagleas](https://github.com/eagleas), +[jamesotron](https://github.com/jamesotron), and +[ritxi](https://github.com/ritxi).