model_iterator 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -4,17 +4,21 @@ Basic library for iterating through large ActiveRecord datasets. For instance,
4
4
  let's say you add a new feature, and you need to backfill data for existing
5
5
  records:
6
6
 
7
- iter = ModelIterator.new(User, :redis => $redis)
8
- iter.each do |user|
9
- backfill(user)
10
- end
7
+ ```ruby
8
+ iter = ModelIterator.new(User, :redis => $redis)
9
+ iter.each do |user|
10
+ backfill(user)
11
+ end
12
+ ```
11
13
 
12
14
  ModelIterator selects the records in batches (100 by default), and loops
13
15
  through the table filtering based on the ID.
14
16
 
15
- SELECT * FROM users WHERE id > 0 LIMIT 100
16
- SELECT * FROM users WHERE id > 100 LIMIT 100
17
- SELECT * FROM users WHERE id > 200 LIMIT 100
17
+ ```sql
18
+ SELECT * FROM users WHERE id > 0 LIMIT 100
19
+ SELECT * FROM users WHERE id > 100 LIMIT 100
20
+ SELECT * FROM users WHERE id > 200 LIMIT 100
21
+ ```
18
22
 
19
23
  Each record's ID is tracked in Redis immediately after being processed. If
20
24
  jobs crash, you can fix code, and re-run from where you left off.
@@ -1,6 +1,6 @@
1
1
  # Iterates over large models, storing state in Redis.
2
2
  class ModelIterator
3
- VERSION = "1.0.1"
3
+ VERSION = "1.0.2"
4
4
 
5
5
  class MaxIterations < StandardError
6
6
  attr_reader :iterator
@@ -83,6 +83,8 @@ class ModelIterator
83
83
  # at a time.
84
84
  # :limit - Fixnum limit of objects to fetch from the db.
85
85
  # Default: 100
86
+ # :conditions - Array of String SQL WHERE clause and optional values
87
+ # (Will override clause/values given in arguments.)
86
88
  #
87
89
  # ModelIterator.new(Repository, :start_id => 5000)
88
90
  # ModelIterator.new(Repository, 'public=?', true, :start_id => 1000)
@@ -101,10 +103,15 @@ class ModelIterator
101
103
  op = @order == :asc ? '>' : '<'
102
104
  @max = @options[:max].to_i
103
105
  @joins = @options[:joins]
104
- @clause = args.empty? ?
105
- "#{@id_clause} #{op} ?" :
106
- "#{@id_clause} #{op} ? AND (#{args.shift})"
107
- @clause_args = args
106
+ @clause = "#{@id_clause} #{op} ?"
107
+ if @options[:conditions]
108
+ conditions = Array(@options[:conditions])
109
+ @clause += " AND (#{conditions.first})"
110
+ @clause_args = conditions[1..-1]
111
+ elsif !args.empty?
112
+ @clause += " AND (#{args.shift})"
113
+ @clause_args = args
114
+ end
108
115
  @current_id = @options[:start_id]
109
116
  @limit = @options[:limit] || 100
110
117
  @job = @prefix = @key = nil
@@ -12,8 +12,8 @@ Gem::Specification.new do |s|
12
12
  ## If your rubyforge_project name is different, then edit it and comment out
13
13
  ## the sub! line in the Rakefile
14
14
  s.name = 'model_iterator'
15
- s.version = '1.0.1'
16
- s.date = '2012-09-07'
15
+ s.version = '1.0.2'
16
+ s.date = '2013-03-19'
17
17
  s.rubyforge_project = 'model_iterator'
18
18
 
19
19
  ## Make sure your summary is short. The description may be as long
@@ -46,6 +46,8 @@ Gem::Specification.new do |s|
46
46
  Rakefile
47
47
  lib/model_iterator.rb
48
48
  model_iterator.gemspec
49
+ script/bootstrap
50
+ script/test
49
51
  test/helper.rb
50
52
  test/init_test.rb
51
53
  test/iterate_test.rb
data/script/bootstrap ADDED
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+ # Usage: script/bootstrap
3
+ # Ensures all gems are in vendor/cache and installed locally.
4
+
5
+ bundle package --all
6
+ bundle install --binstubs --local --path=vendor/gems
data/script/test ADDED
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+ # Usage: script/test
3
+ # Runs the library's test suite.
4
+
5
+ bundle exec rake test
data/test/init_test.rb CHANGED
@@ -87,3 +87,78 @@ class InitializationTestWithCustomWhereClause < ModelIterator::TestCase
87
87
  end
88
88
  end
89
89
 
90
+ class InitializationTestWithCustomWhereClauseInOptions < ModelIterator::TestCase
91
+ def setup
92
+ @iter = ModelIterator.new Model,
93
+ :redis => RedisClient.new, :start_id => 5, :limit => 10,
94
+ :conditions => ['public = ?', true]
95
+ end
96
+
97
+ def test_sets_klass
98
+ assert_equal Model, @iter.klass
99
+ end
100
+
101
+ def test_sets_current_id
102
+ assert_equal 5, @iter.current_id
103
+ end
104
+
105
+ def test_sets_conditions
106
+ assert_equal ['models.id > ? AND (public = ?)', @iter.current_id, true], @iter.conditions
107
+ end
108
+
109
+ def test_sets_limit
110
+ assert_equal 10, @iter.limit
111
+ end
112
+ end
113
+
114
+ class InitializationTestWithCustomWhereClauseInOptionsOverridingArguments < ModelIterator::TestCase
115
+ def setup
116
+ @iter = ModelIterator.new Model,
117
+ 'unknown = ?',
118
+ false,
119
+ :redis => RedisClient.new, :start_id => 5, :limit => 10,
120
+ :conditions => ['public = ?', true]
121
+ end
122
+
123
+ def test_sets_klass
124
+ assert_equal Model, @iter.klass
125
+ end
126
+
127
+ def test_sets_current_id
128
+ assert_equal 5, @iter.current_id
129
+ end
130
+
131
+ def test_sets_conditions
132
+ assert_equal ['models.id > ? AND (public = ?)', @iter.current_id, true], @iter.conditions
133
+ end
134
+
135
+ def test_sets_limit
136
+ assert_equal 10, @iter.limit
137
+ end
138
+ end
139
+
140
+ class InitializationTestWithCustomWhereClauseInOptionsAsSql < ModelIterator::TestCase
141
+ def setup
142
+ @iter = ModelIterator.new Model,
143
+ :redis => RedisClient.new, :start_id => 5, :limit => 10,
144
+ :conditions => 'public = true'
145
+ end
146
+
147
+ def test_sets_klass
148
+ assert_equal Model, @iter.klass
149
+ end
150
+
151
+ def test_sets_current_id
152
+ assert_equal 5, @iter.current_id
153
+ end
154
+
155
+ def test_sets_conditions
156
+ assert_equal ['models.id > ? AND (public = true)', @iter.current_id], @iter.conditions
157
+ end
158
+
159
+ def test_sets_limit
160
+ assert_equal 10, @iter.limit
161
+ end
162
+ end
163
+
164
+
data/test/iterate_test.rb CHANGED
@@ -27,6 +27,30 @@ class IterateTest < ModelIterator::TestCase
27
27
  assert_equal %w(b c), names
28
28
  end
29
29
 
30
+ def test_loops_through_filtered_records_from_options
31
+ names = []
32
+ iter = ModelIterator.new Model,
33
+ :redis => RedisClient.new, :limit => 1,
34
+ :conditions => ['name != ?', 'a']
35
+ iter.each do |m|
36
+ names << m.name
37
+ end
38
+
39
+ assert_equal %w(b c), names
40
+ end
41
+
42
+ def test_loops_through_filtered_records_from_options_as_plain_sql
43
+ names = []
44
+ iter = ModelIterator.new Model,
45
+ :redis => RedisClient.new, :limit => 1,
46
+ :conditions => "name != 'a'"
47
+ iter.each do |m|
48
+ names << m.name
49
+ end
50
+
51
+ assert_equal %w(b c), names
52
+ end
53
+
30
54
  def test_loops_through_records_in_reverse
31
55
  names = []
32
56
  iter = ModelIterator.new Model, :redis => RedisClient.new, :limit => 1,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: model_iterator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-07 00:00:00.000000000 Z
12
+ date: 2013-03-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -55,6 +55,8 @@ files:
55
55
  - Rakefile
56
56
  - lib/model_iterator.rb
57
57
  - model_iterator.gemspec
58
+ - script/bootstrap
59
+ - script/test
58
60
  - test/helper.rb
59
61
  - test/init_test.rb
60
62
  - test/iterate_test.rb
@@ -72,7 +74,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
72
74
  version: '0'
73
75
  segments:
74
76
  - 0
75
- hash: -4149675607368163527
77
+ hash: 1002102300965983738
76
78
  required_rubygems_version: !ruby/object:Gem::Requirement
77
79
  none: false
78
80
  requirements: