schron 0.0.3 → 0.0.4

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