minidoc 1.0.0.rc1 → 1.0.0.rc2

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.
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