mongoid_collection_snapshot 1.0.1 → 1.1.0

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: 8354fa3558b50530619ef83969005b83f797408b
4
- data.tar.gz: 545fe63db62763ff9bf579f5880be6cc7759ecfd
3
+ metadata.gz: 041a83b26ca092439e83e0dd525c73d08ece188a
4
+ data.tar.gz: 4a5e32a1d3f3809279fbb0747988bde7f254db3b
5
5
  SHA512:
6
- metadata.gz: 17ee7b9cc0b5da03afce28a44b2455181bafc26bb83e0185071a0b0437aa46f7301db3f67f182335aed9aab1bda2323fe48796a73ec348c14257566f701e725a
7
- data.tar.gz: 2049669da108f6693b98628cfa24fc0bbfe179b5c8171ee4f36883ddb820845a663f457dc97d55756268c4e999299a467fb0bc8f4bb8e9c99d19ce3af9debbe5
6
+ metadata.gz: b889d8da972a015996384bdf38f2bbe2b53b025efb716a95d55ab88a429e9268dd5cbbdd045edc84fbdbfd34ab694190bcb0554e57c0c3a48802d84393fed2b6
7
+ data.tar.gz: 2945f8661a3dd9eb70795d79cca5efae7a925a3454a6e94ffe607ce3dd81816055a90ecd4cf2a74b9b7d2f9079776aebaa03fa21d38ec529f944468f11354ada
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=progress
data/.travis.yml CHANGED
@@ -13,3 +13,5 @@ env:
13
13
  language: ruby
14
14
 
15
15
  cache: bundler
16
+
17
+ sudo: false
data/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  Next Release
2
2
  ------------
3
3
 
4
+ 1.1.0
5
+ -----
6
+
7
+ * [#11](https://github.com/aaw/mongoid_collection_snapshot/pull/10): Added support for accessing snapshot collection documents via Mongoid::CollectionSnapshot#documents - [@dblock](https://github.com/dblock).
8
+ * [#11](https://github.com/aaw/mongoid_collection_snapshot/pull/11): Upgraded RSpec - [@dblock](https://github.com/dblock).
9
+
4
10
  1.0.1
5
11
  -----
6
12
 
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
1
+ source 'http://rubygems.org'
2
2
 
3
3
  case version = ENV['MONGOID_VERSION'] || '~> 4.0'
4
4
  when /4/
@@ -9,10 +9,10 @@ else
9
9
  gem 'mongoid', version
10
10
  end
11
11
 
12
- gem "mongoid_slug"
12
+ gem 'mongoid_slug'
13
13
 
14
14
  group :development, :test do
15
- gem "rspec", "~> 2.11.0"
16
- gem "rake"
15
+ gem 'rspec', '~> 3.1'
16
+ gem 'rake'
17
+ gem 'timecop'
17
18
  end
18
-
data/README.md CHANGED
@@ -8,30 +8,18 @@ Easy maintenance of collections of processed data in MongoDB with the Mongoid 3.
8
8
  Quick example:
9
9
  --------------
10
10
 
11
- Suppose that you have a Mongoid model called `Artwork`, stored
12
- in a MongoDB collection called `artworks` and the underlying documents
13
- look something like:
11
+ Suppose that you have a Mongoid model called `Artwork`, stored in a MongoDB collection called `artworks` and the underlying documents look something like:
14
12
 
15
13
  { name: 'Flowers', artist: 'Andy Warhol', price: 3000000 }
16
14
 
17
- From time to time, your system runs a map/reduce job to compute the
18
- average price of each artist's works, resulting in a collection called
19
- `artist_average_price` that contains documents that look like:
15
+ From time to time, your system runs a map/reduce job to compute the average price of each artist's works, resulting in a collection called `artist_average_price` that contains documents that look like:
20
16
 
21
- { _id: { artist: 'Andy Warhol'}, value: { price: 1500000 } }
17
+ { _id: { artist: 'Andy Warhol' }, value: { price: 1500000 } }
22
18
 
23
- If your system wants to maintain and use this average price data, it has
24
- to do so at the level of raw MongoDB operations, since
25
- map/reduce result documents don't map well to models in Mongoid.
26
- Furthermore, even though map/reduce jobs can take some time to run, you probably
27
- want the entire `artist_average_price` collection populated atomically
28
- from the point of view of your system, since otherwise you don't ever
29
- know the state of the data in the collection - you could access it in
30
- the middle of a map/reduce and get partial, incorrect results.
19
+ If your system wants to maintain and use this average price data, it has to do so at the level of raw MongoDB operations, since map/reduce result documents don't map well to models in Mongoid.
20
+ Furthermore, even though map/reduce jobs can take some time to run, you probably want the entire `artist_average_price` collection populated atomically from the point of view of your system, since otherwise you don't ever know the state of the data in the collection - you could access it in the middle of a map/reduce and get partial, incorrect results.
31
21
 
32
- mongoid_collection_snapshot solves this problem by providing an atomic
33
- view of collections of data like map/reduce results that live outside
34
- of Mongoid.
22
+ A mongoid_collection_snapshot solves this problem by providing an atomic view of collections of data like map/reduce results that live outside of Mongoid.
35
23
 
36
24
  In the example above, we'd set up our average artist price collection like:
37
25
 
@@ -40,9 +28,10 @@ class AverageArtistPrice
40
28
  include Mongoid::CollectionSnapshot
41
29
 
42
30
  def build
31
+
43
32
  map = <<-EOS
44
33
  function() {
45
- emit({artist: this['artist']}, {count: 1, sum: this['price']})
34
+ emit({ artist_id: this['artist_id']}, { count: 1, sum: this['price'] })
46
35
  }
47
36
  EOS
48
37
 
@@ -51,49 +40,82 @@ class AverageArtistPrice
51
40
  var sum = 0;
52
41
  var count = 0;
53
42
  values.forEach(function(value) {
54
- sum += value['price'];
43
+ sum += value['sum'];
55
44
  count += value['count'];
56
45
  });
57
- return({count: count, sum: sum});
46
+ return({ count: count, sum: sum });
58
47
  }
59
48
  EOS
60
49
 
61
- Mongoid.default_session.command(
62
- "mapreduce" => "artworks",
63
- map: map,
64
- reduce: reduce,
65
- out: collection_snapshot.name)
50
+ Artwork.map_reduce(map, reduce).out(inline: 1).each do |doc|
51
+ collection_snapshot.insert(
52
+ artist_id: doc['_id']['artist_id'],
53
+ count: doc['value']['count'],
54
+ sum: doc['value']['sum']
55
+ )
56
+ end
66
57
  end
58
+ end
67
59
 
68
- def average_price(artist)
69
- doc = collection_snapshot.find({'_id.artist': artist}).first
70
- doc['value']['sum']/doc['value']['count']
60
+ ```
61
+
62
+ Now, if you want to schedule a recomputation, just call `AverageArtistPrice.create`. You can define other methods on collection snapshots.
63
+
64
+ ```ruby
65
+ class AverageArtistPrice
66
+ ...
67
+
68
+ def average_price(artist_name)
69
+ artist = Artist.where(name: artist_name).first
70
+ doc = collection_snapshot.where(artist_id: artist.id).first
71
+ doc['sum'] / doc['count']
71
72
  end
72
73
  end
73
74
  ```
74
75
 
75
- Now, if you want
76
- to schedule a recomputation, just call `AverageArtistPrice.create`. The latest
77
- snapshot is always available as `AverageArtistPrice.latest`, so you can write
78
- code like:
76
+ The latest snapshot is always available as `AverageArtistPrice.latest`, so you can write code like:
79
77
 
80
- ``` ruby
78
+ ```ruby
81
79
  warhol_expected_price = AverageArtistPrice.latest.average_price('Andy Warhol')
82
80
  ```
83
81
 
84
- And always be sure that you'll never be looking at partial results. The only
85
- thing you need to do to hook into mongoid_collection_snapshot is implement the
86
- method `build`, which populates the collection snapshot and any indexes you need.
82
+ And always be sure that you'll never be looking at partial results. The only thing you need to do to hook into mongoid_collection_snapshot is implement the method `build`, which populates the collection snapshot and any indexes you need.
87
83
 
88
- By default, mongoid_collection_snapshot maintains the most recent two snapshots
89
- computed any given time.
84
+ By default, mongoid_collection_snapshot maintains the most recent two snapshots computed any given time.
85
+
86
+ Query Snapshot Data with Mongoid
87
+ --------------------------------
88
+
89
+ You can do better than the average price example above and define first-class models for your collection snapshot data, then access them as any other Mongoid collection via collection snapshot's `.documents` method.
90
+
91
+ ```ruby
92
+ class AverageArtistPrice
93
+ document do
94
+ belongs_to :artist, inverse_of: nil
95
+ field :sum, type: Integer
96
+ field :count, type: Integer
97
+ end
98
+
99
+ def average_price(artist_name)
100
+ artist = Artist.where(name: artist_name).first
101
+ doc = documents.where(artist: artist).first
102
+ doc.sum / doc.count
103
+ end
104
+ end
105
+ ```
106
+
107
+ Another example iterates through all latest artist price averages.
108
+
109
+ ```ruby
110
+ AverageArtistPrice.latest.documents.each do |doc|
111
+ puts "#{doc.artist.name}: #{doc.sum / doc.count}"
112
+ end
113
+ ```
90
114
 
91
115
  Multi-collection snapshots
92
116
  --------------------------
93
117
 
94
- You can maintain multiple collections atomically within the same snapshot by
95
- passing unique collection identifiers to ``collection_snaphot`` when you call it
96
- in your build or query methods:
118
+ You can maintain multiple collections atomically within the same snapshot by passing unique collection identifiers to `collection_snaphot` when you call it in your build or query methods:
97
119
 
98
120
  ``` ruby
99
121
  class ArtistStats
@@ -103,22 +125,53 @@ class ArtistStats
103
125
  # ...
104
126
  # define map/reduce for average and max aggregations
105
127
  # ...
106
- Mongoid.default_session.command("mapreduce" => "artworks", map: map_avg, reduce: reduce_avg, out: collection_snapshot('average'))
107
- Mongoid.default_session.command("mapreduce" => "artworks", map: map_max, reduce: reduce_max, out: collection_snapshot('max'))
128
+ Mongoid.default_session.command('mapreduce' => 'artworks', map: map_avg, reduce: reduce_avg, out: collection_snapshot('average'))
129
+ Mongoid.default_session.command('mapreduce' => 'artworks', map: map_max, reduce: reduce_max, out: collection_snapshot('max'))
108
130
  end
109
131
 
110
132
  def average_price(artist)
111
- doc = collection_snapshot('average').find({'_id.artist': artist}).first
112
- doc['value']['sum']/doc['value']['count']
133
+ doc = collection_snapshot('average').find('_id.artist' => artist).first
134
+ doc['value']['sum'] / doc['value']['count']
113
135
  end
114
136
 
115
137
  def max_price(artist)
116
- doc = collection_snapshot('max').find({'_id.artist': artist}).first
138
+ doc = collection_snapshot('max').find('_id.artist' => artist).first
117
139
  doc['value']['max']
118
140
  end
119
141
  end
120
142
  ```
121
143
 
144
+ Specify the name of the collection to define first class Mongoid models.
145
+
146
+ ```ruby
147
+ class ArtistStats
148
+ document('average') do
149
+ field :value, type: Hash
150
+ end
151
+
152
+ document('max') do
153
+ field :value, type: Hash
154
+ end
155
+ end
156
+ ```
157
+
158
+ Access these by name.
159
+
160
+ ```ruby
161
+ ArtistStats.latest.documents('average')
162
+ ArtistStats.latest.documents('max')
163
+ ```
164
+
165
+ If fields across multiple collection snapshots are identical, a single default `document` is sufficient.
166
+
167
+ ```ruby
168
+ class ArtistStats
169
+ document do
170
+ field :value, type: Hash
171
+ end
172
+ end
173
+ ```
174
+
122
175
  Custom database connections
123
176
  ---------------------------
124
177
 
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ begin
9
9
  Bundler.setup(:default, :development)
10
10
  rescue Bundler::BundlerError => e
11
11
  $stderr.puts e.message
12
- $stderr.puts "Run `bundle install` to install missing gems"
12
+ $stderr.puts 'Run `bundle install` to install missing gems'
13
13
  exit e.status_code
14
14
  end
15
15
 
@@ -19,7 +19,7 @@ require 'rspec/core'
19
19
  require 'rspec/core/rake_task'
20
20
  RSpec::Core::RakeTask.new(:spec) do |spec|
21
21
  spec.pattern = FileList['spec/**/*_spec.rb']
22
- spec.rspec_opts = "--color --format progress"
22
+ spec.rspec_opts = '--color --format progress'
23
23
  end
24
24
 
25
- task :default => :spec
25
+ task default: :spec
@@ -1,59 +1,89 @@
1
1
  require 'mongoid_collection_snapshot/version'
2
2
 
3
- module Mongoid::CollectionSnapshot
4
- extend ActiveSupport::Concern
3
+ module Mongoid
4
+ module CollectionSnapshot
5
+ extend ActiveSupport::Concern
5
6
 
6
- included do
7
- require 'mongoid_slug'
7
+ DEFAULT_COLLECTION_KEY_NAME = '*'
8
8
 
9
- include Mongoid::Document
10
- include Mongoid::Timestamps::Created
11
- include Mongoid::Slug
9
+ included do
10
+ require 'mongoid_slug'
12
11
 
13
- field :workspace_basename, default: 'snapshot'
14
- slug :workspace_basename
12
+ include Mongoid::Document
13
+ include Mongoid::Timestamps::Created
14
+ include Mongoid::Slug
15
15
 
16
- field :max_collection_snapshot_instances, default: 2
16
+ field :workspace_basename, default: 'snapshot'
17
+ slug :workspace_basename
17
18
 
18
- before_create :build
19
- after_create :ensure_at_most_two_instances_exist
20
- before_destroy :drop_snapshot_collections
21
- end
19
+ field :max_collection_snapshot_instances, default: 2
20
+
21
+ before_create :build
22
+ after_create :ensure_at_most_two_instances_exist
23
+ before_destroy :drop_snapshot_collections
24
+
25
+ cattr_accessor :document_blocks
26
+ cattr_accessor :document_classes
22
27
 
23
- module ClassMethods
24
- def latest
25
- order_by([[:created_at, :desc]]).first
28
+ # Mongoid documents on this snapshot.
29
+ def documents(name = nil)
30
+ self.document_classes ||= {}
31
+ class_name = "#{self.class.name}#{id}#{name}".underscore.camelize
32
+ key = "#{class_name}-#{name || DEFAULT_COLLECTION_KEY_NAME}"
33
+ self.document_classes[key] ||= begin
34
+ document_block = document_blocks[name || DEFAULT_COLLECTION_KEY_NAME] if document_blocks
35
+ collection_name = collection_snapshot(name).name
36
+ klass = Class.new do
37
+ include Mongoid::Document
38
+ cattr_accessor :mongo_session
39
+ instance_eval(&document_block) if document_block
40
+ store_in collection: collection_name
41
+ end
42
+ klass.mongo_session = snapshot_session
43
+ Object.const_set(class_name, klass)
44
+ klass
45
+ end
46
+ end
26
47
  end
27
- end
28
48
 
29
- def collection_snapshot(name=nil)
30
- if name
31
- snapshot_session["#{self.collection.name}.#{name}.#{slug}"]
32
- else
33
- snapshot_session["#{self.collection.name}.#{slug}"]
49
+ module ClassMethods
50
+ def latest
51
+ order_by([[:created_at, :desc]]).first
52
+ end
53
+
54
+ def document(name = nil, &block)
55
+ self.document_blocks ||= {}
56
+ self.document_blocks[name || DEFAULT_COLLECTION_KEY_NAME] = block
57
+ end
34
58
  end
35
- end
36
59
 
37
- def drop_snapshot_collections
38
- snapshot_session.collections.each do |collection|
39
- collection.drop if collection.name =~ /^#{self.collection.name}\.([^\.]+\.)?#{slug}$/
60
+ def collection_snapshot(name = nil)
61
+ if name
62
+ snapshot_session["#{collection.name}.#{name}.#{slug}"]
63
+ else
64
+ snapshot_session["#{collection.name}.#{slug}"]
65
+ end
40
66
  end
41
- end
42
67
 
43
- # Since we should always be using the latest instance of this class, this method is
44
- # called after each save - making sure only at most two instances exists should be
45
- # sufficient to ensure that this data can be rebuilt live without corrupting any
46
- # existing computations that might have a handle to the previous "latest" instance.
47
- def ensure_at_most_two_instances_exist
48
- all_instances = self.class.order_by([[:created_at, :desc]]).to_a
49
- if all_instances.length > self.max_collection_snapshot_instances
50
- all_instances[self.max_collection_snapshot_instances..-1].each { |instance| instance.destroy }
68
+ def drop_snapshot_collections
69
+ snapshot_session.collections.each do |collection|
70
+ collection.drop if collection.name =~ /^#{self.collection.name}\.([^\.]+\.)?#{slug}$/
71
+ end
51
72
  end
52
- end
53
73
 
54
- # Override to supply custom database connection for snapshots
55
- def snapshot_session
56
- Mongoid.default_session
57
- end
74
+ # Since we should always be using the latest instance of this class, this method is
75
+ # called after each save - making sure only at most two instances exists should be
76
+ # sufficient to ensure that this data can be rebuilt live without corrupting any
77
+ # existing computations that might have a handle to the previous "latest" instance.
78
+ def ensure_at_most_two_instances_exist
79
+ all_instances = self.class.order_by([[:created_at, :desc]]).to_a
80
+ return unless all_instances.length > max_collection_snapshot_instances
81
+ all_instances[max_collection_snapshot_instances..-1].each(&:destroy)
82
+ end
58
83
 
84
+ # Override to supply custom database connection for snapshots
85
+ def snapshot_session
86
+ Mongoid.default_session
87
+ end
88
+ end
59
89
  end
@@ -1,7 +1,5 @@
1
1
  module Mongoid
2
2
  module CollectionSnapshot
3
- VERSION = '1.0.1'
3
+ VERSION = '1.1.0'
4
4
  end
5
5
  end
6
-
7
-
@@ -0,0 +1,7 @@
1
+ class Artist
2
+ include Mongoid::Document
3
+
4
+ field :name
5
+
6
+ has_many :artworks
7
+ end
@@ -2,6 +2,7 @@ class Artwork
2
2
  include Mongoid::Document
3
3
 
4
4
  field :name
5
- field :artist
6
5
  field :price
6
+
7
+ belongs_to :artist
7
8
  end
@@ -1,10 +1,16 @@
1
1
  class AverageArtistPrice
2
2
  include Mongoid::CollectionSnapshot
3
3
 
4
+ document do
5
+ belongs_to :artist, inverse_of: nil
6
+ field :sum, type: Integer
7
+ field :count, type: Integer
8
+ end
9
+
4
10
  def build
5
11
  map = <<-EOS
6
12
  function() {
7
- emit({artist: this['artist']}, {count: 1, sum: this['price']})
13
+ emit({ artist_id: this['artist_id']}, { count: 1, sum: this['price'] })
8
14
  }
9
15
  EOS
10
16
 
@@ -16,20 +22,24 @@ class AverageArtistPrice
16
22
  sum += value['sum'];
17
23
  count += value['count'];
18
24
  });
