schron 0.0.3 → 0.0.4

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: 7769c035395e049f20cbede7cda51f0b39c8ed28
4
- data.tar.gz: 6eacb27127b5d4770d36dd17692697b1bd39d93e
3
+ metadata.gz: 8f4bf73bf955f3f82baa2bab5bc3acc1b812cd61
4
+ data.tar.gz: 334dc42717de4a920f6c698a031829d6113615c3
5
5
  SHA512:
6
- metadata.gz: e2f2c532a880389220906e087c45772d8430159ff4b102c33aa770793ba1c0e52e7db0714cfb6e3833c3a463e9aa3b273950d362d1e490b58d5600565280fe62
7
- data.tar.gz: a3becfbd86f34a23e6a5d3038f35df103ce72cadb31753fbacae795574644987488a9fd8c83693a92fc458f2bc04a78dfc344e116a857ceeee283408b40babca
6
+ metadata.gz: 978dfca8ba3c7ea74efd6ad4825fcc34f6cf9641c2b8417185a9242c15e9a68122442c3736711288ccbb5b2fb48ce73364aaf602598ab517d5391d8be5b4dac1
7
+ data.tar.gz: 4385936c35b1df5c92c15d00fef37d12641f613766305d78baf319f1a3317618205f7f1d736734f03ab6027bf0f6ff0abba6dd3dd10be757f0b8d10d6d94d0b0
data/Rakefile CHANGED
@@ -12,8 +12,9 @@ task :bundle do
12
12
  system "cd #{root} && bundle install"
13
13
  end
14
14
 
15
- task :spec do
16
- system "cd #{root} && bundle exec rspec"
15
+ require 'rspec/core/rake_task'
16
+ RSpec::Core::RakeTask.new do |t|
17
+ t.rspec_opts = '--color'
17
18
  end
18
19
 
19
- task :default => [:bundle, :spec]
20
+ task :default => [:bundle, :spec]
@@ -27,11 +27,28 @@ module Schron
27
27
  def query(&block)
28
28
  Query.new(self, &block)
29
29
  end
30
+
31
+ def exec_query(query)
32
+ raw_results = datastore.exec_query(query)
33
+ load_all(raw_results)
34
+ end
35
+
36
+ def exec_count(query)
37
+ datastore.exec_count(query)
38
+ end
30
39
 
31
40
  def all
32
41
  query.all
33
42
  end
34
43
 
44
+ def batches(size, &block)
45
+ query.batches(size, &block)
46
+ end
47
+
48
+ def batched_each(size, &block)
49
+ query.batched_each(size, &block)
50
+ end
51
+
35
52
  def first
36
53
  query.first
37
54
  end
@@ -30,43 +30,55 @@ module Schron
30
30
  end
31
31
 
32
32
  def insert(kind, hash)
33
- hash[:id] ||= Schron::Id.generate
34
- serialized = serialize(kind, hash)
35
- coll(kind).insert(serialized)
36
- hash
33
+ with_write_errors! do
34
+ hash[:id] ||= Schron::Id.generate
35
+ serialized = serialize(kind, hash)
36
+ coll(kind).insert(serialized)
37
+ hash
38
+ end
37
39
  end
38
40
 
39
41
  def multi_insert(kind, hashes)
40
- hashes.each { |h| h[:id] ||= Schron::Id.generate }
41
- docs = hashes.map { |h| serialize(kind, h) }
42
- coll(kind).insert(docs)
43
- hashes
42
+ with_write_errors! do
43
+ hashes.each { |h| h[:id] ||= Schron::Id.generate }
44
+ docs = hashes.map { |h| serialize(kind, h) }
45
+ coll(kind).insert(docs)
46
+ hashes
47
+ end
44
48
  end
45
49
 
46
50
  def update(kind, hash)
47
- Schron::Id.require!(hash)
48
- doc = serialize(kind, hash)
49
- coll(kind).update(id_selector(hash[:id]), doc)
50
- hash
51
+ with_write_errors! do
52
+ Schron::Id.require!(hash)
53
+ doc = serialize(kind, hash)
54
+ coll(kind).update(id_selector(hash[:id]), doc)
55
+ hash
56
+ end
51
57
  end
52
58
 
53
59
  def multi_update(kind, hashes)
54
- Schron::Id.require_all!(hashes)
55
- hashes.each do |hash|
56
- doc = serialize(kind, hash)
57
- coll(kind).update(id_selector(hash[:id]), doc)
60
+ with_write_errors! do
61
+ Schron::Id.require_all!(hashes)
62
+ hashes.each do |hash|
63
+ doc = serialize(kind, hash)
64
+ coll(kind).update(id_selector(hash[:id]), doc)
65
+ end
66
+ hashes
58
67
  end
59
- hashes
60
68
  end
61
69
 
62
70
  def remove(kind, id)
63
- coll(kind).remove(id_selector(id))
64
- nil
71
+ with_write_errors! do
72
+ coll(kind).remove(id_selector(id))
73
+ nil
74
+ end
65
75
  end
66
76
 
67
77
  def multi_remove(kind, ids)
68
- coll(kind).remove(multiple_id_selector(ids))
69
- nil
78
+ with_write_errors! do
79
+ coll(kind).remove(multiple_id_selector(ids))
80
+ nil
81
+ end
70
82
  end
71
83
 
72
84
  def exec_query(query)
@@ -138,6 +150,22 @@ module Schron
138
150
  {_id: {"$in" => serialized }}
139
151
  end
140
152
 
153
+
154
+ def with_write_errors!(&block)
155
+ block.call
156
+ rescue ::Mongo::OperationFailure => detail
157
+ if detail.result &&
158
+ detail.result['writeErrors'] &&
159
+ !detail.result['writeErrors'].empty?
160
+ case detail.result['writeErrors'].first['code']
161
+ when 11000
162
+ raise Schron::DuplicateKeyError.new(nil, detail.result)
163
+ end
164
+ end
165
+
166
+ raise detail
167
+ end
168
+
141
169
  def selector_for(query)
142
170
  selector = {}
143
171
  query.filters.each do |(field, op, filter_value)|
@@ -186,4 +214,4 @@ module Schron
186
214
 
187
215
  end
188
216
  end
189
- end
217
+ end
data/lib/schron/db.rb ADDED
@@ -0,0 +1,36 @@
1
+ module Schron
2
+ class DB
3
+
4
+ def initialize(repos={})
5
+ @repos = repos.each_with_object({}) do |(k,v), hash|
6
+ hash[k.to_sym] = v
7
+ end
8
+ @repos.each do |name, repo|
9
+ define_singleton_method(name) { self[name] }
10
+ end
11
+ end
12
+
13
+ def [](name)
14
+ @repos[name.to_sym]
15
+ end
16
+
17
+ def session_begin
18
+ @repos.each do |name, repo|
19
+ repo.session_begin if repo.respond_to?(:session_begin)
20
+ end
21
+ end
22
+
23
+ def session_end
24
+ @repos.each do |name, repo|
25
+ repo.session_end if repo.respond_to?(:session_end)
26
+ end
27
+ end
28
+
29
+ def session(&block)
30
+ session_begin
31
+ block.call
32
+ ensure
33
+ session_end
34
+ end
35
+ end
36
+ end
data/lib/schron/error.rb CHANGED
@@ -4,4 +4,12 @@ module Schron
4
4
 
5
5
  class OperationDisabledError < Error
6
6
  end
7
- end
7
+
8
+ class DuplicateKeyError < Error
9
+
10
+ def initialize(message=nil, datastore_info=nil)
11
+ super(message)
12
+ @datastore_info = datastore_info
13
+ end
14
+ end
15
+ end
@@ -21,6 +21,14 @@ module Schron
21
21
  def has?(id)
22
22
  @data.key?(id)
23
23
  end
24
+
25
+ def delete(id)
26
+ @data.delete(id)
27
+ end
24
28
 
29
+ def clear
30
+ @data = {}
31
+ end
32
+
25
33
  end
26
- end
34
+ end
@@ -0,0 +1,168 @@
1
+ require 'schron/identity_map'
2
+ require 'schron/repository/interface'
3
+ require 'schron/repository'
4
+ require 'schron/archive/interface'
5
+
6
+ module Schron
7
+ module IdentityMapRepository
8
+ include Schron::Archive::Interface
9
+ include Schron::Repository::Interface
10
+
11
+ include Schron::Repository
12
+
13
+ def self.included(archive_class)
14
+ archive_class.extend Schron::DSL
15
+ end
16
+
17
+
18
+ # def initialize(*args)
19
+ # super(*args)
20
+ # @id_map = nil
21
+ # end
22
+
23
+ # def method_missing(method_name, *args, &block)
24
+ # @repo.method(method_name).unbind.bind(self).call(*args, &block)
25
+ # # @repo.__send__(method_name, *args, &block)
26
+ # end
27
+
28
+ # def respond_to_missing?(method_name, include_private = false)
29
+ # @repo.respond_to?(method_name, include_private)
30
+ # end
31
+
32
+ def session_begin
33
+ @id_map ||= IdentityMap.new
34
+ @sessions ||= 0
35
+ @sessions += 1
36
+ nil
37
+ end
38
+
39
+ def session_end
40
+ raise 'no session is open' unless @id_map
41
+ @sessions -= 1
42
+ @id_map = nil if @sessions == 0
43
+ nil
44
+ end
45
+
46
+ def session(&block)
47
+ session_begin
48
+ block.call
49
+ ensure
50
+ session_end
51
+ end
52
+
53
+ # [:datastore, :kind, :entity_class, :indexed_fields,
54
+ # :identity].each do |repo_method|
55
+ # define_method(repo_method) do |*args, &block|
56
+ # @repo.__send__(repo_method, *args, &block)
57
+ # end
58
+ # end
59
+
60
+ # def query(&block)
61
+ # Query.new(self, &block)
62
+ # end
63
+
64
+ def exec_query(query)
65
+ results = super
66
+ results.map do |object|
67
+ if identity_map.has?(object.id)
68
+ id_map_object = identity_map.get(object.id)
69
+ assign_attributes(from: object, to: id_map_object)
70
+ id_map_object
71
+ else
72
+ identity_map.put(object.id, object)
73
+ object
74
+ end
75
+ end
76
+ end
77
+
78
+ def get(id)
79
+ identity_map.fetch(id) do
80
+ super
81
+ # @repo.get(id)
82
+ end
83
+ end
84
+
85
+ def multi_get(ids)
86
+ ids = ids.to_a unless ids.kind_of?(Array)
87
+ result = Array.new(ids.size)
88
+ needed_ids = []
89
+
90
+ ids.each_with_index do |id, index|
91
+ if identity_map.has?(id)
92
+ result[index] = identity_map.get(id)
93
+ else
94
+ needed_ids << id
95
+ end
96
+ end
97
+
98
+ needed_objects = super(needed_ids)#@repo.multi_get(needed_ids)
99
+ needed_objects.each do |object|
100
+ if object
101
+ identity_map.put(object.id, object)
102
+ result[ids.index(object.id)] = object
103
+ end
104
+ end
105
+
106
+ result.compact
107
+ end
108
+
109
+ def insert(object)
110
+ result = super
111
+ # result = @repo.insert(object)
112
+ assign_attributes(from: result, to: object)
113
+ identity_map.put(object.id, object)
114
+ object
115
+ end
116
+
117
+ def multi_insert(objects)
118
+ # results = @repo.multi_insert(objects)
119
+ results = super
120
+ objects.each_with_index do |o, i|
121
+ assign_attributes(from: results[i], to: o)
122
+ identity_map.put(o.id, o)
123
+ end
124
+ objects
125
+ end
126
+
127
+ def update(object)
128
+ super
129
+ # @repo.update(object)
130
+ object
131
+ end
132
+
133
+ def multi_update(objects)
134
+ super
135
+ # @repo.multi_update(objects)
136
+ objects
137
+ end
138
+
139
+ def remove(id)
140
+ super
141
+ object = identity_map.delete(id)
142
+ object.freeze if object
143
+ nil
144
+ end
145
+
146
+ def multi_remove(ids)
147
+ super
148
+ ids.each do |id|
149
+ object = identity_map.delete(id)
150
+ object.freeze if object
151
+ end
152
+ nil
153
+ end
154
+
155
+ def identity_map
156
+ @id_map || raise('Must start a session before accessing data from an IdentityMapRepository')
157
+ end
158
+
159
+ private
160
+
161
+ def assign_attributes(from: from, to: to)
162
+ if to.respond_to?(:attributes=)
163
+ to.attributes = from.attributes
164
+ end
165
+ end
166
+
167
+ end
168
+ end
@@ -0,0 +1,58 @@
1
+ require 'delegate'
2
+ require 'logger'
3
+ require 'benchmark'
4
+
5
+ module Schron
6
+ class LoggedRepository
7
+
8
+ def initialize(repo,
9
+ logger: nil,
10
+ log_level: :info,
11
+ exclude_regexp: nil,
12
+ include_regexp: nil)
13
+ @repo = repo
14
+ @logger = logger || Logger.new(STDOUT)
15
+ @log_level = log_level
16
+ @exclude = exclude_regexp
17
+ @include = include_regexp
18
+ end
19
+
20
+ def method_missing(method_name, *args, &block)
21
+ if @repo.respond_to?(method_name)
22
+ log(method_name, *args) do
23
+ @repo.__send__(method_name, *args, &block)
24
+ end
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ def respond_to_missing?(method_name, include_private=false)
31
+ @repo.respond_to?(method_name, include_private)
32
+ end
33
+
34
+ private
35
+
36
+ def log(method_name, *args, &block)
37
+ if should_log_method?(method_name)
38
+ label = "#{@repo.class.name}##{method_name}(#{args.inspect})"
39
+ return_val = nil
40
+ time = Benchmark.realtime { return_val = block.call }
41
+ @logger.__send__(@log_level, ":repo: finished #{label} (#{time}s)")
42
+ return_val
43
+ else
44
+ block.call
45
+ end
46
+ end
47
+
48
+ def should_log_method?(method_name)
49
+ if @exclude && method_name =~ @exclude
50
+ false
51
+ elsif @include && method_name !~ @include
52
+ false
53
+ else
54
+ true
55
+ end
56
+ end
57
+ end
58
+ end
data/lib/schron/query.rb CHANGED
@@ -71,9 +71,7 @@ module Schron
71
71
  ## Query Terminators
72
72
 
73
73
  def all
74
- raw_results = datastore.exec_query(self)
75
- loaded_results = archive.load_all(raw_results)
76
- with_pagination(loaded_results)
74
+ with_pagination(archive.exec_query(self))
77
75
  end
78
76
 
79
77
  def first
@@ -81,13 +79,25 @@ module Schron
81
79
  end
82
80
 
83
81
  def count
84
- datastore.exec_count(self)
82
+ archive.exec_count(self)
85
83
  end
86
84
 
87
85
  def exists?
88
86
  count > 0
89
87
  end
90
88
 
89
+ def batched_each(size, &block)
90
+ batches(size) { |group| group.each(&block) }
91
+ end
92
+
93
+ def batches(size, &block)
94
+ current_page = 1
95
+ begin
96
+ results = page(current_page, per_page: size).all
97
+ block.call(results)
98
+ current_page = results.paging.next_page
99
+ end while current_page
100
+ end
91
101
 
92
102
  protected
93
103
 
@@ -2,7 +2,8 @@ module Schron
2
2
  module Test
3
3
  class Entity
4
4
 
5
- attr_reader :attributes
5
+ attr_accessor :attributes
6
+
6
7
  def initialize(attrs={})
7
8
  @attributes = attrs
8
9
  end
@@ -18,4 +19,4 @@ module Schron
18
19
 
19
20
  end
20
21
  end
21
- end
22
+ end
@@ -65,4 +65,4 @@ if defined?(RSpec)
65
65
 
66
66
  end
67
67
 
68
- end
68
+ end
data/schron.gemspec CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "schron"
7
- spec.version = "0.0.3"
7
+ spec.version = "0.0.4"
8
8
  spec.authors = ["David Faber"]
9
9
  spec.email = ["david@1bios.co"]
10
10
  spec.summary = %q{Repository implementation for entity persistence}
@@ -20,4 +20,21 @@ describe Schron::Datastore::Mongo do
20
20
 
21
21
  include_examples 'schron datastore'
22
22
 
23
- end
23
+ describe 'error handling' do
24
+ context 'duplicate key error' do
25
+ let(:coll_name) { 'books' }
26
+
27
+ before(:each) do
28
+ db[coll_name].create_index(:title, unique: true)
29
+ end
30
+
31
+ it 'raises a Schron::DuplicateKeyError' do
32
+ ds.insert('books', title: 'Hyperion')
33
+ expect do
34
+ ds.insert('books', title: 'Hyperion')
35
+ end.to raise_error(Schron::DuplicateKeyError)
36
+ end
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'schron/db'
3
+
4
+ describe Schron::DB do
5
+ subject(:db) do
6
+ described_class.new(
7
+ users: id_map_repo,
8
+ foos: non_id_map_repo)
9
+ end
10
+
11
+ let(:id_map_repo) { double }
12
+
13
+ let(:non_id_map_repo) { double }
14
+
15
+ it 'defines accessors for each repository' do
16
+ expect(db.users).to be(id_map_repo)
17
+ expect(db.foos).to be(non_id_map_repo)
18
+ end
19
+
20
+ it 'access repos by name as a symbol with []' do
21
+ expect(db[:users]).to eq(id_map_repo)
22
+ expect(db[:foos]).to eq(non_id_map_repo)
23
+ end
24
+
25
+ it 'access repos by name as string with []' do
26
+ expect(db['users']).to eq(id_map_repo)
27
+ expect(db['foos']).to eq(non_id_map_repo)
28
+ end
29
+
30
+ it 'opens sessions on all repos at once' do
31
+ expect(id_map_repo).to receive(:session_begin)
32
+ expect(id_map_repo).to receive(:session_end)
33
+ db.session {}
34
+ end
35
+ end
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+
3
+ require 'schron/repository'
4
+ require 'schron/datastore/memory'
5
+ require 'schron/identity_map_repository'
6
+
7
+ describe Schron::IdentityMapRepository do
8
+ include Schron::Test
9
+
10
+ subject(:repo) { repo_class.new(ds) }
11
+
12
+ let(:archive) { repo }
13
+
14
+ let(:repo_class) do
15
+ Class.new do
16
+ include Schron::IdentityMapRepository
17
+ entity_class Schron::Test::Entity
18
+ kind 'test'
19
+ end
20
+ end
21
+ # let(:underlying_repo) do
22
+ # underlying_repo_class.new(ds,
23
+ # entity_class: Schron::Test::Entity,
24
+ # kind: 'test')
25
+ # end
26
+
27
+ let(:ds) { Schron::Datastore::Memory.new }
28
+
29
+ describe 'session' do
30
+ it 'creates an identity map' do
31
+ expect { repo.identity_map }.to raise_error
32
+ repo.session do
33
+ expect(repo.identity_map).not_to be_nil
34
+ end
35
+ end
36
+
37
+ it 'creates a new one for each session' do
38
+ id_map = nil
39
+ repo.session { id_map = repo.identity_map }
40
+ repo.session do
41
+ expect(id_map).not_to be(repo.identity_map)
42
+ end
43
+ end
44
+
45
+ it 'uses the same identity map if a session has already started' do
46
+ repo.session_begin
47
+ id_map = repo.identity_map
48
+ repo.session_begin
49
+ expect(id_map).to be(repo.identity_map)
50
+ end
51
+
52
+ it 'closes sessions after an equal number of begin and end calls have been made' do
53
+ repo.session_begin
54
+ repo.session_begin
55
+ repo.session_end
56
+ expect(repo.identity_map).not_to be_nil
57
+ repo.session_end
58
+ expect{ repo.identity_map }.to raise_error
59
+ end
60
+ end
61
+
62
+ describe 'shared examples' do
63
+ around(:each) do |example|
64
+ repo.session { example.run }
65
+ end
66
+
67
+ include_examples 'schron archive'
68
+ include_examples 'schron repository'
69
+ end
70
+
71
+ context 'within a session' do
72
+ around(:each) do |example|
73
+ repo.session { example.run }
74
+ end
75
+
76
+ let(:object) { repo.insert(repo.entity_class.new) }
77
+ let(:object2) { repo.insert(repo.entity_class.new) }
78
+
79
+ it 'returns the same object after insertion' do
80
+ obj = repo.entity_class.new
81
+ inserted = repo.insert(obj)
82
+ expect(inserted).to be(obj)
83
+ end
84
+
85
+ it 'returns the same objects after multi insertion' do
86
+ obj = repo.entity_class.new
87
+ inserted = repo.multi_insert([obj]).first
88
+ expect(obj).to be(inserted)
89
+ end
90
+
91
+ it 'returns one object ref when retreived with get' do
92
+ loaded = repo.get(object.id)
93
+ expect(object).to be(loaded)
94
+ end
95
+
96
+ it 'returns the same object ref when retrieved by multi get' do
97
+ loaded = repo.multi_get([object.id]).first
98
+ expect(object).to be(loaded)
99
+ end
100
+
101
+ it 'can multi_get a set of ids' do
102
+ object
103
+ object2
104
+ repo.identity_map.clear
105
+ result = repo.multi_get(Set.new([object.id, object2.id]))
106
+ expect(result.map(&:id)).to include(object.id, object2.id)
107
+ end
108
+
109
+ it 'syncs updates' do
110
+ loaded = repo.get(object.id)
111
+ loaded.attributes[:thing] = 'thing'
112
+ updated = repo.update(loaded)
113
+ expect(updated).to be(object)
114
+ expect(object.attributes[:thing]).to eq('thing')
115
+ reloaded = repo.get(object.id)
116
+ expect(reloaded.attributes[:thing]).to eq('thing')
117
+ expect(reloaded).to be(object)
118
+ end
119
+
120
+ it 'syncs multi updates' do
121
+ l1 = repo.get(object.id)
122
+ l2 = repo.get(object2.id)
123
+ l1.attributes[:a] = 'b'
124
+ l2.attributes[:b] = 'c'
125
+
126
+ results = repo.multi_update([l1, l2])
127
+ expect(results[0]).to be(object)
128
+ expect(results[1]).to be(object2)
129
+
130
+ expect(object.attributes[:a]).to eq('b')
131
+ expect(object2.attributes[:b]).to eq('c')
132
+ end
133
+
134
+ it 'freezes removed objects' do
135
+ loaded = repo.get(object.id)
136
+ repo.remove(loaded.id)
137
+ expect(object).to be_frozen
138
+ end
139
+
140
+ it 'freezes multi-removed objects' do
141
+ loaded = repo.get(object.id)
142
+ repo.multi_remove([loaded.id])
143
+ expect(object).to be_frozen
144
+ end
145
+
146
+ # it 'calls custom repo methods on the original repo' do
147
+ # repo.define_singleton_method(:my_query) do |a1, &block|
148
+ # block.call(a1)
149
+ # end
150
+ # results = []
151
+ # repo.my_query('testing') do |val|
152
+ # results << val
153
+ # end
154
+ # expect(results).to eq(['testing'])
155
+ # end
156
+
157
+ # it 'responds_to? methods on the original repo' do
158
+ # underlying_repo.define_singleton_method(:custom_thing) {}
159
+ # expect(repo).to respond_to(:custom_thing)
160
+ # end
161
+
162
+ end
163
+
164
+ describe 'queries' do
165
+ it 'puts query results into the identiy map' do
166
+ repo.session { repo.insert(repo.entity_class.new) }
167
+ repo.session do
168
+ obj = repo.query.all.first
169
+ expect(obj).to be(repo.get(obj.id))
170
+ end
171
+ end
172
+
173
+ it 'pulls query results from the identity map' do
174
+ repo.session do
175
+ obj = repo.insert(repo.entity_class.new)
176
+ expect(obj).to be(repo.query.first)
177
+ end
178
+ end
179
+
180
+ end
181
+ end
@@ -62,4 +62,26 @@ describe Schron::Query do
62
62
  expect(query.first).to eq('loaded')
63
63
  end
64
64
  end
65
+
66
+ describe 'batches' do
67
+ it 'iterates over all results in groups, only loading the specified amount into memory at a time' do
68
+ expect(ds).to receive(:exec_query).at_least(2).and_call_original
69
+ 2.times { archive.insert(Schron::GenericEntity.new) }
70
+ query.batches(1) do |group|
71
+ expect(group.size).to eq(1)
72
+ end
73
+ end
74
+ end
75
+
76
+ describe 'batched each' do
77
+ it 'iterates over each result, only loading the specified amount into memory at a time' do
78
+ expect(ds).to receive(:exec_query).at_least(2).and_call_original
79
+ 2.times { archive.insert(Schron::GenericEntity.new) }
80
+ i = 0
81
+ query.batches(1) do |item|
82
+ i += 1
83
+ end
84
+ expect(i).to eq(2)
85
+ end
86
+ end
65
87
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schron
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Faber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-20 00:00:00.000000000 Z
11
+ date: 2014-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -128,12 +128,15 @@ files:
128
128
  - lib/schron/datastore/mongo/serializer.rb
129
129
  - lib/schron/datastore/sequel.rb
130
130
  - lib/schron/datastore/serializer.rb
131
+ - lib/schron/db.rb
131
132
  - lib/schron/dsl.rb
132
133
  - lib/schron/error.rb
133
134
  - lib/schron/generic_archive.rb
134
135
  - lib/schron/generic_entity.rb
135
136
  - lib/schron/id.rb
136
137
  - lib/schron/identity_map.rb
138
+ - lib/schron/identity_map_repository.rb
139
+ - lib/schron/logged_repository.rb
137
140
  - lib/schron/paginated_results.rb
138
141
  - lib/schron/paging.rb
139
142
  - lib/schron/query.rb
@@ -150,7 +153,9 @@ files:
150
153
  - spec/lib/schron/datastore/memory_spec.rb
151
154
  - spec/lib/schron/datastore/mongo_spec.rb
152
155
  - spec/lib/schron/datastore/sequel_spec.rb
156
+ - spec/lib/schron/db_spec.rb
153
157
  - spec/lib/schron/dsl_spec.rb
158
+ - spec/lib/schron/identity_map_repository_spec.rb
154
159
  - spec/lib/schron/identity_map_spec.rb
155
160
  - spec/lib/schron/paginaged_results_spec.rb
156
161
  - spec/lib/schron/query_spec.rb
@@ -185,7 +190,9 @@ test_files:
185
190
  - spec/lib/schron/datastore/memory_spec.rb
186
191
  - spec/lib/schron/datastore/mongo_spec.rb
187
192
  - spec/lib/schron/datastore/sequel_spec.rb
193
+ - spec/lib/schron/db_spec.rb
188
194
  - spec/lib/schron/dsl_spec.rb
195
+ - spec/lib/schron/identity_map_repository_spec.rb
189
196
  - spec/lib/schron/identity_map_spec.rb
190
197
  - spec/lib/schron/paginaged_results_spec.rb
191
198
  - spec/lib/schron/query_spec.rb