mongoid-ids 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,396 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Ids do
4
+ let(:document_class) do
5
+ Object.send(:remove_const, :Document) if Object.const_defined?(:Document)
6
+ class Document
7
+ include Mongoid::Document
8
+ include Mongoid::Ids
9
+ end
10
+ Class.new(Document)
11
+ end
12
+
13
+ let(:document) do
14
+ document_class.create
15
+ end
16
+
17
+ describe '#token' do
18
+ describe 'default "_id"' do
19
+
20
+ before(:each) { document_class.send(:token) }
21
+
22
+ it 'should be created' do
23
+ expect(document).to have_field(:_id)
24
+ end
25
+
26
+ it 'should be valid' do
27
+ expect(document).to be_valid
28
+ end
29
+
30
+ it 'should be persisted' do
31
+ document.save
32
+ # why the anonymous #document_class doesn't work?
33
+ expect(Document.count).to eq(1)
34
+ end
35
+
36
+ it 'should not create any secondary index' do
37
+ expect(document.index_specifications).to be_empty
38
+ end
39
+ end
40
+
41
+ describe 'field "token"' do
42
+ before(:each) { document_class.send(:token, :token) }
43
+ it 'should be created' do
44
+ expect(document).to have_field(:token)
45
+ end
46
+
47
+ it 'should be indexed' do
48
+ index = document.index_specifications.first
49
+ expect(index.fields).to eq([:token])
50
+ expect(index.options).to have_key(:unique)
51
+ expect(index.options).to have_key(:sparse)
52
+ end
53
+ end
54
+
55
+ describe 'options' do
56
+ it 'should accept custom field names' do
57
+ document_class.send(:token, :smells_as_sweet)
58
+ expect(document).to have_field(:smells_as_sweet)
59
+ end
60
+
61
+ it 'should accept custom lengths' do
62
+ document_class.send(:token, :length => 13)
63
+ expect(document.id.length).to eq 13
64
+ end
65
+
66
+ it 'should accept custom lengths on custom fields' do
67
+ document_class.send(:token, :sweet, :length => 13)
68
+ expect(document.sweet.length).to eq 13
69
+ end
70
+
71
+ it 'should not create any custom finder' do
72
+ class UntaintedDocument
73
+ include Mongoid::Document
74
+ include Mongoid::Ids
75
+ end
76
+ dc = Class.new(UntaintedDocument)
77
+
78
+ dc.send(:token, :skip_finders => true)
79
+ expect(dc.public_methods).to_not include(:find_with_id)
80
+ end
81
+
82
+ it 'should not create custom finders with default id' do
83
+ class UntaintedDocument
84
+ include Mongoid::Document
85
+ include Mongoid::Ids
86
+ end
87
+ dc = Class.new(UntaintedDocument)
88
+
89
+ dc.send(:token)
90
+ expect(dc.public_methods).to_not include(:find_with_id)
91
+ end
92
+
93
+ it 'should disable custom finders with optional field' do
94
+ class UntaintedDocument
95
+ include Mongoid::Document
96
+ include Mongoid::Ids
97
+ end
98
+ dc = Class.new(UntaintedDocument)
99
+
100
+ dc.send(:token, :token, skip_finders: true)
101
+ expect(dc.public_methods).to_not include(:find_with_token)
102
+ end
103
+
104
+ it 'should not change `to_param`' do
105
+ document_class.send(:token, override_to_param: false)
106
+ expect(document.to_param).to eq document.id
107
+ end
108
+
109
+ it 'should disable `to_param` overrides' do
110
+ document_class.send(:token, :token, override_to_param: false)
111
+ expect(document.to_param).to_not eq document.token
112
+ end
113
+
114
+ it 'should return id when token does not exist when calling `to_param`' do
115
+ document_class.send(:token, :token, override_to_param: true)
116
+ document.unset :token
117
+ expect(document.to_param).to eq document.id.to_s
118
+ end
119
+
120
+ describe 'contains' do
121
+ context 'with :alphanumeric' do
122
+ it 'should contain only letters and numbers' do
123
+ document_class.send(:token, contains: :alphanumeric, length: 64)
124
+ expect(document.id).to match(/[A-Za-z0-9]{64}/)
125
+ end
126
+
127
+ it 'should contain only letters and numbers on custom field' do
128
+ document_class.send(:token, :token, :contains => :alphanumeric, :length => 64)
129
+ expect(document.token).to match(/[A-Za-z0-9]{64}/)
130
+ end
131
+ end
132
+
133
+ context 'with :alpha' do
134
+ it 'should contain only letters' do
135
+ document_class.send(:token, contains: :alpha, length: 64)
136
+ expect(document.id).to match(/[A-Za-z]{64}/)
137
+ end
138
+
139
+ it 'should contain only letters on custom fields' do
140
+ document_class.send(:token, :token, contains: :alpha, length: 64)
141
+ expect(document.token).to match(/[A-Za-z]{64}/)
142
+ end
143
+ end
144
+
145
+ context 'with :alpha_upper' do
146
+ it 'should contain only uppercase letters' do
147
+ document_class.send(:token, contains: :alpha_upper, length: 64)
148
+ expect(document.id).to match(/[A-Z]{64}/)
149
+ end
150
+
151
+ it 'should contain only uppercase letters on custom fields' do
152
+ document_class.send(:token, :token, contains: :alpha_upper, length: 64)
153
+ expect(document.token).to match(/[A-Z]{64}/)
154
+ end
155
+ end
156
+
157
+ context "with :alpha_lower" do
158
+ it "should contain only lowercase letters" do
159
+ document_class.send(:token, :token, contains: :alpha_lower, length: 64)
160
+ expect(document.token).to match(/[a-z]{64}/)
161
+ end
162
+ end
163
+
164
+ context 'with :numeric' do
165
+ it 'should only contain numbers' do
166
+ document_class.send(:token, :token, :contains => :numeric, :length => 64)
167
+ expect(document.token).to match(/[0-9]{1,64}/)
168
+ end
169
+ end
170
+
171
+ context 'with :fixed_numeric' do
172
+ it 'should contain only numbers and be a fixed-length' do
173
+ document_class.send(:token, :token, :contains => :fixed_numeric, :length => 64)
174
+ expect(document.token).to match(/[0-9]{64}/)
175
+ end
176
+ end
177
+
178
+ context 'with :fixed_numeric_no_leading_zeros' do
179
+ it 'should contain only numbers, be a fixed length, and have no leading zeros' do
180
+ document_class.send(:token, :token, :contains => :fixed_numeric_no_leading_zeros, :length => 64)
181
+ expect(document.token).to match(/[1-9]{1}[0-9]{63}/)
182
+ end
183
+ end
184
+ end
185
+
186
+ describe 'pattern' do
187
+ it 'should conform' do
188
+ document_class.send(:token, :token, :pattern => "%d%d%d%d%C%C%C%C")
189
+ expect(document.token).to match(/[0-9]{4}[A-Z]{4}/)
190
+ end
191
+ context 'when there\'s a static prefix' do
192
+ it 'should start with the prefix' do
193
+ document_class.send(:token, :token, :pattern => "PREFIX-%d%d%d%d")
194
+ expect(document.token).to match(/PREFIX\-[0-9]{4}/)
195
+ end
196
+ end
197
+ context 'when there\'s an infix' do
198
+ it 'should contain the infix' do
199
+ document_class.send(:token, :token, :pattern => "%d%d%d%d-INFIX-%d%d%d%d")
200
+ expect(document.token).to match(/[0-9]{4}\-INFIX\-[0-9]{4}/)
201
+ end
202
+ end
203
+ context 'when there\'s a suffix' do
204
+ it 'should end with the suffix' do
205
+ document_class.send(:token, :token, :pattern => "%d%d%d%d-SUFFIX")
206
+ expect(document.token).to match(/[0-9]{4}\-SUFFIX/)
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ it 'should allow for multiple tokens of different names' do
213
+ document_class.send(:token, :token, :contains => :alpha_upper)
214
+ document_class.send(:token, :sharing_id, :contains => :alpha_lower)
215
+ expect(document.token).to match(/[A-Z]{4}/)
216
+ expect(document.sharing_id).to match(/[a-z]{4}/)
217
+ end
218
+ end
219
+
220
+ describe 'callbacks' do
221
+ context 'when the document is a new record' do
222
+ let(:document){ document_class.new }
223
+ it 'should create the token after being saved' do
224
+ document_class.send(:token, :token)
225
+ expect(document.token).to be_nil
226
+ document.save
227
+ expect(document.token).to_not be_nil
228
+ end
229
+ end
230
+
231
+ context 'when the document is not a new record' do
232
+ it 'should not change the token after being saved' do
233
+ document_class.send(:token, :token)
234
+ token_before = document.token
235
+ document.save
236
+ expect(document.token).to eq token_before
237
+ end
238
+ context 'and the token is nil' do
239
+ it 'should create a new token after being saved' do
240
+ document_class.send(:token, :token)
241
+ token_before = document.token
242
+ document.token = nil
243
+ document.save
244
+ expect(document.token).to_not be_nil
245
+ expect(document.token).to_not eq token_before
246
+ end
247
+ end
248
+ context 'when the document is initialized with a token' do
249
+ it 'should not change the token after being saved' do
250
+ document_class.send(:token, :token)
251
+ token = 'test token'
252
+ expect(document_class.create!(token: token).token).to eq token
253
+ end
254
+ end
255
+ end
256
+ context 'when the document is cloned' do
257
+ it 'should set the token to nil' do
258
+ document.class.send(:token, :token, length: 64, contains: :alpha_upper)
259
+ d2 = document.clone
260
+ expect(d2.token).to be_nil
261
+ end
262
+
263
+ it 'should generate a new token with the same options as the source document' do
264
+ document.class.send(:token, :token, length: 64, contains: :alpha_upper)
265
+ d2 = document.clone
266
+ d2.save
267
+ expect(d2.token).to_not eq document.token
268
+ expect(d2.token).to match(/[A-Z]{64}/)
269
+ end
270
+ end
271
+ end
272
+
273
+ describe 'finders' do
274
+ it 'should not create a custom find method for id' do
275
+ document_class.send(:token)
276
+ expect(document.class.public_methods).to_not include(:find_by_id)
277
+ end
278
+
279
+ it 'should create a custom find method' do
280
+ document_class.send(:token, field_name: :other_token)
281
+ expect(document.class.public_methods).to include(:find_by_other_token)
282
+ end
283
+ end
284
+
285
+ describe '.to_param' do
286
+ it 'should return the token' do
287
+ document_class.send(:token, :token)
288
+ expect(document.to_param).to eq document.token
289
+ end
290
+ end
291
+
292
+ describe 'collision resolution on id field' do
293
+ before(:each) do
294
+ document_class.send(:token)
295
+ document_class.create_indexes
296
+ end
297
+
298
+ context 'when creating a new record' do
299
+ it 'should raise an exception when collisions can\'t be resolved on save' do
300
+ allow_any_instance_of(document_class).to receive(:generate_token).and_return('1234')
301
+ # expect_any_instance_of(document_class)
302
+ # .to receive(:generate_token).exactly(3).times.and_return('1234')
303
+ # expect(d2).to receive(:generate_token).and_return('1234')
304
+ d2 = document.clone
305
+ expect { d2.save }.to raise_exception(Mongoid::Ids::CollisionRetriesExceeded)
306
+ # binding.pry
307
+ end
308
+
309
+ it 'should raise an exception when collisions can\'t be resolved on create!' do
310
+ allow_any_instance_of(document_class).to receive(:generate_token).and_return('1234')
311
+ document.save
312
+ expect { document_class.create! }.to raise_exception(Mongoid::Ids::CollisionRetriesExceeded)
313
+ end
314
+ end
315
+
316
+ it 'should not raise a custom error if another error is thrown during saving' do
317
+ I18n.enforce_available_locales = false # Supress warnings in this example
318
+ document_class.send(:field, :name)
319
+ document_class.send(:validates_presence_of, :name)
320
+ allow_any_instance_of(document_class).to receive(:generate_token).and_return('1234')
321
+ allow(document_class).to receive(:model_name).and_return(ActiveModel::Name.new(document_class, nil, "temp"))
322
+ expect { document_class.create! }.to raise_exception(Mongoid::Errors::Validations)
323
+ end
324
+
325
+ context 'with other unique indexes present' do
326
+ before(:each) do
327
+ document_class.send(:field, :name)
328
+ document_class.send(:index, { name: 1 }, { unique: true })
329
+ document_class.create_indexes
330
+ end
331
+
332
+ context 'when violating the other index' do
333
+ it 'should raise an operation failure' do
334
+ duplicate_name = 'Got Duped.'
335
+ document_class.create!(name: duplicate_name)
336
+ expect{ document_class.create!(name: duplicate_name) }
337
+ .to raise_exception(Moped::Errors::OperationFailure)
338
+ end
339
+ end
340
+ end
341
+ end
342
+
343
+ describe 'collision resolution on custom field' do
344
+ before(:each) do
345
+ document_class.send(:token, :token)
346
+ document_class.create_indexes
347
+ end
348
+
349
+ context 'when creating a new record' do
350
+ it 'should raise when collisions can\'t be resolved on save' do
351
+ document.token = '1234'
352
+ document.save
353
+ d2 = document.clone
354
+ expect(d2)
355
+ .to receive(:generate_token).exactly(3).times.and_return('1234')
356
+ expect { d2.save }
357
+ .to raise_exception(Mongoid::Ids::CollisionRetriesExceeded)
358
+ end
359
+
360
+ it 'should raise when collisions can\'t be resolved on create!' do
361
+ document.token = '1234'
362
+ document.save
363
+ allow_any_instance_of(document_class).to receive(:generate_token).and_return('1234')
364
+ expect { document_class.create! }
365
+ .to raise_exception(Mongoid::Ids::CollisionRetriesExceeded)
366
+ end
367
+ end
368
+
369
+ it 'should not raise a custom error if another error is thrown during saving' do
370
+ I18n.enforce_available_locales = false # Supress warnings in this example
371
+ document_class.send(:field, :name)
372
+ document_class.send(:validates_presence_of, :name)
373
+ allow_any_instance_of(document_class).to receive(:generate_token).and_return('1234')
374
+ allow(document_class).to receive(:model_name).and_return(ActiveModel::Name.new(document_class, nil, 'temp'))
375
+ expect{ document_class.create! }
376
+ .to raise_exception(Mongoid::Errors::Validations)
377
+ end
378
+
379
+ context 'with other unique indexes present' do
380
+ before(:each) do
381
+ document_class.send(:field, :name)
382
+ document_class.send(:index, { name: 1 }, { unique: true })
383
+ document_class.create_indexes
384
+ end
385
+
386
+ context 'when violating the other index' do
387
+ it 'should raise an operation failure' do
388
+ duplicate_name = 'Got Duped.'
389
+ document_class.create!(name: duplicate_name)
390
+ expect{ document_class.create!(name: duplicate_name) }
391
+ .to raise_exception(Moped::Errors::OperationFailure)
392
+ end
393
+ end
394
+ end
395
+ end
396
+ end
@@ -0,0 +1,35 @@
1
+ # require 'codeclimate-test-reporter'
2
+ # CodeClimate::TestReporter.start
3
+
4
+ $: << File.expand_path("../../lib", __FILE__)
5
+
6
+ # require 'pry'
7
+ # require 'database_cleaner'
8
+ require 'mongoid'
9
+ require 'mongoid-rspec'
10
+
11
+ require 'mongoid/ids'
12
+
13
+ ENV['MONGOID_ENV'] = "test"
14
+
15
+ Mongoid.configure do |config|
16
+ config.sessions = {
17
+ default: {
18
+ database: 'mongoid_ids_test',
19
+ hosts: [ "localhost:#{ENV['BOXEN_MONGODB_PORT'] || 27017}" ],
20
+ options: {}
21
+ }
22
+ }
23
+ end
24
+
25
+ RSpec.configure do |config|
26
+ config.include Mongoid::Matchers
27
+ # config.before(:suite) do
28
+ # # DatabaseCleaner.strategy = :truncation
29
+ # end
30
+
31
+ config.before(:each) do
32
+ # DatabaseCleaner.clean
33
+ Mongoid.purge!
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid-ids
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Nicholas Bruning
8
+ - Marcos Piccinini
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-01-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongoid
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 4.0.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 4.0.0
28
+ description: Mongoid token is a gem for creating random, unique tokens for mongoid
29
+ documents. Highly configurable and great for making URLs a little more compact.
30
+ email:
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".autotest"
36
+ - ".gitignore"
37
+ - ".rspec"
38
+ - ".travis.yml"
39
+ - Gemfile
40
+ - Guardfile
41
+ - MIT-LICENSE.txt
42
+ - README.md
43
+ - Rakefile
44
+ - benchmarks/benchmark.rb
45
+ - lib/mongoid/ids.rb
46
+ - lib/mongoid/ids/collision_resolver.rb
47
+ - lib/mongoid/ids/collisions.rb
48
+ - lib/mongoid/ids/exceptions.rb
49
+ - lib/mongoid/ids/finders.rb
50
+ - lib/mongoid/ids/generator.rb
51
+ - lib/mongoid/ids/options.rb
52
+ - lib/mongoid/ids/version.rb
53
+ - mongoid-ids.gemspec
54
+ - spec/mongoid/ids/collisions_spec.rb
55
+ - spec/mongoid/ids/exceptions_spec.rb
56
+ - spec/mongoid/ids/finders_spec.rb
57
+ - spec/mongoid/ids/generator_spec.rb
58
+ - spec/mongoid/ids/options_spec.rb
59
+ - spec/mongoid/token_spec.rb
60
+ - spec/spec_helper.rb
61
+ homepage: http://github.com/nofxx/mongoid-ids
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project: mongoid-ids
81
+ rubygems_version: 2.4.5
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: A little random, unique token generator for Mongoid documents.
85
+ test_files:
86
+ - spec/mongoid/ids/collisions_spec.rb
87
+ - spec/mongoid/ids/exceptions_spec.rb
88
+ - spec/mongoid/ids/finders_spec.rb
89
+ - spec/mongoid/ids/generator_spec.rb
90
+ - spec/mongoid/ids/options_spec.rb
91
+ - spec/mongoid/token_spec.rb
92
+ - spec/spec_helper.rb
93
+ has_rdoc: