randumb 0.4.1 → 0.5.0
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 +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
|