model_iterator 1.0.1 → 1.0.2

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