randumb 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/randumb/relation.rb +59 -20
- data/lib/randumb/syntax.rb +17 -15
- data/lib/randumb/version.rb +1 -1
- data/test/randumb_test.rb +14 -0
- data/test/test_helper.rb +3 -0
- data/test/weighted_test.rb +26 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7a8de94fe998a9315531dc1785ccec096ed4a7e
|
4
|
+
data.tar.gz: b7e3d1440cc41ecd3231f8f9cde03361414cc329
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b215658a671a55d23fa68db3807ba2f2b6b204263ecb200f1a664f2f25bb58c0137132b1a67d4bbacaebf73ab7025f5a1348c7d2c722dc9f58837fa92faf024c
|
7
|
+
data.tar.gz: 7ea4b738b0cca2b7544bda4a55eb0e74c0e662cab6d85a46322086ddae7fa32def04afad55e82b5cb3fec8542a86116ca0ae00551803fdcc002695e665d0316b
|
data/lib/randumb/relation.rb
CHANGED
@@ -10,36 +10,32 @@ module Randumb
|
|
10
10
|
# If the max_items argument is omitted, one random entity will be returned.
|
11
11
|
# If you provide the integer argument, you will get back an array of records.
|
12
12
|
def random(max_items = nil, opts={})
|
13
|
-
|
13
|
+
ActiveSupport::Deprecation.warn "The random() method will be depricated in randumb 1.0 in favor of the order_by_rand scope."
|
14
|
+
relation = clone
|
15
|
+
return random_by_id_shuffle(max_items, opts) if is_randumb_postges_case?(relation)
|
16
|
+
scope = relation.order_by_rand(opts)
|
17
|
+
|
18
|
+
scope = scope.limit(max_items) if override_limit?(max_items, relation)
|
19
|
+
|
20
|
+
# return first record if method was called without parameters
|
21
|
+
max_items ? scope.to_a : scope.first
|
14
22
|
end
|
15
23
|
|
16
24
|
# If ranking_column is provided, that named column wil be multiplied
|
17
25
|
# by a random number to determine probability of order. The ranking column must be numeric.
|
18
26
|
def random_weighted(ranking_column, max_items = nil, opts={})
|
27
|
+
ActiveSupport::Deprecation.warn "The random_weighted() method will be depricated in randumb 1.0 in favor of the order_by_rand_weighted scope."
|
19
28
|
relation = clone
|
20
29
|
return random_by_id_shuffle(max_items, opts) if is_randumb_postges_case?(relation, ranking_column)
|
21
30
|
raise_unless_valid_ranking_column(ranking_column)
|
22
31
|
|
23
|
-
|
24
|
-
order_clause = Randumb::Syntax.random_order_clause(ranking_column, opts.merge(connection: connection, table_name: table_name))
|
32
|
+
scope = relation.order_by_rand_weighted(ranking_column, opts)
|
25
33
|
|
26
|
-
the_scope = if ::ActiveRecord::VERSION::MAJOR == 3 && ::ActiveRecord::VERSION::MINOR < 2
|
27
|
-
# AR 3.0.0 support
|
28
|
-
relation.order(order_clause)
|
29
|
-
else
|
30
|
-
# keep prior orders and append random
|
31
|
-
all_orders = (relation.orders + [order_clause]).join(", ")
|
32
|
-
# override all previous orders
|
33
|
-
relation.reorder(all_orders)
|
34
|
-
end
|
35
|
-
|
36
34
|
# override the limit if they are requesting multiple records
|
37
|
-
|
38
|
-
the_scope = the_scope.limit(max_items)
|
39
|
-
end
|
35
|
+
scope = scope.limit(max_items) if override_limit?(max_items, relation)
|
40
36
|
|
41
37
|
# return first record if method was called without parameters
|
42
|
-
max_items ?
|
38
|
+
max_items ? scope.to_a : scope.first
|
43
39
|
end
|
44
40
|
|
45
41
|
|
@@ -67,14 +63,44 @@ module Randumb
|
|
67
63
|
return_first_record ? records.first : records
|
68
64
|
end
|
69
65
|
|
66
|
+
def order_by_rand(opts={})
|
67
|
+
build_order_scope(opts)
|
68
|
+
end
|
69
|
+
|
70
|
+
def order_by_rand_weighted(ranking_column, opts={})
|
71
|
+
raise_unless_valid_ranking_column(ranking_column)
|
72
|
+
is_randumb_postges_case?(self, ranking_column)
|
73
|
+
build_order_scope(opts, ranking_column)
|
74
|
+
end
|
75
|
+
|
70
76
|
private
|
71
77
|
|
78
|
+
def build_order_scope(options, ranking_column=nil)
|
79
|
+
opts = options.reverse_merge(connection: connection, table_name: table_name)
|
80
|
+
|
81
|
+
order_clause = if ranking_column
|
82
|
+
Randumb::Syntax.random_weighted_order_clause(ranking_column, opts)
|
83
|
+
else
|
84
|
+
Randumb::Syntax.random_order_clause(opts)
|
85
|
+
end
|
86
|
+
|
87
|
+
if ::ActiveRecord::VERSION::MAJOR == 3 && ::ActiveRecord::VERSION::MINOR < 2
|
88
|
+
# AR 3.0.0 support
|
89
|
+
order(order_clause)
|
90
|
+
else
|
91
|
+
# keep prior orders and append random
|
92
|
+
all_orders = (orders + [order_clause]).join(", ")
|
93
|
+
# override all previous orders
|
94
|
+
reorder(all_orders)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
72
98
|
# postgres won't let you do an order_by when also doing a distinct
|
73
99
|
# let's just use the in-memory option in this case
|
74
|
-
def is_randumb_postges_case?(relation, ranking_column)
|
100
|
+
def is_randumb_postges_case?(relation, ranking_column=nil)
|
75
101
|
if relation.respond_to?(:uniq_value) && relation.uniq_value && connection.adapter_name =~ /(postgres|postgis)/i
|
76
102
|
if ranking_column
|
77
|
-
raise Exception, "
|
103
|
+
raise Exception, "order_by_rand_weighted: not possible when using .uniq and the postgres/postgis db adapter"
|
78
104
|
else
|
79
105
|
return true
|
80
106
|
end
|
@@ -102,7 +128,6 @@ module Randumb
|
|
102
128
|
|
103
129
|
id_results = connection.select_all(id_only_relation.to_sql)
|
104
130
|
|
105
|
-
|
106
131
|
rng = random_number_generator(opts)
|
107
132
|
if max_ids == 1 && id_results.count > 0
|
108
133
|
rand_index = rng.rand(id_results.count)
|
@@ -122,6 +147,10 @@ module Randumb
|
|
122
147
|
end
|
123
148
|
end
|
124
149
|
|
150
|
+
def override_limit?(max_items, relation)
|
151
|
+
max_items && (!relation.limit_value || relation.limit_value > max_items)
|
152
|
+
end
|
153
|
+
|
125
154
|
end
|
126
155
|
|
127
156
|
|
@@ -138,6 +167,14 @@ module Randumb
|
|
138
167
|
def random_by_id_shuffle(max_items = nil, opts = {})
|
139
168
|
relation.random_by_id_shuffle(max_items, opts)
|
140
169
|
end
|
170
|
+
|
171
|
+
def order_by_rand(opts={})
|
172
|
+
relation.order_by_rand(opts)
|
173
|
+
end
|
174
|
+
|
175
|
+
def order_by_rand_weighted(ranking_column, opts={})
|
176
|
+
relation.order_by_rand_weighted(ranking_column, opts)
|
177
|
+
end
|
141
178
|
end
|
142
179
|
|
143
180
|
|
@@ -145,6 +182,7 @@ module Randumb
|
|
145
182
|
module MethodMissingMagicks
|
146
183
|
def method_missing(symbol, *args)
|
147
184
|
if symbol.to_s =~ /^random_weighted_by_(\w+)$/
|
185
|
+
ActiveSupport::Deprecation.warn "Dynamic finders will be removed in randumb 1.0 http://guides.rubyonrails.org/active_record_querying.html#dynamic-finders"
|
148
186
|
random_weighted($1, *args)
|
149
187
|
else
|
150
188
|
super
|
@@ -153,6 +191,7 @@ module Randumb
|
|
153
191
|
|
154
192
|
def respond_to?(symbol, include_private=false)
|
155
193
|
if symbol.to_s =~ /^random_weighted_by_(\w+)$/
|
194
|
+
ActiveSupport::Deprecation.warn "Dynamic finders will be removed in randumb 1.0 http://guides.rubyonrails.org/active_record_querying.html#dynamic-finders"
|
156
195
|
true
|
157
196
|
else
|
158
197
|
super
|
data/lib/randumb/syntax.rb
CHANGED
@@ -3,23 +3,25 @@ module Randumb
|
|
3
3
|
class << self
|
4
4
|
|
5
5
|
# builds the order clause to be appended in where clause
|
6
|
-
def random_order_clause(
|
7
|
-
|
8
|
-
|
6
|
+
def random_order_clause(opts={})
|
7
|
+
random_for(opts)
|
8
|
+
end
|
9
|
+
|
10
|
+
# builds the order clause to be appended in where clause
|
11
|
+
def random_weighted_order_clause(ranking_column, opts={})
|
12
|
+
connection = opts[:connection]
|
13
|
+
|
14
|
+
if connection.adapter_name =~ /sqlite/i
|
15
|
+
# computer multiplication is faster than division I was once taught...so translate here
|
16
|
+
max_int = 9223372036854775807.0
|
17
|
+
multiplier = 1.0 / max_int
|
18
|
+
"(#{ranking_column} * ABS(#{random_for(opts)} * #{multiplier}) ) DESC"
|
9
19
|
else
|
10
|
-
|
11
|
-
if connection.adapter_name =~ /sqlite/i
|
12
|
-
# computer multiplication is faster than division I was once taught...so translate here
|
13
|
-
max_int = 9223372036854775807.0
|
14
|
-
multiplier = 1.0 / max_int
|
15
|
-
"(#{ranking_column} * ABS(#{random_for(opts)} * #{multiplier}) ) DESC"
|
16
|
-
else
|
17
|
-
"(#{ranking_column} * #{random_for(opts)}) DESC"
|
18
|
-
end
|
20
|
+
"(#{ranking_column} * #{random_for(opts)}) DESC"
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
22
|
-
private
|
24
|
+
private
|
23
25
|
|
24
26
|
# sligtly different for each DB
|
25
27
|
def random_for(opts)
|
@@ -40,7 +42,7 @@ module Randumb
|
|
40
42
|
if seed = opts[:seed]
|
41
43
|
table_name = opts[:table_name]
|
42
44
|
# SQLLite does not support a random seed. However, pseudo-randomness
|
43
|
-
# can be achieved by sorting on a hash of the id field (generated by
|
45
|
+
# can be achieved by sorting on a hash of the id field (generated by
|
44
46
|
# multiplying the id by the random seed and ignoring everything before
|
45
47
|
# the decimal).
|
46
48
|
# See http://stackoverflow.com/questions/2171578/seeding-sqlite-random
|
@@ -77,4 +79,4 @@ module Randumb
|
|
77
79
|
|
78
80
|
end
|
79
81
|
end
|
80
|
-
end
|
82
|
+
end
|
data/lib/randumb/version.rb
CHANGED
data/test/randumb_test.rb
CHANGED
@@ -14,11 +14,13 @@ class RandumbTest < Test::Unit::TestCase
|
|
14
14
|
# above is equivalent to:
|
15
15
|
# assert_equal nil, Artist.random
|
16
16
|
# assert_equal nil, Artist.random_by_id_shuffle
|
17
|
+
assert_nil Artist.order_by_rand.first
|
17
18
|
|
18
19
|
assert_equal_for_both_methods [], Artist, 1
|
19
20
|
# above is equivalent to:
|
20
21
|
# assert_equal [], Artist.random(1)
|
21
22
|
# assert_equal [], Artist.random_by_id_shuffle(1)
|
23
|
+
assert_equal [], Artist.order_by_rand.limit(1).all
|
22
24
|
|
23
25
|
assert_equal_for_both_methods nil, Artist.limit(50)
|
24
26
|
end
|
@@ -32,8 +34,11 @@ class RandumbTest < Test::Unit::TestCase
|
|
32
34
|
assert_equal 1, Artist.count
|
33
35
|
|
34
36
|
assert_equal_for_both_methods @high_on_fire, Artist
|
37
|
+
assert_equal @high_on_fire, Artist.order_by_rand.first
|
38
|
+
|
35
39
|
assert_equal_for_both_methods [@high_on_fire], Artist, 1
|
36
40
|
assert_equal_for_both_methods [@high_on_fire], Artist, 30
|
41
|
+
assert_equal [@high_on_fire], Artist.limit(30).order_by_rand.all
|
37
42
|
end
|
38
43
|
|
39
44
|
should "not return a record that doesnt match where" do
|
@@ -66,6 +71,10 @@ class RandumbTest < Test::Unit::TestCase
|
|
66
71
|
assert_equal false, artists.first.name.nil?
|
67
72
|
assert_raise (ActiveModel::MissingAttributeError) {artists.first.views}
|
68
73
|
|
74
|
+
artists = Artist.select(:name).order_by_rand.limit(3)
|
75
|
+
assert_equal false, artists.first.name.nil?
|
76
|
+
assert_raise (ActiveModel::MissingAttributeError) {artists.first.views}
|
77
|
+
|
69
78
|
artists = Artist.select(:name).random_by_id_shuffle(3)
|
70
79
|
assert_equal false, artists.first.name.nil?
|
71
80
|
assert_raise (ActiveModel::MissingAttributeError) {artists.first.views}
|
@@ -73,6 +82,7 @@ class RandumbTest < Test::Unit::TestCase
|
|
73
82
|
|
74
83
|
should "respect scopes" do
|
75
84
|
assert_equal_for_both_methods [@fiona_apple], Artist.at_least_three_views, 3
|
85
|
+
assert_equal [@fiona_apple], Artist.at_least_three_views.order_by_rand.limit(3)
|
76
86
|
end
|
77
87
|
|
78
88
|
should "select only as many as in the db if we request more" do
|
@@ -200,6 +210,10 @@ class RandumbTest < Test::Unit::TestCase
|
|
200
210
|
10.times do
|
201
211
|
assert_equal seeded_order, Artist.random(2, seed: @seed)
|
202
212
|
end
|
213
|
+
|
214
|
+
10.times do
|
215
|
+
assert_equal seeded_order, Artist.order_by_rand(seed: @seed).limit(2)
|
216
|
+
end
|
203
217
|
end
|
204
218
|
|
205
219
|
should "always return the same order using shuffle method" do
|
data/test/test_helper.rb
CHANGED
data/test/weighted_test.rb
CHANGED
@@ -4,7 +4,7 @@ class WeightedTest < Test::Unit::TestCase
|
|
4
4
|
|
5
5
|
should "raise exception when called with a non-existent column" do
|
6
6
|
assert_raises(ArgumentError) do
|
7
|
-
Artist.
|
7
|
+
Artist.order_by_rand_weighted(:blah)
|
8
8
|
end
|
9
9
|
assert_raises(ArgumentError) do
|
10
10
|
Artist.random_weighted_by_blah
|
@@ -13,7 +13,7 @@ class WeightedTest < Test::Unit::TestCase
|
|
13
13
|
|
14
14
|
should "raise exception when called with a non-numeric column" do
|
15
15
|
assert_raises(ArgumentError) do
|
16
|
-
Artist.
|
16
|
+
Artist.order_by_rand_weighted(:name)
|
17
17
|
end
|
18
18
|
assert_raises(ArgumentError) do
|
19
19
|
Artist.random_weighted_by_name
|
@@ -25,7 +25,7 @@ class WeightedTest < Test::Unit::TestCase
|
|
25
25
|
if ENV["DB"] == "postgres"
|
26
26
|
should "raise exception if being called with uniq/postgres" do
|
27
27
|
assert_raises(Exception) do
|
28
|
-
Artist.uniq.
|
28
|
+
Artist.uniq.order_by_rand_weighted(:views)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
else
|
@@ -66,6 +66,9 @@ class WeightedTest < Test::Unit::TestCase
|
|
66
66
|
assert_hits_per_views do
|
67
67
|
Artist.random_weighted("views").views
|
68
68
|
end
|
69
|
+
assert_hits_per_views do
|
70
|
+
Artist.order_by_rand_weighted("views").first.views
|
71
|
+
end
|
69
72
|
end
|
70
73
|
|
71
74
|
should "order by ranking column with method_missing" do
|
@@ -80,6 +83,12 @@ class WeightedTest < Test::Unit::TestCase
|
|
80
83
|
assert(result.size == 5)
|
81
84
|
result.first.views
|
82
85
|
end
|
86
|
+
|
87
|
+
assert_hits_per_views do
|
88
|
+
result = Artist.order_by_rand_weighted("views").limit(5).all
|
89
|
+
assert(result.size == 5)
|
90
|
+
result.first.views
|
91
|
+
end
|
83
92
|
end
|
84
93
|
|
85
94
|
should "order by ranking column with method_missing using max_items" do
|
@@ -98,6 +107,14 @@ class WeightedTest < Test::Unit::TestCase
|
|
98
107
|
result.last.views
|
99
108
|
end
|
100
109
|
end
|
110
|
+
|
111
|
+
assert_raises(MiniTest::Assertion) do
|
112
|
+
assert_hits_per_views do
|
113
|
+
result = Artist.order_by_rand_weighted(:views).limit(3)
|
114
|
+
assert(result.size == 3)
|
115
|
+
result.last.views
|
116
|
+
end
|
117
|
+
end
|
101
118
|
end
|
102
119
|
|
103
120
|
should "order by ranking column with method_missing using 1 max_items" do
|
@@ -106,6 +123,12 @@ class WeightedTest < Test::Unit::TestCase
|
|
106
123
|
assert(result.size == 1)
|
107
124
|
result.first.views
|
108
125
|
end
|
126
|
+
|
127
|
+
assert_hits_per_views do
|
128
|
+
result = Artist.order_by_rand_weighted(:views).limit(1)
|
129
|
+
assert(result.size == 1)
|
130
|
+
result.first.views
|
131
|
+
end
|
109
132
|
end
|
110
133
|
end
|
111
134
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: randumb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zachary Kloepping
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|