randumb 0.4.0 → 0.6.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 +6 -14
- data/lib/randumb.rb +1 -0
- data/lib/randumb/relation.rb +93 -64
- data/lib/randumb/syntax.rb +82 -0
- data/lib/randumb/version.rb +1 -1
- data/test/models/artist.rb +8 -2
- data/test/models/factories.rb +1 -1
- data/test/randumb_test.rb +77 -22
- data/test/test_helper.rb +14 -13
- data/test/weighted_test.rb +54 -21
- metadata +98 -28
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
OTkzMmIzZmQ1ZDg0YjhjYmFiODhhNDRhZWFkN2RkNzdjYTQ4NTE5ZDgzOTIx
|
10
|
-
NTY4NTk4YjVhOTZmMTFhNWJlZjM5Zjg0OGE3YzMyNmFmM2QwZDA1ZDRjYTY3
|
11
|
-
YTRmYzVkNjExY2UwMTc4MDBhZWFmZjFjNzkzYTgwMzI2MjQzYjM=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
OWJjZjUzODMzOWY2MWYwZWU1YWQzZDAxMWNkOWYyM2E3ZTQyZTNmMmE4ODI4
|
14
|
-
ZjY4NjE5ZjBjMzkyNDFmZTFhZWJlMGQ4YWNlOTg2YjdiNzA3M2NmNzhhNGZj
|
15
|
-
ZDUzMTZjNjgwYTI3ZWYzNjE0MDdkMWNmNjNlOWY4MGQ0ZWU2ZWY=
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9fd0e54ae7de8a15766df8a33d65edd4a5d1eab2cb3ba36b27c6f9541ab39a72
|
4
|
+
data.tar.gz: ed1afd68fa829e7e236d92d44fda28439492b004eda2c8d45b11b1b85f8feee3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7031236bbde306b4c508d019956514ebe0a9b93b037f0d2cc2a539ea694b55c33c05b6728cfaae952f663b2727f919b974a863ddc905b15c77e70ca99c149c91
|
7
|
+
data.tar.gz: 7431df3f579c21e3076507b85564375646db6e78135ac0420d399c62e50474bafe3a1d827bc1f174edb57f22c8a68dbbaf7eb6b8e656e845e23476dfbb4bc310
|
data/lib/randumb.rb
CHANGED
data/lib/randumb/relation.rb
CHANGED
@@ -9,47 +9,44 @@ module Randumb
|
|
9
9
|
|
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
|
-
def random(max_items = nil)
|
13
|
-
|
12
|
+
def random(max_items = nil, opts={})
|
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
|
-
def random_weighted(ranking_column, max_items = nil)
|
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
|
-
return random_by_id_shuffle(max_items) if is_randumb_postges_case?(relation, ranking_column)
|
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
|
-
# get clause for current db type
|
23
|
-
order_clause = random_order_clause(ranking_column)
|
24
31
|
|
25
|
-
|
26
|
-
|
27
|
-
relation.order(order_clause)
|
28
|
-
else
|
29
|
-
# keep prior orders and append random
|
30
|
-
all_orders = (relation.orders + [order_clause]).join(", ")
|
31
|
-
# override all previous orders
|
32
|
-
relation.reorder(all_orders)
|
33
|
-
end
|
34
|
-
|
32
|
+
scope = relation.order_by_rand_weighted(ranking_column, opts)
|
33
|
+
|
35
34
|
# override the limit if they are requesting multiple records
|
36
|
-
|
37
|
-
the_scope = the_scope.limit(max_items)
|
38
|
-
end
|
35
|
+
scope = scope.limit(max_items) if override_limit?(max_items, relation)
|
39
36
|
|
40
37
|
# return first record if method was called without parameters
|
41
|
-
max_items ?
|
38
|
+
max_items ? scope.to_a : scope.first
|
42
39
|
end
|
43
40
|
|
44
41
|
|
45
42
|
# This was my first implementation, adding it as an option for people to use
|
46
43
|
# and to fall back on for pesky DB one off situations...
|
47
44
|
# https://github.com/spilliton/randumb/issues/7
|
48
|
-
def random_by_id_shuffle(max_items = nil)
|
45
|
+
def random_by_id_shuffle(max_items = nil, opts={})
|
49
46
|
return_first_record = max_items.nil? # see return switch at end
|
50
47
|
max_items ||= 1
|
51
48
|
relation = clone
|
52
|
-
ids = fetch_random_ids(relation, max_items)
|
49
|
+
ids = fetch_random_ids(relation, max_items, opts)
|
53
50
|
|
54
51
|
# build new scope for final query
|
55
52
|
the_scope = klass.includes(includes_values)
|
@@ -58,21 +55,54 @@ module Randumb
|
|
58
55
|
the_scope = the_scope.select(select_values) unless select_values.empty?
|
59
56
|
|
60
57
|
# get the records and shuffle since the order of the ids
|
61
|
-
# passed to
|
62
|
-
|
58
|
+
# passed to where() isn't retained in the result set
|
59
|
+
rng = random_number_generator(opts)
|
60
|
+
records = the_scope.where(:id => ids).to_a.shuffle!(:random => rng)
|
63
61
|
|
64
62
|
# return first record if method was called without parameters
|
65
63
|
return_first_record ? records.first : records
|
66
64
|
end
|
67
65
|
|
66
|
+
def order_by_rand(opts = {})
|
67
|
+
if opts.is_a?(Hash)
|
68
|
+
build_order_scope(opts)
|
69
|
+
else
|
70
|
+
raise ArgumentError.new(
|
71
|
+
"order_by_rand() expects a hash of options. If you need to limit "\
|
72
|
+
"results simply add a limit to your scope ex: Artist.order_by_rand.limit(1)"
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def order_by_rand_weighted(ranking_column, opts={})
|
78
|
+
raise_unless_valid_ranking_column(ranking_column)
|
79
|
+
is_randumb_postges_case?(self, ranking_column)
|
80
|
+
build_order_scope(opts, ranking_column)
|
81
|
+
end
|
82
|
+
|
68
83
|
private
|
69
84
|
|
85
|
+
def build_order_scope(options, ranking_column=nil)
|
86
|
+
opts = options.reverse_merge(connection: connection, table_name: table_name)
|
87
|
+
|
88
|
+
order_clause = if ranking_column
|
89
|
+
Randumb::Syntax.random_weighted_order_clause(ranking_column, opts)
|
90
|
+
else
|
91
|
+
Randumb::Syntax.random_order_clause(opts)
|
92
|
+
end
|
93
|
+
|
94
|
+
# keep prior orders and append random
|
95
|
+
all_orders = (arel.orders + [order_clause])
|
96
|
+
# override all previous orders
|
97
|
+
reorder(all_orders)
|
98
|
+
end
|
99
|
+
|
70
100
|
# postgres won't let you do an order_by when also doing a distinct
|
71
101
|
# let's just use the in-memory option in this case
|
72
|
-
def is_randumb_postges_case?(relation, ranking_column)
|
102
|
+
def is_randumb_postges_case?(relation, ranking_column=nil)
|
73
103
|
if relation.respond_to?(:uniq_value) && relation.uniq_value && connection.adapter_name =~ /(postgres|postgis)/i
|
74
104
|
if ranking_column
|
75
|
-
raise Exception, "
|
105
|
+
raise Exception, "order_by_rand_weighted: not possible when using .uniq and the postgres/postgis db adapter"
|
76
106
|
else
|
77
107
|
return true
|
78
108
|
end
|
@@ -88,36 +118,9 @@ module Randumb
|
|
88
118
|
end
|
89
119
|
end
|
90
120
|
|
91
|
-
# sligtly different for each DB
|
92
|
-
def random_syntax
|
93
|
-
if connection.adapter_name =~ /(sqlite|postgres|postgis)/i
|
94
|
-
"RANDOM()"
|
95
|
-
elsif connection.adapter_name =~ /mysql/i
|
96
|
-
"RAND()"
|
97
|
-
else
|
98
|
-
raise Exception, "ActiveRecord adapter: '#{connection.adapter_name}' not supported by randumb. Send a pull request or open a ticket: https://github.com/spilliton/randumb"
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# builds the order clause to be appended in where clause
|
103
|
-
def random_order_clause(ranking_column)
|
104
|
-
if ranking_column.nil?
|
105
|
-
random_syntax
|
106
|
-
else
|
107
|
-
if connection.adapter_name =~ /sqlite/i
|
108
|
-
# computer multiplication is faster than division I was once taught...so translate here
|
109
|
-
max_int = 9223372036854775807.0
|
110
|
-
multiplier = 1.0 / max_int
|
111
|
-
"(#{ranking_column} * ABS(#{random_syntax} * #{multiplier}) ) DESC"
|
112
|
-
else
|
113
|
-
"(#{ranking_column} * #{random_syntax}) DESC"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
121
|
# Returns all matching ids from the db, shuffles them,
|
119
122
|
# then returns an array containing at most max_ids
|
120
|
-
def fetch_random_ids(relation, max_ids)
|
123
|
+
def fetch_random_ids(relation, max_ids, opts = {})
|
121
124
|
# clear these for our id only query
|
122
125
|
relation.select_values = []
|
123
126
|
relation.includes_values = []
|
@@ -127,30 +130,54 @@ module Randumb
|
|
127
130
|
|
128
131
|
id_results = connection.select_all(id_only_relation.to_sql)
|
129
132
|
|
133
|
+
rng = random_number_generator(opts)
|
130
134
|
if max_ids == 1 && id_results.count > 0
|
131
|
-
|
135
|
+
rand_index = rng.rand(id_results.count)
|
136
|
+
[id_results[rand_index]["id"]]
|
132
137
|
else
|
133
|
-
# ActiveRecord 4 requires .
|
134
|
-
arr = id_results.respond_to?(:
|
135
|
-
arr.shuffle![0,max_ids].collect!{ |h| h[
|
138
|
+
# ActiveRecord 4 requires .to_a
|
139
|
+
arr = id_results.respond_to?(:to_a) ? id_results.to_a : id_results
|
140
|
+
arr.shuffle!(random: rng)[0, max_ids].collect! { |h| h["id"] }
|
136
141
|
end
|
137
142
|
end
|
138
143
|
|
144
|
+
def random_number_generator(opts={})
|
145
|
+
if seed = opts[:seed]
|
146
|
+
Random.new(seed)
|
147
|
+
else
|
148
|
+
Random.new
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def override_limit?(max_items, relation)
|
153
|
+
max_items && (!relation.limit_value || relation.limit_value > max_items)
|
154
|
+
end
|
155
|
+
|
139
156
|
end
|
140
157
|
|
141
158
|
|
142
159
|
# Class methods
|
160
|
+
# where(nil) is because:
|
161
|
+
# http://stackoverflow.com/questions/18198963/with-rails-4-model-scoped-is-deprecated-but-model-all-cant-replace-it
|
143
162
|
module Base
|
144
|
-
def random(max_items = nil)
|
145
|
-
|
163
|
+
def random(max_items = nil, opts = {})
|
164
|
+
where(nil).random(max_items, opts)
|
165
|
+
end
|
166
|
+
|
167
|
+
def random_weighted(ranking_column, max_items = nil, opts = {})
|
168
|
+
where(nil).random_weighted(ranking_column, max_items, opts)
|
169
|
+
end
|
170
|
+
|
171
|
+
def random_by_id_shuffle(max_items = nil, opts = {})
|
172
|
+
where(nil).random_by_id_shuffle(max_items, opts)
|
146
173
|
end
|
147
174
|
|
148
|
-
def
|
149
|
-
|
175
|
+
def order_by_rand(opts = {})
|
176
|
+
where(nil).order_by_rand(opts)
|
150
177
|
end
|
151
178
|
|
152
|
-
def
|
153
|
-
|
179
|
+
def order_by_rand_weighted(ranking_column, opts={})
|
180
|
+
where(nil).order_by_rand_weighted(ranking_column, opts)
|
154
181
|
end
|
155
182
|
end
|
156
183
|
|
@@ -159,6 +186,7 @@ module Randumb
|
|
159
186
|
module MethodMissingMagicks
|
160
187
|
def method_missing(symbol, *args)
|
161
188
|
if symbol.to_s =~ /^random_weighted_by_(\w+)$/
|
189
|
+
ActiveSupport::Deprecation.warn "Dynamic finders will be removed in randumb 1.0 http://guides.rubyonrails.org/active_record_querying.html#dynamic-finders"
|
162
190
|
random_weighted($1, *args)
|
163
191
|
else
|
164
192
|
super
|
@@ -167,6 +195,7 @@ module Randumb
|
|
167
195
|
|
168
196
|
def respond_to?(symbol, include_private=false)
|
169
197
|
if symbol.to_s =~ /^random_weighted_by_(\w+)$/
|
198
|
+
ActiveSupport::Deprecation.warn "Dynamic finders will be removed in randumb 1.0 http://guides.rubyonrails.org/active_record_querying.html#dynamic-finders"
|
170
199
|
true
|
171
200
|
else
|
172
201
|
super
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Randumb
|
2
|
+
class Syntax
|
3
|
+
class << self
|
4
|
+
|
5
|
+
# builds the order clause to be appended in where clause
|
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"
|
19
|
+
else
|
20
|
+
"(#{ranking_column} * #{random_for(opts)}) DESC"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# sligtly different for each DB
|
27
|
+
def random_for(opts)
|
28
|
+
connection = opts[:connection]
|
29
|
+
adapter_name = connection.adapter_name
|
30
|
+
if adapter_name =~ /(sqlite)/i
|
31
|
+
random_for_sqlite(opts)
|
32
|
+
elsif adapter_name =~ /(postgres|postgis)/i
|
33
|
+
random_for_postgres(opts)
|
34
|
+
elsif adapter_name =~ /mysql/i
|
35
|
+
random_for_mysql(opts)
|
36
|
+
else
|
37
|
+
raise Exception, "ActiveRecord adapter: '#{adapter_name}' not supported by randumb. Send a pull request or open a ticket: https://github.com/spilliton/randumb"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def random_for_sqlite(opts)
|
42
|
+
if seed = opts[:seed]
|
43
|
+
table_name = opts[:table_name]
|
44
|
+
# SQLLite does not support a random seed. However, pseudo-randomness
|
45
|
+
# can be achieved by sorting on a hash of the id field (generated by
|
46
|
+
# multiplying the id by the random seed and ignoring everything before
|
47
|
+
# the decimal).
|
48
|
+
# See http://stackoverflow.com/questions/2171578/seeding-sqlite-random
|
49
|
+
seed_value = Random.new(seed.to_i).rand
|
50
|
+
"(SUBSTR(#{table_name}.id * #{seed_value}, LENGTH(#{table_name}.id) + 2))"
|
51
|
+
else
|
52
|
+
"RANDOM()"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def random_for_postgres(opts)
|
57
|
+
# Postgres random seeding requires executing an extra "SELECT SETSEED(value)" on the connection.
|
58
|
+
# See http://www.postgresql.org/docs/8.3/static/sql-set.html:
|
59
|
+
#
|
60
|
+
# Sets the internal seed for the random number generator (the
|
61
|
+
# function random). Allowed values are floating-point numbers
|
62
|
+
# between 0 and 1, which are then multiplied by 2^(31)-1.
|
63
|
+
#
|
64
|
+
if seed = opts[:seed]
|
65
|
+
connection = opts[:connection]
|
66
|
+
seed_value = Random.new(seed.to_i).rand # map integer seed to a value: 0 <= value < 1
|
67
|
+
connection.execute "SELECT SETSEED(#{seed_value})"
|
68
|
+
end
|
69
|
+
"RANDOM()"
|
70
|
+
end
|
71
|
+
|
72
|
+
def random_for_mysql(opts)
|
73
|
+
if seed = opts[:seed]
|
74
|
+
"RAND(#{seed.to_i})"
|
75
|
+
else
|
76
|
+
"RAND()"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/randumb/version.rb
CHANGED
data/test/models/artist.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
class Artist < ActiveRecord::Base
|
2
2
|
has_many :albums
|
3
|
-
|
3
|
+
|
4
|
+
if Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new("3.2")
|
5
|
+
default_scope { where("artists.deleted_at IS NULL") }
|
6
|
+
else
|
7
|
+
default_scope where("artists.deleted_at IS NULL")
|
8
|
+
end
|
9
|
+
|
4
10
|
scope :at_least_three_views, -> { where("views >= 3") }
|
5
|
-
end
|
11
|
+
end
|
data/test/models/factories.rb
CHANGED
data/test/randumb_test.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
$:.unshift '.'; require File.dirname(__FILE__) + '/test_helper'
|
2
2
|
|
3
|
-
class RandumbTest < Test
|
4
|
-
|
5
|
-
def assert_equal_for_both_methods(expected, obj, params
|
6
|
-
|
7
|
-
|
3
|
+
class RandumbTest < Minitest::Test
|
4
|
+
|
5
|
+
def assert_equal_for_both_methods(expected, obj, *params)
|
6
|
+
if expected.nil?
|
7
|
+
assert_nil obj.send(:random, *params), "when calling random"
|
8
|
+
assert_nil obj.send(:random_by_id_shuffle, *params), "when calling random_by_id_shuffle"
|
9
|
+
else
|
10
|
+
assert_equal expected, obj.send(:random, *params), "when calling random"
|
11
|
+
assert_equal expected, obj.send(:random_by_id_shuffle, *params), "when calling random_by_id_shuffle"
|
12
|
+
end
|
8
13
|
end
|
9
14
|
|
10
|
-
|
11
15
|
should "should return empty when no record in table" do
|
12
16
|
assert_equal 0, Artist.count
|
13
17
|
|
@@ -15,36 +19,56 @@ class RandumbTest < Test::Unit::TestCase
|
|
15
19
|
# above is equivalent to:
|
16
20
|
# assert_equal nil, Artist.random
|
17
21
|
# assert_equal nil, Artist.random_by_id_shuffle
|
22
|
+
assert_nil Artist.order_by_rand.first
|
18
23
|
|
19
24
|
assert_equal_for_both_methods [], Artist, 1
|
20
25
|
# above is equivalent to:
|
21
26
|
# assert_equal [], Artist.random(1)
|
22
27
|
# assert_equal [], Artist.random_by_id_shuffle(1)
|
28
|
+
assert_equal [], Artist.order_by_rand.limit(1).all
|
23
29
|
|
24
30
|
assert_equal_for_both_methods nil, Artist.limit(50)
|
25
31
|
end
|
26
32
|
|
33
|
+
should "raise helpful error if non-hash passed" do
|
34
|
+
error = assert_raises ArgumentError do
|
35
|
+
Artist.order_by_rand(10)
|
36
|
+
end
|
37
|
+
assert error.message.include?("order_by_rand")
|
38
|
+
end
|
39
|
+
|
27
40
|
context "1 record in the table" do
|
28
41
|
setup do
|
29
|
-
@high_on_fire =
|
42
|
+
@high_on_fire = FactoryBot.create(:artist, :name => "High On Fire", :views => 1)
|
30
43
|
end
|
31
44
|
|
32
45
|
should "select only 1 record even when you request more" do
|
33
46
|
assert_equal 1, Artist.count
|
34
47
|
|
35
48
|
assert_equal_for_both_methods @high_on_fire, Artist
|
49
|
+
assert_equal @high_on_fire, Artist.order_by_rand.first
|
50
|
+
|
36
51
|
assert_equal_for_both_methods [@high_on_fire], Artist, 1
|
37
52
|
assert_equal_for_both_methods [@high_on_fire], Artist, 30
|
53
|
+
assert_equal [@high_on_fire], Artist.limit(30).order_by_rand.all
|
38
54
|
end
|
39
55
|
|
40
56
|
should "not return a record that doesnt match where" do
|
41
57
|
assert_equal_for_both_methods nil, Artist.where(:name => "The Little Gentlemen")
|
42
58
|
end
|
43
59
|
|
60
|
+
should "respect default scope" do
|
61
|
+
@high_on_fire.deleted_at = Time.now.utc
|
62
|
+
@high_on_fire.save!
|
63
|
+
assert_equal 0, Artist.count
|
64
|
+
assert_nil Artist.order_by_rand.first
|
65
|
+
assert_equal @high_on_fire, Artist.unscoped.order_by_rand.first
|
66
|
+
end
|
67
|
+
|
44
68
|
context "3 records in table" do
|
45
69
|
setup do
|
46
|
-
@fiona_apple =
|
47
|
-
@magnetic_fields =
|
70
|
+
@fiona_apple = FactoryBot.create(:artist, :name => "Fiona Apple", :views => 3)
|
71
|
+
@magnetic_fields = FactoryBot.create(:artist, :name => "The Magnetic Fields", :views => 2)
|
48
72
|
end
|
49
73
|
|
50
74
|
should "apply randomness after other orders when using sql method" do
|
@@ -61,19 +85,27 @@ class RandumbTest < Test::Unit::TestCase
|
|
61
85
|
end
|
62
86
|
|
63
87
|
should "respect selecting certain columns" do
|
88
|
+
# this version doesn't throw for some reason...
|
89
|
+
skip_throw_missing = ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
90
|
+
|
64
91
|
assert_equal 3, Artist.find(@fiona_apple.id).views
|
65
92
|
|
66
93
|
artists = Artist.select(:name).random(3)
|
67
94
|
assert_equal false, artists.first.name.nil?
|
68
|
-
|
95
|
+
assert_raises (ActiveModel::MissingAttributeError) { artists.first.views } unless skip_throw_missing
|
96
|
+
|
97
|
+
artists = Artist.select(:name).order_by_rand.limit(3)
|
98
|
+
assert_equal false, artists.first.name.nil?
|
99
|
+
assert_raises (ActiveModel::MissingAttributeError) { artists.first.views } unless skip_throw_missing
|
69
100
|
|
70
101
|
artists = Artist.select(:name).random_by_id_shuffle(3)
|
71
102
|
assert_equal false, artists.first.name.nil?
|
72
|
-
|
103
|
+
assert_raises (ActiveModel::MissingAttributeError) { artists.first.views } unless skip_throw_missing
|
73
104
|
end
|
74
105
|
|
75
106
|
should "respect scopes" do
|
76
107
|
assert_equal_for_both_methods [@fiona_apple], Artist.at_least_three_views, 3
|
108
|
+
assert_equal [@fiona_apple], Artist.at_least_three_views.order_by_rand.limit(3)
|
77
109
|
end
|
78
110
|
|
79
111
|
should "select only as many as in the db if we request more" do
|
@@ -92,10 +124,10 @@ class RandumbTest < Test::Unit::TestCase
|
|
92
124
|
|
93
125
|
context "with some albums" do
|
94
126
|
setup do
|
95
|
-
@tidal =
|
96
|
-
@extraordinary_machine =
|
97
|
-
@sixty_nine_love_songs =
|
98
|
-
@snakes_for_the_divine =
|
127
|
+
@tidal = FactoryBot.create(:album, :name => "Tidal", :artist => @fiona_apple)
|
128
|
+
@extraordinary_machine = FactoryBot.create(:album, :name => "Extraordinary Machine", :artist => @fiona_apple)
|
129
|
+
@sixty_nine_love_songs = FactoryBot.create(:album, :name => "69 Love Songs", :artist => @magnetic_fields)
|
130
|
+
@snakes_for_the_divine = FactoryBot.create(:album, :name => "Snakes For the Divine", :artist => @high_on_fire)
|
99
131
|
end
|
100
132
|
|
101
133
|
|
@@ -103,7 +135,7 @@ class RandumbTest < Test::Unit::TestCase
|
|
103
135
|
artists = Artist.includes(:albums).random(10)
|
104
136
|
fiona_apple = artists.find { |a| a.name == "Fiona Apple" }
|
105
137
|
# if I add a new album now, it shouldn't be in the albums assocation yet b/c it was already loaded
|
106
|
-
|
138
|
+
FactoryBot.create(:album, :name => "When The Pawn", :artist => @fiona_apple)
|
107
139
|
|
108
140
|
assert_equal 2, fiona_apple.albums.length
|
109
141
|
assert_equal 3, @fiona_apple.reload.albums.length
|
@@ -113,7 +145,7 @@ class RandumbTest < Test::Unit::TestCase
|
|
113
145
|
artists = Artist.includes(:albums).random_by_id_shuffle(10)
|
114
146
|
fiona_apple = artists.find { |a| a.name == "Fiona Apple" }
|
115
147
|
# if I add a new album now, it shouldn't be in the albums assocation yet b/c it was already loaded
|
116
|
-
|
148
|
+
FactoryBot.create(:album, :name => "When The Pawn", :artist => @fiona_apple)
|
117
149
|
|
118
150
|
assert_equal 2, fiona_apple.albums.length
|
119
151
|
assert_equal 3, @fiona_apple.reload.albums.length
|
@@ -144,8 +176,8 @@ class RandumbTest < Test::Unit::TestCase
|
|
144
176
|
should "work with uniq" do
|
145
177
|
assert_equal 2, Artist.uniq.random(2).length
|
146
178
|
assert_equal 2, Artist.uniq.random_by_id_shuffle(2).length
|
147
|
-
|
148
|
-
|
179
|
+
assert !Artist.uniq.random.nil?
|
180
|
+
assert !Artist.uniq.random_by_id_shuffle.nil?
|
149
181
|
end
|
150
182
|
end
|
151
183
|
|
@@ -157,8 +189,8 @@ class RandumbTest < Test::Unit::TestCase
|
|
157
189
|
|
158
190
|
context "2 records in table" do
|
159
191
|
setup do
|
160
|
-
@hum =
|
161
|
-
@minutemen =
|
192
|
+
@hum = FactoryBot.create(:artist, :name => "Hum", :views => 3)
|
193
|
+
@minutemen = FactoryBot.create(:artist, :name => "Minutemen", :views => 2)
|
162
194
|
end
|
163
195
|
|
164
196
|
should "eventually render the 2 possible orders using default method" do
|
@@ -190,6 +222,29 @@ class RandumbTest < Test::Unit::TestCase
|
|
190
222
|
assert order1_found
|
191
223
|
assert order2_found
|
192
224
|
end
|
193
|
-
end
|
194
225
|
|
226
|
+
context "using seed" do
|
227
|
+
setup do
|
228
|
+
@seed = 123
|
229
|
+
end
|
230
|
+
|
231
|
+
should "always return the same order using default method" do
|
232
|
+
seeded_order = Artist.random(2, seed: @seed)
|
233
|
+
10.times do
|
234
|
+
assert_equal seeded_order, Artist.random(2, seed: @seed)
|
235
|
+
end
|
236
|
+
|
237
|
+
10.times do
|
238
|
+
assert_equal seeded_order, Artist.order_by_rand(seed: @seed).limit(2)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
should "always return the same order using shuffle method" do
|
243
|
+
seeded_order = Artist.random_by_id_shuffle(2, seed: @seed)
|
244
|
+
10.times do
|
245
|
+
assert_equal seeded_order, Artist.random_by_id_shuffle(2, seed: @seed)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
195
250
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,33 +1,29 @@
|
|
1
1
|
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
2
2
|
require 'rubygems'
|
3
|
-
require
|
3
|
+
require "minitest/autorun"
|
4
4
|
require 'shoulda'
|
5
|
-
require '
|
5
|
+
require 'factory_bot'
|
6
6
|
require 'faker'
|
7
7
|
require 'active_record'
|
8
8
|
require 'active_support/dependencies'
|
9
|
-
require 'active_support/core_ext/logger'
|
10
|
-
# require 'active_record/fixtures'
|
11
9
|
require 'randumb'
|
12
10
|
|
13
11
|
MODELS_PATH = File.join(File.dirname(__FILE__), 'models')
|
14
12
|
|
15
|
-
|
16
|
-
ActiveRecord::Base.logger = Logger.new(STDERR)
|
17
|
-
ActiveRecord::Base.logger.level = Logger::WARN
|
18
|
-
|
19
13
|
config = YAML::load(File.open(File.expand_path("../databases.yml", __FILE__)))
|
20
14
|
version = ActiveRecord::VERSION::STRING
|
21
15
|
driver = (ENV["DB"] or "sqlite3").downcase
|
22
16
|
in_memory = config[driver]["database"] == ":memory:"
|
23
|
-
|
17
|
+
|
24
18
|
# http://about.travis-ci.org/docs/user/database-setup/
|
25
19
|
commands = {
|
26
20
|
"mysql" => "mysql -e 'create database randumb_test;'",
|
27
21
|
"postgres" => "psql -c 'create database randumb_test;' -U postgres"
|
28
22
|
}
|
29
23
|
%x{#{commands[driver] || true}}
|
30
|
-
|
24
|
+
|
25
|
+
|
26
|
+
|
31
27
|
ActiveRecord::Base.establish_connection config[driver]
|
32
28
|
puts "Using #{RUBY_VERSION} AR #{version} with #{driver}"
|
33
29
|
|
@@ -38,16 +34,18 @@ ActiveRecord::Base.connection.create_table(:artists, :force => true) do |t|
|
|
38
34
|
t.float "rating"
|
39
35
|
t.datetime "created_at"
|
40
36
|
t.datetime "updated_at"
|
37
|
+
t.datetime "deleted_at"
|
41
38
|
end
|
42
|
-
|
39
|
+
|
43
40
|
ActiveRecord::Base.connection.create_table(:albums, :force => true) do |t|
|
44
41
|
t.string "name"
|
45
42
|
t.integer "views"
|
46
43
|
t.integer "artist_id"
|
47
44
|
t.datetime "created_at"
|
48
45
|
t.datetime "updated_at"
|
46
|
+
t.datetime "deleted_at"
|
49
47
|
end
|
50
|
-
|
48
|
+
|
51
49
|
# setup models for lazy load
|
52
50
|
dep = defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies : ::Dependencies
|
53
51
|
dep.autoload_paths.unshift MODELS_PATH
|
@@ -56,7 +54,7 @@ dep.autoload_paths.unshift MODELS_PATH
|
|
56
54
|
require 'test/models/factories'
|
57
55
|
|
58
56
|
# clear db for every test
|
59
|
-
class Test
|
57
|
+
class Minitest::Test
|
60
58
|
|
61
59
|
def setup
|
62
60
|
Artist.delete_all
|
@@ -65,3 +63,6 @@ class Test::Unit::TestCase
|
|
65
63
|
|
66
64
|
end
|
67
65
|
|
66
|
+
|
67
|
+
# Silence deprications
|
68
|
+
ActiveSupport::Deprecation.silenced = true
|
data/test/weighted_test.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
$:.unshift '.'; require File.dirname(__FILE__) + '/test_helper'
|
2
2
|
|
3
|
-
class WeightedTest < Test
|
3
|
+
class WeightedTest < Minitest::Test
|
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
|
@@ -41,7 +41,7 @@ class WeightedTest < Test::Unit::TestCase
|
|
41
41
|
end
|
42
42
|
|
43
43
|
should "not interfere with active record dynamic methods that use method_missing" do
|
44
|
-
@artist =
|
44
|
+
@artist = FactoryBot.create(:artist, :name => 'Spiritualized')
|
45
45
|
assert_equal @artist, Artist.find_by_name('Spiritualized')
|
46
46
|
end
|
47
47
|
|
@@ -59,13 +59,26 @@ class WeightedTest < Test::Unit::TestCase
|
|
59
59
|
context "order by ranking_column" do
|
60
60
|
setup do
|
61
61
|
@view_counts = [1, 2, 3, 4, 5]
|
62
|
-
@view_counts.each { |views|
|
62
|
+
@view_counts.each { |views| FactoryBot.create(:artist, views: views) }
|
63
|
+
end
|
64
|
+
|
65
|
+
should "respect default scope" do
|
66
|
+
artist = Artist.last
|
67
|
+
artist.deleted_at = Time.now.utc
|
68
|
+
artist.save!
|
69
|
+
50.times do
|
70
|
+
assert Artist.random_weighted("views").id != artist.id, "should never pick deleted artist"
|
71
|
+
end
|
72
|
+
assert_equal 4, Artist.order_by_rand_weighted("views").all.length
|
63
73
|
end
|
64
74
|
|
65
75
|
should "order by ranking column with explicit method call" do
|
66
76
|
assert_hits_per_views do
|
67
77
|
Artist.random_weighted("views").views
|
68
78
|
end
|
79
|
+
assert_hits_per_views do
|
80
|
+
Artist.order_by_rand_weighted("views").first.views
|
81
|
+
end
|
69
82
|
end
|
70
83
|
|
71
84
|
should "order by ranking column with method_missing" do
|
@@ -80,6 +93,12 @@ class WeightedTest < Test::Unit::TestCase
|
|
80
93
|
assert(result.size == 5)
|
81
94
|
result.first.views
|
82
95
|
end
|
96
|
+
|
97
|
+
assert_hits_per_views do
|
98
|
+
result = Artist.order_by_rand_weighted("views").limit(5).all
|
99
|
+
assert(result.size == 5)
|
100
|
+
result.first.views
|
101
|
+
end
|
83
102
|
end
|
84
103
|
|
85
104
|
should "order by ranking column with method_missing using max_items" do
|
@@ -98,31 +117,45 @@ class WeightedTest < Test::Unit::TestCase
|
|
98
117
|
result.last.views
|
99
118
|
end
|
100
119
|
end
|
120
|
+
|
121
|
+
assert_raises(MiniTest::Assertion) do
|
122
|
+
assert_hits_per_views do
|
123
|
+
result = Artist.order_by_rand_weighted(:views).limit(3)
|
124
|
+
assert(result.size == 3)
|
125
|
+
result.last.views
|
126
|
+
end
|
127
|
+
end
|
101
128
|
end
|
102
129
|
|
103
130
|
should "order by ranking column with method_missing using 1 max_items" do
|
104
131
|
assert_hits_per_views do
|
105
132
|
result = Artist.random_weighted_by_views(1)
|
106
|
-
|
133
|
+
assert_equal 1, result.length
|
107
134
|
result.first.views
|
108
135
|
end
|
109
|
-
end
|
110
136
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
1000.times do
|
116
|
-
hits_per_views[yield] += 1
|
117
|
-
end
|
118
|
-
last_count = 0
|
119
|
-
@view_counts.each do |views|
|
120
|
-
hits = hits_per_views[views]
|
121
|
-
assert(hits >= last_count, "#{hits} > #{last_count} : There were an unexpected number of visits: #{hits_per_views.to_yaml}")
|
122
|
-
last_count = hits
|
137
|
+
assert_hits_per_views do
|
138
|
+
result = Artist.order_by_rand_weighted(:views).limit(1)
|
139
|
+
assert_equal 1, result.length
|
140
|
+
result.first.views
|
123
141
|
end
|
124
142
|
end
|
125
143
|
end
|
126
144
|
|
145
|
+
def assert_hits_per_views
|
146
|
+
hits_per_views = Hash.new
|
147
|
+
@view_counts.each { |views| hits_per_views[views] = 0 }
|
148
|
+
|
149
|
+
1000.times do
|
150
|
+
hits_per_views[yield] += 1
|
151
|
+
end
|
152
|
+
last_count = 0
|
153
|
+
@view_counts.each do |views|
|
154
|
+
hits = hits_per_views[views]
|
155
|
+
assert(hits >= last_count, "#{hits} > #{last_count} : There were an unexpected number of visits: #{hits_per_views.to_yaml}")
|
156
|
+
last_count = hits
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
127
160
|
|
128
|
-
end
|
161
|
+
end
|
metadata
CHANGED
@@ -1,111 +1,181 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: randumb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.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: 2020-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 3.0.
|
33
|
+
version: 3.0.20
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 3.0.
|
40
|
+
version: 3.0.20
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: activerecord
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 3.0.
|
47
|
+
version: 3.0.20
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 3.0.
|
54
|
+
version: 3.0.20
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: sqlite3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.3.6
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.3.6
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mysql2
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - '='
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
75
|
+
version: 0.4.10
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - '='
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
82
|
+
version: 0.4.10
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pg
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.19.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.19.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bigdecimal
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 1.4.2
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.4.2
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: minitest
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
69
125
|
- !ruby/object:Gem::Dependency
|
70
126
|
name: shoulda
|
71
127
|
requirement: !ruby/object:Gem::Requirement
|
72
128
|
requirements:
|
73
|
-
- -
|
129
|
+
- - ">="
|
74
130
|
- !ruby/object:Gem::Version
|
75
131
|
version: '0'
|
76
132
|
type: :development
|
77
133
|
prerelease: false
|
78
134
|
version_requirements: !ruby/object:Gem::Requirement
|
79
135
|
requirements:
|
80
|
-
- -
|
136
|
+
- - ">="
|
81
137
|
- !ruby/object:Gem::Version
|
82
138
|
version: '0'
|
83
139
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
140
|
+
name: factory_bot
|
85
141
|
requirement: !ruby/object:Gem::Requirement
|
86
142
|
requirements:
|
87
|
-
- -
|
143
|
+
- - ">="
|
88
144
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
145
|
+
version: '0'
|
90
146
|
type: :development
|
91
147
|
prerelease: false
|
92
148
|
version_requirements: !ruby/object:Gem::Requirement
|
93
149
|
requirements:
|
94
|
-
- -
|
150
|
+
- - ">="
|
95
151
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
152
|
+
version: '0'
|
97
153
|
- !ruby/object:Gem::Dependency
|
98
154
|
name: faker
|
99
155
|
requirement: !ruby/object:Gem::Requirement
|
100
156
|
requirements:
|
101
|
-
- -
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: pry
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
102
172
|
- !ruby/object:Gem::Version
|
103
173
|
version: '0'
|
104
174
|
type: :development
|
105
175
|
prerelease: false
|
106
176
|
version_requirements: !ruby/object:Gem::Requirement
|
107
177
|
requirements:
|
108
|
-
- -
|
178
|
+
- - ">="
|
109
179
|
- !ruby/object:Gem::Version
|
110
180
|
version: '0'
|
111
181
|
description:
|
@@ -114,9 +184,10 @@ executables: []
|
|
114
184
|
extensions: []
|
115
185
|
extra_rdoc_files: []
|
116
186
|
files:
|
187
|
+
- lib/randumb.rb
|
117
188
|
- lib/randumb/relation.rb
|
189
|
+
- lib/randumb/syntax.rb
|
118
190
|
- lib/randumb/version.rb
|
119
|
-
- lib/randumb.rb
|
120
191
|
- test/models/album.rb
|
121
192
|
- test/models/artist.rb
|
122
193
|
- test/models/factories.rb
|
@@ -133,17 +204,16 @@ require_paths:
|
|
133
204
|
- lib
|
134
205
|
required_ruby_version: !ruby/object:Gem::Requirement
|
135
206
|
requirements:
|
136
|
-
- -
|
207
|
+
- - ">="
|
137
208
|
- !ruby/object:Gem::Version
|
138
209
|
version: '0'
|
139
210
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
211
|
requirements:
|
141
|
-
- -
|
212
|
+
- - ">="
|
142
213
|
- !ruby/object:Gem::Version
|
143
214
|
version: '0'
|
144
215
|
requirements: []
|
145
|
-
|
146
|
-
rubygems_version: 2.0.5
|
216
|
+
rubygems_version: 3.0.3
|
147
217
|
signing_key:
|
148
218
|
specification_version: 4
|
149
219
|
summary: Adds the ability to pull random records from ActiveRecord
|