19
- return({count: count, sum: sum});
25
+ return({ count: count, sum: sum });
20
26
  }
21
27
  EOS
22
28
 
23
- Mongoid.default_session.command(
24
- "mapreduce" => "artworks",
25
- map: map,
26
- reduce: reduce,
27
- out: collection_snapshot.name)
29
+ Artwork.map_reduce(map, reduce).out(inline: 1).each do |doc|
30
+ collection_snapshot.insert(
31
+ artist_id: doc['_id']['artist_id'],
32
+ count: doc['value']['count'],
33
+ sum: doc['value']['sum']
34
+ )
35
+ end
28
36
  end
29
37
 
30
- def average_price(artist)
31
- doc = collection_snapshot.where({'_id.artist' => artist}).first
32
- doc['value']['sum']/doc['value']['count']
38
+ def average_price(artist_name)
39
+ artist = Artist.where(name: artist_name).first
40
+ fail 'missing artist' unless artist
41
+ doc = documents.where(artist: artist).first
42
+ fail 'missing record' unless doc
43
+ doc.sum / doc.count
33
44
  end
34
-
35
45
  end
@@ -2,7 +2,7 @@ class CustomConnectionSnapshot
2
2
  include Mongoid::CollectionSnapshot
3
3
 
4
4
  def self.snapshot_session
5
- @@snapshot_session ||= Moped::Session.new(['127.0.0.1:27017']).tap do |session|
5
+ @snapshot_session ||= Moped::Session.new(['127.0.0.1:27017']).tap do |session|
6
6
  session.use :snapshot_test
7
7
  end
8
8
  end
@@ -13,6 +13,6 @@ class CustomConnectionSnapshot
13
13
 
14
14
  def build
15
15
  collection_snapshot.insert('name' => 'foo')
16
- collection_snapshot('foo').insert({'name' => 'bar'})
16
+ collection_snapshot('foo').insert('name' => 'bar')
17
17
  end
18
18
  end
@@ -1,14 +1,28 @@
1
1
  class MultiCollectionSnapshot
2
2
  include Mongoid::CollectionSnapshot
3
-
4
- def build
5
- collection_snapshot('foo').insert({'name' => 'foo!'})
6
- collection_snapshot('bar').insert({'name' => 'bar!'})
7
- collection_snapshot('baz').insert({'name' => 'baz!'})
3
+
4
+ document('foo') do
5
+ field :name, type: String
6
+ field :count, type: Integer
7
+ end
8
+
9
+ document('bar') do
10
+ field :name, type: String
11
+ field :number, type: Integer
8
12
  end
9
13
 
10
- def get_names
11
- ['foo', 'bar', 'baz'].map{ |x| collection_snapshot(x).find.first['name'] }.join('')
14
+ document('baz') do
15
+ field :name, type: String
16
+ field :digit, type: Integer
12
17
  end
13
18
 
19
+ def build
20
+ collection_snapshot('foo').insert('name' => 'foo!', count: 1)
21
+ collection_snapshot('bar').insert('name' => 'bar!', number: 2)
22
+ collection_snapshot('baz').insert('name' => 'baz!', digit: 3)
23
+ end
24
+
25
+ def names
26
+ %w(foo bar baz).map { |x| collection_snapshot(x).find.first['name'] }.join('')
27
+ end
14
28
  end
@@ -2,104 +2,128 @@ require 'spec_helper'
2
2
 
3
3
  module Mongoid
4
4
  describe CollectionSnapshot do
5
-
6
- it "has a version" do
7
- Mongoid::CollectionSnapshot::VERSION.should_not be_nil
5
+ it 'has a version' do
6
+ expect(Mongoid::CollectionSnapshot::VERSION).not_to be_nil
8
7
  end
9
8
 
10
- context "creating a basic snapshot" do
11
-
12
- let!(:flowers) { Artwork.create(:name => 'Flowers', :artist => 'Andy Warhol', :price => 3000000) }
13
- let!(:guns) { Artwork.create(:name => 'Guns', :artist => 'Andy Warhol', :price => 1000000) }
14
- let!(:vinblastine) { Artwork.create(:name => 'Vinblastine', :artist => 'Damien Hirst', :price => 1500000) }
9
+ context 'creating a basic snapshot' do
10
+ let!(:andy_warhol) { Artist.create!(name: 'Andy Warhol') }
11
+ let!(:damien_hirst) { Artist.create!(name: 'Damien Hirst') }
12
+ let!(:flowers) { Artwork.create!(name: 'Flowers', artist: andy_warhol, price: 3_000_000) }
13
+ let!(:guns) { Artwork.create!(name: 'Guns', artist: andy_warhol, price: 1_000_000) }
14
+ let!(:vinblastine) { Artwork.create!(name: 'Vinblastine', artist: damien_hirst, price: 1_500_000) }
15
15
 
