minidoc 1.0.0.rc1 → 1.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca8f2d071dcb6f2ef8e43e9d995e36ef0b1d4c6e
4
- data.tar.gz: 0b0a1037fa259ab0602d2e21401a368495739803
3
+ metadata.gz: ac23e96443960c47789feec77de1e521febb6813
4
+ data.tar.gz: 946ccd3f12984a8f148225336f12cf9320a15b57
5
5
  SHA512:
6
- metadata.gz: d5029b2a00f9427603fe3b3e50270959b1333c0312bd8b50fcd5b8b1d1d9144724a1d893e2bc77f6497435047def4f3fe3f9ce4deaeecc8f14639590f9f66ba4
7
- data.tar.gz: c398c3570ac71d56aef8a27309ab3c8a4321e172ee829403e8050a64542944288f96a750a1fc24e826f3ecfe5a592df4661c3a528b1bc42f725c5de88b6b7dac
6
+ metadata.gz: ef711ba9125436d4bc9d5c599169f35aaebb67aca99a87de0e76eada3badaa81cdc0ad1bd420310ac0ceacb94d00e7874777fbc00f2d412642888156b6908692
7
+ data.tar.gz: 8aa19f3f9798c80dbba905dfb50d8cb7adde092067325f952bf64e81b295dcf25c3024ca74379d5c52d4e831fed09c0ed840e382cb3d196b29555bd67a7972f7
data/.codeclimate.yml CHANGED
@@ -13,4 +13,4 @@ ratings:
13
13
  paths:
14
14
  - "**.rb"
15
15
  exclude_paths:
16
- - test/
16
+ - spec/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/CHANGELOG.md CHANGED
@@ -8,6 +8,20 @@
8
8
 
9
9
  ### Changes
10
10
 
11
+ ## v1.0.0.rc2 (2016-10-26)
12
+
13
+ ### New features
14
+
15
+ * Allow omitting selectors to finder methods. ([@maxjacobson][])
16
+
17
+ ### Bug fixes
18
+
19
+ * Minidoc#reload will raise Minidoc::DocumentNotFoundError when the document no longer exists, rather than a nil error. ([@maxjacobson][])
20
+
21
+ ### Changes
22
+
23
+ * Make Minidoc.wrap and Minidoc.from_db private. ([@maxjacobson][])
24
+
11
25
  ## v1.0.0.rc1 (2016-09-29)
12
26
 
13
27
  ### New features
data/Gemfile CHANGED
@@ -1,9 +1,8 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem "minitest"
4
- gem "mocha"
3
+ gem "codeclimate-test-reporter", "1.0.0.pre.rc2"
5
4
  gem "rake"
5
+ gem "rspec"
6
6
  gem "simplecov"
7
- gem "codeclimate-test-reporter", "1.0.0.pre.rc2"
8
7
 
9
8
  gemspec
data/README.md CHANGED
@@ -144,7 +144,7 @@ When making a pull request, please update the [changelog](CHANGELOG.md).
144
144
 
145
145
  * Update the changelog to mark the unreleased changes as part of the new release.
146
146
  * Update the version.rb with the new version number
147
- * Commit and push to master
147
+ * Commit and push to master (may need to make a Pull Request because master is protected)
148
148
  * `rake release` which will
149
149
  * tag the latest commit based on version.rb
150
150
  * push to github
data/Rakefile CHANGED
@@ -1,11 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
- require "rake/testtask"
2
+ require "rspec/core/rake_task"
3
3
 
4
- Rake::TestTask.new do |t|
5
- t.libs.push "lib"
6
- t.ruby_opts = %w[-W0]
7
- t.test_files = FileList["test/*_test.rb"]
8
- t.verbose = true
9
- end
4
+ RSpec::Core::RakeTask.new(:spec)
10
5
 
11
- task :default => :test
6
+ task :default => :spec
@@ -33,19 +33,21 @@ module Minidoc::Finders
33
33
  end
34
34
  end
35
35
 
36
- def find_one(selector, options = {})
36
+ def find_one(selector = {}, options = {})
37
37
  wrap(collection.find_one(selector, options))
38
38
  end
39
39
 
40
- def find_one!(query, options = {})
41
- find_one(query, options) or raise DocumentNotFoundError
40
+ def find_one!(selector = {}, options = {})
41
+ find_one(selector, options) or raise DocumentNotFoundError
42
42
  end