16
- it "returns nil if no snapshot has been created" do
17
- AverageArtistPrice.latest.should be_nil
16
+ it 'returns nil if no snapshot has been created' do
17
+ expect(AverageArtistPrice.latest).to be_nil
18
18
  end
19
19
 
20
- it "runs the build method on creation" do
20
+ it 'runs the build method on creation' do
21
21
  snapshot = AverageArtistPrice.create
22
- snapshot.average_price('Andy Warhol').should == 2000000
23
- snapshot.average_price('Damien Hirst').should == 1500000
22
+ expect(snapshot.average_price('Andy Warhol')).to eq(2_000_000)
23
+ expect(snapshot.average_price('Damien Hirst')).to eq(1_500_000)
24
24
  end
25
25
 
26
- it "returns the most recent snapshot through the latest methods" do
26
+ it 'returns the most recent snapshot through the latest methods' do
27
27
  first = AverageArtistPrice.create
28
- first.should == AverageArtistPrice.latest
28
+ expect(first).to eq(AverageArtistPrice.latest)
29
29
  # "latest" only works up to a resolution of 1 second since it relies on Mongoid::Timestamp. But this
30
30
  # module is meant to snapshot long-running collection creation, so if you need a resolution of less
31
31
  # than a second for "latest" then you're probably using the wrong gem. In tests, sleeping for a second
32
32
  # makes sure we get what we expect.
33
- sleep(1)
33
+ Timecop.travel(1.second.from_now)
34
34
  second = AverageArtistPrice.create
35
- AverageArtistPrice.latest.should == second
36
- sleep(1)
35
+ expect(AverageArtistPrice.latest).to eq(second)
36
+ Timecop.travel(1.second.from_now)
37
37
  third = AverageArtistPrice.create
38
- AverageArtistPrice.latest.should == third
38
+ expect(AverageArtistPrice.latest).to eq(third)
39
39
  end
40
40
 
41
- it "should only maintain at most two of the latest snapshots to support its calculations" do
41
+ it 'maintains at most two of the latest snapshots to support its calculations' do
42
42
  AverageArtistPrice.create
43
43
  10.times do
44
44
  AverageArtistPrice.create
45
- AverageArtistPrice.count.should == 2
45
+ expect(AverageArtistPrice.count).to eq(2)
46
46
  end
47
47
  end
48
48
 
49
- end
49
+ context '#documents' do
50
+ it 'provides access to a Mongoid collection' do
51
+ snapshot = AverageArtistPrice.create
52
+ expect(snapshot.documents.count).to eq 2
53
+ document = snapshot.documents.where(artist: andy_warhol).first
54
+ expect(document.artist).to eq andy_warhol
55
+ expect(document.count).to eq 2
56
+ expect(document.sum).to eq 4_000_000
57
+ end
50
58
 
51
- context "creating a snapshot containing multiple collections" do
59
+ it 'only creates one global class reference' do
60
+ 3.times do
61
+ index = AverageArtistPrice.create
62
+ 2.times { expect(index.documents.count).to eq 2 }
63
+ end
64
+ expect(AverageArtistPrice.document_classes.count).to be >= 3
65
+ end
66
+ end
67
+ end
52
68
 
53
- it "populates several collections and allows them to be queried" do
54
- MultiCollectionSnapshot.latest.should be_nil
69
+ context 'creating a snapshot containing multiple collections' do
70
+ it 'populates several collections and allows them to be queried' do
71
+ expect(MultiCollectionSnapshot.latest).to be_nil
55
72
  10.times { MultiCollectionSnapshot.create }
56
- MultiCollectionSnapshot.latest.get_names.should == "foo!bar!baz!"
73
+ expect(MultiCollectionSnapshot.latest.names).to eq('foo!bar!baz!')
57
74
  end
58
75
 
59
- it "safely cleans up all collections used by the snapshot" do
76
+ it 'safely cleans up all collections used by the snapshot' do
60
77
  # Create some collections with names close to the snapshots we'll create
61
- Mongoid.default_session["#{MultiCollectionSnapshot.collection.name}.do.not_delete"].insert({'a' => 1})
62
- Mongoid.default_session["#{MultiCollectionSnapshot.collection.name}.snapshorty"].insert({'a' => 1})
63
- Mongoid.default_session["#{MultiCollectionSnapshot.collection.name}.hello.1"].insert({'a' => 1})
78
+ Mongoid.default_session["#{MultiCollectionSnapshot.collection.name}.do.not_delete"].insert('a' => 1)
79
+ Mongoid.default_session["#{MultiCollectionSnapshot.collection.name}.snapshorty"].insert('a' => 1)
80
+ Mongoid.default_session["#{MultiCollectionSnapshot.collection.name}.hello.1"].insert('a' => 1)
64
81
 
65
82
  MultiCollectionSnapshot.create
66
- before_create = Mongoid.default_session.collections.map{ |c| c.name }
67
- before_create.length.should > 0
83
+ before_create = Mongoid.default_session.collections.map(&:name)
84
+ expect(before_create.length).to be > 0
68
85
 
69
- sleep(1)
86
+ Timecop.travel(1.second.from_now)
70
87
  MultiCollectionSnapshot.create
71
- after_create = Mongoid.default_session.collections.map{ |c| c.name }
88
+ after_create = Mongoid.default_session.collections.map(&:name)
72
89
  collections_created = (after_create - before_create).sort
73
- collections_created.length.should == 3
90
+ expect(collections_created.length).to eq(3)
74
91
 
75
92
  MultiCollectionSnapshot.latest.destroy
76
- after_destroy = Mongoid.default_session.collections.map{ |c| c.name }
93
+ after_destroy = Mongoid.default_session.collections.map(&:name)
77
94
  collections_destroyed = (after_create - after_destroy).sort
78
- collections_created.should == collections_destroyed
95
+ expect(collections_created).to eq(collections_destroyed)
79
96
  end
80
-
81
97
  end
82
98
 
83
- context "with a custom snapshot connection" do
84
-
99
+ context 'with a custom snapshot connection' do
85
100
  around(:each) do |example|
86
101
  CustomConnectionSnapshot.snapshot_session.drop
87
102
  example.run
88
103
  CustomConnectionSnapshot.snapshot_session.drop
89
104
  end
90
105
 
91
- it "builds snapshot in custom database" do
106
+ it 'builds snapshot in custom database' do
92
107
  snapshot = CustomConnectionSnapshot.create
93
108
  [
94
109
  "#{CustomConnectionSnapshot.collection.name}.foo.#{snapshot.slug}",
95
110
  "#{CustomConnectionSnapshot.collection.name}.#{snapshot.slug}"
96
111
  ].each do |collection_name|
97
- Mongoid.default_session[collection_name].find.count.should == 0
98
- CustomConnectionSnapshot.snapshot_session[collection_name].find.count.should == 1
112
+ expect(Mongoid.default_session[collection_name].find.count).to eq(0)
113
+ expect(CustomConnectionSnapshot.snapshot_session[collection_name].find.count).to eq(1)
99
114
  end
100
115
  end
101
116
 
117
+ context '#documents' do
118
+ it 'uses the custom session' do
119
+ expect(CustomConnectionSnapshot.new.documents.mongo_session).to eq CustomConnectionSnapshot.snapshot_session
120
+ end
121
+ it 'provides access to a Mongoid collection' do
122
+ snapshot = CustomConnectionSnapshot.create
123
+ expect(snapshot.collection_snapshot.find.count).to eq 1
124
+ expect(snapshot.documents.count).to eq 1
125
+ end
126
+ end
102
127
  end
103
-
104
128
  end
105
129
  end
data/spec/spec_helper.rb CHANGED
@@ -3,12 +3,13 @@ require 'bundler/setup'
3
3
  require 'rspec'
4
4
 
5
5
  require 'mongoid'
6
+ require 'timecop'
6
7
 
7
8
  Mongoid.configure do |config|
8
- config.connect_to("mongoid_collection_snapshot_test")
9
+ config.connect_to('mongoid_collection_snapshot_test')
9
10
  end
10
11
 
11
- require File.expand_path("../../lib/mongoid_collection_snapshot", __FILE__)
12
+ require File.expand_path('../../lib/mongoid_collection_snapshot', __FILE__)
12
13
  Dir["#{File.dirname(__FILE__)}/models/**/*.rb"].each { |f| require f }
13
14
 
14
15
  RSpec.configure do |c|
@@ -20,3 +21,4 @@ RSpec.configure do |c|
20
21
  end
21
22
  end
22
23
 
24
+ RSpec.configure(&:raise_errors_for_deprecations!)
metadata CHANGED
@@ -1,41 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid_collection_snapshot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Windsor
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-07 00:00:00.000000000 Z
11
+ date: 2015-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mongoid
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: '3.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: mongoid_slug
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  description:
@@ -44,8 +44,9 @@ executables: []
44
44
  extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
- - ".gitignore"
48
- - ".travis.yml"
47
+ - .gitignore
48
+ - .rspec
49
+ - .travis.yml
49
50
  - CHANGELOG.md
50
51
  - Gemfile
51
52
  - LICENSE.txt
@@ -54,6 +55,7 @@ files:
54
55
  - lib/mongoid_collection_snapshot.rb
55
56
  - lib/mongoid_collection_snapshot/version.rb
56
57
  - mongoid_collection_snapshot.gemspec
58
+ - spec/models/artist.rb
57
59
  - spec/models/artwork.rb
58
60
  - spec/models/average_artist_price.rb
59
61
  - spec/models/custom_connection_snapshot.rb
@@ -70,17 +72,17 @@ require_paths:
70
72
  - lib
71
73
  required_ruby_version: !ruby/object:Gem::Requirement
72
74
  requirements:
73
- - - ">="
75
+ - - '>='
74
76
  - !ruby/object:Gem::Version
75
77
  version: '0'
76
78
  required_rubygems_version: !ruby/object:Gem::Requirement
77
79
  requirements:
78
- - - ">="
80
+ - - '>='
79
81
  - !ruby/object:Gem::Version
80
82
  version: 1.3.6
81
83
  requirements: []
82
84
  rubyforge_project:
83
- rubygems_version: 2.2.2
85
+ rubygems_version: 2.0.14
84
86
  signing_key:
85
87
  specification_version: 4
86
88
  summary: Easy maintenence of collections of processed data in MongoDB with the Mongoid