43
43
 
44
- def find_one_or_initialize(attributes, options = {})
44
+ def find_one_or_initialize(attributes = {}, options = {})
45
45
  raise ArgumentError unless attributes.is_a?(Hash)
46
46
  find_one(attributes, options) || new(attributes)
47
47
  end
48
48
 
49
+ private
50
+
49
51
  def from_db(attrs)
50
52
  doc = new(attrs)
51
53
  doc.instance_variable_set("@new_record", false)
@@ -1,3 +1,3 @@
1
1
  class Minidoc
2
- VERSION = "1.0.0.rc1"
2
+ VERSION = "1.0.0.rc2".freeze
3
3
  end
data/lib/minidoc.rb CHANGED
@@ -157,7 +157,7 @@ class Minidoc
157
157
  end
158
158
 
159
159
  def reload
160
- new_object = self.class.find(self.id)
160
+ new_object = self.class.find(self.id) or raise DocumentNotFoundError
161
161
 
162
162
  self.class.attribute_set.each do |attr|
163
163
  self[attr.name] = new_object[attr.name]
File without changes
@@ -0,0 +1,88 @@
1
+ require "spec_helper"
2
+
3
+ describe Minidoc::Associations do
4
+ class Cat < Minidoc
5
+ include Minidoc::Associations
6
+
7
+ belongs_to :owner, class_name: "User"
8
+ end
9
+
10
+ class Doggie < Minidoc
11
+ include Minidoc::Associations
12
+
13
+ belongs_to :user
14
+ end
15
+
16
+ module Animal
17
+ class Armadillo < Minidoc
18
+ include Minidoc::Associations
19
+
20
+ belongs_to :predator
21
+ end
22
+
23
+ class Predator < Minidoc
24
+ end
25
+ end
26
+
27
+ describe "dynamically defined association methods" do
28
+ it "can load associated documents" do
29
+ expect(Cat.new.owner).to be_nil
30
+ user = User.create
31
+ cat = Cat.new(owner_id: user.id)
32
+ expect(cat.owner.id).to eq user.id
33
+ cat.save
34
+ expect(cat.owner.id).to eq user.id
35
+ end
36
+
37
+ it "caches the association cache rather than go to the database each time" do
38
+ user = User.create(name: "Bryan")
39
+ cat = Cat.create(owner_id: user.id)
40
+ expect(cat.owner.name).to eq "Bryan"
41
+ user.set(name: "Noah")
42
+ expect(cat.owner.name).to eq "Bryan"
43
+ expect(cat.reload.owner.name).to eq "Noah"
44
+ end
45
+
46
+ it "expires the association cache when a new associated document is provided" do
47
+ user = User.create(name: "Bryan")
48
+ cat = Cat.create(owner_id: user.id)
49
+ expect(cat.owner.name).to eq "Bryan"
50
+ user.set(name: "Noah")
51
+ expect(cat.owner.name).to eq "Bryan"
52
+ cat.owner = user
53
+ expect(cat.owner.name).to eq "Noah"
54
+ end
55
+
56
+ it "expires the association cache when the related foreign-key id field is updated" do
57
+ user = User.create(name: "Bryan")
58
+ cat = Cat.create(owner_id: user.id)
59
+ expect(cat.owner.name).to eq "Bryan"
60
+ user.set(name: "Noah")
61
+ expect(cat.owner.name).to eq "Bryan"
62
+ cat.owner_id = user.id
63
+ expect(cat.owner.name).to eq "Noah"
64
+ end
65
+ end
66
+
67
+ context "when the classes are not namespaced" do
68
+ it "infers the class name of the association" do
69
+ expect(Doggie.new.user).to be_nil
70
+ user = User.create
71
+ sam = Doggie.new(user_id: user.id)
72
+ expect(sam.user.id).to eq user.id
73
+ sam.save
74
+ expect(sam.user.id).to eq user.id
75
+ end
76
+ end
77
+
78
+ context "when the classes are namespaced" do
79
+ it "infers the class name of the association" do
80
+ expect(Animal::Armadillo.new.predator).to be_nil
81
+ predator = Animal::Predator.create
82
+ arnie = Animal::Armadillo.new(predator_id: predator.id)
83
+ expect(arnie.predator.id).to eq predator.id
84
+ arnie.save
85
+ expect(arnie.predator.id).to eq predator.id
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe "connection" do
5
+ class Company < Minidoc
6
+ self.collection_name = "accounts"
7
+ end
8
+
9
+ describe ".collection_name" do
10
+ it "is inferred based on the class name" do
11
+ expect(User.collection_name).to eq "users"
12
+ end
13
+
14
+ it "can be overridden" do
15
+ expect(Company.collection_name).to eq "accounts"
16
+ end
17
+ end
18
+
19
+ describe ".collection" do
20
+ it "exposes the underlying Mongo object" do
21
+ expect(User.collection).to be_a Mongo::Collection
22
+ end
23
+
24
+ it "passes through the collection_name to the underlying Mongo::Collection" do
25
+ expect(User.collection.name).to eq "users"
26
+ expect(Company.collection.name).to eq "accounts"
27
+ end
28
+ end
29
+
30
+ describe ".database" do
31
+ it "exposes the underlying Mongo object" do
32
+ expect(User.database).to be_a Mongo::DB
33
+ end
34
+
35
+ it "passes through the database name to the underlying Mongo::DB" do
36
+ expect(User.database.name).to eq "minidoc_test"
37
+ end
38
+ end
39
+
40
+ describe "first run experience" do
41
+ it "fails helpfully if you haven't configured the necessary values" do
42
+ with_alternative_configuration do
43
+ Minidoc.connection = nil
44
+ Minidoc.database_name = nil
45
+ expect { User.create(name: "Kären") }.to raise_error(Minidoc::MissingConfiguration, "Make sure to set Minidoc.connection")
46
+ end
47
+ end
48
+
49
+ it "fails helpfully if you've only neglected the database_name" do
50
+ with_alternative_configuration do
51
+ Minidoc.database_name = nil
52
+ expect { User.create(name: "Kären") }.to raise_error(Minidoc::MissingConfiguration, "Make sure to set Minidoc.database_name")
53
+ end
54
+ end
55
+ end
56
+
57
+ def with_alternative_configuration
58
+ original_connection = Minidoc.connection
59
+ original_database_name = Minidoc.database_name
60
+ yield
61
+ ensure
62
+ Minidoc.connection = original_connection
63
+ Minidoc.database_name = original_database_name
64
+ end
65
+ end
@@ -0,0 +1,42 @@
1
+ require "spec_helper"
2
+
3
+ describe Minidoc::Counters do
4
+ class SimpleCounter < Minidoc
5
+ include Minidoc::Counters
6
+
7
+ counter :counter
8
+ end
9
+
10
+ class AdvancedCounter < Minidoc
11
+ include Minidoc::Counters
12
+
13
+ counter :counter, start: 2, step_size: 3
14
+ end
15
+
16
+ describe "#increment_counter" do
17
+ it "increments the field" do
18
+ x = SimpleCounter.create!
19
+ expect(x.counter).to eq 0
20
+ expect(x.increment_counter).to eq 1
21
+ expect(x.increment_counter).to eq 2
22
+ expect(x.increment_counter).to eq 3
23
+ expect(x.reload.counter).to eq 3
24
+ end
25
+
26
+ it "can be customized with some options" do
27
+ x = AdvancedCounter.create!
28
+ expect(x.counter).to eq 2
29
+ expect(x.increment_counter).to eq 5
30
+ expect(x.increment_counter).to eq 8
31
+ expect(x.increment_counter).to eq 11
32
+ expect(x.reload.counter).to eq 11
33
+ end
34
+
35
+ it "is thread safe" do
36
+ x = SimpleCounter.create!
37
+ counters = []
38
+ [Thread.new { 5.times { (counters << x.increment_counter) } }, Thread.new { 5.times { (counters << x.increment_counter) } }, Thread.new { 5.times { (counters << x.increment_counter) } }].map(&:join)
39
+ expect(counters.uniq.length).to eq 15
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,91 @@
1
+ require "spec_helper"
2
+
3
+ describe Minidoc::Finders do
4
+ describe ".all" do
5
+ it "returns all of the documents in the collection" do
6
+ (User.collection << { name: "Joe" })
7
+ (User.collection << { name: "Bryan" })
8
+ users = User.all
9
+ expect(["Bryan", "Joe"]).to match_array(users.map(&:name))
10
+ end
11
+ end
12
+
13
+ describe ".count" do
14
+ it "counts the documents in the collection" do
15
+ (User.collection << { name: "Joe" })
16
+ (User.collection << { name: "Bryan" })
17
+ expect(User.count).to eq 2
18
+ end
19
+
20
+ it "can be scoped by a query" do
21
+ (User.collection << { name: "Joe" })
22
+ (User.collection << { name: "Bryan" })
23
+ expect(User.count(name: "Bryan")).to eq 1
24
+ end
25
+ end
26
+
27
+ describe ".exists?" do
28
+ it "tells you if any documents exist that match a query" do
29
+ (User.collection << { name: "Joe" })
30
+ expect(User.exists?(name: "Joe")).to be(true)
31
+ expect(User.exists?(name: "Bryan")).to be(false)
32
+ end
33
+ end
34
+
35
+ describe ".first" do
36
+ it "returns a document from the collection" do
37
+ expect(User.first).to be_nil
38
+ user = User.create(name: "Bryan")
39
+ expect(User.first.name).to eq "Bryan"
40
+ expect(User.first).to eq user
41
+ end
42
+ end
43
+
44
+ describe ".find" do
45
+ it "querys the collection using the mongodb query language" do
46
+ user = User.create(name: "Bryan")
47
+ expect(User.find({}).to_a).to eq [user]
48
+ expect(User.find(name: "Noah").to_a).to eq []
49
+ end
50
+ end
51
+
52
+ describe ".find_one" do
53
+ it "returns the first document that matches a query" do
54
+ user = User.create(name: "Bryan")
55
+ expect(User.find_one({})).to eq user
56
+ expect(User.find_one).to eq user
57
+ expect(User.find_one(name: "Noah")).to eq nil
58
+ end
59
+ end
60
+
61
+ describe ".find_one!" do
62
+ it "returns the first document that matches a query or raises an error" do
63
+ expect { User.find_one! }.to raise_error(Minidoc::DocumentNotFoundError)
64
+ user = User.create(name: "Bryan")
65
+ expect(User.find_one!(name: "Bryan")).to eq user
66
+ expect(User.find_one!).to eq user
67
+ expect { User.find_one!(name: "Noah") }.to raise_error(Minidoc::DocumentNotFoundError)
68
+ end
69
+ end
70
+
71
+ describe ".find_one_or_initialize" do
72
+ it "returns the first document that matches a query or makes a non-persisted document from that query" do
73
+ user = User.create(name: "Bryan", age: 1)
74
+ expect((user == User.find_one_or_initialize(name: "Bryan"))).to be_truthy
75
+ expect(User.find_one_or_initialize(name: "Noah").is_a?(User)).to eq true
76
+ expect(User.find_one_or_initialize(name: "Noah").new_record?).to eq true
77
+ end
78
+
79
+ it "doesn't require attributes" do
80
+ expect(User.find_one_or_initialize).to be_a User
81
+ user = User.create(name: "Bryan", age: 1)
82
+ expect(user).to eq User.find_one_or_initialize
83
+ end
84
+
85
+ it "allows query options" do
86
+ user = User.create(name: "Noah", age: 1)
87
+ expect((user == User.find_one_or_initialize({ age: 1 }, sort: { name: -1 }))).to be_truthy
88
+ expect { User.find_one_or_initialize("foo") }.to raise_error(ArgumentError)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,38 @@
1
+ require "spec_helper"
2
+
3
+ describe Minidoc::Grid do
4
+ let(:grid) { Minidoc::Grid.new(Minidoc.database) }
5
+
6
+ describe "#put and #get" do
7
+ it "can store raw string data" do
8
+ doc_id = grid.put "estamos en espana"
9
+ expect(doc_id).to be_a BSON::ObjectId
10
+
11
+ document = grid.get doc_id.to_s
12
+
13
+ expect(document).to be_a Mongo::GridIO
14
+ expect(document.read).to eq "estamos en espana"
15
+ end
16
+ end
17
+
18
+ describe "#get_json" do
19
+ it "parses the JSON into structured ruby objects" do
20
+ hash = { "foo" => { "bar" => 1 } }
21
+ doc_id = grid.put(hash.to_json)
22
+
23
+ value = grid.get_json(doc_id.to_s)
24
+
25
+ expect(value).to eq hash
26
+ end
27
+ end
28
+
29
+ describe "#delete" do
30
+ it "removes the 'file' from the database" do
31
+ doc_id = grid.put "estamos en espana"
32
+
33
+ grid.delete doc_id.to_s
34
+
35
+ expect { grid.get doc_id }.to raise_error(Mongo::GridFileNotFound)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ describe Minidoc::ReadOnly do
4
+ class ReadOnlyUser < Minidoc::ReadOnly
5
+ self.collection_name = "users"
6
+ attribute :name, String
7
+ end
8
+
9
+ it "is read only, meaning it can't change over time" do
10
+ expect { ReadOnlyUser.create(name: "Bryan") }.to raise_error(NoMethodError)
11
+
12
+ rw_user = User.create(name: "Bryan")
13
+ user = ReadOnlyUser.first
14
+ expect(user.name).to eq "Bryan"
15
+ expect(rw_user.id).to eq user.id
16
+ expect(user).to eq user.as_value
17
+
18
+ expect { user.name = "Noah" }.to raise_error(NoMethodError)
19
+ end
20
+
21
+ it "can become a value object" do
22
+ user = User.new(name: "Bryan")
23
+ user.name = "Noah"
24
+ user = user.as_value
25
+
26
+ expect { user.name = "Noah" }.to raise_error(NoMethodError)
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ describe Minidoc::Timestamps do
4
+ class TimestampsUser < Minidoc
5
+ include Minidoc::Timestamps
6
+
7
+ timestamps!
8
+ end
9
+
10
+ describe ".timestamps!" do
11
+ it "automatically sets created_at and updated_at" do
12
+ user = TimestampsUser.create!
13
+ expect(user.created_at).to_not be_nil
14
+ expect(user.created_at).to eq user.updated_at
15
+ end
16
+
17
+ it "updates the updated_at when the document changes" do
18
+ user = TimestampsUser.create
19
+ sleep 0.001
20
+ user.save
21
+ expect(user.created_at).to_not eq user.updated_at
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,64 @@
1
+ require "spec_helper"
2
+
3
+ describe Minidoc::Validations do
4
+ describe Minidoc::Validations::UniquenessValidator do
5
+ class UniquenessUser < User
6
+ validates :name, uniqueness: true
7
+ end
8
+
9
+ class InsensitiveUser < User
10
+ attribute :insensitive, String
11
+ validates :insensitive, uniqueness: { case_sensitive: false }
12
+ end
13
+
14
+ class ScopedUser < User
15
+ attribute :country_id, Integer
16
+ attribute :tax_id_number, Integer
17
+ validates :tax_id_number, uniqueness: { scope: :country_id }
18
+ end
19
+
20
+ it "is invalid with existing value" do
21
+ UniquenessUser.create!(name: "same name")
22
+ user = UniquenessUser.new(name: "same name")
23
+ expect(user).to_not be_valid
24
+ expect(user.errors[:name]).to eq ["has already been taken"]
25
+ end
26
+
27
+ it "is case insensitive by default" do
28
+ UniquenessUser.create!(name: "different case")
29
+ user = UniquenessUser.new(name: "DIFFERENT CASE")
30
+ expect(user).to be_valid
31
+ end
32
+
33
+ it "persists a document that passes validations" do
34
+ user = UniquenessUser.create!(name: "lonely")
35
+ expect(user).to be_valid
36
+ end
37
+
38
+ it "can be made case insensitive" do
39
+ InsensitiveUser.create!(insensitive: "equivalent")
40
+ user = InsensitiveUser.new(insensitive: "EquivALENT")
41
+ expect(user).to_not be_valid
42
+ expect(user.errors["insensitive"]).to eq ["has already been taken"]
43
+ end
44
+
45
+ it "does not allow the same value within a scope" do
46
+ ScopedUser.create!(country_id: 1, tax_id_number: 1)
47
+ user = ScopedUser.new(country_id: 1, tax_id_number: 1)
48
+ expect(user).to_not be_valid
49
+ expect(user.errors[:tax_id_number]).to eq ["has already been taken"]
50
+ end
51
+
52
+ it "allows a different value within the same scope" do
53
+ ScopedUser.create!(country_id: 1, tax_id_number: 1)
54
+ user = ScopedUser.new(country_id: 1, tax_id_number: 2)
55
+ expect(user).to be_valid
56
+ end
57
+
58
+ it "does allow the same value in a different scope" do
59
+ ScopedUser.create!(country_id: 1, tax_id_number: 1)
60
+ user = ScopedUser.new(country_id: 2, tax_id_number: 1)
61
+ expect(user).to be_valid
62
+ end
63
+ end
64
+ end